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