Initial import
[samba] / source / 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 #include "includes.h"
23
24 #ifdef SOCKET_WRAPPER_REPLACE
25 #undef accept
26 #undef connect
27 #undef bind
28 #undef getpeername
29 #undef getsockname
30 #undef getsockopt
31 #undef setsockopt
32 #undef recvfrom
33 #undef sendto
34 #undef socket
35 #undef close
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 #undef malloc
57 #undef calloc
58 #undef strdup
59 /* we need to use a very terse format here as IRIX 6.4 silently
60    truncates names to 16 chars, so if we use a longer name then we
61    can't tell which port a packet came from with recvfrom() 
62    
63    with this format we have 8 chars left for the directory name
64 */
65 #define SOCKET_FORMAT "%u_%u"
66
67 static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
68 {
69         struct sockaddr *ret = (struct sockaddr *)malloc(len);
70         memcpy(ret, data, len);
71         return ret;
72 }
73
74 struct socket_info
75 {
76         int fd;
77
78         int domain;
79         int type;
80         int protocol;
81         int bound;
82
83         char *path;
84         char *tmp_path;
85
86         struct sockaddr *myname;
87         socklen_t myname_len;
88
89         struct sockaddr *peername;
90         socklen_t peername_len;
91
92         struct socket_info *prev, *next;
93 };
94
95 static struct socket_info *sockets = NULL;
96
97
98 static const char *socket_wrapper_dir(void)
99 {
100         const char *s = getenv("SOCKET_WRAPPER_DIR");
101         if (s == NULL) {
102                 return NULL;
103         }
104         if (strncmp(s, "./", 2) == 0) {
105                 s += 2;
106         }
107         return s;
108 }
109
110 static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
111 {
112         unsigned int prt;
113         const char *p;
114         int type;
115
116         if ((*len) < sizeof(struct sockaddr_in)) {
117                 return 0;
118         }
119
120         in->sin_family = AF_INET;
121         in->sin_port = htons(1025); /* Default to 1025 */
122         p = strrchr(un->sun_path, '/');
123         if (p) p++; else p = un->sun_path;
124
125         if (sscanf(p, SOCKET_FORMAT, &type, &prt) == 2) {
126                 in->sin_port = htons(prt);
127         }
128         in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
129         *len = sizeof(struct sockaddr_in);
130         return 0;
131 }
132
133 static int convert_in_un(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un)
134 {
135         int type = si->type;
136         uint16_t prt = ntohs(in->sin_port);
137         if (prt == 0) {
138                 struct stat st;
139                 /* handle auto-allocation of ephemeral ports */
140                 prt = 5000;
141                 do {
142                         snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, 
143                                  socket_wrapper_dir(), type, ++prt);
144                 } while (stat(un->sun_path, &st) == 0 && prt < 10000);
145                 ((struct sockaddr_in *)si->myname)->sin_port = htons(prt);
146         } 
147         snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, 
148                  socket_wrapper_dir(), type, prt);
149         return 0;
150 }
151
152 static struct socket_info *find_socket_info(int fd)
153 {
154         struct socket_info *i;
155         for (i = sockets; i; i = i->next) {
156                 if (i->fd == fd) 
157                         return i;
158         }
159
160         return NULL;
161 }
162
163 static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len, 
164                                          struct sockaddr_un *out_addr)
165 {
166         if (!out_addr)
167                 return 0;
168
169         out_addr->sun_family = AF_UNIX;
170
171         switch (in_addr->sa_family) {
172         case AF_INET:
173                 return convert_in_un(si, (const struct sockaddr_in *)in_addr, out_addr);
174         case AF_UNIX:
175                 memcpy(out_addr, in_addr, sizeof(*out_addr));
176                 return 0;
177         default:
178                 break;
179         }
180         
181         errno = EAFNOSUPPORT;
182         return -1;
183 }
184
185 static int sockaddr_convert_from_un(const struct socket_info *si, 
186                                     const struct sockaddr_un *in_addr, 
187                                     socklen_t un_addrlen,
188                                     int family,
189                                     struct sockaddr *out_addr,
190                                     socklen_t *out_len)
191 {
192         if (out_addr == NULL || out_len == NULL) 
193                 return 0;
194
195         if (un_addrlen == 0) {
196                 *out_len = 0;
197                 return 0;
198         }
199
200         switch (family) {
201         case AF_INET:
202                 return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, out_len);
203         case AF_UNIX:
204                 memcpy(out_addr, in_addr, sizeof(*in_addr));
205                 *out_len = sizeof(*in_addr);
206                 return 0;
207         default:
208                 break;
209         }
210
211         errno = EAFNOSUPPORT;
212         return -1;
213 }
214
215 int swrap_socket(int domain, int type, int protocol)
216 {
217         struct socket_info *si;
218         int fd;
219
220         if (!socket_wrapper_dir()) {
221                 return real_socket(domain, type, protocol);
222         }
223         
224         fd = real_socket(AF_UNIX, type, 0);
225
226         if (fd == -1) return -1;
227
228         si = calloc(1, sizeof(struct socket_info));
229
230         si->domain = domain;
231         si->type = type;
232         si->protocol = protocol;
233         si->fd = fd;
234
235         DLIST_ADD(sockets, si);
236
237         return si->fd;
238 }
239
240 int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
241 {
242         struct socket_info *parent_si, *child_si;
243         int fd;
244         socklen_t un_addrlen = sizeof(struct sockaddr_un);
245         struct sockaddr_un un_addr;
246         int ret;
247
248         parent_si = find_socket_info(s);
249         if (!parent_si) {
250                 return real_accept(s, addr, addrlen);
251         }
252
253         memset(&un_addr, 0, sizeof(un_addr));
254
255         ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
256         if (ret == -1) return ret;
257
258         fd = ret;
259
260         ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen,
261                                        parent_si->domain, addr, addrlen);
262         if (ret == -1) return ret;
263
264         child_si = malloc(sizeof(struct socket_info));
265         memset(child_si, 0, sizeof(*child_si));
266
267         child_si->fd = fd;
268         child_si->bound = 1;
269
270         child_si->myname_len = parent_si->myname_len;
271         child_si->myname = sockaddr_dup(parent_si->myname, parent_si->myname_len);
272
273         child_si->peername_len = *addrlen;
274         child_si->peername = sockaddr_dup(addr, *addrlen);
275
276         DLIST_ADD(sockets, child_si);
277
278         return fd;
279 }
280
281 /* using sendto() or connect() on an unbound socket would give the
282    recipient no way to reply, as unlike UDP and TCP, a unix domain
283    socket can't auto-assign emphemeral port numbers, so we need to
284    assign it here */
285 static int swrap_auto_bind(struct socket_info *si)
286 {
287         struct sockaddr_un un_addr;
288         struct sockaddr_in in;
289         int i;
290         
291         un_addr.sun_family = AF_UNIX;
292         
293         for (i=0;i<1000;i++) {
294                 snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), 
295                          "%s/"SOCKET_FORMAT, socket_wrapper_dir(),
296                          SOCK_DGRAM, i + 10000);
297                 if (bind(si->fd, (struct sockaddr *)&un_addr, 
298                          sizeof(un_addr)) == 0) {
299                         si->tmp_path = strdup(un_addr.sun_path);
300                         si->bound = 1;
301                         break;
302                 }
303         }
304         if (i == 1000) {
305                 return -1;
306         }
307         
308         memset(&in, 0, sizeof(in));
309         in.sin_family = AF_INET;
310         in.sin_port   = htons(i);
311         in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
312         
313         si->myname_len = sizeof(in);
314         si->myname = sockaddr_dup(&in, si->myname_len);
315         si->bound = 1;
316         return 0;
317 }
318
319
320 int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
321 {
322         int ret;
323         struct sockaddr_un un_addr;
324         struct socket_info *si = find_socket_info(s);
325
326         if (!si) {
327                 return real_connect(s, serv_addr, addrlen);
328         }
329
330         /* only allow pseudo loopback connections */
331         if (serv_addr->sa_family == AF_INET &&
332                 ((const struct sockaddr_in *)serv_addr)->sin_addr.s_addr != 
333             htonl(INADDR_LOOPBACK)) {
334                 errno = ENETUNREACH;
335                 return -1;
336         }
337
338         if (si->bound == 0 && si->domain != AF_UNIX) {
339                 ret = swrap_auto_bind(si);
340                 if (ret == -1) return -1;
341         }
342
343         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr);
344         if (ret == -1) return -1;
345
346         ret = real_connect(s, (struct sockaddr *)&un_addr, 
347                            sizeof(struct sockaddr_un));
348
349         if (ret == 0) {
350                 si->peername_len = addrlen;
351                 si->peername = sockaddr_dup(serv_addr, addrlen);
352         }
353
354         return ret;
355 }
356
357 int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
358 {
359         int ret;
360         struct sockaddr_un un_addr;
361         struct socket_info *si = find_socket_info(s);
362
363         if (!si) {
364                 return real_bind(s, myaddr, addrlen);
365         }
366
367         si->myname_len = addrlen;
368         si->myname = sockaddr_dup(myaddr, addrlen);
369
370         if (myaddr->sa_family == AF_INET &&
371             ((const struct sockaddr_in *)myaddr)->sin_addr.s_addr == 0) {
372                 ((struct sockaddr_in *)si->myname)->sin_addr.s_addr = 
373                         htonl(INADDR_LOOPBACK);
374         }
375         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr);
376         if (ret == -1) return -1;
377
378         unlink(un_addr.sun_path);
379
380         ret = real_bind(s, (struct sockaddr *)&un_addr,
381                         sizeof(struct sockaddr_un));
382
383         if (ret == 0) {
384                 si->bound = 1;
385         }
386
387         return ret;
388 }
389
390 int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
391 {
392         struct socket_info *si = find_socket_info(s);
393
394         if (!si) {
395                 return real_getpeername(s, name, addrlen);
396         }
397
398         if (!si->peername) 
399         {
400                 errno = ENOTCONN;
401                 return -1;
402         }
403
404         memcpy(name, si->peername, si->peername_len);
405         *addrlen = si->peername_len;
406
407         return 0;
408 }
409
410 int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
411 {
412         struct socket_info *si = find_socket_info(s);
413
414         if (!si) {
415                 return real_getsockname(s, name, addrlen);
416         }
417
418         memcpy(name, si->myname, si->myname_len);
419         *addrlen = si->myname_len;
420
421         return 0;
422 }
423
424 int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
425 {
426         struct socket_info *si = find_socket_info(s);
427
428         if (!si) {
429                 return real_getsockopt(s, level, optname, optval, optlen);
430         }
431
432         if (level == SOL_SOCKET) {
433                 return real_getsockopt(s, level, optname, optval, optlen);
434         } 
435
436         switch (si->domain) {
437         case AF_UNIX:
438                 return real_getsockopt(s, level, optname, optval, optlen);
439         default:
440                 errno = ENOPROTOOPT;
441                 return -1;
442         }
443 }
444
445 int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *optval, socklen_t optlen)
446 {
447         struct socket_info *si = find_socket_info(s);
448
449         if (!si) {
450                 return real_setsockopt(s, level, optname, optval, optlen);
451         }
452
453         if (level == SOL_SOCKET) {
454                 return real_setsockopt(s, level, optname, optval, optlen);
455         }
456
457         switch (si->domain) {
458         case AF_UNIX:
459                 return real_setsockopt(s, level, optname, optval, optlen);
460         case AF_INET:
461                 /* Silence some warnings */
462 #ifdef TCP_NODELAY
463                 if (optname == TCP_NODELAY) 
464                         return 0;
465 #endif
466         default:
467                 errno = ENOPROTOOPT;
468                 return -1;
469         }
470 }
471
472 ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
473 {
474         struct sockaddr_un un_addr;
475         socklen_t un_addrlen = sizeof(un_addr);
476         int ret;
477         struct socket_info *si = find_socket_info(s);
478
479         if (!si) {
480                 return real_recvfrom(s, buf, len, flags, from, fromlen);
481         }
482
483         /* irix 6.4 forgets to null terminate the sun_path string :-( */
484         memset(&un_addr, 0, sizeof(un_addr));
485         ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
486         if (ret == -1) 
487                 return ret;
488
489         if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
490                                      si->domain, from, fromlen) == -1) {
491                 return -1;
492         }
493         
494         return ret;
495 }
496
497
498 ssize_t swrap_sendto(int  s,  const  void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
499 {
500         struct sockaddr_un un_addr;
501         int ret;
502         struct socket_info *si = find_socket_info(s);
503
504         if (!si) {
505                 return real_sendto(s, buf, len, flags, to, tolen);
506         }
507
508         if (si->bound == 0 && si->domain != AF_UNIX) {
509                 ret = swrap_auto_bind(si);
510                 if (ret == -1) return -1;
511         }
512
513         ret = sockaddr_convert_to_un(si, to, tolen, &un_addr);
514         if (ret == -1) return -1;
515
516         ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
517
518         return ret;
519 }
520
521 int swrap_close(int fd)
522 {
523         struct socket_info *si = find_socket_info(fd);
524
525         if (si) {
526                 DLIST_REMOVE(sockets, si);
527
528                 free(si->path);
529                 free(si->myname);
530                 free(si->peername);
531                 if (si->tmp_path) {
532                         unlink(si->tmp_path);
533                         free(si->tmp_path);
534                 }
535                 free(si);
536         }
537
538         return real_close(fd);
539 }