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