Re: ftp(1): new -s flag to specify source IP address

2012-04-30 Thread Christiano F. Haesbaert
I'm ok with the diff, can I get another ok before commiting ?

On Tue, Apr 24, 2012 at 10:53:34PM -0400, Lawrence Teo wrote:
> On Wed, Apr 18, 2012 at 11:58:26PM -0400, Lawrence Teo wrote:
> > This diff adds a -s flag to ftp(1) to let the user specify the
> > source IP address of the connection.  This is useful when using
> > ftp(1) over VPN tunnels or when an alternate source IP is required
> > to fetch a file from a FTP/HTTP/HTTPS server due to access control
> > policies.
> 
> I have revised this diff based on feedback from haesbaert@.  The primary
> change is to use AI_NUMERICHOST in the hints addrinfo struct that's
> passed to getaddrinfo().
> 
> The resulting ftp(1) binary still passes all 48 tests from my test
> script at http://lteo.net/stuff/ftp-test
> 
> Comments and more testing are welcome!
> 
> Thanks,
> Lawrence
> 
> 
> Index: fetch.c
> ===
> RCS file: /cvs/src/usr.bin/ftp/fetch.c,v
> retrieving revision 1.104
> diff -u -p -r1.104 fetch.c
> --- fetch.c   23 Apr 2012 21:22:02 -  1.104
> +++ fetch.c   25 Apr 2012 02:12:13 -
> @@ -179,7 +179,7 @@ url_get(const char *origline, const char
>   char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
>   char *epath, *redirurl, *loctail;
>   int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
> - struct addrinfo hints, *res0, *res;
> + struct addrinfo hints, *res0, *res, *ares = NULL;
>   const char * volatile savefile;
>   char * volatile proxyurl = NULL;
>   char *cookie = NULL;
> @@ -198,6 +198,7 @@ url_get(const char *origline, const char
>  #endif /* !SMALL */
>   SSL *ssl = NULL;
>   int status;
> + int save_errno;
>  
>   direction = "received";
>  
> @@ -490,6 +491,17 @@ noslash:
>   goto cleanup_url_get;
>   }
>  
> +#ifndef SMALL
> + if (srcaddr) {
> + hints.ai_flags |= AI_NUMERICHOST;
> + error = getaddrinfo(srcaddr, NULL, &hints, &ares);
> + if (error) {
> + warnx("%s: %s", gai_strerror(error), srcaddr);
> + goto cleanup_url_get;
> + }
> + }
> +#endif /* !SMALL */
> +
>   s = -1;
>   for (res = res0; res; res = res->ai_next) {
>   if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
> @@ -504,10 +516,28 @@ noslash:
>   continue;
>   }
>  
> +#ifndef SMALL
> + if (srcaddr) {
> + if (ares->ai_family != res->ai_family) {
> + close(s);
> + s = -1;
> + errno = EINVAL;
> + cause = "bind";
> + continue;
> + }
> + if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) {
> + save_errno = errno;
> + close(s);
> + errno = save_errno;
> + s = -1;
> + cause = "bind";
> + continue;
> + }
> + }
> +#endif /* !SMALL */
> +
>  again:
>   if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
> - int save_errno;
> -
>   if (errno == EINTR)
>   goto again;
>   save_errno = errno;
> @@ -532,6 +562,10 @@ again:
>   break;
>   }
>   freeaddrinfo(res0);
> +#ifndef SMALL
> + if (srcaddr)
> + freeaddrinfo(ares);
> +#endif /* !SMALL */
>   if (s < 0) {
>   warn("%s", cause);
>   goto cleanup_url_get;
> Index: ftp.1
> ===
> RCS file: /cvs/src/usr.bin/ftp/ftp.1,v
> retrieving revision 1.81
> diff -u -p -r1.81 ftp.1
> --- ftp.1 26 Jul 2010 21:31:34 -  1.81
> +++ ftp.1 25 Apr 2012 02:12:13 -
> @@ -42,10 +42,12 @@
>  .Op Fl k Ar seconds
>  .Op Fl P Ar port
>  .Op Fl r Ar seconds
> +.Op Fl s Ar srcaddr
>  .Op Ar host Op Ar port
>  .Nm ftp
>  .Op Fl C
>  .Op Fl o Ar output
> +.Op Fl s Ar srcaddr
>  .Sm off
>  .No ftp:// Oo Ar user : password No @
>  .Oc Ar host Oo : Ar port
> @@ -57,6 +59,7 @@
>  .Op Fl C
>  .Op Fl c Ar cookie
>  .Op Fl o Ar output
> +.Op Fl s Ar srcaddr
>  .Sm off
>  .No http:// Ar host Oo : Ar port
>  .Oc No / Ar file
> @@ -66,6 +69,7 @@
>  .Op Fl C
>  .Op Fl c Ar cookie
>  .Op Fl o Ar output
> +.Op Fl s Ar srcaddr
>  .Sm off
>  .No https:// Ar host Oo : Ar port
>  .Oc No / Ar file
> @@ -74,6 +78,7 @@
>  .Nm ftp
>  .Op Fl C
>  .Op Fl o Ar output
> +.Op Fl s Ar srcaddr
>  .Sm off
>  .No file: Ar file
>  .Sm on
> @@ -81,6 +86,7 @@
>  .Nm ftp
>  .Op Fl C
>  .Op Fl o Ar output
> +.Op Fl s Ar srcaddr
>  .Sm off
>  .Ar host : No / Ar file Oo /
>  .Oc
> @@ -220,6 +226,12 @@ if the server does not suppor

Re: ftp(1): new -s flag to specify source IP address

2012-04-24 Thread Lawrence Teo
On Wed, Apr 18, 2012 at 11:58:26PM -0400, Lawrence Teo wrote:
> This diff adds a -s flag to ftp(1) to let the user specify the
> source IP address of the connection.  This is useful when using
> ftp(1) over VPN tunnels or when an alternate source IP is required
> to fetch a file from a FTP/HTTP/HTTPS server due to access control
> policies.

I have revised this diff based on feedback from haesbaert@.  The primary
change is to use AI_NUMERICHOST in the hints addrinfo struct that's
passed to getaddrinfo().

The resulting ftp(1) binary still passes all 48 tests from my test
script at http://lteo.net/stuff/ftp-test

Comments and more testing are welcome!

Thanks,
Lawrence


Index: fetch.c
===
RCS file: /cvs/src/usr.bin/ftp/fetch.c,v
retrieving revision 1.104
diff -u -p -r1.104 fetch.c
--- fetch.c 23 Apr 2012 21:22:02 -  1.104
+++ fetch.c 25 Apr 2012 02:12:13 -
@@ -179,7 +179,7 @@ url_get(const char *origline, const char
char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
char *epath, *redirurl, *loctail;
int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
-   struct addrinfo hints, *res0, *res;
+   struct addrinfo hints, *res0, *res, *ares = NULL;
const char * volatile savefile;
char * volatile proxyurl = NULL;
char *cookie = NULL;
@@ -198,6 +198,7 @@ url_get(const char *origline, const char
 #endif /* !SMALL */
SSL *ssl = NULL;
int status;
+   int save_errno;
 
direction = "received";
 
@@ -490,6 +491,17 @@ noslash:
goto cleanup_url_get;
}
 
+#ifndef SMALL
+   if (srcaddr) {
+   hints.ai_flags |= AI_NUMERICHOST;
+   error = getaddrinfo(srcaddr, NULL, &hints, &ares);
+   if (error) {
+   warnx("%s: %s", gai_strerror(error), srcaddr);
+   goto cleanup_url_get;
+   }
+   }
+#endif /* !SMALL */
+
s = -1;
for (res = res0; res; res = res->ai_next) {
if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
@@ -504,10 +516,28 @@ noslash:
continue;
}
 
+#ifndef SMALL
+   if (srcaddr) {
+   if (ares->ai_family != res->ai_family) {
+   close(s);
+   s = -1;
+   errno = EINVAL;
+   cause = "bind";
+   continue;
+   }
+   if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) {
+   save_errno = errno;
+   close(s);
+   errno = save_errno;
+   s = -1;
+   cause = "bind";
+   continue;
+   }
+   }
+#endif /* !SMALL */
+
 again:
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
-   int save_errno;
-
if (errno == EINTR)
goto again;
save_errno = errno;
@@ -532,6 +562,10 @@ again:
break;
}
freeaddrinfo(res0);
+#ifndef SMALL
+   if (srcaddr)
+   freeaddrinfo(ares);
+#endif /* !SMALL */
if (s < 0) {
warn("%s", cause);
goto cleanup_url_get;
Index: ftp.1
===
RCS file: /cvs/src/usr.bin/ftp/ftp.1,v
retrieving revision 1.81
diff -u -p -r1.81 ftp.1
--- ftp.1   26 Jul 2010 21:31:34 -  1.81
+++ ftp.1   25 Apr 2012 02:12:13 -
@@ -42,10 +42,12 @@
 .Op Fl k Ar seconds
 .Op Fl P Ar port
 .Op Fl r Ar seconds
+.Op Fl s Ar srcaddr
 .Op Ar host Op Ar port
 .Nm ftp
 .Op Fl C
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No ftp:// Oo Ar user : password No @
 .Oc Ar host Oo : Ar port
@@ -57,6 +59,7 @@
 .Op Fl C
 .Op Fl c Ar cookie
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No http:// Ar host Oo : Ar port
 .Oc No / Ar file
@@ -66,6 +69,7 @@
 .Op Fl C
 .Op Fl c Ar cookie
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No https:// Ar host Oo : Ar port
 .Oc No / Ar file
@@ -74,6 +78,7 @@
 .Nm ftp
 .Op Fl C
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No file: Ar file
 .Sm on
@@ -81,6 +86,7 @@
 .Nm ftp
 .Op Fl C
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .Ar host : No / Ar file Oo /
 .Oc
@@ -220,6 +226,12 @@ if the server does not support passive c
 .It Fl r Ar seconds
 Retry to connect if failed, pausing for number of
 .Ar seconds .
+.It Fl s Ar srcaddr
+Use
+.Ar srcaddr
+on the local machine as the source address
+of the connection.
+Only useful on systems with more than one address.
 .It Fl t
 Enables packet tracing.
 .It Fl V
Index: ftp.c
=

ftp(1): new -s flag to specify source IP address

2012-04-18 Thread Lawrence Teo
This diff adds a -s flag to ftp(1) to let the user specify the
source IP address of the connection.  This is useful when using
ftp(1) over VPN tunnels or when an alternate source IP is required
to fetch a file from a FTP/HTTP/HTTPS server due to access control
policies.

The -s flag is present in ftp(1) on FreeBSD, NetBSD, DragonFly BSD,
and even OS X so it is very portable in shell scripts. :)

I have tested -s with both IPv4 and IPv6 with a variety of different
flags.  The following is the test script that I used during
development to confirm that this diff does not break existing
behavior and to ensure that -s works with related flags:

http://lteo.net/stuff/ftp-test

These are the test results:

http://lteo.net/stuff/ftp-test-results.txt
(with my IPv6 address masked out)

Note: If you would like to replicate the test with my test script,
please assign two IPv4 addresses and two IPv6 addresses to your
egress interface.  If you don't have IPv6 connectivity, you can run
the script in IPv4-only mode with "ftp-test -4"

Comments and feedback would be appreciated.

Thanks,
Lawrence


Index: fetch.c
===
RCS file: /cvs/src/usr.bin/ftp/fetch.c,v
retrieving revision 1.103
diff -u -p -r1.103 fetch.c
--- fetch.c 25 Aug 2010 20:32:37 -  1.103
+++ fetch.c 15 Apr 2012 02:08:43 -
@@ -179,7 +179,7 @@ url_get(const char *origline, const char
char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
char *epath, *redirurl, *loctail;
int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
-   struct addrinfo hints, *res0, *res;
+   struct addrinfo hints, *res0, *res, *ares = NULL;
const char * volatile savefile;
char * volatile proxyurl = NULL;
char *cookie = NULL;
@@ -198,6 +198,7 @@ url_get(const char *origline, const char
 #endif /* !SMALL */
SSL *ssl = NULL;
int status;
+   int save_errno;
 
direction = "received";
 
@@ -490,6 +491,16 @@ noslash:
goto cleanup_url_get;
}
 
+#ifndef SMALL
+   if (srcaddr) {
+   error = getaddrinfo(srcaddr, NULL, &hints, &ares);
+   if (error) {
+   warnx("%s: %s", gai_strerror(error), srcaddr);
+   goto cleanup_url_get;
+   }
+   }
+#endif /* !SMALL */
+
s = -1;
for (res = res0; res; res = res->ai_next) {
if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
@@ -504,6 +515,26 @@ noslash:
continue;
}
 
+#ifndef SMALL
+   if (srcaddr) {
+   if (ares->ai_family != res->ai_family) {
+   close(s);
+   s = -1;
+   errno = EINVAL;
+   cause = "bind";
+   continue;
+   }
+   if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) {
+   save_errno = errno;
+   close(s);
+   errno = save_errno;
+   s = -1;
+   cause = "bind";
+   continue;
+   }
+   }
+#endif /* !SMALL */
+
 again:
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
int save_errno;
@@ -532,6 +563,10 @@ again:
break;
}
freeaddrinfo(res0);
+#ifndef SMALL
+   if (srcaddr)
+   freeaddrinfo(ares);
+#endif /* !SMALL */
if (s < 0) {
warn("%s", cause);
goto cleanup_url_get;
Index: ftp.1
===
RCS file: /cvs/src/usr.bin/ftp/ftp.1,v
retrieving revision 1.81
diff -u -p -r1.81 ftp.1
--- ftp.1   26 Jul 2010 21:31:34 -  1.81
+++ ftp.1   15 Apr 2012 02:08:43 -
@@ -42,10 +42,12 @@
 .Op Fl k Ar seconds
 .Op Fl P Ar port
 .Op Fl r Ar seconds
+.Op Fl s Ar srcaddr
 .Op Ar host Op Ar port
 .Nm ftp
 .Op Fl C
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No ftp:// Oo Ar user : password No @
 .Oc Ar host Oo : Ar port
@@ -57,6 +59,7 @@
 .Op Fl C
 .Op Fl c Ar cookie
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No http:// Ar host Oo : Ar port
 .Oc No / Ar file
@@ -66,6 +69,7 @@
 .Op Fl C
 .Op Fl c Ar cookie
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No https:// Ar host Oo : Ar port
 .Oc No / Ar file
@@ -74,6 +78,7 @@
 .Nm ftp
 .Op Fl C
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .No file: Ar file
 .Sm on
@@ -81,6 +86,7 @@
 .Nm ftp
 .Op Fl C
 .Op Fl o Ar output
+.Op Fl s Ar srcaddr
 .Sm off
 .Ar host : No / Ar file Oo /
 .Oc
@@ -220,6 +226,12 @@ if the server does not support passive c
 .It Fl r Ar seconds
 Retry to connect if failed, pausing for number o