1 /*$Id: jftp.c,v 1.64 2000/05/27 13:47:43 jens Exp $*/
2 /*
3 * Copyright (c) 1997, 1998, 1999, 2000
4 * Jens A. Nilsson, jnilsson@ludd.luth.se. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #ifdef MTYPES
29 #include "missing/defs.h"
30 #endif
31
32 #ifdef MEMDEBUG
33 #include <memdebug.h>
34 #endif
35
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42
43 #include <ctype.h>
44 #include <setjmp.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <errno.h>
48 #include <netdb.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <stdarg.h>
52 #include <fcntl.h>
53 #include <stdlib.h>
54
55 #ifdef NO_STRLCPY
56 #include "strlcpy.h"
57 #endif
58
59 #include "jftp.h"
60 #include "e_err.h"
61
62 #ifdef lint
63 #undef va_start
64 #define va_start(x, y) { x = x ;}
65 #endif
66
67 /* Older versions of NetBSD than 1.3K doesn't have socklen_t defined */
68 #ifdef __NetBSD__
69 #ifdef __NetBSD_Version
70 #if __NetBSD_Version__ < 103110000
71 #define socklen_t int
72 #endif
73 #else
74 #define socklen_t int
75 #endif /* __NetBSD_Version__ */
76 #endif /* __NetBSD__ */
77
78 /* FreeBSD doesn't have socklen_t defined */
79 #ifdef __FreeBSD__
80 #define socklen_t int
81 #endif
82
83 #ifdef NOPROTOS
84 int socket(int, int, int);
85 int connect(int, const struct sockaddr *, int);
86 int setsockopt(int s, int level, int optname, const void *optval, int len);
87 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
88 struct timeval *timeout);
89 void bzero(void *, size_t);
90 int vsnprintf(char *str, size_t size, const char *format, va_list ap);
91 char *strdup(const char *);
92 int gethostname(char *, int);
93 int bind(int, const struct sockaddr *, int);
94 int getsockname(int, struct sockaddr *, int *);
95 int listen(int, int);
96 int accept(int, struct sockaddr * addr, int *);
97 int mkstemp(char *);
98 #endif
99
100 int ftp_set_sock_opts(struct ftp_con *c, int fd);
101 char *ftp_dir2(struct ftp_con * c, const char *flags, const char *dir);
102
103 #define FD_CLOSE(fd) \
104 if ((fd) != -1) { \
105 (void) close(fd); \
106 (fd) = -1; \
107 }
108
109 #if defined(lint)
110 #define __FUNCTION__ "__FUNCTION__"
111 #endif
112
113 #define E_LOGX(level, fmt) \
114 e_logx(level, "ftp: %s: " fmt, __FUNCTION__);
115 #define E_LOGX_1(level, fmt, a1) \
116 e_logx(level, "ftp: %s: " fmt, __FUNCTION__, a1);
117 #define E_LOGX_2(level, fmt, a1, a2) \
118 e_logx(level, "ftp: %s: " fmt, __FUNCTION__, a1, a2);
119 #define E_LOGX_3(level, fmt, a1, a2, a3) \
120 e_logx(level, "ftp: %s: " fmt, __FUNCTION__, a1, a2, a3);
121 #define E_LOGX_4(level, fmt, a1, a2, a3, a4) \
122 e_logx(level, "ftp: %s: " fmt, __FUNCTION__, a1, a2, a3, a4);
123
124 #define E_LOG(level, fmt) \
125 e_log(level, "ftp: %s: " fmt, __FUNCTION__);
126 #define E_LOG_1(level, fmt, a1) \
127 e_log(level, "ftp: %s: " fmt, __FUNCTION__, a1);
128 #define E_LOG_2(level, fmt, a1, a2) \
129 e_log(level, "ftp: %s: " fmt, __FUNCTION__, a1, a2);
130 #define E_LOG_3(level, fmt, a1, a2, a3) \
131 e_log(level, "ftp: %s: " fmt, __FUNCTION__, a1, a2, a3);
132 #define E_LOG_4(level, fmt, a1, a2, a3, a4) \
133 e_log(level, "ftp: %s: " fmt, __FUNCTION__, a1, a2, a3, a4);
134
135 #ifdef INET6
136 /* translate IPv4 mapped IPv6 address to IPv4 address */
137 static void
unmappedaddr(struct sockaddr_in6 * sin6,int * len)138 unmappedaddr(struct sockaddr_in6 *sin6, int *len)
139 {
140 struct sockaddr_in *sin4;
141 u_int32_t addr;
142 int port;
143
144 if (sin6->sin6_family != AF_INET6 ||
145 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
146 return;
147 sin4 = (struct sockaddr_in *)sin6;
148 addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
149 port = sin6->sin6_port;
150 memset(sin4, 0, sizeof(struct sockaddr_in));
151 sin4->sin_addr.s_addr = addr;
152 sin4->sin_port = port;
153 sin4->sin_family = AF_INET;
154 #ifdef SIN6_LEN
155 sin4->sin_len = sizeof(struct sockaddr_in);
156 #endif
157 *len = sizeof(struct sockaddr_in);
158 }
159 #endif
160
161 void
ftp_set_timeout_val(struct ftp_con * c,int n)162 ftp_set_timeout_val(struct ftp_con *c, int n)
163 {
164 c->ftp_timeout = n < 0 ? 150 : n;
165 }
166
167 void
ftp_set_passive(struct ftp_con * c,int passive)168 ftp_set_passive(struct ftp_con *c, int passive)
169 {
170 c->ftp_passive = passive;
171 }
172
173 void
ftp_set_tempdir(struct ftp_con * c,char * tempdir)174 ftp_set_tempdir(struct ftp_con *c, char *tempdir)
175 {
176 c->ftp_tempdir = tempdir;
177 }
178
179 static ssize_t
ftp_write(struct ftp_con * c,int fd,void * buf,size_t nbytes)180 ftp_write(struct ftp_con *c, int fd, void *buf, size_t nbytes)
181 {
182 ssize_t res;
183 #ifndef SO_RCVTIMEO
184 struct timeval tv;
185 fd_set fdset;
186
187 tv.tv_sec = c->ftp_timeout;
188 tv.tv_usec = 0;
189 FD_ZERO(&fdset);
190 FD_SET(fd, &fdset);
191 do
192 res = select(fd + 1, NULL, &fdset, &tv);
193 while (res < 0 && errno == EINTR);
194 if (res < 0)
195 return res;
196 if (res == 0) {
197 c->ftp_timed_out = 1;
198 return -1;
199 }
200 do
201 res = write(fd, buf, nbytes);
202 while (res < 0 && errno == EINTR);
203 #endif
204 do
205 res = write(fd, buf, nbytes);
206 while (res < 0 && errno == EINTR);
207 c->ftp_timed_out = (res < 0 && errno == EWOULDBLOCK);
208 return res;
209 }
210
211 static ssize_t
ftp_read(struct ftp_con * c,int fd,void * buf,size_t nbytes)212 ftp_read(struct ftp_con *c, int fd, void *buf, size_t nbytes)
213 {
214 ssize_t res;
215 #ifndef SO_RCVTIMEO
216 struct timeval tv;
217 fd_set fdset;
218
219 tv.tv_sec = c->ftp_timeout;
220 tv.tv_usec = 0;
221 FD_ZERO(&fdset);
222 FD_SET(fd, &fdset);
223 do
224 res = select(fd + 1, &fdset, NULL, &tv);
225 while (res < 0 && errno == EINTR);
226 if (res < 0)
227 return res;
228 if (res == 0) {
229 c->ftp_timed_out = 1;
230 return -1;
231 }
232 do
233 res = read(fd, buf, nbytes);
234 while (res < 0 && errno == EINTR);
235 #endif
236 do
237 res = read(fd, buf, nbytes);
238 while (res < 0 && errno == EINTR);
239 c->ftp_timed_out = (res < 0 && errno == EWOULDBLOCK);
240 return res;
241 }
242
243 int
ftp_req(struct ftp_con * c,const char * fmt,...)244 ftp_req(struct ftp_con *c, const char *fmt, ...)
245 {
246 va_list ap;
247 int ftp_status, eol, i, islongtext;
248 ssize_t done, res, size, pos;
249 char *nfmt;
250 char rescode[5];
251
252 if (*fmt != ' ') {
253 if ((nfmt = alloca(strlen(fmt) + 3)) == NULL) {
254 c->ftp_resp = JFTP_ERRNO;
255 return -1;
256 }
257 (void)strcpy(nfmt, fmt);
258 (void)strcat(nfmt, "\r\n");
259 va_start(ap, fmt);
260 (void)vsnprintf(c->ftp_buf, sizeof(c->ftp_buf), nfmt, ap);
261 va_end(ap);
262 E_LOGX_2(2, "command: %s %d", c->ftp_buf, 3);
263 res = ftp_write(c, c->ftp_com, c->ftp_buf, strlen(c->ftp_buf));
264
265 /* sometimes it's possible to get res = 0 under Solaris */
266 if (res <= 0 || c->ftp_timed_out)
267 goto io_err;
268
269 c->ftp_sent += res;
270 }
271 (void)memset(c->ftp_buf, 0, sizeof(c->ftp_buf));
272 size = sizeof(c->ftp_buf);
273 pos = 0;
274 eol = 0;
275 islongtext = 0;
276 ftp_status = 0;
277 do {
278 done = ftp_read(c, c->ftp_com, c->ftp_buf + pos, (size_t)1);
279
280 /* We shouldn't get done == 0 since we
281 * are expecting a newline
282 */
283 if (done <= 0 || c->ftp_timed_out)
284 goto io_err;
285
286 c->ftp_recd += done;
287 for (i = (int)pos; i < (pos + done); i++) {
288 if (c->ftp_buf[i] == '\n') {
289 eol = 1;
290 c->ftp_buf[i] = 0;
291 break;
292 }
293 }
294 size -= done;
295 pos += done;
296 if(eol) {
297 ftp_status=0;
298
299 /* We are interested in the first 4 bytes */
300 (void)memset(rescode, '\0', sizeof(rescode));
301 (void)memcpy(rescode, c->ftp_buf, sizeof(rescode) - 1);
302
303 if (isdigit(rescode[0])
304 && isdigit(rescode[1])
305 && isdigit(rescode[2])) {
306 res = sscanf(rescode, "%3d", &ftp_status);
307
308 E_LOGX_1(3, "ftp_status: %d", ftp_status);
309 E_LOGX_1(3, "c->ftp_buf: %s", c->ftp_buf);
310
311 if ((rescode[3] == '-' || rescode[3] == ' ')
312 && (res == 1)) {
313 islongtext = (rescode[3] == '-');
314 if (ftp_status < 100) {
315 E_LOGX_1(1, "%s", c->ftp_buf);
316 c->ftp_resp = JFTP_ERR;
317 return -1;
318 }
319 }
320 }
321 if(islongtext) {
322 size = sizeof(c->ftp_buf);
323 pos = 0;
324 eol = 0;
325 }
326 }
327 } while (size && !eol);
328 if ((!eol) || (!ftp_status)) {
329 E_LOGX_1(1, "%s", c->ftp_buf);
330 c->ftp_resp = JFTP_ERR;
331 return -1;
332 }
333 c->ftp_resp = ftp_status;
334 return 0;
335
336 io_err:
337 if (c->ftp_timed_out) {
338 E_LOGX(0, "timeout");
339 c->ftp_resp = JFTP_TIMEOUT;
340 } else {
341 E_LOGX(0, "command connection broken");
342 c->ftp_resp = JFTP_BROKEN;
343 }
344 FD_CLOSE(c->ftp_com);
345 FD_CLOSE(c->ftp_listen);
346 FD_CLOSE(c->ftp_data);
347 return -1;
348 }
349
350 struct ftp_con *
ftp_login(char * host,int port,int family,char * username,char * password,FILE * logfile,int verbose)351 ftp_login(char *host, int port, int family, char *username,
352 char *password, FILE * logfile, int verbose)
353 {
354 struct ftp_con *c;
355
356 c = calloc((size_t)1, sizeof(*c));
357 if ((c->ftp_remote_host = strdup(host)) == NULL)
358 goto ret_bad;
359 if ((c->ftp_user_name = strdup(username)) == NULL)
360 goto ret_bad;
361 if ((c->ftp_password = strdup(password)) == NULL)
362 goto ret_bad;
363 c->ftp_logfile = logfile;
364 c->ftp_verbose = verbose;
365 e_set_level(verbose); /* XXX - global */
366 c->ftp_com = -1;
367 c->ftp_listen = -1;
368 c->ftp_data = -1;
369 c->ftp_port = port;
370 c->ftp_family = family;
371 c->ftp_retries = 20;
372 c->ftp_timeout = JFTP_TIMEOUT_VAL;
373 c->ftp_relogins = -1;
374 if ((c->ftp_tempdir = strdup(JFTP_DIR)) == NULL)
375 goto ret_bad;
376
377 if (ftp_relogin(c) < 0)
378 goto ret_bad;
379
380 return c;
381
382 ret_bad:
383 ftp_unalloc(c);
384 return NULL;
385 }
386
387 void
ftp_unalloc(struct ftp_con * c)388 ftp_unalloc(struct ftp_con *c)
389 {
390 #define FC_FREE(var) if (c->var) free(c->var)
391
392 if (c == NULL)
393 return;
394 FC_FREE(ftp_remote_host);
395 FC_FREE(ftp_user_name);
396 FC_FREE(ftp_password);
397 FC_FREE(ftp_tempdir);
398 free(c);
399 }
400
401 int
ftp_relogin(struct ftp_con * c)402 ftp_relogin(struct ftp_con *c)
403 {
404 #ifdef INET6
405 struct addrinfo hints, *res0 = NULL, *res;
406 char str_port[NI_MAXSERV];
407 #else
408 struct sockaddr_in server;
409 struct hostent *hp;
410 #endif
411
412 c->ftp_relogins++;
413 FD_CLOSE(c->ftp_com);
414 FD_CLOSE(c->ftp_listen);
415 FD_CLOSE(c->ftp_data);
416 #ifdef INET6
417 snprintf(str_port, sizeof(str_port), "%d", c->ftp_port);
418 memset(&hints, 0, sizeof(hints));
419 hints.ai_family = c->ftp_family;
420 hints.ai_socktype = SOCK_STREAM;
421 hints.ai_flags = AI_CANONNAME;
422 if (getaddrinfo(c->ftp_remote_host, str_port, &hints, &res0) != 0) {
423 E_LOGX_1(0, "getaddrinfo(%s): failed", c->ftp_remote_host);
424 c->ftp_resp = JFTP_ERR;
425 return -1;
426 }
427 c->ftp_com = -1;
428 for (res = res0; res; res = res->ai_next) {
429 c->ftp_com = socket(res->ai_family, res->ai_socktype,
430 res->ai_protocol);
431 if (c->ftp_com < 0)
432 continue;
433 if (connect(c->ftp_com, res->ai_addr, res->ai_addrlen) >= 0)
434 break;
435 FD_CLOSE(c->ftp_com);
436 c->ftp_com = -1;
437 }
438 freeaddrinfo(res0);
439 if (c->ftp_com < 0) {
440 E_LOG(0, "connect");
441 c->ftp_resp = JFTP_ERR;
442 return -1;
443 }
444 #else
445 c->ftp_com = socket(AF_INET, SOCK_STREAM, 0);
446 if (c->ftp_com < 0) {
447 E_LOG(0, "socket");
448 c->ftp_resp = JFTP_ERR;
449 return -1;
450 }
451 server.sin_family = AF_INET;
452 hp = gethostbyname(c->ftp_remote_host);
453 if (hp == NULL) {
454 E_LOGX_1(0, "gethostbyname(%s): failed", c->ftp_remote_host);
455 FD_CLOSE(c->ftp_com);
456 c->ftp_resp = JFTP_ERR;
457 return -1;
458 }
459 (void) memcpy(&server.sin_addr, hp->h_addr, sizeof(server.sin_addr));
460 server.sin_port = htons(c->ftp_port);
461
462 /* NOSTRICT server */
463 if (connect(c->ftp_com, (struct sockaddr *) & server, sizeof(server)) < 0) {
464 E_LOG(0, "connect");
465 FD_CLOSE(c->ftp_com);
466 c->ftp_resp = JFTP_ERR;
467 return -1;
468 }
469 #endif
470 if (ftp_req(c, JFTP_RESPONSE) < 0 || c->ftp_resp != 220) {
471 E_LOGX_1(0, "unexpected greeting from server: %s", c->ftp_buf);
472 FD_CLOSE(c->ftp_com);
473 c->ftp_resp = JFTP_ERR;
474 return -1;
475 }
476 if (ftp_req(c, "user %s", c->ftp_user_name) < 0
477 || (c->ftp_resp != 331 && c->ftp_resp != 230)) {
478 E_LOGX_1(0, "Username %s: failed", c->ftp_user_name);
479 FD_CLOSE(c->ftp_com);
480 c->ftp_resp = JFTP_ERR;
481 return -1;
482 }
483 /* USER command can respond 230 immediately in some cases */
484 if (c->ftp_resp != 230) {
485 if (ftp_req(c, "pass %s", c->ftp_password) < 0
486 || c->ftp_resp != 230) {
487 E_LOGX(0, "Password xxxxx: failed");
488 FD_CLOSE(c->ftp_com);
489 c->ftp_resp = JFTP_ERR;
490 return -1;
491 }
492 }
493 c->ftp_resp = 0;
494 if (ftp_req(c, "TYPE I") < 0 || c->ftp_resp != 200) {
495 E_LOGX(0, "Setting BIN type: failed");
496 FD_CLOSE(c->ftp_com);
497 c->ftp_resp = JFTP_ERR;
498 return -1;
499 }
500 if (c->ftp_remote_dir != NULL && strcmp(c->ftp_remote_dir, "/") != 0)
501 return ftp_cd(c, c->ftp_remote_dir);
502 return 0;
503 }
504
505
506 int
ftp_cd(struct ftp_con * c,const char * dir)507 ftp_cd(struct ftp_con *c, const char *dir)
508 {
509 char *p;
510 int res;
511
512 if ((p = strdup(dir)) == NULL) { /* Thank You John Polstra */
513 c->ftp_resp = JFTP_ERRNO;
514 return -1;
515 }
516
517 /* Save new directory on server */
518 if (c->ftp_remote_dir != NULL)
519 free(c->ftp_remote_dir);
520 c->ftp_remote_dir = p;
521
522 /* try to change directory on server */
523 res = ftp_req(c, "CWD %s", c->ftp_remote_dir);
524
525 /* req failed, ftp_resp has the error */
526 if (res < 0)
527 return res;
528
529 /* our request wasn't sucessful */
530 if (c->ftp_resp != 250) {
531 c->ftp_resp = JFTP_ERR;
532 return -1;
533 }
534 return 0;
535 }
536
537 /* ARGSUSED */
538 int
ftp_set_sock_opts(struct ftp_con * c,int fd)539 ftp_set_sock_opts(struct ftp_con *c, int fd)
540 {
541 #if !defined(ULTRIX)
542 int buf_size = 65536; /* 64k, this can speed up if
543 * delay x bandwidth is large
544 */
545 static int failed = 0;
546
547 c = NULL; /* quiet gcc */
548
549 /* Don't make the same misstake again */
550 if (failed)
551 return -1;
552 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf_size,
553 (socklen_t)sizeof(buf_size)) < 0) {
554
555 E_LOG_1(1, "setsockopt: SO_RCVBUF = %d", buf_size);
556 failed = 1;
557 return -1;
558 }
559 #endif
560 return 0;
561 }
562
563 int
ftp_port(struct ftp_con * c)564 ftp_port(struct ftp_con *c)
565 {
566 char *p, *a;
567 #ifdef INET6
568 struct sockaddr_storage sin2;
569 struct sockaddr_in6 *sin6;
570 char hname[INET6_ADDRSTRLEN];
571 u_char addr[4];
572 int port;
573 #else
574 struct sockaddr_in sin2;
575 #endif
576 struct sockaddr_in *sin4;
577 int arg;
578 int len, res;
579 int a0, a1, a2, a3, p0, p1;
580
581 c->ftp_downloads++;
582
583 len = sizeof(sin2);
584 if (getsockname(c->ftp_com, (struct sockaddr *)&sin2, &len) == -1) {
585 E_LOG(0, "getsockname");
586 FD_CLOSE(c->ftp_com);
587 c->ftp_resp = JFTP_BROKEN;
588 return -1;
589 }
590 switch (((struct sockaddr *)&sin2)->sa_family) {
591 case AF_INET:
592 break;
593 #ifdef INET6
594 case AF_INET6:
595 unmappedaddr((struct sockaddr_in6 *)&sin2, &len);
596 break;
597 #endif
598 default:
599 E_LOG(0, "getsockname");
600 FD_CLOSE(c->ftp_com);
601 c->ftp_resp = JFTP_BROKEN;
602 return -1;
603 }
604
605 /* doing active ftp */
606 if (!c->ftp_passive) {
607 /* sanity check */
608 if (c->ftp_listen != -1) {
609 FD_CLOSE(c->ftp_listen);
610 E_LOGX(1, "ERROR This shouldn't happen, "
611 "data connection wasn't closed");
612 }
613
614 /* create listen socket */
615 c->ftp_listen = socket(((struct sockaddr *)&sin2)->sa_family,
616 SOCK_STREAM, 0);
617 if (c->ftp_listen < 0) {
618 E_LOGX(0, "socket");
619 c->ftp_listen = -1;
620 c->ftp_resp = JFTP_BROKEN;
621 return -1;
622 }
623
624 /* bind socket */
625 switch (((struct sockaddr *)&sin2)->sa_family) {
626 case AF_INET:
627 ((struct sockaddr_in *)&sin2)->sin_port = 0;
628 #ifdef IP_PORTRANGE
629 arg = IP_PORTRANGE_HIGH;
630 setsockopt(c->ftp_listen, IPPROTO_IP, IP_PORTRANGE,
631 (char *)&arg, sizeof(arg));
632 #endif
633 break;
634 #ifdef INET6
635 case AF_INET6:
636 ((struct sockaddr_in6 *)&sin2)->sin6_port = 0;
637 #ifdef IPV6_PORTRANGE
638 arg = IPV6_PORTRANGE_HIGH;
639 setsockopt(c->ftp_listen, IPPROTO_IPV6, IPV6_PORTRANGE,
640 (char *)&arg, sizeof(arg));
641 #endif
642 break;
643 #endif
644 }
645 if (bind(c->ftp_listen, (struct sockaddr *) &sin2, len) < 0) {
646 E_LOG(0, "bind");
647 c->ftp_resp = JFTP_BROKEN;
648 FD_CLOSE(c->ftp_listen);
649 FD_CLOSE(c->ftp_com);
650 return -1;
651 }
652
653 /* get the port we are going to listen on */
654 len = sizeof(sin2);
655 /* NOSTRICT sin2 */
656 if (getsockname(c->ftp_listen, (struct sockaddr *) &sin2, &len) < 0) {
657 E_LOG(0, "getsockname");
658 FD_CLOSE(c->ftp_listen);
659 FD_CLOSE(c->ftp_com);
660 c->ftp_resp = JFTP_BROKEN;
661 return -1;
662 }
663
664 (void) ftp_set_sock_opts(c, c->ftp_listen);
665 if (listen(c->ftp_listen, 1) < 0) {
666 E_LOG(0, "listen");
667 FD_CLOSE(c->ftp_listen);
668 FD_CLOSE(c->ftp_com);
669 c->ftp_resp = JFTP_BROKEN;
670 return -1;
671 }
672
673 /* do the port command */
674 switch (((struct sockaddr *)&sin2)->sa_family) {
675 case AF_INET:
676 sin4 = (struct sockaddr_in *)&sin2;
677 a = (char *)&sin4->sin_addr.s_addr;
678 p = (char *)&sin4->sin_port;
679 res = ftp_req(c, "PORT %d,%d,%d,%d,%d,%d",
680 a[0] & 0xff, a[1] & 0xff,
681 a[2] & 0xff, a[3] & 0xff,
682 p[0] & 0xff, p[1] & 0xff);
683 break;
684 #ifdef INET6
685 case AF_INET6:
686 sin6 = (struct sockaddr_in6 *)&sin2;
687 if (getnameinfo((struct sockaddr *)&sin2, sin2.ss_len,
688 hname, sizeof(hname),
689 NULL, 0, NI_NUMERICHOST) != 0) {
690 E_LOG(0, "getnameinfo");
691 FD_CLOSE(c->ftp_listen);
692 FD_CLOSE(c->ftp_com);
693 c->ftp_resp = JFTP_BROKEN;
694 return -1;
695 }
696 res = ftp_req(c, "EPRT |%d|%s|%d|", 2, hname,
697 htons(sin6->sin6_port));
698 break;
699 #endif
700 }
701 if (res < 0 || c->ftp_resp != 200) {
702 if (res >= 0)
703 c->ftp_resp = JFTP_ERR;
704 FD_CLOSE(c->ftp_listen);
705 return -1;
706 }
707 return 0;
708 } else { /* passive ftp */
709 switch (((struct sockaddr *)&sin2)->sa_family) {
710 case AF_INET:
711 res = ftp_req(c, "PASV");
712 if (res < 0 || c->ftp_resp != 227) {
713 if (res >= 0)
714 c->ftp_resp = JFTP_ERR;
715 return -1;
716 }
717 for (p = c->ftp_buf; *p != '\0' && *p != '('; p++);
718 res = sscanf(p, "(%d,%d,%d,%d,%d,%d",
719 &a0, &a1, &a2, &a3, &p0, &p1);
720 if (res != 6) {
721 c->ftp_resp = JFTP_ERR;
722 return -1;
723 }
724 sin4 = (struct sockaddr_in *)&sin2;
725 /* NOSTRICT a */
726 a = (char *)&sin4->sin_addr.s_addr;
727 a[0] = a0 & 0xff;
728 a[1] = a1 & 0xff;
729 a[2] = a2 & 0xff;
730 a[3] = a3 & 0xff;
731 /* NOSTRICT p */
732 p = (char *)&sin4->sin_port;
733 p[0] = p0 & 0xff;
734 p[1] = p1 & 0xff;
735 sin4->sin_family = AF_INET;
736 break;
737 #ifdef INET6
738 case AF_INET6:
739 res = ftp_req(c, "EPSV");
740 if (res < 0 || c->ftp_resp != 229) {
741 if (res >= 0)
742 c->ftp_resp = JFTP_ERR;
743 return -1;
744 }
745 for (p = c->ftp_buf; *p != '\0' && *p != '('; p++);
746 if (*p == '\0') {
747 c->ftp_resp = JFTP_ERR;
748 return -1;
749 }
750 ++p;
751 res = sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1],
752 &addr[2], &port, &addr[3]);
753 if (res != 5 || addr[0] != addr[1] ||
754 addr[0] != addr[2] || addr[0] != addr[3]) {
755 c->ftp_resp = JFTP_ERR;
756 return -1;
757 }
758 len = sizeof(sin2);
759 res = getpeername(c->ftp_com,
760 (struct sockaddr *)&sin2, &len);
761 if (res == -1 || sin2.ss_family != AF_INET6) {
762 E_LOG(0, "getpeername");
763 c->ftp_resp = JFTP_BROKEN;
764 return -1;
765 }
766 sin6 = (struct sockaddr_in6 *)&sin2;
767 sin6->sin6_port = htons(port);
768 break;
769 #endif
770 }
771
772 /* sanity check */
773 if (c->ftp_data != -1) {
774 FD_CLOSE(c->ftp_data);
775 E_LOGX(1, "ERROR1 This shouldn't happen, "
776 "data connection wasn't closed");
777 }
778 /* connect to server */
779 c->ftp_data = socket(((struct sockaddr *)&sin2)->sa_family,
780 SOCK_STREAM, 0);
781 if (c->ftp_data < 0) {
782 E_LOG(0, "socket");
783 c->ftp_resp = JFTP_ERR;
784 return -1;
785 }
786 (void)ftp_set_sock_opts(c, c->ftp_data);
787 if (connect(c->ftp_data,
788 /* NOSTRICT sin2 */
789 (struct sockaddr *)&sin2, len) < 0) {
790 E_LOG(0, "connect");
791 FD_CLOSE(c->ftp_data);
792 c->ftp_resp = JFTP_ERR;
793 return -1;
794 }
795 return 0;
796 }
797 /* NOT REACHED */
798 }
799
800 int
ftp_read_data(struct ftp_con * c,char * buf,size_t size)801 ftp_read_data(struct ftp_con *c, char *buf, size_t size)
802 {
803 int res;
804 ssize_t done = 0;
805 struct timeval tv;
806 fd_set fdset;
807 #ifndef SO_RCVTIMEO
808 int flags;
809 #endif
810
811 if ((c->ftp_data == -1) && (c->ftp_listen != -1)) {
812 tv.tv_sec = c->ftp_timeout;
813 tv.tv_usec = 0;
814 FD_ZERO(&fdset);
815 /* LINTED fdset */
816 FD_SET(c->ftp_listen, &fdset);
817 do
818 res = select(c->ftp_listen + 1, &fdset, NULL, NULL, &tv);
819 while (res < 0 && errno == EINTR);
820 if (res < 0) {
821 E_LOG(0, "select");
822 c->ftp_resp = JFTP_ERR;
823 return -1;
824 }
825 if (res == 0) {
826 FD_CLOSE(c->ftp_listen);
827 c->ftp_resp = JFTP_TIMEOUT;
828 c->ftp_timeouts++;
829 return -1;
830 }
831
832 c->ftp_data = accept(c->ftp_listen, 0, 0);
833 FD_CLOSE(c->ftp_listen);
834 if (c->ftp_data < 0) {
835 E_LOG(0, "accept");
836 c->ftp_data = -1;
837 c->ftp_resp = JFTP_BROKEN;
838 return -1;
839 }
840 #ifdef SO_RCVTIMEO
841 if (setsockopt(c->ftp_data, SOL_SOCKET, SO_RCVTIMEO,
842 /* LINTED */
843 (char *) &tv, (size_t) sizeof(tv)) < 0) {
844 E_LOG(0, "setsockopt");
845 FD_CLOSE(c->ftp_data);
846 c->ftp_resp = JFTP_ERR;
847 return -1;
848 }
849 #else
850 if ((flags = fcntl(c->ftp_data, F_GETFL, 0)) < 0) {
851 E_LOG(0, "fcntl F_GETFL");
852 FD_CLOSE(c->ftp_data);
853 c->ftp_resp = JFTP_ERR;
854 return -1;
855 }
856 flags |= O_NONBLOCK;
857 if (fcntl(c->ftp_data, F_SETFS, flags) < 0) {
858 E_LOG(0, "fcntl F_SETFS");
859 FD_CLOSE(c->ftp_data);
860 c->ftp_resp = JFTP_ERR;
861 return -1;
862 }
863 #endif
864 }
865 done = ftp_read(c, c->ftp_data, buf, size);
866 if (done < 0 || c->ftp_timed_out) {
867 FD_CLOSE(c->ftp_data);
868 if (c->ftp_timed_out)
869 c->ftp_resp = JFTP_TIMEOUT;
870 else
871 c->ftp_resp = JFTP_BROKEN;
872 return -1;
873 }
874 c->ftp_recd += done;
875 c->ftp_resp = (int)done;
876 return 0;
877 }
878
879 int
ftp_get(struct ftp_con * c,char * local_file,char * remote_file,size_t seekto)880 ftp_get(struct ftp_con *c, char *local_file, char *remote_file, size_t seekto)
881 {
882 FILE *fp;
883 int done, recieved;
884 ssize_t res;
885
886 if (seekto > 0) {
887 fp = fopen(local_file, "ab");
888 if (fp == NULL) {
889 E_LOG_1(0, "opening of %s for writing failed", local_file);
890 c->ftp_resp = JFTP_WRITEERR;
891 return -1;
892 }
893 if (fseek(fp, (long) seekto, SEEK_SET) < 0) {
894 E_LOG_2(0, "fseek to pos %d in %s failed",
895 (unsigned) seekto, local_file);
896 c->ftp_resp = JFTP_ERRNO;
897 return -1;
898 }
899 } else {
900 fp = fopen(local_file, "wb");
901 if (fp == NULL) {
902 E_LOG_1(0, "creation of %s failed", local_file);
903 c->ftp_resp = JFTP_WRITEERR;
904 return -1;
905 }
906 }
907 if (ftp_port(c) < 0) {
908 E_LOGX(0, "PORT command failed");
909 if (c->ftp_resp != JFTP_BROKEN && c->ftp_resp != JFTP_TIMEOUT)
910 c->ftp_resp = JFTP_ERR;
911 goto ret_bad;
912 return -1;
913 }
914 if (seekto > 0) {
915 res = ftp_req(c, "REST %lu", (unsigned long) seekto);
916 if (res < 0 || c->ftp_resp != 350) {
917 E_LOGX(0, "REST command failed");
918 if (res >= 0)
919 c->ftp_resp = JFTP_ERR;
920 goto ret_bad;
921 }
922 }
923 res = ftp_req(c, "RETR %s", remote_file);
924 if (res < 0 || c->ftp_resp != 150) {
925 E_LOGX(0, "RETR command failed");
926 if (res >= 0)
927 c->ftp_resp = JFTP_ERR;
928 goto ret_bad;
929 }
930 recieved = 0;
931 if (ftp_read_data(c, c->ftp_buf, sizeof(c->ftp_buf)) < 0)
932 goto ret_bad;
933 done = c->ftp_resp;
934 while (done > 0) {
935 recieved += done;
936 res = fwrite(c->ftp_buf, (size_t)done, (size_t)1, fp);
937 if (res < 0) {
938 E_LOG(0, "write");
939 /* XXX can't report error if this failes */
940 (void) ftp_req(c, JFTP_RESPONSE);
941 c->ftp_resp = JFTP_WRITEERR;
942 goto ret_bad;
943 }
944 if (ftp_read_data(c, c->ftp_buf, sizeof(c->ftp_buf)) < 0)
945 goto ret_bad;
946 done = c->ftp_resp;
947 }
948
949 (void)fclose(fp);
950 FD_CLOSE(c->ftp_listen);
951 FD_CLOSE(c->ftp_data);
952 res = ftp_req(c, JFTP_RESPONSE);
953 if (res < 0) {
954 E_LOGX(0, "broken connection");
955 FD_CLOSE(c->ftp_com);
956 c->ftp_resp = JFTP_BROKEN;
957 goto ret_bad;
958 }
959 if (c->ftp_resp != 226) {
960 E_LOGX_1(0, "retrieve of %s was aborted by server", remote_file);
961 c->ftp_resp = JFTP_ERR;
962 goto ret_bad;
963 }
964 return 0;
965
966 ret_bad:
967 FD_CLOSE(c->ftp_listen);
968 FD_CLOSE(c->ftp_data);
969 (void)fclose(fp);
970 return -1;
971 }
972
973 char *
ftp_dir2(struct ftp_con * c,const char * flags,const char * dir)974 ftp_dir2(struct ftp_con * c, const char *flags, const char *dir)
975 {
976 int done, recieved;
977 ssize_t res;
978 char *tmp;
979 int fd;
980
981 tmp = malloc(strlen(c->ftp_tempdir) + strlen(JFTP_TEMPFILE) + 2);
982 if (tmp == NULL) {
983 c->ftp_resp = JFTP_ERRNO;
984 return NULL;
985 }
986 (void) sprintf(tmp, "%s/%s", c->ftp_tempdir, JFTP_TEMPFILE);
987
988 fd = mkstemp(tmp);
989 if (fd < 0) {
990 c->ftp_resp = JFTP_WRITEERR;
991 E_LOG_1(0, "mkstemp(%s)", tmp);
992 return NULL;
993 }
994 if (ftp_port(c) < 0) {
995 E_LOGX(0, "PORT command failed");
996 FD_CLOSE(c->ftp_listen);
997 FD_CLOSE(c->ftp_data);
998 (void) close(fd);
999 (void) unlink(tmp);
1000 return NULL;
1001 }
1002
1003 /* Late versions of wu-ftpd does some kind of recursive
1004 * listing if only a '.' is given as directory.
1005 */
1006 if ((strcmp(dir, ".") == 0) || !*dir)
1007 res = ftp_req(c, "list %s", flags);
1008 else
1009 res = ftp_req(c, "list %s %s", flags, dir);
1010
1011 if (res < 0 || c->ftp_resp != 150) {
1012 FD_CLOSE(c->ftp_listen);
1013 FD_CLOSE(c->ftp_data);
1014 E_LOGX_1(0, "LIST command for %s failed", dir);
1015 if (res >= 0)
1016 c->ftp_resp = JFTP_ERR;
1017 (void) close(fd);
1018 (void) unlink(tmp);
1019 return NULL;
1020 }
1021 recieved = 0;
1022 if (ftp_read_data(c, c->ftp_buf, sizeof(c->ftp_buf)) < 0) {
1023 (void) close(fd);
1024 (void) unlink(tmp);
1025 return NULL;
1026 }
1027 done = c->ftp_resp;
1028 while (done > 0) {
1029 recieved += done;
1030 while((res = write(fd, c->ftp_buf, (size_t)done)) < 0 &&
1031 errno == EINTR);
1032 if (res < 0) {
1033 E_LOG_1(0, "write(%s)", tmp);
1034 c->ftp_resp = JFTP_WRITEERR;
1035 FD_CLOSE(c->ftp_listen);
1036 FD_CLOSE(c->ftp_data);
1037 (void) close(fd);
1038 (void) unlink(tmp);
1039 (void) ftp_req(c, JFTP_RESPONSE);
1040 return NULL;
1041 }
1042 if (ftp_read_data(c, c->ftp_buf, sizeof(c->ftp_buf)) < 0) {
1043 FD_CLOSE(c->ftp_listen);
1044 FD_CLOSE(c->ftp_data);
1045 (void) close(fd);
1046 (void) unlink(tmp);
1047 return NULL;
1048 }
1049 done = c->ftp_resp;
1050 }
1051
1052 (void) close(fd);
1053 FD_CLOSE(c->ftp_listen);
1054 FD_CLOSE(c->ftp_data);
1055 res = ftp_req(c, JFTP_RESPONSE);
1056 if (res < 0) {
1057 E_LOGX(0, "broken connection");
1058 FD_CLOSE(c->ftp_com);
1059 c->ftp_resp = JFTP_BROKEN;
1060 (void) unlink(tmp);
1061 return NULL;
1062 }
1063 if (c->ftp_resp != 226) {
1064 E_LOGX_1(0, "retrieve of dir %s was aborted by server", dir);
1065 (void) unlink(tmp);
1066 return NULL;
1067 }
1068 return tmp;
1069 }
1070
1071 char *
ftp_dir(struct ftp_con * c,const char * dir)1072 ftp_dir(struct ftp_con *c, const char *dir)
1073 {
1074 char buf[PATH_MAX];
1075 char *res;
1076
1077 strlcpy(buf, c->ftp_remote_dir, PATH_MAX);
1078 if (ftp_cd(c, dir) < 0)
1079 return NULL;
1080 res = ftp_dir2(c, "-lag", "");
1081 if (ftp_cd(c, buf) < 0)
1082 return NULL; /* XXX - maybe abort here */
1083 return res;
1084 }
1085
1086 #if NOTYET
1087 char *
ftp_dir_recurs(struct ftp_con * c,char * tempfile)1088 ftp_dir_recurs(struct ftp_con *c, char *tempfile)
1089 {
1090 return ftp_dir2(c, "-lagR", tempfile);
1091 }
1092 #endif
1093
1094
1095 int
ftp_bye(struct ftp_con * c)1096 ftp_bye(struct ftp_con * c)
1097 {
1098 int res;
1099
1100 res = ftp_req(c, "quit");
1101 FD_CLOSE(c->ftp_com);
1102 FD_CLOSE(c->ftp_listen);
1103 FD_CLOSE(c->ftp_data);
1104 if (res >= 0 && c->ftp_resp == 221)
1105 return 0;
1106 return -1;
1107 }
1108