openrsync --address option

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

openrsync --address option

Claudio Jeker
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 | Threaded
Open this post in threaded view
|

Re: openrsync --address option

Stuart Henderson
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;
>

Reply | Threaded
Open this post in threaded view
|

Re: openrsync --address option

Claudio Jeker
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 | Threaded
Open this post in threaded view
|

Re: openrsync --address option

Sebastian Benoit-3
Claudio Jeker([hidden email]) on 2019.08.09 00:16:42 +0200:

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

We definatly will need to add -4/6 options.

>
> I will commit this tomorrow morning so that improvements can happen in
> tree.

ok

> --
> :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 | Threaded
Open this post in threaded view
|

Re: openrsync --address option

Christian Weisgerber
In reply to this post by Stuart Henderson
On 2019-08-08, Stuart Henderson <[hidden email]> wrote:

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

Not sure what scenario you mean exactly.  This works fine for me:

openrsync --address=2003:e5:cf15:c101:76dd:6f32:911c:ce5c -av \
    rsync://anoncvs.spacehopper.org/... ...

--
Christian "naddy" Weisgerber                          [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: openrsync --address option

Stuart Henderson
On 2019/08/09 14:31, Christian Weisgerber wrote:
> On 2019-08-08, Stuart Henderson <[hidden email]> wrote:
>
> > 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.
>
> Not sure what scenario you mean exactly.  This works fine for me:
>
> openrsync --address=2003:e5:cf15:c101:76dd:6f32:911c:ce5c -av \
>     rsync://anoncvs.spacehopper.org/... ...

Ah, you have "family inet6 inet4" in resolv.conf. In that case
--address=$some_v4_address will fail instead.