1*6af255d5Sjsg /* $OpenBSD: socket.c,v 1.34 2024/10/13 03:35:59 jsg Exp $ */ 260a32ee9Sbenno /* 360a32ee9Sbenno * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 460a32ee9Sbenno * 560a32ee9Sbenno * Permission to use, copy, modify, and distribute this software for any 660a32ee9Sbenno * purpose with or without fee is hereby granted, provided that the above 760a32ee9Sbenno * copyright notice and this permission notice appear in all copies. 860a32ee9Sbenno * 960a32ee9Sbenno * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1060a32ee9Sbenno * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1160a32ee9Sbenno * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1260a32ee9Sbenno * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1360a32ee9Sbenno * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1460a32ee9Sbenno * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1560a32ee9Sbenno * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1660a32ee9Sbenno */ 1760a32ee9Sbenno #include <sys/stat.h> 1860a32ee9Sbenno #include <sys/socket.h> 1960a32ee9Sbenno #include <arpa/inet.h> 2060a32ee9Sbenno #include <netinet/in.h> 2160a32ee9Sbenno 2260a32ee9Sbenno #include <assert.h> 2360a32ee9Sbenno #include <ctype.h> 2460a32ee9Sbenno #include <errno.h> 2560a32ee9Sbenno #include <fcntl.h> 2660a32ee9Sbenno #include <inttypes.h> 2760a32ee9Sbenno #include <netdb.h> 2860a32ee9Sbenno #include <poll.h> 2960a32ee9Sbenno #include <resolv.h> 3060a32ee9Sbenno #include <stdlib.h> 3160a32ee9Sbenno #include <string.h> 3260a32ee9Sbenno #include <unistd.h> 33ef859540Sderaadt #include <err.h> 34*6af255d5Sjsg #include <stdio.h> 3560a32ee9Sbenno 3660a32ee9Sbenno #include "extern.h" 3760a32ee9Sbenno 3860a32ee9Sbenno /* 3960a32ee9Sbenno * Defines a resolved IP address for the host 4060a32ee9Sbenno * There can be many, IPV4 or IPV6. 4160a32ee9Sbenno */ 4260a32ee9Sbenno struct source { 4360a32ee9Sbenno int family; /* PF_INET or PF_INET6 */ 4460a32ee9Sbenno char ip[INET6_ADDRSTRLEN]; /* formatted string */ 4560a32ee9Sbenno struct sockaddr_storage sa; /* socket */ 4660a32ee9Sbenno socklen_t salen; /* length of socket buffer */ 4760a32ee9Sbenno }; 4860a32ee9Sbenno 4960a32ee9Sbenno /* 50d9a51c35Sjmc * Try to bind to a local IP address matching the address family passed. 51a52e5c3aSclaudio * Return -1 on failure to bind to any address, 0 on success. 52a52e5c3aSclaudio */ 53a52e5c3aSclaudio static int 54a52e5c3aSclaudio inet_bind(int s, sa_family_t af, const struct source *bsrc, size_t bsrcsz) 55a52e5c3aSclaudio { 56a52e5c3aSclaudio size_t i; 57a52e5c3aSclaudio 58a52e5c3aSclaudio if (bsrc == NULL) 59a52e5c3aSclaudio return 0; 60a52e5c3aSclaudio for (i = 0; i < bsrcsz; i++) { 61a52e5c3aSclaudio if (bsrc[i].family != af) 62a52e5c3aSclaudio continue; 63a52e5c3aSclaudio if (bind(s, (const struct sockaddr *)&bsrc[i].sa, 64a52e5c3aSclaudio bsrc[i].salen) == -1) 65a52e5c3aSclaudio continue; 66a52e5c3aSclaudio return 0; 67a52e5c3aSclaudio } 68a52e5c3aSclaudio return -1; 69a52e5c3aSclaudio } 70a52e5c3aSclaudio 71a52e5c3aSclaudio /* 7260a32ee9Sbenno * Connect to an IP address representing a host. 7360a32ee9Sbenno * Return <0 on failure, 0 on try another address, >0 on success. 7460a32ee9Sbenno */ 7560a32ee9Sbenno static int 76a52e5c3aSclaudio inet_connect(int *sd, const struct source *src, const char *host, 77a52e5c3aSclaudio const struct source *bsrc, size_t bsrcsz) 7860a32ee9Sbenno { 792eaa6f4eSjob struct pollfd pfd; 802eaa6f4eSjob socklen_t optlen; 812eaa6f4eSjob int c; 822eaa6f4eSjob int optval; 8360a32ee9Sbenno 84f1dcb30aSderaadt if (*sd != -1) 8560a32ee9Sbenno close(*sd); 8660a32ee9Sbenno 87b2a7eac7Sbenno LOG2("trying: %s, %s", src->ip, host); 8860a32ee9Sbenno 892eaa6f4eSjob if ((*sd = socket(src->family, SOCK_STREAM | SOCK_NONBLOCK, 0)) 902eaa6f4eSjob == -1) { 91b2a7eac7Sbenno ERR("socket"); 9260a32ee9Sbenno return -1; 9360a32ee9Sbenno } 9460a32ee9Sbenno 95a52e5c3aSclaudio if (inet_bind(*sd, src->family, bsrc, bsrcsz) == -1) { 96a52e5c3aSclaudio ERR("bind"); 97a52e5c3aSclaudio return -1; 98a52e5c3aSclaudio } 99a52e5c3aSclaudio 10060a32ee9Sbenno /* 10160a32ee9Sbenno * Initiate blocking connection. 1022eaa6f4eSjob * We use non-blocking connect() so we can poll() for contimeout. 10360a32ee9Sbenno */ 10460a32ee9Sbenno 1052eaa6f4eSjob if ((c = connect(*sd, (const struct sockaddr *)&src->sa, src->salen)) 1062eaa6f4eSjob != 0 && errno == EINPROGRESS) { 1072eaa6f4eSjob pfd.fd = *sd; 1082eaa6f4eSjob pfd.events = POLLOUT; 1092eaa6f4eSjob switch (c = poll(&pfd, 1, poll_contimeout)) { 1102eaa6f4eSjob case 1: 1112eaa6f4eSjob optlen = sizeof(optval); 1122eaa6f4eSjob if ((c = getsockopt(*sd, SOL_SOCKET, SO_ERROR, &optval, 1132eaa6f4eSjob &optlen)) == 0) { 1142eaa6f4eSjob errno = optval; 1152eaa6f4eSjob if (optval != 0) 1162eaa6f4eSjob c = -1; 1172eaa6f4eSjob } 1182eaa6f4eSjob break; 1192eaa6f4eSjob case 0: 1202eaa6f4eSjob errno = ETIMEDOUT; 1212eaa6f4eSjob WARNX("connect timeout: %s, %s", src->ip, host); 1222eaa6f4eSjob return 0; 1232eaa6f4eSjob default: 1242eaa6f4eSjob ERR("poll failed"); 1252eaa6f4eSjob return -1; 1262eaa6f4eSjob } 1272eaa6f4eSjob } 128f1dcb30aSderaadt if (c == -1) { 12955a1066fSkn if (errno == EADDRNOTAVAIL) 13055a1066fSkn return 0; 131f1dcb30aSderaadt if (errno == ECONNREFUSED || errno == EHOSTUNREACH) { 1322eaa6f4eSjob WARNX("connect refused: %s, %s", src->ip, host); 13360a32ee9Sbenno return 0; 13460a32ee9Sbenno } 135b2a7eac7Sbenno ERR("connect"); 13660a32ee9Sbenno return -1; 13760a32ee9Sbenno } 13860a32ee9Sbenno 13960a32ee9Sbenno return 1; 14060a32ee9Sbenno } 14160a32ee9Sbenno 14260a32ee9Sbenno /* 14360a32ee9Sbenno * Resolve the socket addresses for host, both in IPV4 and IPV6. 14460a32ee9Sbenno * Once completed, the "dns" pledge may be dropped. 14560a32ee9Sbenno * Returns the addresses on success, NULL on failure (sz is always zero, 14660a32ee9Sbenno * in this case). 14760a32ee9Sbenno */ 14860a32ee9Sbenno static struct source * 149a52e5c3aSclaudio inet_resolve(struct sess *sess, const char *host, size_t *sz, int passive) 15060a32ee9Sbenno { 15160a32ee9Sbenno struct addrinfo hints, *res0, *res; 15260a32ee9Sbenno struct sockaddr *sa; 15360a32ee9Sbenno struct source *src = NULL; 154a52e5c3aSclaudio const char *port = sess->opts->port; 15560a32ee9Sbenno size_t i, srcsz = 0; 15660a32ee9Sbenno int error; 15760a32ee9Sbenno 15860a32ee9Sbenno *sz = 0; 15960a32ee9Sbenno 16060a32ee9Sbenno memset(&hints, 0, sizeof(hints)); 16160a32ee9Sbenno hints.ai_family = PF_UNSPEC; 162971126b2Sderaadt hints.ai_socktype = SOCK_STREAM; 163a52e5c3aSclaudio if (passive) { 164a52e5c3aSclaudio hints.ai_flags = SOCK_STREAM; 165a52e5c3aSclaudio port = NULL; 166a52e5c3aSclaudio } 16760a32ee9Sbenno 168a52e5c3aSclaudio error = getaddrinfo(host, port, &hints, &res0); 16960a32ee9Sbenno 170b2a7eac7Sbenno LOG2("resolving: %s", host); 17160a32ee9Sbenno 17260a32ee9Sbenno if (error == EAI_AGAIN || error == EAI_NONAME) { 173b2a7eac7Sbenno ERRX("could not resolve hostname %s: %s", 17460a32ee9Sbenno host, gai_strerror(error)); 17560a32ee9Sbenno return NULL; 176882186e9Sderaadt } else if (error == EAI_SERVICE) { 177b2a7eac7Sbenno ERRX("could not resolve service rsync: %s", 17855cb9f91Sbenno gai_strerror(error)); 179882186e9Sderaadt return NULL; 18060a32ee9Sbenno } else if (error) { 181b2a7eac7Sbenno ERRX("getaddrinfo: %s: %s", host, gai_strerror(error)); 18260a32ee9Sbenno return NULL; 18360a32ee9Sbenno } 18460a32ee9Sbenno 18560a32ee9Sbenno /* Allocate for all available addresses. */ 18660a32ee9Sbenno 187ed5cc9fbSderaadt for (res = res0; res != NULL; res = res->ai_next) 18860a32ee9Sbenno if (res->ai_family == AF_INET || 18960a32ee9Sbenno res->ai_family == AF_INET6) 19060a32ee9Sbenno srcsz++; 19160a32ee9Sbenno 192f1dcb30aSderaadt if (srcsz == 0) { 193b2a7eac7Sbenno ERRX("no addresses resolved: %s", host); 19460a32ee9Sbenno freeaddrinfo(res0); 19560a32ee9Sbenno return NULL; 19660a32ee9Sbenno } 19760a32ee9Sbenno 19860a32ee9Sbenno src = calloc(srcsz, sizeof(struct source)); 199f1dcb30aSderaadt if (src == NULL) { 200b2a7eac7Sbenno ERRX("calloc"); 20160a32ee9Sbenno freeaddrinfo(res0); 20260a32ee9Sbenno return NULL; 20360a32ee9Sbenno } 20460a32ee9Sbenno 205ed5cc9fbSderaadt for (i = 0, res = res0; res != NULL; res = res->ai_next) { 20660a32ee9Sbenno if (res->ai_family != AF_INET && 20760a32ee9Sbenno res->ai_family != AF_INET6) 20860a32ee9Sbenno continue; 20960a32ee9Sbenno 21060a32ee9Sbenno assert(i < srcsz); 21160a32ee9Sbenno 21260a32ee9Sbenno /* Copy the socket address. */ 21360a32ee9Sbenno 21460a32ee9Sbenno src[i].salen = res->ai_addrlen; 21560a32ee9Sbenno memcpy(&src[i].sa, res->ai_addr, src[i].salen); 21660a32ee9Sbenno 21760a32ee9Sbenno /* Format as a string, too. */ 21860a32ee9Sbenno 21960a32ee9Sbenno sa = res->ai_addr; 220f1dcb30aSderaadt if (res->ai_family == AF_INET) { 22160a32ee9Sbenno src[i].family = PF_INET; 22260a32ee9Sbenno inet_ntop(AF_INET, 22360a32ee9Sbenno &(((struct sockaddr_in *)sa)->sin_addr), 22460a32ee9Sbenno src[i].ip, INET6_ADDRSTRLEN); 22560a32ee9Sbenno } else { 22660a32ee9Sbenno src[i].family = PF_INET6; 22760a32ee9Sbenno inet_ntop(AF_INET6, 22860a32ee9Sbenno &(((struct sockaddr_in6 *)sa)->sin6_addr), 22960a32ee9Sbenno src[i].ip, INET6_ADDRSTRLEN); 23060a32ee9Sbenno } 23160a32ee9Sbenno 232b2a7eac7Sbenno LOG2("hostname resolved: %s: %s", host, src[i].ip); 23360a32ee9Sbenno i++; 23460a32ee9Sbenno } 23560a32ee9Sbenno 23660a32ee9Sbenno freeaddrinfo(res0); 23760a32ee9Sbenno *sz = srcsz; 23860a32ee9Sbenno return src; 23960a32ee9Sbenno } 24060a32ee9Sbenno 24160a32ee9Sbenno /* 24260a32ee9Sbenno * Process an rsyncd preamble line. 24360a32ee9Sbenno * This is either free-form text or @RSYNCD commands. 24460a32ee9Sbenno * Return <0 on failure, 0 on try more lines, >0 on finished. 24560a32ee9Sbenno */ 24660a32ee9Sbenno static int 247ba617adaSbenno protocol_line(struct sess *sess, __attribute__((unused)) const char *host, 248ba617adaSbenno const char *cp) 24960a32ee9Sbenno { 25060a32ee9Sbenno int major, minor; 25160a32ee9Sbenno 25260a32ee9Sbenno if (strncmp(cp, "@RSYNCD: ", 9)) { 2533bf4cfb6Sjob if (sess->opts->no_motd == 0) 2547b590dfaSclaudio LOG1("%s", cp); 25560a32ee9Sbenno return 0; 25660a32ee9Sbenno } 25760a32ee9Sbenno 25860a32ee9Sbenno cp += 9; 25960a32ee9Sbenno while (isspace((unsigned char)*cp)) 26060a32ee9Sbenno cp++; 26160a32ee9Sbenno 26260a32ee9Sbenno /* @RSYNCD: OK indicates that we're finished. */ 26360a32ee9Sbenno 264f1dcb30aSderaadt if (strcmp(cp, "OK") == 0) 26560a32ee9Sbenno return 1; 26660a32ee9Sbenno 26760a32ee9Sbenno /* 26860a32ee9Sbenno * Otherwise, all we have left is our version. 26960a32ee9Sbenno * There are two formats: x.y (w/submodule) and x. 27060a32ee9Sbenno */ 27160a32ee9Sbenno 272f1dcb30aSderaadt if (sscanf(cp, "%d.%d", &major, &minor) == 2) { 27360a32ee9Sbenno sess->rver = major; 27460a32ee9Sbenno return 0; 275f1dcb30aSderaadt } else if (sscanf(cp, "%d", &major) == 1) { 27660a32ee9Sbenno sess->rver = major; 27760a32ee9Sbenno return 0; 27860a32ee9Sbenno } 27960a32ee9Sbenno 280b2a7eac7Sbenno ERRX("rsyncd protocol error: unknown command"); 28160a32ee9Sbenno return -1; 28260a32ee9Sbenno } 28360a32ee9Sbenno 28460a32ee9Sbenno /* 285ac024dd4Snaddy * Connect to a remote rsync://-enabled server sender. 286ac024dd4Snaddy * Returns exit code 0 on success, 1 on failure. 28760a32ee9Sbenno */ 28860a32ee9Sbenno int 289ac024dd4Snaddy rsync_connect(const struct opts *opts, int *sd, const struct fargs *f) 29060a32ee9Sbenno { 29160a32ee9Sbenno struct sess sess; 292a52e5c3aSclaudio struct source *src = NULL, *bsrc = NULL; 293a52e5c3aSclaudio size_t i, srcsz = 0, bsrcsz = 0; 294ac024dd4Snaddy int c, rc = 1; 29560a32ee9Sbenno 296ef859540Sderaadt if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil", 297ef859540Sderaadt NULL) == -1) 298dd3c9d24Sclaudio err(ERR_IPC, "pledge"); 299ef859540Sderaadt 30060a32ee9Sbenno memset(&sess, 0, sizeof(struct sess)); 30160a32ee9Sbenno sess.opts = opts; 30260a32ee9Sbenno 303f1dcb30aSderaadt assert(f->host != NULL); 30460a32ee9Sbenno 30560a32ee9Sbenno /* Resolve all IP addresses from the host. */ 30660a32ee9Sbenno 307a52e5c3aSclaudio if ((src = inet_resolve(&sess, f->host, &srcsz, 0)) == NULL) { 308b2a7eac7Sbenno ERRX1("inet_resolve"); 309ef859540Sderaadt exit(1); 31060a32ee9Sbenno } 311a52e5c3aSclaudio if (opts->address != NULL) 312a52e5c3aSclaudio if ((bsrc = inet_resolve(&sess, opts->address, &bsrcsz, 1)) == 313a52e5c3aSclaudio NULL) { 314a52e5c3aSclaudio ERRX1("inet_resolve bind"); 315a52e5c3aSclaudio exit(1); 316a52e5c3aSclaudio } 31760a32ee9Sbenno 31860a32ee9Sbenno /* Drop the DNS pledge. */ 31960a32ee9Sbenno 320ef859540Sderaadt if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw inet unveil", 321ef859540Sderaadt NULL) == -1) { 322b2a7eac7Sbenno ERR("pledge"); 323ef859540Sderaadt exit(1); 32460a32ee9Sbenno } 32560a32ee9Sbenno 32660a32ee9Sbenno /* 32760a32ee9Sbenno * Iterate over all addresses, trying to connect. 32860a32ee9Sbenno * When we succeed, then continue using the connected socket. 32960a32ee9Sbenno */ 33060a32ee9Sbenno 33160a32ee9Sbenno assert(srcsz); 33260a32ee9Sbenno for (i = 0; i < srcsz; i++) { 333a52e5c3aSclaudio c = inet_connect(sd, &src[i], f->host, bsrc, bsrcsz); 33483649630Sderaadt if (c < 0) { 335b2a7eac7Sbenno ERRX1("inet_connect"); 33660a32ee9Sbenno goto out; 33783649630Sderaadt } else if (c > 0) 33860a32ee9Sbenno break; 33960a32ee9Sbenno } 34060a32ee9Sbenno 34160a32ee9Sbenno /* Drop the inet pledge. */ 342ef859540Sderaadt if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", 343ef859540Sderaadt NULL) == -1) { 344b2a7eac7Sbenno ERR("pledge"); 34560a32ee9Sbenno goto out; 34660a32ee9Sbenno } 34760a32ee9Sbenno 34860a32ee9Sbenno if (i == srcsz) { 349b2a7eac7Sbenno ERRX("cannot connect to host: %s", f->host); 35060a32ee9Sbenno goto out; 35160a32ee9Sbenno } 35260a32ee9Sbenno 353b2a7eac7Sbenno LOG2("connected: %s, %s", src[i].ip, f->host); 35460a32ee9Sbenno 355ac024dd4Snaddy free(src); 356a52e5c3aSclaudio free(bsrc); 357ac024dd4Snaddy return 0; 358ac024dd4Snaddy out: 359ac024dd4Snaddy free(src); 360a52e5c3aSclaudio free(bsrc); 361ac024dd4Snaddy if (*sd != -1) 362ac024dd4Snaddy close(*sd); 363ac024dd4Snaddy return rc; 364ac024dd4Snaddy } 365ac024dd4Snaddy 366ac024dd4Snaddy /* 367ac024dd4Snaddy * Talk to a remote rsync://-enabled server sender. 368ac024dd4Snaddy * Returns exit code 0 on success, 1 on failure, 2 on failure with 369ac024dd4Snaddy * incompatible protocols. 370ac024dd4Snaddy */ 371ac024dd4Snaddy int 372ac024dd4Snaddy rsync_socket(const struct opts *opts, int sd, const struct fargs *f) 373ac024dd4Snaddy { 374ac024dd4Snaddy struct sess sess; 375ac024dd4Snaddy size_t i, skip; 376ac024dd4Snaddy int c, rc = 1; 377ac024dd4Snaddy char **args, buf[BUFSIZ]; 378ac024dd4Snaddy uint8_t byte; 379ac024dd4Snaddy 380ac024dd4Snaddy if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", 381ac024dd4Snaddy NULL) == -1) 382dd3c9d24Sclaudio err(ERR_IPC, "pledge"); 383ac024dd4Snaddy 384ac024dd4Snaddy memset(&sess, 0, sizeof(struct sess)); 385ac024dd4Snaddy sess.lver = RSYNC_PROTOCOL; 386ac024dd4Snaddy sess.opts = opts; 387ac024dd4Snaddy 388ac024dd4Snaddy assert(f->host != NULL); 389ac024dd4Snaddy assert(f->module != NULL); 390ac024dd4Snaddy 391dd3c9d24Sclaudio args = fargs_cmdline(&sess, f, &skip); 392ac024dd4Snaddy 393ac024dd4Snaddy /* Initiate with the rsyncd version and module request. */ 394ac024dd4Snaddy 39560a32ee9Sbenno (void)snprintf(buf, sizeof(buf), "@RSYNCD: %d", sess.lver); 39660a32ee9Sbenno if (!io_write_line(&sess, sd, buf)) { 397b2a7eac7Sbenno ERRX1("io_write_line"); 39860a32ee9Sbenno goto out; 39960a32ee9Sbenno } 40060a32ee9Sbenno 401b2a7eac7Sbenno LOG2("requesting module: %s, %s", f->module, f->host); 40260a32ee9Sbenno 40360a32ee9Sbenno if (!io_write_line(&sess, sd, f->module)) { 404b2a7eac7Sbenno ERRX1("io_write_line"); 40560a32ee9Sbenno goto out; 40660a32ee9Sbenno } 40760a32ee9Sbenno 40860a32ee9Sbenno /* 40960a32ee9Sbenno * Now we read the server's response, byte-by-byte, one newline 41060a32ee9Sbenno * terminated at a time, limited to BUFSIZ line length. 41160a32ee9Sbenno * For this protocol version, this consists of either @RSYNCD 41260a32ee9Sbenno * followed by some text (just "ok" and the remote version) or 41360a32ee9Sbenno * the message of the day. 41460a32ee9Sbenno */ 41560a32ee9Sbenno 41660a32ee9Sbenno for (;;) { 41760a32ee9Sbenno for (i = 0; i < sizeof(buf); i++) { 41860a32ee9Sbenno if (!io_read_byte(&sess, sd, &byte)) { 419b2a7eac7Sbenno ERRX1("io_read_byte"); 42060a32ee9Sbenno goto out; 42160a32ee9Sbenno } 422f1dcb30aSderaadt if ((buf[i] = byte) == '\n') 42360a32ee9Sbenno break; 42460a32ee9Sbenno } 42560a32ee9Sbenno if (i == sizeof(buf)) { 426b2a7eac7Sbenno ERRX("line buffer overrun"); 42760a32ee9Sbenno goto out; 428f1dcb30aSderaadt } else if (i == 0) 42960a32ee9Sbenno continue; 43060a32ee9Sbenno 43160a32ee9Sbenno /* 43260a32ee9Sbenno * The rsyncd protocol isn't very clear as to whether we 43360a32ee9Sbenno * get a CRLF or not: I don't actually see this being 43460a32ee9Sbenno * transmitted over the wire. 43560a32ee9Sbenno */ 43660a32ee9Sbenno 43760a32ee9Sbenno assert(i > 0); 43860a32ee9Sbenno buf[i] = '\0'; 439f1dcb30aSderaadt if (buf[i - 1] == '\r') 44060a32ee9Sbenno buf[i - 1] = '\0'; 44160a32ee9Sbenno 44283649630Sderaadt if ((c = protocol_line(&sess, f->host, buf)) < 0) { 443b2a7eac7Sbenno ERRX1("protocol_line"); 44460a32ee9Sbenno goto out; 44583649630Sderaadt } else if (c > 0) 44660a32ee9Sbenno break; 44760a32ee9Sbenno } 44860a32ee9Sbenno 44960a32ee9Sbenno /* 45060a32ee9Sbenno * Now we've exchanged all of our protocol information. 45160a32ee9Sbenno * We want to send our command-line arguments over the wire, 45260a32ee9Sbenno * each with a newline termination. 45360a32ee9Sbenno * Use the same arguments when invoking the server, but leave 45460a32ee9Sbenno * off the binary name(s). 45560a32ee9Sbenno * Emit a standalone newline afterward. 45660a32ee9Sbenno */ 45760a32ee9Sbenno 458ac024dd4Snaddy for (i = skip ; args[i] != NULL; i++) 45960a32ee9Sbenno if (!io_write_line(&sess, sd, args[i])) { 460b2a7eac7Sbenno ERRX1("io_write_line"); 46160a32ee9Sbenno goto out; 46260a32ee9Sbenno } 46360a32ee9Sbenno if (!io_write_byte(&sess, sd, '\n')) { 464b2a7eac7Sbenno ERRX1("io_write_line"); 46560a32ee9Sbenno goto out; 46660a32ee9Sbenno } 46760a32ee9Sbenno 46860a32ee9Sbenno /* 46960a32ee9Sbenno * All data after this point is going to be multiplexed, so turn 47060a32ee9Sbenno * on the multiplexer for our reads and writes. 47160a32ee9Sbenno */ 47260a32ee9Sbenno 47360a32ee9Sbenno /* Protocol exchange: get the random seed. */ 47460a32ee9Sbenno 47560a32ee9Sbenno if (!io_read_int(&sess, sd, &sess.seed)) { 476b2a7eac7Sbenno ERRX1("io_read_int"); 47760a32ee9Sbenno goto out; 47860a32ee9Sbenno } 47960a32ee9Sbenno 48060a32ee9Sbenno /* Now we've completed the handshake. */ 48160a32ee9Sbenno 48260a32ee9Sbenno if (sess.rver < sess.lver) { 483b2a7eac7Sbenno ERRX("remote protocol is older than our own (%d < %d): " 48455cb9f91Sbenno "this is not supported", 48560a32ee9Sbenno sess.rver, sess.lver); 48683649630Sderaadt rc = 2; 48760a32ee9Sbenno goto out; 48860a32ee9Sbenno } 48960a32ee9Sbenno 49060a32ee9Sbenno sess.mplex_reads = 1; 491b2a7eac7Sbenno LOG2("read multiplexing enabled"); 49260a32ee9Sbenno 493b2a7eac7Sbenno LOG2("socket detected client version %d, server version %d, seed %d", 49460a32ee9Sbenno sess.lver, sess.rver, sess.seed); 49560a32ee9Sbenno 496f1dcb30aSderaadt assert(f->mode == FARGS_RECEIVER); 49760a32ee9Sbenno 498b2a7eac7Sbenno LOG2("client starting receiver: %s", f->host); 49960a32ee9Sbenno if (!rsync_receiver(&sess, sd, sd, f->sink)) { 500b2a7eac7Sbenno ERRX1("rsync_receiver"); 50160a32ee9Sbenno goto out; 50260a32ee9Sbenno } 50360a32ee9Sbenno 50460a32ee9Sbenno #if 0 50560a32ee9Sbenno /* Probably the EOF. */ 50660a32ee9Sbenno if (io_read_check(&sess, sd)) 507b2a7eac7Sbenno WARNX("data remains in read pipe"); 50860a32ee9Sbenno #endif 50960a32ee9Sbenno 51083649630Sderaadt rc = 0; 51160a32ee9Sbenno out: 51260a32ee9Sbenno free(args); 51360a32ee9Sbenno return rc; 51460a32ee9Sbenno } 515