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