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