Coverage Report

Created: 2026-09-07 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kamailio/src/core/tsend.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 *
4
 * This file is part of Kamailio, a free SIP server.
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 *
8
 * Kamailio is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version
12
 *
13
 * Kamailio is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
23
/*!
24
 * \file
25
 * \brief Kamailio core :: send with timeout for stream and datagram sockets
26
 * \ingroup core
27
 * Module: \ref core
28
 */
29
30
#include <string.h>
31
#include <errno.h>
32
#include <poll.h>
33
34
#include <sys/types.h>
35
#include <sys/socket.h>
36
#include <sys/uio.h>
37
38
#include "dprint.h"
39
#include "timer.h"
40
#include "timer_ticks.h"
41
42
/* the functions below are very similar => some generic macros */
43
#define TSEND_INIT                                            \
44
0
  int n;                                                    \
45
0
  struct pollfd pf;                                         \
46
0
  ticks_t expire;                                           \
47
0
  s_ticks_t diff;                                           \
48
0
  expire = get_ticks_raw() + MS_TO_TICKS((ticks_t)timeout); \
49
0
  pf.fd = fd;                                               \
50
0
  pf.events = POLLOUT
51
52
#define TSEND_POLL(f_name)                                                   \
53
0
  poll_loop:                                                               \
54
0
  while(1) {                                                               \
55
0
    if(timeout == -1)                                                    \
56
0
      n = poll(&pf, 1, -1);                                            \
57
0
    else {                                                               \
58
0
      diff = expire - get_ticks_raw();                                 \
59
0
      if(diff <= 0) {                                                  \
60
0
        LM_ERR(" - " f_name ": send timeout (%d)\n", timeout);       \
61
0
        goto error;                                                  \
62
0
      }                                                                \
63
0
      n = poll(&pf, 1, TICKS_TO_MS((ticks_t)diff));                    \
64
0
    }                                                                    \
65
0
    if(n < 0) {                                                          \
66
0
      if(errno == EINTR)                                               \
67
0
        continue; /* signal, ignore */                               \
68
0
      LM_ERR(" - " f_name ": poll failed: %s [%d]\n", strerror(errno), \
69
0
          errno);                                                  \
70
0
      goto error;                                                      \
71
0
    } else if(n == 0) {                                                  \
72
0
      /* timeout */                                                    \
73
0
      LM_ERR(" - " f_name ": send timeout (p %d)\n", timeout);         \
74
0
      goto error;                                                      \
75
0
    }                                                                    \
76
0
    if(pf.revents & POLLOUT) {                                           \
77
0
      /* we can write again */                                         \
78
0
      goto again;                                                      \
79
0
    } else if(pf.revents & (POLLERR | POLLHUP | POLLNVAL)) {             \
80
0
      LM_ERR(" - " f_name ": bad poll flags %x\n", pf.revents);        \
81
0
      goto error;                                                      \
82
0
    }                                                                    \
83
0
    /* if POLLIN or POLLPRI or other non-harmful events happened,    \
84
0
     * continue ( although poll should never signal them since we're  \
85
0
     * not interested in them => we should never reach this point) */   \
86
0
  }
87
88
89
#define TSEND_ERR_CHECK(f_name)                                       \
90
0
  if(n < 0) {                                                       \
91
0
    if(errno == EINTR)                                            \
92
0
      goto again;                                               \
93
0
    else if(errno != EAGAIN && errno != EWOULDBLOCK) {            \
94
0
      LM_ERR(" - " f_name ": failed to send: (%d) %s\n", errno, \
95
0
          strerror(errno));                                 \
96
0
      goto error;                                               \
97
0
    } else                                                        \
98
0
      goto poll_loop;                                           \
99
0
  }
100
101
102
/* sends on fd (which must be O_NONBLOCK if you want a finite timeout); if it
103
 * cannot send any data
104
 * in timeout milliseconds it will return ERROR
105
 * if timeout==-1, it waits forever
106
 * returns: -1 on error, or number of bytes written
107
 *  (if less than len => couldn't send all)
108
 *  bugs: signals will reset the timer
109
 */
110
int tsend_stream(int fd, const char *buf, unsigned int len, int timeout)
111
0
{
112
0
  int written;
113
0
  TSEND_INIT;
114
115
0
  written = 0;
116
0
again:
117
0
  n = send(fd, buf, len,
118
0
#ifdef HAVE_MSG_NOSIGNAL
119
0
      MSG_NOSIGNAL
120
#else
121
      0
122
#endif
123
0
  );
124
0
  TSEND_ERR_CHECK("tsend_stream");
125
0
  written += n;
126
0
  if(n < (int)len) {
127
    /* partial write */
128
0
    buf += n;
129
0
    len -= n;
130
0
  } else {
131
    /* successful full write */
132
0
    return written;
133
0
  }
134
0
  TSEND_POLL("tsend_stream");
135
0
error:
136
0
  return -1;
137
0
}
138
139
140
/* sends on dgram fd (which must be O_NONBLOCK); if it cannot send any data
141
 * in timeout milliseconds it will return ERROR
142
 * returns: -1 on error, or number of bytes written
143
 *  (if less than len => couldn't send all)
144
 *  bugs: signals will reset the timer
145
 */
146
int tsend_dgram(int fd, const char *buf, unsigned int len,
147
    const struct sockaddr *to, socklen_t tolen, int timeout)
148
0
{
149
0
  TSEND_INIT;
150
0
again:
151
0
  n = sendto(fd, buf, len, 0, to, tolen);
152
0
  TSEND_ERR_CHECK("tsend_dgram");
153
  /* we don't care about partial writes: they shouldn't happen on
154
   * a datagram socket */
155
0
  return n;
156
0
  TSEND_POLL("tsend_datagram");
157
0
error:
158
0
  return -1;
159
0
}
160
161
162
/* sends on connected datagram fd (which must be O_NONBLOCK);
163
 * if it cannot send any data in timeout milliseconds it will return ERROR
164
 * returns: -1 on error, or number of bytes written
165
 *  (if less than len => couldn't send all)
166
 *  bugs: signals will reset the timer
167
 */
168
169
int tsend_dgram_ev(int fd, const struct iovec *v, int count, int timeout)
170
0
{
171
0
  TSEND_INIT;
172
0
again:
173
0
  n = writev(fd, v, count);
174
0
  TSEND_ERR_CHECK("tsend_datagram_ev");
175
0
  return n;
176
0
  TSEND_POLL("tsend_datagram_ev");
177
0
error:
178
0
  return -1;
179
0
}