Changed the mode arg from an int to a mode_t.
[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,
4313d6f9 130 const char *bind_addr)
f8be7d42
MP
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;
4313d6f9 139 if ((error = getaddrinfo(bind_addr, NULL, &bhints, &bres_all))) {
f8be7d42 140 rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
4313d6f9 141 bind_addr, gai_strerror(error));
f8be7d42
MP
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 *
4313d6f9 175 * @param bind_addr Local address to use. Normally NULL to bind
d5d4b282
MP
176 * the wildcard address.
177 *
178 * @param af_hint Address family, e.g. AF_INET or AF_INET6.
06963d0f 179 **/
4313d6f9 180int open_socket_out(char *host, int port, const char *bind_addr,
d5d4b282 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? */
b31c92ed 201 if ((cp = strrchr(buffer, '@')) != NULL) {
a3a84107
WD
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
4313d6f9 256 if (bind_addr
a3a84107 257 && try_bind_local(s, res->ai_family, type,
4313d6f9 258 bind_addr) == -1) {
a3a84107
WD
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 295 *
4313d6f9 296 * @param bind_addr Local address to use. Normally NULL to get the stack default.
eecd22ff 297 **/
4313d6f9 298int open_socket_out_wrapped(char *host, int port, const char *bind_addr,
9c07d253 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 309 return sock_exec(prog);
4313d6f9 310 return open_socket_out(host, port, bind_addr, 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 *
4313d6f9 325 * @param bind_addr Local address to bind, or NULL to allow it to
06963d0f
MP
326 * default.
327 **/
4313d6f9 328static int *open_socket_in(int type, int port, const char *bind_addr,
b0fd253a 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);
4313d6f9 342 error = getaddrinfo(bind_addr, portbuf, &hints, &all_ai);
06963d0f 343 if (error) {
7ef6aa64 344 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
4313d6f9 345 bind_addr, 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
f0fca04e 468 /* now accept incoming connections - forking a new process
a3a84107 469 * for each incoming connection */
f0fca04e
AT
470 while (1) {
471 fd_set fds;
c4a5c57d 472 pid_t pid;
f0fca04e 473 int fd;
2d6dbe29 474 struct sockaddr_storage addr;
d54765c4 475 socklen_t addrlen = sizeof addr;
f0fca04e 476
15b84e14 477 /* close log file before the potentially very long select so
a3a84107
WD
478 * file can be trimmed by another process instead of growing
479 * forever */
8ee6adef 480 logfile_close();
45a83540 481
b0fd253a
WD
482#ifdef FD_COPY
483 FD_COPY(&deffds, &fds);
484#else
485 fds = deffds;
486#endif
f0fca04e 487
b0fd253a 488 if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1)
9c07d253 489 continue;
f0fca04e 490
2c7d63c7 491 for (i = 0, fd = -1; sp[i] >= 0; i++) {
b0fd253a
WD
492 if (FD_ISSET(sp[i], &fds)) {
493 fd = accept(sp[i], (struct sockaddr *)&addr,
494 &addrlen);
495 break;
496 }
497 }
f0fca04e 498
b0fd253a 499 if (fd < 0)
9c07d253 500 continue;
f0fca04e 501
ca20c7fd 502 signal(SIGCHLD, sigchld_handler);
31f440e6 503
c4a5c57d 504 if ((pid = fork()) == 0) {
9f639210 505 int ret;
2c7d63c7
WD
506 for (i = 0; sp[i] >= 0; i++)
507 close(sp[i]);
1da05366 508 /* Re-open log file in child before possibly giving
8ee6adef
WD
509 * up privileges (see logfile_close() above). */
510 logfile_reopen();
9f639210
DD
511 ret = fn(fd, fd);
512 close_all();
513 _exit(ret);
c4a5c57d 514 } else if (pid < 0) {
d62bcc17
WD
515 rsyserr(FERROR, errno,
516 "could not create child server process");
c4a5c57d
MP
517 close(fd);
518 /* This might have happened because we're
519 * overloaded. Sleep briefly before trying to
520 * accept again. */
521 sleep(2);
bd37c666 522 } else {
79845f28 523 /* Parent doesn't need this fd anymore. */
bd37c666 524 close(fd);
f0fca04e 525 }
f0fca04e 526 }
f0fca04e
AT
527}
528
529
530enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
531
532struct
533{
534 char *name;
535 int level;
536 int option;
537 int value;
538 int opttype;
539} socket_options[] = {
540 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
541 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
542 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
543#ifdef TCP_NODELAY
544 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
545#endif
546#ifdef IPTOS_LOWDELAY
547 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
548#endif
549#ifdef IPTOS_THROUGHPUT
550 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
551#endif
552#ifdef SO_SNDBUF
553 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
554#endif
555#ifdef SO_RCVBUF
556 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
557#endif
558#ifdef SO_SNDLOWAT
559 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
560#endif
561#ifdef SO_RCVLOWAT
562 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
563#endif
564#ifdef SO_SNDTIMEO
565 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
566#endif
567#ifdef SO_RCVTIMEO
568 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
569#endif
570 {NULL,0,0,0,0}};
571
9c07d253 572
f0fca04e 573
a7dc44d2
MP
574/**
575 * Set user socket options
576 **/
f0fca04e
AT
577void set_socket_options(int fd, char *options)
578{
579 char *tok;
9c07d253
WD
580
581 if (!options || !*options)
582 return;
a6801c39 583
f0fca04e 584 options = strdup(options);
f0fca04e 585
9c07d253
WD
586 if (!options)
587 out_of_memory("set_socket_options");
588
589 for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
f0fca04e
AT
590 int ret=0,i;
591 int value = 1;
592 char *p;
593 int got_value = 0;
594
595 if ((p = strchr(tok,'='))) {
596 *p = 0;
597 value = atoi(p+1);
598 got_value = 1;
599 }
600
9c07d253 601 for (i = 0; socket_options[i].name; i++) {
f0fca04e
AT
602 if (strcmp(socket_options[i].name,tok)==0)
603 break;
9c07d253 604 }
f0fca04e
AT
605
606 if (!socket_options[i].name) {
607 rprintf(FERROR,"Unknown socket option %s\n",tok);
608 continue;
609 }
610
611 switch (socket_options[i].opttype) {
612 case OPT_BOOL:
613 case OPT_INT:
614 ret = setsockopt(fd,socket_options[i].level,
a3a84107
WD
615 socket_options[i].option,
616 (char *)&value, sizeof (int));
f0fca04e 617 break;
9c07d253 618
f0fca04e
AT
619 case OPT_ON:
620 if (got_value)
4ccfd96c 621 rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
f0fca04e
AT
622
623 {
624 int on = socket_options[i].value;
625 ret = setsockopt(fd,socket_options[i].level,
a3a84107
WD
626 socket_options[i].option,
627 (char *)&on, sizeof (int));
f0fca04e 628 }
9c07d253 629 break;
f0fca04e 630 }
9c07d253 631
d62bcc17
WD
632 if (ret != 0) {
633 rsyserr(FERROR, errno,
634 "failed to set socket option %s", tok);
635 }
f0fca04e
AT
636 }
637
638 free(options);
639}
640
a7dc44d2
MP
641/**
642 * Become a daemon, discarding the controlling terminal
643 **/
f0fca04e
AT
644void become_daemon(void)
645{
b11ed3b1
AT
646 int i;
647
c46ded46 648 if (fork()) {
f0fca04e 649 _exit(0);
c46ded46 650 }
f0fca04e
AT
651
652 /* detach from the terminal */
4f5b0756 653#ifdef HAVE_SETSID
f0fca04e 654 setsid();
4f5b0756 655#elif defined TIOCNOTTY
c46ded46
AT
656 i = open("/dev/tty", O_RDWR);
657 if (i >= 0) {
9c07d253 658 ioctl(i, (int)TIOCNOTTY, (char *)0);
c46ded46 659 close(i);
f0fca04e 660 }
f0fca04e 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;
eb8ffa90 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);
4f5b0756 696#ifdef 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);
ab217f7f
WD
726 listener = -1;
727
728 set_blocking(fd[1]);
729
eecd22ff 730 if (connect_done == 0) {
a3a84107 731 if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0
9c07d253
WD
732 && errno != EISCONN)
733 goto failed;
eecd22ff
MP
734 }
735
eecd22ff
MP
736 /* all OK! */
737 return 0;
738
739 failed:
9c07d253
WD
740 if (fd[0] != -1)
741 close(fd[0]);
742 if (fd[1] != -1)
743 close(fd[1]);
744 if (listener != -1)
745 close(listener);
eecd22ff
MP
746 return -1;
747}
748
749
d02984bb
MP
750
751/**
752 * Run a program on a local tcp socket, so that we can talk to it's
255810c0
MP
753 * stdin and stdout. This is used to fake a connection to a daemon
754 * for testing -- not for the normal case of running SSH.
d02984bb
MP
755 *
756 * @return a socket which is attached to a subprocess running
757 * "prog". stdin and stdout are attached. stderr is left attached to
758 * the original stderr
759 **/
eecd22ff
MP
760int sock_exec(const char *prog)
761{
762 int fd[2];
9c07d253 763
eecd22ff 764 if (socketpair_tcp(fd) != 0) {
d62bcc17 765 rsyserr(FERROR, errno, "socketpair_tcp failed");
eecd22ff
MP
766 return -1;
767 }
df5cd107
WD
768 if (verbose >= 2)
769 rprintf(FINFO, "Running socket program: \"%s\"\n", prog);
eecd22ff
MP
770 if (fork() == 0) {
771 close(fd[0]);
772 close(0);
773 close(1);
774 dup(fd[1]);
775 dup(fd[1]);
9c07d253 776 exit(system(prog));
eecd22ff 777 }
9c07d253 778 close(fd[1]);
eecd22ff
MP
779 return fd[0];
780}