The base4_encode() function now takes a "pad" arg which indicates
[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
173f5bf8 61 if ((len*8 + 5) / 6 >= (int)sizeof authbuf) {
a3a84107
WD
62 rprintf(FERROR,
63 "authentication information is too long\n");
64 return -1;
65 }
66
67 base64_encode(buffer, len, authbuf);
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
WD
335 int one = 1;
336 int s, *socks, maxs, i;
13e29995 337 struct addrinfo hints, *all_ai, *resp;
06963d0f
MP
338 char portbuf[10];
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
WD
355
356 if (!(socks = new_array(int, maxs + 1)))
357 out_of_memory("open_socket_in");
06963d0f 358
13e29995
MP
359 /* We may not be able to create the socket, if for example the
360 * machine knows about IPv6 in the C library, but not in the
361 * kernel. */
2c7d63c7 362 for (resp = all_ai, i = 0; resp; resp = resp->ai_next) {
13e29995
MP
363 s = socket(resp->ai_family, resp->ai_socktype,
364 resp->ai_protocol);
365
b0fd253a 366 if (s == -1) {
13e29995
MP
367 /* See if there's another address that will work... */
368 continue;
b0fd253a 369 }
9c07d253 370
13e29995
MP
371 setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
372 (char *)&one, sizeof one);
9c07d253 373
b0fd253a
WD
374#ifdef IPV6_V6ONLY
375 if (resp->ai_family == AF_INET6) {
dcd08dc5
WD
376 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
377 (char *)&one, sizeof one) < 0
378 && default_af_hint != AF_INET6) {
379 close(s);
380 continue;
381 }
b0fd253a
WD
382 }
383#endif
384
e028b9ff
WD
385 /* Now we've got a socket - we need to bind it. */
386 if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) {
13e29995
MP
387 /* Nope, try another */
388 close(s);
389 continue;
b8771f96 390 }
e23d790f 391
2c7d63c7 392 socks[i++] = s;
f0fca04e 393 }
2c7d63c7 394 socks[i] = -1;
f0fca04e 395
b0fd253a
WD
396 if (all_ai)
397 freeaddrinfo(all_ai);
b8771f96 398
2c7d63c7 399 if (!i) {
b0fd253a 400 rprintf(FERROR,
2c7d63c7
WD
401 "unable to bind any inbound sockets on port %d\n",
402 port);
b0fd253a
WD
403 free(socks);
404 return NULL;
405 }
406 return socks;
f0fca04e
AT
407}
408
409
7c1b4daa
MP
410/*
411 * Determine if a file descriptor is in fact a socket
412 */
f0fca04e
AT
413int is_a_socket(int fd)
414{
ac2a1a44 415 int v;
a3a84107
WD
416 socklen_t l = sizeof (int);
417
418 /* Parameters to getsockopt, setsockopt etc are very
419 * unstandardized across platforms, so don't be surprised if
420 * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
421 * It seems they all eventually get the right idea.
422 *
423 * Debian says: ``The fifth argument of getsockopt and
424 * setsockopt is in reality an int [*] (and this is what BSD
425 * 4.* and libc4 and libc5 have). Some POSIX confusion
426 * resulted in the present socklen_t. The draft standard has
427 * not been adopted yet, but glibc2 already follows it and
428 * also has socklen_t [*]. See also accept(2).''
429 *
430 * We now return to your regularly scheduled programming. */
9c07d253 431 return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0;
f0fca04e
AT
432}
433
434
067669da
WD
435static RETSIGTYPE sigchld_handler(UNUSED(int val))
436{
ca20c7fd
WD
437#ifdef WNOHANG
438 while (waitpid(-1, NULL, WNOHANG) > 0) {}
439#endif
44e604f4 440#ifndef HAVE_SIGACTION
cb984e62 441 signal(SIGCHLD, sigchld_handler);
2b28968d 442#endif
ca20c7fd
WD
443}
444
445
39993af5 446void start_accept_loop(int port, int (*fn)(int, int))
f0fca04e 447{
b0fd253a 448 fd_set deffds;
d8d36af4 449 int *sp, maxfd, i;
f0fca04e 450
44e604f4 451#ifdef HAVE_SIGACTION
2b28968d
WD
452 sigact.sa_flags = SA_NOCLDSTOP;
453#endif
454
f0fca04e 455 /* open an incoming socket */
b0fd253a
WD
456 sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
457 if (sp == NULL)
65417579 458 exit_cleanup(RERR_SOCKETIO);
f0fca04e
AT
459
460 /* ready to listen */
b0fd253a 461 FD_ZERO(&deffds);
2c7d63c7
WD
462 for (i = 0, maxfd = -1; sp[i] >= 0; i++) {
463 if (listen(sp[i], 5) < 0) {
d62bcc17 464 rsyserr(FERROR, errno, "listen() on socket failed");
4f5b0756 465#ifdef INET6
2c7d63c7
WD
466 if (errno == EADDRINUSE && i > 0) {
467 rprintf(FINFO,
880570f2 468 "Try using --ipv4 or --ipv6 to avoid this listen() error.\n");
2c7d63c7
WD
469 }
470#endif
b0fd253a
WD
471 exit_cleanup(RERR_SOCKETIO);
472 }
473 FD_SET(sp[i], &deffds);
474 if (maxfd < sp[i])
475 maxfd = sp[i];
f0fca04e
AT
476 }
477
f0fca04e 478 /* now accept incoming connections - forking a new process
a3a84107 479 * for each incoming connection */
f0fca04e
AT
480 while (1) {
481 fd_set fds;
c4a5c57d 482 pid_t pid;
f0fca04e 483 int fd;
2d6dbe29 484 struct sockaddr_storage addr;
d54765c4 485 socklen_t addrlen = sizeof addr;
f0fca04e 486
15b84e14 487 /* close log file before the potentially very long select so
a3a84107
WD
488 * file can be trimmed by another process instead of growing
489 * forever */
8ee6adef 490 logfile_close();
45a83540 491
b0fd253a
WD
492#ifdef FD_COPY
493 FD_COPY(&deffds, &fds);
494#else
495 fds = deffds;
496#endif
f0fca04e 497
b0fd253a 498 if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1)
9c07d253 499 continue;
f0fca04e 500
2c7d63c7 501 for (i = 0, fd = -1; sp[i] >= 0; i++) {
b0fd253a
WD
502 if (FD_ISSET(sp[i], &fds)) {
503 fd = accept(sp[i], (struct sockaddr *)&addr,
504 &addrlen);
505 break;
506 }
507 }
f0fca04e 508
b0fd253a 509 if (fd < 0)
9c07d253 510 continue;
f0fca04e 511
2b28968d 512 SIGACTION(SIGCHLD, sigchld_handler);
31f440e6 513
c4a5c57d 514 if ((pid = fork()) == 0) {
9f639210 515 int ret;
2c7d63c7
WD
516 for (i = 0; sp[i] >= 0; i++)
517 close(sp[i]);
1da05366 518 /* Re-open log file in child before possibly giving
8ee6adef
WD
519 * up privileges (see logfile_close() above). */
520 logfile_reopen();
9f639210
DD
521 ret = fn(fd, fd);
522 close_all();
523 _exit(ret);
c4a5c57d 524 } else if (pid < 0) {
d62bcc17
WD
525 rsyserr(FERROR, errno,
526 "could not create child server process");
c4a5c57d
MP
527 close(fd);
528 /* This might have happened because we're
529 * overloaded. Sleep briefly before trying to
530 * accept again. */
531 sleep(2);
bd37c666 532 } else {
79845f28 533 /* Parent doesn't need this fd anymore. */
bd37c666 534 close(fd);
f0fca04e 535 }
f0fca04e 536 }
f0fca04e
AT
537}
538
539
540enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
541
542struct
543{
544 char *name;
545 int level;
546 int option;
547 int value;
548 int opttype;
549} socket_options[] = {
550 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
551 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
552 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
553#ifdef TCP_NODELAY
554 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
555#endif
556#ifdef IPTOS_LOWDELAY
557 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
558#endif
559#ifdef IPTOS_THROUGHPUT
560 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
561#endif
562#ifdef SO_SNDBUF
563 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
564#endif
565#ifdef SO_RCVBUF
566 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
567#endif
568#ifdef SO_SNDLOWAT
569 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
570#endif
571#ifdef SO_RCVLOWAT
572 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
573#endif
574#ifdef SO_SNDTIMEO
575 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
576#endif
577#ifdef SO_RCVTIMEO
578 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
579#endif
580 {NULL,0,0,0,0}};
581
9c07d253 582
f0fca04e 583
a7dc44d2
MP
584/**
585 * Set user socket options
586 **/
f0fca04e
AT
587void set_socket_options(int fd, char *options)
588{
589 char *tok;
9c07d253
WD
590
591 if (!options || !*options)
592 return;
a6801c39 593
f0fca04e 594 options = strdup(options);
f0fca04e 595
9c07d253
WD
596 if (!options)
597 out_of_memory("set_socket_options");
598
599 for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
f0fca04e
AT
600 int ret=0,i;
601 int value = 1;
602 char *p;
603 int got_value = 0;
604
605 if ((p = strchr(tok,'='))) {
606 *p = 0;
607 value = atoi(p+1);
608 got_value = 1;
609 }
610
9c07d253 611 for (i = 0; socket_options[i].name; i++) {
f0fca04e
AT
612 if (strcmp(socket_options[i].name,tok)==0)
613 break;
9c07d253 614 }
f0fca04e
AT
615
616 if (!socket_options[i].name) {
617 rprintf(FERROR,"Unknown socket option %s\n",tok);
618 continue;
619 }
620
621 switch (socket_options[i].opttype) {
622 case OPT_BOOL:
623 case OPT_INT:
624 ret = setsockopt(fd,socket_options[i].level,
a3a84107
WD
625 socket_options[i].option,
626 (char *)&value, sizeof (int));
f0fca04e 627 break;
9c07d253 628
f0fca04e
AT
629 case OPT_ON:
630 if (got_value)
4ccfd96c 631 rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
f0fca04e
AT
632
633 {
634 int on = socket_options[i].value;
635 ret = setsockopt(fd,socket_options[i].level,
a3a84107
WD
636 socket_options[i].option,
637 (char *)&on, sizeof (int));
f0fca04e 638 }
9c07d253 639 break;
f0fca04e 640 }
9c07d253 641
d62bcc17
WD
642 if (ret != 0) {
643 rsyserr(FERROR, errno,
644 "failed to set socket option %s", tok);
645 }
f0fca04e
AT
646 }
647
648 free(options);
649}
650
a7dc44d2
MP
651/**
652 * Become a daemon, discarding the controlling terminal
653 **/
f0fca04e
AT
654void become_daemon(void)
655{
b11ed3b1
AT
656 int i;
657
c46ded46 658 if (fork()) {
f0fca04e 659 _exit(0);
c46ded46 660 }
f0fca04e
AT
661
662 /* detach from the terminal */
4f5b0756 663#ifdef HAVE_SETSID
f0fca04e 664 setsid();
4f5b0756 665#elif defined TIOCNOTTY
c46ded46
AT
666 i = open("/dev/tty", O_RDWR);
667 if (i >= 0) {
9c07d253 668 ioctl(i, (int)TIOCNOTTY, (char *)0);
c46ded46 669 close(i);
f0fca04e 670 }
f0fca04e 671#endif
b11ed3b1 672 /* make sure that stdin, stdout an stderr don't stuff things
a3a84107 673 * up (library functions, for example) */
9c07d253
WD
674 for (i = 0; i < 3; i++) {
675 close(i);
b11ed3b1
AT
676 open("/dev/null", O_RDWR);
677 }
bc2e93eb 678}
ff8b29b8 679
eecd22ff 680
a7dc44d2
MP
681/**
682 * This is like socketpair but uses tcp. It is used by the Samba
683 * regression test code.
9c07d253 684 *
a7dc44d2
MP
685 * The function guarantees that nobody else can attach to the socket,
686 * or if they do that this function fails and the socket gets closed
687 * returns 0 on success, -1 on failure the resulting file descriptors
688 * are symmetrical.
689 **/
eecd22ff
MP
690static int socketpair_tcp(int fd[2])
691{
692 int listener;
693 struct sockaddr_in sock;
694 struct sockaddr_in sock2;
a3a84107 695 socklen_t socklen = sizeof sock;
eb8ffa90 696 int connect_done = 0;
9c07d253 697
eecd22ff
MP
698 fd[0] = fd[1] = listener = -1;
699
a3a84107 700 memset(&sock, 0, sizeof sock);
9c07d253
WD
701
702 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
703 goto failed;
eecd22ff 704
a3a84107 705 memset(&sock2, 0, sizeof sock2);
4f5b0756 706#ifdef HAVE_SOCKADDR_IN_LEN
a3a84107 707 sock2.sin_len = sizeof sock2;
eecd22ff 708#endif
a3a84107 709 sock2.sin_family = PF_INET;
eecd22ff 710
a3a84107 711 bind(listener, (struct sockaddr *)&sock2, sizeof sock2);
eecd22ff 712
9c07d253
WD
713 if (listen(listener, 1) != 0)
714 goto failed;
eecd22ff 715
9c07d253
WD
716 if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0)
717 goto failed;
eecd22ff 718
9c07d253
WD
719 if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
720 goto failed;
eecd22ff
MP
721
722 set_nonblocking(fd[1]);
723
724 sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
725
a3a84107 726 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) == -1) {
9c07d253
WD
727 if (errno != EINPROGRESS)
728 goto failed;
729 } else
eecd22ff 730 connect_done = 1;
eecd22ff 731
9c07d253
WD
732 if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1)
733 goto failed;
eecd22ff
MP
734
735 close(listener);
ab217f7f
WD
736 listener = -1;
737
738 set_blocking(fd[1]);
739
eecd22ff 740 if (connect_done == 0) {
a3a84107 741 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0
9c07d253
WD
742 && errno != EISCONN)
743 goto failed;
eecd22ff
MP
744 }
745
eecd22ff
MP
746 /* all OK! */
747 return 0;
748
749 failed:
9c07d253
WD
750 if (fd[0] != -1)
751 close(fd[0]);
752 if (fd[1] != -1)
753 close(fd[1]);
754 if (listener != -1)
755 close(listener);
eecd22ff
MP
756 return -1;
757}
758
759
d02984bb
MP
760
761/**
762 * Run a program on a local tcp socket, so that we can talk to it's
255810c0
MP
763 * stdin and stdout. This is used to fake a connection to a daemon
764 * for testing -- not for the normal case of running SSH.
d02984bb
MP
765 *
766 * @return a socket which is attached to a subprocess running
767 * "prog". stdin and stdout are attached. stderr is left attached to
768 * the original stderr
769 **/
eecd22ff
MP
770int sock_exec(const char *prog)
771{
772 int fd[2];
9c07d253 773
eecd22ff 774 if (socketpair_tcp(fd) != 0) {
d62bcc17 775 rsyserr(FERROR, errno, "socketpair_tcp failed");
eecd22ff
MP
776 return -1;
777 }
df5cd107
WD
778 if (verbose >= 2)
779 rprintf(FINFO, "Running socket program: \"%s\"\n", prog);
eecd22ff
MP
780 if (fork() == 0) {
781 close(fd[0]);
782 close(0);
783 close(1);
784 dup(fd[1]);
785 dup(fd[1]);
9c07d253 786 exit(system(prog));
eecd22ff 787 }
9c07d253 788 close(fd[1]);
eecd22ff
MP
789 return fd[0];
790}