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