On Sat, 09 Jul 2022 12:53:17 +0100, Stuart Henderson wrote:

> I'm trying to teach ftp(1) to do something like gui web browsers do
> and reduce the HTTP/HTTPS connection timeout from the default (75 seconds)
> if there are multiple addresses behind a hostname.
>
> There's an existing connection timeout mechanism that I thought it
> might make sense to reuse...
>
>      -w seconds
>              For URL format connections to HTTP/HTTPS servers, abort a slow
>              connection after seconds.

Here's a diff to implement timed_connect().  It replaces the alarm
used for slow connections with non-blocking connect(2) and ppoll(2).
I've also replaced the other connect(2) + connect_wait() calls with
timed_connect() so the -w option now works for more that just http.

 - todd

Index: usr.bin/ftp/extern.h
===================================================================
RCS file: /cvs/src/usr.bin/ftp/extern.h,v
retrieving revision 1.52
diff -u -p -u -r1.52 extern.h
--- usr.bin/ftp/extern.h        2 Feb 2021 12:58:42 -0000       1.52
+++ usr.bin/ftp/extern.h        13 Sep 2022 16:43:30 -0000
@@ -62,6 +62,7 @@
  */
 
 #include <sys/types.h>
+#include <sys/socket.h>
 
 void   abort_remote(FILE *);
 void   abortpt(int);
@@ -75,7 +76,6 @@ void  cmdabort(int);
 void   cmdscanner(int);
 int    command(const char *, ...);
 int    confirm(const char *, const char *);
-int    connect_wait(int);
 FILE   *dataconn(const char *);
 int    foregroundproc(void);
 int    fileindir(const char *, const char *);
@@ -109,6 +109,7 @@ void        sethash(int, char **);
 void   setpeer(int, char **);
 void   setttywidth(int);
 char   *slurpstring(void);
+int    timed_connect(int s, const struct sockaddr *, socklen_t, int);
 
 __dead void    usage(void);
 
Index: usr.bin/ftp/fetch.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/fetch.c,v
retrieving revision 1.209
diff -u -p -u -r1.209 fetch.c
--- usr.bin/ftp/fetch.c 8 Sep 2022 11:12:44 -0000       1.209
+++ usr.bin/ftp/fetch.c 13 Sep 2022 16:40:00 -0000
@@ -173,14 +173,6 @@ url_encode(const char *path)
        return (epath);
 }
 
-/* ARGSUSED */
-static void
-tooslow(int signo)
-{
-       dprintf(STDERR_FILENO, "%s: connect taking too long\n", __progname);
-       _exit(2);
-}
-
 /*
  * Copy a local file (used by the OpenBSD installer).
  * Returns -1 on failure, 0 on success
@@ -604,14 +596,8 @@ noslash:
                }
 #endif /* !SMALL */
 
-               if (connect_timeout) {
-                       (void)signal(SIGALRM, tooslow);
-                       alarmtimer(connect_timeout);
-               }
-
-               for (error = connect(fd, res->ai_addr, res->ai_addrlen);
-                   error != 0 && errno == EINTR; error = connect_wait(fd))
-                       continue;
+               error = timed_connect(fd, res->ai_addr, res->ai_addrlen,
+                   connect_timeout);
                if (error != 0) {
                        save_errno = errno;
                        close(fd);
@@ -700,11 +686,6 @@ noslash:
        }
 #endif
 
-       if (connect_timeout) {
-               signal(SIGALRM, SIG_DFL);
-               alarmtimer(0);
-       }
-
        /*
         * Construct and send the request. Proxy requests don't want leading /.
         */
@@ -1242,7 +1223,6 @@ aborthttp(int signo)
 {
        const char errmsg[] = "\nfetch aborted.\n";
 
-       alarmtimer(0);
        write(fileno(ttyout), errmsg, sizeof(errmsg) - 1);
        longjmp(httpabort, 1);
 }
Index: usr.bin/ftp/ftp.1
===================================================================
RCS file: /cvs/src/usr.bin/ftp/ftp.1,v
retrieving revision 1.123
diff -u -p -u -r1.123 ftp.1
--- usr.bin/ftp/ftp.1   27 Mar 2022 20:09:12 -0000      1.123
+++ usr.bin/ftp/ftp.1   13 Sep 2022 16:49:38 -0000
@@ -320,9 +320,9 @@ Forces
 to show all responses from the remote server, as well
 as report on data transfer statistics.
 .It Fl w Ar seconds
-For URL format connections to HTTP/HTTPS servers, abort a
-slow connection after
-.Ar seconds .
+Wait for
+.Ar seconds
+for the remote server to connect before giving up.
 .El
 .Pp
 The host with which
Index: usr.bin/ftp/ftp.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/ftp.c,v
retrieving revision 1.107
diff -u -p -u -r1.107 ftp.c
--- usr.bin/ftp/ftp.c   18 Nov 2019 04:37:35 -0000      1.107
+++ usr.bin/ftp/ftp.c   13 Sep 2022 16:43:03 -0000
@@ -212,9 +212,8 @@ hookup(char *host, char *port)
                        }
                }
 #endif /* !SMALL */
-               for (error = connect(s, res->ai_addr, res->ai_addrlen);
-                   error != 0 && errno == EINTR; error = connect_wait(s))
-                       continue;
+               error = timed_connect(s, res->ai_addr, res->ai_addrlen,
+                   connect_timeout);
                if (error != 0) {
                        /* this "if" clause is to prevent print warning twice */
                        if (verbose && res->ai_next) {
@@ -1509,10 +1508,8 @@ reinit:
                } else
                        goto bad;
 
-               for (error = connect(data, &data_addr.sa, data_addr.sa.sa_len);
-                   error != 0 && errno == EINTR;
-                   error = connect_wait(data))
-                       continue;
+               error = timed_connect(data, &data_addr.sa, data_addr.sa.sa_len,
+                   connect_timeout);
                if (error != 0) {
                        if (activefallback) {
                                (void)close(data);
Index: usr.bin/ftp/util.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/util.c,v
retrieving revision 1.95
diff -u -p -u -r1.95 util.c
--- usr.bin/ftp/util.c  2 Feb 2021 12:58:42 -0000       1.95
+++ usr.bin/ftp/util.c  13 Sep 2022 16:46:26 -0000
@@ -1077,6 +1077,84 @@ controlediting(void)
 #endif /* !SMALL */
 
 /*
+ * connect(2) with an optional timeout if secs > 0.
+ */
+int
+timed_connect(int s, const struct sockaddr *name, socklen_t namelen, int secs)
+{
+       struct timespec now, target, timebuf, *timeout = NULL;
+       int flags, nready, optval, ret = -1;
+       socklen_t optlen;
+       struct pollfd pfd;
+
+       if (secs > 0) {
+               timebuf.tv_sec = secs;
+               timebuf.tv_nsec = 0;
+               timeout = &timebuf;
+               clock_gettime(CLOCK_MONOTONIC, &target);
+               timespecadd(&target, timeout, &target);
+       }
+
+       flags = fcntl(s, F_GETFL, 0);
+       if (flags == -1) {
+               warn("fcntl(F_GETFL)");
+               return -1;
+       }
+       if (!(flags & O_NONBLOCK)) {
+               if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
+                       warn("fcntl(F_SETFL)");
+                       return -1;
+               }
+       }
+
+       ret = connect(s, name, namelen);
+       if (ret == 0 || errno != EINPROGRESS)
+               goto done;
+
+       for (;;) {
+               pfd.fd = s;
+               pfd.events = POLLOUT;
+               nready = ppoll(&pfd, 1, timeout, NULL);
+               switch (nready) {
+               case -1:
+                       if (errno != EINTR && errno != EAGAIN) {
+                               warn("ppoll");
+                               goto done;
+                       }
+                       if (timeout == NULL)
+                               continue;
+                       clock_gettime(CLOCK_MONOTONIC, &now);
+                       timespecsub(&now, &target, timeout);
+                       if (timeout->tv_sec >= 0)
+                               continue;
+                       /* FALLTHROUGH */
+               case 0:
+                       errno = ETIMEDOUT;
+                       goto done;
+               default:
+                       optlen = sizeof(optval);
+                       ret = getsockopt(s, SOL_SOCKET, SO_ERROR, &optval,
+                           &optlen);
+                       if (ret == 0 && optval != 0) {
+                               ret = -1;
+                               errno = optval;
+                       }
+                       goto done;
+               }
+       }
+
+done:
+       if (!(flags & O_NONBLOCK)) {
+               if (fcntl(s, F_SETFL, flags) == -1) {
+                       warn("fcntl(F_SETFL)");
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+/*
  * Wait for an asynchronous connect(2) attempt to finish.
  */
 int

Reply via email to