xref: /dragonfly/contrib/tnftp/src/util.c (revision 1612048d3f35192eeb39f3a28148604bef0d5b6e)
1 /*        $NetBSD: util.c,v 1.25 2021/08/27 01:38:49 lukem Exp $      */
2 /*        from      NetBSD: util.c,v 1.162 2021/04/25 08:26:35 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.162 2021/04/25 08:26:35 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
setpeer(int argc,char * argv[])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
parse_feat(const char * fline)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
getremoteinfo(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
cleanuppeer(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
intr(int signo)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
lostpeer(int signo)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
ftp_login(const char * host,const char * luser,const char * lpass)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
another(int * pargc,char *** pargv,const char * aprompt)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 *
remglob(char * argv[],int doswitch,const char ** errbuf)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 *
globulize(const char * pattern)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
remotesize(const char * file,int noisy)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
remotemodtime(const char * file,int noisy)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 buggy ftp servers 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 *
rfc2822time(const struct tm * tm)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 *
parse_rfc2616time(struct tm * parsed,const char * httpdate)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
updatelocalcwd(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
updateremotecwd(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
fileindir(const char * file,const char * dir)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
list_vertical(StringList * sl)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
setttywidth(int a)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
crankrate(int sig)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
controlediting(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
strsuftoi(const char * arg)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
setupsockbufsize(int sock)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
ftpvis(char * dst,size_t dstlen,const char * src,size_t srclen)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
formatbuf(char * buf,size_t len,const char * src)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
isipv6addr(const char * addr)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
get_line(FILE * stream,char * buf,size_t buflen,const char ** errormsg)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
ftp_connect(int sock,const struct sockaddr * name,socklen_t namelen,int pe)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
ftp_listen(int sock,int backlog)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
ftp_poll(struct pollfd * fds,int nfds,int timeout)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 *
ftp_malloc(size_t size)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 *
ftp_sl_init(void)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
ftp_sl_add(StringList * sl,char * i)1600 ftp_sl_add(StringList *sl, char *i)
1601 {
1602 
1603           sl_add(sl, i);
1604 }
1605 
1606 /*
1607  * strdup() with inbuilt error checking
1608  */
1609 char *
ftp_strdup(const char * str)1610 ftp_strdup(const char *str)
1611 {
1612           char *s;
1613 
1614           if (str == NULL)
1615                     errx(1, "ftp_strdup: called with NULL argument");
1616           s = strdup(str);
1617           if (s == NULL)
1618                     err(1, "Unable to allocate memory for string copy");
1619           return (s);
1620 }
1621 #endif
1622