[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.32

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