Tweaked a comment.
[rsync/rsync.git] / socket.c
CommitLineData
7c1b4daa 1/* -*- c-file-style: "linux" -*-
9c07d253 2
d54765c4 3 rsync -- fast file replication program
9c07d253 4
eecd22ff 5 Copyright (C) 1992-2001 by Andrew Tridgell <tridge@samba.org>
362099a5 6 Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
9c07d253 7
bc2e93eb
AT
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
9c07d253 12
bc2e93eb
AT
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
9c07d253 17
bc2e93eb
AT
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
d5d4b282
MP
23/**
24 * @file socket.c
9c07d253 25 *
d5d4b282 26 * Socket functions used in rsync.
362099a5
MP
27 *
28 * This file is now converted to use the new-style getaddrinfo()
29 * interface, which supports IPv6 but is also supported on recent
30 * IPv4-only machines. On systems that don't have that interface, we
31 * emulate it using the KAME implementation.
d5d4b282 32 **/
bc2e93eb 33
f0fca04e
AT
34#include "rsync.h"
35
2c7d63c7
WD
36extern char *bind_address;
37extern int default_af_hint;
9a5a8673 38
44e604f4 39#ifdef HAVE_SIGACTION
2b28968d
WD
40static struct sigaction sigact;
41#endif
42
a7dc44d2
MP
43/**
44 * Establish a proxy connection on an open socket to a web proxy by
a3a84107
WD
45 * using the CONNECT method. If proxy_user and proxy_pass are not NULL,
46 * they are used to authenticate to the proxy using the "Basic"
47 * proxy-authorization protocol
a7dc44d2 48 **/
a3a84107
WD
49static int establish_proxy_connection(int fd, char *host, int port,
50 char *proxy_user, char *proxy_pass)
4c3b4b25 51{
a3a84107
WD
52 char *cp, buffer[1024];
53 char *authhdr, authbuf[1024];
54 int len;
55
56 if (proxy_user && proxy_pass) {
893c4cc0
WD
57 stringjoin(buffer, sizeof buffer,
58 proxy_user, ":", proxy_pass, NULL);
a3a84107 59 len = strlen(buffer);
4c3b4b25 60
8030b28f 61 if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
a3a84107
WD
62 rprintf(FERROR,
63 "authentication information is too long\n");
64 return -1;
65 }
66
6854bf69 67 base64_encode(buffer, len, authbuf, 1);
a3a84107
WD
68 authhdr = "\r\nProxy-Authorization: Basic ";
69 } else {
70 *authbuf = '\0';
71 authhdr = "";
72 }
73
74 snprintf(buffer, sizeof buffer, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n",
75 host, port, authhdr, authbuf);
76 len = strlen(buffer);
77 if (write(fd, buffer, len) != len) {
d62bcc17 78 rsyserr(FERROR, errno, "failed to write to proxy");
4c3b4b25
AT
79 return -1;
80 }
81
a3a84107 82 for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
4c3b4b25 83 if (read(fd, cp, 1) != 1) {
d62bcc17 84 rsyserr(FERROR, errno, "failed to read from proxy");
4c3b4b25
AT
85 return -1;
86 }
87 if (*cp == '\n')
88 break;
89 }
90
91 if (*cp != '\n')
92 cp++;
93 *cp-- = '\0';
94 if (*cp == '\r')
95 *cp = '\0';
96 if (strncmp(buffer, "HTTP/", 5) != 0) {
4ccfd96c 97 rprintf(FERROR, "bad response from proxy -- %s\n",
4c3b4b25
AT
98 buffer);
99 return -1;
100 }
9c07d253 101 for (cp = &buffer[5]; isdigit(*(uchar*)cp) || *cp == '.'; cp++) {}
4c3b4b25
AT
102 while (*cp == ' ')
103 cp++;
104 if (*cp != '2') {
4ccfd96c 105 rprintf(FERROR, "bad response from proxy -- %s\n",
4c3b4b25
AT
106 buffer);
107 return -1;
108 }
109 /* throw away the rest of the HTTP header */
110 while (1) {
a3a84107 111 for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
4c3b4b25 112 if (read(fd, cp, 1) != 1) {
d62bcc17
WD
113 rsyserr(FERROR, errno,
114 "failed to read from proxy");
4c3b4b25
AT
115 return -1;
116 }
117 if (*cp == '\n')
118 break;
119 }
9c07d253 120 if (cp > buffer && *cp == '\n')
4c3b4b25 121 cp--;
9c07d253 122 if (cp == buffer && (*cp == '\n' || *cp == '\r'))
4c3b4b25
AT
123 break;
124 }
125 return 0;
126}
127
128
f8be7d42
MP
129/**
130 * Try to set the local address for a newly-created socket. Return -1
131 * if this fails.
132 **/
e028b9ff 133int try_bind_local(int s, int ai_family, int ai_socktype,
4313d6f9 134 const char *bind_addr)
f8be7d42
MP
135{
136 int error;
137 struct addrinfo bhints, *bres_all, *r;
138
a3a84107 139 memset(&bhints, 0, sizeof bhints);
f8be7d42
MP
140 bhints.ai_family = ai_family;
141 bhints.ai_socktype = ai_socktype;
142 bhints.ai_flags = AI_PASSIVE;
4313d6f9 143 if ((error = getaddrinfo(bind_addr, NULL, &bhints, &bres_all))) {
f8be7d42 144 rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
4313d6f9 145 bind_addr, gai_strerror(error));
f8be7d42
MP
146 return -1;
147 }
148
149 for (r = bres_all; r; r = r->ai_next) {
9ec75284 150 if (bind(s, r->ai_addr, r->ai_addrlen) == -1)
f8be7d42 151 continue;
6b2d24de 152 freeaddrinfo(bres_all);
f8be7d42
MP
153 return s;
154 }
155
156 /* no error message; there might be some problem that allows
157 * creation of the socket but not binding, perhaps if the
158 * machine has no ipv6 address of this name. */
6b2d24de 159 freeaddrinfo(bres_all);
f8be7d42
MP
160 return -1;
161}
162
eecd22ff 163
d5d4b282
MP
164/**
165 * Open a socket to a tcp remote host with the specified port .
06963d0f 166 *
d5d4b282
MP
167 * Based on code from Warren. Proxy support by Stephen Rothwell.
168 * getaddrinfo() rewrite contributed by KAME.net.
06963d0f 169 *
d5d4b282
MP
170 * Now that we support IPv6 we need to look up the remote machine's
171 * address first, using @p af_hint to set a preference for the type
172 * of address. Then depending on whether it has v4 or v6 addresses we
173 * try to open a connection.
06963d0f 174 *
d5d4b282
MP
175 * The loop allows for machines with some addresses which may not be
176 * reachable, perhaps because we can't e.g. route ipv6 to that network
177 * but we can get ip4 packets through.
178 *
4313d6f9 179 * @param bind_addr Local address to use. Normally NULL to bind
d5d4b282
MP
180 * the wildcard address.
181 *
182 * @param af_hint Address family, e.g. AF_INET or AF_INET6.
06963d0f 183 **/
4313d6f9 184int open_socket_out(char *host, int port, const char *bind_addr,
d5d4b282 185 int af_hint)
bc2e93eb 186{
f0fca04e 187 int type = SOCK_STREAM;
a3a84107 188 int error, s;
06963d0f
MP
189 struct addrinfo hints, *res0, *res;
190 char portbuf[10];
a3a84107 191 char *h, *cp;
4c3b4b25
AT
192 int proxied = 0;
193 char buffer[1024];
a3a84107 194 char *proxy_user = NULL, *proxy_pass = NULL;
4c3b4b25 195
660c6fbd 196 /* if we have a RSYNC_PROXY env variable then redirect our
a3a84107 197 * connetcion via a web proxy at the given address. */
4c3b4b25 198 h = getenv("RSYNC_PROXY");
9c07d253 199 proxied = h != NULL && *h != '\0';
4c3b4b25
AT
200
201 if (proxied) {
a3a84107
WD
202 strlcpy(buffer, h, sizeof buffer);
203
204 /* Is the USER:PASS@ prefix present? */
b31c92ed 205 if ((cp = strrchr(buffer, '@')) != NULL) {
a3a84107
WD
206 *cp++ = '\0';
207 /* The remainder is the HOST:PORT part. */
208 h = cp;
209
210 if ((cp = strchr(buffer, ':')) == NULL) {
211 rprintf(FERROR,
212 "invalid proxy specification: should be USER:PASS@HOST:PORT\n");
213 return -1;
214 }
215 *cp++ = '\0';
216
217 proxy_user = buffer;
218 proxy_pass = cp;
219 } else {
220 /* The whole buffer is the HOST:PORT part. */
221 h = buffer;
222 }
223
224 if ((cp = strchr(h, ':')) == NULL) {
660c6fbd
MP
225 rprintf(FERROR,
226 "invalid proxy specification: should be HOST:PORT\n");
4c3b4b25
AT
227 return -1;
228 }
229 *cp++ = '\0';
a3a84107 230 strlcpy(portbuf, cp, sizeof portbuf);
7bea78ce
MP
231 if (verbose >= 2) {
232 rprintf(FINFO, "connection via http proxy %s port %s\n",
233 h, portbuf);
234 }
4c3b4b25 235 } else {
a3a84107 236 snprintf(portbuf, sizeof portbuf, "%d", port);
4c3b4b25 237 h = host;
4c3b4b25 238 }
f0fca04e 239
a3a84107 240 memset(&hints, 0, sizeof hints);
d5d4b282 241 hints.ai_family = af_hint;
06963d0f
MP
242 hints.ai_socktype = type;
243 error = getaddrinfo(h, portbuf, &hints, &res0);
244 if (error) {
d5d4b282
MP
245 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
246 h, portbuf, gai_strerror(error));
f0fca04e
AT
247 return -1;
248 }
249
06963d0f 250 s = -1;
2d6dbe29
MP
251 /* Try to connect to all addresses for this machine until we get
252 * through. It might e.g. be multi-homed, or have both IPv4 and IPv6
253 * addresses. We need to create a socket for each record, since the
254 * address record tells us what protocol to use to try to connect. */
06963d0f
MP
255 for (res = res0; res; res = res->ai_next) {
256 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
257 if (s < 0)
258 continue;
f0fca04e 259
4313d6f9 260 if (bind_addr
a3a84107 261 && try_bind_local(s, res->ai_family, type,
4313d6f9 262 bind_addr) == -1) {
a3a84107
WD
263 close(s);
264 s = -1;
265 continue;
266 }
06963d0f
MP
267 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
268 close(s);
269 s = -1;
270 continue;
271 }
a3a84107
WD
272 if (proxied
273 && establish_proxy_connection(s, host, port,
274 proxy_user, proxy_pass) != 0) {
06963d0f
MP
275 close(s);
276 s = -1;
277 continue;
a3a84107
WD
278 }
279 break;
4c3b4b25 280 }
06963d0f
MP
281 freeaddrinfo(res0);
282 if (s < 0) {
d62bcc17 283 rsyserr(FERROR, errno, "failed to connect to %s", h);
f0fca04e
AT
284 return -1;
285 }
06963d0f 286 return s;
f0fca04e
AT
287}
288
289
eecd22ff
MP
290/**
291 * Open an outgoing socket, but allow for it to be intercepted by
292 * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
293 * socketpair rather than really opening a socket.
294 *
295 * We use this primarily in testing to detect TCP flow bugs, but not
296 * cause security problems by really opening remote connections.
297 *
298 * This is based on the Samba LIBSMB_PROG feature.
06963d0f 299 *
4313d6f9 300 * @param bind_addr Local address to use. Normally NULL to get the stack default.
eecd22ff 301 **/
4313d6f9 302int open_socket_out_wrapped(char *host, int port, const char *bind_addr,
9c07d253 303 int af_hint)
eecd22ff 304{
df5cd107 305 char *prog = getenv("RSYNC_CONNECT_PROG");
eecd22ff 306
df5cd107
WD
307 if (verbose >= 2) {
308 rprintf(FINFO, "%sopening tcp connection to %s port %d\n",
309 prog ? "Using RSYNC_CONNECT_PROG instead of " : "",
310 host, port);
311 }
312 if (prog)
9c07d253 313 return sock_exec(prog);
4313d6f9 314 return open_socket_out(host, port, bind_addr, af_hint);
eecd22ff
MP
315}
316
317
318
06963d0f 319/**
2c7d63c7
WD
320 * Open one or more sockets for incoming data using the specified type,
321 * port, and address.
06963d0f 322 *
2c7d63c7
WD
323 * The getaddrinfo() call may return several address results, e.g. for
324 * the machine's IPv4 and IPv6 name.
9c07d253 325 *
2c7d63c7
WD
326 * We return an array of file-descriptors to the sockets, with a trailing
327 * -1 value to indicate the end of the list.
9c07d253 328 *
4313d6f9 329 * @param bind_addr Local address to bind, or NULL to allow it to
06963d0f
MP
330 * default.
331 **/
4313d6f9 332static int *open_socket_in(int type, int port, const char *bind_addr,
b0fd253a 333 int af_hint)
f0fca04e 334{
2c7d63c7 335 int one = 1;
5c6d4632 336 int s, *socks, maxs, i, ecnt;
13e29995 337 struct addrinfo hints, *all_ai, *resp;
5c6d4632 338 char portbuf[10], **errmsgs;
06963d0f
MP
339 int error;
340
a3a84107 341 memset(&hints, 0, sizeof hints);
d5d4b282 342 hints.ai_family = af_hint;
06963d0f
MP
343 hints.ai_socktype = type;
344 hints.ai_flags = AI_PASSIVE;
a3a84107 345 snprintf(portbuf, sizeof portbuf, "%d", port);
4313d6f9 346 error = getaddrinfo(bind_addr, portbuf, &hints, &all_ai);
06963d0f 347 if (error) {
7ef6aa64 348 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
4313d6f9 349 bind_addr, gai_strerror(error));
b0fd253a
WD
350 return NULL;
351 }
352
353 /* Count max number of sockets we might open. */
354 for (maxs = 0, resp = all_ai; resp; resp = resp->ai_next, maxs++) {}
2c7d63c7 355
5c6d4632
WD
356 socks = new_array(int, maxs + 1);
357 errmsgs = new_array(char *, maxs);
358 if (!socks || !errmsgs)
2c7d63c7 359 out_of_memory("open_socket_in");
06963d0f 360
13e29995
MP
361 /* We may not be able to create the socket, if for example the
362 * machine knows about IPv6 in the C library, but not in the
363 * kernel. */
5c6d4632 364 for (resp = all_ai, i = ecnt = 0; resp; resp = resp->ai_next) {
13e29995
MP
365 s = socket(resp->ai_family, resp->ai_socktype,
366 resp->ai_protocol);
367
b0fd253a 368 if (s == -1) {
5c6d4632
WD
369 int r = asprintf(&errmsgs[ecnt++],
370 "socket(%d,%d,%d) failed: %s\n",
371 (int)resp->ai_family, (int)resp->ai_socktype,
372 (int)resp->ai_protocol, strerror(errno));
373 if (r < 0)
374 out_of_memory("open_socket_in");
13e29995
MP
375 /* See if there's another address that will work... */
376 continue;
b0fd253a 377 }
9c07d253 378
13e29995
MP
379 setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
380 (char *)&one, sizeof one);
9c07d253 381
b0fd253a
WD
382#ifdef IPV6_V6ONLY
383 if (resp->ai_family == AF_INET6) {
dcd08dc5
WD
384 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
385 (char *)&one, sizeof one) < 0
386 && default_af_hint != AF_INET6) {
387 close(s);
388 continue;
389 }
b0fd253a
WD
390 }
391#endif
392
e028b9ff
WD
393 /* Now we've got a socket - we need to bind it. */
394 if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) {
13e29995 395 /* Nope, try another */
5c6d4632 396 int r = asprintf(&errmsgs[ecnt++],
e2d774cd
WD
397 "bind() failed: %s (address-family %d)\n",
398 strerror(errno), (int)resp->ai_family);
5c6d4632
WD
399 if (r < 0)
400 out_of_memory("open_socket_in");
13e29995
MP
401 close(s);
402 continue;
b8771f96 403 }
e23d790f 404
2c7d63c7 405 socks[i++] = s;
f0fca04e 406 }
2c7d63c7 407 socks[i] = -1;
f0fca04e 408
b0fd253a
WD
409 if (all_ai)
410 freeaddrinfo(all_ai);
b8771f96 411
5c6d4632
WD
412 /* Only output the socket()/bind() messages if we were totally
413 * unsuccessful, or if the daemon is being run with -vv. */
414 for (s = 0; s < ecnt; s++) {
415 if (!i || verbose > 1)
416 rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]));
417 free(errmsgs[s]);
418 }
419 free(errmsgs);
420
2c7d63c7 421 if (!i) {
b0fd253a 422 rprintf(FERROR,
2c7d63c7
WD
423 "unable to bind any inbound sockets on port %d\n",
424 port);
b0fd253a
WD
425 free(socks);
426 return NULL;
427 }
428 return socks;
f0fca04e
AT
429}
430
431
7c1b4daa
MP
432/*
433 * Determine if a file descriptor is in fact a socket
434 */
f0fca04e
AT
435int is_a_socket(int fd)
436{
ac2a1a44 437 int v;
a3a84107
WD
438 socklen_t l = sizeof (int);
439
440 /* Parameters to getsockopt, setsockopt etc are very
441 * unstandardized across platforms, so don't be surprised if
442 * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
443 * It seems they all eventually get the right idea.
444 *
445 * Debian says: ``The fifth argument of getsockopt and
446 * setsockopt is in reality an int [*] (and this is what BSD
447 * 4.* and libc4 and libc5 have). Some POSIX confusion
448 * resulted in the present socklen_t. The draft standard has
449 * not been adopted yet, but glibc2 already follows it and
450 * also has socklen_t [*]. See also accept(2).''
451 *
452 * We now return to your regularly scheduled programming. */
9c07d253 453 return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0;
f0fca04e
AT
454}
455
456
067669da
WD
457static RETSIGTYPE sigchld_handler(UNUSED(int val))
458{
ca20c7fd
WD
459#ifdef WNOHANG
460 while (waitpid(-1, NULL, WNOHANG) > 0) {}
461#endif
44e604f4 462#ifndef HAVE_SIGACTION
cb984e62 463 signal(SIGCHLD, sigchld_handler);
2b28968d 464#endif
ca20c7fd
WD
465}
466
467
39993af5 468void start_accept_loop(int port, int (*fn)(int, int))
f0fca04e 469{
b0fd253a 470 fd_set deffds;
d8d36af4 471 int *sp, maxfd, i;
f0fca04e 472
44e604f4 473#ifdef HAVE_SIGACTION
2b28968d
WD
474 sigact.sa_flags = SA_NOCLDSTOP;
475#endif
476
f0fca04e 477 /* open an incoming socket */
b0fd253a
WD
478 sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
479 if (sp == NULL)
65417579 480 exit_cleanup(RERR_SOCKETIO);
f0fca04e
AT
481
482 /* ready to listen */
b0fd253a 483 FD_ZERO(&deffds);
2c7d63c7
WD
484 for (i = 0, maxfd = -1; sp[i] >= 0; i++) {
485 if (listen(sp[i], 5) < 0) {
d62bcc17 486 rsyserr(FERROR, errno, "listen() on socket failed");
4f5b0756 487#ifdef INET6
2c7d63c7
WD
488 if (errno == EADDRINUSE && i > 0) {
489 rprintf(FINFO,
880570f2 490 "Try using --ipv4 or --ipv6 to avoid this listen() error.\n");
2c7d63c7
WD
491 }
492#endif
b0fd253a
WD
493 exit_cleanup(RERR_SOCKETIO);
494 }
495 FD_SET(sp[i], &deffds);
496 if (maxfd < sp[i])
497 maxfd = sp[i];
f0fca04e
AT
498 }
499
f0fca04e 500 /* now accept incoming connections - forking a new process
a3a84107 501 * for each incoming connection */
f0fca04e
AT
502 while (1) {
503 fd_set fds;
c4a5c57d 504 pid_t pid;
f0fca04e 505 int fd;
2d6dbe29 506 struct sockaddr_storage addr;
d54765c4 507 socklen_t addrlen = sizeof addr;
f0fca04e 508
15b84e14 509 /* close log file before the potentially very long select so
a3a84107
WD
510 * file can be trimmed by another process instead of growing
511 * forever */
8ee6adef 512 logfile_close();
45a83540 513
b0fd253a
WD
514#ifdef FD_COPY
515 FD_COPY(&deffds, &fds);
516#else
517 fds = deffds;
518#endif
f0fca04e 519
b0fd253a 520 if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1)
9c07d253 521 continue;
f0fca04e 522
2c7d63c7 523 for (i = 0, fd = -1; sp[i] >= 0; i++) {
b0fd253a
WD
524 if (FD_ISSET(sp[i], &fds)) {
525 fd = accept(sp[i], (struct sockaddr *)&addr,
526 &addrlen);
527 break;
528 }
529 }
f0fca04e 530
b0fd253a 531 if (fd < 0)
9c07d253 532 continue;
f0fca04e 533
2b28968d 534 SIGACTION(SIGCHLD, sigchld_handler);
31f440e6 535
c4a5c57d 536 if ((pid = fork()) == 0) {
9f639210 537 int ret;
2c7d63c7
WD
538 for (i = 0; sp[i] >= 0; i++)
539 close(sp[i]);
1da05366 540 /* Re-open log file in child before possibly giving
8ee6adef
WD
541 * up privileges (see logfile_close() above). */
542 logfile_reopen();
9f639210
DD
543 ret = fn(fd, fd);
544 close_all();
545 _exit(ret);
c4a5c57d 546 } else if (pid < 0) {
d62bcc17
WD
547 rsyserr(FERROR, errno,
548 "could not create child server process");
c4a5c57d
MP
549 close(fd);
550 /* This might have happened because we're
551 * overloaded. Sleep briefly before trying to
552 * accept again. */
553 sleep(2);
bd37c666 554 } else {
79845f28 555 /* Parent doesn't need this fd anymore. */
bd37c666 556 close(fd);
f0fca04e 557 }
f0fca04e 558 }
f0fca04e
AT
559}
560
561
562enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
563
564struct
565{
566 char *name;
567 int level;
568 int option;
569 int value;
570 int opttype;
571} socket_options[] = {
572 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
573 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
574 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
575#ifdef TCP_NODELAY
576 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
577#endif
578#ifdef IPTOS_LOWDELAY
579 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
580#endif
581#ifdef IPTOS_THROUGHPUT
582 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
583#endif
584#ifdef SO_SNDBUF
585 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
586#endif
587#ifdef SO_RCVBUF
588 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
589#endif
590#ifdef SO_SNDLOWAT
591 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
592#endif
593#ifdef SO_RCVLOWAT
594 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
595#endif
596#ifdef SO_SNDTIMEO
597 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
598#endif
599#ifdef SO_RCVTIMEO
600 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
601#endif
602 {NULL,0,0,0,0}};
603
9c07d253 604
f0fca04e 605
a7dc44d2
MP
606/**
607 * Set user socket options
608 **/
f0fca04e
AT
609void set_socket_options(int fd, char *options)
610{
611 char *tok;
9c07d253
WD
612
613 if (!options || !*options)
614 return;
a6801c39 615
f0fca04e 616 options = strdup(options);
f0fca04e 617
9c07d253
WD
618 if (!options)
619 out_of_memory("set_socket_options");
620
621 for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
f0fca04e
AT
622 int ret=0,i;
623 int value = 1;
624 char *p;
625 int got_value = 0;
626
627 if ((p = strchr(tok,'='))) {
628 *p = 0;
629 value = atoi(p+1);
630 got_value = 1;
631 }
632
9c07d253 633 for (i = 0; socket_options[i].name; i++) {
f0fca04e
AT
634 if (strcmp(socket_options[i].name,tok)==0)
635 break;
9c07d253 636 }
f0fca04e
AT
637
638 if (!socket_options[i].name) {
639 rprintf(FERROR,"Unknown socket option %s\n",tok);
640 continue;
641 }
642
643 switch (socket_options[i].opttype) {
644 case OPT_BOOL:
645 case OPT_INT:
646 ret = setsockopt(fd,socket_options[i].level,
a3a84107
WD
647 socket_options[i].option,
648 (char *)&value, sizeof (int));
f0fca04e 649 break;
9c07d253 650
f0fca04e
AT
651 case OPT_ON:
652 if (got_value)
4ccfd96c 653 rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
f0fca04e
AT
654
655 {
656 int on = socket_options[i].value;
657 ret = setsockopt(fd,socket_options[i].level,
a3a84107
WD
658 socket_options[i].option,
659 (char *)&on, sizeof (int));
f0fca04e 660 }
9c07d253 661 break;
f0fca04e 662 }
9c07d253 663
d62bcc17
WD
664 if (ret != 0) {
665 rsyserr(FERROR, errno,
666 "failed to set socket option %s", tok);
667 }
f0fca04e
AT
668 }
669
670 free(options);
671}
672
a7dc44d2
MP
673/**
674 * Become a daemon, discarding the controlling terminal
675 **/
f0fca04e
AT
676void become_daemon(void)
677{
b11ed3b1
AT
678 int i;
679
c46ded46 680 if (fork()) {
f0fca04e 681 _exit(0);
c46ded46 682 }
f0fca04e
AT
683
684 /* detach from the terminal */
4f5b0756 685#ifdef HAVE_SETSID
f0fca04e 686 setsid();
4f5b0756 687#elif defined TIOCNOTTY
c46ded46
AT
688 i = open("/dev/tty", O_RDWR);
689 if (i >= 0) {
9c07d253 690 ioctl(i, (int)TIOCNOTTY, (char *)0);
c46ded46 691 close(i);
f0fca04e 692 }
f0fca04e 693#endif
b11ed3b1 694 /* make sure that stdin, stdout an stderr don't stuff things
a3a84107 695 * up (library functions, for example) */
9c07d253
WD
696 for (i = 0; i < 3; i++) {
697 close(i);
b11ed3b1
AT
698 open("/dev/null", O_RDWR);
699 }
bc2e93eb 700}
ff8b29b8 701
eecd22ff 702
a7dc44d2
MP
703/**
704 * This is like socketpair but uses tcp. It is used by the Samba
705 * regression test code.
9c07d253 706 *
a7dc44d2
MP
707 * The function guarantees that nobody else can attach to the socket,
708 * or if they do that this function fails and the socket gets closed
709 * returns 0 on success, -1 on failure the resulting file descriptors
710 * are symmetrical.
711 **/
eecd22ff
MP
712static int socketpair_tcp(int fd[2])
713{
714 int listener;
715 struct sockaddr_in sock;
716 struct sockaddr_in sock2;
a3a84107 717 socklen_t socklen = sizeof sock;
eb8ffa90 718 int connect_done = 0;
9c07d253 719
eecd22ff
MP
720 fd[0] = fd[1] = listener = -1;
721
a3a84107 722 memset(&sock, 0, sizeof sock);
9c07d253
WD
723
724 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
725 goto failed;
eecd22ff 726
a3a84107 727 memset(&sock2, 0, sizeof sock2);
4f5b0756 728#ifdef HAVE_SOCKADDR_IN_LEN
a3a84107 729 sock2.sin_len = sizeof sock2;
eecd22ff 730#endif
a3a84107 731 sock2.sin_family = PF_INET;
eecd22ff 732
a3a84107 733 bind(listener, (struct sockaddr *)&sock2, sizeof sock2);
eecd22ff 734
9c07d253
WD
735 if (listen(listener, 1) != 0)
736 goto failed;
eecd22ff 737
9c07d253
WD
738 if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0)
739 goto failed;
eecd22ff 740
9c07d253
WD
741 if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
742 goto failed;
eecd22ff
MP
743
744 set_nonblocking(fd[1]);
745
746 sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
747
a3a84107 748 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) == -1) {
9c07d253
WD
749 if (errno != EINPROGRESS)
750 goto failed;
751 } else
eecd22ff 752 connect_done = 1;
eecd22ff 753
9c07d253
WD
754 if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1)
755 goto failed;
eecd22ff
MP
756
757 close(listener);
ab217f7f
WD
758 listener = -1;
759
760 set_blocking(fd[1]);
761
eecd22ff 762 if (connect_done == 0) {
a3a84107 763 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0
9c07d253
WD
764 && errno != EISCONN)
765 goto failed;
eecd22ff
MP
766 }
767
eecd22ff
MP
768 /* all OK! */
769 return 0;
770
771 failed:
9c07d253
WD
772 if (fd[0] != -1)
773 close(fd[0]);
774 if (fd[1] != -1)
775 close(fd[1]);
776 if (listener != -1)
777 close(listener);
eecd22ff
MP
778 return -1;
779}
780
781
d02984bb
MP
782
783/**
784 * Run a program on a local tcp socket, so that we can talk to it's
255810c0
MP
785 * stdin and stdout. This is used to fake a connection to a daemon
786 * for testing -- not for the normal case of running SSH.
d02984bb
MP
787 *
788 * @return a socket which is attached to a subprocess running
789 * "prog". stdin and stdout are attached. stderr is left attached to
790 * the original stderr
791 **/
eecd22ff
MP
792int sock_exec(const char *prog)
793{
794 int fd[2];
9c07d253 795
eecd22ff 796 if (socketpair_tcp(fd) != 0) {
d62bcc17 797 rsyserr(FERROR, errno, "socketpair_tcp failed");
eecd22ff
MP
798 return -1;
799 }
df5cd107
WD
800 if (verbose >= 2)
801 rprintf(FINFO, "Running socket program: \"%s\"\n", prog);
eecd22ff
MP
802 if (fork() == 0) {
803 close(fd[0]);
804 close(0);
805 close(1);
806 dup(fd[1]);
807 dup(fd[1]);
9c07d253 808 exit(system(prog));
eecd22ff 809 }
9c07d253 810 close(fd[1]);
eecd22ff
MP
811 return fd[0];
812}