Try to be better about handling the results of getaddrinfo(): when
[rsync/rsync.git] / socket.c
... / ...
CommitLineData
1/* -*- c-file-style: "linux" -*-
2
3 Copyright (C) 1992-2001 by Andrew Tridgell <tridge@samba.org>
4 Copyright (C) 2001 by Martin Pool <mbp@samba.org>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21/**
22 * @file socket.c
23 *
24 * Socket functions used in rsync.
25 **/
26
27#include "rsync.h"
28
29#ifndef HAVE_GETADDRINFO
30#include "lib/addrinfo.h"
31#endif
32
33// extern int af; /* NO MORE BLOODY GLOBALS! */
34
35/* Establish a proxy connection on an open socket to a web roxy by
36 * using the CONNECT method. */
37static int establish_proxy_connection(int fd, char *host, int port)
38{
39 char buffer[1024];
40 char *cp;
41
42 snprintf(buffer, sizeof(buffer), "CONNECT %s:%d HTTP/1.0\r\n\r\n", host, port);
43 if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) {
44 rprintf(FERROR, "failed to write to proxy: %s\n",
45 strerror(errno));
46 return -1;
47 }
48
49 for (cp = buffer; cp < &buffer[sizeof(buffer) - 1]; cp++) {
50 if (read(fd, cp, 1) != 1) {
51 rprintf(FERROR, "failed to read from proxy: %s\n",
52 strerror(errno));
53 return -1;
54 }
55 if (*cp == '\n')
56 break;
57 }
58
59 if (*cp != '\n')
60 cp++;
61 *cp-- = '\0';
62 if (*cp == '\r')
63 *cp = '\0';
64 if (strncmp(buffer, "HTTP/", 5) != 0) {
65 rprintf(FERROR, "bad response from proxy - %s\n",
66 buffer);
67 return -1;
68 }
69 for (cp = &buffer[5]; isdigit(*cp) || (*cp == '.'); cp++)
70 ;
71 while (*cp == ' ')
72 cp++;
73 if (*cp != '2') {
74 rprintf(FERROR, "bad response from proxy - %s\n",
75 buffer);
76 return -1;
77 }
78 /* throw away the rest of the HTTP header */
79 while (1) {
80 for (cp = buffer; cp < &buffer[sizeof(buffer) - 1];
81 cp++) {
82 if (read(fd, cp, 1) != 1) {
83 rprintf(FERROR, "failed to read from proxy: %s\n",
84 strerror(errno));
85 return -1;
86 }
87 if (*cp == '\n')
88 break;
89 }
90 if ((cp > buffer) && (*cp == '\n'))
91 cp--;
92 if ((cp == buffer) && ((*cp == '\n') || (*cp == '\r')))
93 break;
94 }
95 return 0;
96}
97
98
99
100/**
101 * Open a socket to a tcp remote host with the specified port .
102 *
103 * Based on code from Warren. Proxy support by Stephen Rothwell.
104 * getaddrinfo() rewrite contributed by KAME.net.
105 *
106 * Now that we support IPv6 we need to look up the remote machine's
107 * address first, using @p af_hint to set a preference for the type
108 * of address. Then depending on whether it has v4 or v6 addresses we
109 * try to open a connection.
110 *
111 * The loop allows for machines with some addresses which may not be
112 * reachable, perhaps because we can't e.g. route ipv6 to that network
113 * but we can get ip4 packets through.
114 *
115 * @param bind_address Local address to use. Normally NULL to bind
116 * the wildcard address.
117 *
118 * @param af_hint Address family, e.g. AF_INET or AF_INET6.
119 **/
120int open_socket_out(char *host, int port, const char *bind_address,
121 int af_hint)
122{
123 int type = SOCK_STREAM;
124 int error;
125 int s;
126 int result;
127 struct addrinfo hints, *res0, *res;
128 char portbuf[10];
129 char *h;
130 int proxied = 0;
131 char buffer[1024];
132 char *cp;
133
134 /* if we have a RSYNC_PROXY env variable then redirect our
135 * connetcion via a web proxy at the given address. The format
136 * is hostname:port */
137 h = getenv("RSYNC_PROXY");
138 proxied = (h != NULL) && (*h != '\0');
139
140 if (proxied) {
141 strlcpy(buffer, h, sizeof(buffer));
142 cp = strchr(buffer, ':');
143 if (cp == NULL) {
144 rprintf(FERROR,
145 "invalid proxy specification: should be HOST:PORT\n");
146 return -1;
147 }
148 *cp++ = '\0';
149 strcpy(portbuf, cp);
150 h = buffer;
151 } else {
152 snprintf(portbuf, sizeof(portbuf), "%d", port);
153 h = host;
154 }
155
156 memset(&hints, 0, sizeof(hints));
157 hints.ai_family = af_hint;
158 hints.ai_socktype = type;
159 error = getaddrinfo(h, portbuf, &hints, &res0);
160 if (error) {
161 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
162 h, portbuf, gai_strerror(error));
163 return -1;
164 }
165
166 s = -1;
167 for (res = res0; res; res = res->ai_next) {
168 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
169 if (s < 0)
170 continue;
171
172 if (bind_address) {
173 struct addrinfo bhints, *bres;
174
175 memset(&bhints, 0, sizeof(bhints));
176 bhints.ai_family = res->ai_family;
177 bhints.ai_socktype = type;
178 bhints.ai_flags = AI_PASSIVE;
179 error = getaddrinfo(bind_address, NULL, &bhints, &bres);
180 if (error) {
181 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s <noport>: %s\n",
182 bind_address, gai_strerror(error));
183 continue;
184 }
185 if (bres->ai_next) {
186 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s resolved to multiple hosts\n",
187 bind_address);
188 freeaddrinfo(bres);
189 continue;
190 }
191 bind(s, bres->ai_addr, bres->ai_addrlen);
192 }
193
194 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
195 close(s);
196 s = -1;
197 continue;
198 }
199 if (proxied &&
200 establish_proxy_connection(s, host, port) != 0) {
201 close(s);
202 s = -1;
203 continue;
204 } else
205 break;
206 }
207 freeaddrinfo(res0);
208 if (s < 0) {
209 rprintf(FERROR, RSYNC_NAME ": failed to connect to %s: %s\n",
210 h, strerror(errno));
211 return -1;
212 }
213 return s;
214}
215
216
217/**
218 * Open an outgoing socket, but allow for it to be intercepted by
219 * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
220 * socketpair rather than really opening a socket.
221 *
222 * We use this primarily in testing to detect TCP flow bugs, but not
223 * cause security problems by really opening remote connections.
224 *
225 * This is based on the Samba LIBSMB_PROG feature.
226 *
227 * @param bind_address Local address to use. Normally NULL to get the stack default.
228 **/
229int open_socket_out_wrapped (char *host,
230 int port,
231 const char *bind_address,
232 int af_hint)
233{
234 char *prog;
235
236 if ((prog = getenv ("RSYNC_CONNECT_PROG")) != NULL)
237 return sock_exec (prog);
238 else
239 return open_socket_out (host, port, bind_address,
240 af_hint);
241}
242
243
244
245/**
246 * Open a socket of the specified type, port and address for incoming data
247 *
248 * Try to be better about handling the results of getaddrinfo(): when
249 * opening an inbound socket, we might get several address results,
250 * e.g. for the machine's ipv4 and ipv6 name.
251 *
252 * If binding a wildcard, then any one of them should do. If an address
253 * was specified but it's insufficiently specific then that's not our
254 * fault.
255 *
256 * However, some of the advertized addresses may not work because e.g. we
257 * don't have IPv6 support in the kernel. In that case go on and try all
258 * addresses until one succeeds.
259 *
260 * @param bind_address Local address to bind, or NULL to allow it to
261 * default.
262 **/
263static int open_socket_in(int type, int port, const char *bind_address,
264 int af_hint)
265{
266 int one=1;
267 int s;
268 struct addrinfo hints, *res, *resp;
269 char portbuf[10];
270 int error;
271
272 memset(&hints, 0, sizeof(hints));
273 hints.ai_family = af_hint;
274 hints.ai_socktype = type;
275 hints.ai_flags = AI_PASSIVE;
276 snprintf(portbuf, sizeof(portbuf), "%d", port);
277 error = getaddrinfo(bind_address, portbuf, &hints, &res);
278 if (error) {
279 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
280 bind_address, gai_strerror(error));
281 return -1;
282 }
283 /* XXX: Do we need to care about getting multiple results
284 * back? I think probably not; if the user passed
285 * bind_address == NULL and we set AI_PASSIVE then we ought to
286 * get a wildcard result. */
287
288 resp = res;
289 while (1) {
290 s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol);
291
292 if (s >= 0) {
293 break; /* got a socket */
294 } else if ((resp = resp->ai_next)) {
295 switch (errno) {
296 case EPROTONOSUPPORT:
297 case EAFNOSUPPORT:
298 case EPFNOSUPPORT:
299 /* See if there's another address that will work... */
300 continue;
301 }
302 }
303
304 rprintf(FERROR, RSYNC_NAME ": open inbound socket"
305 "(dom=%d, type=%d, proto=%d) failed: %s\n",
306 resp->ai_family, resp->ai_socktype, resp->ai_protocol,
307 strerror(errno));
308 goto fail;
309 }
310
311 setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one));
312
313 /* now we've got a socket - we need to bind it */
314 if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
315 rprintf(FERROR, RSYNC_NAME ": bind failed on port %d\n", port);
316 close(s);
317 goto fail;
318 }
319
320 return s;
321
322fail:
323 freeaddrinfo(res);
324 return -1;
325}
326
327
328/*
329 * Determine if a file descriptor is in fact a socket
330 */
331int is_a_socket(int fd)
332{
333 int v;
334 socklen_t l;
335 l = sizeof(int);
336
337 /* Parameters to getsockopt, setsockopt etc are very
338 * unstandardized across platforms, so don't be surprised if
339 * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
340 * It seems they all eventually get the right idea.
341 *
342 * Debian says: ``The fifth argument of getsockopt and
343 * setsockopt is in reality an int [*] (and this is what BSD
344 * 4.* and libc4 and libc5 have). Some POSIX confusion
345 * resulted in the present socklen_t. The draft standard has
346 * not been adopted yet, but glibc2 already follows it and
347 * also has socklen_t [*]. See also accept(2).''
348 *
349 * We now return to your regularly scheduled programming. */
350 return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
351}
352
353
354void start_accept_loop(int port, int (*fn)(int ))
355{
356 int s;
357 extern char *bind_address;
358
359 /* open an incoming socket */
360 s = open_socket_in(SOCK_STREAM, port, bind_address,
361 global_opts.af_hint);
362 if (s == -1)
363 exit_cleanup(RERR_SOCKETIO);
364
365 /* ready to listen */
366 if (listen(s, 5) == -1) {
367 close(s);
368 exit_cleanup(RERR_SOCKETIO);
369 }
370
371
372 /* now accept incoming connections - forking a new process
373 for each incoming connection */
374 while (1) {
375 fd_set fds;
376 int fd;
377 struct sockaddr addr;
378 int in_addrlen = sizeof(addr);
379
380 /* close log file before the potentially very long select so
381 file can be trimmed by another process instead of growing
382 forever */
383 log_close();
384
385 FD_ZERO(&fds);
386 FD_SET(s, &fds);
387
388 if (select(s+1, &fds, NULL, NULL, NULL) != 1) {
389 continue;
390 }
391
392 if(!FD_ISSET(s, &fds)) continue;
393
394 fd = accept(s,(struct sockaddr *)&addr,&in_addrlen);
395
396 if (fd == -1) continue;
397
398 signal(SIGCHLD, SIG_IGN);
399
400 /* we shouldn't have any children left hanging around
401 but I have had reports that on Digital Unix zombies
402 are produced, so this ensures that they are reaped */
403#ifdef WNOHANG
404 while (waitpid(-1, NULL, WNOHANG) > 0);
405#endif
406
407 if (fork()==0) {
408 close(s);
409 /* open log file in child before possibly giving
410 up privileges */
411 log_open();
412 _exit(fn(fd));
413 }
414
415 close(fd);
416 }
417}
418
419
420enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
421
422struct
423{
424 char *name;
425 int level;
426 int option;
427 int value;
428 int opttype;
429} socket_options[] = {
430 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
431 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
432 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
433#ifdef TCP_NODELAY
434 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
435#endif
436#ifdef IPTOS_LOWDELAY
437 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
438#endif
439#ifdef IPTOS_THROUGHPUT
440 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
441#endif
442#ifdef SO_SNDBUF
443 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
444#endif
445#ifdef SO_RCVBUF
446 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
447#endif
448#ifdef SO_SNDLOWAT
449 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
450#endif
451#ifdef SO_RCVLOWAT
452 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
453#endif
454#ifdef SO_SNDTIMEO
455 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
456#endif
457#ifdef SO_RCVTIMEO
458 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
459#endif
460 {NULL,0,0,0,0}};
461
462
463
464/****************************************************************************
465set user socket options
466****************************************************************************/
467void set_socket_options(int fd, char *options)
468{
469 char *tok;
470 if (!options || !*options) return;
471
472 options = strdup(options);
473
474 if (!options) out_of_memory("set_socket_options");
475
476 for (tok=strtok(options, " \t,"); tok; tok=strtok(NULL," \t,")) {
477 int ret=0,i;
478 int value = 1;
479 char *p;
480 int got_value = 0;
481
482 if ((p = strchr(tok,'='))) {
483 *p = 0;
484 value = atoi(p+1);
485 got_value = 1;
486 }
487
488 for (i=0;socket_options[i].name;i++)
489 if (strcmp(socket_options[i].name,tok)==0)
490 break;
491
492 if (!socket_options[i].name) {
493 rprintf(FERROR,"Unknown socket option %s\n",tok);
494 continue;
495 }
496
497 switch (socket_options[i].opttype) {
498 case OPT_BOOL:
499 case OPT_INT:
500 ret = setsockopt(fd,socket_options[i].level,
501 socket_options[i].option,(char *)&value,sizeof(int));
502 break;
503
504 case OPT_ON:
505 if (got_value)
506 rprintf(FERROR,"syntax error - %s does not take a value\n",tok);
507
508 {
509 int on = socket_options[i].value;
510 ret = setsockopt(fd,socket_options[i].level,
511 socket_options[i].option,(char *)&on,sizeof(int));
512 }
513 break;
514 }
515
516 if (ret != 0)
517 rprintf(FERROR, "failed to set socket option %s: %s\n", tok,
518 strerror(errno));
519 }
520
521 free(options);
522}
523
524/****************************************************************************
525become a daemon, discarding the controlling terminal
526****************************************************************************/
527void become_daemon(void)
528{
529 int i;
530
531 if (fork()) {
532 _exit(0);
533 }
534
535 /* detach from the terminal */
536#ifdef HAVE_SETSID
537 setsid();
538#else
539#ifdef TIOCNOTTY
540 i = open("/dev/tty", O_RDWR);
541 if (i >= 0) {
542 ioctl(i, (int) TIOCNOTTY, (char *)0);
543 close(i);
544 }
545#endif /* TIOCNOTTY */
546#endif
547 /* make sure that stdin, stdout an stderr don't stuff things
548 up (library functions, for example) */
549 for (i=0;i<3;i++) {
550 close(i);
551 open("/dev/null", O_RDWR);
552 }
553}
554
555/**
556 * Return the IP addr of the client as a string
557 **/
558char *client_addr(int fd)
559{
560 struct sockaddr ss;
561 int length = sizeof(ss);
562 static char addr_buf[100];
563 static int initialised;
564
565 if (initialised) return addr_buf;
566
567 initialised = 1;
568
569 if (getpeername(fd, &ss, &length)) {
570 exit_cleanup(RERR_SOCKETIO);
571 }
572
573 getnameinfo(&ss, length,
574 addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
575 return addr_buf;
576}
577
578
579/**
580 * Return the DNS name of the client
581 **/
582char *client_name(int fd)
583{
584 struct sockaddr ss;
585 int length = sizeof(ss);
586 static char name_buf[100];
587 static char port_buf[100];
588 char *def = "UNKNOWN";
589 static int initialised;
590 struct addrinfo hints, *res, *res0;
591 int error;
592
593 if (initialised) return name_buf;
594
595 initialised = 1;
596
597 strcpy(name_buf,def);
598
599 if (getpeername(fd, (struct sockaddr *)&ss, &length)) {
600 /* FIXME: Can we really not continue? */
601 rprintf(FERROR, RSYNC_NAME ": getpeername on fd%d failed: %s\n",
602 strerror(errno));
603 exit_cleanup(RERR_SOCKETIO);
604 }
605
606#ifdef INET6
607 if (ss.ss_family == AF_INET6 &&
608 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&ss)->sin6_addr)) {
609 struct sockaddr_in6 sin6;
610 struct sockaddr_in *sin;
611
612 memcpy(&sin6, &ss, sizeof(sin6));
613 sin = (struct sockaddr_in *)&ss;
614 memset(sin, 0, sizeof(*sin));
615 sin->sin_family = AF_INET;
616 length = sizeof(struct sockaddr_in);
617#ifdef HAVE_SOCKADDR_LEN
618 sin->sin_len = length;
619#endif
620 sin->sin_port = sin6.sin6_port;
621 memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
622 sizeof(sin->sin_addr));
623 }
624#endif
625
626 /* reverse lookup */
627 if (getnameinfo((struct sockaddr *)&ss, length,
628 name_buf, sizeof(name_buf), port_buf, sizeof(port_buf),
629 NI_NAMEREQD | NI_NUMERICSERV) != 0) {
630 strcpy(name_buf, def);
631 rprintf(FERROR, "reverse name lookup failed\n");
632 }
633
634 /* forward lookup */
635 memset(&hints, 0, sizeof(hints));
636 hints.ai_family = PF_UNSPEC;
637 hints.ai_flags = AI_CANONNAME;
638 hints.ai_socktype = SOCK_STREAM;
639 error = getaddrinfo(name_buf, port_buf, &hints, &res0);
640 if (error) {
641 strcpy(name_buf, def);
642 rprintf(FERROR,
643 RSYNC_NAME ": forward name lookup for %s failed: %s\n",
644 port_buf,
645 gai_strerror(error));
646 return name_buf;
647 }
648
649 /* XXX sin6_flowinfo and other fields */
650 for (res = res0; res; res = res->ai_next) {
651 if (res->ai_family != ss.sa_family)
652 continue;
653 if (res->ai_addrlen != length)
654 continue;
655 if (memcmp(res->ai_addr, &ss, res->ai_addrlen) == 0)
656 break;
657 }
658
659 /* TODO: Do a forward lookup as well to prevent spoofing */
660
661 if (res == NULL) {
662 strcpy(name_buf, def);
663 rprintf(FERROR, RSYNC_NAME ": "
664 "reverse name lookup mismatch on fd%d - spoofed address?\n",
665 fd);
666 }
667
668 freeaddrinfo(res0);
669 return name_buf;
670}
671
672/**
673 * Convert a string to an IP address. The string can be a name or
674 * dotted decimal number.
675 *
676 * Returns a pointer to a static in_addr struct -- if you call this
677 * more than once then you should copy it.
678 *
679 * TODO: Use getaddrinfo() instead, or make this function call getnameinfo
680 **/
681struct in_addr *ip_address(const char *str)
682{
683 static struct in_addr ret;
684 struct hostent *hp;
685
686 if (!str) {
687 rprintf (FERROR, "ip_address received NULL name\n");
688 return NULL;
689 }
690
691 /* try as an IP address */
692 if (inet_aton(str, &ret) != 0) {
693 return &ret;
694 }
695
696 /* otherwise assume it's a network name of some sort and use
697 gethostbyname */
698 if ((hp = gethostbyname (str)) == 0) {
699 rprintf(FERROR, "gethostbyname failed for \"%s\": unknown host?\n",str);
700 return NULL;
701 }
702
703 if (hp->h_addr == NULL) {
704 rprintf(FERROR, "gethostbyname: host address is invalid for host \"%s\"\n",str);
705 return NULL;
706 }
707
708 if (hp->h_length > sizeof ret) {
709 rprintf(FERROR, "gethostbyname: host address for \"%s\" is too large\n",
710 str);
711 return NULL;
712 }
713
714 if (hp->h_addrtype != AF_INET) {
715 rprintf (FERROR, "gethostname: host address for \"%s\" is not IPv4\n",
716 str);
717 return NULL;
718 }
719
720 /* This is kind of difficult. The only field in ret is
721 s_addr, which is the IP address as a 32-bit int. On
722 UNICOS, s_addr is in fact a *bitfield* for reasons best
723 know to Cray. This means we can't memcpy in to it. On the
724 other hand, h_addr is a char*, so we can't just assign.
725
726 Since there's meant to be only one field inside the in_addr
727 structure we will try just copying over the top and see how
728 that goes. */
729 memcpy (&ret, hp->h_addr, hp->h_length);
730
731 return &ret;
732}
733
734
735
736/*******************************************************************
737this is like socketpair but uses tcp. It is used by the Samba
738regression test code
739The function guarantees that nobody else can attach to the socket,
740or if they do that this function fails and the socket gets closed
741returns 0 on success, -1 on failure
742the resulting file descriptors are symmetrical
743 ******************************************************************/
744static int socketpair_tcp(int fd[2])
745{
746 int listener;
747 struct sockaddr_in sock;
748 struct sockaddr_in sock2;
749 socklen_t socklen = sizeof(sock);
750 int connect_done = 0;
751
752 fd[0] = fd[1] = listener = -1;
753
754 memset(&sock, 0, sizeof(sock));
755
756 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
757
758 memset(&sock2, 0, sizeof(sock2));
759#ifdef HAVE_SOCK_SIN_LEN
760 sock2.sin_len = sizeof(sock2);
761#endif
762 sock2.sin_family = PF_INET;
763
764 bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
765
766 if (listen(listener, 1) != 0) goto failed;
767
768 if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
769
770 if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
771
772 set_nonblocking(fd[1]);
773
774 sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
775
776 if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
777 if (errno != EINPROGRESS) goto failed;
778 } else {
779 connect_done = 1;
780 }
781
782 if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
783
784 close(listener);
785 if (connect_done == 0) {
786 if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
787 && errno != EISCONN) goto failed;
788 }
789
790 set_blocking (fd[1]);
791
792 /* all OK! */
793 return 0;
794
795 failed:
796 if (fd[0] != -1) close(fd[0]);
797 if (fd[1] != -1) close(fd[1]);
798 if (listener != -1) close(listener);
799 return -1;
800}
801
802
803/*******************************************************************
804run a program on a local tcp socket, this is used to launch smbd
805when regression testing
806the return value is a socket which is attached to a subprocess
807running "prog". stdin and stdout are attached. stderr is left
808attached to the original stderr
809 ******************************************************************/
810int sock_exec(const char *prog)
811{
812 int fd[2];
813 if (socketpair_tcp(fd) != 0) {
814 rprintf (FERROR, RSYNC_NAME
815 ": socketpair_tcp failed (%s)\n",
816 strerror(errno));
817 return -1;
818 }
819 if (fork() == 0) {
820 close(fd[0]);
821 close(0);
822 close(1);
823 dup(fd[1]);
824 dup(fd[1]);
825 if (verbose > 3)
826 fprintf (stderr,
827 RSYNC_NAME ": execute socket program \"%s\"\n",
828 prog);
829 exit (system (prog));
830 }
831 close (fd[1]);
832 return fd[0];
833}
834
835
836