On 2019/08/08 15:42, Claudio Jeker wrote:
> 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.
Works as expected for IPv4. For IPv6 it only works if you use a hostname that
only has a v6 address and not also a v4 address. In this situation GPL rsync
prints a warning (unless you also use -6), but then moves onto a v6 destination
address.
(Maybe openrsync works if you use an IPv6 literal address as the destination,
but I didn't see a way to do that).
OK for now, but as rpki-client may well end up used in places with fiddly
addressing requirements (which is why you're adding it in the first place ;)
it would be good to improve v6 as a later step.
> --
> :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;
>