Coverage Report

Created: 2025-07-12 06:14

/src/opensips/net/tcp_passfd.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips 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
 * opensips 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
 * History:
21
 * --------
22
 *  2002-11-29  created by andrei
23
 *  2003-02-20  added solaris support (! HAVE_MSGHDR_MSG_CONTROL) (andrei)
24
 *  2003-11-03  added send_all, recv_all  and updated send/get_fd
25
 *               to handle signals  (andrei)
26
 */
27
28
#include <sys/types.h>
29
#include <sys/socket.h>
30
#include <sys/uio.h>
31
#include <stdlib.h> /* for NULL definition on openbsd */
32
#include <errno.h>
33
#include <string.h>
34
35
#include "../dprint.h"
36
37
38
39
/* receive all the data or returns error (handles EINTR etc.)
40
 * params: socket
41
 *         data     - buffer for the results
42
 *         data_len -
43
 *         flags    - recv flags for the first recv (see recv(2)), only
44
 *                    0, MSG_WAITALL and MSG_DONTWAIT make sense
45
 * if flags is set to MSG_DONWAIT (or to 0 and the socket fd is non-blocking),
46
 * and if no data is queued on the fd, recv_all will not wait (it will
47
 * return error and set errno to EAGAIN/EWOULDBLOCK). However if even 1 byte
48
 *  is queued, the call will block until the whole data_len was read or an
49
 *  error or eof occurred ("semi-nonblocking" behaviour,  some tcp code
50
 *   counts on it).
51
 * if flags is set to MSG_WAITALL it will block even if no byte is available.
52
 *
53
 * returns: bytes read or error (<0)
54
 * can return < data_len if EOF */
55
int recv_all(int socket, void* data, int data_len, int flags)
56
0
{
57
0
  int b_read;
58
0
  int n;
59
60
0
  b_read=0;
61
0
again:
62
0
  n=recv(socket, (char*)data, data_len, flags);
63
0
  if (n<0){
64
    /* error */
65
0
    if (errno==EINTR) goto again; /* signal, try again */
66
    /* on EAGAIN just return (let the caller know) */
67
0
    if ((errno==EAGAIN)||(errno==EWOULDBLOCK)) return n;
68
0
      LM_CRIT("1st recv on %d failed: %s\n", socket, strerror(errno));
69
0
      return n;
70
0
  }
71
0
  b_read+=n;
72
0
  while( (b_read!=data_len) && (n)){
73
0
    n=recv(socket, (char*)data+b_read, data_len-b_read, MSG_WAITALL);
74
0
    if (n<0){
75
      /* error */
76
0
      if (errno==EINTR) continue; /* signal, try again */
77
0
      LM_CRIT("2nd recv on %d failed: %s\n",
78
0
          socket, strerror(errno));
79
0
      return n;
80
0
    }
81
0
    b_read+=n;
82
0
  }
83
0
  return b_read;
84
0
}
85
86
87
88
/* sends all data (takes care of signals) (assumes blocking fd)
89
 * returns number of bytes sent or < 0 for an error */
90
int send_all(int socket, void* data, int data_len)
91
0
{
92
0
  int n;
93
94
0
again:
95
0
  n=send(socket, data, data_len, 0);
96
0
  if (n<0){
97
      /* error */
98
0
    if (errno==EINTR) goto again; /* signal, try again */
99
0
    LM_CRIT("send on %d failed: %s\n", socket, strerror(errno));
100
0
  }
101
0
  return n;
102
0
}
103
104
/* gcc does the right thing */
105
static inline int get_unaligned_int(const void *p)
106
0
{
107
0
  const struct { int d; } __attribute__((packed)) *pp = p;
108
0
  return pp->d;
109
0
}
110
111
/* gcc does the right thing */
112
static inline void put_unaligned_int(void *p, int value)
113
0
{
114
0
  struct { int d; } __attribute__((packed, may_alias)) *pp = p;
115
0
  pp->d = value;
116
0
}
117
118
/* at least 1 byte must be sent! */
119
int send_fd(int unix_socket, void* data, int data_len, int fd)
120
0
{
121
0
  struct msghdr msg;
122
0
  struct iovec iov[1];
123
0
  int ret;
124
0
#ifdef HAVE_MSGHDR_MSG_CONTROL
125
0
  struct cmsghdr* cmsg;
126
  /* make sure msg_control will point to properly aligned data */
127
0
  union {
128
0
    struct cmsghdr cm;
129
0
    char control[CMSG_SPACE(sizeof(fd))];
130
0
  }control_un;
131
132
0
  msg.msg_control=control_un.control;
133
  /* openbsd doesn't like "more space", msg_controllen must not
134
   * include the end padding */
135
0
  msg.msg_controllen=CMSG_LEN(sizeof(fd));
136
137
0
  cmsg=CMSG_FIRSTHDR(&msg);
138
0
  cmsg->cmsg_level = SOL_SOCKET;
139
0
  cmsg->cmsg_type = SCM_RIGHTS;
140
0
  cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
141
0
  put_unaligned_int(CMSG_DATA(cmsg), fd);
142
0
  msg.msg_flags=0;
143
#else
144
  msg.msg_accrights=(caddr_t) &fd;
145
  msg.msg_accrightslen=sizeof(fd);
146
#endif
147
148
0
  msg.msg_name=0;
149
0
  msg.msg_namelen=0;
150
151
0
  iov[0].iov_base=data;
152
0
  iov[0].iov_len=data_len;
153
0
  msg.msg_iov=iov;
154
0
  msg.msg_iovlen=1;
155
156
0
again:
157
0
  ret=sendmsg(unix_socket, &msg, 0);
158
0
  if (ret<0){
159
0
    if (errno==EINTR) goto again;
160
0
    if (errno==EAGAIN || errno==EWOULDBLOCK) {
161
0
      LM_ERR("sendmsg would block on %d: %s\n",
162
0
        unix_socket, strerror(errno));
163
0
    } else {
164
0
      LM_CRIT("sendmsg failed on %d: %s\n",
165
0
        unix_socket, strerror(errno));
166
0
    }
167
0
  }
168
169
0
  return ret;
170
0
}
171
172
173
174
/* receives a fd and data_len data
175
 * params: unix_socket
176
 *         data
177
 *         data_len
178
 *         fd         - will be set to the passed fd value or -1 if no fd
179
 *                      was passed
180
 *         flags      - 0, MSG_DONTWAIT, MSG_WAITALL; same as recv_all flags
181
 * returns: bytes read on success, -1 on error (and sets errno) */
182
int receive_fd(int unix_socket, void* data, int data_len, int* fd, int flags)
183
0
{
184
0
  struct msghdr msg;
185
0
  struct iovec iov[1];
186
0
  int new_fd;
187
0
  int ret;
188
0
  int n;
189
0
#ifdef HAVE_MSGHDR_MSG_CONTROL
190
0
  struct cmsghdr* cmsg;
191
0
  union{
192
0
    struct cmsghdr cm;
193
0
    char control[CMSG_SPACE(sizeof(new_fd))];
194
0
  }control_un;
195
196
0
  msg.msg_control=control_un.control;
197
0
  msg.msg_controllen=sizeof(control_un.control);
198
#else
199
  msg.msg_accrights=(caddr_t) &new_fd;
200
  msg.msg_accrightslen=sizeof(int);
201
#endif
202
203
0
  msg.msg_name=0;
204
0
  msg.msg_namelen=0;
205
206
0
  iov[0].iov_base=data;
207
0
  iov[0].iov_len=data_len;
208
0
  msg.msg_iov=iov;
209
0
  msg.msg_iovlen=1;
210
0
  msg.msg_flags = 0;
211
212
0
again:
213
0
  ret=recvmsg(unix_socket, &msg, flags);
214
0
  if (ret<0){
215
0
    if (errno==EINTR) goto again;
216
0
    if ((errno==EAGAIN)||(errno==EWOULDBLOCK)) goto error;
217
0
    LM_CRIT("recvmsg on %d failed: %s\n", unix_socket, strerror(errno));
218
0
    goto error;
219
0
  }
220
0
  if (ret==0){
221
    /* EOF */
222
0
    LM_DBG("EOF on %d\n", unix_socket);
223
0
    goto error;
224
0
  }
225
0
  if (ret<data_len){
226
0
    LM_WARN("too few bytes read (%d from %d) trying to fix...\n",
227
0
        ret, data_len);
228
    /* blocking recv_all */
229
0
    n=recv_all(unix_socket, (char*)data+ret, data_len-ret, MSG_WAITALL);
230
0
    if (n>=0) ret+=n;
231
0
    else{
232
0
      ret=n;
233
0
      goto error;
234
0
    }
235
0
  }
236
237
0
#ifdef HAVE_MSGHDR_MSG_CONTROL
238
0
  cmsg=CMSG_FIRSTHDR(&msg);
239
0
  if ((cmsg!=0) && (cmsg->cmsg_len==CMSG_LEN(sizeof(new_fd)))){
240
0
    if (cmsg->cmsg_type!= SCM_RIGHTS){
241
0
      LM_ERR("msg control type != SCM_RIGHTS\n");
242
0
      ret=-1;
243
0
      goto error;
244
0
    }
245
0
    if (cmsg->cmsg_level!= SOL_SOCKET){
246
0
      LM_ERR("msg level != SOL_SOCKET\n");
247
0
      ret=-1;
248
0
      goto error;
249
0
    }
250
0
    *fd = get_unaligned_int(CMSG_DATA(cmsg));
251
0
  }else{
252
    /*
253
    LM_ERR("no descriptor passed, cmsg=%p,len=%d\n",
254
      cmsg, (unsigned)cmsg->cmsg_len); */
255
0
    *fd=-1;
256
    /* it's not really an error */
257
0
  }
258
#else
259
  if (msg.msg_accrightslen==sizeof(int)){
260
    *fd=new_fd;
261
  }else{
262
    /*LM_ERR("no descriptor passed, accrightslen=%d\n",
263
      msg.msg_accrightslen); */
264
    *fd=-1;
265
  }
266
#endif
267
268
0
error:
269
0
  return ret;
270
0
}