OSDN Git Service

Add MS7619SE
[uclinux-h8/uClinux-dist.git] / user / ser / pass_fd.c
1 /*
2  * $Id: pass_fd.c,v 1.7.2.1 2005/08/31 13:18:29 andrei Exp $
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser 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  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27  /*
28   * History:
29   * --------
30   *  2002-11-29  created by andrei
31   *  2003-02-20  added solaris support (! HAVE_MSGHDR_MSG_CONTROL) (andrei)
32   *  2003-11-03  added send_all, recv_all  and updated send/get_fd
33   *               to handle signals  (andrei)
34   */
35
36 #ifdef USE_TCP
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/uio.h>
41 #include <stdlib.h> /* for NULL definition on openbsd */
42 #include <errno.h>
43 #include <string.h>
44
45 #include "dprint.h"
46
47
48
49 /* receive all the data or returns error (handles EINTR etc.)
50  * returns: bytes read or error (<0)
51  * can return < data_len if EOF */
52 int recv_all(int socket, void* data, int data_len)
53 {
54         int b_read;
55         int n;
56         
57         b_read=0;
58         do{
59                 n=recv(socket, (char*)data+b_read, data_len-b_read, MSG_WAITALL);
60                 if (n<0){
61                         /* error */
62                         if (errno==EINTR) continue; /* signal, try again */
63                         LOG(L_CRIT, "ERROR: recv_all: recv on %d failed: %s\n",
64                                         socket, strerror(errno));
65                         return n;
66                 }
67                 b_read+=n;
68         }while( (b_read!=data_len) && (n));
69         return b_read;
70 }
71
72
73 /* sends all data (takes care of signals) (assumes blocking fd)
74  * returns number of bytes sent or < 0 for an error */
75 int send_all(int socket, void* data, int data_len)
76 {
77         int n;
78         
79 again:
80         n=send(socket, data, data_len, 0);
81         if (n<0){
82                         /* error */
83                 if (errno==EINTR) goto again; /* signal, try again */
84                 LOG(L_CRIT, "ERROR: send_all: send on %d failed: %s\n",
85                                         socket, strerror(errno));
86         }
87         return n;
88 }
89
90
91 /* at least 1 byte must be sent! */
92 int send_fd(int unix_socket, void* data, int data_len, int fd)
93 {
94         struct msghdr msg;
95         struct iovec iov[1];
96         int ret;
97 #ifdef HAVE_MSGHDR_MSG_CONTROL
98         struct cmsghdr* cmsg;
99         /* make sure msg_control will point to properly aligned data */
100         union {
101                 struct cmsghdr cm;
102                 char control[CMSG_SPACE(sizeof(fd))];
103         }control_un;
104         
105         msg.msg_control=control_un.control;
106         /* openbsd doesn't like "more space", msg_controllen must not
107          * include the end padding */
108         msg.msg_controllen=CMSG_LEN(sizeof(fd));
109         
110         cmsg=CMSG_FIRSTHDR(&msg);
111         cmsg->cmsg_level = SOL_SOCKET;
112         cmsg->cmsg_type = SCM_RIGHTS;
113         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
114         *(int*)CMSG_DATA(cmsg)=fd;
115         msg.msg_flags=0;
116 #else
117         msg.msg_accrights=(caddr_t) &fd;
118         msg.msg_accrightslen=sizeof(fd);
119 #endif
120         
121         msg.msg_name=0;
122         msg.msg_namelen=0;
123         
124         iov[0].iov_base=data;
125         iov[0].iov_len=data_len;
126         msg.msg_iov=iov;
127         msg.msg_iovlen=1;
128         
129 again:
130         ret=sendmsg(unix_socket, &msg, 0);
131         if (ret<0){
132                 if (errno==EINTR) goto again;
133                 LOG(L_CRIT, "ERROR: send_fd: sendmsg failed on %d: %s\n",
134                                 unix_socket, strerror(errno));
135         }
136         
137         return ret;
138 }
139
140
141
142 int receive_fd(int unix_socket, void* data, int data_len, int* fd)
143 {
144         struct msghdr msg;
145         struct iovec iov[1];
146         int new_fd;
147         int ret;
148         int n;
149 #ifdef HAVE_MSGHDR_MSG_CONTROL
150         struct cmsghdr* cmsg;
151         union{
152                 struct cmsghdr cm;
153                 char control[CMSG_SPACE(sizeof(new_fd))];
154         }control_un;
155         
156         msg.msg_control=control_un.control;
157         msg.msg_controllen=sizeof(control_un.control);
158 #else
159         msg.msg_accrights=(caddr_t) &new_fd;
160         msg.msg_accrightslen=sizeof(int);
161 #endif
162         
163         msg.msg_name=0;
164         msg.msg_namelen=0;
165         
166         iov[0].iov_base=data;
167         iov[0].iov_len=data_len;
168         msg.msg_iov=iov;
169         msg.msg_iovlen=1;
170         
171 again:
172         ret=recvmsg(unix_socket, &msg, MSG_WAITALL);
173         if (ret<0){
174                 if (errno==EINTR) goto again;
175                 LOG(L_CRIT, "ERROR: receive_fd: recvmsg on %d failed: %s\n",
176                                 unix_socket, strerror(errno));
177                 goto error;
178         }
179         if (ret==0){
180                 /* EOF */
181                 LOG(L_CRIT, "ERROR: receive_fd: EOF on %d\n", unix_socket);
182                 goto error;
183         }
184         if (ret<data_len){
185                 LOG(L_WARN, "WARNING: receive_fd: too few bytes read (%d from %d)"
186                                     "trying to fix...\n", ret, data_len);
187                 n=recv_all(unix_socket, (char*)data+ret, data_len-ret);
188                 if (n>=0) ret+=n;
189                 else{
190                         ret=n;
191                         goto error;
192                 }
193         }
194         
195 #ifdef HAVE_MSGHDR_MSG_CONTROL
196         cmsg=CMSG_FIRSTHDR(&msg);
197         if ((cmsg!=0) && (cmsg->cmsg_len==CMSG_LEN(sizeof(new_fd)))){
198                 if (cmsg->cmsg_type!= SCM_RIGHTS){
199                         LOG(L_ERR, "ERROR: receive_fd: msg control type != SCM_RIGHTS\n");
200                         ret=-1;
201                         goto error;
202                 }
203                 if (cmsg->cmsg_level!= SOL_SOCKET){
204                         LOG(L_ERR, "ERROR: receive_fd: msg level != SOL_SOCKET\n");
205                         ret=-1;
206                         goto error;
207                 }
208                 *fd=*((int*) CMSG_DATA(cmsg));
209         }else{
210                 LOG(L_ERR, "ERROR: receive_fd: no descriptor passed, cmsg=%p,"
211                                 "len=%d\n", cmsg, (unsigned)cmsg->cmsg_len);
212                 *fd=-1;
213                 /* it's not really an error */
214         }
215 #else
216         if (msg.msg_accrightslen==sizeof(int)){
217                 *fd=new_fd;
218         }else{
219                 LOG(L_ERR, "ERROR: receive_fd: no descriptor passed,"
220                                 " accrightslen=%d\n", msg.msg_accrightslen);
221                 *fd=-1;
222         }
223 #endif
224         
225 error:
226         return ret;
227 }
228 #endif