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 -0000 1.104
> +++ fetch.c 25 Apr 2012 02:12:13 -0000
> @@ -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 -0000 1.81
> +++ ftp.1 25 Apr 2012 02:12:13 -0000
> @@ -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
> ===================================================================
> RCS file: /cvs/src/usr.bin/ftp/ftp.c,v
> retrieving revision 1.81
> diff -u -p -r1.81 ftp.c
> --- ftp.c 3 Sep 2010 03:49:37 -0000 1.81
> +++ ftp.c 25 Apr 2012 02:12:14 -0000
> @@ -114,7 +114,7 @@ hookup(char *host, char *port)
> {
> int s, tos, error;
> static char hostnamebuf[MAXHOSTNAMELEN];
> - struct addrinfo hints, *res, *res0;
> + struct addrinfo hints, *res, *res0, *ares;
> char hbuf[NI_MAXHOST];
> char *cause = "unknown";
> socklen_t namelen;
> @@ -163,6 +163,25 @@ hookup(char *host, char *port)
> else
> strlcpy(hostnamebuf, host, sizeof(hostnamebuf));
> hostname = hostnamebuf;
> +
> +#ifndef SMALL
> + if (srcaddr) {
> + struct addrinfo ahints;
> +
> + memset(&ahints, 0, sizeof(ahints));
> + ahints.ai_family = family;
> + ahints.ai_socktype = SOCK_STREAM;
> + ahints.ai_flags |= AI_NUMERICHOST;
> + ahints.ai_protocol = 0;
> +
> + error = getaddrinfo(srcaddr, NULL, &ahints, &ares);
> + if (error) {
> + warnx("%s: %s", gai_strerror(error), srcaddr);
> + code = -1;
> + return (0);
> + }
> + }
> +#endif /* !SMALL */
>
> s = -1;
> for (res = res0; res; res = res->ai_next) {
> @@ -183,6 +202,25 @@ hookup(char *host, char *port)
> cause = "socket";
> 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) {
> + cause = "bind";
> + error = errno;
> + close(s);
> + errno = error;
> + s = -1;
> + continue;
> + }
> + }
> +#endif /* !SMALL */
> while ((error = connect(s, res->ai_addr, res->ai_addrlen)) < 0
> && errno == EINTR) {
> ;
> @@ -218,6 +256,12 @@ hookup(char *host, char *port)
> namelen = res->ai_addrlen;
> freeaddrinfo(res0);
> res0 = res = NULL;
> +#ifndef SMALL
> + if (srcaddr) {
> + freeaddrinfo(ares);
> + ares = NULL;
> + }
> +#endif /* !SMALL */
> if (getsockname(s, (struct sockaddr *)&myctladdr, &namelen) < 0) {
> warn("getsockname");
> code = -1;
> @@ -1240,12 +1284,31 @@ initconn(void)
> u_int af, hal, pal;
> char *pasvcmd = NULL;
> socklen_t namelen;
> + struct addrinfo *ares;
>
> if (myctladdr.su_family == AF_INET6
> && (IN6_IS_ADDR_LINKLOCAL(&myctladdr.su_sin6.sin6_addr)
> || IN6_IS_ADDR_SITELOCAL(&myctladdr.su_sin6.sin6_addr))) {
> warnx("use of scoped address can be troublesome");
> }
> +#ifndef SMALL
> + if (srcaddr) {
> + struct addrinfo ahints;
> +
> + memset(&ahints, 0, sizeof(ahints));
> + ahints.ai_family = family;
> + ahints.ai_socktype = SOCK_STREAM;
> + ahints.ai_flags |= AI_NUMERICHOST;
> + ahints.ai_protocol = 0;
> +
> + error = getaddrinfo(srcaddr, NULL, &ahints, &ares);
> + if (error) {
> + warnx("%s: %s", gai_strerror(error), srcaddr);
> + code = -1;
> + return (0);
> + }
> + }
> +#endif /* !SMALL */
> reinit:
> if (passivemode) {
> data_addr = myctladdr;
> @@ -1255,6 +1318,13 @@ reinit:
> return (1);
> }
> #ifndef SMALL
> + if (srcaddr) {
> + if (bind(data, ares->ai_addr, ares->ai_addrlen) < 0) {
> + warn("bind");
> + close(data);
> + return (1);
> + }
> + }
> if ((options & SO_DEBUG) &&
> setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
> sizeof(on)) < 0)
> Index: ftp_var.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/ftp/ftp_var.h,v
> retrieving revision 1.31
> diff -u -p -r1.31 ftp_var.h
> --- ftp_var.h 2 Jul 2010 22:01:10 -0000 1.31
> +++ ftp_var.h 25 Apr 2012 02:12:14 -0000
> @@ -165,6 +165,7 @@ size_t cursor_argc; /* location of cu
> size_t cursor_argo; /* offset of cursor in
> margv[cursor_argc] */
> char *cookiefile; /* cookie jar to use */
> int resume; /* continue transfer */
> +char *srcaddr; /* source address to bind to */
> #endif /* !SMALL */
>
> off_t bytes; /* current # of bytes read */
> Index: main.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/ftp/main.c,v
> retrieving revision 1.81
> diff -u -p -r1.81 main.c
> --- main.c 29 Jun 2010 23:12:33 -0000 1.81
> +++ main.c 25 Apr 2012 02:12:14 -0000
> @@ -114,6 +114,7 @@ main(volatile int argc, char *argv[])
> hist = NULL;
> cookiefile = NULL;
> resume = 0;
> + srcaddr = NULL;
> marg_sl = sl_init();
> #endif /* !SMALL */
> mark = HASHBYTES;
> @@ -174,7 +175,7 @@ main(volatile int argc, char *argv[])
> cookiefile = getenv("http_cookies");
> #endif /* !SMALL */
>
> - while ((ch = getopt(argc, argv, "46AaCc:dEegik:mno:pP:r:tvV")) != -1) {
> + while ((ch = getopt(argc, argv, "46AaCc:dEegik:mno:pP:r:s:tvV")) != -1)
> {
> switch (ch) {
> case '4':
> family = PF_INET;
> @@ -275,6 +276,12 @@ main(volatile int argc, char *argv[])
> }
> break;
>
> + case 's':
> +#ifndef SMALL
> + srcaddr = optarg;
> +#endif /* !SMALL */
> + break;
> +
> case 't':
> trace = 1;
> break;
> @@ -756,15 +763,21 @@ usage(void)
> " %s [-C] "
> #endif /* !SMALL */
> "[-o output] "
> +#ifndef SMALL
> + "[-s srcaddr] "
> +#endif /* !SMALL */
> "ftp://[user:password@]host[:port]/file[/] ...\n"
> " %s "
> #ifndef SMALL
> "[-C] [-c cookie] "
> #endif /* !SMALL */
> "[-o output] "
> +#ifndef SMALL
> + "[-s srcaddr] "
> +#endif /* !SMALL */
> "http://host[:port]/file ...\n"
> #ifndef SMALL
> - " %s [-C] [-c cookie] [-o output] "
> + " %s [-C] [-c cookie] [-o output] [-s srcaddr] "
> "https://host[:port]/file ...\n"
> #endif /* !SMALL */
> " %s "
> @@ -772,12 +785,19 @@ usage(void)
> "[-C] "
> #endif /* !SMALL */
> "[-o output] "
> +#ifndef SMALL
> + "[-s srcaddr] "
> +#endif /* !SMALL */
> "file:file ...\n"
> " %s "
> #ifndef SMALL
> "[-C] "
> #endif /* !SMALL */
> - "[-o output] host:/file[/] ...\n",
> + "[-o output] "
> +#ifndef SMALL
> + "[-s srcaddr] "
> +#endif /* !SMALL */
> + "host:/file[/] ...\n",
> #ifndef SMALL
> __progname, __progname, __progname, __progname, __progname,
> __progname);