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