"Fossies" - the Fresh Open Source Software Archive

Member "tnftp-20200705/src/util.c" (4 Jul 2020, 37702 Bytes) of package /linux/privat/tnftp-20200705.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "util.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 20151004_vs_20200705.

    1 /*  $NetBSD: util.c,v 1.24 2020/07/04 09:59:07 lukem Exp $  */
    2 /*  from    NetBSD: util.c,v 1.161 2020/06/08 01:33:27 lukem Exp    */
    3 
    4 /*-
    5  * Copyright (c) 1997-2020 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to The NetBSD Foundation
    9  * by Luke Mewburn.
   10  *
   11  * This code is derived from software contributed to The NetBSD Foundation
   12  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
   13  * NASA Ames Research Center.
   14  *
   15  * Redistribution and use in source and binary forms, with or without
   16  * modification, are permitted provided that the following conditions
   17  * are met:
   18  * 1. Redistributions of source code must retain the above copyright
   19  *    notice, this list of conditions and the following disclaimer.
   20  * 2. Redistributions in binary form must reproduce the above copyright
   21  *    notice, this list of conditions and the following disclaimer in the
   22  *    documentation and/or other materials provided with the distribution.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34  * POSSIBILITY OF SUCH DAMAGE.
   35  */
   36 
   37 /*
   38  * Copyright (c) 1985, 1989, 1993, 1994
   39  *  The Regents of the University of California.  All rights reserved.
   40  *
   41  * Redistribution and use in source and binary forms, with or without
   42  * modification, are permitted provided that the following conditions
   43  * are met:
   44  * 1. Redistributions of source code must retain the above copyright
   45  *    notice, this list of conditions and the following disclaimer.
   46  * 2. Redistributions in binary form must reproduce the above copyright
   47  *    notice, this list of conditions and the following disclaimer in the
   48  *    documentation and/or other materials provided with the distribution.
   49  * 3. Neither the name of the University nor the names of its contributors
   50  *    may be used to endorse or promote products derived from this software
   51  *    without specific prior written permission.
   52  *
   53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   63  * SUCH DAMAGE.
   64  */
   65 
   66 #include "tnftp.h"
   67 
   68 #if 0   /* tnftp */
   69 
   70 #include <sys/cdefs.h>
   71 #ifndef lint
   72 __RCSID(" NetBSD: util.c,v 1.161 2020/06/08 01:33:27 lukem Exp  ");
   73 #endif /* not lint */
   74 
   75 /*
   76  * FTP User Program -- Misc support routines
   77  */
   78 #include <sys/param.h>
   79 #include <sys/socket.h>
   80 #include <sys/ioctl.h>
   81 #include <sys/time.h>
   82 #include <netinet/in.h>
   83 #include <arpa/ftp.h>
   84 
   85 #include <ctype.h>
   86 #include <err.h>
   87 #include <errno.h>
   88 #include <fcntl.h>
   89 #include <glob.h>
   90 #include <signal.h>
   91 #include <libgen.h>
   92 #include <limits.h>
   93 #include <locale.h>
   94 #include <netdb.h>
   95 #include <stdio.h>
   96 #include <stdlib.h>
   97 #include <string.h>
   98 #include <termios.h>
   99 #include <time.h>
  100 #include <tzfile.h>
  101 #include <unistd.h>
  102 
  103 #endif  /* tnftp */
  104 
  105 #include "ftp_var.h"
  106 
  107 /*
  108  * Connect to peer server and auto-login, if possible.
  109  */
  110 void
  111 setpeer(int argc, char *argv[])
  112 {
  113     char *host;
  114     const char *port;
  115 
  116     if (argc == 0)
  117         goto usage;
  118     if (connected) {
  119         fprintf(ttyout, "Already connected to %s, use close first.\n",
  120             hostname);
  121         code = -1;
  122         return;
  123     }
  124     if (argc < 2)
  125         (void)another(&argc, &argv, "to");
  126     if (argc < 2 || argc > 3) {
  127  usage:
  128         UPRINTF("usage: %s host-name [port]\n", argv[0]);
  129         code = -1;
  130         return;
  131     }
  132     if (gatemode)
  133         port = gateport;
  134     else
  135         port = ftpport;
  136     if (argc > 2)
  137         port = argv[2];
  138 
  139     if (gatemode) {
  140         if (gateserver == NULL || *gateserver == '\0')
  141             errx(1, "main: gateserver not defined");
  142         host = hookup(gateserver, port);
  143     } else
  144         host = hookup(argv[1], port);
  145 
  146     if (host) {
  147         if (gatemode && verbose) {
  148             fprintf(ttyout,
  149                 "Connecting via pass-through server %s\n",
  150                 gateserver);
  151         }
  152 
  153         connected = 1;
  154         /*
  155          * Set up defaults for FTP.
  156          */
  157         (void)strlcpy(typename, "ascii", sizeof(typename));
  158         type = TYPE_A;
  159         curtype = TYPE_A;
  160         (void)strlcpy(formname, "non-print", sizeof(formname));
  161         form = FORM_N;
  162         (void)strlcpy(modename, "stream", sizeof(modename));
  163         mode = MODE_S;
  164         (void)strlcpy(structname, "file", sizeof(structname));
  165         stru = STRU_F;
  166         (void)strlcpy(bytename, "8", sizeof(bytename));
  167         bytesize = 8;
  168         if (autologin)
  169             (void)ftp_login(argv[1], NULL, NULL);
  170     }
  171 }
  172 
  173 static void
  174 parse_feat(const char *fline)
  175 {
  176 
  177             /*
  178              * work-around broken ProFTPd servers that can't
  179              * even obey RFC 2389.
  180              */
  181     while (*fline && isspace((int)*fline))
  182         fline++;
  183 
  184     if (strcasecmp(fline, "MDTM") == 0)
  185         features[FEAT_MDTM] = 1;
  186     else if (strncasecmp(fline, "MLST", sizeof("MLST") - 1) == 0) {
  187         features[FEAT_MLST] = 1;
  188     } else if (strcasecmp(fline, "REST STREAM") == 0)
  189         features[FEAT_REST_STREAM] = 1;
  190     else if (strcasecmp(fline, "SIZE") == 0)
  191         features[FEAT_SIZE] = 1;
  192     else if (strcasecmp(fline, "TVFS") == 0)
  193         features[FEAT_TVFS] = 1;
  194 }
  195 
  196 /*
  197  * Determine the remote system type (SYST) and features (FEAT).
  198  * Call after a successful login (i.e, connected = -1)
  199  */
  200 void
  201 getremoteinfo(void)
  202 {
  203     int overbose, i;
  204 
  205     overbose = verbose;
  206     if (ftp_debug == 0)
  207         verbose = -1;
  208 
  209             /* determine remote system type */
  210     if (command("SYST") == COMPLETE) {
  211         if (overbose) {
  212             int os_len = strcspn(reply_string + 4, " \r\n\t");
  213             if (os_len > 1 && reply_string[4 + os_len - 1] == '.')
  214                 os_len--;
  215             fprintf(ttyout, "Remote system type is %.*s.\n",
  216                 os_len, reply_string + 4);
  217         }
  218         /*
  219          * Decide whether we should default to bninary.
  220          * Traditionally checked for "215 UNIX Type: L8", but
  221          * some printers report "Linux" ! so be more forgiving.
  222          * In reality we probably almost never want text any more.
  223          */
  224         if (!strncasecmp(reply_string + 4, "unix", 4) ||
  225             !strncasecmp(reply_string + 4, "linux", 5)) {
  226             if (proxy)
  227                 unix_proxy = 1;
  228             else
  229                 unix_server = 1;
  230             /*
  231              * Set type to 0 (not specified by user),
  232              * meaning binary by default, but don't bother
  233              * telling server.  We can use binary
  234              * for text files unless changed by the user.
  235              */
  236             type = 0;
  237             (void)strlcpy(typename, "binary", sizeof(typename));
  238             if (overbose)
  239                 fprintf(ttyout,
  240                 "Using %s mode to transfer files.\n",
  241                 typename);
  242         } else {
  243             if (proxy)
  244                 unix_proxy = 0;
  245             else
  246                 unix_server = 0;
  247             if (overbose &&
  248                 !strncmp(reply_string, "215 TOPS20", 10))
  249                 fputs(
  250 "Remember to set tenex mode when transferring binary files from this machine.\n",
  251                     ttyout);
  252         }
  253     }
  254 
  255             /* determine features (if any) */
  256     for (i = 0; i < FEAT_max; i++)
  257         features[i] = -1;
  258     reply_callback = parse_feat;
  259     if (command("FEAT") == COMPLETE) {
  260         for (i = 0; i < FEAT_max; i++) {
  261             if (features[i] == -1)
  262                 features[i] = 0;
  263         }
  264         features[FEAT_FEAT] = 1;
  265     } else
  266         features[FEAT_FEAT] = 0;
  267 #ifndef NO_DEBUG
  268     if (ftp_debug) {
  269 #define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)])
  270         DEBUG_FEAT(FEAT_FEAT);
  271         DEBUG_FEAT(FEAT_MDTM);
  272         DEBUG_FEAT(FEAT_MLST);
  273         DEBUG_FEAT(FEAT_REST_STREAM);
  274         DEBUG_FEAT(FEAT_SIZE);
  275         DEBUG_FEAT(FEAT_TVFS);
  276 #undef DEBUG_FEAT
  277     }
  278 #endif
  279     reply_callback = NULL;
  280 
  281     verbose = overbose;
  282 }
  283 
  284 /*
  285  * Reset the various variables that indicate connection state back to
  286  * disconnected settings.
  287  * The caller is responsible for issuing any commands to the remote server
  288  * to perform a clean shutdown before this is invoked.
  289  */
  290 void
  291 cleanuppeer(void)
  292 {
  293 
  294     if (cout)
  295         (void)fclose(cout);
  296     cout = NULL;
  297     connected = 0;
  298     unix_server = 0;
  299     unix_proxy = 0;
  300             /*
  301              * determine if anonftp was specifically set with -a
  302              * (1), or implicitly set by auto_fetch() (2). in the
  303              * latter case, disable after the current xfer
  304              */
  305     if (anonftp == 2)
  306         anonftp = 0;
  307     data = -1;
  308     epsv4bad = 0;
  309     epsv6bad = 0;
  310     if (username)
  311         free(username);
  312     username = NULL;
  313     if (!proxy)
  314         macnum = 0;
  315 }
  316 
  317 /*
  318  * Top-level signal handler for interrupted commands.
  319  */
  320 void
  321 intr(int signo)
  322 {
  323 
  324     sigint_raised = 1;
  325     alarmtimer(0);
  326     if (fromatty)
  327         write(fileno(ttyout), "\n", 1);
  328     siglongjmp(toplevel, 1);
  329 }
  330 
  331 /*
  332  * Signal handler for lost connections; cleanup various elements of
  333  * the connection state, and call cleanuppeer() to finish it off.
  334  * This function is not signal safe, so exit if called by a signal.
  335  */
  336 void
  337 lostpeer(int signo)
  338 {
  339     int oerrno = errno;
  340 
  341     alarmtimer(0);
  342     if (connected) {
  343         if (cout != NULL) {
  344             (void)shutdown(fileno(cout), 1+1);
  345             (void)fclose(cout);
  346             cout = NULL;
  347         }
  348         if (data >= 0) {
  349             (void)shutdown(data, 1+1);
  350             (void)close(data);
  351             data = -1;
  352         }
  353         connected = 0;
  354     }
  355     pswitch(1);
  356     if (connected) {
  357         if (cout != NULL) {
  358             (void)shutdown(fileno(cout), 1+1);
  359             (void)fclose(cout);
  360             cout = NULL;
  361         }
  362         connected = 0;
  363     }
  364     proxflag = 0;
  365     pswitch(0);
  366     cleanuppeer();
  367     if (signo) {
  368         errx(1, "lostpeer due to signal %d", signo);
  369     }
  370     errno = oerrno;
  371 }
  372 
  373 
  374 /*
  375  * Login to remote host, using given username & password if supplied.
  376  * Return non-zero if successful.
  377  */
  378 int
  379 ftp_login(const char *host, const char *luser, const char *lpass)
  380 {
  381     char tmp[80];
  382     char *fuser, *pass, *facct, *p;
  383     char emptypass[] = "";
  384     const char *errormsg;
  385     int n, aflag, rval, nlen;
  386 
  387     aflag = rval = 0;
  388     fuser = pass = facct = NULL;
  389     if (luser)
  390         fuser = ftp_strdup(luser);
  391     if (lpass)
  392         pass = ftp_strdup(lpass);
  393 
  394     DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n",
  395         STRorNULL(fuser), STRorNULL(pass), STRorNULL(host));
  396 
  397     /*
  398      * Set up arguments for an anonymous FTP session, if necessary.
  399      */
  400     if (anonftp) {
  401         FREEPTR(fuser);
  402         fuser = ftp_strdup("anonymous");    /* as per RFC 1635 */
  403         FREEPTR(pass);
  404         pass = ftp_strdup(getoptionvalue("anonpass"));
  405     }
  406 
  407     if (ruserpass(host, &fuser, &pass, &facct) < 0) {
  408         code = -1;
  409         goto cleanup_ftp_login;
  410     }
  411 
  412     while (fuser == NULL) {
  413         if (localname)
  414             fprintf(ttyout, "Name (%s:%s): ", host, localname);
  415         else
  416             fprintf(ttyout, "Name (%s): ", host);
  417         errormsg = NULL;
  418         nlen = get_line(stdin, tmp, sizeof(tmp), &errormsg);
  419         if (nlen < 0) {
  420             fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login");
  421             code = -1;
  422             goto cleanup_ftp_login;
  423         } else if (nlen == 0) {
  424             fuser = ftp_strdup(localname);
  425         } else {
  426             fuser = ftp_strdup(tmp);
  427         }
  428     }
  429 
  430     if (gatemode) {
  431         char *nuser;
  432         size_t len;
  433 
  434         len = strlen(fuser) + 1 + strlen(host) + 1;
  435         nuser = ftp_malloc(len);
  436         (void)strlcpy(nuser, fuser, len);
  437         (void)strlcat(nuser, "@",  len);
  438         (void)strlcat(nuser, host, len);
  439         FREEPTR(fuser);
  440         fuser = nuser;
  441     }
  442 
  443     n = command("USER %s", fuser);
  444     if (n == CONTINUE) {
  445         if (pass == NULL) {
  446             p = getpass("Password: ");
  447             if (p == NULL)
  448                 p = emptypass;
  449             pass = ftp_strdup(p);
  450             memset(p, 0, strlen(p));
  451         }
  452         n = command("PASS %s", pass);
  453         memset(pass, 0, strlen(pass));
  454     }
  455     if (n == CONTINUE) {
  456         aflag++;
  457         if (facct == NULL) {
  458             p = getpass("Account: ");
  459             if (p == NULL)
  460                 p = emptypass;
  461             facct = ftp_strdup(p);
  462             memset(p, 0, strlen(p));
  463         }
  464         if (facct[0] == '\0') {
  465             warnx("Login failed");
  466             goto cleanup_ftp_login;
  467         }
  468         n = command("ACCT %s", facct);
  469         memset(facct, 0, strlen(facct));
  470     }
  471     if ((n != COMPLETE) ||
  472         (!aflag && facct != NULL && command("ACCT %s", facct) != COMPLETE)) {
  473         warnx("Login failed");
  474         goto cleanup_ftp_login;
  475     }
  476     rval = 1;
  477     username = ftp_strdup(fuser);
  478     if (proxy)
  479         goto cleanup_ftp_login;
  480 
  481     connected = -1;
  482     getremoteinfo();
  483     for (n = 0; n < macnum; ++n) {
  484         if (!strcmp("init", macros[n].mac_name)) {
  485             (void)strlcpy(line, "$init", sizeof(line));
  486             makeargv();
  487             domacro(margc, margv);
  488             break;
  489         }
  490     }
  491     updatelocalcwd();
  492     remotecwd[0] = '\0';
  493     remcwdvalid = 0;
  494 
  495  cleanup_ftp_login:
  496     FREEPTR(fuser);
  497     if (pass != NULL)
  498         memset(pass, 0, strlen(pass));
  499     FREEPTR(pass);
  500     if (facct != NULL)
  501         memset(facct, 0, strlen(facct));
  502     FREEPTR(facct);
  503     return (rval);
  504 }
  505 
  506 /*
  507  * `another' gets another argument, and stores the new argc and argv.
  508  * It reverts to the top level (via intr()) on EOF/error.
  509  *
  510  * Returns false if no new arguments have been added.
  511  */
  512 int
  513 another(int *pargc, char ***pargv, const char *aprompt)
  514 {
  515     const char  *errormsg;
  516     int     ret, nlen;
  517     size_t      len;
  518 
  519     len = strlen(line);
  520     if (len >= sizeof(line) - 3) {
  521         fputs("Sorry, arguments too long.\n", ttyout);
  522         intr(0);
  523     }
  524     fprintf(ttyout, "(%s) ", aprompt);
  525     line[len++] = ' ';
  526     errormsg = NULL;
  527     nlen = get_line(stdin, line + len, sizeof(line)-len, &errormsg);
  528     if (nlen < 0) {
  529         fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation");
  530         intr(0);
  531     }
  532     len += nlen;
  533     makeargv();
  534     ret = margc > *pargc;
  535     *pargc = margc;
  536     *pargv = margv;
  537     return (ret);
  538 }
  539 
  540 /*
  541  * glob files given in argv[] from the remote server.
  542  * if errbuf isn't NULL, store error messages there instead
  543  * of writing to the screen.
  544  */
  545 char *
  546 remglob(char *argv[], int doswitch, const char **errbuf)
  547 {
  548     static char buf[MAXPATHLEN];
  549     static FILE *ftemp = NULL;
  550     static char **args;
  551     char temp[MAXPATHLEN];
  552     int oldverbose, oldhash, oldprogress, fd;
  553     char *cp;
  554     const char *rmode;
  555     size_t len;
  556 
  557     if (!mflag || !connected) {
  558         if (!doglob)
  559             args = NULL;
  560         else {
  561             if (ftemp) {
  562                 (void)fclose(ftemp);
  563                 ftemp = NULL;
  564             }
  565         }
  566         return (NULL);
  567     }
  568     if (!doglob) {
  569         if (args == NULL)
  570             args = argv;
  571         if ((cp = *++args) == NULL)
  572             args = NULL;
  573         return (cp);
  574     }
  575     if (ftemp == NULL) {
  576         len = strlcpy(temp, tmpdir, sizeof(temp));
  577         if (temp[len - 1] != '/')
  578             (void)strlcat(temp, "/", sizeof(temp));
  579         (void)strlcat(temp, TMPFILE, sizeof(temp));
  580         if ((fd = mkstemp(temp)) < 0) {
  581             warn("Unable to create temporary file `%s'", temp);
  582             return (NULL);
  583         }
  584         close(fd);
  585         oldverbose = verbose;
  586         verbose = (errbuf != NULL) ? -1 : 0;
  587         oldhash = hash;
  588         oldprogress = progress;
  589         hash = 0;
  590         progress = 0;
  591         if (doswitch)
  592             pswitch(!proxy);
  593         for (rmode = "w"; *++argv != NULL; rmode = "a")
  594             recvrequest("NLST", temp, *argv, rmode, 0, 0);
  595         if ((code / 100) != COMPLETE) {
  596             if (errbuf != NULL)
  597                 *errbuf = reply_string;
  598         }
  599         if (doswitch)
  600             pswitch(!proxy);
  601         verbose = oldverbose;
  602         hash = oldhash;
  603         progress = oldprogress;
  604         ftemp = fopen(temp, "r");
  605         (void)unlink(temp);
  606         if (ftemp == NULL) {
  607             if (errbuf == NULL)
  608                 warnx("Can't find list of remote files");
  609             else
  610                 *errbuf =
  611                     "Can't find list of remote files";
  612             return (NULL);
  613         }
  614     }
  615     if (fgets(buf, sizeof(buf), ftemp) == NULL) {
  616         (void)fclose(ftemp);
  617         ftemp = NULL;
  618         return (NULL);
  619     }
  620     if ((cp = strchr(buf, '\n')) != NULL)
  621         *cp = '\0';
  622     return (buf);
  623 }
  624 
  625 /*
  626  * Glob a local file name specification with the expectation of a single
  627  * return value. Can't control multiple values being expanded from the
  628  * expression, we return only the first.
  629  * Returns NULL on error, or a pointer to a buffer containing the filename
  630  * that's the caller's responsiblity to free(3) when finished with.
  631  */
  632 char *
  633 globulize(const char *pattern)
  634 {
  635     glob_t gl;
  636     int flags;
  637     char *p;
  638 
  639     if (!doglob)
  640         return (ftp_strdup(pattern));
  641 
  642     flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
  643     memset(&gl, 0, sizeof(gl));
  644     if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
  645         warnx("Glob pattern `%s' not found", pattern);
  646         globfree(&gl);
  647         return (NULL);
  648     }
  649     p = ftp_strdup(gl.gl_pathv[0]);
  650     globfree(&gl);
  651     return (p);
  652 }
  653 
  654 /*
  655  * determine size of remote file
  656  */
  657 off_t
  658 remotesize(const char *file, int noisy)
  659 {
  660     int overbose, r;
  661     off_t size;
  662 
  663     overbose = verbose;
  664     size = -1;
  665     if (ftp_debug == 0)
  666         verbose = -1;
  667     if (! features[FEAT_SIZE]) {
  668         if (noisy)
  669             fprintf(ttyout,
  670                 "SIZE is not supported by remote server.\n");
  671         goto cleanup_remotesize;
  672     }
  673     r = command("SIZE %s", file);
  674     if (r == COMPLETE) {
  675         char *cp, *ep;
  676 
  677         cp = strchr(reply_string, ' ');
  678         if (cp != NULL) {
  679             cp++;
  680             size = STRTOLL(cp, &ep, 10);
  681             if (*ep != '\0' && !isspace((unsigned char)*ep))
  682                 size = -1;
  683         }
  684     } else {
  685         if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1)
  686             features[FEAT_SIZE] = 0;
  687         if (noisy && ftp_debug == 0) {
  688             fputs(reply_string, ttyout);
  689             putc('\n', ttyout);
  690         }
  691     }
  692  cleanup_remotesize:
  693     verbose = overbose;
  694     return (size);
  695 }
  696 
  697 /*
  698  * determine last modification time (in GMT) of remote file
  699  */
  700 time_t
  701 remotemodtime(const char *file, int noisy)
  702 {
  703     int overbose, ocode, r;
  704     time_t  rtime;
  705 
  706     overbose = verbose;
  707     ocode = code;
  708     rtime = -1;
  709     if (ftp_debug == 0)
  710         verbose = -1;
  711     if (! features[FEAT_MDTM]) {
  712         if (noisy)
  713             fprintf(ttyout,
  714                 "MDTM is not supported by remote server.\n");
  715         goto cleanup_parse_time;
  716     }
  717     r = command("MDTM %s", file);
  718     if (r == COMPLETE) {
  719         struct tm timebuf;
  720         char *timestr, *frac;
  721 
  722         /*
  723          * time-val = 14DIGIT [ "." 1*DIGIT ]
  724          *      YYYYMMDDHHMMSS[.sss]
  725          * mdtm-response = "213" SP time-val CRLF / error-response
  726          */
  727         timestr = reply_string + 4;
  728 
  729                     /*
  730                      * parse fraction.
  731                      * XXX: ignored for now
  732                      */
  733         frac = strchr(timestr, '\r');
  734         if (frac != NULL)
  735             *frac = '\0';
  736         frac = strchr(timestr, '.');
  737         if (frac != NULL)
  738             *frac++ = '\0';
  739         if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) {
  740             /*
  741              * XXX: Workaround for lame ftpd's that return
  742              *  `19100' instead of `2000'
  743              */
  744             fprintf(ttyout,
  745         "Y2K warning! Incorrect time-val `%s' received from server.\n",
  746                 timestr);
  747             timestr++;
  748             timestr[0] = '2';
  749             timestr[1] = '0';
  750             fprintf(ttyout, "Converted to `%s'\n", timestr);
  751         }
  752         memset(&timebuf, 0, sizeof(timebuf));
  753         if (strlen(timestr) != 14 ||
  754             (strptime(timestr, "%Y%m%d%H%M%S", &timebuf) == NULL)) {
  755  bad_parse_time:
  756             fprintf(ttyout, "Can't parse time `%s'.\n", timestr);
  757             goto cleanup_parse_time;
  758         }
  759         timebuf.tm_isdst = -1;
  760         rtime = timegm(&timebuf);
  761         if (rtime == -1) {
  762             if (noisy || ftp_debug != 0)
  763                 goto bad_parse_time;
  764             else
  765                 goto cleanup_parse_time;
  766         } else {
  767             DPRINTF("remotemodtime: parsed time `%s' as " LLF
  768                 ", %s",
  769                 timestr, (LLT)rtime,
  770                 rfc2822time(localtime(&rtime)));
  771         }
  772     } else {
  773         if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1)
  774             features[FEAT_MDTM] = 0;
  775         if (noisy && ftp_debug == 0) {
  776             fputs(reply_string, ttyout);
  777             putc('\n', ttyout);
  778         }
  779     }
  780  cleanup_parse_time:
  781     verbose = overbose;
  782     if (rtime == -1)
  783         code = ocode;
  784     return (rtime);
  785 }
  786 
  787 /*
  788  * Format tm in an RFC 2822 compatible manner, with a trailing \n.
  789  * Returns a pointer to a static string containing the result.
  790  */
  791 const char *
  792 rfc2822time(const struct tm *tm)
  793 {
  794     static char result[50];
  795 
  796     if (strftime(result, sizeof(result),
  797         "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0)
  798         errx(1, "Can't convert RFC 2822 time: buffer too small");
  799     return result;
  800 }
  801 
  802 /*
  803  * Parse HTTP-date as per RFC 2616.
  804  * Return a pointer to the next character of the consumed date string,
  805  * or NULL if failed.
  806  */
  807 const char *
  808 parse_rfc2616time(struct tm *parsed, const char *httpdate)
  809 {
  810     const char *t;
  811 #if defined(HAVE_SETLOCALE)
  812     const char *curlocale;
  813 
  814     /* The representation of %a depends on the current locale. */
  815     curlocale = setlocale(LC_TIME, NULL);
  816     (void)setlocale(LC_TIME, "C");
  817 #endif
  818                                 /* RFC 1123 */
  819     if ((t = strptime(httpdate, "%a, %d %b %Y %H:%M:%S GMT", parsed)) ||
  820                                 /* RFC 850 */
  821         (t = strptime(httpdate, "%a, %d-%b-%y %H:%M:%S GMT", parsed)) ||
  822                                 /* asctime */
  823         (t = strptime(httpdate, "%a, %b %d %H:%M:%S %Y", parsed))) {
  824         ;           /* do nothing */
  825     }
  826 #if defined(HAVE_SETLOCALE)
  827     (void)setlocale(LC_TIME, curlocale);
  828 #endif
  829     return t;
  830 }
  831 
  832 /*
  833  * Update global `localcwd', which contains the state of the local cwd
  834  */
  835 void
  836 updatelocalcwd(void)
  837 {
  838 
  839     if (getcwd(localcwd, sizeof(localcwd)) == NULL)
  840         localcwd[0] = '\0';
  841     DPRINTF("updatelocalcwd: got `%s'\n", localcwd);
  842 }
  843 
  844 /*
  845  * Update global `remotecwd', which contains the state of the remote cwd
  846  */
  847 void
  848 updateremotecwd(void)
  849 {
  850     int  overbose, ocode;
  851     size_t   i;
  852     char    *cp;
  853 
  854     remcwdvalid = 1;    /* whether it works or not, we are done */
  855     overbose = verbose;
  856     ocode = code;
  857     if (ftp_debug == 0)
  858         verbose = -1;
  859     if (command("PWD") != COMPLETE)
  860         goto badremotecwd;
  861     cp = strchr(reply_string, ' ');
  862     if (cp == NULL || cp[0] == '\0' || cp[1] != '"')
  863         goto badremotecwd;
  864     cp += 2;
  865     for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) {
  866         if (cp[0] == '"') {
  867             if (cp[1] == '"')
  868                 cp++;
  869             else
  870                 break;
  871         }
  872         remotecwd[i] = *cp;
  873     }
  874     remotecwd[i] = '\0';
  875     DPRINTF("updateremotecwd: got `%s'\n", remotecwd);
  876     goto cleanupremotecwd;
  877  badremotecwd:
  878     remotecwd[0]='\0';
  879  cleanupremotecwd:
  880     verbose = overbose;
  881     code = ocode;
  882 }
  883 
  884 /*
  885  * Ensure file is in or under dir.
  886  * Returns 1 if so, 0 if not (or an error occurred).
  887  */
  888 int
  889 fileindir(const char *file, const char *dir)
  890 {
  891     char    parentdirbuf[PATH_MAX+1], *parentdir;
  892     char    realdir[PATH_MAX+1];
  893     size_t  dirlen;
  894 
  895                     /* determine parent directory of file */
  896     (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
  897     parentdir = dirname(parentdirbuf);
  898     if (strcmp(parentdir, ".") == 0)
  899         return 1;       /* current directory is ok */
  900 
  901                     /* find the directory */
  902     if (realpath(parentdir, realdir) == NULL) {
  903         warn("Unable to determine real path of `%s'", parentdir);
  904         return 0;
  905     }
  906     if (realdir[0] != '/')      /* relative result is ok */
  907         return 1;
  908     dirlen = strlen(dir);
  909     if (strncmp(realdir, dir, dirlen) == 0 &&
  910         (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
  911         return 1;
  912     return 0;
  913 }
  914 
  915 /*
  916  * List words in stringlist, vertically arranged
  917  */
  918 void
  919 list_vertical(StringList *sl)
  920 {
  921     size_t i, j;
  922     size_t columns, lines;
  923     char *p;
  924     size_t w, width;
  925 
  926     width = 0;
  927 
  928     for (i = 0 ; i < sl->sl_cur ; i++) {
  929         w = strlen(sl->sl_str[i]);
  930         if (w > width)
  931             width = w;
  932     }
  933     width = (width + 8) &~ 7;
  934 
  935     columns = ttywidth / width;
  936     if (columns == 0)
  937         columns = 1;
  938     lines = (sl->sl_cur + columns - 1) / columns;
  939     for (i = 0; i < lines; i++) {
  940         for (j = 0; j < columns; j++) {
  941             p = sl->sl_str[j * lines + i];
  942             if (p)
  943                 fputs(p, ttyout);
  944             if (j * lines + i + lines >= sl->sl_cur) {
  945                 putc('\n', ttyout);
  946                 break;
  947             }
  948             if (p) {
  949                 w = strlen(p);
  950                 while (w < width) {
  951                     w = (w + 8) &~ 7;
  952                     (void)putc('\t', ttyout);
  953                 }
  954             }
  955         }
  956     }
  957 }
  958 
  959 /*
  960  * Update the global ttywidth value, using TIOCGWINSZ.
  961  */
  962 void
  963 setttywidth(int a)
  964 {
  965     struct winsize winsize;
  966     int oerrno = errno;
  967 
  968     if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 &&
  969         winsize.ws_col != 0)
  970         ttywidth = winsize.ws_col;
  971     else
  972         ttywidth = 80;
  973     errno = oerrno;
  974 }
  975 
  976 /*
  977  * Change the rate limit up (SIGUSR1) or down (SIGUSR2)
  978  */
  979 void
  980 crankrate(int sig)
  981 {
  982 
  983     switch (sig) {
  984     case SIGUSR1:
  985         if (rate_get)
  986             rate_get += rate_get_incr;
  987         if (rate_put)
  988             rate_put += rate_put_incr;
  989         break;
  990     case SIGUSR2:
  991         if (rate_get && rate_get > rate_get_incr)
  992             rate_get -= rate_get_incr;
  993         if (rate_put && rate_put > rate_put_incr)
  994             rate_put -= rate_put_incr;
  995         break;
  996     default:
  997         err(1, "crankrate invoked with unknown signal: %d", sig);
  998     }
  999 }
 1000 
 1001 
 1002 /*
 1003  * Setup or cleanup EditLine structures
 1004  */
 1005 #ifndef NO_EDITCOMPLETE
 1006 void
 1007 controlediting(void)
 1008 {
 1009     if (editing && el == NULL && hist == NULL) {
 1010         HistEvent ev;
 1011         int editmode;
 1012 
 1013         el = el_init(getprogname(), stdin, ttyout, stderr);
 1014         /* init editline */
 1015         hist = history_init();      /* init the builtin history */
 1016         history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */
 1017         el_set(el, EL_HIST, history, hist); /* use history */
 1018 
 1019         el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
 1020         el_set(el, EL_PROMPT, prompt);  /* set the prompt functions */
 1021         el_set(el, EL_RPROMPT, rprompt);
 1022 
 1023         /* add local file completion, bind to TAB */
 1024         el_set(el, EL_ADDFN, "ftp-complete",
 1025             "Context sensitive argument completion",
 1026             complete);
 1027         el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
 1028         el_source(el, NULL);    /* read ~/.editrc */
 1029         if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0)
 1030             editing = 0;    /* the user doesn't want editing,
 1031                      * so disable, and let statement
 1032                      * below cleanup */
 1033         else
 1034             el_set(el, EL_SIGNAL, 1);
 1035     }
 1036     if (!editing) {
 1037         if (hist) {
 1038             history_end(hist);
 1039             hist = NULL;
 1040         }
 1041         if (el) {
 1042             el_end(el);
 1043             el = NULL;
 1044         }
 1045     }
 1046 }
 1047 #endif /* !NO_EDITCOMPLETE */
 1048 
 1049 /*
 1050  * Convert the string `arg' to an int, which may have an optional SI suffix
 1051  * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
 1052  */
 1053 int
 1054 strsuftoi(const char *arg)
 1055 {
 1056     char *cp;
 1057     long val;
 1058 
 1059     if (!isdigit((unsigned char)arg[0]))
 1060         return (-1);
 1061 
 1062     val = strtol(arg, &cp, 10);
 1063     if (cp != NULL) {
 1064         if (cp[0] != '\0' && cp[1] != '\0')
 1065              return (-1);
 1066         switch (tolower((unsigned char)cp[0])) {
 1067         case '\0':
 1068         case 'b':
 1069             break;
 1070         case 'k':
 1071             val <<= 10;
 1072             break;
 1073         case 'm':
 1074             val <<= 20;
 1075             break;
 1076         case 'g':
 1077             val <<= 30;
 1078             break;
 1079         default:
 1080             return (-1);
 1081         }
 1082     }
 1083     if (val < 0 || val > INT_MAX)
 1084         return (-1);
 1085 
 1086     return (val);
 1087 }
 1088 
 1089 /*
 1090  * Set up socket buffer sizes before a connection is made.
 1091  */
 1092 void
 1093 setupsockbufsize(int sock)
 1094 {
 1095     socklen_t slen;
 1096 
 1097     if (0 == rcvbuf_size) {
 1098         slen = sizeof(rcvbuf_size);
 1099         if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
 1100             (void *)&rcvbuf_size, &slen) == -1)
 1101             err(1, "Unable to determine rcvbuf size");
 1102         if (rcvbuf_size <= 0)
 1103             rcvbuf_size = 8 * 1024;
 1104         if (rcvbuf_size > 8 * 1024 * 1024)
 1105             rcvbuf_size = 8 * 1024 * 1024;
 1106         DPRINTF("setupsockbufsize: rcvbuf_size determined as %d\n",
 1107             rcvbuf_size);
 1108     }
 1109     if (0 == sndbuf_size) {
 1110         slen = sizeof(sndbuf_size);
 1111         if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF,
 1112             (void *)&sndbuf_size, &slen) == -1)
 1113             err(1, "Unable to determine sndbuf size");
 1114         if (sndbuf_size <= 0)
 1115             sndbuf_size = 8 * 1024;
 1116         if (sndbuf_size > 8 * 1024 * 1024)
 1117             sndbuf_size = 8 * 1024 * 1024;
 1118         DPRINTF("setupsockbufsize: sndbuf_size determined as %d\n",
 1119             sndbuf_size);
 1120     }
 1121 
 1122     if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
 1123         (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1)
 1124         warn("Unable to set sndbuf size %d", sndbuf_size);
 1125 
 1126     if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
 1127         (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1)
 1128         warn("Unable to set rcvbuf size %d", rcvbuf_size);
 1129 }
 1130 
 1131 /*
 1132  * Copy characters from src into dst, \ quoting characters that require it
 1133  */
 1134 void
 1135 ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
 1136 {
 1137     size_t  di, si;
 1138 
 1139     di = si = 0;
 1140     while (src[si] != '\0' && di < dstlen && si < srclen) {
 1141         switch (src[si]) {
 1142         case '\\':
 1143         case ' ':
 1144         case '\t':
 1145         case '\r':
 1146         case '\n':
 1147         case '"':
 1148             /*
 1149              * Need room for two characters and NUL, avoiding
 1150              * incomplete escape sequences at end of dst.
 1151              */
 1152             if (di >= dstlen - 3)
 1153                 break;
 1154             dst[di++] = '\\';
 1155             /* FALLTHROUGH */
 1156         default:
 1157             dst[di] = src[si++];
 1158             if (di < dstlen)
 1159                 di++;
 1160         }
 1161     }
 1162     dst[di] = '\0';
 1163 }
 1164 
 1165 /*
 1166  * Copy src into buf (which is len bytes long), expanding % sequences.
 1167  */
 1168 void
 1169 formatbuf(char *buf, size_t len, const char *src)
 1170 {
 1171     const char  *p, *p2, *q;
 1172     size_t       i;
 1173     int      op, updirs, pdirs;
 1174 
 1175 #define ADDBUF(x) do { \
 1176         if (i >= len - 1) \
 1177             goto endbuf; \
 1178         buf[i++] = (x); \
 1179     } while (0)
 1180 
 1181     p = src;
 1182     for (i = 0; *p; p++) {
 1183         if (*p != '%') {
 1184             ADDBUF(*p);
 1185             continue;
 1186         }
 1187         p++;
 1188 
 1189         switch (op = *p) {
 1190 
 1191         case '/':
 1192         case '.':
 1193         case 'c':
 1194             if (connected && !remcwdvalid)
 1195                 updateremotecwd();
 1196             p2 = connected ? remotecwd : "";
 1197             updirs = pdirs = 0;
 1198 
 1199             /* option to determine fixed # of dirs from path */
 1200             if (op == '.' || op == 'c') {
 1201                 int skip;
 1202 
 1203                 q = p2;
 1204                 while (*p2)     /* calc # of /'s */
 1205                     if (*p2++ == '/')
 1206                         updirs++;
 1207                 if (p[1] == '0') {  /* print <x> or ... */
 1208                     pdirs = 1;
 1209                     p++;
 1210                 }
 1211                 if (p[1] >= '1' && p[1] <= '9') {
 1212                             /* calc # to skip  */
 1213                     skip = p[1] - '0';
 1214                     p++;
 1215                 } else
 1216                     skip = 1;
 1217 
 1218                 updirs -= skip;
 1219                 while (skip-- > 0) {
 1220                     while ((p2 > q) && (*p2 != '/'))
 1221                         p2--;   /* back up */
 1222                     if (skip && p2 > q)
 1223                         p2--;
 1224                 }
 1225                 if (*p2 == '/' && p2 != q)
 1226                     p2++;
 1227             }
 1228 
 1229             if (updirs > 0 && pdirs) {
 1230                 if (i >= len - 5)
 1231                     break;
 1232                 if (op == '.') {
 1233                     ADDBUF('.');
 1234                     ADDBUF('.');
 1235                     ADDBUF('.');
 1236                 } else {
 1237                     ADDBUF('/');
 1238                     ADDBUF('<');
 1239                     if (updirs > 9) {
 1240                         ADDBUF('9');
 1241                         ADDBUF('+');
 1242                     } else
 1243                         ADDBUF('0' + updirs);
 1244                     ADDBUF('>');
 1245                 }
 1246             }
 1247             for (; *p2; p2++)
 1248                 ADDBUF(*p2);
 1249             break;
 1250 
 1251         case 'M':
 1252         case 'm':
 1253             for (p2 = connected && hostname ? hostname : "-";
 1254                 *p2 ; p2++) {
 1255                 if (op == 'm' && *p2 == '.')
 1256                     break;
 1257                 ADDBUF(*p2);
 1258             }
 1259             break;
 1260 
 1261         case 'n':
 1262             for (p2 = connected ? username : "-"; *p2 ; p2++)
 1263                 ADDBUF(*p2);
 1264             break;
 1265 
 1266         case '%':
 1267             ADDBUF('%');
 1268             break;
 1269 
 1270         default:        /* display unknown codes literally */
 1271             ADDBUF('%');
 1272             ADDBUF(op);
 1273             break;
 1274 
 1275         }
 1276     }
 1277  endbuf:
 1278     buf[i] = '\0';
 1279 }
 1280 
 1281 /*
 1282  * Determine if given string is an IPv6 address or not.
 1283  * Return 1 for yes, 0 for no
 1284  */
 1285 int
 1286 isipv6addr(const char *addr)
 1287 {
 1288     int rv = 0;
 1289 #ifdef INET6
 1290     struct addrinfo hints, *res;
 1291 
 1292     memset(&hints, 0, sizeof(hints));
 1293     hints.ai_family = AF_INET6;
 1294     hints.ai_socktype = SOCK_DGRAM; /*dummy*/
 1295     hints.ai_flags = AI_NUMERICHOST;
 1296     if (getaddrinfo(addr, "0", &hints, &res) != 0)
 1297         rv = 0;
 1298     else {
 1299         rv = 1;
 1300         freeaddrinfo(res);
 1301     }
 1302     DPRINTF("isipv6addr: got %d for %s\n", rv, addr);
 1303 #endif
 1304     return (rv == 1) ? 1 : 0;
 1305 }
 1306 
 1307 /*
 1308  * Read a line from the FILE stream into buf/buflen using fgets(), so up
 1309  * to buflen-1 chars will be read and the result will be NUL terminated.
 1310  * If the line has a trailing newline it will be removed.
 1311  * If the line is too long, excess characters will be read until
 1312  * newline/EOF/error.
 1313  * If EOF/error occurs or a too-long line is encountered and errormsg
 1314  * isn't NULL, it will be changed to a description of the problem.
 1315  * (The EOF message has a leading \n for cosmetic purposes).
 1316  * Returns:
 1317  *  >=0 length of line (excluding trailing newline) if all ok
 1318  *  -1  error occurred
 1319  *  -2  EOF encountered
 1320  *  -3  line was too long
 1321  */
 1322 int
 1323 get_line(FILE *stream, char *buf, size_t buflen, const char **errormsg)
 1324 {
 1325     int rv, ch;
 1326     size_t  len;
 1327 
 1328     if (fgets(buf, buflen, stream) == NULL) {
 1329         if (feof(stream)) { /* EOF */
 1330             rv = -2;
 1331             if (errormsg)
 1332                 *errormsg = "\nEOF received";
 1333         } else  {       /* error */
 1334             rv = -1;
 1335             if (errormsg)
 1336                 *errormsg = "Error encountered";
 1337         }
 1338         clearerr(stream);
 1339         return rv;
 1340     }
 1341     len = strlen(buf);
 1342     if (buf[len-1] == '\n') {   /* clear any trailing newline */
 1343         buf[--len] = '\0';
 1344     } else if (len == buflen-1) {   /* line too long */
 1345         while ((ch = getchar()) != '\n' && ch != EOF)
 1346             continue;
 1347         if (errormsg)
 1348             *errormsg = "Input line is too long";
 1349         clearerr(stream);
 1350         return -3;
 1351     }
 1352     if (errormsg)
 1353         *errormsg = NULL;
 1354     return len;
 1355 }
 1356 
 1357 /*
 1358  * Internal version of connect(2); sets socket buffer sizes,
 1359  * binds to a specific local address (if set), and
 1360  * supports a connection timeout using a non-blocking connect(2) with
 1361  * a poll(2).
 1362  * Socket fcntl flags are temporarily updated to include O_NONBLOCK;
 1363  * these will not be reverted on connection failure.
 1364  * Returns 0 on success, or -1 upon failure (with an appropriate
 1365  * error message displayed.)
 1366  */
 1367 int
 1368 ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen, int pe)
 1369 {
 1370     int     flags, rv, timeout, error;
 1371     socklen_t   slen;
 1372     struct timeval  endtime, now, td;
 1373     struct pollfd   pfd[1];
 1374     char        hname[NI_MAXHOST];
 1375     char        sname[NI_MAXSERV];
 1376 
 1377     setupsockbufsize(sock);
 1378     if (getnameinfo(name, namelen,
 1379         hname, sizeof(hname), sname, sizeof(sname),
 1380         NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
 1381         strlcpy(hname, "?", sizeof(hname));
 1382         strlcpy(sname, "?", sizeof(sname));
 1383     }
 1384 
 1385     if (bindai != NULL) {           /* bind to specific addr */
 1386         struct addrinfo *ai;
 1387 
 1388         for (ai = bindai; ai != NULL; ai = ai->ai_next) {
 1389             if (ai->ai_family == name->sa_family)
 1390                 break;
 1391         }
 1392         if (ai == NULL)
 1393             ai = bindai;
 1394         if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
 1395             char    bname[NI_MAXHOST];
 1396             int saveerr;
 1397 
 1398             saveerr = errno;
 1399             if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
 1400                 bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0)
 1401                 strlcpy(bname, "?", sizeof(bname));
 1402             errno = saveerr;
 1403             warn("Can't bind to `%s'", bname);
 1404             return -1;
 1405         }
 1406     }
 1407 
 1408                         /* save current socket flags */
 1409     if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
 1410         warn("Can't %s socket flags for connect to `%s:%s'",
 1411             "save", hname, sname);
 1412         return -1;
 1413     }
 1414                         /* set non-blocking connect */
 1415     if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
 1416         warn("Can't set socket non-blocking for connect to `%s:%s'",
 1417             hname, sname);
 1418         return -1;
 1419     }
 1420 
 1421     /* NOTE: we now must restore socket flags on successful exit */
 1422 
 1423     pfd[0].fd = sock;
 1424     pfd[0].events = POLLIN|POLLOUT;
 1425 
 1426     if (quit_time > 0) {            /* want a non default timeout */
 1427         (void)gettimeofday(&endtime, NULL);
 1428         endtime.tv_sec += quit_time;    /* determine end time */
 1429     }
 1430 
 1431     rv = connect(sock, name, namelen);  /* inititate the connection */
 1432     if (rv == -1) {             /* connection error */
 1433         if (errno != EINPROGRESS) { /* error isn't "please wait" */
 1434             if (pe || (errno != EHOSTUNREACH))
 1435  connecterror:
 1436                 warn("Can't connect to `%s:%s'", hname, sname);
 1437             return -1;
 1438         }
 1439 
 1440                         /* connect EINPROGRESS; wait */
 1441         do {
 1442             if (quit_time > 0) {    /* determine timeout */
 1443                 (void)gettimeofday(&now, NULL);
 1444                 timersub(&endtime, &now, &td);
 1445                 timeout = td.tv_sec * 1000 + td.tv_usec/1000;
 1446                 if (timeout < 0)
 1447                     timeout = 0;
 1448             } else {
 1449                 timeout = INFTIM;
 1450             }
 1451             pfd[0].revents = 0;
 1452             rv = ftp_poll(pfd, 1, timeout);
 1453                         /* loop until poll ! EINTR */
 1454         } while (rv == -1 && errno == EINTR);
 1455 
 1456         if (rv == 0) {          /* poll (connect) timed out */
 1457             errno = ETIMEDOUT;
 1458             goto connecterror;
 1459         }
 1460 
 1461         if (rv == -1) {         /* poll error */
 1462             goto connecterror;
 1463         } else if (pfd[0].revents & (POLLIN|POLLOUT)) {
 1464             slen = sizeof(error);   /* OK, or pending error */
 1465             if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
 1466                 &error, &slen) == -1) {
 1467                         /* Solaris pending error */
 1468                 goto connecterror;
 1469             } else if (error != 0) {
 1470                 errno = error;  /* BSD pending error */
 1471                 goto connecterror;
 1472             }
 1473         } else {
 1474             errno = EBADF;      /* this shouldn't happen ... */
 1475             goto connecterror;
 1476         }
 1477     }
 1478 
 1479     if (fcntl(sock, F_SETFL, flags) == -1) {
 1480                         /* restore socket flags */
 1481         warn("Can't %s socket flags for connect to `%s:%s'",
 1482             "restore", hname, sname);
 1483         return -1;
 1484     }
 1485     return 0;
 1486 }
 1487 
 1488 /*
 1489  * Internal version of listen(2); sets socket buffer sizes first.
 1490  */
 1491 int
 1492 ftp_listen(int sock, int backlog)
 1493 {
 1494 
 1495     setupsockbufsize(sock);
 1496     return (listen(sock, backlog));
 1497 }
 1498 
 1499 /*
 1500  * Internal version of poll(2), to allow reimplementation by select(2)
 1501  * on platforms without the former.
 1502  */
 1503 int
 1504 ftp_poll(struct pollfd *fds, int nfds, int timeout)
 1505 {
 1506 #if defined(HAVE_POLL)
 1507     return poll(fds, nfds, timeout);
 1508 
 1509 #elif defined(HAVE_SELECT)
 1510         /* implement poll(2) using select(2) */
 1511     fd_set      rset, wset, xset;
 1512     const int   rsetflags = POLLIN | POLLRDNORM;
 1513     const int   wsetflags = POLLOUT | POLLWRNORM;
 1514     const int   xsetflags = POLLRDBAND;
 1515     struct timeval  tv, *ptv;
 1516     int     i, max, rv;
 1517 
 1518     FD_ZERO(&rset);         /* build list of read & write events */
 1519     FD_ZERO(&wset);
 1520     FD_ZERO(&xset);
 1521     max = 0;
 1522     for (i = 0; i < nfds; i++) {
 1523         if (fds[i].fd > FD_SETSIZE) {
 1524             warnx("can't select fd %d", fds[i].fd);
 1525             errno = EINVAL;
 1526             return -1;
 1527         } else if (fds[i].fd > max)
 1528             max = fds[i].fd;
 1529         if (fds[i].events & rsetflags)
 1530             FD_SET(fds[i].fd, &rset);
 1531         if (fds[i].events & wsetflags)
 1532             FD_SET(fds[i].fd, &wset);
 1533         if (fds[i].events & xsetflags)
 1534             FD_SET(fds[i].fd, &xset);
 1535     }
 1536 
 1537     ptv = &tv;          /* determine timeout */
 1538     if (timeout == -1) {        /* wait forever */
 1539         ptv = NULL;
 1540     } else if (timeout == 0) {  /* poll once */
 1541         ptv->tv_sec = 0;
 1542         ptv->tv_usec = 0;
 1543     }
 1544     else if (timeout != 0) {    /* wait timeout milliseconds */
 1545         ptv->tv_sec = timeout / 1000;
 1546         ptv->tv_usec = (timeout % 1000) * 1000;
 1547     }
 1548     rv = select(max + 1, &rset, &wset, &xset, ptv);
 1549     if (rv <= 0)            /* -1 == error, 0 == timeout */
 1550         return rv;
 1551 
 1552     for (i = 0; i < nfds; i++) {    /* determine results */
 1553         if (FD_ISSET(fds[i].fd, &rset))
 1554             fds[i].revents |= (fds[i].events & rsetflags);
 1555         if (FD_ISSET(fds[i].fd, &wset))
 1556             fds[i].revents |= (fds[i].events & wsetflags);
 1557         if (FD_ISSET(fds[i].fd, &xset))
 1558             fds[i].revents |= (fds[i].events & xsetflags);
 1559     }
 1560     return rv;
 1561 
 1562 #else
 1563 # error no way to implement xpoll
 1564 #endif
 1565 }
 1566 
 1567 #ifndef SMALL
 1568 /*
 1569  * malloc() with inbuilt error checking
 1570  */
 1571 void *
 1572 ftp_malloc(size_t size)
 1573 {
 1574     void *p;
 1575 
 1576     p = malloc(size);
 1577     if (p == NULL)
 1578         err(1, "Unable to allocate %ld bytes of memory", (long)size);
 1579     return (p);
 1580 }
 1581 
 1582 /*
 1583  * sl_init() with inbuilt error checking
 1584  */
 1585 StringList *
 1586 ftp_sl_init(void)
 1587 {
 1588     StringList *p;
 1589 
 1590     p = sl_init();
 1591     if (p == NULL)
 1592         err(1, "Unable to allocate memory for stringlist");
 1593     return (p);
 1594 }
 1595 
 1596 /*
 1597  * sl_add() with inbuilt error checking
 1598  */
 1599 void
 1600 ftp_sl_add(StringList *sl, char *i)
 1601 {
 1602 
 1603     if (sl_add(sl, i) == -1)
 1604         err(1, "Unable to add `%s' to stringlist", i);
 1605 }
 1606 
 1607 /*
 1608  * strdup() with inbuilt error checking
 1609  */
 1610 char *
 1611 ftp_strdup(const char *str)
 1612 {
 1613     char *s;
 1614 
 1615     if (str == NULL)
 1616         errx(1, "ftp_strdup: called with NULL argument");
 1617     s = strdup(str);
 1618     if (s == NULL)
 1619         err(1, "Unable to allocate memory for string copy");
 1620     return (s);
 1621 }
 1622 #endif