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