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