Annotation of src/usr.bin/rsync/socket.c, Revision 1.32
1.32 ! job 1: /* $OpenBSD: socket.c,v 1.31 2021/06/30 13:10:04 claudio Exp $ */
1.1 benno 2: /*
3: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/stat.h>
18: #include <sys/socket.h>
19: #include <arpa/inet.h>
20: #include <netinet/in.h>
21:
22: #include <assert.h>
23: #include <ctype.h>
24: #include <errno.h>
25: #include <fcntl.h>
26: #include <inttypes.h>
27: #include <netdb.h>
28: #include <poll.h>
29: #include <resolv.h>
30: #include <stdlib.h>
31: #include <string.h>
32: #include <unistd.h>
1.19 deraadt 33: #include <err.h>
1.1 benno 34:
35: #include "extern.h"
36:
37: /*
38: * Defines a resolved IP address for the host
39: * There can be many, IPV4 or IPV6.
40: */
41: struct source {
1.2 benno 42: int family; /* PF_INET or PF_INET6 */
43: char ip[INET6_ADDRSTRLEN]; /* formatted string */
1.1 benno 44: struct sockaddr_storage sa; /* socket */
45: socklen_t salen; /* length of socket buffer */
46: };
47:
48: /*
1.26 claudio 49: * Try to bind to a local IP address matching the addres family passed.
50: * Return -1 on failure to bind to any address, 0 on success.
51: */
52: static int
53: inet_bind(int s, sa_family_t af, const struct source *bsrc, size_t bsrcsz)
54: {
55: size_t i;
56:
57: if (bsrc == NULL)
58: return 0;
59: for (i = 0; i < bsrcsz; i++) {
60: if (bsrc[i].family != af)
61: continue;
62: if (bind(s, (const struct sockaddr *)&bsrc[i].sa,
63: bsrc[i].salen) == -1)
64: continue;
65: return 0;
66: }
67: return -1;
68: }
69:
70: /*
1.1 benno 71: * Connect to an IP address representing a host.
72: * Return <0 on failure, 0 on try another address, >0 on success.
73: */
74: static int
1.26 claudio 75: inet_connect(int *sd, const struct source *src, const char *host,
76: const struct source *bsrc, size_t bsrcsz)
1.1 benno 77: {
1.32 ! job 78: struct pollfd pfd;
! 79: socklen_t optlen;
! 80: int c;
! 81: int optval;
1.1 benno 82:
1.5 deraadt 83: if (*sd != -1)
1.1 benno 84: close(*sd);
85:
1.23 benno 86: LOG2("trying: %s, %s", src->ip, host);
1.1 benno 87:
1.32 ! job 88: if ((*sd = socket(src->family, SOCK_STREAM | SOCK_NONBLOCK, 0))
! 89: == -1) {
1.23 benno 90: ERR("socket");
1.1 benno 91: return -1;
92: }
93:
1.26 claudio 94: if (inet_bind(*sd, src->family, bsrc, bsrcsz) == -1) {
95: ERR("bind");
96: return -1;
97: }
98:
1.1 benno 99: /*
100: * Initiate blocking connection.
1.32 ! job 101: * We use non-blocking connect() so we can poll() for contimeout.
1.1 benno 102: */
103:
1.32 ! job 104: if ((c = connect(*sd, (const struct sockaddr *)&src->sa, src->salen))
! 105: != 0 && errno == EINPROGRESS) {
! 106: pfd.fd = *sd;
! 107: pfd.events = POLLOUT;
! 108: switch (c = poll(&pfd, 1, poll_contimeout)) {
! 109: case 1:
! 110: optlen = sizeof(optval);
! 111: if ((c = getsockopt(*sd, SOL_SOCKET, SO_ERROR, &optval,
! 112: &optlen)) == 0) {
! 113: errno = optval;
! 114: if (optval != 0)
! 115: c = -1;
! 116: }
! 117: break;
! 118: case 0:
! 119: errno = ETIMEDOUT;
! 120: WARNX("connect timeout: %s, %s", src->ip, host);
! 121: return 0;
! 122: default:
! 123: ERR("poll failed");
! 124: return -1;
! 125: }
! 126: }
1.5 deraadt 127: if (c == -1) {
1.28 kn 128: if (errno == EADDRNOTAVAIL)
129: return 0;
1.5 deraadt 130: if (errno == ECONNREFUSED || errno == EHOSTUNREACH) {
1.32 ! job 131: WARNX("connect refused: %s, %s", src->ip, host);
1.1 benno 132: return 0;
133: }
1.23 benno 134: ERR("connect");
1.1 benno 135: return -1;
136: }
137:
138: return 1;
139: }
140:
141: /*
142: * Resolve the socket addresses for host, both in IPV4 and IPV6.
143: * Once completed, the "dns" pledge may be dropped.
144: * Returns the addresses on success, NULL on failure (sz is always zero,
145: * in this case).
146: */
147: static struct source *
1.26 claudio 148: inet_resolve(struct sess *sess, const char *host, size_t *sz, int passive)
1.1 benno 149: {
150: struct addrinfo hints, *res0, *res;
151: struct sockaddr *sa;
152: struct source *src = NULL;
1.26 claudio 153: const char *port = sess->opts->port;
1.1 benno 154: size_t i, srcsz = 0;
155: int error;
156:
157: *sz = 0;
158:
159: memset(&hints, 0, sizeof(hints));
160: hints.ai_family = PF_UNSPEC;
1.12 deraadt 161: hints.ai_socktype = SOCK_STREAM;
1.26 claudio 162: if (passive) {
163: hints.ai_flags = SOCK_STREAM;
164: port = NULL;
165: }
1.1 benno 166:
1.26 claudio 167: error = getaddrinfo(host, port, &hints, &res0);
1.1 benno 168:
1.23 benno 169: LOG2("resolving: %s", host);
1.1 benno 170:
171: if (error == EAI_AGAIN || error == EAI_NONAME) {
1.23 benno 172: ERRX("could not resolve hostname %s: %s",
1.7 deraadt 173: host, gai_strerror(error));
1.1 benno 174: return NULL;
1.10 deraadt 175: } else if (error == EAI_SERVICE) {
1.23 benno 176: ERRX("could not resolve service rsync: %s",
1.18 benno 177: gai_strerror(error));
1.10 deraadt 178: return NULL;
1.1 benno 179: } else if (error) {
1.23 benno 180: ERRX("getaddrinfo: %s: %s", host, gai_strerror(error));
1.1 benno 181: return NULL;
182: }
183:
184: /* Allocate for all available addresses. */
185:
1.14 deraadt 186: for (res = res0; res != NULL; res = res->ai_next)
1.1 benno 187: if (res->ai_family == AF_INET ||
188: res->ai_family == AF_INET6)
189: srcsz++;
190:
1.5 deraadt 191: if (srcsz == 0) {
1.23 benno 192: ERRX("no addresses resolved: %s", host);
1.1 benno 193: freeaddrinfo(res0);
194: return NULL;
195: }
196:
197: src = calloc(srcsz, sizeof(struct source));
1.5 deraadt 198: if (src == NULL) {
1.23 benno 199: ERRX("calloc");
1.1 benno 200: freeaddrinfo(res0);
201: return NULL;
202: }
203:
1.14 deraadt 204: for (i = 0, res = res0; res != NULL; res = res->ai_next) {
1.1 benno 205: if (res->ai_family != AF_INET &&
206: res->ai_family != AF_INET6)
207: continue;
208:
209: assert(i < srcsz);
210:
211: /* Copy the socket address. */
212:
213: src[i].salen = res->ai_addrlen;
214: memcpy(&src[i].sa, res->ai_addr, src[i].salen);
215:
216: /* Format as a string, too. */
217:
218: sa = res->ai_addr;
1.5 deraadt 219: if (res->ai_family == AF_INET) {
1.1 benno 220: src[i].family = PF_INET;
221: inet_ntop(AF_INET,
1.7 deraadt 222: &(((struct sockaddr_in *)sa)->sin_addr),
223: src[i].ip, INET6_ADDRSTRLEN);
1.1 benno 224: } else {
225: src[i].family = PF_INET6;
226: inet_ntop(AF_INET6,
1.7 deraadt 227: &(((struct sockaddr_in6 *)sa)->sin6_addr),
228: src[i].ip, INET6_ADDRSTRLEN);
1.1 benno 229: }
1.2 benno 230:
1.23 benno 231: LOG2("hostname resolved: %s: %s", host, src[i].ip);
1.1 benno 232: i++;
233: }
234:
235: freeaddrinfo(res0);
236: *sz = srcsz;
237: return src;
238: }
239:
240: /*
241: * Process an rsyncd preamble line.
242: * This is either free-form text or @RSYNCD commands.
243: * Return <0 on failure, 0 on try more lines, >0 on finished.
244: */
245: static int
1.24 benno 246: protocol_line(struct sess *sess, __attribute__((unused)) const char *host,
247: const char *cp)
1.1 benno 248: {
249: int major, minor;
250:
251: if (strncmp(cp, "@RSYNCD: ", 9)) {
1.29 job 252: if (sess->opts->no_motd == 0)
253: LOG1("%s", cp);
1.1 benno 254: return 0;
255: }
256:
257: cp += 9;
258: while (isspace((unsigned char)*cp))
259: cp++;
260:
261: /* @RSYNCD: OK indicates that we're finished. */
262:
1.5 deraadt 263: if (strcmp(cp, "OK") == 0)
1.1 benno 264: return 1;
265:
266: /*
267: * Otherwise, all we have left is our version.
268: * There are two formats: x.y (w/submodule) and x.
269: */
270:
1.5 deraadt 271: if (sscanf(cp, "%d.%d", &major, &minor) == 2) {
1.1 benno 272: sess->rver = major;
273: return 0;
1.5 deraadt 274: } else if (sscanf(cp, "%d", &major) == 1) {
1.1 benno 275: sess->rver = major;
276: return 0;
277: }
278:
1.23 benno 279: ERRX("rsyncd protocol error: unknown command");
1.1 benno 280: return -1;
281: }
282:
283: /*
1.22 naddy 284: * Connect to a remote rsync://-enabled server sender.
285: * Returns exit code 0 on success, 1 on failure.
1.1 benno 286: */
287: int
1.22 naddy 288: rsync_connect(const struct opts *opts, int *sd, const struct fargs *f)
1.1 benno 289: {
290: struct sess sess;
1.26 claudio 291: struct source *src = NULL, *bsrc = NULL;
292: size_t i, srcsz = 0, bsrcsz = 0;
1.22 naddy 293: int c, rc = 1;
1.1 benno 294:
1.19 deraadt 295: if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil",
296: NULL) == -1)
1.30 claudio 297: err(ERR_IPC, "pledge");
1.19 deraadt 298:
1.1 benno 299: memset(&sess, 0, sizeof(struct sess));
300: sess.opts = opts;
301:
1.5 deraadt 302: assert(f->host != NULL);
1.1 benno 303:
304: /* Resolve all IP addresses from the host. */
305:
1.26 claudio 306: if ((src = inet_resolve(&sess, f->host, &srcsz, 0)) == NULL) {
1.23 benno 307: ERRX1("inet_resolve");
1.19 deraadt 308: exit(1);
1.1 benno 309: }
1.26 claudio 310: if (opts->address != NULL)
311: if ((bsrc = inet_resolve(&sess, opts->address, &bsrcsz, 1)) ==
312: NULL) {
313: ERRX1("inet_resolve bind");
314: exit(1);
315: }
1.1 benno 316:
317: /* Drop the DNS pledge. */
318:
1.19 deraadt 319: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw inet unveil",
320: NULL) == -1) {
1.23 benno 321: ERR("pledge");
1.19 deraadt 322: exit(1);
1.1 benno 323: }
324:
325: /*
326: * Iterate over all addresses, trying to connect.
327: * When we succeed, then continue using the connected socket.
328: */
329:
330: assert(srcsz);
331: for (i = 0; i < srcsz; i++) {
1.26 claudio 332: c = inet_connect(sd, &src[i], f->host, bsrc, bsrcsz);
1.20 deraadt 333: if (c < 0) {
1.23 benno 334: ERRX1("inet_connect");
1.1 benno 335: goto out;
1.20 deraadt 336: } else if (c > 0)
1.1 benno 337: break;
338: }
339:
340: /* Drop the inet pledge. */
1.19 deraadt 341: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil",
342: NULL) == -1) {
1.23 benno 343: ERR("pledge");
1.1 benno 344: goto out;
345: }
346:
347: if (i == srcsz) {
1.23 benno 348: ERRX("cannot connect to host: %s", f->host);
1.1 benno 349: goto out;
350: }
351:
1.23 benno 352: LOG2("connected: %s, %s", src[i].ip, f->host);
1.22 naddy 353:
354: free(src);
1.26 claudio 355: free(bsrc);
1.22 naddy 356: return 0;
357: out:
358: free(src);
1.26 claudio 359: free(bsrc);
1.22 naddy 360: if (*sd != -1)
361: close(*sd);
362: return rc;
363: }
364:
365: /*
366: * Talk to a remote rsync://-enabled server sender.
367: * Returns exit code 0 on success, 1 on failure, 2 on failure with
368: * incompatible protocols.
369: */
370: int
371: rsync_socket(const struct opts *opts, int sd, const struct fargs *f)
372: {
373: struct sess sess;
374: size_t i, skip;
375: int c, rc = 1;
376: char **args, buf[BUFSIZ];
377: uint8_t byte;
378:
379: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil",
380: NULL) == -1)
1.30 claudio 381: err(ERR_IPC, "pledge");
1.22 naddy 382:
383: memset(&sess, 0, sizeof(struct sess));
384: sess.lver = RSYNC_PROTOCOL;
385: sess.opts = opts;
386:
387: assert(f->host != NULL);
388: assert(f->module != NULL);
389:
1.30 claudio 390: args = fargs_cmdline(&sess, f, &skip);
1.22 naddy 391:
1.1 benno 392: /* Initiate with the rsyncd version and module request. */
393:
394: (void)snprintf(buf, sizeof(buf), "@RSYNCD: %d", sess.lver);
1.4 deraadt 395: if (!io_write_line(&sess, sd, buf)) {
1.23 benno 396: ERRX1("io_write_line");
1.1 benno 397: goto out;
398: }
399:
1.23 benno 400: LOG2("requesting module: %s, %s", f->module, f->host);
1.1 benno 401:
1.4 deraadt 402: if (!io_write_line(&sess, sd, f->module)) {
1.23 benno 403: ERRX1("io_write_line");
1.1 benno 404: goto out;
405: }
406:
407: /*
408: * Now we read the server's response, byte-by-byte, one newline
409: * terminated at a time, limited to BUFSIZ line length.
410: * For this protocol version, this consists of either @RSYNCD
411: * followed by some text (just "ok" and the remote version) or
412: * the message of the day.
413: */
414:
415: for (;;) {
416: for (i = 0; i < sizeof(buf); i++) {
1.4 deraadt 417: if (!io_read_byte(&sess, sd, &byte)) {
1.23 benno 418: ERRX1("io_read_byte");
1.1 benno 419: goto out;
420: }
1.5 deraadt 421: if ((buf[i] = byte) == '\n')
1.1 benno 422: break;
423: }
424: if (i == sizeof(buf)) {
1.23 benno 425: ERRX("line buffer overrun");
1.1 benno 426: goto out;
1.5 deraadt 427: } else if (i == 0)
1.1 benno 428: continue;
429:
430: /*
431: * The rsyncd protocol isn't very clear as to whether we
432: * get a CRLF or not: I don't actually see this being
433: * transmitted over the wire.
434: */
435:
436: assert(i > 0);
437: buf[i] = '\0';
1.5 deraadt 438: if (buf[i - 1] == '\r')
1.1 benno 439: buf[i - 1] = '\0';
440:
1.20 deraadt 441: if ((c = protocol_line(&sess, f->host, buf)) < 0) {
1.23 benno 442: ERRX1("protocol_line");
1.1 benno 443: goto out;
1.20 deraadt 444: } else if (c > 0)
1.1 benno 445: break;
446: }
447:
448: /*
449: * Now we've exchanged all of our protocol information.
450: * We want to send our command-line arguments over the wire,
451: * each with a newline termination.
452: * Use the same arguments when invoking the server, but leave
453: * off the binary name(s).
454: * Emit a standalone newline afterward.
455: */
456:
1.22 naddy 457: for (i = skip ; args[i] != NULL; i++)
1.4 deraadt 458: if (!io_write_line(&sess, sd, args[i])) {
1.23 benno 459: ERRX1("io_write_line");
1.1 benno 460: goto out;
461: }
1.4 deraadt 462: if (!io_write_byte(&sess, sd, '\n')) {
1.23 benno 463: ERRX1("io_write_line");
1.1 benno 464: goto out;
465: }
466:
467: /*
468: * All data after this point is going to be multiplexed, so turn
469: * on the multiplexer for our reads and writes.
470: */
471:
472: /* Protocol exchange: get the random seed. */
473:
1.4 deraadt 474: if (!io_read_int(&sess, sd, &sess.seed)) {
1.23 benno 475: ERRX1("io_read_int");
1.1 benno 476: goto out;
477: }
478:
479: /* Now we've completed the handshake. */
480:
481: if (sess.rver < sess.lver) {
1.23 benno 482: ERRX("remote protocol is older than our own (%d < %d): "
1.21 deraadt 483: "this is not supported",
484: sess.rver, sess.lver);
1.20 deraadt 485: rc = 2;
1.1 benno 486: goto out;
487: }
488:
489: sess.mplex_reads = 1;
1.23 benno 490: LOG2("read multiplexing enabled");
1.1 benno 491:
1.23 benno 492: LOG2("socket detected client version %d, server version %d, seed %d",
1.21 deraadt 493: sess.lver, sess.rver, sess.seed);
1.1 benno 494:
1.5 deraadt 495: assert(f->mode == FARGS_RECEIVER);
1.1 benno 496:
1.23 benno 497: LOG2("client starting receiver: %s", f->host);
1.4 deraadt 498: if (!rsync_receiver(&sess, sd, sd, f->sink)) {
1.23 benno 499: ERRX1("rsync_receiver");
1.1 benno 500: goto out;
501: }
502:
503: #if 0
504: /* Probably the EOF. */
505: if (io_read_check(&sess, sd))
1.23 benno 506: WARNX("data remains in read pipe");
1.1 benno 507: #endif
508:
1.20 deraadt 509: rc = 0;
1.1 benno 510: out:
511: free(args);
512: return rc;
513: }