I need to be able to control the bind address for openrsync connections.
This implements this but it only works for connections to an rsync daemon.
For rsync over ssh you need to use -e 'ssh -b address' instead.

-- 
:wq Claudio

Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/rsync/extern.h,v
retrieving revision 1.31
diff -u -p -r1.31 extern.h
--- extern.h    2 Jun 2019 17:36:48 -0000       1.31
+++ extern.h    8 Aug 2019 13:41:02 -0000
@@ -120,6 +120,7 @@ struct      opts {
        char            *rsync_path;            /* --rsync-path */
        char            *ssh_prog;              /* --rsh or -e */
        char            *port;                  /* --port */
+       char            *address;               /* --address */
 };
 
 /*
Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/main.c,v
retrieving revision 1.47
diff -u -p -r1.47 main.c
--- main.c      3 Jun 2019 15:37:48 -0000       1.47
+++ main.c      8 Aug 2019 13:41:02 -0000
@@ -307,6 +307,7 @@ main(int argc, char *argv[])
                { "no-times",   no_argument,    &opts.preserve_times,   0 },
                { "verbose",    no_argument,    &verbose,               1 },
                { "no-verbose", no_argument,    &verbose,               0 },
+               { "address",    required_argument, NULL,                4 },
                { NULL,         0,              NULL,                   0 }};
 
        /* Global pledge. */
@@ -380,6 +381,9 @@ main(int argc, char *argv[])
                case 3:
                        opts.port = optarg;
                        break;
+               case 4:
+                       opts.address = optarg;
+                       break;
                case 'h':
                default:
                        goto usage;
@@ -505,9 +509,9 @@ main(int argc, char *argv[])
        exit(rc);
 usage:
        fprintf(stderr, "usage: %s"
-           " [-aDglnoprtvx] [-e program] [--del] [--numeric-ids]\n"
-           "\t[--port=portnumber] [--rsync-path=program] [--version]\n"
-           "\tsource ... directory\n",
+           " [-aDglnoprtvx] [-e program] [--address=bind_address] [--del]\n"
+           "\t[--numeric-ids] [--port=portnumber] [--rsync-path=program]\n"
+           "\t[--version] source ... directory\n",
            getprogname());
        exit(1);
 }
Index: rsync.1
===================================================================
RCS file: /cvs/src/usr.bin/rsync/rsync.1,v
retrieving revision 1.18
diff -u -p -r1.18 rsync.1
--- rsync.1     6 May 2019 15:44:34 -0000       1.18
+++ rsync.1     8 Aug 2019 13:41:02 -0000
@@ -24,6 +24,7 @@
 .Nm openrsync
 .Op Fl aDglnoprtvx
 .Op Fl e Ar program
+.Op Fl -address Ns = Ns Ar bind_address
 .Op Fl -del
 .Op Fl -numeric-ids
 .Op Fl -port Ns = Ns Ar service
@@ -50,6 +51,12 @@ The arguments are as follows:
 .It Fl a , -archive
 Shorthand for
 .Fl Dgloprt .
+.It Fl -address Ns = Ns Ar bind_address
+Use
+.Ar bind_address
+on the local machine as the source address of the connection.
+Only useful when connecting to an rsync daemon and on systems with more than
+one address.
 .It Fl D
 Also transfer device and special files.
 Shorthand for
Index: socket.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/socket.c,v
retrieving revision 1.25
diff -u -p -r1.25 socket.c
--- socket.c    3 Jun 2019 15:37:48 -0000       1.25
+++ socket.c    8 Aug 2019 13:41:02 -0000
@@ -46,11 +46,34 @@ struct      source {
 };
 
 /*
+ * Try to bind to a local IP address matching the addres family passed.
+ * Return -1 on failure to bind to any address, 0 on success.
+ */
+static int
+inet_bind(int s, sa_family_t af, const struct source *bsrc, size_t bsrcsz)
+{
+       size_t i;
+
+       if (bsrc == NULL)
+               return 0;
+       for (i = 0; i < bsrcsz; i++) {
+               if (bsrc[i].family != af)
+                       continue;
+               if (bind(s, (const struct sockaddr *)&bsrc[i].sa,
+                   bsrc[i].salen) == -1)
+                       continue;
+               return 0;
+       }
+       return -1;
+}
+
+/*
  * Connect to an IP address representing a host.
  * Return <0 on failure, 0 on try another address, >0 on success.
  */
 static int
-inet_connect(int *sd, const struct source *src, const char *host)
+inet_connect(int *sd, const struct source *src, const char *host,
+    const struct source *bsrc, size_t bsrcsz)
 {
        int      c, flags;
 
@@ -64,6 +87,11 @@ inet_connect(int *sd, const struct sourc
                return -1;
        }
 
+       if (inet_bind(*sd, src->family, bsrc, bsrcsz) == -1) {
+               ERR("bind");
+               return -1;
+       }
+
        /*
         * Initiate blocking connection.
         * We use the blocking connect() instead of passing NONBLOCK to
@@ -102,11 +130,12 @@ inet_connect(int *sd, const struct sourc
  * in this case).
  */
 static struct source *
-inet_resolve(struct sess *sess, const char *host, size_t *sz)
+inet_resolve(struct sess *sess, const char *host, size_t *sz, int passive)
 {
        struct addrinfo  hints, *res0, *res;
        struct sockaddr *sa;
        struct source   *src = NULL;
+       const char      *port = sess->opts->port;
        size_t           i, srcsz = 0;
        int              error;
 
@@ -115,8 +144,12 @@ inet_resolve(struct sess *sess, const ch
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
+       if (passive) {
+               hints.ai_flags = SOCK_STREAM;
+               port = NULL;
+       }
 
-       error = getaddrinfo(host, sess->opts->port, &hints, &res0);
+       error = getaddrinfo(host, port, &hints, &res0);
 
        LOG2("resolving: %s", host);
 
@@ -239,8 +272,8 @@ int
 rsync_connect(const struct opts *opts, int *sd, const struct fargs *f)
 {
        struct sess       sess;
-       struct source    *src = NULL;
-       size_t            i, srcsz = 0;
+       struct source    *src = NULL, *bsrc = NULL;
+       size_t            i, srcsz = 0, bsrcsz = 0;
        int               c, rc = 1;
 
        if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns 
getpw unveil",
@@ -254,10 +287,16 @@ rsync_connect(const struct opts *opts, i
 
        /* Resolve all IP addresses from the host. */
 
-       if ((src = inet_resolve(&sess, f->host, &srcsz)) == NULL) {
+       if ((src = inet_resolve(&sess, f->host, &srcsz, 0)) == NULL) {
                ERRX1("inet_resolve");
                exit(1);
        }
+       if (opts->address != NULL)
+               if ((bsrc = inet_resolve(&sess, opts->address, &bsrcsz, 1)) ==
+                   NULL) {
+                       ERRX1("inet_resolve bind");
+                       exit(1);
+               }
 
        /* Drop the DNS pledge. */
 
@@ -274,7 +313,7 @@ rsync_connect(const struct opts *opts, i
 
        assert(srcsz);
        for (i = 0; i < srcsz; i++) {
-               c = inet_connect(sd, &src[i], f->host);
+               c = inet_connect(sd, &src[i], f->host, bsrc, bsrcsz);
                if (c < 0) {
                        ERRX1("inet_connect");
                        goto out;
@@ -297,9 +336,11 @@ rsync_connect(const struct opts *opts, i
        LOG2("connected: %s, %s", src[i].ip, f->host);
 
        free(src);
+       free(bsrc);
        return 0;
 out:
        free(src);
+       free(bsrc);
        if (*sd != -1)
                close(*sd);
        return rc;

Reply via email to