14ca1ab94SDag-Erling Smørgrav /*- 24ca1ab94SDag-Erling Smørgrav * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 34ca1ab94SDag-Erling Smørgrav * All rights reserved. 44ca1ab94SDag-Erling Smørgrav * 54ca1ab94SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 64ca1ab94SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 74ca1ab94SDag-Erling Smørgrav * are met: 84ca1ab94SDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 94ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 104ca1ab94SDag-Erling Smørgrav * in this position and unchanged. 114ca1ab94SDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 124ca1ab94SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 134ca1ab94SDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 144ca1ab94SDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 154ca1ab94SDag-Erling Smørgrav * derived from this software without specific prior written permission 164ca1ab94SDag-Erling Smørgrav * 174ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 184ca1ab94SDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 194ca1ab94SDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 204ca1ab94SDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 214ca1ab94SDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 224ca1ab94SDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 234ca1ab94SDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 244ca1ab94SDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 254ca1ab94SDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 264ca1ab94SDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 274ca1ab94SDag-Erling Smørgrav * 287f3dea24SPeter Wemm * $FreeBSD$ 294ca1ab94SDag-Erling Smørgrav */ 304ca1ab94SDag-Erling Smørgrav 314ca1ab94SDag-Erling Smørgrav /* 328e3986eaSDag-Erling Smørgrav * Portions of this code were taken from or based on ftpio.c: 334ca1ab94SDag-Erling Smørgrav * 344ca1ab94SDag-Erling Smørgrav * ---------------------------------------------------------------------------- 354ca1ab94SDag-Erling Smørgrav * "THE BEER-WARE LICENSE" (Revision 42): 364ca1ab94SDag-Erling Smørgrav * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you 374ca1ab94SDag-Erling Smørgrav * can do whatever you want with this stuff. If we meet some day, and you think 384ca1ab94SDag-Erling Smørgrav * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 394ca1ab94SDag-Erling Smørgrav * ---------------------------------------------------------------------------- 404ca1ab94SDag-Erling Smørgrav * 414ca1ab94SDag-Erling Smørgrav * Major Changelog: 424ca1ab94SDag-Erling Smørgrav * 434ca1ab94SDag-Erling Smørgrav * Dag-Erling Co�dan Sm�rgrav 444ca1ab94SDag-Erling Smørgrav * 9 Jun 1998 454ca1ab94SDag-Erling Smørgrav * 464ca1ab94SDag-Erling Smørgrav * Incorporated into libfetch 474ca1ab94SDag-Erling Smørgrav * 484ca1ab94SDag-Erling Smørgrav * Jordan K. Hubbard 494ca1ab94SDag-Erling Smørgrav * 17 Jan 1996 504ca1ab94SDag-Erling Smørgrav * 514ca1ab94SDag-Erling Smørgrav * Turned inside out. Now returns xfers as new file ids, not as a special 524ca1ab94SDag-Erling Smørgrav * `state' of FTP_t 534ca1ab94SDag-Erling Smørgrav * 544ca1ab94SDag-Erling Smørgrav * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 554ca1ab94SDag-Erling Smørgrav * 564ca1ab94SDag-Erling Smørgrav */ 574ca1ab94SDag-Erling Smørgrav 580fba3a00SDag-Erling Smørgrav #include <sys/param.h> 594ca1ab94SDag-Erling Smørgrav #include <sys/socket.h> 604ca1ab94SDag-Erling Smørgrav #include <netinet/in.h> 614ca1ab94SDag-Erling Smørgrav 624ca1ab94SDag-Erling Smørgrav #include <ctype.h> 63fc6e9e65SDag-Erling Smørgrav #include <errno.h> 6432425dafSDag-Erling Smørgrav #include <netdb.h> 65346298f0SDag-Erling Smørgrav #include <stdarg.h> 664ca1ab94SDag-Erling Smørgrav #include <stdio.h> 678e3986eaSDag-Erling Smørgrav #include <stdlib.h> 684ca1ab94SDag-Erling Smørgrav #include <string.h> 695aea254fSDag-Erling Smørgrav #include <time.h> 708e3986eaSDag-Erling Smørgrav #include <unistd.h> 714ca1ab94SDag-Erling Smørgrav 724ca1ab94SDag-Erling Smørgrav #include "fetch.h" 73842a95ccSDag-Erling Smørgrav #include "common.h" 740fba3a00SDag-Erling Smørgrav #include "ftperr.h" 754ca1ab94SDag-Erling Smørgrav 764ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_USER "ftp" 774ca1ab94SDag-Erling Smørgrav #define FTP_ANONYMOUS_PASSWORD "ftp" 78346298f0SDag-Erling Smørgrav 79346298f0SDag-Erling Smørgrav #define FTP_OPEN_DATA_CONNECTION 150 80346298f0SDag-Erling Smørgrav #define FTP_OK 200 815aea254fSDag-Erling Smørgrav #define FTP_FILE_STATUS 213 823b7a6740SDag-Erling Smørgrav #define FTP_SERVICE_READY 220 83346298f0SDag-Erling Smørgrav #define FTP_PASSIVE_MODE 227 8428c645cfSHajimu UMEMOTO #define FTP_LPASSIVE_MODE 228 8528c645cfSHajimu UMEMOTO #define FTP_EPASSIVE_MODE 229 86346298f0SDag-Erling Smørgrav #define FTP_LOGGED_IN 230 87346298f0SDag-Erling Smørgrav #define FTP_FILE_ACTION_OK 250 88346298f0SDag-Erling Smørgrav #define FTP_NEED_PASSWORD 331 89346298f0SDag-Erling Smørgrav #define FTP_NEED_ACCOUNT 332 9032425dafSDag-Erling Smørgrav #define FTP_FILE_OK 350 91fc6e9e65SDag-Erling Smørgrav #define FTP_SYNTAX_ERROR 500 925cd33c40SDag-Erling Smørgrav #define FTP_PROTOCOL_ERROR 999 934ca1ab94SDag-Erling Smørgrav 94d8acd8dcSDag-Erling Smørgrav static struct url cached_host; 95fc6e9e65SDag-Erling Smørgrav static int cached_socket; 964ca1ab94SDag-Erling Smørgrav 97fc6e9e65SDag-Erling Smørgrav static char *last_reply; 98fc6e9e65SDag-Erling Smørgrav static size_t lr_size, lr_length; 99fc6e9e65SDag-Erling Smørgrav static int last_code; 100fc6e9e65SDag-Erling Smørgrav 101fc6e9e65SDag-Erling Smørgrav #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 1026efb30c8SDag-Erling Smørgrav && isdigit(foo[2]) \ 1036efb30c8SDag-Erling Smørgrav && (foo[3] == ' ' || foo[3] == '\0')) 104fc6e9e65SDag-Erling Smørgrav #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 105fc6e9e65SDag-Erling Smørgrav && isdigit(foo[2]) && foo[3] == '-') 1064ca1ab94SDag-Erling Smørgrav 10728c645cfSHajimu UMEMOTO /* translate IPv4 mapped IPv6 address to IPv4 address */ 10828c645cfSHajimu UMEMOTO static void 10928c645cfSHajimu UMEMOTO unmappedaddr(struct sockaddr_in6 *sin6) 11028c645cfSHajimu UMEMOTO { 11128c645cfSHajimu UMEMOTO struct sockaddr_in *sin4; 11228c645cfSHajimu UMEMOTO u_int32_t addr; 11328c645cfSHajimu UMEMOTO int port; 11428c645cfSHajimu UMEMOTO 11528c645cfSHajimu UMEMOTO if (sin6->sin6_family != AF_INET6 || 11628c645cfSHajimu UMEMOTO !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 11728c645cfSHajimu UMEMOTO return; 11828c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)sin6; 11928c645cfSHajimu UMEMOTO addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 12028c645cfSHajimu UMEMOTO port = sin6->sin6_port; 12128c645cfSHajimu UMEMOTO memset(sin4, 0, sizeof(struct sockaddr_in)); 12228c645cfSHajimu UMEMOTO sin4->sin_addr.s_addr = addr; 12328c645cfSHajimu UMEMOTO sin4->sin_port = port; 12428c645cfSHajimu UMEMOTO sin4->sin_family = AF_INET; 12528c645cfSHajimu UMEMOTO sin4->sin_len = sizeof(struct sockaddr_in); 12628c645cfSHajimu UMEMOTO } 12728c645cfSHajimu UMEMOTO 1284ca1ab94SDag-Erling Smørgrav /* 129fc6e9e65SDag-Erling Smørgrav * Get server response 1308e3986eaSDag-Erling Smørgrav */ 1318e3986eaSDag-Erling Smørgrav static int 132fc6e9e65SDag-Erling Smørgrav _ftp_chkerr(int cd) 1338e3986eaSDag-Erling Smørgrav { 134fc6e9e65SDag-Erling Smørgrav if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 135842a95ccSDag-Erling Smørgrav _fetch_syserr(); 1368e3986eaSDag-Erling Smørgrav return -1; 1378e3986eaSDag-Erling Smørgrav } 138eac7a1e0SDag-Erling Smørgrav if (isftpinfo(last_reply)) { 139eac7a1e0SDag-Erling Smørgrav while (!isftpreply(last_reply)) { 140eac7a1e0SDag-Erling Smørgrav if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 141eac7a1e0SDag-Erling Smørgrav _fetch_syserr(); 142eac7a1e0SDag-Erling Smørgrav return -1; 143eac7a1e0SDag-Erling Smørgrav } 144eac7a1e0SDag-Erling Smørgrav } 145eac7a1e0SDag-Erling Smørgrav } 146346298f0SDag-Erling Smørgrav 147fc6e9e65SDag-Erling Smørgrav while (lr_length && isspace(last_reply[lr_length-1])) 148fc6e9e65SDag-Erling Smørgrav lr_length--; 149fc6e9e65SDag-Erling Smørgrav last_reply[lr_length] = 0; 150fc6e9e65SDag-Erling Smørgrav 151fc6e9e65SDag-Erling Smørgrav if (!isftpreply(last_reply)) { 1525cd33c40SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 1538e3986eaSDag-Erling Smørgrav return -1; 1548e3986eaSDag-Erling Smørgrav } 1558e3986eaSDag-Erling Smørgrav 156fc6e9e65SDag-Erling Smørgrav last_code = (last_reply[0] - '0') * 100 157fc6e9e65SDag-Erling Smørgrav + (last_reply[1] - '0') * 10 158fc6e9e65SDag-Erling Smørgrav + (last_reply[2] - '0'); 159fc6e9e65SDag-Erling Smørgrav 160fc6e9e65SDag-Erling Smørgrav return last_code; 1618e3986eaSDag-Erling Smørgrav } 1628e3986eaSDag-Erling Smørgrav 1638e3986eaSDag-Erling Smørgrav /* 164346298f0SDag-Erling Smørgrav * Send a command and check reply 1654ca1ab94SDag-Erling Smørgrav */ 1664ca1ab94SDag-Erling Smørgrav static int 167fc6e9e65SDag-Erling Smørgrav _ftp_cmd(int cd, char *fmt, ...) 1684ca1ab94SDag-Erling Smørgrav { 169346298f0SDag-Erling Smørgrav va_list ap; 170e137bcebSDag-Erling Smørgrav size_t len; 171fc6e9e65SDag-Erling Smørgrav char *msg; 172fc6e9e65SDag-Erling Smørgrav int r; 1738e3986eaSDag-Erling Smørgrav 174346298f0SDag-Erling Smørgrav va_start(ap, fmt); 175e137bcebSDag-Erling Smørgrav len = vasprintf(&msg, fmt, ap); 176346298f0SDag-Erling Smørgrav va_end(ap); 177346298f0SDag-Erling Smørgrav 178fc6e9e65SDag-Erling Smørgrav if (msg == NULL) { 179fc6e9e65SDag-Erling Smørgrav errno = ENOMEM; 180fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 181fc6e9e65SDag-Erling Smørgrav return -1; 182fc6e9e65SDag-Erling Smørgrav } 183e137bcebSDag-Erling Smørgrav 184e137bcebSDag-Erling Smørgrav r = _fetch_putln(cd, msg, len); 185fc6e9e65SDag-Erling Smørgrav free(msg); 186e137bcebSDag-Erling Smørgrav 187fc6e9e65SDag-Erling Smørgrav if (r == -1) { 188fc6e9e65SDag-Erling Smørgrav _fetch_syserr(); 189fc6e9e65SDag-Erling Smørgrav return -1; 190fc6e9e65SDag-Erling Smørgrav } 191fc6e9e65SDag-Erling Smørgrav 192fc6e9e65SDag-Erling Smørgrav return _ftp_chkerr(cd); 1934ca1ab94SDag-Erling Smørgrav } 1944ca1ab94SDag-Erling Smørgrav 1954ca1ab94SDag-Erling Smørgrav /* 1961a5faa10SDag-Erling Smørgrav * Return a pointer to the filename part of a path 1971a5faa10SDag-Erling Smørgrav */ 1981a5faa10SDag-Erling Smørgrav static char * 1991a5faa10SDag-Erling Smørgrav _ftp_filename(char *file) 2001a5faa10SDag-Erling Smørgrav { 2011a5faa10SDag-Erling Smørgrav char *s; 2021a5faa10SDag-Erling Smørgrav 2031a5faa10SDag-Erling Smørgrav if ((s = strrchr(file, '/')) == NULL) 2041a5faa10SDag-Erling Smørgrav return file; 2051a5faa10SDag-Erling Smørgrav else 2061a5faa10SDag-Erling Smørgrav return s + 1; 2071a5faa10SDag-Erling Smørgrav } 2081a5faa10SDag-Erling Smørgrav 2091a5faa10SDag-Erling Smørgrav /* 2101a5faa10SDag-Erling Smørgrav * Change working directory to the directory that contains the 2111a5faa10SDag-Erling Smørgrav * specified file. 2121a5faa10SDag-Erling Smørgrav */ 2131a5faa10SDag-Erling Smørgrav static int 2141a5faa10SDag-Erling Smørgrav _ftp_cwd(int cd, char *file) 2151a5faa10SDag-Erling Smørgrav { 2161a5faa10SDag-Erling Smørgrav char *s; 2171a5faa10SDag-Erling Smørgrav int e; 2181a5faa10SDag-Erling Smørgrav 2195e3f46b5SDag-Erling Smørgrav if ((s = strrchr(file, '/')) == NULL || s == file) { 2201a5faa10SDag-Erling Smørgrav e = _ftp_cmd(cd, "CWD /"); 2211a5faa10SDag-Erling Smørgrav } else { 2221a5faa10SDag-Erling Smørgrav e = _ftp_cmd(cd, "CWD %.*s", s - file, file); 2231a5faa10SDag-Erling Smørgrav } 2241a5faa10SDag-Erling Smørgrav if (e != FTP_FILE_ACTION_OK) { 2251a5faa10SDag-Erling Smørgrav _ftp_seterr(e); 2261a5faa10SDag-Erling Smørgrav return -1; 2271a5faa10SDag-Erling Smørgrav } 2281a5faa10SDag-Erling Smørgrav return 0; 2291a5faa10SDag-Erling Smørgrav } 2301a5faa10SDag-Erling Smørgrav 2311a5faa10SDag-Erling Smørgrav /* 2321a5faa10SDag-Erling Smørgrav * Request and parse file stats 2331a5faa10SDag-Erling Smørgrav */ 2341a5faa10SDag-Erling Smørgrav static int 2351a5faa10SDag-Erling Smørgrav _ftp_stat(int cd, char *file, struct url_stat *us) 2361a5faa10SDag-Erling Smørgrav { 2371a5faa10SDag-Erling Smørgrav char *ln, *s; 2381a5faa10SDag-Erling Smørgrav struct tm tm; 2391a5faa10SDag-Erling Smørgrav time_t t; 2401a5faa10SDag-Erling Smørgrav int e; 2411a5faa10SDag-Erling Smørgrav 242269532d9SDag-Erling Smørgrav us->size = -1; 243269532d9SDag-Erling Smørgrav us->atime = us->mtime = 0; 244269532d9SDag-Erling Smørgrav 2451a5faa10SDag-Erling Smørgrav if ((s = strrchr(file, '/')) == NULL) 2461a5faa10SDag-Erling Smørgrav s = file; 2471a5faa10SDag-Erling Smørgrav else 2481a5faa10SDag-Erling Smørgrav ++s; 2491a5faa10SDag-Erling Smørgrav 2501a5faa10SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "SIZE %s", s)) != FTP_FILE_STATUS) { 2511a5faa10SDag-Erling Smørgrav _ftp_seterr(e); 2521a5faa10SDag-Erling Smørgrav return -1; 2531a5faa10SDag-Erling Smørgrav } 2541a5faa10SDag-Erling Smørgrav for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 2551a5faa10SDag-Erling Smørgrav /* nothing */ ; 2561a5faa10SDag-Erling Smørgrav for (us->size = 0; *ln && isdigit(*ln); ln++) 2571a5faa10SDag-Erling Smørgrav us->size = us->size * 10 + *ln - '0'; 2581a5faa10SDag-Erling Smørgrav if (*ln && !isspace(*ln)) { 2591a5faa10SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 2601a5faa10SDag-Erling Smørgrav return -1; 2611a5faa10SDag-Erling Smørgrav } 2621a5faa10SDag-Erling Smørgrav DEBUG(fprintf(stderr, "size: [\033[1m%lld\033[m]\n", us->size)); 2631a5faa10SDag-Erling Smørgrav 2641a5faa10SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) { 2651a5faa10SDag-Erling Smørgrav _ftp_seterr(e); 2661a5faa10SDag-Erling Smørgrav return -1; 2671a5faa10SDag-Erling Smørgrav } 2681a5faa10SDag-Erling Smørgrav for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 2691a5faa10SDag-Erling Smørgrav /* nothing */ ; 2701a5faa10SDag-Erling Smørgrav switch (strspn(ln, "0123456789")) { 2711a5faa10SDag-Erling Smørgrav case 14: 2721a5faa10SDag-Erling Smørgrav break; 2731a5faa10SDag-Erling Smørgrav case 15: 2741a5faa10SDag-Erling Smørgrav ln++; 2751a5faa10SDag-Erling Smørgrav ln[0] = '2'; 2761a5faa10SDag-Erling Smørgrav ln[1] = '0'; 2771a5faa10SDag-Erling Smørgrav break; 2781a5faa10SDag-Erling Smørgrav default: 2791a5faa10SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 2801a5faa10SDag-Erling Smørgrav return -1; 2811a5faa10SDag-Erling Smørgrav } 2821a5faa10SDag-Erling Smørgrav if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 2831a5faa10SDag-Erling Smørgrav &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 2841a5faa10SDag-Erling Smørgrav &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 2851a5faa10SDag-Erling Smørgrav _ftp_seterr(FTP_PROTOCOL_ERROR); 2861a5faa10SDag-Erling Smørgrav return -1; 2871a5faa10SDag-Erling Smørgrav } 2881a5faa10SDag-Erling Smørgrav tm.tm_mon--; 2891a5faa10SDag-Erling Smørgrav tm.tm_year -= 1900; 2901a5faa10SDag-Erling Smørgrav tm.tm_isdst = -1; 2911a5faa10SDag-Erling Smørgrav t = timegm(&tm); 2921a5faa10SDag-Erling Smørgrav if (t == (time_t)-1) 2931a5faa10SDag-Erling Smørgrav t = time(NULL); 2941a5faa10SDag-Erling Smørgrav us->mtime = t; 2951a5faa10SDag-Erling Smørgrav us->atime = t; 2961a5faa10SDag-Erling Smørgrav DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d " 2971a5faa10SDag-Erling Smørgrav "%02d:%02d:%02d\033[m]\n", 2981a5faa10SDag-Erling Smørgrav tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 2991a5faa10SDag-Erling Smørgrav tm.tm_hour, tm.tm_min, tm.tm_sec)); 3001a5faa10SDag-Erling Smørgrav return 0; 3011a5faa10SDag-Erling Smørgrav } 3021a5faa10SDag-Erling Smørgrav 3031a5faa10SDag-Erling Smørgrav /* 304f62e5228SDag-Erling Smørgrav * Transfer file 3054ca1ab94SDag-Erling Smørgrav */ 3064ca1ab94SDag-Erling Smørgrav static FILE * 30732425dafSDag-Erling Smørgrav _ftp_transfer(int cd, char *oper, char *file, 30832425dafSDag-Erling Smørgrav char *mode, off_t offset, char *flags) 3094ca1ab94SDag-Erling Smørgrav { 31028c645cfSHajimu UMEMOTO struct sockaddr_storage sin; 31128c645cfSHajimu UMEMOTO struct sockaddr_in6 *sin6; 31228c645cfSHajimu UMEMOTO struct sockaddr_in *sin4; 313f5f109a0SDag-Erling Smørgrav int pasv, high, verbose; 314f5f109a0SDag-Erling Smørgrav int e, sd = -1; 315f5f109a0SDag-Erling Smørgrav socklen_t l; 316346298f0SDag-Erling Smørgrav char *s; 317346298f0SDag-Erling Smørgrav FILE *df; 3188e3986eaSDag-Erling Smørgrav 319f5f109a0SDag-Erling Smørgrav /* check flags */ 320f5f109a0SDag-Erling Smørgrav pasv = (flags && strchr(flags, 'p')); 321f5f109a0SDag-Erling Smørgrav high = (flags && strchr(flags, 'h')); 322f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 323f5f109a0SDag-Erling Smørgrav 324d02e84a6SDag-Erling Smørgrav /* passive mode */ 325d02e84a6SDag-Erling Smørgrav if (!pasv && (s = getenv("FTP_PASSIVE_MODE")) != NULL) 326d02e84a6SDag-Erling Smørgrav pasv = (strncasecmp(s, "no", 2) != 0); 327d02e84a6SDag-Erling Smørgrav 32828c645cfSHajimu UMEMOTO /* find our own address, bind, and listen */ 32928c645cfSHajimu UMEMOTO l = sizeof sin; 33028c645cfSHajimu UMEMOTO if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1) 33128c645cfSHajimu UMEMOTO goto sysouch; 33228c645cfSHajimu UMEMOTO if (sin.ss_family == AF_INET6) 33328c645cfSHajimu UMEMOTO unmappedaddr((struct sockaddr_in6 *)&sin); 33428c645cfSHajimu UMEMOTO 335346298f0SDag-Erling Smørgrav /* open data socket */ 33628c645cfSHajimu UMEMOTO if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 337842a95ccSDag-Erling Smørgrav _fetch_syserr(); 338346298f0SDag-Erling Smørgrav return NULL; 339346298f0SDag-Erling Smørgrav } 340346298f0SDag-Erling Smørgrav 341346298f0SDag-Erling Smørgrav if (pasv) { 34228c645cfSHajimu UMEMOTO u_char addr[64]; 343346298f0SDag-Erling Smørgrav char *ln, *p; 344346298f0SDag-Erling Smørgrav int i; 34528c645cfSHajimu UMEMOTO int port; 346346298f0SDag-Erling Smørgrav 347346298f0SDag-Erling Smørgrav /* send PASV command */ 348f5f109a0SDag-Erling Smørgrav if (verbose) 349f5f109a0SDag-Erling Smørgrav _fetch_info("setting passive mode"); 35028c645cfSHajimu UMEMOTO switch (sin.ss_family) { 35128c645cfSHajimu UMEMOTO case AF_INET: 352fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE) 353346298f0SDag-Erling Smørgrav goto ouch; 35428c645cfSHajimu UMEMOTO break; 35528c645cfSHajimu UMEMOTO case AF_INET6: 35628c645cfSHajimu UMEMOTO if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) { 35728c645cfSHajimu UMEMOTO if (e == -1) 35828c645cfSHajimu UMEMOTO goto ouch; 35928c645cfSHajimu UMEMOTO if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE) 36028c645cfSHajimu UMEMOTO goto ouch; 36128c645cfSHajimu UMEMOTO } 36228c645cfSHajimu UMEMOTO break; 36328c645cfSHajimu UMEMOTO default: 3645cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 36528c645cfSHajimu UMEMOTO goto ouch; 36628c645cfSHajimu UMEMOTO } 367346298f0SDag-Erling Smørgrav 368f5f109a0SDag-Erling Smørgrav /* 369f5f109a0SDag-Erling Smørgrav * Find address and port number. The reply to the PASV command 370f5f109a0SDag-Erling Smørgrav * is IMHO the one and only weak point in the FTP protocol. 371f5f109a0SDag-Erling Smørgrav */ 372fc6e9e65SDag-Erling Smørgrav ln = last_reply; 373fa5dce6cSHajimu UMEMOTO switch (e) { 374fa5dce6cSHajimu UMEMOTO case FTP_PASSIVE_MODE: 375fa5dce6cSHajimu UMEMOTO case FTP_LPASSIVE_MODE: 37651e3d46eSDag-Erling Smørgrav for (p = ln + 3; *p && !isdigit(*p); p++) 377346298f0SDag-Erling Smørgrav /* nothing */ ; 37828c645cfSHajimu UMEMOTO if (!*p) { 3795cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 3806efb30c8SDag-Erling Smørgrav goto ouch; 381346298f0SDag-Erling Smørgrav } 38228c645cfSHajimu UMEMOTO l = (e == FTP_PASSIVE_MODE ? 6 : 21); 38328c645cfSHajimu UMEMOTO for (i = 0; *p && i < l; i++, p++) 38428c645cfSHajimu UMEMOTO addr[i] = strtol(p, &p, 10); 38528c645cfSHajimu UMEMOTO if (i < l) { 3865cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 38728c645cfSHajimu UMEMOTO goto ouch; 38828c645cfSHajimu UMEMOTO } 38928c645cfSHajimu UMEMOTO break; 39028c645cfSHajimu UMEMOTO case FTP_EPASSIVE_MODE: 391fa5dce6cSHajimu UMEMOTO for (p = ln + 3; *p && *p != '('; p++) 392fa5dce6cSHajimu UMEMOTO /* nothing */ ; 393fa5dce6cSHajimu UMEMOTO if (!*p) { 3945cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 395fa5dce6cSHajimu UMEMOTO goto ouch; 396fa5dce6cSHajimu UMEMOTO } 397fa5dce6cSHajimu UMEMOTO ++p; 39828c645cfSHajimu UMEMOTO if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 39928c645cfSHajimu UMEMOTO &port, &addr[3]) != 5 || 40028c645cfSHajimu UMEMOTO addr[0] != addr[1] || 40128c645cfSHajimu UMEMOTO addr[0] != addr[2] || addr[0] != addr[3]) { 4025cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; 40328c645cfSHajimu UMEMOTO goto ouch; 40428c645cfSHajimu UMEMOTO } 40528c645cfSHajimu UMEMOTO break; 40628c645cfSHajimu UMEMOTO } 407346298f0SDag-Erling Smørgrav 40832425dafSDag-Erling Smørgrav /* seek to required offset */ 40932425dafSDag-Erling Smørgrav if (offset) 41032425dafSDag-Erling Smørgrav if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 41132425dafSDag-Erling Smørgrav goto sysouch; 41232425dafSDag-Erling Smørgrav 413346298f0SDag-Erling Smørgrav /* construct sockaddr for data socket */ 41432425dafSDag-Erling Smørgrav l = sizeof sin; 415fc6e9e65SDag-Erling Smørgrav if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1) 416346298f0SDag-Erling Smørgrav goto sysouch; 41728c645cfSHajimu UMEMOTO if (sin.ss_family == AF_INET6) 41828c645cfSHajimu UMEMOTO unmappedaddr((struct sockaddr_in6 *)&sin); 41928c645cfSHajimu UMEMOTO switch (sin.ss_family) { 42028c645cfSHajimu UMEMOTO case AF_INET6: 42128c645cfSHajimu UMEMOTO sin6 = (struct sockaddr_in6 *)&sin; 42228c645cfSHajimu UMEMOTO if (e == FTP_EPASSIVE_MODE) 42328c645cfSHajimu UMEMOTO sin6->sin6_port = htons(port); 42428c645cfSHajimu UMEMOTO else { 42528c645cfSHajimu UMEMOTO bcopy(addr + 2, (char *)&sin6->sin6_addr, 16); 42628c645cfSHajimu UMEMOTO bcopy(addr + 19, (char *)&sin6->sin6_port, 2); 42728c645cfSHajimu UMEMOTO } 42828c645cfSHajimu UMEMOTO break; 42928c645cfSHajimu UMEMOTO case AF_INET: 43028c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)&sin; 43128c645cfSHajimu UMEMOTO if (e == FTP_EPASSIVE_MODE) 43228c645cfSHajimu UMEMOTO sin4->sin_port = htons(port); 43328c645cfSHajimu UMEMOTO else { 43428c645cfSHajimu UMEMOTO bcopy(addr, (char *)&sin4->sin_addr, 4); 43528c645cfSHajimu UMEMOTO bcopy(addr + 4, (char *)&sin4->sin_port, 2); 43628c645cfSHajimu UMEMOTO } 43728c645cfSHajimu UMEMOTO break; 43828c645cfSHajimu UMEMOTO default: 4395cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 44028c645cfSHajimu UMEMOTO break; 44128c645cfSHajimu UMEMOTO } 442346298f0SDag-Erling Smørgrav 443346298f0SDag-Erling Smørgrav /* connect to data port */ 444f5f109a0SDag-Erling Smørgrav if (verbose) 445f5f109a0SDag-Erling Smørgrav _fetch_info("opening data connection"); 44628c645cfSHajimu UMEMOTO if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1) 447346298f0SDag-Erling Smørgrav goto sysouch; 448346298f0SDag-Erling Smørgrav 449346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 450f5f109a0SDag-Erling Smørgrav if (verbose) 451def5f54cSDag-Erling Smørgrav _fetch_info("initiating transfer"); 4521a5faa10SDag-Erling Smørgrav e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file)); 4535aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 454346298f0SDag-Erling Smørgrav goto ouch; 455346298f0SDag-Erling Smørgrav 456346298f0SDag-Erling Smørgrav } else { 457346298f0SDag-Erling Smørgrav u_int32_t a; 458346298f0SDag-Erling Smørgrav u_short p; 459f5f109a0SDag-Erling Smørgrav int arg, d; 46028c645cfSHajimu UMEMOTO char *ap; 46128c645cfSHajimu UMEMOTO char hname[INET6_ADDRSTRLEN]; 462346298f0SDag-Erling Smørgrav 46328c645cfSHajimu UMEMOTO switch (sin.ss_family) { 46428c645cfSHajimu UMEMOTO case AF_INET6: 46528c645cfSHajimu UMEMOTO ((struct sockaddr_in6 *)&sin)->sin6_port = 0; 46628c645cfSHajimu UMEMOTO #ifdef IPV6_PORTRANGE 46728c645cfSHajimu UMEMOTO arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; 46828c645cfSHajimu UMEMOTO if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 46928c645cfSHajimu UMEMOTO (char *)&arg, sizeof(arg)) == -1) 470346298f0SDag-Erling Smørgrav goto sysouch; 47128c645cfSHajimu UMEMOTO #endif 47228c645cfSHajimu UMEMOTO break; 47328c645cfSHajimu UMEMOTO case AF_INET: 47428c645cfSHajimu UMEMOTO ((struct sockaddr_in *)&sin)->sin_port = 0; 475f5f109a0SDag-Erling Smørgrav arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; 476f5f109a0SDag-Erling Smørgrav if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 47732425dafSDag-Erling Smørgrav (char *)&arg, sizeof arg) == -1) 478f5f109a0SDag-Erling Smørgrav goto sysouch; 47928c645cfSHajimu UMEMOTO break; 48028c645cfSHajimu UMEMOTO } 481f5f109a0SDag-Erling Smørgrav if (verbose) 482f5f109a0SDag-Erling Smørgrav _fetch_info("binding data socket"); 48328c645cfSHajimu UMEMOTO if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1) 484346298f0SDag-Erling Smørgrav goto sysouch; 485ecc91352SDag-Erling Smørgrav if (listen(sd, 1) == -1) 486346298f0SDag-Erling Smørgrav goto sysouch; 487346298f0SDag-Erling Smørgrav 488346298f0SDag-Erling Smørgrav /* find what port we're on and tell the server */ 489ecc91352SDag-Erling Smørgrav if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) 490346298f0SDag-Erling Smørgrav goto sysouch; 49128c645cfSHajimu UMEMOTO switch (sin.ss_family) { 49228c645cfSHajimu UMEMOTO case AF_INET: 49328c645cfSHajimu UMEMOTO sin4 = (struct sockaddr_in *)&sin; 49428c645cfSHajimu UMEMOTO a = ntohl(sin4->sin_addr.s_addr); 49528c645cfSHajimu UMEMOTO p = ntohs(sin4->sin_port); 496fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d", 4975aea254fSDag-Erling Smørgrav (a >> 24) & 0xff, (a >> 16) & 0xff, 4985aea254fSDag-Erling Smørgrav (a >> 8) & 0xff, a & 0xff, 4995aea254fSDag-Erling Smørgrav (p >> 8) & 0xff, p & 0xff); 50028c645cfSHajimu UMEMOTO break; 50128c645cfSHajimu UMEMOTO case AF_INET6: 50228c645cfSHajimu UMEMOTO #define UC(b) (((int)b)&0xff) 50328c645cfSHajimu UMEMOTO e = -1; 50428c645cfSHajimu UMEMOTO sin6 = (struct sockaddr_in6 *)&sin; 50528c645cfSHajimu UMEMOTO if (getnameinfo((struct sockaddr *)&sin, sin.ss_len, 50628c645cfSHajimu UMEMOTO hname, sizeof(hname), 50728c645cfSHajimu UMEMOTO NULL, 0, NI_NUMERICHOST) == 0) { 50828c645cfSHajimu UMEMOTO e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname, 50928c645cfSHajimu UMEMOTO htons(sin6->sin6_port)); 51028c645cfSHajimu UMEMOTO if (e == -1) 51128c645cfSHajimu UMEMOTO goto ouch; 51228c645cfSHajimu UMEMOTO } 51328c645cfSHajimu UMEMOTO if (e != FTP_OK) { 51428c645cfSHajimu UMEMOTO ap = (char *)&sin6->sin6_addr; 51528c645cfSHajimu UMEMOTO e = _ftp_cmd(cd, 51628c645cfSHajimu UMEMOTO "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 51728c645cfSHajimu UMEMOTO 6, 16, 51828c645cfSHajimu UMEMOTO UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 51928c645cfSHajimu UMEMOTO UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 52028c645cfSHajimu UMEMOTO UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 52128c645cfSHajimu UMEMOTO UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 52228c645cfSHajimu UMEMOTO 2, 52328c645cfSHajimu UMEMOTO (ntohs(sin6->sin6_port) >> 8) & 0xff, 52428c645cfSHajimu UMEMOTO ntohs(sin6->sin6_port) & 0xff); 52528c645cfSHajimu UMEMOTO } 52628c645cfSHajimu UMEMOTO break; 52728c645cfSHajimu UMEMOTO default: 5285cd33c40SDag-Erling Smørgrav e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 52928c645cfSHajimu UMEMOTO goto ouch; 53028c645cfSHajimu UMEMOTO } 5315aea254fSDag-Erling Smørgrav if (e != FTP_OK) 532346298f0SDag-Erling Smørgrav goto ouch; 533346298f0SDag-Erling Smørgrav 534893980adSDag-Erling Smørgrav /* seek to required offset */ 535893980adSDag-Erling Smørgrav if (offset) 536893980adSDag-Erling Smørgrav if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 537893980adSDag-Erling Smørgrav goto sysouch; 538893980adSDag-Erling Smørgrav 539346298f0SDag-Erling Smørgrav /* make the server initiate the transfer */ 540f5f109a0SDag-Erling Smørgrav if (verbose) 541f5f109a0SDag-Erling Smørgrav _fetch_info("initiating transfer"); 5421a5faa10SDag-Erling Smørgrav e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file)); 5435aea254fSDag-Erling Smørgrav if (e != FTP_OPEN_DATA_CONNECTION) 544346298f0SDag-Erling Smørgrav goto ouch; 545346298f0SDag-Erling Smørgrav 546346298f0SDag-Erling Smørgrav /* accept the incoming connection and go to town */ 547ecc91352SDag-Erling Smørgrav if ((d = accept(sd, NULL, NULL)) == -1) 548346298f0SDag-Erling Smørgrav goto sysouch; 549346298f0SDag-Erling Smørgrav close(sd); 550346298f0SDag-Erling Smørgrav sd = d; 551346298f0SDag-Erling Smørgrav } 552346298f0SDag-Erling Smørgrav 553f62e5228SDag-Erling Smørgrav if ((df = fdopen(sd, mode)) == NULL) 554346298f0SDag-Erling Smørgrav goto sysouch; 555346298f0SDag-Erling Smørgrav return df; 556346298f0SDag-Erling Smørgrav 557346298f0SDag-Erling Smørgrav sysouch: 558842a95ccSDag-Erling Smørgrav _fetch_syserr(); 55928c645cfSHajimu UMEMOTO if (sd >= 0) 5605aea254fSDag-Erling Smørgrav close(sd); 5615aea254fSDag-Erling Smørgrav return NULL; 5625aea254fSDag-Erling Smørgrav 563346298f0SDag-Erling Smørgrav ouch: 564fc6e9e65SDag-Erling Smørgrav if (e != -1) 5655aea254fSDag-Erling Smørgrav _ftp_seterr(e); 56628c645cfSHajimu UMEMOTO if (sd >= 0) 567346298f0SDag-Erling Smørgrav close(sd); 5684ca1ab94SDag-Erling Smørgrav return NULL; 5694ca1ab94SDag-Erling Smørgrav } 5704ca1ab94SDag-Erling Smørgrav 5718e3986eaSDag-Erling Smørgrav /* 57210851dc4SDag-Erling Smørgrav * Return default port 57310851dc4SDag-Erling Smørgrav */ 57410851dc4SDag-Erling Smørgrav static int 57510851dc4SDag-Erling Smørgrav _ftp_default_port(void) 57610851dc4SDag-Erling Smørgrav { 57710851dc4SDag-Erling Smørgrav struct servent *se; 57810851dc4SDag-Erling Smørgrav 57910851dc4SDag-Erling Smørgrav if ((se = getservbyname("ftp", "tcp")) != NULL) 58010851dc4SDag-Erling Smørgrav return ntohs(se->s_port); 58110851dc4SDag-Erling Smørgrav return FTP_DEFAULT_PORT; 58210851dc4SDag-Erling Smørgrav } 58310851dc4SDag-Erling Smørgrav 58410851dc4SDag-Erling Smørgrav /* 5858e3986eaSDag-Erling Smørgrav * Log on to FTP server 5864ca1ab94SDag-Erling Smørgrav */ 587fc6e9e65SDag-Erling Smørgrav static int 588f5f109a0SDag-Erling Smørgrav _ftp_connect(char *host, int port, char *user, char *pwd, char *flags) 5898e3986eaSDag-Erling Smørgrav { 59032425dafSDag-Erling Smørgrav int cd, e, pp = 0, direct, verbose; 59128c645cfSHajimu UMEMOTO #ifdef INET6 59228c645cfSHajimu UMEMOTO int af = AF_UNSPEC; 59328c645cfSHajimu UMEMOTO #else 59428c645cfSHajimu UMEMOTO int af = AF_INET; 59528c645cfSHajimu UMEMOTO #endif 596f62e5228SDag-Erling Smørgrav char *p, *q; 5974ab587f4SHajimu UMEMOTO const char *logname; 5984ab587f4SHajimu UMEMOTO char localhost[MAXHOSTNAMELEN]; 5994ab587f4SHajimu UMEMOTO char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 6008e3986eaSDag-Erling Smørgrav 601f5f109a0SDag-Erling Smørgrav direct = (flags && strchr(flags, 'd')); 602f5f109a0SDag-Erling Smørgrav verbose = (flags && strchr(flags, 'v')); 60328c645cfSHajimu UMEMOTO if ((flags && strchr(flags, '4'))) 60428c645cfSHajimu UMEMOTO af = AF_INET; 60528c645cfSHajimu UMEMOTO else if ((flags && strchr(flags, '6'))) 60628c645cfSHajimu UMEMOTO af = AF_INET6; 607f5f109a0SDag-Erling Smørgrav 608f62e5228SDag-Erling Smørgrav /* check for proxy */ 609dfe7c55fSDag-Erling Smørgrav if (!direct && (p = getenv("FTP_PROXY")) != NULL && *p) { 61028c645cfSHajimu UMEMOTO char c = 0; 61128c645cfSHajimu UMEMOTO 61228c645cfSHajimu UMEMOTO #ifdef INET6 61328c645cfSHajimu UMEMOTO if (*p != '[' || (q = strchr(p + 1, ']')) == NULL || 61428c645cfSHajimu UMEMOTO (*++q != '\0' && *q != ':')) 61528c645cfSHajimu UMEMOTO #endif 61628c645cfSHajimu UMEMOTO q = strchr(p, ':'); 61728c645cfSHajimu UMEMOTO if (q != NULL && *q == ':') { 61832425dafSDag-Erling Smørgrav if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) { 61932425dafSDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 62032425dafSDag-Erling Smørgrav } 621f62e5228SDag-Erling Smørgrav pp = atoi(q+1); 62232425dafSDag-Erling Smørgrav if (pp < 1 || pp > 65535) { 62332425dafSDag-Erling Smørgrav /* XXX we should emit some kind of warning */ 62432425dafSDag-Erling Smørgrav } 62532425dafSDag-Erling Smørgrav } 62610851dc4SDag-Erling Smørgrav if (!pp) 62710851dc4SDag-Erling Smørgrav pp = _ftp_default_port(); 62828c645cfSHajimu UMEMOTO if (q) { 62928c645cfSHajimu UMEMOTO #ifdef INET6 63028c645cfSHajimu UMEMOTO if (q > p && *p == '[' && *(q - 1) == ']') { 63128c645cfSHajimu UMEMOTO p++; 63228c645cfSHajimu UMEMOTO q--; 63328c645cfSHajimu UMEMOTO } 63428c645cfSHajimu UMEMOTO #endif 63528c645cfSHajimu UMEMOTO c = *q; 636f62e5228SDag-Erling Smørgrav *q = 0; 63728c645cfSHajimu UMEMOTO } 63828c645cfSHajimu UMEMOTO cd = _fetch_connect(p, pp, af, verbose); 639f62e5228SDag-Erling Smørgrav if (q) 64028c645cfSHajimu UMEMOTO *q = c; 641f62e5228SDag-Erling Smørgrav } else { 642f62e5228SDag-Erling Smørgrav /* no proxy, go straight to target */ 64328c645cfSHajimu UMEMOTO cd = _fetch_connect(host, port, af, verbose); 644f5f109a0SDag-Erling Smørgrav p = NULL; 645f62e5228SDag-Erling Smørgrav } 646f62e5228SDag-Erling Smørgrav 647f62e5228SDag-Erling Smørgrav /* check connection */ 648fc6e9e65SDag-Erling Smørgrav if (cd == -1) { 649842a95ccSDag-Erling Smørgrav _fetch_syserr(); 6508e3986eaSDag-Erling Smørgrav return NULL; 6518e3986eaSDag-Erling Smørgrav } 652f62e5228SDag-Erling Smørgrav 6538e3986eaSDag-Erling Smørgrav /* expect welcome message */ 654fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 6558e3986eaSDag-Erling Smørgrav goto fouch; 6568e3986eaSDag-Erling Smørgrav 6578e3986eaSDag-Erling Smørgrav /* send user name and password */ 658f62e5228SDag-Erling Smørgrav if (!user || !*user) 659f62e5228SDag-Erling Smørgrav user = FTP_ANONYMOUS_USER; 6602e88106bSDag-Erling Smørgrav if (p && port == FTP_DEFAULT_PORT) 6612e88106bSDag-Erling Smørgrav e = _ftp_cmd(cd, "USER %s@%s", user, host); 6622e88106bSDag-Erling Smørgrav else if (p) 6632e88106bSDag-Erling Smørgrav e = _ftp_cmd(cd, "USER %s@%s@%d", user, host, port); 6642e88106bSDag-Erling Smørgrav else 6652e88106bSDag-Erling Smørgrav e = _ftp_cmd(cd, "USER %s", user); 666f62e5228SDag-Erling Smørgrav 667f62e5228SDag-Erling Smørgrav /* did the server request a password? */ 668f62e5228SDag-Erling Smørgrav if (e == FTP_NEED_PASSWORD) { 669f62e5228SDag-Erling Smørgrav if (!pwd || !*pwd) 6704ab587f4SHajimu UMEMOTO pwd = getenv("FTP_PASSWORD"); 6714ab587f4SHajimu UMEMOTO if (!pwd || !*pwd) { 6724ab587f4SHajimu UMEMOTO if ((logname = getlogin()) == 0) 6734ab587f4SHajimu UMEMOTO logname = FTP_ANONYMOUS_PASSWORD; 6744ab587f4SHajimu UMEMOTO gethostname(localhost, sizeof localhost); 6754ab587f4SHajimu UMEMOTO snprintf(pbuf, sizeof pbuf, "%s@%s", logname, localhost); 6764ab587f4SHajimu UMEMOTO pwd = pbuf; 6774ab587f4SHajimu UMEMOTO } 678fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cd, "PASS %s", pwd); 679f62e5228SDag-Erling Smørgrav } 680f62e5228SDag-Erling Smørgrav 681f62e5228SDag-Erling Smørgrav /* did the server request an account? */ 6825aea254fSDag-Erling Smørgrav if (e == FTP_NEED_ACCOUNT) 6833b7a6740SDag-Erling Smørgrav goto fouch; 684f62e5228SDag-Erling Smørgrav 685f62e5228SDag-Erling Smørgrav /* we should be done by now */ 6865aea254fSDag-Erling Smørgrav if (e != FTP_LOGGED_IN) 6878e3986eaSDag-Erling Smørgrav goto fouch; 6888e3986eaSDag-Erling Smørgrav 6898e3986eaSDag-Erling Smørgrav /* might as well select mode and type at once */ 6908e3986eaSDag-Erling Smørgrav #ifdef FTP_FORCE_STREAM_MODE 691fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 6925aea254fSDag-Erling Smørgrav goto fouch; 6938e3986eaSDag-Erling Smørgrav #endif 694fc6e9e65SDag-Erling Smørgrav if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 6955aea254fSDag-Erling Smørgrav goto fouch; 6968e3986eaSDag-Erling Smørgrav 6978e3986eaSDag-Erling Smørgrav /* done */ 698fc6e9e65SDag-Erling Smørgrav return cd; 6998e3986eaSDag-Erling Smørgrav 7008e3986eaSDag-Erling Smørgrav fouch: 701fc6e9e65SDag-Erling Smørgrav if (e != -1) 7025aea254fSDag-Erling Smørgrav _ftp_seterr(e); 703fc6e9e65SDag-Erling Smørgrav close(cd); 7048e3986eaSDag-Erling Smørgrav return NULL; 7058e3986eaSDag-Erling Smørgrav } 7068e3986eaSDag-Erling Smørgrav 7078e3986eaSDag-Erling Smørgrav /* 7088e3986eaSDag-Erling Smørgrav * Disconnect from server 7098e3986eaSDag-Erling Smørgrav */ 7108e3986eaSDag-Erling Smørgrav static void 711fc6e9e65SDag-Erling Smørgrav _ftp_disconnect(int cd) 7128e3986eaSDag-Erling Smørgrav { 713fc6e9e65SDag-Erling Smørgrav (void)_ftp_cmd(cd, "QUIT"); 714fc6e9e65SDag-Erling Smørgrav close(cd); 7158e3986eaSDag-Erling Smørgrav } 7168e3986eaSDag-Erling Smørgrav 7178e3986eaSDag-Erling Smørgrav /* 7188e3986eaSDag-Erling Smørgrav * Check if we're already connected 7198e3986eaSDag-Erling Smørgrav */ 7208e3986eaSDag-Erling Smørgrav static int 721d8acd8dcSDag-Erling Smørgrav _ftp_isconnected(struct url *url) 7228e3986eaSDag-Erling Smørgrav { 7238e3986eaSDag-Erling Smørgrav return (cached_socket 7248e3986eaSDag-Erling Smørgrav && (strcmp(url->host, cached_host.host) == 0) 7258e3986eaSDag-Erling Smørgrav && (strcmp(url->user, cached_host.user) == 0) 7268e3986eaSDag-Erling Smørgrav && (strcmp(url->pwd, cached_host.pwd) == 0) 7278e3986eaSDag-Erling Smørgrav && (url->port == cached_host.port)); 7288e3986eaSDag-Erling Smørgrav } 7298e3986eaSDag-Erling Smørgrav 730f62e5228SDag-Erling Smørgrav /* 7315aea254fSDag-Erling Smørgrav * Check the cache, reconnect if no luck 732f62e5228SDag-Erling Smørgrav */ 733fc6e9e65SDag-Erling Smørgrav static int 7345aea254fSDag-Erling Smørgrav _ftp_cached_connect(struct url *url, char *flags) 7354ca1ab94SDag-Erling Smørgrav { 736fc6e9e65SDag-Erling Smørgrav int e, cd; 7375aea254fSDag-Erling Smørgrav 738fc6e9e65SDag-Erling Smørgrav cd = -1; 7398e3986eaSDag-Erling Smørgrav 7408e3986eaSDag-Erling Smørgrav /* set default port */ 74110851dc4SDag-Erling Smørgrav if (!url->port) 74210851dc4SDag-Erling Smørgrav url->port = _ftp_default_port(); 7438e3986eaSDag-Erling Smørgrav 7443b7a6740SDag-Erling Smørgrav /* try to use previously cached connection */ 745fc6e9e65SDag-Erling Smørgrav if (_ftp_isconnected(url)) { 746fc6e9e65SDag-Erling Smørgrav e = _ftp_cmd(cached_socket, "NOOP"); 747fc6e9e65SDag-Erling Smørgrav if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 748fc6e9e65SDag-Erling Smørgrav cd = cached_socket; 749fc6e9e65SDag-Erling Smørgrav } 7504ca1ab94SDag-Erling Smørgrav 7518e3986eaSDag-Erling Smørgrav /* connect to server */ 752fc6e9e65SDag-Erling Smørgrav if (cd == -1) { 753fc6e9e65SDag-Erling Smørgrav cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags); 754fc6e9e65SDag-Erling Smørgrav if (cd == -1) 755fc6e9e65SDag-Erling Smørgrav return -1; 7568e3986eaSDag-Erling Smørgrav if (cached_socket) 7578e3986eaSDag-Erling Smørgrav _ftp_disconnect(cached_socket); 758fc6e9e65SDag-Erling Smørgrav cached_socket = cd; 75932425dafSDag-Erling Smørgrav memcpy(&cached_host, url, sizeof *url); 7608e3986eaSDag-Erling Smørgrav } 7618e3986eaSDag-Erling Smørgrav 762fc6e9e65SDag-Erling Smørgrav return cd; 7638e3986eaSDag-Erling Smørgrav } 7648e3986eaSDag-Erling Smørgrav 7658e3986eaSDag-Erling Smørgrav /* 766dfe7c55fSDag-Erling Smørgrav * Check to see if we should use an HTTP proxy instead 767dfe7c55fSDag-Erling Smørgrav */ 768dfe7c55fSDag-Erling Smørgrav static int 769dfe7c55fSDag-Erling Smørgrav _ftp_use_http_proxy(void) 770dfe7c55fSDag-Erling Smørgrav { 771dfe7c55fSDag-Erling Smørgrav char *p; 772dfe7c55fSDag-Erling Smørgrav 773dfe7c55fSDag-Erling Smørgrav return ((p = getenv("HTTP_PROXY")) && *p && !getenv("FTP_PROXY")); 774dfe7c55fSDag-Erling Smørgrav } 775dfe7c55fSDag-Erling Smørgrav 776dfe7c55fSDag-Erling Smørgrav /* 7771a5faa10SDag-Erling Smørgrav * Get and stat file 7788e3986eaSDag-Erling Smørgrav */ 7794ca1ab94SDag-Erling Smørgrav FILE * 7801a5faa10SDag-Erling Smørgrav fetchXGetFTP(struct url *url, struct url_stat *us, char *flags) 781f62e5228SDag-Erling Smørgrav { 782fc6e9e65SDag-Erling Smørgrav int cd; 7835aea254fSDag-Erling Smørgrav 784dfe7c55fSDag-Erling Smørgrav if (_ftp_use_http_proxy()) 785dfe7c55fSDag-Erling Smørgrav return fetchXGetHTTP(url, us, flags); 786dfe7c55fSDag-Erling Smørgrav 7875aea254fSDag-Erling Smørgrav /* connect to server */ 788fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 7895aea254fSDag-Erling Smørgrav return NULL; 7905aea254fSDag-Erling Smørgrav 7911a5faa10SDag-Erling Smørgrav /* change directory */ 7921a5faa10SDag-Erling Smørgrav if (_ftp_cwd(cd, url->doc) == -1) 7931a5faa10SDag-Erling Smørgrav return NULL; 7941a5faa10SDag-Erling Smørgrav 7951a5faa10SDag-Erling Smørgrav /* stat file */ 796269532d9SDag-Erling Smørgrav if (us && _ftp_stat(cd, url->doc, us) == -1 797269532d9SDag-Erling Smørgrav && fetchLastErrCode != FETCH_UNAVAIL) 7981a5faa10SDag-Erling Smørgrav return NULL; 7991a5faa10SDag-Erling Smørgrav 8005aea254fSDag-Erling Smørgrav /* initiate the transfer */ 80132425dafSDag-Erling Smørgrav return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags); 802f62e5228SDag-Erling Smørgrav } 803f62e5228SDag-Erling Smørgrav 8045aea254fSDag-Erling Smørgrav /* 8051a5faa10SDag-Erling Smørgrav * Get file 8061a5faa10SDag-Erling Smørgrav */ 8071a5faa10SDag-Erling Smørgrav FILE * 8081a5faa10SDag-Erling Smørgrav fetchGetFTP(struct url *url, char *flags) 8091a5faa10SDag-Erling Smørgrav { 8101a5faa10SDag-Erling Smørgrav return fetchXGetFTP(url, NULL, flags); 8111a5faa10SDag-Erling Smørgrav } 8121a5faa10SDag-Erling Smørgrav 8131a5faa10SDag-Erling Smørgrav /* 8145aea254fSDag-Erling Smørgrav * Put file 8155aea254fSDag-Erling Smørgrav */ 816f62e5228SDag-Erling Smørgrav FILE * 817d8acd8dcSDag-Erling Smørgrav fetchPutFTP(struct url *url, char *flags) 8184ca1ab94SDag-Erling Smørgrav { 819fc6e9e65SDag-Erling Smørgrav int cd; 8205aea254fSDag-Erling Smørgrav 821dfe7c55fSDag-Erling Smørgrav if (_ftp_use_http_proxy()) 822dfe7c55fSDag-Erling Smørgrav return fetchPutHTTP(url, flags); 823dfe7c55fSDag-Erling Smørgrav 8245aea254fSDag-Erling Smørgrav /* connect to server */ 825fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 8265aea254fSDag-Erling Smørgrav return NULL; 8275aea254fSDag-Erling Smørgrav 8281a5faa10SDag-Erling Smørgrav /* change directory */ 8291a5faa10SDag-Erling Smørgrav if (_ftp_cwd(cd, url->doc) == -1) 8301a5faa10SDag-Erling Smørgrav return NULL; 8311a5faa10SDag-Erling Smørgrav 8325aea254fSDag-Erling Smørgrav /* initiate the transfer */ 833fc6e9e65SDag-Erling Smørgrav return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", 83432425dafSDag-Erling Smørgrav url->doc, "w", url->offset, flags); 8358e3986eaSDag-Erling Smørgrav } 836d8acd8dcSDag-Erling Smørgrav 8375aea254fSDag-Erling Smørgrav /* 8385aea254fSDag-Erling Smørgrav * Get file stats 8395aea254fSDag-Erling Smørgrav */ 840d8acd8dcSDag-Erling Smørgrav int 841d8acd8dcSDag-Erling Smørgrav fetchStatFTP(struct url *url, struct url_stat *us, char *flags) 842d8acd8dcSDag-Erling Smørgrav { 8431a5faa10SDag-Erling Smørgrav int cd; 8445aea254fSDag-Erling Smørgrav 845dfe7c55fSDag-Erling Smørgrav if (_ftp_use_http_proxy()) 846dfe7c55fSDag-Erling Smørgrav return fetchStatHTTP(url, us, flags); 847dfe7c55fSDag-Erling Smørgrav 8480669702cSDag-Erling Smørgrav us->size = -1; 8490669702cSDag-Erling Smørgrav us->atime = us->mtime = 0; 8500669702cSDag-Erling Smørgrav 8515aea254fSDag-Erling Smørgrav /* connect to server */ 852fc6e9e65SDag-Erling Smørgrav if ((cd = _ftp_cached_connect(url, flags)) == NULL) 8535aea254fSDag-Erling Smørgrav return -1; 8545aea254fSDag-Erling Smørgrav 8555aea254fSDag-Erling Smørgrav /* change directory */ 8561a5faa10SDag-Erling Smørgrav if (_ftp_cwd(cd, url->doc) == -1) 8575aea254fSDag-Erling Smørgrav return -1; 8585aea254fSDag-Erling Smørgrav 8591a5faa10SDag-Erling Smørgrav /* stat file */ 8601a5faa10SDag-Erling Smørgrav return _ftp_stat(cd, url->doc, us); 861d8acd8dcSDag-Erling Smørgrav } 862ce71b736SDag-Erling Smørgrav 863ce71b736SDag-Erling Smørgrav /* 864ce71b736SDag-Erling Smørgrav * List a directory 865ce71b736SDag-Erling Smørgrav */ 866ce71b736SDag-Erling Smørgrav extern void warnx(char *, ...); 867ce71b736SDag-Erling Smørgrav struct url_ent * 868ce71b736SDag-Erling Smørgrav fetchListFTP(struct url *url, char *flags) 869ce71b736SDag-Erling Smørgrav { 870dfe7c55fSDag-Erling Smørgrav if (_ftp_use_http_proxy()) 871dfe7c55fSDag-Erling Smørgrav return fetchListHTTP(url, flags); 872dfe7c55fSDag-Erling Smørgrav 873def5f54cSDag-Erling Smørgrav warnx("fetchListFTP(): not implemented"); 874ce71b736SDag-Erling Smørgrav return NULL; 875ce71b736SDag-Erling Smørgrav } 876