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

1.3     ! benno       1: /*     $Id: socket.c,v 1.2 2019/02/10 23:24:14 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:
                     57:        if (-1 != *sd)
                     58:                close(*sd);
                     59:
                     60:        LOG2(sess, "trying: %s, %s", src->ip, host);
                     61:
                     62:        if (-1 == (*sd = socket(src->family, SOCK_STREAM, 0))) {
                     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 (-1 == c) {
                     78:                if (ECONNREFUSED == errno ||
                     79:                    EHOSTUNREACH == errno) {
                     80:                        WARNX(sess, "connect refused: "
                     81:                                "%s, %s", src->ip, host);
                     82:                        return 0;
                     83:                }
                     84:                ERR(sess, "connect");
                     85:                return -1;
                     86:        }
                     87:
                     88:        /* Set up non-blocking mode. */
                     89:
                     90:        if (-1 == (flags = fcntl(*sd, F_GETFL, 0))) {
                     91:                ERR(sess, "fcntl");
                     92:                return -1;
                     93:        } else if (-1 == fcntl(*sd, F_SETFL, flags|O_NONBLOCK)) {
                     94:                ERR(sess, "fcntl");
                     95:                return -1;
                     96:        }
                     97:
                     98:        return 1;
                     99: }
                    100:
                    101: /*
                    102:  * Resolve the socket addresses for host, both in IPV4 and IPV6.
                    103:  * Once completed, the "dns" pledge may be dropped.
                    104:  * Returns the addresses on success, NULL on failure (sz is always zero,
                    105:  * in this case).
                    106:  */
                    107: static struct source *
                    108: inet_resolve(struct sess *sess, const char *host, size_t *sz)
                    109: {
                    110:        struct addrinfo  hints, *res0, *res;
                    111:        struct sockaddr *sa;
                    112:        struct source   *src = NULL;
                    113:        size_t           i, srcsz = 0;
                    114:        int              error;
                    115:
                    116:        *sz = 0;
                    117:
                    118:        memset(&hints, 0, sizeof(hints));
                    119:        hints.ai_family = PF_UNSPEC;
                    120:        hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
                    121:
                    122:        error = getaddrinfo(host, "873", &hints, &res0);
                    123:
                    124:        LOG2(sess, "resolving: %s", host);
                    125:
                    126:        if (error == EAI_AGAIN || error == EAI_NONAME) {
                    127:                ERRX(sess, "DNS resolve error: %s: %s",
                    128:                        host, gai_strerror(error));
                    129:                return NULL;
                    130:        } else if (error) {
                    131:                ERRX(sess, "DNS parse error: %s: %s",
                    132:                        host, gai_strerror(error));
                    133:                return NULL;
                    134:        }
                    135:
                    136:        /* Allocate for all available addresses. */
                    137:
                    138:        for (res = res0; NULL != res; res = res->ai_next)
                    139:                if (res->ai_family == AF_INET ||
                    140:                    res->ai_family == AF_INET6)
                    141:                        srcsz++;
                    142:
                    143:        if (0 == srcsz) {
                    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 (NULL == src) {
                    151:                ERRX(sess, "calloc");
                    152:                freeaddrinfo(res0);
                    153:                return NULL;
                    154:        }
                    155:
                    156:        for (i = 0, res = res0; NULL != res; 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 (AF_INET == res->ai_family) {
                    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:                }
1.2       benno     182:
1.1       benno     183:                LOG2(sess, "DNS 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 (0 == strcmp(cp, "OK"))
                    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 (2 == sscanf(cp, "%d.%d", &major, &minor)) {
                    222:                sess->rver = major;
                    223:                return 0;
                    224:        } else if (1 == sscanf(cp, "%d", &major)) {
                    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, unveil, rpath, cpath, wpath, stdio, fattr.
                    235:  *
                    236:  * Pledges (dry-run): -cpath, -wpath, -fattr.
                    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(NULL != f->host);
                    254:        assert(NULL != f->module);
                    255:
                    256:        if (NULL == (args = fargs_cmdline(&sess, f))) {
                    257:                ERRX1(&sess, "fargs_cmdline");
                    258:                return 0;
                    259:        }
                    260:
                    261:        /* Resolve all IP addresses from the host. */
                    262:
                    263:        if (NULL == (src = inet_resolve(&sess, f->host, &srcsz))) {
                    264:                ERRX1(&sess, "inet_resolve");
                    265:                free(args);
                    266:                return 0;
                    267:        }
                    268:
                    269:        /* Drop the DNS pledge. */
                    270:
1.3     ! benno     271:        if (-1 == pledge("stdio rpath wpath cpath fattr inet unveil", NULL)) {
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. */
                    292:
1.3     ! benno     293:        if (-1 == pledge("stdio rpath wpath cpath fattr unveil", NULL)) {
1.1       benno     294:                ERR(&sess, "pledge");
                    295:                goto out;
                    296:        }
                    297:
                    298:        if (i == srcsz) {
                    299:                ERRX(&sess, "cannot connect to host: %s", f->host);
                    300:                goto out;
                    301:        }
                    302:
                    303:        /* Initiate with the rsyncd version and module request. */
                    304:
                    305:        LOG2(&sess, "connected: %s, %s", src[i].ip, f->host);
                    306:
                    307:        (void)snprintf(buf, sizeof(buf), "@RSYNCD: %d", sess.lver);
                    308:        if ( ! io_write_line(&sess, sd, buf)) {
                    309:                ERRX1(&sess, "io_write_line");
                    310:                goto out;
                    311:        }
                    312:
                    313:        LOG2(&sess, "requesting module: %s, %s", f->module, f->host);
                    314:
                    315:        if ( ! io_write_line(&sess, sd, f->module)) {
                    316:                ERRX1(&sess, "io_write_line");
                    317:                goto out;
                    318:        }
                    319:
                    320:        /*
                    321:         * Now we read the server's response, byte-by-byte, one newline
                    322:         * terminated at a time, limited to BUFSIZ line length.
                    323:         * For this protocol version, this consists of either @RSYNCD
                    324:         * followed by some text (just "ok" and the remote version) or
                    325:         * the message of the day.
                    326:         */
                    327:
                    328:        for (;;) {
                    329:                for (i = 0; i < sizeof(buf); i++) {
                    330:                        if ( ! io_read_byte(&sess, sd, &byte)) {
                    331:                                ERRX1(&sess, "io_read_byte");
                    332:                                goto out;
                    333:                        }
                    334:                        if ('\n' == (buf[i] = byte))
                    335:                                break;
                    336:                }
                    337:                if (i == sizeof(buf)) {
                    338:                        ERRX(&sess, "line buffer overrun");
                    339:                        goto out;
                    340:                } else if (0 == i)
                    341:                        continue;
                    342:
                    343:                /*
                    344:                 * The rsyncd protocol isn't very clear as to whether we
                    345:                 * get a CRLF or not: I don't actually see this being
                    346:                 * transmitted over the wire.
                    347:                 */
                    348:
                    349:                assert(i > 0);
                    350:                buf[i] = '\0';
                    351:                if ('\r' == buf[i - 1])
                    352:                        buf[i - 1] = '\0';
                    353:
                    354:                if ((c = protocol_line(&sess, f->host, buf)) < 0) {
                    355:                        ERRX1(&sess, "protocol_line");
                    356:                        goto out;
                    357:                } else if (c > 0)
                    358:                        break;
                    359:        }
                    360:
                    361:        /*
                    362:         * Now we've exchanged all of our protocol information.
                    363:         * We want to send our command-line arguments over the wire,
                    364:         * each with a newline termination.
                    365:         * Use the same arguments when invoking the server, but leave
                    366:         * off the binary name(s).
                    367:         * Emit a standalone newline afterward.
                    368:         */
                    369:
                    370:        if (FARGS_RECEIVER == f->mode || FARGS_SENDER == f->mode)
                    371:                i = 3; /* ssh host rsync... */
                    372:        else
                    373:                i = 1; /* rsync... */
                    374:
                    375:        for ( ; NULL != args[i]; i++)
                    376:                if ( ! io_write_line(&sess, sd, args[i])) {
                    377:                        ERRX1(&sess, "io_write_line");
                    378:                        goto out;
                    379:                }
                    380:        if ( ! io_write_byte(&sess, sd, '\n')) {
                    381:                ERRX1(&sess, "io_write_line");
                    382:                goto out;
                    383:        }
                    384:
                    385:        /*
                    386:         * All data after this point is going to be multiplexed, so turn
                    387:         * on the multiplexer for our reads and writes.
                    388:         */
                    389:
                    390:        /* Protocol exchange: get the random seed. */
                    391:
                    392:        if ( ! io_read_int(&sess, sd, &sess.seed)) {
                    393:                ERRX1(&sess, "io_read_int");
                    394:                goto out;
                    395:        }
                    396:
                    397:        /* Now we've completed the handshake. */
                    398:
                    399:        if (sess.rver < sess.lver) {
                    400:                ERRX(&sess, "remote protocol is older "
                    401:                        "than our own (%" PRId32 " < %" PRId32 "): "
                    402:                        "this is not supported",
                    403:                        sess.rver, sess.lver);
                    404:                goto out;
                    405:        }
                    406:
                    407:        sess.mplex_reads = 1;
                    408:        LOG2(&sess, "read multiplexing enabled");
                    409:
                    410:        LOG2(&sess, "socket detected client version %" PRId32
                    411:                ", server version %" PRId32 ", seed %" PRId32,
                    412:                sess.lver, sess.rver, sess.seed);
                    413:
                    414:        assert(FARGS_RECEIVER == f->mode);
                    415:
                    416:        LOG2(&sess, "client starting receiver: %s", f->host);
                    417:        if ( ! rsync_receiver(&sess, sd, sd, f->sink)) {
                    418:                ERRX1(&sess, "rsync_receiver");
                    419:                goto out;
                    420:        }
                    421:
                    422: #if 0
                    423:        /* Probably the EOF. */
                    424:        if (io_read_check(&sess, sd))
                    425:                WARNX(&sess, "data remains in read pipe");
                    426: #endif
                    427:
                    428:        rc = 1;
                    429: out:
                    430:        free(src);
                    431:        free(args);
                    432:        if (-1 != sd)
                    433:                close(sd);
                    434:        return rc;
                    435: }