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