238155b26b17276aeac5c68b93182dd274b76904
[bbaumbach/samba-autobuild/.git] / source4 / 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
72         char *path;
73
74         struct sockaddr *myname;
75         socklen_t myname_len;
76
77         struct sockaddr *peername;
78         socklen_t peername_len;
79
80         struct socket_info *prev, *next;
81 } *sockets = NULL;
82
83 static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
84 {
85         unsigned int prt;
86         const char *p;
87         int type;
88
89         if ((*len) < sizeof(struct sockaddr_in)) {
90                 return 0;
91         }
92
93         in->sin_family = AF_INET;
94         in->sin_port = 1025; /* Default to 1025 */
95         p = strchr(un->sun_path, '/');
96         if (p) p++; else p = un->sun_path;
97
98         if(sscanf(p, "sock_ip_%d_%u", &type, &prt) == 1) 
99         {
100                 in->sin_port = htons(prt);
101         }
102         in->sin_addr.s_addr = INADDR_LOOPBACK;
103         *len = sizeof(struct sockaddr_in);
104         return 0;
105 }
106
107 static int convert_in_un(int type, const struct sockaddr_in *in, struct sockaddr_un *un)
108 {
109         uint16_t prt = ntohs(in->sin_port);
110         /* FIXME: ENETUNREACH if in->sin_addr is not loopback */
111         snprintf(un->sun_path, sizeof(un->sun_path), "%s/sock_ip_%d_%u", getenv("SOCKET_WRAPPER_DIR"), type, prt);
112         return 0;
113 }
114
115 static struct socket_info *find_socket_info(int fd)
116 {
117         struct socket_info *i;
118         for (i = sockets; i; i = i->next) {
119                 if (i->fd == fd) 
120                         return i;
121         }
122
123         return NULL;
124 }
125
126 static int sockaddr_convert_to_un(const struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len, 
127                                          struct sockaddr_un *out_addr)
128 {
129         if (!out_addr)
130                 return 0;
131
132         out_addr->sun_family = AF_UNIX;
133
134         switch (in_addr->sa_family) {
135         case AF_INET:
136                 return convert_in_un(si->type, (const struct sockaddr_in *)in_addr, out_addr);
137         case AF_UNIX:
138                 memcpy(out_addr, in_addr, sizeof(*out_addr));
139                 return 0;
140         default:
141                 break;
142         }
143         
144         errno = EAFNOSUPPORT;
145         return -1;
146 }
147
148 static int sockaddr_convert_from_un(const struct socket_info *si, 
149                                                                         const struct sockaddr_un *in_addr, 
150                                                                         int family,
151                                                  struct sockaddr *out_addr,
152                                                  socklen_t *out_len)
153 {
154         if (!out_addr) 
155                 return 0;
156
157         switch (family) {
158         case AF_INET:
159                 return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, out_len);
160         case AF_UNIX:
161                 memcpy(out_addr, in_addr, sizeof(*in_addr));
162                 *out_len = sizeof(*in_addr);
163                 return 0;
164         default:
165                 break;
166         }
167
168         errno = EAFNOSUPPORT;
169         return -1;
170 }
171
172 int swrap_socket(int domain, int type, int protocol)
173 {
174         struct socket_info *si;
175         int fd;
176
177         if (!getenv("SOCKET_WRAPPER_DIR")) {
178                 return real_socket(domain, type, protocol);
179         }
180         
181         fd = real_socket(AF_UNIX, type, 0);
182
183         if (fd < 0) 
184                 return fd;
185
186         si = malloc(sizeof(struct socket_info));
187         memset(si, 0, sizeof(*si));
188
189         si->domain = domain;
190         si->type = type;
191         si->protocol = protocol;
192         si->fd = fd;
193
194         DLIST_ADD(sockets, si);
195
196         return si->fd;
197 }
198
199 int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
200 {
201         struct socket_info *parent_si, *child_si;
202         int fd;
203         socklen_t un_addrlen = sizeof(struct sockaddr_un);
204         struct sockaddr_un un_addr;
205         int ret;
206
207         parent_si = find_socket_info(s);
208         if (!parent_si) {
209                 return real_accept(s, addr, addrlen);
210         }
211
212         ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
213         if (ret < 0) return ret;
214
215         fd = ret;
216
217         ret = sockaddr_convert_from_un(parent_si, &un_addr, parent_si->domain, addr, addrlen);
218
219         if (ret < 0) return ret;
220
221         child_si = malloc(sizeof(struct socket_info));
222         memset(child_si, 0, sizeof(*child_si));
223
224         child_si->fd = fd;
225
226         if (addr && addrlen) {
227                 child_si->myname_len = *addrlen;
228                 child_si->myname = sockaddr_dup(addr, *addrlen);
229         }
230
231         return fd;
232 }
233
234 int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
235 {
236         int ret;
237         struct sockaddr_un un_addr;
238         struct socket_info *si = find_socket_info(s);
239
240         if (!si) {
241                 return real_connect(s, serv_addr, addrlen);
242         }
243
244         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr);
245         if (ret < 0) return ret;
246
247         ret = real_connect(s, 
248                                            (struct sockaddr *)&un_addr, 
249                                            sizeof(struct sockaddr_un));
250
251         if (ret >= 0) {
252                 si->peername_len = addrlen;
253                 si->peername = sockaddr_dup(serv_addr, addrlen);
254         }
255
256         return ret;
257 }
258
259 int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
260 {
261         int ret;
262         struct sockaddr_un un_addr;
263         struct socket_info *si = find_socket_info(s);
264
265         if (!si) {
266                 return real_bind(s, myaddr, addrlen);
267         }
268
269         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr);
270         if (ret < 0) return ret;
271
272         unlink(un_addr.sun_path);
273
274         ret = real_bind(s, 
275                                         (struct sockaddr *)&un_addr,
276                                         sizeof(struct sockaddr_un));
277
278         if (ret >= 0) {
279                 si->myname_len = addrlen;
280                 si->myname = sockaddr_dup(myaddr, addrlen);
281         }
282
283         return ret;
284 }
285
286 int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
287 {
288         struct socket_info *si = find_socket_info(s);
289
290         if (!si) {
291                 return real_getpeername(s, name, addrlen);
292         }
293
294         if (!si->peername) 
295         {
296                 errno = ENOTCONN;
297                 return -1;
298         }
299
300         memcpy(name, si->peername, si->peername_len);
301         *addrlen = si->peername_len;
302
303         return 0;
304 }
305
306 int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
307 {
308         struct socket_info *si = find_socket_info(s);
309
310         if (!si) {
311                 return real_getpeername(s, name, addrlen);
312         }
313
314         memcpy(name, si->myname, si->myname_len);
315         *addrlen = si->myname_len;
316
317         return 0;
318 }
319
320 int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
321 {
322         struct socket_info *si = find_socket_info(s);
323
324         if (!si) {
325                 return real_getsockopt(s, level, optname, optval, optlen);
326         }
327
328         if (level == SOL_SOCKET) {
329                 return real_getsockopt(s, level, optname, optval, optlen);
330         } 
331
332         switch (si->domain) {
333         case AF_UNIX:
334                 return real_getsockopt(s, level, optname, optval, optlen);
335         default:
336                 errno = ENOPROTOOPT;
337                 return -1;
338         }
339 }
340
341 int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *optval, socklen_t optlen)
342 {
343         struct socket_info *si = find_socket_info(s);
344
345         if (!si) {
346                 return real_setsockopt(s, level, optname, optval, optlen);
347         }
348
349         if (level == SOL_SOCKET) {
350                 return real_setsockopt(s, level, optname, optval, optlen);
351         }
352
353         switch (si->domain) {
354         case AF_UNIX:
355                 return real_setsockopt(s, level, optname, optval, optlen);
356         case AF_INET:
357                 /* Silence some warnings */
358 #ifdef TCP_NODELAY
359                 if (optname == TCP_NODELAY) 
360                         return 0;
361 #endif
362         default:
363                 errno = ENOPROTOOPT;
364                 return -1;
365         }
366 }
367
368 ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
369 {
370         socklen_t un_addrlen;
371         struct sockaddr_un un_addr;
372         int ret;
373         struct socket_info *si = find_socket_info(s);
374
375         if (!si) {
376                 return real_recvfrom(s, buf, len, flags, from, fromlen);
377         }
378
379         ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
380         if (ret < 0) 
381                 return ret;
382
383         ret = sockaddr_convert_from_un(si, &un_addr, si->domain, from, fromlen);
384         
385         return ret;
386 }
387
388 ssize_t swrap_sendto(int  s,  const  void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
389 {
390         struct sockaddr_un un_addr;
391         int ret;
392         struct socket_info *si = find_socket_info(s);
393
394         if (!si) {
395                 return real_sendto(s, buf, len, flags, to, tolen);
396         }
397
398         ret = sockaddr_convert_to_un(si, to, tolen, &un_addr);
399         if (ret < 0) 
400                 return ret;
401
402         ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
403         
404         return ret;
405 }
406
407 int swrap_close(int fd)
408 {
409         struct socket_info *si = find_socket_info(fd);
410
411         if (si) {
412                 DLIST_REMOVE(sockets, si);
413
414                 free(si->path);
415                 free(si->myname);
416                 free(si->peername);
417                 free(si);
418         }
419
420         return real_close(fd);
421 }