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;