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