[BACK]Return to socket.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rsync

Annotation of src/usr.bin/rsync/socket.c, Revision 1.17

1.17    ! benno       1: /*     $Id: socket.c,v 1.16 2019/02/18 21:34:54 benno Exp $ */
1.1       benno       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 {
1.2       benno      41:        int              family; /* PF_INET or PF_INET6 */
                     42:        char             ip[INET6_ADDRSTRLEN]; /* formatted string */
1.1       benno      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:
1.5       deraadt    57:        if (*sd != -1)
1.1       benno      58:                close(*sd);
                     59:
                     60:        LOG2(sess, "trying: %s, %s", src->ip, host);
                     61:
1.5       deraadt    62:        if ((*sd = socket(src->family, SOCK_STREAM, 0)) == -1) {
1.1       benno      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:
1.7       deraadt    74:        c = connect(*sd, (const struct sockaddr *)&src->sa, src->salen);
1.5       deraadt    75:        if (c == -1) {
                     76:                if (errno == ECONNREFUSED || errno == EHOSTUNREACH) {
1.7       deraadt    77:                        WARNX(sess, "connect refused: %s, %s",
                     78:                            src->ip, host);
1.1       benno      79:                        return 0;
                     80:                }
                     81:                ERR(sess, "connect");
                     82:                return -1;
                     83:        }
                     84:
                     85:        /* Set up non-blocking mode. */
                     86:
1.5       deraadt    87:        if ((flags = fcntl(*sd, F_GETFL, 0)) == -1) {
1.1       benno      88:                ERR(sess, "fcntl");
                     89:                return -1;
1.5       deraadt    90:        } else if (fcntl(*sd, F_SETFL, flags|O_NONBLOCK) == -1) {
1.1       benno      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;
1.12      deraadt   117:        hints.ai_socktype = SOCK_STREAM;
1.1       benno     118:
1.15      deraadt   119:        error = getaddrinfo(host, sess->opts->port, &hints, &res0);
1.1       benno     120:
                    121:        LOG2(sess, "resolving: %s", host);
                    122:
                    123:        if (error == EAI_AGAIN || error == EAI_NONAME) {
1.17    ! benno     124:                ERRX(sess, "Could not resolve hostname %s: %s",
1.7       deraadt   125:                    host, gai_strerror(error));
1.1       benno     126:                return NULL;
1.10      deraadt   127:        } else if (error == EAI_SERVICE) {
1.17    ! benno     128:                ERRX(sess, "Could not resolve service '%s': %s",
        !           129:                    sess->opts->port, gai_strerror(error));
1.10      deraadt   130:                return NULL;
1.1       benno     131:        } else if (error) {
1.10      deraadt   132:                ERRX(sess, "getaddrinfo: %s: %s", host, gai_strerror(error));
1.1       benno     133:                return NULL;
                    134:        }
                    135:
                    136:        /* Allocate for all available addresses. */
                    137:
1.14      deraadt   138:        for (res = res0; res != NULL; res = res->ai_next)
1.1       benno     139:                if (res->ai_family == AF_INET ||
                    140:                    res->ai_family == AF_INET6)
                    141:                        srcsz++;
                    142:
1.5       deraadt   143:        if (srcsz == 0) {
1.1       benno     144:                ERRX(sess, "no addresses resolved: %s", host);
                    145:                freeaddrinfo(res0);
                    146:                return NULL;
                    147:        }
                    148:
                    149:        src = calloc(srcsz, sizeof(struct source));
1.5       deraadt   150:        if (src == NULL) {
1.1       benno     151:                ERRX(sess, "calloc");
                    152:                freeaddrinfo(res0);
                    153:                return NULL;
                    154:        }
                    155:
1.14      deraadt   156:        for (i = 0, res = res0; res != NULL; res = res->ai_next) {
1.1       benno     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;
1.5       deraadt   171:                if (res->ai_family == AF_INET) {
1.1       benno     172:                        src[i].family = PF_INET;
                    173:                        inet_ntop(AF_INET,
1.7       deraadt   174:                            &(((struct sockaddr_in *)sa)->sin_addr),
                    175:                            src[i].ip, INET6_ADDRSTRLEN);
1.1       benno     176:                } else {
                    177:                        src[i].family = PF_INET6;
                    178:                        inet_ntop(AF_INET6,
1.7       deraadt   179:                            &(((struct sockaddr_in6 *)sa)->sin6_addr),
                    180:                            src[i].ip, INET6_ADDRSTRLEN);
1.1       benno     181:                }
1.2       benno     182:
1.10      deraadt   183:                LOG2(sess, "hostname resolved: %s: %s", host, src[i].ip);
1.1       benno     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:
1.5       deraadt   213:        if (strcmp(cp, "OK") == 0)
1.1       benno     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:
1.5       deraadt   221:        if (sscanf(cp, "%d.%d", &major, &minor) == 2) {
1.1       benno     222:                sess->rver = major;
                    223:                return 0;
1.5       deraadt   224:        } else if (sscanf(cp, "%d", &major) == 1) {
1.1       benno     225:                sess->rver = major;
                    226:                return 0;
                    227:        }
                    228:
                    229:        ERRX(sess, "rsyncd protocol error: unknown command");
                    230:        return -1;
                    231: }
                    232:
                    233: /*
1.9       florian   234:  * Pledges: dns, inet, unix, unveil, rpath, cpath, wpath, stdio, fattr, chown.
1.1       benno     235:  *
1.9       florian   236:  * Pledges (dry-run): -unix, -cpath, -wpath, -fattr, -chown.
1.1       benno     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:
1.5       deraadt   253:        assert(f->host != NULL);
                    254:        assert(f->module != NULL);
1.1       benno     255:
1.5       deraadt   256:        if ((args = fargs_cmdline(&sess, f)) == NULL) {
1.1       benno     257:                ERRX1(&sess, "fargs_cmdline");
                    258:                return 0;
                    259:        }
                    260:
                    261:        /* Resolve all IP addresses from the host. */
                    262:
1.5       deraadt   263:        if ((src = inet_resolve(&sess, f->host, &srcsz)) == NULL) {
1.1       benno     264:                ERRX1(&sess, "inet_resolve");
                    265:                free(args);
                    266:                return 0;
                    267:        }
                    268:
                    269:        /* Drop the DNS pledge. */
                    270:
1.9       florian   271:        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw inet unveil", NULL) == -1) {
1.1       benno     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. */
1.9       florian   292:        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) {
1.1       benno     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);
1.4       deraadt   307:        if (!io_write_line(&sess, sd, buf)) {
1.1       benno     308:                ERRX1(&sess, "io_write_line");
                    309:                goto out;
                    310:        }
                    311:
                    312:        LOG2(&sess, "requesting module: %s, %s", f->module, f->host);
                    313:
1.4       deraadt   314:        if (!io_write_line(&sess, sd, f->module)) {
1.1       benno     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++) {
1.4       deraadt   329:                        if (!io_read_byte(&sess, sd, &byte)) {
1.1       benno     330:                                ERRX1(&sess, "io_read_byte");
                    331:                                goto out;
                    332:                        }
1.5       deraadt   333:                        if ((buf[i] = byte) == '\n')
1.1       benno     334:                                break;
                    335:                }
                    336:                if (i == sizeof(buf)) {
                    337:                        ERRX(&sess, "line buffer overrun");
                    338:                        goto out;
1.5       deraadt   339:                } else if (i == 0)
1.1       benno     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';
1.5       deraadt   350:                if (buf[i - 1] == '\r')
1.1       benno     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:
1.5       deraadt   369:        if (f->mode == FARGS_RECEIVER || f->mode == FARGS_SENDER)
1.1       benno     370:                i = 3; /* ssh host rsync... */
                    371:        else
                    372:                i = 1; /* rsync... */
                    373:
1.14      deraadt   374:        for ( ; args[i] != NULL; i++)
1.4       deraadt   375:                if (!io_write_line(&sess, sd, args[i])) {
1.1       benno     376:                        ERRX1(&sess, "io_write_line");
                    377:                        goto out;
                    378:                }
1.4       deraadt   379:        if (!io_write_byte(&sess, sd, '\n')) {
1.1       benno     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:
1.4       deraadt   391:        if (!io_read_int(&sess, sd, &sess.seed)) {
1.1       benno     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) {
1.17    ! benno     399:                ERRX(&sess,
        !           400:                    "remote protocol %d is older than own %d: unsupported\n",
        !           401:                    sess.rver, sess.lver);
        !           402:                rc = 2; /* Protocol incompatibility*/
1.1       benno     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:
1.5       deraadt   413:        assert(f->mode == FARGS_RECEIVER);
1.1       benno     414:
                    415:        LOG2(&sess, "client starting receiver: %s", f->host);
1.4       deraadt   416:        if (!rsync_receiver(&sess, sd, sd, f->sink)) {
1.1       benno     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);
1.5       deraadt   431:        if (sd != -1)
1.1       benno     432:                close(sd);
                    433:        return rc;
                    434: }