1 /* $OpenBSD: socket.c,v 1.33 2022/12/26 19:16:02 jmc 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 address 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 struct pollfd pfd; 79 socklen_t optlen; 80 int c; 81 int optval; 82 83 if (*sd != -1) 84 close(*sd); 85 86 LOG2("trying: %s, %s", src->ip, host); 87 88 if ((*sd = socket(src->family, SOCK_STREAM | SOCK_NONBLOCK, 0)) 89 == -1) { 90 ERR("socket"); 91 return -1; 92 } 93 94 if (inet_bind(*sd, src->family, bsrc, bsrcsz) == -1) { 95 ERR("bind"); 96 return -1; 97 } 98 99 /* 100 * Initiate blocking connection. 101 * We use non-blocking connect() so we can poll() for contimeout. 102 */ 103 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 } 127 if (c == -1) { 128 if (errno == EADDRNOTAVAIL) 129 return 0; 130 if (errno == ECONNREFUSED || errno == EHOSTUNREACH) { 131 WARNX("connect refused: %s, %s", src->ip, host); 132 return 0; 133 } 134 ERR("connect"); 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 * 148 inet_resolve(struct sess *sess, const char *host, size_t *sz, int passive) 149 { 150 struct addrinfo hints, *res0, *res; 151 struct sockaddr *sa; 152 struct source *src = NULL; 153 const char *port = sess->opts->port; 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; 161 hints.ai_socktype = SOCK_STREAM; 162 if (passive) { 163 hints.ai_flags = SOCK_STREAM; 164 port = NULL; 165 } 166 167 error = getaddrinfo(host, port, &hints, &res0); 168 169 LOG2("resolving: %s", host); 170 171 if (error == EAI_AGAIN || error == EAI_NONAME) { 172 ERRX("could not resolve hostname %s: %s", 173 host, gai_strerror(error)); 174 return NULL; 175 } else if (error == EAI_SERVICE) { 176 ERRX("could not resolve service rsync: %s", 177 gai_strerror(error)); 178 return NULL; 179 } else if (error) { 180 ERRX("getaddrinfo: %s: %s", host, gai_strerror(error)); 181 return NULL; 182 } 183 184 /* Allocate for all available addresses. */ 185 186 for (res = res0; res != NULL; res = res->ai_next) 187 if (res->ai_family == AF_INET || 188 res->ai_family == AF_INET6) 189 srcsz++; 190 191 if (srcsz == 0) { 192 ERRX("no addresses resolved: %s", host); 193 freeaddrinfo(res0); 194 return NULL; 195 } 196 197 src = calloc(srcsz, sizeof(struct source)); 198 if (src == NULL) { 199 ERRX("calloc"); 200 freeaddrinfo(res0); 201 return NULL; 202 } 203 204 for (i = 0, res = res0; res != NULL; res = res->ai_next) { 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; 219 if (res->ai_family == AF_INET) { 220 src[i].family = PF_INET; 221 inet_ntop(AF_INET, 222 &(((struct sockaddr_in *)sa)->sin_addr), 223 src[i].ip, INET6_ADDRSTRLEN); 224 } else { 225 src[i].family = PF_INET6; 226 inet_ntop(AF_INET6, 227 &(((struct sockaddr_in6 *)sa)->sin6_addr), 228 src[i].ip, INET6_ADDRSTRLEN); 229 } 230 231 LOG2("hostname resolved: %s: %s", host, src[i].ip); 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 246 protocol_line(struct sess *sess, __attribute__((unused)) const char *host, 247 const char *cp) 248 { 249 int major, minor; 250 251 if (strncmp(cp, "@RSYNCD: ", 9)) { 252 if (sess->opts->no_motd == 0) 253 LOG1("%s", cp); 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 263 if (strcmp(cp, "OK") == 0) 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 271 if (sscanf(cp, "%d.%d", &major, &minor) == 2) { 272 sess->rver = major; 273 return 0; 274 } else if (sscanf(cp, "%d", &major) == 1) { 275 sess->rver = major; 276 return 0; 277 } 278 279 ERRX("rsyncd protocol error: unknown command"); 280 return -1; 281 } 282 283 /* 284 * Connect to a remote rsync://-enabled server sender. 285 * Returns exit code 0 on success, 1 on failure. 286 */ 287 int 288 rsync_connect(const struct opts *opts, int *sd, const struct fargs *f) 289 { 290 struct sess sess; 291 struct source *src = NULL, *bsrc = NULL; 292 size_t i, srcsz = 0, bsrcsz = 0; 293 int c, rc = 1; 294 295 if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil", 296 NULL) == -1) 297 err(ERR_IPC, "pledge"); 298 299 memset(&sess, 0, sizeof(struct sess)); 300 sess.opts = opts; 301 302 assert(f->host != NULL); 303 304 /* Resolve all IP addresses from the host. */ 305 306 if ((src = inet_resolve(&sess, f->host, &srcsz, 0)) == NULL) { 307 ERRX1("inet_resolve"); 308 exit(1); 309 } 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 } 316 317 /* Drop the DNS pledge. */ 318 319 if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw inet unveil", 320 NULL) == -1) { 321 ERR("pledge"); 322 exit(1); 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++) { 332 c = inet_connect(sd, &src[i], f->host, bsrc, bsrcsz); 333 if (c < 0) { 334 ERRX1("inet_connect"); 335 goto out; 336 } else if (c > 0) 337 break; 338 } 339 340 /* Drop the inet pledge. */ 341 if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", 342 NULL) == -1) { 343 ERR("pledge"); 344 goto out; 345 } 346 347 if (i == srcsz) { 348 ERRX("cannot connect to host: %s", f->host); 349 goto out; 350 } 351 352 LOG2("connected: %s, %s", src[i].ip, f->host); 353 354 free(src); 355 free(bsrc); 356 return 0; 357 out: 358 free(src); 359 free(bsrc); 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) 381 err(ERR_IPC, "pledge"); 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 390 args = fargs_cmdline(&sess, f, &skip); 391 392 /* Initiate with the rsyncd version and module request. */ 393 394 (void)snprintf(buf, sizeof(buf), "@RSYNCD: %d", sess.lver); 395 if (!io_write_line(&sess, sd, buf)) { 396 ERRX1("io_write_line"); 397 goto out; 398 } 399 400 LOG2("requesting module: %s, %s", f->module, f->host); 401 402 if (!io_write_line(&sess, sd, f->module)) { 403 ERRX1("io_write_line"); 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++) { 417 if (!io_read_byte(&sess, sd, &byte)) { 418 ERRX1("io_read_byte"); 419 goto out; 420 } 421 if ((buf[i] = byte) == '\n') 422 break; 423 } 424 if (i == sizeof(buf)) { 425 ERRX("line buffer overrun"); 426 goto out; 427 } else if (i == 0) 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'; 438 if (buf[i - 1] == '\r') 439 buf[i - 1] = '\0'; 440 441 if ((c = protocol_line(&sess, f->host, buf)) < 0) { 442 ERRX1("protocol_line"); 443 goto out; 444 } else if (c > 0) 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 457 for (i = skip ; args[i] != NULL; i++) 458 if (!io_write_line(&sess, sd, args[i])) { 459 ERRX1("io_write_line"); 460 goto out; 461 } 462 if (!io_write_byte(&sess, sd, '\n')) { 463 ERRX1("io_write_line"); 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 474 if (!io_read_int(&sess, sd, &sess.seed)) { 475 ERRX1("io_read_int"); 476 goto out; 477 } 478 479 /* Now we've completed the handshake. */ 480 481 if (sess.rver < sess.lver) { 482 ERRX("remote protocol is older than our own (%d < %d): " 483 "this is not supported", 484 sess.rver, sess.lver); 485 rc = 2; 486 goto out; 487 } 488 489 sess.mplex_reads = 1; 490 LOG2("read multiplexing enabled"); 491 492 LOG2("socket detected client version %d, server version %d, seed %d", 493 sess.lver, sess.rver, sess.seed); 494 495 assert(f->mode == FARGS_RECEIVER); 496 497 LOG2("client starting receiver: %s", f->host); 498 if (!rsync_receiver(&sess, sd, sd, f->sink)) { 499 ERRX1("rsync_receiver"); 500 goto out; 501 } 502 503 #if 0 504 /* Probably the EOF. */ 505 if (io_read_check(&sess, sd)) 506 WARNX("data remains in read pipe"); 507 #endif 508 509 rc = 0; 510 out: 511 free(args); 512 return rc; 513 } 514