On Thu, Aug 08, 2019 at 11:06:30PM +0100, Stuart Henderson wrote:
> 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.
> 

I think this has also to do with the way the host (aka destination) lookup
happens. My assumption is that you first try to connect to a IPv4 address
and so there is no binding to the IPv6 address happening.
To be honest, the GPL rsync way of doing things is also not entierly clear to
me. It may be necessary to add -4 and -6 support or maybe force the address
family to match bind_address and destination (or error out if they
mismatch) would be enough.

I will commit this tomorrow morning so that improvements can happen in
tree.
-- 
: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;
> > 
> 

-- 
:wq Claudio

Reply via email to