5b532c7f |
/*
* $Id$
*
|
53c7e0f1 |
* Copyright (C) 2001-2003 FhG Fokus
|
5b532c7f |
*
* This file is part of ser, a free SIP server.
*
* ser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
|
6c6659cb |
/*
* History:
* --------
* 2002-11-29 created by andrei
* 2003-02-20 added solaris support (! HAVE_MSGHDR_MSG_CONTROL) (andrei)
|
06aaa54f |
* 2003-11-03 added send_all, recv_all and updated send/get_fd
* to handle signals (andrei)
|
0ba367ec |
* 2005-06-13 added flags to recv_all & receive_fd, to allow full blocking
* or semi-nonblocking mode (andrei)
|
3d4bb4b1 |
* 2008-04-30 added MSG_WAITALL emulation for cygwin (andrei)
|
6c6659cb |
*/
|
5b532c7f |
#ifdef USE_TCP
|
3d4bb4b1 |
#include "pass_fd.h"
|
5b532c7f |
#include <sys/types.h>
#include <sys/socket.h>
|
b988daef |
#include <sys/uio.h>
#include <stdlib.h> /* for NULL definition on openbsd */
|
06aaa54f |
#include <errno.h>
#include <string.h>
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
#include <poll.h>
#endif /* NO_MSG_WAITALL */
|
5b532c7f |
|
b988daef |
#include "dprint.h"
|
5b532c7f |
|
06aaa54f |
/* receive all the data or returns error (handles EINTR etc.)
|
0ba367ec |
* params: socket
* data - buffer for the results
* data_len -
* flags - recv flags for the first recv (see recv(2)), only
* 0, MSG_WAITALL and MSG_DONTWAIT make sense
* if flags is set to MSG_DONWAIT (or to 0 and the socket fd is non-blocking),
* and if no data is queued on the fd, recv_all will not wait (it will
* return error and set errno to EAGAIN/EWOULDBLOCK). However if even 1 byte
* is queued, the call will block until the whole data_len was read or an
* error or eof occured ("semi-nonblocking" behaviour, some tcp code
* counts on it).
* if flags is set to MSG_WAITALL it will block even if no byte is available.
*
|
06aaa54f |
* returns: bytes read or error (<0)
* can return < data_len if EOF */
|
0ba367ec |
int recv_all(int socket, void* data, int data_len, int flags)
|
06aaa54f |
{
int b_read;
int n;
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
struct pollfd pfd;
#endif /* NO_MSG_WAITALL */
|
06aaa54f |
b_read=0;
|
0ba367ec |
again:
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
if (flags & MSG_WAITALL){
n=-1;
goto poll_recv; /* simulate MSG_WAITALL */
}
#endif /* NO_MSG_WAITALL */
|
0ba367ec |
n=recv(socket, (char*)data, data_len, flags);
if (n<0){
/* error */
if (errno==EINTR) goto again; /* signal, try again */
/* on EAGAIN just return (let the caller know) */
if ((errno==EAGAIN)||(errno==EWOULDBLOCK)) return n;
LOG(L_CRIT, "ERROR: recv_all: 1st recv on %d failed: %s\n",
socket, strerror(errno));
return n;
}
b_read+=n;
while( (b_read!=data_len) && (n)){
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
/* cygwin & win do not support MSG_WAITALL => workaround using poll */
poll_recv:
n=recv(socket, (char*)data+b_read, data_len-b_read, 0);
#else /* NO_MSG_WAITALL */
|
b23a7a00 |
n=recv(socket, (char*)data+b_read, data_len-b_read, MSG_WAITALL);
|
3d4bb4b1 |
#endif /* NO_MSG_WAITALL */
|
06aaa54f |
if (n<0){
/* error */
if (errno==EINTR) continue; /* signal, try again */
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
if (errno==EAGAIN || errno==EWOULDBLOCK){
/* emulate MSG_WAITALL using poll */
pfd.fd=socket;
pfd.events=POLLIN;
poll_retry:
n=poll(&pfd, 1, -1);
if (n<0){
if (errno==EINTR) goto poll_retry;
LOG(L_CRIT, "ERROR: recv_all: poll on %d failed: %s\n",
socket, strerror(errno));
return n;
} else continue; /* try recv again */
}
#endif /* NO_MSG_WAITALL */
|
0ba367ec |
LOG(L_CRIT, "ERROR: recv_all: 2nd recv on %d failed: %s\n",
|
06aaa54f |
socket, strerror(errno));
return n;
}
b_read+=n;
|
0ba367ec |
}
|
06aaa54f |
return b_read;
}
|
0ba367ec |
|
06aaa54f |
/* sends all data (takes care of signals) (assumes blocking fd)
* returns number of bytes sent or < 0 for an error */
int send_all(int socket, void* data, int data_len)
{
int n;
again:
n=send(socket, data, data_len, 0);
if (n<0){
/* error */
if (errno==EINTR) goto again; /* signal, try again */
|
28260509 |
if ((errno!=EAGAIN) &&(errno!=EWOULDBLOCK))
LOG(L_CRIT, "ERROR: send_all: send on %d failed: %s\n",
|
06aaa54f |
socket, strerror(errno));
}
return n;
}
|
5b532c7f |
/* at least 1 byte must be sent! */
int send_fd(int unix_socket, void* data, int data_len, int fd)
{
struct msghdr msg;
struct iovec iov[1];
int ret;
|
6c6659cb |
#ifdef HAVE_MSGHDR_MSG_CONTROL
struct cmsghdr* cmsg;
|
1d5ef189 |
/* make sure msg_control will point to properly aligned data */
|
5b532c7f |
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(fd))];
}control_un;
msg.msg_control=control_un.control;
|
1d5ef189 |
/* openbsd doesn't like "more space", msg_controllen must not
* include the end padding */
msg.msg_controllen=CMSG_LEN(sizeof(fd));
|
5b532c7f |
|
6c6659cb |
cmsg=CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
*(int*)CMSG_DATA(cmsg)=fd;
msg.msg_flags=0;
#else
msg.msg_accrights=(caddr_t) &fd;
msg.msg_accrightslen=sizeof(fd);
#endif
|
5b532c7f |
msg.msg_name=0;
msg.msg_namelen=0;
iov[0].iov_base=data;
iov[0].iov_len=data_len;
msg.msg_iov=iov;
msg.msg_iovlen=1;
|
06aaa54f |
again:
|
5b532c7f |
ret=sendmsg(unix_socket, &msg, 0);
|
06aaa54f |
if (ret<0){
if (errno==EINTR) goto again;
|
28260509 |
if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK))
LOG(L_CRIT, "ERROR: send_fd: sendmsg failed on %d: %s\n",
unix_socket, strerror(errno));
|
06aaa54f |
}
|
5b532c7f |
return ret;
}
|
0ba367ec |
/* receives a fd and data_len data
* params: unix_socket
* data
* data_len
* fd - will be set to the passed fd value or -1 if no fd
* was passed
* flags - 0, MSG_DONTWAIT, MSG_WAITALL; same as recv_all flags
* returns: bytes read on success, -1 on error (and sets errno) */
int receive_fd(int unix_socket, void* data, int data_len, int* fd, int flags)
|
5b532c7f |
{
struct msghdr msg;
struct iovec iov[1];
int new_fd;
int ret;
|
06aaa54f |
int n;
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
struct pollfd pfd;
int f;
#endif /*NO_MSG_WAITALL */
|
6c6659cb |
#ifdef HAVE_MSGHDR_MSG_CONTROL
struct cmsghdr* cmsg;
|
5b532c7f |
union{
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(new_fd))];
}control_un;
msg.msg_control=control_un.control;
msg.msg_controllen=sizeof(control_un.control);
|
6c6659cb |
#else
msg.msg_accrights=(caddr_t) &new_fd;
msg.msg_accrightslen=sizeof(int);
#endif
|
5b532c7f |
msg.msg_name=0;
msg.msg_namelen=0;
iov[0].iov_base=data;
iov[0].iov_len=data_len;
msg.msg_iov=iov;
msg.msg_iovlen=1;
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
f=flags & ~MSG_WAITALL;
#endif /* NO_MSG_WAITALL */
|
06aaa54f |
again:
|
3d4bb4b1 |
#ifdef NO_MSG_WAITALL
ret=recvmsg(unix_socket, &msg, f);
#else /* NO_MSG_WAITALL */
ret=recvmsg(unix_socket, &msg, flags);
#endif /* NO_MSG_WAITALL */
|
06aaa54f |
if (ret<0){
if (errno==EINTR) goto again;
|
3d4bb4b1 |
if ((errno==EAGAIN)||(errno==EWOULDBLOCK)){
#ifdef NO_MSG_WAITALL
if (flags & MSG_WAITALL){
/* emulate MSG_WAITALL using poll */
pfd.fd=unix_socket;
pfd.events=POLLIN;
poll_again:
ret=poll(&pfd, 1, -1);
if (ret>=0) goto again;
else if (errno==EINTR) goto poll_again;
LOG(L_CRIT, "ERROR: receive_fd: poll on %d failed: %s\n",
unix_socket, strerror(errno));
}
#endif /* NO_MSG_WAITALL */
goto error;
}
|
06aaa54f |
LOG(L_CRIT, "ERROR: receive_fd: recvmsg on %d failed: %s\n",
unix_socket, strerror(errno));
goto error;
}
if (ret==0){
/* EOF */
LOG(L_CRIT, "ERROR: receive_fd: EOF on %d\n", unix_socket);
goto error;
}
if (ret<data_len){
LOG(L_WARN, "WARNING: receive_fd: too few bytes read (%d from %d)"
"trying to fix...\n", ret, data_len);
|
0ba367ec |
/* blocking recv_all */
n=recv_all(unix_socket, (char*)data+ret, data_len-ret, MSG_WAITALL);
|
06aaa54f |
if (n>=0) ret+=n;
else{
ret=n;
goto error;
}
}
|
5b532c7f |
|
6c6659cb |
#ifdef HAVE_MSGHDR_MSG_CONTROL
|
5b532c7f |
cmsg=CMSG_FIRSTHDR(&msg);
if ((cmsg!=0) && (cmsg->cmsg_len==CMSG_LEN(sizeof(new_fd)))){
if (cmsg->cmsg_type!= SCM_RIGHTS){
|
06aaa54f |
LOG(L_ERR, "ERROR: receive_fd: msg control type != SCM_RIGHTS\n");
|
5b532c7f |
ret=-1;
goto error;
}
if (cmsg->cmsg_level!= SOL_SOCKET){
|
06aaa54f |
LOG(L_ERR, "ERROR: receive_fd: msg level != SOL_SOCKET\n");
|
5b532c7f |
ret=-1;
goto error;
}
*fd=*((int*) CMSG_DATA(cmsg));
}else{
|
0ba367ec |
/*
|
06aaa54f |
LOG(L_ERR, "ERROR: receive_fd: no descriptor passed, cmsg=%p,"
|
0ba367ec |
"len=%d\n", cmsg, (unsigned)cmsg->cmsg_len); */
|
5b532c7f |
*fd=-1;
/* it's not really an error */
}
|
6c6659cb |
#else
if (msg.msg_accrightslen==sizeof(int)){
*fd=new_fd;
}else{
|
0ba367ec |
/*LOG(L_ERR, "ERROR: receive_fd: no descriptor passed,"
" accrightslen=%d\n", msg.msg_accrightslen); */
|
6c6659cb |
*fd=-1;
}
#endif
|
5b532c7f |
error:
return ret;
}
#endif
|