Coverage Report

Created: 2024-02-25 06:34

/src/kamailio/src/core/pass_fd.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 *
4
 * This file is part of Kamailio, a free SIP server.
5
 *
6
 * Kamailio is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * Kamailio is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
 */
20
21
/*!
22
 * \file
23
 * \brief Kamailio core ::
24
 * \ingroup core
25
 * Module: \ref core
26
 */
27
28
#ifdef USE_TCP
29
30
#include "pass_fd.h"
31
32
#include <sys/types.h>
33
#include <sys/socket.h>
34
#include <sys/uio.h>
35
#include <stdlib.h> /* for NULL definition on openbsd */
36
#include <errno.h>
37
#include <string.h>
38
#ifdef NO_MSG_WAITALL
39
#include <poll.h>
40
#endif /* NO_MSG_WAITALL */
41
42
#include "dprint.h"
43
44
45
/** receive all the data or returns error (handles EINTR etc.)
46
 * params: socket
47
 *         data     - buffer for the results
48
 *         data_len -
49
 *         flags    - recv flags for the first recv (see recv(2)), only
50
 *                    0, MSG_WAITALL and MSG_DONTWAIT make sense
51
 * if flags is set to MSG_DONTWAIT (or to 0 and the socket fd is non-blocking),
52
 * and if no data is queued on the fd, recv_all will not wait (it will
53
 * return error and set errno to EAGAIN/EWOULDBLOCK). However if even 1 byte
54
 *  is queued, the call will block until the whole data_len was read or an
55
 *  error or eof occurred ("semi-nonblocking" behaviour,  some tcp code
56
 *   counts on it).
57
 * if flags is set to MSG_WAITALL it will block even if no byte is available.
58
 *
59
 * returns: bytes read or error (<0)
60
 * can return < data_len if EOF */
61
int recv_all(int socket, void *data, int data_len, int flags)
62
0
{
63
0
  int b_read;
64
0
  int n;
65
#ifdef NO_MSG_WAITALL
66
  struct pollfd pfd;
67
#endif /* NO_MSG_WAITALL */
68
69
0
  b_read = 0;
70
0
again:
71
#ifdef NO_MSG_WAITALL
72
  if(flags & MSG_WAITALL) {
73
    n = -1;
74
    goto poll_recv; /* simulate MSG_WAITALL */
75
  }
76
#endif /* NO_MSG_WAITALL */
77
0
  n = recv(socket, (char *)data, data_len, flags);
78
0
  if(n < 0) {
79
    /* error */
80
0
    if(errno == EINTR)
81
0
      goto again; /* signal, try again */
82
    /* on EAGAIN just return (let the caller know) */
83
0
    if((errno == EAGAIN) || (errno == EWOULDBLOCK))
84
0
      return n;
85
0
    LM_CRIT("1st recv on %d failed: %s\n", socket, strerror(errno));
86
0
    return n;
87
0
  }
88
0
  b_read += n;
89
0
  while((b_read != data_len) && (n)) {
90
#ifdef NO_MSG_WAITALL
91
    /* cygwin & win do not support MSG_WAITALL => workaround using poll */
92
  poll_recv:
93
    n = recv(socket, (char *)data + b_read, data_len - b_read, 0);
94
#else  /* NO_MSG_WAITALL */
95
0
    n = recv(socket, (char *)data + b_read, data_len - b_read, MSG_WAITALL);
96
0
#endif /* NO_MSG_WAITALL */
97
0
    if(n < 0) {
98
      /* error */
99
0
      if(errno == EINTR)
100
0
        continue; /* signal, try again */
101
#ifdef NO_MSG_WAITALL
102
      if(errno == EAGAIN || errno == EWOULDBLOCK) {
103
        /* emulate MSG_WAITALL using poll */
104
        pfd.fd = socket;
105
        pfd.events = POLLIN;
106
      poll_retry:
107
        n = poll(&pfd, 1, -1);
108
        if(n < 0) {
109
          if(errno == EINTR)
110
            goto poll_retry;
111
          LM_CRIT("poll on %d failed: %s\n", socket, strerror(errno));
112
          return n;
113
        } else
114
          continue; /* try recv again */
115
      }
116
#endif /* NO_MSG_WAITALL */
117
0
      LM_CRIT("2nd recv on %d failed: %s\n", socket, strerror(errno));
118
0
      return n;
119
0
    }
120
0
    b_read += n;
121
0
  }
122
0
  return b_read;
123
0
}
124
125
126
/** sends all data (takes care of signals) (assumes blocking fd)
127
 * returns number of bytes sent or < 0 for an error */
128
int send_all(int socket, void *data, int data_len)
129
0
{
130
0
  int n;
131
132
0
again:
133
0
  n = send(socket, data, data_len, 0);
134
0
  if(n < 0) {
135
    /* error */
136
0
    if(errno == EINTR)
137
0
      goto again; /* signal, try again */
138
0
    if((errno != EAGAIN) && (errno != EWOULDBLOCK))
139
0
      LM_CRIT("send on %d failed: %s\n", socket, strerror(errno));
140
0
  }
141
0
  return n;
142
0
}
143
144
145
/** at least 1 byte must be sent! */
146
int send_fd(int unix_socket, void *data, int data_len, int fd)
147
0
{
148
0
  struct msghdr msg;
149
0
  struct iovec iov[1];
150
0
  int ret;
151
0
#ifdef HAVE_MSGHDR_MSG_CONTROL
152
0
  int *pi;
153
0
  struct cmsghdr *cmsg;
154
  /* make sure msg_control will point to properly aligned data */
155
0
  union
156
0
  {
157
0
    struct cmsghdr cm;
158
0
    char control[CMSG_SPACE(sizeof(fd))];
159
0
  } control_un;
160
161
0
  memset(&msg, 0, sizeof(struct msghdr));
162
0
  msg.msg_control = control_un.control;
163
  /* openbsd doesn't like "more space", msg_controllen must not
164
   * include the end padding */
165
0
  msg.msg_controllen = CMSG_LEN(sizeof(fd));
166
167
0
  cmsg = CMSG_FIRSTHDR(&msg);
168
0
  cmsg->cmsg_level = SOL_SOCKET;
169
0
  cmsg->cmsg_type = SCM_RIGHTS;
170
0
  cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
171
0
  pi = (int *)CMSG_DATA(cmsg);
172
0
  *pi = fd;
173
0
  msg.msg_flags = 0;
174
#else
175
  msg.msg_accrights = (caddr_t)&fd;
176
  msg.msg_accrightslen = sizeof(fd);
177
#endif
178
179
0
  msg.msg_name = 0;
180
0
  msg.msg_namelen = 0;
181
182
0
  iov[0].iov_base = data;
183
0
  iov[0].iov_len = data_len;
184
0
  msg.msg_iov = iov;
185
0
  msg.msg_iovlen = 1;
186
187
0
again:
188
0
  ret = sendmsg(unix_socket, &msg, 0);
189
0
  if(ret < 0) {
190
0
    if(errno == EINTR)
191
0
      goto again;
192
0
    if((errno != EAGAIN) && (errno != EWOULDBLOCK))
193
0
      LM_CRIT("sendmsg failed sending %d on %d: %s (%d)\n", fd,
194
0
          unix_socket, strerror(errno), errno);
195
0
  }
196
197
0
  return ret;
198
0
}
199
200
201
/** receives a fd and data_len data
202
 * params: unix_socket
203
 *         data
204
 *         data_len
205
 *         fd         - will be set to the passed fd value or -1 if no fd
206
 *                      was passed
207
 *         flags      - 0, MSG_DONTWAIT, MSG_WAITALL; same as recv_all flags
208
 * returns: bytes read on success, -1 on error (and sets errno) */
209
int receive_fd(int unix_socket, void *data, int data_len, int *fd, int flags)
210
0
{
211
0
  struct msghdr msg;
212
0
  struct iovec iov[1];
213
0
  int new_fd;
214
0
  int ret;
215
0
  int n;
216
#ifdef NO_MSG_WAITALL
217
  struct pollfd pfd;
218
  int f;
219
#endif /*NO_MSG_WAITALL */
220
0
#ifdef HAVE_MSGHDR_MSG_CONTROL
221
0
  int *pi;
222
0
  struct cmsghdr *cmsg;
223
0
  union
224
0
  {
225
0
    struct cmsghdr cm;
226
0
    char control[CMSG_SPACE(sizeof(new_fd))];
227
0
  } control_un;
228
229
0
  memset(&msg, 0, sizeof(struct msghdr));
230
0
  msg.msg_control = control_un.control;
231
0
  msg.msg_controllen = sizeof(control_un.control);
232
#else
233
  msg.msg_accrights = (caddr_t)&new_fd;
234
  msg.msg_accrightslen = sizeof(int);
235
#endif
236
237
0
  msg.msg_name = 0;
238
0
  msg.msg_namelen = 0;
239
240
0
  iov[0].iov_base = data;
241
0
  iov[0].iov_len = data_len;
242
0
  msg.msg_iov = iov;
243
0
  msg.msg_iovlen = 1;
244
245
#ifdef NO_MSG_WAITALL
246
  f = flags & ~MSG_WAITALL;
247
#endif /* NO_MSG_WAITALL */
248
249
0
again:
250
#ifdef NO_MSG_WAITALL
251
  ret = recvmsg(unix_socket, &msg, f);
252
#else  /* NO_MSG_WAITALL */
253
0
  ret = recvmsg(unix_socket, &msg, flags);
254
0
#endif /* NO_MSG_WAITALL */
255
0
  if(ret < 0) {
256
0
    if(errno == EINTR)
257
0
      goto again;
258
0
    if((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
259
#ifdef NO_MSG_WAITALL
260
      if(flags & MSG_WAITALL) {
261
        /* emulate MSG_WAITALL using poll */
262
        pfd.fd = unix_socket;
263
        pfd.events = POLLIN;
264
      poll_again:
265
        ret = poll(&pfd, 1, -1);
266
        if(ret >= 0)
267
          goto again;
268
        else if(errno == EINTR)
269
          goto poll_again;
270
        LM_CRIT("poll on %d failed: %s\n", unix_socket,
271
            strerror(errno));
272
      }
273
#endif /* NO_MSG_WAITALL */
274
0
      goto error;
275
0
    }
276
0
    LM_CRIT("recvmsg on %d failed: %s\n", unix_socket, strerror(errno));
277
0
    goto error;
278
0
  }
279
0
  if(ret == 0) {
280
    /* EOF */
281
0
    LM_CRIT("EOF on %d\n", unix_socket);
282
0
    goto error;
283
0
  }
284
0
  if(ret < data_len) {
285
0
    LM_WARN("too few bytes read (%d from %d) trying to fix...\n", ret,
286
0
        data_len);
287
    /* blocking recv_all */
288
0
    n = recv_all(
289
0
        unix_socket, (char *)data + ret, data_len - ret, MSG_WAITALL);
290
0
    if(n >= 0)
291
0
      ret += n;
292
0
    else {
293
0
      ret = n;
294
0
      goto error;
295
0
    }
296
0
  }
297
298
0
#ifdef HAVE_MSGHDR_MSG_CONTROL
299
0
  cmsg = CMSG_FIRSTHDR(&msg);
300
0
  if((cmsg != 0) && (cmsg->cmsg_len == CMSG_LEN(sizeof(new_fd)))) {
301
0
    if(cmsg->cmsg_type != SCM_RIGHTS) {
302
0
      LM_ERR("msg control type != SCM_RIGHTS\n");
303
0
      ret = -1;
304
0
      goto error;
305
0
    }
306
0
    if(cmsg->cmsg_level != SOL_SOCKET) {
307
0
      LM_ERR("msg level != SOL_SOCKET\n");
308
0
      ret = -1;
309
0
      goto error;
310
0
    }
311
0
    pi = (int *)CMSG_DATA(cmsg);
312
0
    *fd = *pi;
313
0
  } else {
314
    /*LM_ERR("no descriptor passed, cmsg=%p, len=%d\n",
315
      cmsg, (unsigned)cmsg->cmsg_len); */
316
0
    *fd = -1;
317
    /* it's not really an error */
318
0
  }
319
#else
320
  if(msg.msg_accrightslen == sizeof(int)) {
321
    *fd = new_fd;
322
  } else {
323
    /*LM_ERR("no descriptor passed, accrightslen=%d\n",
324
      msg.msg_accrightslen); */
325
    *fd = -1;
326
  }
327
#endif
328
329
0
error:
330
0
  return ret;
331
0
}
332
#endif