Made log_open() non-static and moved log_close() next to it.
[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) {
4ccfd96c 93 rprintf(FERROR, "bad response from proxy -- %s\n",
4c3b4b25
AT
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') {
4ccfd96c 101 rprintf(FERROR, "bad response from proxy -- %s\n",
4c3b4b25
AT
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");
4f5b0756 455#ifdef INET6
2c7d63c7
WD
456 if (errno == EADDRINUSE && i > 0) {
457 rprintf(FINFO,
880570f2 458 "Try using --ipv4 or --ipv6 to avoid this listen() error.\n");
2c7d63c7
WD
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]);
9f639210
DD
509 ret = fn(fd, fd);
510 close_all();
511 _exit(ret);
c4a5c57d 512 } else if (pid < 0) {
d62bcc17
WD
513 rsyserr(FERROR, errno,
514 "could not create child server process");
c4a5c57d
MP
515 close(fd);
516 /* This might have happened because we're
517 * overloaded. Sleep briefly before trying to
518 * accept again. */
519 sleep(2);
bd37c666 520 } else {
79845f28 521 /* Parent doesn't need this fd anymore. */
bd37c666 522 close(fd);
f0fca04e 523 }
f0fca04e 524 }
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)
4ccfd96c 619 rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
f0fca04e
AT
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 */
4f5b0756 651#ifdef HAVE_SETSID
f0fca04e 652 setsid();
4f5b0756 653#elif defined TIOCNOTTY
c46ded46
AT
654 i = open("/dev/tty", O_RDWR);
655 if (i >= 0) {
9c07d253 656 ioctl(i, (int)TIOCNOTTY, (char *)0);
c46ded46 657 close(i);
f0fca04e 658 }
f0fca04e 659#endif
b11ed3b1 660 /* make sure that stdin, stdout an stderr don't stuff things
a3a84107 661 * up (library functions, for example) */
9c07d253
WD
662 for (i = 0; i < 3; i++) {
663 close(i);
b11ed3b1
AT
664 open("/dev/null", O_RDWR);
665 }
bc2e93eb 666}
ff8b29b8 667
eecd22ff 668
a7dc44d2
MP
669/**
670 * This is like socketpair but uses tcp. It is used by the Samba
671 * regression test code.
9c07d253 672 *
a7dc44d2
MP
673 * The function guarantees that nobody else can attach to the socket,
674 * or if they do that this function fails and the socket gets closed
675 * returns 0 on success, -1 on failure the resulting file descriptors
676 * are symmetrical.
677 **/
eecd22ff
MP
678static int socketpair_tcp(int fd[2])
679{
680 int listener;
681 struct sockaddr_in sock;
682 struct sockaddr_in sock2;
a3a84107 683 socklen_t socklen = sizeof sock;
eb8ffa90 684 int connect_done = 0;
9c07d253 685
eecd22ff
MP
686 fd[0] = fd[1] = listener = -1;
687
a3a84107 688 memset(&sock, 0, sizeof sock);
9c07d253
WD
689
690 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
691 goto failed;
eecd22ff 692
a3a84107 693 memset(&sock2, 0, sizeof sock2);
4f5b0756 694#ifdef HAVE_SOCKADDR_IN_LEN
a3a84107 695 sock2.sin_len = sizeof sock2;
eecd22ff 696#endif
a3a84107 697 sock2.sin_family = PF_INET;
eecd22ff 698
a3a84107 699 bind(listener, (struct sockaddr *)&sock2, sizeof sock2);
eecd22ff 700
9c07d253
WD
701 if (listen(listener, 1) != 0)
702 goto failed;
eecd22ff 703
9c07d253
WD
704 if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0)
705 goto failed;
eecd22ff 706
9c07d253
WD
707 if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
708 goto failed;
eecd22ff
MP
709
710 set_nonblocking(fd[1]);
711
712 sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
713
a3a84107 714 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) == -1) {
9c07d253
WD
715 if (errno != EINPROGRESS)
716 goto failed;
717 } else
eecd22ff 718 connect_done = 1;
eecd22ff 719
9c07d253
WD
720 if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1)
721 goto failed;
eecd22ff
MP
722
723 close(listener);
ab217f7f
WD
724 listener = -1;
725
726 set_blocking(fd[1]);
727
eecd22ff 728 if (connect_done == 0) {
a3a84107 729 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0
9c07d253
WD
730 && errno != EISCONN)
731 goto failed;
eecd22ff
MP
732 }
733
eecd22ff
MP
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}