5decde2cea14804875a2f710367a31f3540967b0
[bbaumbach/samba-autobuild/.git] / source4 / lib / socket_wrapper.c
1 /* 
2    Socket wrapper library. Passes all socket communication over 
3    unix domain sockets if the environment variable SOCKET_WRAPPER_DIR 
4    is set.
5    Copyright (C) Jelmer Vernooij 2005
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #ifdef SAMBA_MAJOR_VERSION
23 #include "includes.h"
24 #include "system/network.h"
25 #else
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <errno.h>
29 #include <sys/un.h>
30 #include <netinet/in.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include "dlinklist.h"
36 #endif
37
38 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
39  * for now */
40 #define REWRITE_CALLS 
41
42 #ifdef REWRITE_CALLS
43 #define real_accept accept
44 #define real_connect connect
45 #define real_bind bind
46 #define real_getpeername getpeername
47 #define real_getsockname getsockname
48 #define real_getsockopt getsockopt
49 #define real_setsockopt setsockopt
50 #define real_recvfrom recvfrom
51 #define real_sendto sendto
52 #define real_socket socket
53 #define real_close close
54 #endif
55
56 static struct sockaddr *memdup(const void *data, socklen_t len)
57 {
58         struct sockaddr *ret = (struct sockaddr *)malloc(len);
59         memcpy(ret, data, len);
60         return ret;
61 }
62
63 struct socket_info
64 {
65         int fd;
66
67         int domain;
68         int type;
69         int protocol;
70
71         char *path;
72
73         struct sockaddr *myname;
74         socklen_t myname_len;
75
76         struct sockaddr *peername;
77         socklen_t peername_len;
78
79         struct socket_info *prev, *next;
80 } *sockets = NULL;
81
82 static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
83 {
84         unsigned int prt;
85         const char *p;
86
87         if ((*len) < sizeof(struct sockaddr_in)) {
88                 return 0;
89         }
90
91         in->sin_family = AF_INET;
92         in->sin_port = 1025; /* Default to 1025 */
93         p = strchr(un->sun_path, '/');
94         if (p) p++; else p = un->sun_path;
95
96         if(sscanf(p, "sock_ip_%u", &prt) == 1) 
97         {
98                 in->sin_port = htons(prt);
99         }
100         in->sin_addr.s_addr = INADDR_LOOPBACK;
101         *len = sizeof(struct sockaddr_in);
102         return 0;
103 }
104
105 static int convert_in_un(const struct sockaddr_in *in, struct sockaddr_un *un)
106 {
107         uint16_t prt = ntohs(in->sin_port);
108         /* FIXME: ENETUNREACH if in->sin_addr is not loopback */
109         un->sun_family = AF_LOCAL;
110         snprintf(un->sun_path, sizeof(un->sun_path), "%s/sock_ip_%u", getenv("SOCKET_WRAPPER_DIR"), prt);
111         return 0;
112 }
113
114 static struct socket_info *find_socket_info(int fd)
115 {
116         struct socket_info *i;
117         for (i = sockets; i; i = i->next) {
118                 if (i->fd == fd) 
119                         return i;
120         }
121
122         return NULL;
123 }
124
125 static int sockaddr_convert_to_un(const struct sockaddr *in_addr, socklen_t in_len, 
126                                          struct sockaddr_un *out_addr)
127 {
128         if (!out_addr)
129                 return 0;
130
131         switch (in_addr->sa_family) {
132         case AF_INET:
133                 return convert_in_un((const struct sockaddr_in *)in_addr, out_addr);
134         case AF_LOCAL:
135                 memcpy(out_addr, in_addr, sizeof(*out_addr));
136                 return 0;
137         default:
138                 break;
139         }
140         
141         errno = EAFNOSUPPORT;
142         return -1;
143 }
144
145 static int sockaddr_convert_from_un(const struct sockaddr_un *in_addr, 
146                                                                         int family,
147                                                  struct sockaddr *out_addr,
148                                                  socklen_t *out_len)
149 {
150         if (!out_addr) 
151                 return 0;
152
153         switch (family) {
154         case AF_INET:
155                 return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, out_len);
156         case AF_LOCAL:
157                 memcpy(out_addr, in_addr, sizeof(*in_addr));
158                 *out_len = sizeof(*in_addr);
159                 return 0;
160         default:
161                 break;
162         }
163
164         errno = EAFNOSUPPORT;
165         return -1;
166 }
167
168 int swrap_socket(int domain, int type, int protocol)
169 {
170         struct socket_info *si;
171         int fd;
172
173         if (!getenv("SOCKET_WRAPPER_DIR")) {
174                 return real_socket(domain, type, protocol);
175         }
176         
177         fd = real_socket(PF_LOCAL, type, 0);
178
179         if (fd < 0) 
180                 return fd;
181
182         si = malloc(sizeof(struct socket_info));
183         memset(si, 0, sizeof(*si));
184
185         si->domain = domain;
186         si->type = type;
187         si->protocol = protocol;
188         si->fd = fd;
189
190         DLIST_ADD(sockets, si);
191
192         return si->fd;
193 }
194
195 int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
196 {
197         struct socket_info *parent_si, *child_si;
198         int fd;
199         socklen_t un_addrlen = sizeof(struct sockaddr_un);
200         struct sockaddr_un un_addr;
201         int ret;
202
203         parent_si = find_socket_info(s);
204         if (!parent_si) {
205                 return real_accept(s, addr, addrlen);
206         }
207
208         ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
209         if (ret < 0) return ret;
210
211         fd = ret;
212
213         ret = sockaddr_convert_from_un(&un_addr, parent_si->domain, addr, addrlen);
214
215         if (ret < 0) return ret;
216
217         child_si = malloc(sizeof(struct socket_info));
218         memset(child_si, 0, sizeof(*child_si));
219
220         child_si->fd = fd;
221
222         if (addr && addrlen) {
223                 child_si->myname_len = *addrlen;
224                 child_si->myname = memdup(addr, *addrlen);
225         }
226
227         return fd;
228 }
229
230 int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
231 {
232         int ret;
233         struct sockaddr_un un_addr;
234         struct socket_info *si = find_socket_info(s);
235
236         if (!si) {
237                 return real_connect(s, serv_addr, addrlen);
238         }
239
240         ret = sockaddr_convert_to_un((const struct sockaddr *)serv_addr, addrlen, &un_addr);
241         if (ret < 0) return ret;
242
243         ret = real_connect(s, 
244                                            (struct sockaddr *)&un_addr, 
245                                            sizeof(struct sockaddr_un));
246
247         if (ret >= 0) {
248                 si->peername_len = addrlen;
249                 si->peername = memdup(serv_addr, addrlen);
250         }
251
252         return ret;
253 }
254
255 int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
256 {
257         int ret;
258         struct sockaddr_un un_addr;
259         struct socket_info *si = find_socket_info(s);
260
261         if (!si) {
262                 return real_bind(s, myaddr, addrlen);
263         }
264
265         ret = sockaddr_convert_to_un((const struct sockaddr *)myaddr, addrlen, &un_addr);
266         if (ret < 0) return ret;
267
268         unlink(un_addr.sun_path);
269
270         ret = real_bind(s, 
271                                         (struct sockaddr *)&un_addr,
272                                         sizeof(struct sockaddr_un));
273
274         if (ret >= 0) {
275                 si->myname_len = addrlen;
276                 si->myname = memdup(myaddr, addrlen);
277         }
278
279         return ret;
280 }
281
282 int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
283 {
284         struct socket_info *si = find_socket_info(s);
285
286         if (!si) {
287                 return real_getpeername(s, name, addrlen);
288         }
289
290         if (!si->peername) 
291         {
292                 errno = ENOTCONN;
293                 return -1;
294         }
295
296         memcpy(name, si->peername, si->peername_len);
297         *addrlen = si->peername_len;
298
299         return 0;
300 }
301
302 int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
303 {
304         struct socket_info *si = find_socket_info(s);
305
306         if (!si) {
307                 return real_getpeername(s, name, addrlen);
308         }
309
310         memcpy(name, si->myname, si->myname_len);
311         *addrlen = si->myname_len;
312
313         return 0;
314 }
315
316 int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
317 {
318         struct socket_info *si = find_socket_info(s);
319
320         if (!si) {
321                 return real_getsockopt(s, level, optname, optval, optlen);
322         }
323
324         if (level != SOL_SOCKET) {
325                 errno = ENOPROTOOPT;
326                 return -1;
327         }
328
329         return real_getsockopt(s, level, optname, optval, optlen);
330 }
331
332 int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *optval, socklen_t optlen)
333 {
334         struct socket_info *si = find_socket_info(s);
335
336         if (!si) {
337                 return real_setsockopt(s, level, optname, optval, optlen);
338         }
339
340         if (level != SOL_SOCKET) {
341                 errno = ENOPROTOOPT;
342                 return -1;
343         }
344
345         return real_setsockopt(s, level, optname, optval, optlen);
346 }
347
348 ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
349 {
350         socklen_t un_addrlen;
351         struct sockaddr_un un_addr;
352         int ret;
353         struct socket_info *si = find_socket_info(s);
354
355         if (!si) {
356                 return real_recvfrom(s, buf, len, flags, from, fromlen);
357         }
358
359         ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
360         if (ret < 0) 
361                 return ret;
362
363         ret = sockaddr_convert_from_un(&un_addr, si->domain, from, fromlen);
364         
365         return ret;
366 }
367
368 ssize_t swrap_sendto(int  s,  const  void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
369 {
370         struct sockaddr_un un_addr;
371         int ret;
372         struct socket_info *si = find_socket_info(s);
373
374         if (!si) {
375                 return real_sendto(s, buf, len, flags, to, tolen);
376         }
377
378         ret = sockaddr_convert_to_un(to, tolen, &un_addr);
379         if (ret < 0) 
380                 return ret;
381
382         ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
383         
384         return ret;
385 }
386
387 int swrap_close(int fd)
388 {
389         struct socket_info *si = find_socket_info(fd);
390
391         if (si) {
392                 DLIST_REMOVE(sockets, si);
393
394                 free(si->path);
395                 free(si->myname);
396                 free(si->peername);
397                 free(si);
398         }
399
400         return real_close(fd);
401 }