socket.c 20.6 KB
Newer Older
1
/*
2
 * Socket functions used in rsync.
Martin Pool's avatar
Martin Pool committed
3
 *
4 5
 * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
 * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
Wayne Davison's avatar
Wayne Davison committed
6
 * Copyright (C) 2003-2009 Wayne Davison
7 8
 *
 * This program is free software; you can redistribute it and/or modify
9 10 11
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
12 13 14 15 16 17
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
18
 * You should have received a copy of the GNU General Public License along
Wayne Davison's avatar
Wayne Davison committed
19
 * with this program; if not, visit the http://fsf.org website.
20 21 22
 */

/* This file is now converted to use the new-style getaddrinfo()
Martin Pool's avatar
Martin Pool committed
23 24
 * interface, which supports IPv6 but is also supported on recent
 * IPv4-only machines.  On systems that don't have that interface, we
25
 * emulate it using the KAME implementation. */
Andrew Tridgell's avatar
 
Andrew Tridgell committed
26

Andrew Tridgell's avatar
 
Andrew Tridgell committed
27
#include "rsync.h"
28
#include "ifuncs.h"
29 30
#include <netinet/in_systm.h>
#include <netinet/ip.h>
31
#include <netinet/tcp.h>
Andrew Tridgell's avatar
 
Andrew Tridgell committed
32

33
extern char *bind_address;
34
extern char *sockopts;
35
extern int default_af_hint;
36
extern int connect_timeout;
37

38
#ifdef HAVE_SIGACTION
39 40 41
static struct sigaction sigact;
#endif

42 43 44 45 46
static int sock_exec(const char *prog);

/* Establish a proxy connection on an open socket to a web proxy by using the
 * CONNECT method.  If proxy_user and proxy_pass are not NULL, they are used to
 * authenticate to the proxy using the "Basic" proxy-authorization protocol. */
47 48
static int establish_proxy_connection(int fd, char *host, int port,
				      char *proxy_user, char *proxy_pass)
49
{
50 51 52 53 54
	char *cp, buffer[1024];
	char *authhdr, authbuf[1024];
	int len;

	if (proxy_user && proxy_pass) {
Wayne Davison's avatar
Wayne Davison committed
55 56
		stringjoin(buffer, sizeof buffer,
			 proxy_user, ":", proxy_pass, NULL);
57
		len = strlen(buffer);
58

59
		if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
60 61 62 63 64
			rprintf(FERROR,
				"authentication information is too long\n");
			return -1;
		}

65
		base64_encode(buffer, len, authbuf, 1);
66 67 68 69 70 71 72 73 74 75
		authhdr = "\r\nProxy-Authorization: Basic ";
	} else {
		*authbuf = '\0';
		authhdr = "";
	}

	snprintf(buffer, sizeof buffer, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n",
		 host, port, authhdr, authbuf);
	len = strlen(buffer);
	if (write(fd, buffer, len) != len) {
76
		rsyserr(FERROR, errno, "failed to write to proxy");
77 78 79
		return -1;
	}

80
	for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
81
		if (read(fd, cp, 1) != 1) {
82
			rsyserr(FERROR, errno, "failed to read from proxy");
83 84 85 86 87 88 89 90 91 92 93 94
			return -1;
		}
		if (*cp == '\n')
			break;
	}

	if (*cp != '\n')
		cp++;
	*cp-- = '\0';
	if (*cp == '\r')
		*cp = '\0';
	if (strncmp(buffer, "HTTP/", 5) != 0) {
95
		rprintf(FERROR, "bad response from proxy -- %s\n",
96 97 98
			buffer);
		return -1;
	}
99
	for (cp = &buffer[5]; isDigit(cp) || *cp == '.'; cp++) {}
100 101 102
	while (*cp == ' ')
		cp++;
	if (*cp != '2') {
103
		rprintf(FERROR, "bad response from proxy -- %s\n",
104 105 106 107 108
			buffer);
		return -1;
	}
	/* throw away the rest of the HTTP header */
	while (1) {
109
		for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
110
			if (read(fd, cp, 1) != 1) {
111 112
				rsyserr(FERROR, errno,
					"failed to read from proxy");
113 114 115 116 117
				return -1;
			}
			if (*cp == '\n')
				break;
		}
118
		if (cp > buffer && *cp == '\n')
119
			cp--;
120
		if (cp == buffer && (*cp == '\n' || *cp == '\r'))
121 122 123 124 125 126
			break;
	}
	return 0;
}


127 128
/* Try to set the local address for a newly-created socket.
 * Return -1 if this fails. */
129
int try_bind_local(int s, int ai_family, int ai_socktype,
130
		   const char *bind_addr)
131 132 133 134
{
	int error;
	struct addrinfo bhints, *bres_all, *r;

135
	memset(&bhints, 0, sizeof bhints);
136 137 138
	bhints.ai_family = ai_family;
	bhints.ai_socktype = ai_socktype;
	bhints.ai_flags = AI_PASSIVE;
139
	if ((error = getaddrinfo(bind_addr, NULL, &bhints, &bres_all))) {
140
		rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
141
			bind_addr, gai_strerror(error));
142 143 144 145
		return -1;
	}

	for (r = bres_all; r; r = r->ai_next) {
Martin Pool's avatar
Typo  
Martin Pool committed
146
		if (bind(s, r->ai_addr, r->ai_addrlen) == -1)
147
			continue;
148
		freeaddrinfo(bres_all);
149 150 151 152 153 154
		return s;
	}

	/* no error message; there might be some problem that allows
	 * creation of the socket but not binding, perhaps if the
	 * machine has no ipv6 address of this name. */
155
	freeaddrinfo(bres_all);
156 157 158
	return -1;
}

159 160 161 162 163
/* connect() timeout handler based on alarm() */
static RETSIGTYPE contimeout_handler(UNUSED(int val))
{
	connect_timeout = -1;
}
164

165
/* Open a socket to a tcp remote host with the specified port.
166
 *
167 168
 * Based on code from Warren.  Proxy support by Stephen Rothwell.
 * getaddrinfo() rewrite contributed by KAME.net.
169
 *
170 171 172
 * Now that we support IPv6 we need to look up the remote machine's address
 * first, using af_hint to set a preference for the type of address.  Then
 * depending on whether it has v4 or v6 addresses we try to open a connection.
173
 *
174 175 176
 * The loop allows for machines with some addresses which may not be reachable,
 * perhaps because we can't e.g. route ipv6 to that network but we can get ip4
 * packets through.
177
 *
178
 * bind_addr: local address to use.  Normally NULL to bind the wildcard address.
179
 *
180
 * af_hint: address family, e.g. AF_INET or AF_INET6. */
181
int open_socket_out(char *host, int port, const char *bind_addr,
182
		    int af_hint)
Andrew Tridgell's avatar
 
Andrew Tridgell committed
183
{
Andrew Tridgell's avatar
 
Andrew Tridgell committed
184
	int type = SOCK_STREAM;
185
	int error, s;
186 187
	struct addrinfo hints, *res0, *res;
	char portbuf[10];
188
	char *h, *cp;
189 190
	int proxied = 0;
	char buffer[1024];
191
	char *proxy_user = NULL, *proxy_pass = NULL;
192

193
	/* if we have a RSYNC_PROXY env variable then redirect our
194
	 * connetcion via a web proxy at the given address. */
195
	h = getenv("RSYNC_PROXY");
196
	proxied = h != NULL && *h != '\0';
197 198

	if (proxied) {
199 200 201
		strlcpy(buffer, h, sizeof buffer);

		/* Is the USER:PASS@ prefix present? */
202
		if ((cp = strrchr(buffer, '@')) != NULL) {
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
			*cp++ = '\0';
			/* The remainder is the HOST:PORT part. */
			h = cp;

			if ((cp = strchr(buffer, ':')) == NULL) {
				rprintf(FERROR,
					"invalid proxy specification: should be USER:PASS@HOST:PORT\n");
				return -1;
			}
			*cp++ = '\0';

			proxy_user = buffer;
			proxy_pass = cp;
		} else {
			/* The whole buffer is the HOST:PORT part. */
			h = buffer;
		}

		if ((cp = strchr(h, ':')) == NULL) {
222 223
			rprintf(FERROR,
				"invalid proxy specification: should be HOST:PORT\n");
224 225 226
			return -1;
		}
		*cp++ = '\0';
227
		strlcpy(portbuf, cp, sizeof portbuf);
228 229 230 231
		if (verbose >= 2) {
			rprintf(FINFO, "connection via http proxy %s port %s\n",
				h, portbuf);
		}
232
	} else {
233
		snprintf(portbuf, sizeof portbuf, "%d", port);
234 235
		h = host;
	}
Andrew Tridgell's avatar
 
Andrew Tridgell committed
236

237
	memset(&hints, 0, sizeof hints);
238
	hints.ai_family = af_hint;
239 240 241
	hints.ai_socktype = type;
	error = getaddrinfo(h, portbuf, &hints, &res0);
	if (error) {
242 243
		rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
			h, portbuf, gai_strerror(error));
Andrew Tridgell's avatar
 
Andrew Tridgell committed
244 245 246
		return -1;
	}

247
	s = -1;
248 249 250 251
	/* Try to connect to all addresses for this machine until we get
	 * through.  It might e.g. be multi-homed, or have both IPv4 and IPv6
	 * addresses.  We need to create a socket for each record, since the
	 * address record tells us what protocol to use to try to connect. */
252 253 254 255
	for (res = res0; res; res = res->ai_next) {
		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (s < 0)
			continue;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
256

257
		if (bind_addr
258
		 && try_bind_local(s, res->ai_family, type,
259
				   bind_addr) == -1) {
260 261 262 263
			close(s);
			s = -1;
			continue;
		}
264 265 266 267 268
		if (connect_timeout > 0) {
			SIGACTION(SIGALRM, contimeout_handler);
			alarm(connect_timeout);
		}

269
		set_socket_options(s, sockopts);
270 271 272 273 274
		while (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
			if (connect_timeout < 0)
				exit_cleanup(RERR_CONTIMEOUT);
			if (errno == EINTR)
				continue;
275 276
			close(s);
			s = -1;
277
			break;
278
		}
279 280 281 282 283 284 285

		if (connect_timeout > 0)
			alarm(0);

		if (s < 0)
			continue;

286 287 288
		if (proxied
		 && establish_proxy_connection(s, host, port,
					       proxy_user, proxy_pass) != 0) {
289 290 291
			close(s);
			s = -1;
			continue;
292 293
		}
		break;
294
	}
295 296
	freeaddrinfo(res0);
	if (s < 0) {
297
		rsyserr(FERROR, errno, "failed to connect to %s", h);
Andrew Tridgell's avatar
 
Andrew Tridgell committed
298 299
		return -1;
	}
300
	return s;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
301 302 303
}


304
/* Open an outgoing socket, but allow for it to be intercepted by
305 306 307 308 309 310 311
 * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
 * socketpair rather than really opening a socket.
 *
 * We use this primarily in testing to detect TCP flow bugs, but not
 * cause security problems by really opening remote connections.
 *
 * This is based on the Samba LIBSMB_PROG feature.
312
 *
313
 * bind_addr: local address to use.  Normally NULL to get the stack default. */
314
int open_socket_out_wrapped(char *host, int port, const char *bind_addr,
315
			    int af_hint)
316
{
317
	char *prog = getenv("RSYNC_CONNECT_PROG");
318

319
	if (prog && strchr(prog, '%')) {
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
		int hlen = strlen(host);
		int len = strlen(prog) + 1;
		char *f, *t;
		for (f = prog; *f; f++) {
			if (*f != '%')
				continue;
			/* Compute more than enough room. */
			if (f[1] == '%')
				f++;
			else
				len += hlen;
		}
		f = prog;
		if (!(prog = new_array(char, len)))
			out_of_memory("open_socket_out_wrapped");
		for (t = prog; *f; f++) {
			if (*f == '%') {
				switch (*++f) {
				case '%':
					/* Just skips the extra '%'. */
					break;
				case 'H':
					memcpy(t, host, hlen);
					t += hlen;
					continue;
				default:
					f--; /* pass % through */
					break;
				}
			}
			*t++ = *f;
		}
		*t = '\0';
	}

355 356 357 358 359 360
	if (verbose >= 2) {
		rprintf(FINFO, "%sopening tcp connection to %s port %d\n",
			prog ? "Using RSYNC_CONNECT_PROG instead of " : "",
			host, port);
	}
	if (prog)
361
		return sock_exec(prog);
362
	return open_socket_out(host, port, bind_addr, af_hint);
363 364 365
}


366
/* Open one or more sockets for incoming data using the specified type,
367
 * port, and address.
368
 *
369 370
 * The getaddrinfo() call may return several address results, e.g. for
 * the machine's IPv4 and IPv6 name.
371
 *
372 373
 * We return an array of file-descriptors to the sockets, with a trailing
 * -1 value to indicate the end of the list.
374
 *
375
 * bind_addr: local address to bind, or NULL to allow it to default. */
376
static int *open_socket_in(int type, int port, const char *bind_addr,
377
			   int af_hint)
Andrew Tridgell's avatar
 
Andrew Tridgell committed
378
{
379
	int one = 1;
380
	int s, *socks, maxs, i, ecnt;
381
	struct addrinfo hints, *all_ai, *resp;
382
	char portbuf[10], **errmsgs;
383 384
	int error;

385
	memset(&hints, 0, sizeof hints);
386
	hints.ai_family = af_hint;
387 388
	hints.ai_socktype = type;
	hints.ai_flags = AI_PASSIVE;
389
	snprintf(portbuf, sizeof portbuf, "%d", port);
390
	error = getaddrinfo(bind_addr, portbuf, &hints, &all_ai);
391
	if (error) {
Martin Pool's avatar
Martin Pool committed
392
		rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
393
			bind_addr, gai_strerror(error));
394 395 396 397 398
		return NULL;
	}

	/* Count max number of sockets we might open. */
	for (maxs = 0, resp = all_ai; resp; resp = resp->ai_next, maxs++) {}
399

400 401 402
	socks = new_array(int, maxs + 1);
	errmsgs = new_array(char *, maxs);
	if (!socks || !errmsgs)
403
		out_of_memory("open_socket_in");
404

405 406 407
	/* We may not be able to create the socket, if for example the
	 * machine knows about IPv6 in the C library, but not in the
	 * kernel. */
408
	for (resp = all_ai, i = ecnt = 0; resp; resp = resp->ai_next) {
409 410 411
		s = socket(resp->ai_family, resp->ai_socktype,
			   resp->ai_protocol);

412
		if (s == -1) {
413 414 415 416 417 418
			int r = asprintf(&errmsgs[ecnt++],
				"socket(%d,%d,%d) failed: %s\n",
				(int)resp->ai_family, (int)resp->ai_socktype,
				(int)resp->ai_protocol, strerror(errno));
			if (r < 0)
				out_of_memory("open_socket_in");
419 420
			/* See if there's another address that will work... */
			continue;
421
		}
422

423 424
		setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
			   (char *)&one, sizeof one);
425 426 427 428
		if (sockopts)
			set_socket_options(s, sockopts);
		else
			set_socket_options(s, lp_socket_options());
429

430 431
#ifdef IPV6_V6ONLY
		if (resp->ai_family == AF_INET6) {
432 433 434 435 436 437
			if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
				       (char *)&one, sizeof one) < 0
			    && default_af_hint != AF_INET6) {
				close(s);
				continue;
			}
438 439 440
		}
#endif

441 442
		/* Now we've got a socket - we need to bind it. */
		if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) {
443
			/* Nope, try another */
444
			int r = asprintf(&errmsgs[ecnt++],
445 446
				"bind() failed: %s (address-family %d)\n",
				strerror(errno), (int)resp->ai_family);
447 448
			if (r < 0)
				out_of_memory("open_socket_in");
449 450
			close(s);
			continue;
451
		}
452

453
		socks[i++] = s;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
454
	}
455
	socks[i] = -1;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
456

457 458
	if (all_ai)
		freeaddrinfo(all_ai);
459

460 461 462 463
	/* Only output the socket()/bind() messages if we were totally
	 * unsuccessful, or if the daemon is being run with -vv. */
	for (s = 0; s < ecnt; s++) {
		if (!i || verbose > 1)
464
			rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]), 0);
465 466 467 468
		free(errmsgs[s]);
	}
	free(errmsgs);

469
	if (!i) {
470
		rprintf(FERROR,
471 472
			"unable to bind any inbound sockets on port %d\n",
			port);
473 474 475 476
		free(socks);
		return NULL;
	}
	return socks;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
477 478 479
}


480
/* Determine if a file descriptor is in fact a socket. */
Andrew Tridgell's avatar
 
Andrew Tridgell committed
481 482
int is_a_socket(int fd)
{
483
	int v;
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
	socklen_t l = sizeof (int);

	/* Parameters to getsockopt, setsockopt etc are very
	 * unstandardized across platforms, so don't be surprised if
	 * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
	 * It seems they all eventually get the right idea.
	 *
	 * Debian says: ``The fifth argument of getsockopt and
	 * setsockopt is in reality an int [*] (and this is what BSD
	 * 4.* and libc4 and libc5 have).  Some POSIX confusion
	 * resulted in the present socklen_t.  The draft standard has
	 * not been adopted yet, but glibc2 already follows it and
	 * also has socklen_t [*]. See also accept(2).''
	 *
	 * We now return to your regularly scheduled programming.  */
499
	return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
500 501 502
}


503 504
static RETSIGTYPE sigchld_handler(UNUSED(int val))
{
505 506 507
#ifdef WNOHANG
	while (waitpid(-1, NULL, WNOHANG) > 0) {}
#endif
508
#ifndef HAVE_SIGACTION
509
	signal(SIGCHLD, sigchld_handler);
510
#endif
511 512 513
}


514
void start_accept_loop(int port, int (*fn)(int, int))
Andrew Tridgell's avatar
 
Andrew Tridgell committed
515
{
516
	fd_set deffds;
517
	int *sp, maxfd, i;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
518

519
#ifdef HAVE_SIGACTION
520 521 522
	sigact.sa_flags = SA_NOCLDSTOP;
#endif

Andrew Tridgell's avatar
 
Andrew Tridgell committed
523
	/* open an incoming socket */
524 525
	sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
	if (sp == NULL)
526
		exit_cleanup(RERR_SOCKETIO);
Andrew Tridgell's avatar
 
Andrew Tridgell committed
527 528

	/* ready to listen */
529
	FD_ZERO(&deffds);
530 531
	for (i = 0, maxfd = -1; sp[i] >= 0; i++) {
		if (listen(sp[i], 5) < 0) {
532
			rsyserr(FERROR, errno, "listen() on socket failed");
533
#ifdef INET6
534 535
			if (errno == EADDRINUSE && i > 0) {
				rprintf(FINFO,
536
				    "Try using --ipv4 or --ipv6 to avoid this listen() error.\n");
537 538
			}
#endif
539 540 541 542 543
			exit_cleanup(RERR_SOCKETIO);
		}
		FD_SET(sp[i], &deffds);
		if (maxfd < sp[i])
			maxfd = sp[i];
Andrew Tridgell's avatar
 
Andrew Tridgell committed
544 545 546
	}

	/* now accept incoming connections - forking a new process
547
	 * for each incoming connection */
Andrew Tridgell's avatar
 
Andrew Tridgell committed
548 549
	while (1) {
		fd_set fds;
550
		pid_t pid;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
551
		int fd;
552
		struct sockaddr_storage addr;
553
		socklen_t addrlen = sizeof addr;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
554

555
		/* close log file before the potentially very long select so
556 557
		 * file can be trimmed by another process instead of growing
		 * forever */
558
		logfile_close();
559

560 561 562 563 564
#ifdef FD_COPY
		FD_COPY(&deffds, &fds);
#else
		fds = deffds;
#endif
Andrew Tridgell's avatar
 
Andrew Tridgell committed
565

566
		if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1)
567
			continue;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
568

569
		for (i = 0, fd = -1; sp[i] >= 0; i++) {
570 571 572 573 574 575
			if (FD_ISSET(sp[i], &fds)) {
				fd = accept(sp[i], (struct sockaddr *)&addr,
					    &addrlen);
				break;
			}
		}
Andrew Tridgell's avatar
 
Andrew Tridgell committed
576

577
		if (fd < 0)
578
			continue;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
579

580
		SIGACTION(SIGCHLD, sigchld_handler);
581

582
		if ((pid = fork()) == 0) {
583
			int ret;
584 585
			for (i = 0; sp[i] >= 0; i++)
				close(sp[i]);
586
			/* Re-open log file in child before possibly giving
587 588
			 * up privileges (see logfile_close() above). */
			logfile_reopen();
589 590 591
			ret = fn(fd, fd);
			close_all();
			_exit(ret);
592
		} else if (pid < 0) {
593 594
			rsyserr(FERROR, errno,
				"could not create child server process");
595 596 597 598 599
			close(fd);
			/* This might have happened because we're
			 * overloaded.  Sleep briefly before trying to
			 * accept again. */
			sleep(2);
600
		} else {
Martin Pool's avatar
Doc.  
Martin Pool committed
601
			/* Parent doesn't need this fd anymore. */
602
			close(fd);
Andrew Tridgell's avatar
 
Andrew Tridgell committed
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
		}
	}
}


enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};

struct
{
  char *name;
  int level;
  int option;
  int value;
  int opttype;
} socket_options[] = {
  {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
  {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
  {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
#ifdef TCP_NODELAY
  {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
#endif
#ifdef IPTOS_LOWDELAY
  {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
#endif
#ifdef IPTOS_THROUGHPUT
  {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
#endif
#ifdef SO_SNDBUF
  {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
#endif
#ifdef SO_RCVBUF
  {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
#endif
#ifdef SO_SNDLOWAT
  {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
#endif
#ifdef SO_RCVLOWAT
  {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
#endif
#ifdef SO_SNDTIMEO
  {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
#endif
#ifdef SO_RCVTIMEO
  {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
#endif
648 649
  {NULL,0,0,0,0}
};
650

Andrew Tridgell's avatar
 
Andrew Tridgell committed
651

652
/* Set user socket options. */
Andrew Tridgell's avatar
 
Andrew Tridgell committed
653 654 655
void set_socket_options(int fd, char *options)
{
	char *tok;
656 657 658

	if (!options || !*options)
		return;
659

Andrew Tridgell's avatar
 
Andrew Tridgell committed
660 661
	options = strdup(options);

662 663 664 665
	if (!options)
		out_of_memory("set_socket_options");

	for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
Andrew Tridgell's avatar
 
Andrew Tridgell committed
666 667 668 669 670 671 672 673 674 675 676
		int ret=0,i;
		int value = 1;
		char *p;
		int got_value = 0;

		if ((p = strchr(tok,'='))) {
			*p = 0;
			value = atoi(p+1);
			got_value = 1;
		}

677
		for (i = 0; socket_options[i].name; i++) {
Andrew Tridgell's avatar
 
Andrew Tridgell committed
678 679
			if (strcmp(socket_options[i].name,tok)==0)
				break;
680
		}
Andrew Tridgell's avatar
 
Andrew Tridgell committed
681 682 683 684 685 686 687 688 689 690

		if (!socket_options[i].name) {
			rprintf(FERROR,"Unknown socket option %s\n",tok);
			continue;
		}

		switch (socket_options[i].opttype) {
		case OPT_BOOL:
		case OPT_INT:
			ret = setsockopt(fd,socket_options[i].level,
691 692
					 socket_options[i].option,
					 (char *)&value, sizeof (int));
Andrew Tridgell's avatar
 
Andrew Tridgell committed
693
			break;
694

Andrew Tridgell's avatar
 
Andrew Tridgell committed
695 696
		case OPT_ON:
			if (got_value)
697
				rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
Andrew Tridgell's avatar
 
Andrew Tridgell committed
698 699 700 701

			{
				int on = socket_options[i].value;
				ret = setsockopt(fd,socket_options[i].level,
702 703
						 socket_options[i].option,
						 (char *)&on, sizeof (int));
Andrew Tridgell's avatar
 
Andrew Tridgell committed
704
			}
705
			break;
Andrew Tridgell's avatar
 
Andrew Tridgell committed
706
		}
707

708 709 710 711
		if (ret != 0) {
			rsyserr(FERROR, errno,
				"failed to set socket option %s", tok);
		}
Andrew Tridgell's avatar
 
Andrew Tridgell committed
712 713 714 715 716
	}

	free(options);
}

717

718 719 720 721
/* This is like socketpair but uses tcp.  The function guarantees that nobody
 * else can attach to the socket, or if they do that this function fails and
 * the socket gets closed.  Returns 0 on success, -1 on failure.  The resulting
 * file descriptors are symmetrical.  Currently only for RSYNC_CONNECT_PROG. */
722 723 724 725 726
static int socketpair_tcp(int fd[2])
{
	int listener;
	struct sockaddr_in sock;
	struct sockaddr_in sock2;
727
	socklen_t socklen = sizeof sock;
728
	int connect_done = 0;
729

730 731
	fd[0] = fd[1] = listener = -1;

732
	memset(&sock, 0, sizeof sock);
733 734 735

	if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		goto failed;
736

737
	memset(&sock2, 0, sizeof sock2);
738
#ifdef HAVE_SOCKADDR_IN_LEN
739
	sock2.sin_len = sizeof sock2;
740
#endif
741
	sock2.sin_family = PF_INET;
742
	sock2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
743

744 745 746 747
	if (bind(listener, (struct sockaddr *)&sock2, sizeof sock2) != 0
	 || listen(listener, 1) != 0
	 || getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0
	 || (fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
748
		goto failed;
749 750 751 752 753

	set_nonblocking(fd[1]);

	sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

754
	if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) == -1) {
755 756 757
		if (errno != EINPROGRESS)
			goto failed;
	} else
758 759
		connect_done = 1;

760
	if ((fd[0] = accept(listener, (struct sockaddr *)&sock2, &socklen)) == -1)
761
		goto failed;
762 763

	close(listener);
764 765 766 767
	listener = -1;

	set_blocking(fd[1]);

768
	if (connect_done == 0) {
769
		if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0
770 771
		    && errno != EISCONN)
			goto failed;
772 773 774 775 776 777
	}

	/* all OK! */
	return 0;

 failed:
778 779 780 781 782 783
	if (fd[0] != -1)
		close(fd[0]);
	if (fd[1] != -1)
		close(fd[1]);
	if (listener != -1)
		close(listener);
784 785 786 787
	return -1;
}


788 789 790
/* Run a program on a local tcp socket, so that we can talk to it's stdin and
 * stdout.  This is used to fake a connection to a daemon for testing -- not
 * for the normal case of running SSH.
Martin Pool's avatar
Doc.  
Martin Pool committed
791
 *
792 793 794
 * Retruns a socket which is attached to a subprocess running "prog". stdin and
 * stdout are attached. stderr is left attached to the original stderr. */
static int sock_exec(const char *prog)
795
{
796
	pid_t pid;
797
	int fd[2];
798

799
	if (socketpair_tcp(fd) != 0) {
800
		rsyserr(FERROR, errno, "socketpair_tcp failed");
801 802
		return -1;
	}
803 804
	if (verbose >= 2)
		rprintf(FINFO, "Running socket program: \"%s\"\n", prog);
805 806 807 808 809 810 811 812

	pid = fork();
	if (pid < 0) {
		rsyserr(FERROR, errno, "fork");
		exit_cleanup(RERR_IPC);
	}

	if (pid == 0) {
813
		close(fd[0]);
814 815 816 817 818
		if (dup2(fd[1], STDIN_FILENO) < 0
		 || dup2(fd[1], STDOUT_FILENO) < 0) {
			fprintf(stderr, "Failed to run \"%s\"\n", prog);
			exit(1);
		}
819
		exit(system(prog));
820
	}
821

822
	close(fd[1]);
823 824
	return fd[0];
}