1 /* Basic FTP routines.
2    Copyright (C) 1996-2011, 2014-2015, 2018-2021 Free Software
3    Foundation, Inc.
4 
5 This file is part of GNU Wget.
6 
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version.
11 
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
19 
20 Additional permission under GNU GPL version 3 section 7
21 
22 If you modify this program, or any covered work, by linking or
23 combining it with the OpenSSL project's OpenSSL library (or a
24 modified version of that library), containing parts covered by the
25 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
26 grants you additional permission to convey the resulting work.
27 Corresponding Source for a non-source form of such a combination
28 shall include the source code for the parts of OpenSSL used as well
29 as that of the covered work.  */
30 
31 #include "wget.h"
32 
33 #include <assert.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 
38 #include <string.h>
39 #include <unistd.h>
40 #include "utils.h"
41 #include "connect.h"
42 #include "host.h"
43 #include "ftp.h"
44 #include "retr.h"
45 #include "c-strcase.h"
46 
47 
48 /* Get the response of FTP server and allocate enough room to handle
49    it.  <CR> and <LF> characters are stripped from the line, and the
50    line is 0-terminated.  All the response lines but the last one are
51    skipped.  The last line is determined as described in RFC959.
52 
53    If the line is successfully read, FTPOK is returned, and *ret_line
54    is assigned a freshly allocated line.  Otherwise, FTPRERR is
55    returned, and the value of *ret_line should be ignored.  */
56 
57 uerr_t
ftp_response(int fd,char ** ret_line)58 ftp_response (int fd, char **ret_line)
59 {
60   for (;;)
61     {
62       char *p;
63       char *line = fd_read_line (fd);
64       if (!line)
65         return FTPRERR;
66 
67       /* Strip trailing CRLF before printing the line, so that
68          quoting doesn't include bogus \012 and \015. */
69       if ((p = strpbrk(line , "\r\n")))
70         *p = 0;
71 
72       if (opt.server_response)
73         logprintf (LOG_NOTQUIET, "%s\n",
74                    quotearg_style (escape_quoting_style, line));
75       else
76         DEBUGP (("%s\n", quotearg_style (escape_quoting_style, line)));
77 
78       /* The last line of output is the one that begins with "ddd ". */
79       if (c_isdigit (line[0]) && c_isdigit (line[1]) && c_isdigit (line[2])
80           && line[3] == ' ')
81         {
82           *ret_line = line;
83           return FTPOK;
84         }
85       xfree (line);
86     }
87 }
88 
89 /* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
90    it if printing is required.  If VALUE is NULL, just use
91    command<CR><LF>.  */
92 static char *
ftp_request(const char * command,const char * value)93 ftp_request (const char *command, const char *value)
94 {
95   char *res;
96 
97   if (value)
98     {
99       char *defanged = NULL, buf[256];
100 
101       /* Check for newlines in VALUE (possibly injected by the %0A URL
102          escape) making the callers inadvertently send multiple FTP
103          commands at once.  Without this check an attacker could
104          intentionally redirect to ftp://server/fakedir%0Acommand.../
105          and execute arbitrary FTP command on a remote FTP server.  */
106       if (strpbrk (value, "\r\n"))
107         {
108           /* Copy VALUE to the stack and modify CR/LF to space. */
109           char *p;
110           size_t len = strlen(value);
111 
112           if (len < sizeof (buf))
113             defanged = buf;
114           else
115             defanged = xmalloc (len + 1);
116 
117           memcpy (defanged, value, len + 1);
118 
119           for (p = defanged; *p; p++)
120             if (*p == '\r' || *p == '\n')
121               *p = ' ';
122 
123           DEBUGP (("\nDetected newlines in %s \"%s\"; changing to %s \"%s\"\n",
124                    command, quotearg_style (escape_quoting_style, value),
125                    command, quotearg_style (escape_quoting_style, defanged)));
126 
127           /* Make VALUE point to the defanged copy of the string. */
128           value = defanged;
129         }
130       res = concat_strings (command, " ", value, "\r\n", (char *) 0);
131 
132       if (defanged != buf)
133         xfree (defanged);
134     }
135   else
136     res = concat_strings (command, "\r\n", (char *) 0);
137   if (opt.server_response)
138     {
139       /* Hack: don't print out password.  */
140       if (strncmp (res, "PASS", 4) != 0)
141         logprintf (LOG_ALWAYS, "--> %s\n", res);
142       else
143         logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n\n");
144     }
145   else
146     DEBUGP (("\n--> %s\n", res));
147   return res;
148 }
149 
150 uerr_t
ftp_greeting(int csock)151 ftp_greeting (int csock)
152 {
153   uerr_t err = FTPOK;
154   char *response = NULL;
155 
156   err = ftp_response (csock, &response);
157   if (err != FTPOK)
158     goto bail;
159   if (*response != '2')
160     err = FTPSRVERR;
161 
162 bail:
163   if (response)
164     xfree (response);
165   return err;
166 }
167 /* Sends the USER and PASS commands to the server, to control
168    connection socket csock.  */
169 uerr_t
ftp_login(int csock,const char * acc,const char * pass)170 ftp_login (int csock, const char *acc, const char *pass)
171 {
172   uerr_t err;
173   char *request, *respline;
174   int nwritten;
175 
176   /* Send USER username.  */
177   request = ftp_request ("USER", acc);
178   nwritten = fd_write (csock, request, strlen (request), -1);
179   if (nwritten < 0)
180     {
181       xfree (request);
182       return WRITEFAILED;
183     }
184   xfree (request);
185   /* Get appropriate response.  */
186   err = ftp_response (csock, &respline);
187   if (err != FTPOK)
188     return err;
189   /* An unprobable possibility of logging without a password.  */
190   if (*respline == '2')
191     {
192       xfree (respline);
193       return FTPOK;
194     }
195   /* Else, only response 3 is appropriate.  */
196   if (*respline != '3')
197     {
198       xfree (respline);
199       return FTPLOGREFUSED;
200     }
201 #ifdef ENABLE_OPIE
202   {
203     static const char *skey_head[] = {
204       "331 s/key ",
205       "331 opiekey "
206     };
207     size_t i;
208     const char *seed = NULL;
209 
210     for (i = 0; i < countof (skey_head); i++)
211       {
212         int l = strlen (skey_head[i]);
213         if (0 == c_strncasecmp (skey_head[i], respline, l))
214           {
215             seed = respline + l;
216             break;
217           }
218       }
219     if (seed)
220       {
221         int skey_sequence = 0;
222 
223         /* Extract the sequence from SEED.  */
224         for (; c_isdigit (*seed); seed++)
225           skey_sequence = 10 * skey_sequence + *seed - '0';
226         if (*seed == ' ')
227           ++seed;
228         else
229           {
230             xfree (respline);
231             return FTPLOGREFUSED;
232           }
233         /* Replace the password with the SKEY response to the
234            challenge.  */
235         pass = skey_response (skey_sequence, seed, pass);
236       }
237   }
238 #endif /* ENABLE_OPIE */
239   xfree (respline);
240   /* Send PASS password.  */
241   request = ftp_request ("PASS", pass);
242   nwritten = fd_write (csock, request, strlen (request), -1);
243   if (nwritten < 0)
244     {
245       xfree (request);
246       return WRITEFAILED;
247     }
248   xfree (request);
249   /* Get appropriate response.  */
250   err = ftp_response (csock, &respline);
251   if (err != FTPOK)
252     return err;
253   if (*respline != '2')
254     {
255       xfree (respline);
256       return FTPLOGINC;
257     }
258   xfree (respline);
259   /* All OK.  */
260   return FTPOK;
261 }
262 
263 static void
ip_address_to_port_repr(const ip_address * addr,int port,char * buf,size_t buflen)264 ip_address_to_port_repr (const ip_address *addr, int port, char *buf,
265                          size_t buflen)
266 {
267   unsigned char *ptr;
268 
269   assert (addr->family == AF_INET);
270   /* buf must contain the argument of PORT (of the form a,b,c,d,e,f). */
271   assert (buflen >= 6 * 4);
272 
273   ptr = IP_INADDR_DATA (addr);
274   snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d", ptr[0], ptr[1],
275             ptr[2], ptr[3], (port & 0xff00) >> 8, port & 0xff);
276   buf[buflen - 1] = '\0';
277 }
278 
279 /* Bind a port and send the appropriate PORT command to the FTP
280    server.  Use acceptport after RETR, to get the socket of data
281    connection.  */
282 uerr_t
ftp_port(int csock,int * local_sock)283 ftp_port (int csock, int *local_sock)
284 {
285   uerr_t err;
286   char *request, *respline;
287   ip_address addr;
288   int nwritten;
289   int port;
290   /* Must contain the argument of PORT (of the form a,b,c,d,e,f). */
291   char bytes[6 * 4 + 1];
292 
293   /* Get the address of this side of the connection. */
294   if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
295     return FTPSYSERR;
296 
297   assert (addr.family == AF_INET);
298 
299   /* Setting port to 0 lets the system choose a free port.  */
300   port = 0;
301 
302   /* Bind the port.  */
303   *local_sock = bind_local (&addr, &port);
304   if (*local_sock < 0)
305     return FTPSYSERR;
306 
307   /* Construct the argument of PORT (of the form a,b,c,d,e,f). */
308   ip_address_to_port_repr (&addr, port, bytes, sizeof (bytes));
309 
310   /* Send PORT request.  */
311   request = ftp_request ("PORT", bytes);
312   nwritten = fd_write (csock, request, strlen (request), -1);
313   if (nwritten < 0)
314     {
315       xfree (request);
316       fd_close (*local_sock);
317       return WRITEFAILED;
318     }
319   xfree (request);
320 
321   /* Get appropriate response.  */
322   err = ftp_response (csock, &respline);
323   if (err != FTPOK)
324     {
325       fd_close (*local_sock);
326       return err;
327     }
328   if (*respline != '2')
329     {
330       xfree (respline);
331       fd_close (*local_sock);
332       return FTPPORTERR;
333     }
334   xfree (respline);
335   return FTPOK;
336 }
337 
338 #ifdef ENABLE_IPV6
339 static void
ip_address_to_lprt_repr(const ip_address * addr,int port,char * buf,size_t buflen)340 ip_address_to_lprt_repr (const ip_address *addr, int port, char *buf,
341                          size_t buflen)
342 {
343   unsigned char *ptr = IP_INADDR_DATA (addr);
344 
345   /* buf must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
346   assert (buflen >= 21 * 4);
347 
348   /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
349   switch (addr->family)
350     {
351     case AF_INET:
352       snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4,
353                 ptr[0], ptr[1], ptr[2], ptr[3], 2,
354                 (port & 0xff00) >> 8, port & 0xff);
355       break;
356     case AF_INET6:
357       snprintf (buf, buflen,
358                 "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
359                 6, 16,
360                 ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
361                 ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15],
362                 2, (port & 0xff00) >> 8, port & 0xff);
363       break;
364     default:
365       abort ();
366     }
367 }
368 
369 /* Bind a port and send the appropriate PORT command to the FTP
370    server.  Use acceptport after RETR, to get the socket of data
371    connection.  */
372 uerr_t
ftp_lprt(int csock,int * local_sock)373 ftp_lprt (int csock, int *local_sock)
374 {
375   uerr_t err;
376   char *request, *respline;
377   ip_address addr;
378   int nwritten;
379   int port;
380   /* Must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
381   char bytes[21 * 4 + 1];
382 
383   /* Get the address of this side of the connection. */
384   if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
385     return FTPSYSERR;
386 
387   assert (addr.family == AF_INET || addr.family == AF_INET6);
388 
389   /* Setting port to 0 lets the system choose a free port.  */
390   port = 0;
391 
392   /* Bind the port.  */
393   *local_sock = bind_local (&addr, &port);
394   if (*local_sock < 0)
395     return FTPSYSERR;
396 
397   /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
398   ip_address_to_lprt_repr (&addr, port, bytes, sizeof (bytes));
399 
400   /* Send PORT request.  */
401   request = ftp_request ("LPRT", bytes);
402   nwritten = fd_write (csock, request, strlen (request), -1);
403   if (nwritten < 0)
404     {
405       xfree (request);
406       fd_close (*local_sock);
407       return WRITEFAILED;
408     }
409   xfree (request);
410   /* Get appropriate response.  */
411   err = ftp_response (csock, &respline);
412   if (err != FTPOK)
413     {
414       fd_close (*local_sock);
415       return err;
416     }
417   if (*respline != '2')
418     {
419       xfree (respline);
420       fd_close (*local_sock);
421       return FTPPORTERR;
422     }
423   xfree (respline);
424   return FTPOK;
425 }
426 
427 static void
ip_address_to_eprt_repr(const ip_address * addr,int port,char * buf,size_t buflen)428 ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf,
429                          size_t buflen)
430 {
431   int afnum;
432 
433   /* buf must contain the argument of EPRT (of the form |af|addr|port|).
434    * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
435    * 1 char for af (1-2) and 5 chars for port (0-65535) */
436   assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5);
437 
438   /* Construct the argument of EPRT (of the form |af|addr|port|). */
439   afnum = (addr->family == AF_INET ? 1 : 2);
440   snprintf (buf, buflen, "|%d|%s|%d|", afnum, print_address (addr), port);
441   buf[buflen - 1] = '\0';
442 }
443 
444 /* Bind a port and send the appropriate PORT command to the FTP
445    server.  Use acceptport after RETR, to get the socket of data
446    connection.  */
447 uerr_t
ftp_eprt(int csock,int * local_sock)448 ftp_eprt (int csock, int *local_sock)
449 {
450   uerr_t err;
451   char *request, *respline;
452   ip_address addr;
453   int nwritten;
454   int port;
455   /* Must contain the argument of EPRT (of the form |af|addr|port|).
456    * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
457    * 1 char for af (1-2) and 5 chars for port (0-65535) */
458   char bytes[4 + INET6_ADDRSTRLEN + 1 + 5 + 1];
459 
460   /* Get the address of this side of the connection. */
461   if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
462     return FTPSYSERR;
463 
464   /* Setting port to 0 lets the system choose a free port.  */
465   port = 0;
466 
467   /* Bind the port.  */
468   *local_sock = bind_local (&addr, &port);
469   if (*local_sock < 0)
470     return FTPSYSERR;
471 
472   /* Construct the argument of EPRT (of the form |af|addr|port|). */
473   ip_address_to_eprt_repr (&addr, port, bytes, sizeof (bytes));
474 
475   /* Send PORT request.  */
476   request = ftp_request ("EPRT", bytes);
477   nwritten = fd_write (csock, request, strlen (request), -1);
478   if (nwritten < 0)
479     {
480       xfree (request);
481       fd_close (*local_sock);
482       return WRITEFAILED;
483     }
484   xfree (request);
485   /* Get appropriate response.  */
486   err = ftp_response (csock, &respline);
487   if (err != FTPOK)
488     {
489       fd_close (*local_sock);
490       return err;
491     }
492   if (*respline != '2')
493     {
494       xfree (respline);
495       fd_close (*local_sock);
496       return FTPPORTERR;
497     }
498   xfree (respline);
499   return FTPOK;
500 }
501 #endif
502 
503 #ifdef HAVE_SSL
504 /*
505  * The following three functions defined into this #ifdef block
506  * wrap the extended FTP commands defined in RFC 2228 (FTP Security Extensions).
507  * Currently, only FTPS is supported, so these functions are only compiled when SSL
508  * support is available, because there's no point in using FTPS when there's no SSL.
509  * Shall someone add new secure FTP protocols in the future, feel free to remove this
510  * #ifdef, or add new constants to it.
511  */
512 
513 /*
514  * Sends an AUTH command as defined by RFC 2228,
515  * deriving its argument from the scheme. For example, if the provided scheme
516  * is SCHEME_FTPS, the command sent will be "AUTH TLS". Currently, this is the only
517  * scheme supported, so this function will return FTPNOAUTH when supplied a different
518  * one. It will also return FTPNOAUTH if the target server does not support FTPS.
519  */
520 uerr_t
ftp_auth(int csock,enum url_scheme scheme)521 ftp_auth (int csock, enum url_scheme scheme)
522 {
523   uerr_t err = 0;
524   int written = 0;
525   char *request = NULL, *response = NULL;
526 
527   if (scheme == SCHEME_FTPS)
528     {
529       request = ftp_request ("AUTH", "TLS");
530       written = fd_write (csock, request, strlen (request), -1);
531       if (written < 0)
532         {
533           err = WRITEFAILED;
534           goto bail;
535         }
536       err = ftp_response (csock, &response);
537       if (err != FTPOK)
538         goto bail;
539       if (*response != '2')
540         err = FTPNOAUTH;
541     }
542   else
543     err = FTPNOAUTH;
544 
545 bail:
546   xfree (request);
547   xfree (response);
548 
549   return err;
550 }
551 
552 uerr_t
ftp_pbsz(int csock,int pbsz)553 ftp_pbsz (int csock, int pbsz)
554 {
555   uerr_t err = 0;
556   int written = 0;
557   char spbsz[5];
558   char *request = NULL, *response = NULL;
559 
560   snprintf (spbsz, 5, "%d", pbsz);
561   request = ftp_request ("PBSZ", spbsz);
562   written = fd_write (csock, request, strlen (request), -1);
563   if (written < 0)
564     {
565       err = WRITEFAILED;
566       goto bail;
567     }
568 
569   err = ftp_response (csock, &response);
570   if (err != FTPOK)
571     goto bail;
572   if (*response != '2')
573     err = FTPNOPBSZ;
574 
575 bail:
576   xfree (request);
577   xfree (response);
578 
579   return err;
580 }
581 
582 uerr_t
ftp_prot(int csock,enum prot_level prot)583 ftp_prot (int csock, enum prot_level prot)
584 {
585   uerr_t err = 0;
586   int written = 0;
587   char *request = NULL, *response = NULL;
588   /* value must be a single character value */
589   char value[2];
590 
591   value[0] = prot;
592   value[1] = '\0';
593 
594   request = ftp_request ("PROT", value);
595   written = fd_write (csock, request, strlen (request), -1);
596   if (written < 0)
597     {
598       err = WRITEFAILED;
599       goto bail;
600     }
601 
602   err = ftp_response (csock, &response);
603   if (err != FTPOK)
604     goto bail;
605   if (*response != '2')
606     err = FTPNOPROT;
607 
608 bail:
609   xfree (request);
610   xfree (response);
611 
612   return err;
613 }
614 #endif /* HAVE_SSL */
615 
616 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
617    transfer.  Reads the response from server and parses it.  Reads the
618    host and port addresses and returns them.  */
619 uerr_t
ftp_pasv(int csock,ip_address * addr,int * port)620 ftp_pasv (int csock, ip_address *addr, int *port)
621 {
622   char *request, *respline, *s;
623   int nwritten, i;
624   uerr_t err;
625   unsigned char tmp[6];
626 
627   assert (addr != NULL);
628   assert (port != NULL);
629 
630   xzero (*addr);
631 
632   /* Form the request.  */
633   request = ftp_request ("PASV", NULL);
634   /* And send it.  */
635   nwritten = fd_write (csock, request, strlen (request), -1);
636   if (nwritten < 0)
637     {
638       xfree (request);
639       return WRITEFAILED;
640     }
641   xfree (request);
642   /* Get the server response.  */
643   err = ftp_response (csock, &respline);
644   if (err != FTPOK)
645     return err;
646   if (*respline != '2')
647     {
648       xfree (respline);
649       return FTPNOPASV;
650     }
651   /* Parse the request.  */
652   s = respline;
653   for (s += 4; *s && !c_isdigit (*s); s++)
654     ;
655   if (!*s)
656     {
657       xfree (respline);
658       return FTPINVPASV;
659     }
660   for (i = 0; i < 6; i++)
661     {
662       tmp[i] = 0;
663       for (; c_isdigit (*s); s++)
664         tmp[i] = (*s - '0') + 10 * tmp[i];
665       if (*s == ',')
666         s++;
667       else if (i < 5)
668         {
669           /* When on the last number, anything can be a terminator.  */
670           xfree (respline);
671           return FTPINVPASV;
672         }
673     }
674   xfree (respline);
675 
676   addr->family = AF_INET;
677   memcpy (IP_INADDR_DATA (addr), tmp, 4);
678   *port = ((tmp[4] << 8) & 0xff00) + tmp[5];
679 
680   return FTPOK;
681 }
682 
683 #ifdef ENABLE_IPV6
684 /* Similar to ftp_lprt, but uses `LPSV' to initiate the passive FTP
685    transfer.  Reads the response from server and parses it.  Reads the
686    host and port addresses and returns them.  */
687 uerr_t
ftp_lpsv(int csock,ip_address * addr,int * port)688 ftp_lpsv (int csock, ip_address *addr, int *port)
689 {
690   char *request, *respline, *s;
691   int nwritten, i, af, addrlen, portlen;
692   uerr_t err;
693   unsigned char tmp[16];
694   unsigned char tmpprt[2];
695 
696   assert (addr != NULL);
697   assert (port != NULL);
698 
699   xzero (*addr);
700 
701   /* Form the request.  */
702   request = ftp_request ("LPSV", NULL);
703 
704   /* And send it.  */
705   nwritten = fd_write (csock, request, strlen (request), -1);
706   if (nwritten < 0)
707     {
708       xfree (request);
709       return WRITEFAILED;
710     }
711   xfree (request);
712 
713   /* Get the server response.  */
714   err = ftp_response (csock, &respline);
715   if (err != FTPOK)
716     return err;
717   if (*respline != '2')
718     {
719       xfree (respline);
720       return FTPNOPASV;
721     }
722 
723   /* Parse the response.  */
724   s = respline;
725   for (s += 4; *s && !c_isdigit (*s); s++)
726     ;
727   if (!*s)
728     {
729       xfree (respline);
730       return FTPINVPASV;
731     }
732 
733   /* First, get the address family */
734   af = 0;
735   for (; c_isdigit (*s); s++)
736     af = (*s - '0') + 10 * af;
737 
738   if (af != 4 && af != 6)
739     {
740       xfree (respline);
741       return FTPINVPASV;
742     }
743 
744   if (!*s || *s++ != ',')
745     {
746       xfree (respline);
747       return FTPINVPASV;
748     }
749 
750   /* Then, get the address length */
751   addrlen = 0;
752   for (; c_isdigit (*s); s++)
753     addrlen = (*s - '0') + 10 * addrlen;
754 
755   if (!*s || *s++ != ',')
756     {
757       xfree (respline);
758       return FTPINVPASV;
759     }
760 
761   if (addrlen > 16)
762     {
763       xfree (respline);
764       return FTPINVPASV;
765     }
766 
767   if ((af == 4 && addrlen != 4)
768       || (af == 6 && addrlen != 16))
769     {
770       xfree (respline);
771       return FTPINVPASV;
772     }
773 
774   /* Now, we get the actual address */
775   for (i = 0; i < addrlen; i++)
776     {
777       tmp[i] = 0;
778       for (; c_isdigit (*s); s++)
779         tmp[i] = (*s - '0') + 10 * tmp[i];
780       if (*s == ',')
781         s++;
782       else
783         {
784           xfree (respline);
785           return FTPINVPASV;
786         }
787     }
788 
789   /* Now, get the port length */
790   portlen = 0;
791   for (; c_isdigit (*s); s++)
792     portlen = (*s - '0') + 10 * portlen;
793 
794   if (!*s || *s++ != ',')
795     {
796       xfree (respline);
797       return FTPINVPASV;
798     }
799 
800   if (portlen > 2)
801     {
802       xfree (respline);
803       return FTPINVPASV;
804     }
805 
806   /* Finally, we get the port number */
807   tmpprt[0] = 0;
808   for (; c_isdigit (*s); s++)
809     tmpprt[0] = (*s - '0') + 10 * tmpprt[0];
810 
811   if (!*s || *s++ != ',')
812     {
813       xfree (respline);
814       return FTPINVPASV;
815     }
816 
817   tmpprt[1] = 0;
818   for (; c_isdigit (*s); s++)
819     tmpprt[1] = (*s - '0') + 10 * tmpprt[1];
820 
821   assert (s != NULL);
822 
823   if (af == 4)
824     {
825       addr->family = AF_INET;
826       memcpy (IP_INADDR_DATA (addr), tmp, 4);
827       *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
828       DEBUGP (("lpsv addr is: %s\n", print_address(addr)));
829       DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
830       DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
831       DEBUGP (("*port is: %d\n", *port));
832     }
833   else
834     {
835       assert (af == 6);
836       addr->family = AF_INET6;
837       memcpy (IP_INADDR_DATA (addr), tmp, 16);
838       *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
839       DEBUGP (("lpsv addr is: %s\n", print_address(addr)));
840       DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
841       DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
842       DEBUGP (("*port is: %d\n", *port));
843     }
844 
845   xfree (respline);
846   return FTPOK;
847 }
848 
849 /* Similar to ftp_eprt, but uses `EPSV' to initiate the passive FTP
850    transfer.  Reads the response from server and parses it.  Reads the
851    host and port addresses and returns them.  */
852 uerr_t
ftp_epsv(int csock,ip_address * ip,int * port)853 ftp_epsv (int csock, ip_address *ip, int *port)
854 {
855   char *request, *respline, *start, delim, *s;
856   int nwritten, i;
857   uerr_t err;
858   int tport;
859 
860   assert (ip != NULL);
861   assert (port != NULL);
862 
863   /* IP already contains the IP address of the control connection's
864      peer, so we don't need to call socket_ip_address here.  */
865 
866   /* Form the request.  */
867   /* EPSV 1 means that we ask for IPv4 and EPSV 2 means that we ask for IPv6. */
868   request = ftp_request ("EPSV", (ip->family == AF_INET ? "1" : "2"));
869 
870   /* And send it.  */
871   nwritten = fd_write (csock, request, strlen (request), -1);
872   if (nwritten < 0)
873     {
874       xfree (request);
875       return WRITEFAILED;
876     }
877   xfree (request);
878 
879   /* Get the server response.  */
880   err = ftp_response (csock, &respline);
881   if (err != FTPOK)
882     return err;
883   if (*respline != '2')
884     {
885       xfree (respline);
886       return FTPNOPASV;
887     }
888 
889   assert (respline != NULL);
890 
891   DEBUGP(("respline is %s\n", respline));
892 
893   /* Skip the useless stuff and get what's inside the parentheses */
894   start = strchr (respline, '(');
895   if (start == NULL)
896     {
897       xfree (respline);
898       return FTPINVPASV;
899     }
900 
901   /* Skip the first two void fields */
902   s = start + 1;
903   delim = *s++;
904   if (delim < 33 || delim > 126)
905     {
906       xfree (respline);
907       return FTPINVPASV;
908     }
909 
910   for (i = 0; i < 2; i++)
911     {
912       if (*s++ != delim)
913         {
914           xfree (respline);
915         return FTPINVPASV;
916         }
917     }
918 
919   /* Finally, get the port number */
920   for (tport = 0, i = 0; i < 5 && c_isdigit (*s); i++, s++)
921       tport = (*s - '0') + 10 * tport;
922 
923   /* Make sure that the response terminates correctly */
924   if (*s++ != delim)
925     {
926       xfree (respline);
927       return FTPINVPASV;
928     }
929 
930   if (*s != ')')
931     {
932       xfree (respline);
933       return FTPINVPASV;
934     }
935 
936   *port = tport;
937 
938   xfree (respline);
939   return FTPOK;
940 }
941 #endif
942 
943 /* Sends the TYPE request to the server.  */
944 uerr_t
ftp_type(int csock,int type)945 ftp_type (int csock, int type)
946 {
947   char *request, *respline;
948   int nwritten;
949   uerr_t err;
950   char stype[2];
951 
952   /* Construct argument.  */
953   stype[0] = type;
954   stype[1] = 0;
955   /* Send TYPE request.  */
956   request = ftp_request ("TYPE", stype);
957   nwritten = fd_write (csock, request, strlen (request), -1);
958   if (nwritten < 0)
959     {
960       xfree (request);
961       return WRITEFAILED;
962     }
963   xfree (request);
964   /* Get appropriate response.  */
965   err = ftp_response (csock, &respline);
966   if (err != FTPOK)
967     return err;
968   if (*respline != '2')
969     {
970       xfree (respline);
971       return FTPUNKNOWNTYPE;
972     }
973   xfree (respline);
974   /* All OK.  */
975   return FTPOK;
976 }
977 
978 /* Changes the working directory by issuing a CWD command to the
979    server.  */
980 uerr_t
ftp_cwd(int csock,const char * dir)981 ftp_cwd (int csock, const char *dir)
982 {
983   char *request, *respline;
984   int nwritten;
985   uerr_t err;
986 
987   /* Send CWD request.  */
988   request = ftp_request ("CWD", dir);
989   nwritten = fd_write (csock, request, strlen (request), -1);
990   if (nwritten < 0)
991     {
992       xfree (request);
993       return WRITEFAILED;
994     }
995   xfree (request);
996   /* Get appropriate response.  */
997   err = ftp_response (csock, &respline);
998   if (err != FTPOK)
999     return err;
1000   if (*respline == '5')
1001     {
1002       xfree (respline);
1003       return FTPNSFOD;
1004     }
1005   if (*respline != '2')
1006     {
1007       xfree (respline);
1008       return FTPRERR;
1009     }
1010   xfree (respline);
1011   /* All OK.  */
1012   return FTPOK;
1013 }
1014 
1015 /* Sends REST command to the FTP server.  */
1016 uerr_t
ftp_rest(int csock,wgint offset)1017 ftp_rest (int csock, wgint offset)
1018 {
1019   char *request, *respline;
1020   int nwritten;
1021   uerr_t err;
1022 
1023   request = ftp_request ("REST", number_to_static_string (offset));
1024   nwritten = fd_write (csock, request, strlen (request), -1);
1025   if (nwritten < 0)
1026     {
1027       xfree (request);
1028       return WRITEFAILED;
1029     }
1030   xfree (request);
1031   /* Get appropriate response.  */
1032   err = ftp_response (csock, &respline);
1033   if (err != FTPOK)
1034     return err;
1035   if (*respline != '3')
1036     {
1037       xfree (respline);
1038       return FTPRESTFAIL;
1039     }
1040   xfree (respline);
1041   /* All OK.  */
1042   return FTPOK;
1043 }
1044 
1045 /* Sends RETR command to the FTP server.  */
1046 uerr_t
ftp_retr(int csock,const char * file)1047 ftp_retr (int csock, const char *file)
1048 {
1049   char *request, *respline;
1050   int nwritten;
1051   uerr_t err;
1052 
1053   /* Send RETR request.  */
1054   request = ftp_request ("RETR", file);
1055   nwritten = fd_write (csock, request, strlen (request), -1);
1056   if (nwritten < 0)
1057     {
1058       xfree (request);
1059       return WRITEFAILED;
1060     }
1061   xfree (request);
1062   /* Get appropriate response.  */
1063   err = ftp_response (csock, &respline);
1064   if (err != FTPOK)
1065     return err;
1066   if (*respline == '5')
1067     {
1068       xfree (respline);
1069       return FTPNSFOD;
1070     }
1071   if (*respline != '1')
1072     {
1073       xfree (respline);
1074       return FTPRERR;
1075     }
1076   xfree (respline);
1077   /* All OK.  */
1078   return FTPOK;
1079 }
1080 
1081 /* Sends the LIST command to the server.  If FILE is NULL, send just
1082    `LIST' (no space).  */
1083 uerr_t
ftp_list(int csock,const char * file,bool avoid_list_a,bool avoid_list,bool * list_a_used)1084 ftp_list (int csock, const char *file, bool avoid_list_a, bool avoid_list,
1085           bool *list_a_used)
1086 {
1087   char *request, *respline;
1088   int nwritten;
1089   uerr_t err;
1090   bool ok = false;
1091   size_t i = 0;
1092 
1093   /* 2013-10-12 Andrea Urbani (matfanjol)
1094      For more information about LIST and "LIST -a" please look at ftp.c,
1095      function getftp, text "__LIST_A_EXPLANATION__".
1096 
1097      If somebody changes the following commands, please, checks also the
1098      later "i" variable.  */
1099   static const char *list_commands[] = {
1100     "LIST -a",
1101     "LIST"
1102   };
1103 
1104   *list_a_used = false;
1105 
1106   if (avoid_list_a)
1107     {
1108       i = countof (list_commands)- 1;
1109       DEBUGP (("(skipping \"LIST -a\")"));
1110     }
1111 
1112 
1113   do {
1114     /* Send request.  */
1115     request = ftp_request (list_commands[i], file);
1116     nwritten = fd_write (csock, request, strlen (request), -1);
1117     if (nwritten < 0)
1118       {
1119         xfree (request);
1120         return WRITEFAILED;
1121       }
1122     xfree (request);
1123     /* Get appropriate response.  */
1124     err = ftp_response (csock, &respline);
1125     if (err == FTPOK)
1126       {
1127         if (*respline == '5')
1128           {
1129             err = FTPNSFOD;
1130           }
1131         else if (*respline == '1')
1132           {
1133             err = FTPOK;
1134             ok = true;
1135             /* Which list command was used? */
1136             *list_a_used = (i == 0);
1137           }
1138         else
1139           {
1140             err = FTPRERR;
1141           }
1142         xfree (respline);
1143       }
1144     ++i;
1145     if ((avoid_list) && (i == 1))
1146       {
1147         /* I skip LIST */
1148         ++i;
1149         DEBUGP (("(skipping \"LIST\")"));
1150       }
1151   } while (i < countof (list_commands) && !ok);
1152 
1153   return err;
1154 }
1155 
1156 /* Sends the SYST command to the server. */
1157 uerr_t
ftp_syst(int csock,enum stype * server_type,enum ustype * unix_type)1158 ftp_syst (int csock, enum stype *server_type, enum ustype *unix_type)
1159 {
1160   char *request, *respline;
1161   int nwritten;
1162   uerr_t err;
1163   char *ftp_last_respline;
1164 
1165   /* Send SYST request.  */
1166   request = ftp_request ("SYST", NULL);
1167   nwritten = fd_write (csock, request, strlen (request), -1);
1168   if (nwritten < 0)
1169     {
1170       xfree (request);
1171       return WRITEFAILED;
1172     }
1173   xfree (request);
1174 
1175   /* Get appropriate response.  */
1176   err = ftp_response (csock, &respline);
1177   if (err != FTPOK)
1178     return err;
1179   if (*respline == '5')
1180     {
1181       xfree (respline);
1182       return FTPSRVERR;
1183     }
1184 
1185   ftp_last_respline = strdup (respline);
1186 
1187   /* Skip the number (215, but 200 (!!!) in case of VMS) */
1188   strtok (respline, " ");
1189 
1190   /* Which system type has been reported (we are interested just in the
1191      first word of the server response)?  */
1192   request = strtok (NULL, " ");
1193 
1194   *unix_type = UST_OTHER;
1195 
1196   if (request == NULL)
1197     *server_type = ST_OTHER;
1198   else if (!c_strcasecmp (request, "VMS"))
1199     *server_type = ST_VMS;
1200   else if (!c_strcasecmp (request, "UNIX"))
1201     {
1202       *server_type = ST_UNIX;
1203       /* 2013-10-17 Andrea Urbani (matfanjol)
1204          I check more in depth the system type */
1205       if (!c_strncasecmp (ftp_last_respline, "215 UNIX Type: L8", 17))
1206         *unix_type = UST_TYPE_L8;
1207       else if (!c_strncasecmp (ftp_last_respline,
1208                              "215 UNIX MultiNet Unix Emulation V5.3(93)", 41))
1209         *unix_type = UST_MULTINET;
1210     }
1211   else if (!c_strcasecmp (request, "WINDOWS_NT")
1212            || !c_strcasecmp (request, "WINDOWS2000"))
1213     *server_type = ST_WINNT;
1214   else if (!c_strcasecmp (request, "MACOS"))
1215     *server_type = ST_MACOS;
1216   else if (!c_strcasecmp (request, "OS/400"))
1217     *server_type = ST_OS400;
1218   else
1219     *server_type = ST_OTHER;
1220 
1221   xfree (ftp_last_respline);
1222   xfree (respline);
1223   /* All OK.  */
1224   return FTPOK;
1225 }
1226 
1227 /* Sends the PWD command to the server. */
1228 uerr_t
ftp_pwd(int csock,char ** pwd)1229 ftp_pwd (int csock, char **pwd)
1230 {
1231   char *request, *respline;
1232   int nwritten;
1233   uerr_t err;
1234 
1235   /* Send PWD request.  */
1236   request = ftp_request ("PWD", NULL);
1237   nwritten = fd_write (csock, request, strlen (request), -1);
1238   if (nwritten < 0)
1239     {
1240       xfree (request);
1241       return WRITEFAILED;
1242     }
1243   xfree (request);
1244   /* Get appropriate response.  */
1245   err = ftp_response (csock, &respline);
1246   if (err != FTPOK)
1247     return err;
1248   if (*respline == '5')
1249     {
1250     err:
1251       xfree (respline);
1252       return FTPSRVERR;
1253     }
1254 
1255   /* Skip the number (257), leading citation mark, trailing citation mark
1256      and everything following it. */
1257   strtok (respline, "\"");
1258   request = strtok (NULL, "\"");
1259   if (!request)
1260     /* Treat the malformed response as an error, which the caller has
1261        to handle gracefully anyway.  */
1262     goto err;
1263 
1264   /* Has the `pwd' been already allocated?  Free! */
1265   xfree (*pwd);
1266 
1267   *pwd = xstrdup (request);
1268 
1269   xfree (respline);
1270   /* All OK.  */
1271   return FTPOK;
1272 }
1273 
1274 /* Sends the SIZE command to the server, and returns the value in 'size'.
1275  * If an error occurs, size is set to zero. */
1276 uerr_t
ftp_size(int csock,const char * file,wgint * size)1277 ftp_size (int csock, const char *file, wgint *size)
1278 {
1279   char *request, *respline;
1280   int nwritten;
1281   uerr_t err;
1282 
1283   /* Send PWD request.  */
1284   request = ftp_request ("SIZE", file);
1285   nwritten = fd_write (csock, request, strlen (request), -1);
1286   if (nwritten < 0)
1287     {
1288       xfree (request);
1289       *size = 0;
1290       return WRITEFAILED;
1291     }
1292   xfree (request);
1293   /* Get appropriate response.  */
1294   err = ftp_response (csock, &respline);
1295   if (err != FTPOK)
1296     {
1297       *size = 0;
1298       return err;
1299     }
1300   if (*respline == '5')
1301     {
1302       /*
1303        * Probably means SIZE isn't supported on this server.
1304        * Error is nonfatal since SIZE isn't in RFC 959
1305        */
1306       xfree (respline);
1307       *size = 0;
1308       return FTPOK;
1309     }
1310 
1311   errno = 0;
1312   *size = str_to_wgint (respline + 4, NULL, 10);
1313   if (errno)
1314     {
1315       /*
1316        * Couldn't parse the response for some reason.  On the (few)
1317        * tests I've done, the response is 213 <SIZE> with nothing else -
1318        * maybe something a bit more resilient is necessary.  It's not a
1319        * fatal error, however.
1320        */
1321       xfree (respline);
1322       *size = 0;
1323       return FTPOK;
1324     }
1325 
1326   xfree (respline);
1327   /* All OK.  */
1328   return FTPOK;
1329 }
1330 
1331 /* If URL's params are of the form "type=X", return character X.
1332    Otherwise, return 'I' (the default type).  */
1333 char
ftp_process_type(const char * params)1334 ftp_process_type (const char *params)
1335 {
1336   if (params
1337       && 0 == strncasecmp (params, "type=", 5)
1338       && params[5] != '\0')
1339     return c_toupper (params[5]);
1340   else
1341     return 'I';
1342 }
1343