4814f6c9292303a900231740eeab931be0a02834
[samba.git] / source / lib / socket_wrapper / 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_BUILD
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 <netinet/tcp.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include "dlinklist.h"
37 #endif
38
39 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
40  * for now */
41 #define REWRITE_CALLS 
42
43 #ifdef REWRITE_CALLS
44 #define real_accept accept
45 #define real_connect connect
46 #define real_bind bind
47 #define real_getpeername getpeername
48 #define real_getsockname getsockname
49 #define real_getsockopt getsockopt
50 #define real_setsockopt setsockopt
51 #define real_recvfrom recvfrom
52 #define real_sendto sendto
53 #define real_socket socket
54 #define real_close close
55 #endif
56
57 static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
58 {
59         struct sockaddr *ret = (struct sockaddr *)malloc(len);
60         memcpy(ret, data, len);
61         return ret;
62 }
63
64 struct socket_info
65 {
66         int fd;
67
68         int domain;
69         int type;
70         int protocol;
71         int bound;
72
73         char *path;
74         char *tmp_path;
75
76         struct sockaddr *myname;
77         socklen_t myname_len;
78
79         struct sockaddr *peername;
80         socklen_t peername_len;
81
82         struct socket_info *prev, *next;
83 };
84
85 static struct socket_info *sockets = NULL;
86
87 static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
88 {
89         unsigned int prt;
90         const char *p;
91         int type;
92
93         if ((*len) < sizeof(struct sockaddr_in)) {
94                 return 0;
95         }
96
97         in->sin_family = AF_INET;
98         in->sin_port = 1025; /* Default to 1025 */
99         p = strchr(un->sun_path, '/');
100         if (p) p++; else p = un->sun_path;
101
102         if (sscanf(p, "sock_ip_%d_%u", &type, &prt) == 2) {
103                 in->sin_port = htons(prt);
104         }
105         in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
106         *len = sizeof(struct sockaddr_in);
107         return 0;
108 }
109
110 static int convert_in_un(int type, const struct sockaddr_in *in, struct sockaddr_un *un)
111 {
112         uint16_t prt = ntohs(in->sin_port);
113         snprintf(un->sun_path, sizeof(un->sun_path), "%s/sock_ip_%d_%u", 
114                  getenv("SOCKET_WRAPPER_DIR"), type, prt);
115         return 0;
116 }
117
118 static struct socket_info *find_socket_info(int fd)
119 {
120         struct socket_info *i;
121         for (i = sockets; i; i = i->next) {
122                 if (i->fd == fd) 
123                         return i;
124         }
125
126         return NULL;
127 }
128
129 static int sockaddr_convert_to_un(const struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len, 
130                                          struct sockaddr_un *out_addr)
131 {
132         if (!out_addr)
133                 return 0;
134
135         out_addr->sun_family = AF_UNIX;
136
137         switch (in_addr->sa_family) {
138         case AF_INET:
139                 return convert_in_un(si->type, (const struct sockaddr_in *)in_addr, out_addr);
140         case AF_UNIX:
141                 memcpy(out_addr, in_addr, sizeof(*out_addr));
142                 return 0;
143         default:
144                 break;
145         }
146         
147         errno = EAFNOSUPPORT;
148         return -1;
149 }
150
151 static int sockaddr_convert_from_un(const struct socket_info *si, 
152                                     const struct sockaddr_un *in_addr, 
153                                     socklen_t un_addrlen,
154                                     int family,
155                                     struct sockaddr *out_addr,
156                                     socklen_t *out_len)
157 {
158         if (out_addr == NULL || out_len == NULL) 
159                 return 0;
160
161         if (un_addrlen == 0) {
162                 *out_len = 0;
163                 return 0;
164         }
165
166         switch (family) {
167         case AF_INET:
168                 return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, out_len);
169         case AF_UNIX:
170                 memcpy(out_addr, in_addr, sizeof(*in_addr));
171                 *out_len = sizeof(*in_addr);
172                 return 0;
173         default:
174                 break;
175         }
176
177         errno = EAFNOSUPPORT;
178         return -1;
179 }
180
181 int swrap_socket(int domain, int type, int protocol)
182 {
183         struct socket_info *si;
184         int fd;
185
186         if (!getenv("SOCKET_WRAPPER_DIR")) {
187                 return real_socket(domain, type, protocol);
188         }
189         
190         fd = real_socket(AF_UNIX, type, 0);
191
192         if (fd == -1) return -1;
193
194         si = calloc(1, sizeof(struct socket_info));
195
196         si->domain = domain;
197         si->type = type;
198         si->protocol = protocol;
199         si->fd = fd;
200
201         DLIST_ADD(sockets, si);
202
203         return si->fd;
204 }
205
206 int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
207 {
208         struct socket_info *parent_si, *child_si;
209         int fd;
210         socklen_t un_addrlen = sizeof(struct sockaddr_un);
211         struct sockaddr_un un_addr;
212         int ret;
213
214         parent_si = find_socket_info(s);
215         if (!parent_si) {
216                 return real_accept(s, addr, addrlen);
217         }
218
219         ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
220         if (ret == -1) return ret;
221
222         fd = ret;
223
224         ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen,
225                                        parent_si->domain, addr, addrlen);
226         if (ret == -1) return ret;
227
228         child_si = malloc(sizeof(struct socket_info));
229         memset(child_si, 0, sizeof(*child_si));
230
231         child_si->fd = fd;
232
233         if (addr && addrlen) {
234                 child_si->myname_len = *addrlen;
235                 child_si->myname = sockaddr_dup(addr, *addrlen);
236         }
237
238         return fd;
239 }
240
241 int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
242 {
243         int ret;
244         struct sockaddr_un un_addr;
245         struct socket_info *si = find_socket_info(s);
246
247         if (!si) {
248                 return real_connect(s, serv_addr, addrlen);
249         }
250
251         /* only allow pseudo loopback connections */
252         if (((const struct sockaddr_in *)serv_addr)->sin_addr.s_addr != 
253             htonl(INADDR_LOOPBACK)) {
254                 errno = ENETUNREACH;
255                 return -1;
256         }
257
258         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr);
259         if (ret == -1) return -1;
260
261         ret = real_connect(s, (struct sockaddr *)&un_addr, 
262                            sizeof(struct sockaddr_un));
263
264         if (ret == 0) {
265                 si->peername_len = addrlen;
266                 si->peername = sockaddr_dup(serv_addr, addrlen);
267         }
268
269         return ret;
270 }
271
272 int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
273 {
274         int ret;
275         struct sockaddr_un un_addr;
276         struct socket_info *si = find_socket_info(s);
277
278         if (!si) {
279                 return real_bind(s, myaddr, addrlen);
280         }
281
282         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr);
283         if (ret == -1) return -1;
284
285         unlink(un_addr.sun_path);
286
287         ret = real_bind(s, (struct sockaddr *)&un_addr,
288                         sizeof(struct sockaddr_un));
289
290         if (ret == 0) {
291                 si->myname_len = addrlen;
292                 si->myname = sockaddr_dup(myaddr, addrlen);
293                 si->bound = 1;
294         }
295
296         return ret;
297 }
298
299 int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
300 {
301         struct socket_info *si = find_socket_info(s);
302
303         if (!si) {
304                 return real_getpeername(s, name, addrlen);
305         }
306
307         if (!si->peername) 
308         {
309                 errno = ENOTCONN;
310                 return -1;
311         }
312
313         memcpy(name, si->peername, si->peername_len);
314         *addrlen = si->peername_len;
315
316         return 0;
317 }
318
319 int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
320 {
321         struct socket_info *si = find_socket_info(s);
322
323         if (!si) {
324                 return real_getsockname(s, name, addrlen);
325         }
326
327         memcpy(name, si->myname, si->myname_len);
328         *addrlen = si->myname_len;
329
330         return 0;
331 }
332
333 int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
334 {
335         struct socket_info *si = find_socket_info(s);
336
337         if (!si) {
338                 return real_getsockopt(s, level, optname, optval, optlen);
339         }
340
341         if (level == SOL_SOCKET) {
342                 return real_getsockopt(s, level, optname, optval, optlen);
343         } 
344
345         switch (si->domain) {
346         case AF_UNIX:
347                 return real_getsockopt(s, level, optname, optval, optlen);
348         default:
349                 errno = ENOPROTOOPT;
350                 return -1;
351         }
352 }
353
354 int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *optval, socklen_t optlen)
355 {
356         struct socket_info *si = find_socket_info(s);
357
358         if (!si) {
359                 return real_setsockopt(s, level, optname, optval, optlen);
360         }
361
362         if (level == SOL_SOCKET) {
363                 return real_setsockopt(s, level, optname, optval, optlen);
364         }
365
366         switch (si->domain) {
367         case AF_UNIX:
368                 return real_setsockopt(s, level, optname, optval, optlen);
369         case AF_INET:
370                 /* Silence some warnings */
371 #ifdef TCP_NODELAY
372                 if (optname == TCP_NODELAY) 
373                         return 0;
374 #endif
375         default:
376                 errno = ENOPROTOOPT;
377                 return -1;
378         }
379 }
380
381 ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
382 {
383         struct sockaddr_un un_addr;
384         socklen_t un_addrlen = sizeof(un_addr);
385         int ret;
386         struct socket_info *si = find_socket_info(s);
387
388         if (!si) {
389                 return real_recvfrom(s, buf, len, flags, from, fromlen);
390         }
391
392         ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
393         if (ret == -1) 
394                 return ret;
395
396         if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
397                                      si->domain, from, fromlen) == -1) {
398                 return -1;
399         }
400         
401         return ret;
402 }
403
404 ssize_t swrap_sendto(int  s,  const  void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
405 {
406         struct sockaddr_un un_addr;
407         int ret;
408         struct socket_info *si = find_socket_info(s);
409
410         if (!si) {
411                 return real_sendto(s, buf, len, flags, to, tolen);
412         }
413
414         /* using sendto() on an unbound DGRAM socket would give the
415            recipient no way to reply, as unlike UDP, a unix domain socket
416            can't auto-assign emphemeral port numbers, so we need to assign
417            it here */
418         if (si->bound == 0 && si->type == SOCK_DGRAM) {
419                 int i;
420
421                 un_addr.sun_family = AF_UNIX;
422
423                 for (i=0;i<1000;i++) {
424                         snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), 
425                                  "%s/sock_ip_%u_%u", getenv("SOCKET_WRAPPER_DIR"), 
426                                  SOCK_DGRAM, i + 10000);
427                         if (bind(si->fd, (struct sockaddr *)&un_addr, 
428                                  sizeof(un_addr)) == 0) {
429                                 si->tmp_path = strdup(un_addr.sun_path);
430                                 si->bound = 1;
431                                 break;
432                         }
433                 }
434                 if (i == 1000) {
435                         return -1;
436                 }
437         }
438         
439
440         ret = sockaddr_convert_to_un(si, to, tolen, &un_addr);
441         if (ret == -1) return -1;
442
443         ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
444
445         return ret;
446 }
447
448 int swrap_close(int fd)
449 {
450         struct socket_info *si = find_socket_info(fd);
451
452         if (si) {
453                 DLIST_REMOVE(sockets, si);
454
455                 free(si->path);
456                 free(si->myname);
457                 free(si->peername);
458                 if (si->tmp_path) {
459                         unlink(si->tmp_path);
460                         free(si->tmp_path);
461                 }
462                 free(si);
463         }
464
465         return real_close(fd);
466 }