2 * ftp.c: basic handling of an FTP command connection to check for
3 * directory availability. No transfer is needed.
10 #include <sys/socket.h>
19 /* #define DEBUG_FTP 1 */
24 static int ftpFd = -1;
25 static struct sockaddr_in ftpAddr;
26 static char hostname[100];
27 static int ftpPassive = 1;
28 static int dataFd = -1;
30 #define FTP_COMMAND_OK 200
31 #define FTP_SYNTAX_ERROR 500
32 #define FTP_GET_PASSWD 331
35 * Initialize the FTP handling.
39 gethostname(hostname, sizeof(hostname));
43 * Parsing of the server answer, we just extract the code.
45 * +XXX for last line of response
46 * -XXX for response to be continued
48 int parseFtpResponse(char *buf, int len) {
51 if (len < 3) return(-1);
52 if ((*buf >= '0') && (*buf <= '9'))
53 val = val * 10 + (*buf - '0');
57 if ((*buf >= '0') && (*buf <= '9'))
58 val = val * 10 + (*buf - '0');
62 if ((*buf >= '0') && (*buf <= '9'))
63 val = val * 10 + (*buf - '0');
73 * Read the response from the FTP server after a command.
74 * Returns the code number
77 int readFtpResponse(char *buf, int size) {
82 if (size <= 0) return(-1);
85 if ((len = recv(ftpFd, buf, size - 1, 0)) < 0) {
86 close(ftpFd); ftpFd = -1;
101 res = parseFtpResponse(ptr, end - ptr);
105 fprintf(stderr, "readFtpResponse failed: %s\n", ptr);
109 while ((ptr < end) && (*ptr != '\n')) ptr++;
112 fprintf(stderr, "readFtpResponse: unexpected end %s\n", buf);
114 return((-res) / 100);
116 if (*ptr != '\r') ptr++;
119 if (res < 0) goto get_more;
122 printf("Got %d\n", res);
128 * Get the response from the FTP server after a command.
129 * Returns the code number
132 int getFtpResponse(void) {
133 char buf[16 * 1024 + 1];
144 res = select(ftpFd + 1, &rfd, NULL, NULL, &tv);
145 if (res <= 0) return(res);
148 return(readFtpResponse(buf, 16 * 1024));
152 * Check if there is a response from the FTP server after a command.
153 * Returns the code number, or 0
155 int checkFtpResponse(void) {
164 switch(select(ftpFd + 1, &rfd, NULL, NULL, &tv)) {
175 return(readFtpResponse(buf, 1024));
179 * Send the user authentification
187 len = snprintf(buf, sizeof(buf), "USER anonymous\r\n");
191 res = send(ftpFd, buf, len, 0);
192 if (res < 0) return(res);
197 * Send the password authentification
200 int sendPasswd(void) {
205 len = snprintf(buf, sizeof(buf), "PASS mirrorfind@%s\r\n", hostname);
209 res = send(ftpFd, buf, len, 0);
210 if (res < 0) return(res);
223 len = snprintf(buf, sizeof(buf), "QUIT\r\n");
227 res = send(ftpFd, buf, len, 0);
232 * Connecting to the server, port 21 by default.
235 int connectFtp(const char *server, int port) {
240 * do the blocking DNS query.
242 hp = gethostbyname(server);
249 memset(&ftpAddr, 0, sizeof(ftpAddr));
250 ftpAddr.sin_family = AF_INET;
251 memcpy(&ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
254 ftpAddr.sin_port = htons(port);
255 ftpFd = socket(AF_INET, SOCK_STREAM, 0);
262 if (connect(ftpFd, (struct sockaddr *) &ftpAddr,
263 sizeof(struct sockaddr_in)) < 0) {
264 close(ftpFd); ftpFd = -1;
270 * Wait for the HELLO from the server.
272 res = getFtpResponse();
274 close(ftpFd); ftpFd = -1;
280 * State diagram for the login operation on the FTP server
285 * +---+ USER +---+------------->+---+
286 * | B |---------->| W | 2 ---->| E |
287 * +---+ +---+------ | -->+---+
290 * -------------- ----- | | |
296 * +---+ PASS +---+ 2 | ------>+---+
297 * | |---------->| W |------------->| S |
298 * +---+ +---+ ---------->+---+
301 * -------------- -------- |
307 * +---+ ACCT +---+-- | ----->+---+
308 * | |---------->| W | 4,5 -------->| F |
309 * +---+ +---+------------->+---+
313 close(ftpFd); ftpFd = -1;
317 res = getFtpResponse();
328 close(ftpFd); ftpFd = -1;
334 close(ftpFd); ftpFd = -1;
338 res = getFtpResponse();
343 fprintf(stderr, "FTP server asking for ACCNT on anonymous\n");
349 close(ftpFd); ftpFd = -1;
358 * Check an FTP directory on the server
361 int changeFtpDirectory(char *directory) {
367 * Expected response code for CWD:
371 * 500, 501, 502, 421, 530, 550
373 len = snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
377 res = send(ftpFd, buf, len, 0);
378 if (res < 0) return(res);
379 res = getFtpResponse();
381 close(ftpFd); ftpFd = -1;
385 if (res == 2) return(1);
395 int dataConnectFtp() {
399 unsigned char ad[6], *cur, *adp, *portp;
400 unsigned int temp[6];
401 struct sockaddr_in dataAddr;
404 dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
406 fprintf(stderr, "dataConnectFtp: failed to create socket\n");
408 dataAddrLen = sizeof(dataAddr);
409 memset(&dataAddr, 0, dataAddrLen);
410 dataAddr.sin_family = AF_INET;
413 len = snprintf(buf, sizeof(buf), "PASV\r\n");
417 res = send(ftpFd, buf, len, 0);
419 close(dataFd); dataFd = -1;
422 res = readFtpResponse(buf, sizeof(buf) -1);
425 close(dataFd); dataFd = -1;
429 * retry with an active connection
431 close(dataFd); dataFd = -1;
436 while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
437 if (sscanf(cur, "%d,%d,%d,%d,%d,%d", &temp[0], &temp[1], &temp[2],
438 &temp[3], &temp[4], &temp[5]) != 6) {
439 fprintf(stderr, "Invalid answer to PASV\n");
440 close(dataFd); dataFd = -1;
443 for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
444 memcpy(&dataAddr.sin_addr, &ad[0], 4);
445 memcpy(&dataAddr.sin_port, &ad[4], 2);
446 if (connect(dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
447 fprintf(stderr, "Failed to create a data connection\n");
448 close(dataFd); dataFd = -1;
452 getsockname(dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
453 dataAddr.sin_port = 0;
454 if (bind(dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
455 fprintf(stderr, "Failed to bind a port\n");
456 close(dataFd); dataFd = -1;
459 getsockname(dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
461 if (listen(dataFd, 1) < 0) {
462 fprintf(stderr, "Could not listen on port %d\n",
463 ntohs(dataAddr.sin_port));
464 close(dataFd); dataFd = -1;
467 adp = (unsigned char *) &dataAddr.sin_addr;
468 portp = (unsigned char *) &dataAddr.sin_port;
469 len = snprintf(buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
470 adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
471 portp[0] & 0xff, portp[1] & 0xff);
472 buf[sizeof(buf) - 1] = 0;
477 res = send(ftpFd, buf, len, 0);
479 close(dataFd); dataFd = -1;
482 res = getFtpResponse();
484 close(dataFd); dataFd = -1;
495 int dataConnectEndFtp() {
498 close(dataFd); dataFd = -1;
499 res = getFtpResponse();
501 close(dataFd); dataFd = -1;
502 close(ftpFd); ftpFd = -1;
512 int parseListFtp(const char *list, ftpListCallback callback, void *userData) {
513 const char *cur = list;
523 unsigned long size = 0;
527 if (!strncmp(cur, "total", 5)) {
529 while (*cur == ' ') cur++;
530 while ((*cur >= '0') && (*cur <= '9'))
531 links = (links * 10) + (*cur++ - '0');
532 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
535 } else if (*list == '+') {
538 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
540 if (*cur == 0) return(0);
542 while (*cur != ' ') {
546 if (*cur == 0) return(0);
549 while (*cur == ' ') cur++;
550 if (*cur == 0) return(0);
551 while ((*cur >= '0') && (*cur <= '9'))
552 links = (links * 10) + (*cur++ - '0');
553 while (*cur == ' ') cur++;
554 if (*cur == 0) return(0);
556 while (*cur != ' ') {
560 if (*cur == 0) return(0);
563 while (*cur == ' ') cur++;
564 if (*cur == 0) return(0);
566 while (*cur != ' ') {
570 if (*cur == 0) return(0);
573 while (*cur == ' ') cur++;
574 if (*cur == 0) return(0);
575 while ((*cur >= '0') && (*cur <= '9'))
576 size = (size * 10) + (*cur++ - '0');
577 while (*cur == ' ') cur++;
578 if (*cur == 0) return(0);
580 while (*cur != ' ') {
584 if (*cur == 0) return(0);
587 while (*cur == ' ') cur++;
588 if (*cur == 0) return(0);
589 while ((*cur >= '0') && (*cur <= '9'))
590 day = (day * 10) + (*cur++ - '0');
591 while (*cur == ' ') cur++;
592 if (*cur == 0) return(0);
593 if ((cur[1] == 0) || (cur[2] == 0)) return(0);
594 if ((cur[1] == ':') || (cur[2] == ':')) {
595 while ((*cur >= '0') && (*cur <= '9'))
596 hour = (hour * 10) + (*cur++ - '0');
597 if (*cur == ':') cur++;
598 while ((*cur >= '0') && (*cur <= '9'))
599 minute = (minute * 10) + (*cur++ - '0');
601 while ((*cur >= '0') && (*cur <= '9'))
602 year = (year * 10) + (*cur++ - '0');
604 while (*cur == ' ') cur++;
605 if (*cur == 0) return(0);
607 while ((*cur != '\n') && (*cur != '\r')) {
609 filename[i++] = *cur;
611 if (*cur == 0) return(0);
614 if ((*cur != '\n') && (*cur != '\r'))
616 while ((*cur == '\n') || (*cur == '\r'))
619 if (callback != NULL) {
620 callback(userData, filename, attrib, owner, group, size, links,
621 year, month, day, minute);
629 int listFtp(ftpListCallback callback, void *userData) {
636 dataFd = dataConnectFtp();
638 len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
642 res = send(ftpFd, buf, len, 0);
644 close(dataFd); dataFd = -1;
647 res = readFtpResponse(buf, sizeof(buf) -1);
649 close(dataFd); dataFd = -1;
657 FD_SET(dataFd, &rfd);
659 FD_SET(dataFd, &efd);
660 res = select(dataFd + 1, &rfd, NULL, &efd, &tv);
665 close(dataFd); dataFd = -1;
669 res = checkFtpResponse();
671 close(dataFd); dataFd = -1;
676 close(dataFd); dataFd = -1;
683 if ((len = read(dataFd, &buf[index], sizeof(buf) - (index + 1))) < 0) {
687 close(dataFd); dataFd = -1;
692 write(1, &buf[index], len);
698 res = parseListFtp(&buf[base], callback, userData);
702 memmove(&buf[0], &buf[base], index - base);
713 int getFtpSocket(const char *filename) {
716 if (filename == NULL)
718 dataFd = dataConnectFtp();
720 len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
724 res = send(ftpFd, buf, len, 0);
726 close(dataFd); dataFd = -1;
729 res = readFtpResponse(buf, sizeof(buf) -1);
731 close(dataFd); dataFd = -1;
734 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
738 res = send(ftpFd, buf, len, 0);
740 close(dataFd); dataFd = -1;
743 res = readFtpResponse(buf, sizeof(buf) -1);
745 close(dataFd); dataFd = -1;
755 int closeFtpSocket(int socket) {
756 return(dataConnectEndFtp());
762 int getFtp(ftpDataCallback callback, void *userData, const char *filename) {
768 if (filename == NULL)
770 if (callback == NULL)
772 if (getFtpSocket(filename) < 0)
779 FD_SET(dataFd, &rfd);
780 res = select(dataFd + 1, &rfd, NULL, NULL, &tv);
785 close(dataFd); dataFd = -1;
789 res = checkFtpResponse();
791 close(dataFd); dataFd = -1;
796 close(dataFd); dataFd = -1;
802 if ((len = read(dataFd, &buf, sizeof(buf))) < 0) {
803 callback(userData, buf, len);
804 close(dataFd); dataFd = -1;
808 callback(userData, buf, len);
811 return(closeFtpSocket(dataFd));
815 * Disconnect from the FTP server.
818 int disconnectFtp(void) {
819 if (ftpFd < 0) return(-1);
821 close(ftpFd); ftpFd = -1;
827 void ftpList(void *userData, const char *filename, const char* attrib,
828 const char *owner, const char *group, unsigned long size, int links,
829 int year, const char *month, int day, int minute) {
830 printf("%s %s %s %ld %s\n", attrib, owner, group, size, filename);
832 void ftpData(void *userData, const char *data, int len) {
833 if (userData == NULL) return;
838 fwrite(data, len, 1, userData);
841 int main(int argc, char **argv) {
844 const char *tstfile = "tstfile";
848 res = connectFtp(argv[1], 0);
852 res = connectFtp("localhost", 0);
854 fprintf(stderr, "Couldn't connect to localhost\n");
857 res = changeFtpDirectory("/linux");
859 fprintf(stderr, "disconnected\n");
864 fprintf(stderr, "/linux : CWD failed\n");
866 fprintf(stderr, "/linux : CWD successful\n");
868 res = changeFtpDirectory("/toto");
870 fprintf(stderr, "disconnected\n");
875 fprintf(stderr, "/toto : CWD failed\n");
877 fprintf(stderr, "/toto : CWD successful\n");
879 listFtp(ftpList, NULL);
880 output = fopen("/tmp/tstdata", "w");
881 if (output != NULL) {
882 if (getFtp(ftpData, (void *) output, tstfile) < 0)
883 fprintf(stderr, "Failed to get file %s\n", tstfile);
889 #endif /* STANDALONE */