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