1*0a6a1f1dSLionel Sambuc /* $NetBSD: ftp.c,v 1.7 2014/01/07 02:13:00 joerg Exp $ */
2040ec644SDavid van Moolenbroek /*-
3040ec644SDavid van Moolenbroek * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
4040ec644SDavid van Moolenbroek * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
5040ec644SDavid van Moolenbroek * All rights reserved.
6040ec644SDavid van Moolenbroek *
7040ec644SDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without
8040ec644SDavid van Moolenbroek * modification, are permitted provided that the following conditions
9040ec644SDavid van Moolenbroek * are met:
10040ec644SDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright
11040ec644SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer
12040ec644SDavid van Moolenbroek * in this position and unchanged.
13040ec644SDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright
14040ec644SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer in the
15040ec644SDavid van Moolenbroek * documentation and/or other materials provided with the distribution.
16040ec644SDavid van Moolenbroek * 3. The name of the author may not be used to endorse or promote products
17040ec644SDavid van Moolenbroek * derived from this software without specific prior written permission
18040ec644SDavid van Moolenbroek *
19040ec644SDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20040ec644SDavid van Moolenbroek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21040ec644SDavid van Moolenbroek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22040ec644SDavid van Moolenbroek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23040ec644SDavid van Moolenbroek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24040ec644SDavid van Moolenbroek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25040ec644SDavid van Moolenbroek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26040ec644SDavid van Moolenbroek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27040ec644SDavid van Moolenbroek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28040ec644SDavid van Moolenbroek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29040ec644SDavid van Moolenbroek *
30040ec644SDavid van Moolenbroek * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $
31040ec644SDavid van Moolenbroek */
32040ec644SDavid van Moolenbroek
33040ec644SDavid van Moolenbroek /*
34040ec644SDavid van Moolenbroek * Portions of this code were taken from or based on ftpio.c:
35040ec644SDavid van Moolenbroek *
36040ec644SDavid van Moolenbroek * ----------------------------------------------------------------------------
37040ec644SDavid van Moolenbroek * "THE BEER-WARE LICENSE" (Revision 42):
38040ec644SDavid van Moolenbroek * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
39040ec644SDavid van Moolenbroek * can do whatever you want with this stuff. If we meet some day, and you think
40040ec644SDavid van Moolenbroek * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
41040ec644SDavid van Moolenbroek * ----------------------------------------------------------------------------
42040ec644SDavid van Moolenbroek *
43040ec644SDavid van Moolenbroek * Major Changelog:
44040ec644SDavid van Moolenbroek *
45040ec644SDavid van Moolenbroek * Dag-Erling Co�dan Sm�rgrav
46040ec644SDavid van Moolenbroek * 9 Jun 1998
47040ec644SDavid van Moolenbroek *
48040ec644SDavid van Moolenbroek * Incorporated into libfetch
49040ec644SDavid van Moolenbroek *
50040ec644SDavid van Moolenbroek * Jordan K. Hubbard
51040ec644SDavid van Moolenbroek * 17 Jan 1996
52040ec644SDavid van Moolenbroek *
53040ec644SDavid van Moolenbroek * Turned inside out. Now returns xfers as new file ids, not as a special
54040ec644SDavid van Moolenbroek * `state' of FTP_t
55040ec644SDavid van Moolenbroek *
56040ec644SDavid van Moolenbroek * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
57040ec644SDavid van Moolenbroek *
58040ec644SDavid van Moolenbroek */
59040ec644SDavid van Moolenbroek
60040ec644SDavid van Moolenbroek #ifdef __linux__
61040ec644SDavid van Moolenbroek /* Keep this down to Linux, it can create surprises else where. */
62040ec644SDavid van Moolenbroek #define _GNU_SOURCE
63040ec644SDavid van Moolenbroek #endif
64040ec644SDavid van Moolenbroek
65040ec644SDavid van Moolenbroek #if HAVE_CONFIG_H
66040ec644SDavid van Moolenbroek #include "config.h"
67040ec644SDavid van Moolenbroek #endif
68040ec644SDavid van Moolenbroek #ifndef NETBSD
69040ec644SDavid van Moolenbroek #include <nbcompat.h>
70040ec644SDavid van Moolenbroek #endif
71040ec644SDavid van Moolenbroek
72040ec644SDavid van Moolenbroek #include <sys/types.h>
73040ec644SDavid van Moolenbroek #include <sys/socket.h>
74040ec644SDavid van Moolenbroek
75040ec644SDavid van Moolenbroek #include <netinet/in.h>
76040ec644SDavid van Moolenbroek #include <arpa/inet.h>
77040ec644SDavid van Moolenbroek
78040ec644SDavid van Moolenbroek #include <ctype.h>
79040ec644SDavid van Moolenbroek #include <errno.h>
80040ec644SDavid van Moolenbroek #include <fcntl.h>
81040ec644SDavid van Moolenbroek #if defined(HAVE_INTTYPES_H) || defined(NETBSD)
82040ec644SDavid van Moolenbroek #include <inttypes.h>
83040ec644SDavid van Moolenbroek #endif
84040ec644SDavid van Moolenbroek #include <stdarg.h>
85040ec644SDavid van Moolenbroek #ifndef NETBSD
86040ec644SDavid van Moolenbroek #include <nbcompat/netdb.h>
87040ec644SDavid van Moolenbroek #include <nbcompat/stdio.h>
88040ec644SDavid van Moolenbroek #else
89040ec644SDavid van Moolenbroek #include <netdb.h>
90040ec644SDavid van Moolenbroek #include <stdio.h>
91040ec644SDavid van Moolenbroek #endif
92040ec644SDavid van Moolenbroek #include <stdlib.h>
93040ec644SDavid van Moolenbroek #include <string.h>
94040ec644SDavid van Moolenbroek #include <time.h>
95040ec644SDavid van Moolenbroek #include <unistd.h>
96040ec644SDavid van Moolenbroek
97040ec644SDavid van Moolenbroek #include "fetch.h"
98040ec644SDavid van Moolenbroek #include "common.h"
99040ec644SDavid van Moolenbroek #include "ftperr.h"
100040ec644SDavid van Moolenbroek
101040ec644SDavid van Moolenbroek #define FTP_ANONYMOUS_USER "anonymous"
102040ec644SDavid van Moolenbroek
103040ec644SDavid van Moolenbroek #define FTP_CONNECTION_ALREADY_OPEN 125
104040ec644SDavid van Moolenbroek #define FTP_OPEN_DATA_CONNECTION 150
105040ec644SDavid van Moolenbroek #define FTP_OK 200
106040ec644SDavid van Moolenbroek #define FTP_FILE_STATUS 213
107040ec644SDavid van Moolenbroek #define FTP_SERVICE_READY 220
108040ec644SDavid van Moolenbroek #define FTP_TRANSFER_COMPLETE 226
109040ec644SDavid van Moolenbroek #define FTP_PASSIVE_MODE 227
110040ec644SDavid van Moolenbroek #define FTP_LPASSIVE_MODE 228
111040ec644SDavid van Moolenbroek #define FTP_EPASSIVE_MODE 229
112040ec644SDavid van Moolenbroek #define FTP_LOGGED_IN 230
113040ec644SDavid van Moolenbroek #define FTP_FILE_ACTION_OK 250
114040ec644SDavid van Moolenbroek #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */
115040ec644SDavid van Moolenbroek #define FTP_FILE_CREATED 257 /* multiple meanings */
116040ec644SDavid van Moolenbroek #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */
117040ec644SDavid van Moolenbroek #define FTP_NEED_PASSWORD 331
118040ec644SDavid van Moolenbroek #define FTP_NEED_ACCOUNT 332
119040ec644SDavid van Moolenbroek #define FTP_FILE_OK 350
120040ec644SDavid van Moolenbroek #define FTP_SYNTAX_ERROR 500
121040ec644SDavid van Moolenbroek #define FTP_PROTOCOL_ERROR 999
122040ec644SDavid van Moolenbroek
123040ec644SDavid van Moolenbroek #define isftpreply(foo) \
124040ec644SDavid van Moolenbroek (isdigit((unsigned char)foo[0]) && \
125040ec644SDavid van Moolenbroek isdigit((unsigned char)foo[1]) && \
126040ec644SDavid van Moolenbroek isdigit((unsigned char)foo[2]) && \
127040ec644SDavid van Moolenbroek (foo[3] == ' ' || foo[3] == '\0'))
128040ec644SDavid van Moolenbroek #define isftpinfo(foo) \
129040ec644SDavid van Moolenbroek (isdigit((unsigned char)foo[0]) && \
130040ec644SDavid van Moolenbroek isdigit((unsigned char)foo[1]) && \
131040ec644SDavid van Moolenbroek isdigit((unsigned char)foo[2]) && \
132040ec644SDavid van Moolenbroek foo[3] == '-')
133040ec644SDavid van Moolenbroek
134040ec644SDavid van Moolenbroek /*
135040ec644SDavid van Moolenbroek * Translate IPv4 mapped IPv6 address to IPv4 address
136040ec644SDavid van Moolenbroek */
137040ec644SDavid van Moolenbroek static void
unmappedaddr(struct sockaddr_in6 * sin6,socklen_t * len)138040ec644SDavid van Moolenbroek unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len)
139040ec644SDavid van Moolenbroek {
140040ec644SDavid van Moolenbroek struct sockaddr_in *sin4;
141040ec644SDavid van Moolenbroek void *addrp;
142040ec644SDavid van Moolenbroek uint32_t addr;
143040ec644SDavid van Moolenbroek int port;
144040ec644SDavid van Moolenbroek
145040ec644SDavid van Moolenbroek if (sin6->sin6_family != AF_INET6 ||
146040ec644SDavid van Moolenbroek !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
147040ec644SDavid van Moolenbroek return;
148040ec644SDavid van Moolenbroek sin4 = (struct sockaddr_in *)(void *)sin6;
149040ec644SDavid van Moolenbroek addrp = &sin6->sin6_addr.s6_addr[12];
150040ec644SDavid van Moolenbroek addr = *(uint32_t *)addrp;
151040ec644SDavid van Moolenbroek port = sin6->sin6_port;
152040ec644SDavid van Moolenbroek memset(sin4, 0, sizeof(struct sockaddr_in));
153040ec644SDavid van Moolenbroek sin4->sin_addr.s_addr = addr;
154040ec644SDavid van Moolenbroek sin4->sin_port = port;
155040ec644SDavid van Moolenbroek sin4->sin_family = AF_INET;
156040ec644SDavid van Moolenbroek *len = sizeof(struct sockaddr_in);
157040ec644SDavid van Moolenbroek #ifdef HAVE_SA_LEN
158040ec644SDavid van Moolenbroek sin4->sin_len = sizeof(struct sockaddr_in);
159040ec644SDavid van Moolenbroek #endif
160040ec644SDavid van Moolenbroek }
161040ec644SDavid van Moolenbroek
162040ec644SDavid van Moolenbroek /*
163040ec644SDavid van Moolenbroek * Get server response
164040ec644SDavid van Moolenbroek */
165040ec644SDavid van Moolenbroek static int
ftp_chkerr(conn_t * conn)166040ec644SDavid van Moolenbroek ftp_chkerr(conn_t *conn)
167040ec644SDavid van Moolenbroek {
168040ec644SDavid van Moolenbroek if (fetch_getln(conn) == -1) {
169040ec644SDavid van Moolenbroek fetch_syserr();
170040ec644SDavid van Moolenbroek return (-1);
171040ec644SDavid van Moolenbroek }
172040ec644SDavid van Moolenbroek if (isftpinfo(conn->buf)) {
173040ec644SDavid van Moolenbroek while (conn->buflen && !isftpreply(conn->buf)) {
174040ec644SDavid van Moolenbroek if (fetch_getln(conn) == -1) {
175040ec644SDavid van Moolenbroek fetch_syserr();
176040ec644SDavid van Moolenbroek return (-1);
177040ec644SDavid van Moolenbroek }
178040ec644SDavid van Moolenbroek }
179040ec644SDavid van Moolenbroek }
180040ec644SDavid van Moolenbroek
181040ec644SDavid van Moolenbroek while (conn->buflen &&
182040ec644SDavid van Moolenbroek isspace((unsigned char)conn->buf[conn->buflen - 1]))
183040ec644SDavid van Moolenbroek conn->buflen--;
184040ec644SDavid van Moolenbroek conn->buf[conn->buflen] = '\0';
185040ec644SDavid van Moolenbroek
186040ec644SDavid van Moolenbroek if (!isftpreply(conn->buf)) {
187040ec644SDavid van Moolenbroek ftp_seterr(FTP_PROTOCOL_ERROR);
188040ec644SDavid van Moolenbroek return (-1);
189040ec644SDavid van Moolenbroek }
190040ec644SDavid van Moolenbroek
191040ec644SDavid van Moolenbroek conn->err = (conn->buf[0] - '0') * 100
192040ec644SDavid van Moolenbroek + (conn->buf[1] - '0') * 10
193040ec644SDavid van Moolenbroek + (conn->buf[2] - '0');
194040ec644SDavid van Moolenbroek
195040ec644SDavid van Moolenbroek return (conn->err);
196040ec644SDavid van Moolenbroek }
197040ec644SDavid van Moolenbroek
198040ec644SDavid van Moolenbroek /*
199040ec644SDavid van Moolenbroek * Send a command and check reply
200040ec644SDavid van Moolenbroek */
201*0a6a1f1dSLionel Sambuc __printflike(2, 3)
202040ec644SDavid van Moolenbroek static int
ftp_cmd(conn_t * conn,const char * fmt,...)203040ec644SDavid van Moolenbroek ftp_cmd(conn_t *conn, const char *fmt, ...)
204040ec644SDavid van Moolenbroek {
205040ec644SDavid van Moolenbroek va_list ap;
206040ec644SDavid van Moolenbroek size_t len;
207040ec644SDavid van Moolenbroek char *msg;
208040ec644SDavid van Moolenbroek ssize_t r;
209040ec644SDavid van Moolenbroek
210040ec644SDavid van Moolenbroek va_start(ap, fmt);
211040ec644SDavid van Moolenbroek len = vasprintf(&msg, fmt, ap);
212040ec644SDavid van Moolenbroek va_end(ap);
213040ec644SDavid van Moolenbroek
214040ec644SDavid van Moolenbroek if (msg == NULL) {
215040ec644SDavid van Moolenbroek errno = ENOMEM;
216040ec644SDavid van Moolenbroek fetch_syserr();
217040ec644SDavid van Moolenbroek return (-1);
218040ec644SDavid van Moolenbroek }
219040ec644SDavid van Moolenbroek
220040ec644SDavid van Moolenbroek r = fetch_write(conn, msg, len);
221040ec644SDavid van Moolenbroek free(msg);
222040ec644SDavid van Moolenbroek
223040ec644SDavid van Moolenbroek if (r == -1) {
224040ec644SDavid van Moolenbroek fetch_syserr();
225040ec644SDavid van Moolenbroek return (-1);
226040ec644SDavid van Moolenbroek }
227040ec644SDavid van Moolenbroek
228040ec644SDavid van Moolenbroek return (ftp_chkerr(conn));
229040ec644SDavid van Moolenbroek }
230040ec644SDavid van Moolenbroek
231040ec644SDavid van Moolenbroek /*
232040ec644SDavid van Moolenbroek * Return a pointer to the filename part of a path
233040ec644SDavid van Moolenbroek */
234040ec644SDavid van Moolenbroek static const char *
ftp_filename(const char * file,size_t * len,int * type,int subdir)235040ec644SDavid van Moolenbroek ftp_filename(const char *file, size_t *len, int *type, int subdir)
236040ec644SDavid van Moolenbroek {
237040ec644SDavid van Moolenbroek const char *s;
238040ec644SDavid van Moolenbroek
239040ec644SDavid van Moolenbroek if ((s = strrchr(file, '/')) == NULL || subdir)
240040ec644SDavid van Moolenbroek s = file;
241040ec644SDavid van Moolenbroek else
242040ec644SDavid van Moolenbroek s = s + 1;
243040ec644SDavid van Moolenbroek *len = strlen(s);
244040ec644SDavid van Moolenbroek if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) {
245040ec644SDavid van Moolenbroek *type = s[*len - 1];
246040ec644SDavid van Moolenbroek *len -= 7;
247040ec644SDavid van Moolenbroek } else {
248040ec644SDavid van Moolenbroek *type = '\0';
249040ec644SDavid van Moolenbroek }
250040ec644SDavid van Moolenbroek return (s);
251040ec644SDavid van Moolenbroek }
252040ec644SDavid van Moolenbroek
253040ec644SDavid van Moolenbroek /*
254040ec644SDavid van Moolenbroek * Get current working directory from the reply to a CWD, PWD or CDUP
255040ec644SDavid van Moolenbroek * command.
256040ec644SDavid van Moolenbroek */
257040ec644SDavid van Moolenbroek static int
ftp_pwd(conn_t * conn,char ** pwd)258040ec644SDavid van Moolenbroek ftp_pwd(conn_t *conn, char **pwd)
259040ec644SDavid van Moolenbroek {
260040ec644SDavid van Moolenbroek char *src, *dst, *end;
261040ec644SDavid van Moolenbroek int q;
262040ec644SDavid van Moolenbroek size_t len;
263040ec644SDavid van Moolenbroek
264040ec644SDavid van Moolenbroek if (conn->err != FTP_WORKING_DIRECTORY &&
265040ec644SDavid van Moolenbroek conn->err != FTP_FILE_ACTION_OK)
266040ec644SDavid van Moolenbroek return (FTP_PROTOCOL_ERROR);
267040ec644SDavid van Moolenbroek end = conn->buf + conn->buflen;
268040ec644SDavid van Moolenbroek src = conn->buf + 4;
269040ec644SDavid van Moolenbroek if (src >= end || *src++ != '"')
270040ec644SDavid van Moolenbroek return (FTP_PROTOCOL_ERROR);
271040ec644SDavid van Moolenbroek len = end - src + 1;
272040ec644SDavid van Moolenbroek *pwd = malloc(len);
273040ec644SDavid van Moolenbroek if (*pwd == NULL)
274040ec644SDavid van Moolenbroek return (FTP_PROTOCOL_ERROR);
275040ec644SDavid van Moolenbroek for (q = 0, dst = *pwd; src < end; ++src) {
276040ec644SDavid van Moolenbroek if (!q && *src == '"')
277040ec644SDavid van Moolenbroek q = 1;
278040ec644SDavid van Moolenbroek else if (q && *src != '"')
279040ec644SDavid van Moolenbroek break;
280040ec644SDavid van Moolenbroek else if (q)
281040ec644SDavid van Moolenbroek *dst++ = '"', q = 0;
282040ec644SDavid van Moolenbroek else
283040ec644SDavid van Moolenbroek *dst++ = *src;
284040ec644SDavid van Moolenbroek }
285040ec644SDavid van Moolenbroek *dst = '\0';
286040ec644SDavid van Moolenbroek if (**pwd != '/') {
287040ec644SDavid van Moolenbroek free(*pwd);
288040ec644SDavid van Moolenbroek *pwd = NULL;
289040ec644SDavid van Moolenbroek return (FTP_PROTOCOL_ERROR);
290040ec644SDavid van Moolenbroek }
291040ec644SDavid van Moolenbroek return (FTP_OK);
292040ec644SDavid van Moolenbroek }
293040ec644SDavid van Moolenbroek
294040ec644SDavid van Moolenbroek /*
295040ec644SDavid van Moolenbroek * Change working directory to the directory that contains the specified
296040ec644SDavid van Moolenbroek * file.
297040ec644SDavid van Moolenbroek */
298040ec644SDavid van Moolenbroek static int
ftp_cwd(conn_t * conn,const char * path,int subdir)299040ec644SDavid van Moolenbroek ftp_cwd(conn_t *conn, const char *path, int subdir)
300040ec644SDavid van Moolenbroek {
301040ec644SDavid van Moolenbroek const char *beg, *end;
302040ec644SDavid van Moolenbroek char *pwd, *dst;
303040ec644SDavid van Moolenbroek int e;
304040ec644SDavid van Moolenbroek size_t i, len;
305040ec644SDavid van Moolenbroek
306040ec644SDavid van Moolenbroek if (*path != '/') {
307040ec644SDavid van Moolenbroek ftp_seterr(501);
308040ec644SDavid van Moolenbroek return (-1);
309040ec644SDavid van Moolenbroek }
310040ec644SDavid van Moolenbroek ++path;
311040ec644SDavid van Moolenbroek
312040ec644SDavid van Moolenbroek /* Simple case: still in the home directory and no directory change. */
313040ec644SDavid van Moolenbroek if (conn->ftp_home == NULL && strchr(path, '/') == NULL &&
314040ec644SDavid van Moolenbroek (!subdir || *path == '\0'))
315040ec644SDavid van Moolenbroek return 0;
316040ec644SDavid van Moolenbroek
317040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY ||
318040ec644SDavid van Moolenbroek (e = ftp_pwd(conn, &pwd)) != FTP_OK) {
319040ec644SDavid van Moolenbroek ftp_seterr(e);
320040ec644SDavid van Moolenbroek return (-1);
321040ec644SDavid van Moolenbroek }
322040ec644SDavid van Moolenbroek if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) {
323040ec644SDavid van Moolenbroek fetch_syserr();
324040ec644SDavid van Moolenbroek free(pwd);
325040ec644SDavid van Moolenbroek return (-1);
326040ec644SDavid van Moolenbroek }
327040ec644SDavid van Moolenbroek if (*path == '/') {
328040ec644SDavid van Moolenbroek while (path[1] == '/')
329040ec644SDavid van Moolenbroek ++path;
330040ec644SDavid van Moolenbroek dst = strdup(path);
331040ec644SDavid van Moolenbroek } else if (strcmp(conn->ftp_home, "/") == 0) {
332040ec644SDavid van Moolenbroek dst = strdup(path - 1);
333040ec644SDavid van Moolenbroek } else {
334040ec644SDavid van Moolenbroek asprintf(&dst, "%s/%s", conn->ftp_home, path);
335040ec644SDavid van Moolenbroek }
336040ec644SDavid van Moolenbroek if (dst == NULL) {
337040ec644SDavid van Moolenbroek fetch_syserr();
338040ec644SDavid van Moolenbroek free(pwd);
339040ec644SDavid van Moolenbroek return (-1);
340040ec644SDavid van Moolenbroek }
341040ec644SDavid van Moolenbroek
342040ec644SDavid van Moolenbroek if (subdir)
343040ec644SDavid van Moolenbroek end = dst + strlen(dst);
344040ec644SDavid van Moolenbroek else
345040ec644SDavid van Moolenbroek end = strrchr(dst, '/');
346040ec644SDavid van Moolenbroek
347040ec644SDavid van Moolenbroek for (;;) {
348040ec644SDavid van Moolenbroek len = strlen(pwd);
349040ec644SDavid van Moolenbroek
350040ec644SDavid van Moolenbroek /* Look for a common prefix between PWD and dir to fetch. */
351040ec644SDavid van Moolenbroek for (i = 0; i <= len && i <= (size_t)(end - dst); ++i)
352040ec644SDavid van Moolenbroek if (pwd[i] != dst[i])
353040ec644SDavid van Moolenbroek break;
354040ec644SDavid van Moolenbroek /* Keep going up a dir until we have a matching prefix. */
355040ec644SDavid van Moolenbroek if (strcmp(pwd, "/") == 0)
356040ec644SDavid van Moolenbroek break;
357040ec644SDavid van Moolenbroek if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/'))
358040ec644SDavid van Moolenbroek break;
359040ec644SDavid van Moolenbroek free(pwd);
360040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK ||
361040ec644SDavid van Moolenbroek (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY ||
362040ec644SDavid van Moolenbroek (e = ftp_pwd(conn, &pwd)) != FTP_OK) {
363040ec644SDavid van Moolenbroek ftp_seterr(e);
364040ec644SDavid van Moolenbroek free(dst);
365040ec644SDavid van Moolenbroek return (-1);
366040ec644SDavid van Moolenbroek }
367040ec644SDavid van Moolenbroek }
368040ec644SDavid van Moolenbroek free(pwd);
369040ec644SDavid van Moolenbroek
370040ec644SDavid van Moolenbroek #ifdef FTP_COMBINE_CWDS
371040ec644SDavid van Moolenbroek /* Skip leading slashes, even "////". */
372040ec644SDavid van Moolenbroek for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i)
373040ec644SDavid van Moolenbroek /* nothing */ ;
374040ec644SDavid van Moolenbroek
375040ec644SDavid van Moolenbroek /* If there is no trailing dir, we're already there. */
376040ec644SDavid van Moolenbroek if (beg >= end) {
377040ec644SDavid van Moolenbroek free(dst);
378040ec644SDavid van Moolenbroek return (0);
379040ec644SDavid van Moolenbroek }
380040ec644SDavid van Moolenbroek
381040ec644SDavid van Moolenbroek /* Change to the directory all in one chunk (e.g., foo/bar/baz). */
382040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg);
383040ec644SDavid van Moolenbroek if (e == FTP_FILE_ACTION_OK) {
384040ec644SDavid van Moolenbroek free(dst);
385040ec644SDavid van Moolenbroek return (0);
386040ec644SDavid van Moolenbroek }
387040ec644SDavid van Moolenbroek #endif /* FTP_COMBINE_CWDS */
388040ec644SDavid van Moolenbroek
389040ec644SDavid van Moolenbroek /* That didn't work so go back to legacy behavior (multiple CWDs). */
390040ec644SDavid van Moolenbroek for (beg = dst + i; beg < end; beg = dst + i + 1) {
391040ec644SDavid van Moolenbroek while (*beg == '/')
392040ec644SDavid van Moolenbroek ++beg, ++i;
393040ec644SDavid van Moolenbroek for (++i; dst + i < end && dst[i] != '/'; ++i)
394040ec644SDavid van Moolenbroek /* nothing */ ;
395*0a6a1f1dSLionel Sambuc e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(dst + i - beg), beg);
396040ec644SDavid van Moolenbroek if (e != FTP_FILE_ACTION_OK) {
397040ec644SDavid van Moolenbroek free(dst);
398040ec644SDavid van Moolenbroek ftp_seterr(e);
399040ec644SDavid van Moolenbroek return (-1);
400040ec644SDavid van Moolenbroek }
401040ec644SDavid van Moolenbroek }
402040ec644SDavid van Moolenbroek free(dst);
403040ec644SDavid van Moolenbroek return (0);
404040ec644SDavid van Moolenbroek }
405040ec644SDavid van Moolenbroek
406040ec644SDavid van Moolenbroek /*
407040ec644SDavid van Moolenbroek * Set transfer mode and data type
408040ec644SDavid van Moolenbroek */
409040ec644SDavid van Moolenbroek static int
ftp_mode_type(conn_t * conn,int mode,int type)410040ec644SDavid van Moolenbroek ftp_mode_type(conn_t *conn, int mode, int type)
411040ec644SDavid van Moolenbroek {
412040ec644SDavid van Moolenbroek int e;
413040ec644SDavid van Moolenbroek
414040ec644SDavid van Moolenbroek switch (mode) {
415040ec644SDavid van Moolenbroek case 0:
416040ec644SDavid van Moolenbroek case 's':
417040ec644SDavid van Moolenbroek mode = 'S';
418040ec644SDavid van Moolenbroek /*FALLTHROUGH*/
419040ec644SDavid van Moolenbroek case 'S':
420040ec644SDavid van Moolenbroek break;
421040ec644SDavid van Moolenbroek default:
422040ec644SDavid van Moolenbroek return (FTP_PROTOCOL_ERROR);
423040ec644SDavid van Moolenbroek }
424040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) {
425040ec644SDavid van Moolenbroek if (mode == 'S') {
426040ec644SDavid van Moolenbroek /*
427040ec644SDavid van Moolenbroek * Stream mode is supposed to be the default - so
428040ec644SDavid van Moolenbroek * much so that some servers not only do not
429040ec644SDavid van Moolenbroek * support any other mode, but do not support the
430040ec644SDavid van Moolenbroek * MODE command at all.
431040ec644SDavid van Moolenbroek *
432040ec644SDavid van Moolenbroek * If "MODE S" fails, it is unlikely that we
433040ec644SDavid van Moolenbroek * previously succeeded in setting a different
434040ec644SDavid van Moolenbroek * mode. Therefore, we simply hope that the
435040ec644SDavid van Moolenbroek * server is already in the correct mode, and
436040ec644SDavid van Moolenbroek * silently ignore the failure.
437040ec644SDavid van Moolenbroek */
438040ec644SDavid van Moolenbroek } else {
439040ec644SDavid van Moolenbroek return (e);
440040ec644SDavid van Moolenbroek }
441040ec644SDavid van Moolenbroek }
442040ec644SDavid van Moolenbroek
443040ec644SDavid van Moolenbroek switch (type) {
444040ec644SDavid van Moolenbroek case 0:
445040ec644SDavid van Moolenbroek case 'i':
446040ec644SDavid van Moolenbroek type = 'I';
447040ec644SDavid van Moolenbroek /*FALLTHROUGH*/
448040ec644SDavid van Moolenbroek case 'I':
449040ec644SDavid van Moolenbroek break;
450040ec644SDavid van Moolenbroek case 'a':
451040ec644SDavid van Moolenbroek type = 'A';
452040ec644SDavid van Moolenbroek /*FALLTHROUGH*/
453040ec644SDavid van Moolenbroek case 'A':
454040ec644SDavid van Moolenbroek break;
455040ec644SDavid van Moolenbroek case 'd':
456040ec644SDavid van Moolenbroek type = 'D';
457040ec644SDavid van Moolenbroek /*FALLTHROUGH*/
458040ec644SDavid van Moolenbroek case 'D':
459040ec644SDavid van Moolenbroek /* can't handle yet */
460040ec644SDavid van Moolenbroek default:
461040ec644SDavid van Moolenbroek return (FTP_PROTOCOL_ERROR);
462040ec644SDavid van Moolenbroek }
463040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK)
464040ec644SDavid van Moolenbroek return (e);
465040ec644SDavid van Moolenbroek
466040ec644SDavid van Moolenbroek return (FTP_OK);
467040ec644SDavid van Moolenbroek }
468040ec644SDavid van Moolenbroek
469040ec644SDavid van Moolenbroek /*
470040ec644SDavid van Moolenbroek * Request and parse file stats
471040ec644SDavid van Moolenbroek */
472040ec644SDavid van Moolenbroek static int
ftp_stat(conn_t * conn,const char * file,struct url_stat * us)473040ec644SDavid van Moolenbroek ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
474040ec644SDavid van Moolenbroek {
475040ec644SDavid van Moolenbroek char *ln;
476040ec644SDavid van Moolenbroek const char *filename;
477040ec644SDavid van Moolenbroek size_t filenamelen;
478040ec644SDavid van Moolenbroek int type;
479040ec644SDavid van Moolenbroek struct tm tm;
480040ec644SDavid van Moolenbroek time_t t;
481040ec644SDavid van Moolenbroek int e;
482040ec644SDavid van Moolenbroek
483040ec644SDavid van Moolenbroek us->size = -1;
484040ec644SDavid van Moolenbroek us->atime = us->mtime = 0;
485040ec644SDavid van Moolenbroek
486040ec644SDavid van Moolenbroek filename = ftp_filename(file, &filenamelen, &type, 0);
487040ec644SDavid van Moolenbroek
488040ec644SDavid van Moolenbroek if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) {
489040ec644SDavid van Moolenbroek ftp_seterr(e);
490040ec644SDavid van Moolenbroek return (-1);
491040ec644SDavid van Moolenbroek }
492040ec644SDavid van Moolenbroek
493*0a6a1f1dSLionel Sambuc e = ftp_cmd(conn, "SIZE %.*s\r\n", (int)filenamelen, filename);
494040ec644SDavid van Moolenbroek if (e != FTP_FILE_STATUS) {
495040ec644SDavid van Moolenbroek ftp_seterr(e);
496040ec644SDavid van Moolenbroek return (-1);
497040ec644SDavid van Moolenbroek }
498040ec644SDavid van Moolenbroek for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
499040ec644SDavid van Moolenbroek /* nothing */ ;
500040ec644SDavid van Moolenbroek for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
501040ec644SDavid van Moolenbroek us->size = us->size * 10 + *ln - '0';
502040ec644SDavid van Moolenbroek if (*ln && !isspace((unsigned char)*ln)) {
503040ec644SDavid van Moolenbroek ftp_seterr(FTP_PROTOCOL_ERROR);
504040ec644SDavid van Moolenbroek us->size = -1;
505040ec644SDavid van Moolenbroek return (-1);
506040ec644SDavid van Moolenbroek }
507040ec644SDavid van Moolenbroek if (us->size == 0)
508040ec644SDavid van Moolenbroek us->size = -1;
509040ec644SDavid van Moolenbroek
510*0a6a1f1dSLionel Sambuc e = ftp_cmd(conn, "MDTM %.*s\r\n", (int)filenamelen, filename);
511040ec644SDavid van Moolenbroek if (e != FTP_FILE_STATUS) {
512040ec644SDavid van Moolenbroek ftp_seterr(e);
513040ec644SDavid van Moolenbroek return (-1);
514040ec644SDavid van Moolenbroek }
515040ec644SDavid van Moolenbroek for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
516040ec644SDavid van Moolenbroek /* nothing */ ;
517040ec644SDavid van Moolenbroek switch (strspn(ln, "0123456789")) {
518040ec644SDavid van Moolenbroek case 14:
519040ec644SDavid van Moolenbroek break;
520040ec644SDavid van Moolenbroek case 15:
521040ec644SDavid van Moolenbroek ln++;
522040ec644SDavid van Moolenbroek ln[0] = '2';
523040ec644SDavid van Moolenbroek ln[1] = '0';
524040ec644SDavid van Moolenbroek break;
525040ec644SDavid van Moolenbroek default:
526040ec644SDavid van Moolenbroek ftp_seterr(FTP_PROTOCOL_ERROR);
527040ec644SDavid van Moolenbroek return (-1);
528040ec644SDavid van Moolenbroek }
529040ec644SDavid van Moolenbroek if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
530040ec644SDavid van Moolenbroek &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
531040ec644SDavid van Moolenbroek &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
532040ec644SDavid van Moolenbroek ftp_seterr(FTP_PROTOCOL_ERROR);
533040ec644SDavid van Moolenbroek return (-1);
534040ec644SDavid van Moolenbroek }
535040ec644SDavid van Moolenbroek tm.tm_mon--;
536040ec644SDavid van Moolenbroek tm.tm_year -= 1900;
537040ec644SDavid van Moolenbroek tm.tm_isdst = -1;
538040ec644SDavid van Moolenbroek t = timegm(&tm);
539040ec644SDavid van Moolenbroek if (t == (time_t)-1)
540040ec644SDavid van Moolenbroek t = time(NULL);
541040ec644SDavid van Moolenbroek us->mtime = t;
542040ec644SDavid van Moolenbroek us->atime = t;
543040ec644SDavid van Moolenbroek
544040ec644SDavid van Moolenbroek return (0);
545040ec644SDavid van Moolenbroek }
546040ec644SDavid van Moolenbroek
547040ec644SDavid van Moolenbroek /*
548040ec644SDavid van Moolenbroek * I/O functions for FTP
549040ec644SDavid van Moolenbroek */
550040ec644SDavid van Moolenbroek struct ftpio {
551040ec644SDavid van Moolenbroek conn_t *cconn; /* Control connection */
552040ec644SDavid van Moolenbroek conn_t *dconn; /* Data connection */
553040ec644SDavid van Moolenbroek int dir; /* Direction */
554040ec644SDavid van Moolenbroek int eof; /* EOF reached */
555040ec644SDavid van Moolenbroek int err; /* Error code */
556040ec644SDavid van Moolenbroek };
557040ec644SDavid van Moolenbroek
558040ec644SDavid van Moolenbroek static ssize_t ftp_readfn(void *, void *, size_t);
559040ec644SDavid van Moolenbroek static ssize_t ftp_writefn(void *, const void *, size_t);
560040ec644SDavid van Moolenbroek static void ftp_closefn(void *);
561040ec644SDavid van Moolenbroek
562040ec644SDavid van Moolenbroek static ssize_t
ftp_readfn(void * v,void * buf,size_t len)563040ec644SDavid van Moolenbroek ftp_readfn(void *v, void *buf, size_t len)
564040ec644SDavid van Moolenbroek {
565040ec644SDavid van Moolenbroek struct ftpio *io;
566040ec644SDavid van Moolenbroek ssize_t r;
567040ec644SDavid van Moolenbroek
568040ec644SDavid van Moolenbroek io = (struct ftpio *)v;
569040ec644SDavid van Moolenbroek if (io == NULL) {
570040ec644SDavid van Moolenbroek errno = EBADF;
571040ec644SDavid van Moolenbroek return (-1);
572040ec644SDavid van Moolenbroek }
573040ec644SDavid van Moolenbroek if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {
574040ec644SDavid van Moolenbroek errno = EBADF;
575040ec644SDavid van Moolenbroek return (-1);
576040ec644SDavid van Moolenbroek }
577040ec644SDavid van Moolenbroek if (io->err) {
578040ec644SDavid van Moolenbroek errno = io->err;
579040ec644SDavid van Moolenbroek return (-1);
580040ec644SDavid van Moolenbroek }
581040ec644SDavid van Moolenbroek if (io->eof)
582040ec644SDavid van Moolenbroek return (0);
583040ec644SDavid van Moolenbroek r = fetch_read(io->dconn, buf, len);
584040ec644SDavid van Moolenbroek if (r > 0)
585040ec644SDavid van Moolenbroek return (r);
586040ec644SDavid van Moolenbroek if (r == 0) {
587040ec644SDavid van Moolenbroek io->eof = 1;
588040ec644SDavid van Moolenbroek return (0);
589040ec644SDavid van Moolenbroek }
590040ec644SDavid van Moolenbroek if (errno != EINTR)
591040ec644SDavid van Moolenbroek io->err = errno;
592040ec644SDavid van Moolenbroek return (-1);
593040ec644SDavid van Moolenbroek }
594040ec644SDavid van Moolenbroek
595040ec644SDavid van Moolenbroek static ssize_t
ftp_writefn(void * v,const void * buf,size_t len)596040ec644SDavid van Moolenbroek ftp_writefn(void *v, const void *buf, size_t len)
597040ec644SDavid van Moolenbroek {
598040ec644SDavid van Moolenbroek struct ftpio *io;
599040ec644SDavid van Moolenbroek ssize_t w;
600040ec644SDavid van Moolenbroek
601040ec644SDavid van Moolenbroek io = (struct ftpio *)v;
602040ec644SDavid van Moolenbroek if (io == NULL) {
603040ec644SDavid van Moolenbroek errno = EBADF;
604040ec644SDavid van Moolenbroek return (-1);
605040ec644SDavid van Moolenbroek }
606040ec644SDavid van Moolenbroek if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {
607040ec644SDavid van Moolenbroek errno = EBADF;
608040ec644SDavid van Moolenbroek return (-1);
609040ec644SDavid van Moolenbroek }
610040ec644SDavid van Moolenbroek if (io->err) {
611040ec644SDavid van Moolenbroek errno = io->err;
612040ec644SDavid van Moolenbroek return (-1);
613040ec644SDavid van Moolenbroek }
614040ec644SDavid van Moolenbroek w = fetch_write(io->dconn, buf, len);
615040ec644SDavid van Moolenbroek if (w >= 0)
616040ec644SDavid van Moolenbroek return (w);
617040ec644SDavid van Moolenbroek if (errno != EINTR)
618040ec644SDavid van Moolenbroek io->err = errno;
619040ec644SDavid van Moolenbroek return (-1);
620040ec644SDavid van Moolenbroek }
621040ec644SDavid van Moolenbroek
622040ec644SDavid van Moolenbroek static int
ftp_disconnect(conn_t * conn)623040ec644SDavid van Moolenbroek ftp_disconnect(conn_t *conn)
624040ec644SDavid van Moolenbroek {
625040ec644SDavid van Moolenbroek ftp_cmd(conn, "QUIT\r\n");
626040ec644SDavid van Moolenbroek return fetch_close(conn);
627040ec644SDavid van Moolenbroek }
628040ec644SDavid van Moolenbroek
629040ec644SDavid van Moolenbroek static void
ftp_closefn(void * v)630040ec644SDavid van Moolenbroek ftp_closefn(void *v)
631040ec644SDavid van Moolenbroek {
632040ec644SDavid van Moolenbroek struct ftpio *io;
633040ec644SDavid van Moolenbroek
634040ec644SDavid van Moolenbroek io = (struct ftpio *)v;
635040ec644SDavid van Moolenbroek if (io == NULL) {
636040ec644SDavid van Moolenbroek errno = EBADF;
637040ec644SDavid van Moolenbroek return;
638040ec644SDavid van Moolenbroek }
639040ec644SDavid van Moolenbroek if (io->dir == -1)
640040ec644SDavid van Moolenbroek return;
641040ec644SDavid van Moolenbroek if (io->cconn == NULL || io->dconn == NULL) {
642040ec644SDavid van Moolenbroek errno = EBADF;
643040ec644SDavid van Moolenbroek return;
644040ec644SDavid van Moolenbroek }
645040ec644SDavid van Moolenbroek fetch_close(io->dconn);
646040ec644SDavid van Moolenbroek io->dconn = NULL;
647040ec644SDavid van Moolenbroek io->dir = -1;
648040ec644SDavid van Moolenbroek (void)ftp_chkerr(io->cconn);
649040ec644SDavid van Moolenbroek fetch_cache_put(io->cconn, ftp_disconnect);
650040ec644SDavid van Moolenbroek free(io);
651040ec644SDavid van Moolenbroek return;
652040ec644SDavid van Moolenbroek }
653040ec644SDavid van Moolenbroek
654040ec644SDavid van Moolenbroek static fetchIO *
ftp_setup(conn_t * cconn,conn_t * dconn,int mode)655040ec644SDavid van Moolenbroek ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
656040ec644SDavid van Moolenbroek {
657040ec644SDavid van Moolenbroek struct ftpio *io;
658040ec644SDavid van Moolenbroek fetchIO *f;
659040ec644SDavid van Moolenbroek
660040ec644SDavid van Moolenbroek if (cconn == NULL || dconn == NULL)
661040ec644SDavid van Moolenbroek return (NULL);
662040ec644SDavid van Moolenbroek if ((io = malloc(sizeof(*io))) == NULL)
663040ec644SDavid van Moolenbroek return (NULL);
664040ec644SDavid van Moolenbroek io->cconn = cconn;
665040ec644SDavid van Moolenbroek io->dconn = dconn;
666040ec644SDavid van Moolenbroek io->dir = mode;
667040ec644SDavid van Moolenbroek io->eof = io->err = 0;
668040ec644SDavid van Moolenbroek f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn);
669040ec644SDavid van Moolenbroek if (f == NULL)
670040ec644SDavid van Moolenbroek free(io);
671040ec644SDavid van Moolenbroek return (f);
672040ec644SDavid van Moolenbroek }
673040ec644SDavid van Moolenbroek
674040ec644SDavid van Moolenbroek /*
675040ec644SDavid van Moolenbroek * Transfer file
676040ec644SDavid van Moolenbroek */
677040ec644SDavid van Moolenbroek static fetchIO *
ftp_transfer(conn_t * conn,const char * oper,const char * file,const char * op_arg,int mode,off_t offset,const char * flags)678040ec644SDavid van Moolenbroek ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg,
679040ec644SDavid van Moolenbroek int mode, off_t offset, const char *flags)
680040ec644SDavid van Moolenbroek {
681040ec644SDavid van Moolenbroek union anonymous {
682040ec644SDavid van Moolenbroek struct sockaddr_storage ss;
683040ec644SDavid van Moolenbroek struct sockaddr sa;
684040ec644SDavid van Moolenbroek struct sockaddr_in6 sin6;
685040ec644SDavid van Moolenbroek struct sockaddr_in sin4;
686040ec644SDavid van Moolenbroek } u;
687040ec644SDavid van Moolenbroek const char *bindaddr;
688040ec644SDavid van Moolenbroek const char *filename;
689040ec644SDavid van Moolenbroek size_t filenamelen;
690040ec644SDavid van Moolenbroek int type;
691040ec644SDavid van Moolenbroek int low, pasv, verbose;
692040ec644SDavid van Moolenbroek int e, sd = -1;
693040ec644SDavid van Moolenbroek socklen_t l;
694040ec644SDavid van Moolenbroek char *s;
695040ec644SDavid van Moolenbroek fetchIO *df;
696040ec644SDavid van Moolenbroek
697040ec644SDavid van Moolenbroek /* check flags */
698040ec644SDavid van Moolenbroek low = CHECK_FLAG('l');
699040ec644SDavid van Moolenbroek pasv = !CHECK_FLAG('a');
700040ec644SDavid van Moolenbroek verbose = CHECK_FLAG('v');
701040ec644SDavid van Moolenbroek
702040ec644SDavid van Moolenbroek /* passive mode */
703040ec644SDavid van Moolenbroek if (!pasv)
704040ec644SDavid van Moolenbroek pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL &&
705040ec644SDavid van Moolenbroek strncasecmp(s, "no", 2) != 0);
706040ec644SDavid van Moolenbroek
707040ec644SDavid van Moolenbroek /* isolate filename */
708040ec644SDavid van Moolenbroek filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL);
709040ec644SDavid van Moolenbroek
710040ec644SDavid van Moolenbroek /* set transfer mode and data type */
711040ec644SDavid van Moolenbroek if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK)
712040ec644SDavid van Moolenbroek goto ouch;
713040ec644SDavid van Moolenbroek
714040ec644SDavid van Moolenbroek /* find our own address, bind, and listen */
715040ec644SDavid van Moolenbroek l = sizeof(u.ss);
716040ec644SDavid van Moolenbroek if (getsockname(conn->sd, &u.sa, &l) == -1)
717040ec644SDavid van Moolenbroek goto sysouch;
718040ec644SDavid van Moolenbroek if (u.ss.ss_family == AF_INET6)
719040ec644SDavid van Moolenbroek unmappedaddr(&u.sin6, &l);
720040ec644SDavid van Moolenbroek
721040ec644SDavid van Moolenbroek retry_mode:
722040ec644SDavid van Moolenbroek
723040ec644SDavid van Moolenbroek /* open data socket */
724040ec644SDavid van Moolenbroek if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
725040ec644SDavid van Moolenbroek fetch_syserr();
726040ec644SDavid van Moolenbroek return (NULL);
727040ec644SDavid van Moolenbroek }
728040ec644SDavid van Moolenbroek
729040ec644SDavid van Moolenbroek if (pasv) {
730040ec644SDavid van Moolenbroek unsigned char addr[64];
731040ec644SDavid van Moolenbroek char *ln, *p;
732040ec644SDavid van Moolenbroek unsigned int i;
733040ec644SDavid van Moolenbroek int port;
734040ec644SDavid van Moolenbroek
735040ec644SDavid van Moolenbroek /* send PASV command */
736040ec644SDavid van Moolenbroek if (verbose)
737040ec644SDavid van Moolenbroek fetch_info("setting passive mode");
738040ec644SDavid van Moolenbroek switch (u.ss.ss_family) {
739040ec644SDavid van Moolenbroek case AF_INET:
740040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE)
741040ec644SDavid van Moolenbroek goto ouch;
742040ec644SDavid van Moolenbroek break;
743040ec644SDavid van Moolenbroek case AF_INET6:
744040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) {
745040ec644SDavid van Moolenbroek if (e == -1)
746040ec644SDavid van Moolenbroek goto ouch;
747040ec644SDavid van Moolenbroek if ((e = ftp_cmd(conn, "LPSV\r\n")) !=
748040ec644SDavid van Moolenbroek FTP_LPASSIVE_MODE)
749040ec644SDavid van Moolenbroek goto ouch;
750040ec644SDavid van Moolenbroek }
751040ec644SDavid van Moolenbroek break;
752040ec644SDavid van Moolenbroek default:
753040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
754040ec644SDavid van Moolenbroek goto ouch;
755040ec644SDavid van Moolenbroek }
756040ec644SDavid van Moolenbroek
757040ec644SDavid van Moolenbroek /*
758040ec644SDavid van Moolenbroek * Find address and port number. The reply to the PASV command
759040ec644SDavid van Moolenbroek * is IMHO the one and only weak point in the FTP protocol.
760040ec644SDavid van Moolenbroek */
761040ec644SDavid van Moolenbroek ln = conn->buf;
762040ec644SDavid van Moolenbroek switch (e) {
763040ec644SDavid van Moolenbroek case FTP_PASSIVE_MODE:
764040ec644SDavid van Moolenbroek case FTP_LPASSIVE_MODE:
765040ec644SDavid van Moolenbroek for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
766040ec644SDavid van Moolenbroek /* nothing */ ;
767040ec644SDavid van Moolenbroek if (!*p) {
768040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR;
769040ec644SDavid van Moolenbroek goto ouch;
770040ec644SDavid van Moolenbroek }
771040ec644SDavid van Moolenbroek l = (e == FTP_PASSIVE_MODE ? 6 : 21);
772040ec644SDavid van Moolenbroek for (i = 0; *p && i < l; i++, p++)
773040ec644SDavid van Moolenbroek addr[i] = (unsigned char)strtol(p, &p, 10);
774040ec644SDavid van Moolenbroek if (i < l) {
775040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR;
776040ec644SDavid van Moolenbroek goto ouch;
777040ec644SDavid van Moolenbroek }
778040ec644SDavid van Moolenbroek break;
779040ec644SDavid van Moolenbroek case FTP_EPASSIVE_MODE:
780040ec644SDavid van Moolenbroek for (p = ln + 3; *p && *p != '('; p++)
781040ec644SDavid van Moolenbroek /* nothing */ ;
782040ec644SDavid van Moolenbroek if (!*p) {
783040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR;
784040ec644SDavid van Moolenbroek goto ouch;
785040ec644SDavid van Moolenbroek }
786040ec644SDavid van Moolenbroek ++p;
787040ec644SDavid van Moolenbroek if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
788040ec644SDavid van Moolenbroek &port, &addr[3]) != 5 ||
789040ec644SDavid van Moolenbroek addr[0] != addr[1] ||
790040ec644SDavid van Moolenbroek addr[0] != addr[2] || addr[0] != addr[3]) {
791040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR;
792040ec644SDavid van Moolenbroek goto ouch;
793040ec644SDavid van Moolenbroek }
794040ec644SDavid van Moolenbroek break;
795040ec644SDavid van Moolenbroek case FTP_SYNTAX_ERROR:
796040ec644SDavid van Moolenbroek if (verbose)
797040ec644SDavid van Moolenbroek fetch_info("passive mode failed");
798040ec644SDavid van Moolenbroek /* Close socket and retry with passive mode. */
799040ec644SDavid van Moolenbroek pasv = 0;
800040ec644SDavid van Moolenbroek close(sd);
801040ec644SDavid van Moolenbroek sd = -1;
802040ec644SDavid van Moolenbroek goto retry_mode;
803040ec644SDavid van Moolenbroek }
804040ec644SDavid van Moolenbroek
805040ec644SDavid van Moolenbroek /* seek to required offset */
806040ec644SDavid van Moolenbroek if (offset)
807040ec644SDavid van Moolenbroek if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK)
808040ec644SDavid van Moolenbroek goto sysouch;
809040ec644SDavid van Moolenbroek
810040ec644SDavid van Moolenbroek /* construct sockaddr for data socket */
811040ec644SDavid van Moolenbroek l = sizeof(u.ss);
812040ec644SDavid van Moolenbroek if (getpeername(conn->sd, &u.sa, &l) == -1)
813040ec644SDavid van Moolenbroek goto sysouch;
814040ec644SDavid van Moolenbroek if (u.ss.ss_family == AF_INET6)
815040ec644SDavid van Moolenbroek unmappedaddr(&u.sin6, &l);
816040ec644SDavid van Moolenbroek switch (u.ss.ss_family) {
817040ec644SDavid van Moolenbroek case AF_INET6:
818040ec644SDavid van Moolenbroek if (e == FTP_EPASSIVE_MODE)
819040ec644SDavid van Moolenbroek u.sin6.sin6_port = htons(port);
820040ec644SDavid van Moolenbroek else {
821040ec644SDavid van Moolenbroek memcpy(&u.sin6.sin6_addr, addr + 2, 16);
822040ec644SDavid van Moolenbroek memcpy(&u.sin6.sin6_port, addr + 19, 2);
823040ec644SDavid van Moolenbroek }
824040ec644SDavid van Moolenbroek break;
825040ec644SDavid van Moolenbroek case AF_INET:
826040ec644SDavid van Moolenbroek if (e == FTP_EPASSIVE_MODE)
827040ec644SDavid van Moolenbroek u.sin4.sin_port = htons(port);
828040ec644SDavid van Moolenbroek else {
829040ec644SDavid van Moolenbroek memcpy(&u.sin4.sin_addr, addr, 4);
830040ec644SDavid van Moolenbroek memcpy(&u.sin4.sin_port, addr + 4, 2);
831040ec644SDavid van Moolenbroek }
832040ec644SDavid van Moolenbroek break;
833040ec644SDavid van Moolenbroek default:
834040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
835040ec644SDavid van Moolenbroek break;
836040ec644SDavid van Moolenbroek }
837040ec644SDavid van Moolenbroek
838040ec644SDavid van Moolenbroek /* connect to data port */
839040ec644SDavid van Moolenbroek if (verbose)
840040ec644SDavid van Moolenbroek fetch_info("opening data connection");
841040ec644SDavid van Moolenbroek bindaddr = getenv("FETCH_BIND_ADDRESS");
842040ec644SDavid van Moolenbroek if (bindaddr != NULL && *bindaddr != '\0' &&
843040ec644SDavid van Moolenbroek fetch_bind(sd, u.ss.ss_family, bindaddr) != 0)
844040ec644SDavid van Moolenbroek goto sysouch;
845040ec644SDavid van Moolenbroek if (connect(sd, &u.sa, l) == -1)
846040ec644SDavid van Moolenbroek goto sysouch;
847040ec644SDavid van Moolenbroek
848040ec644SDavid van Moolenbroek /* make the server initiate the transfer */
849040ec644SDavid van Moolenbroek if (verbose)
850040ec644SDavid van Moolenbroek fetch_info("initiating transfer");
851040ec644SDavid van Moolenbroek if (op_arg)
852040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg);
853040ec644SDavid van Moolenbroek else
854040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "%s %.*s\r\n", oper,
855*0a6a1f1dSLionel Sambuc (int)filenamelen, filename);
856040ec644SDavid van Moolenbroek if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
857040ec644SDavid van Moolenbroek goto ouch;
858040ec644SDavid van Moolenbroek
859040ec644SDavid van Moolenbroek } else {
860040ec644SDavid van Moolenbroek uint32_t a;
861040ec644SDavid van Moolenbroek uint16_t p;
862040ec644SDavid van Moolenbroek #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE)
863040ec644SDavid van Moolenbroek int arg;
864040ec644SDavid van Moolenbroek #endif
865040ec644SDavid van Moolenbroek int d;
866040ec644SDavid van Moolenbroek char hname[INET6_ADDRSTRLEN];
867040ec644SDavid van Moolenbroek
868040ec644SDavid van Moolenbroek switch (u.ss.ss_family) {
869040ec644SDavid van Moolenbroek case AF_INET6:
870040ec644SDavid van Moolenbroek u.sin6.sin6_port = 0;
871040ec644SDavid van Moolenbroek #ifdef IPV6_PORTRANGE
872040ec644SDavid van Moolenbroek arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;
873040ec644SDavid van Moolenbroek if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
874040ec644SDavid van Moolenbroek &arg, (socklen_t)sizeof(arg)) == -1)
875040ec644SDavid van Moolenbroek goto sysouch;
876040ec644SDavid van Moolenbroek #endif
877040ec644SDavid van Moolenbroek break;
878040ec644SDavid van Moolenbroek case AF_INET:
879040ec644SDavid van Moolenbroek u.sin4.sin_port = 0;
880040ec644SDavid van Moolenbroek #ifdef IP_PORTRANGE
881040ec644SDavid van Moolenbroek arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;
882040ec644SDavid van Moolenbroek if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
883040ec644SDavid van Moolenbroek &arg, (socklen_t)sizeof(arg)) == -1)
884040ec644SDavid van Moolenbroek goto sysouch;
885040ec644SDavid van Moolenbroek #endif
886040ec644SDavid van Moolenbroek break;
887040ec644SDavid van Moolenbroek }
888040ec644SDavid van Moolenbroek if (verbose)
889040ec644SDavid van Moolenbroek fetch_info("binding data socket");
890040ec644SDavid van Moolenbroek if (bind(sd, &u.sa, l) == -1)
891040ec644SDavid van Moolenbroek goto sysouch;
892040ec644SDavid van Moolenbroek if (listen(sd, 1) == -1)
893040ec644SDavid van Moolenbroek goto sysouch;
894040ec644SDavid van Moolenbroek
895040ec644SDavid van Moolenbroek /* find what port we're on and tell the server */
896040ec644SDavid van Moolenbroek if (getsockname(sd, &u.sa, &l) == -1)
897040ec644SDavid van Moolenbroek goto sysouch;
898040ec644SDavid van Moolenbroek switch (u.ss.ss_family) {
899040ec644SDavid van Moolenbroek case AF_INET:
900040ec644SDavid van Moolenbroek a = ntohl(u.sin4.sin_addr.s_addr);
901040ec644SDavid van Moolenbroek p = ntohs(u.sin4.sin_port);
902040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n",
903040ec644SDavid van Moolenbroek (a >> 24) & 0xff, (a >> 16) & 0xff,
904040ec644SDavid van Moolenbroek (a >> 8) & 0xff, a & 0xff,
905040ec644SDavid van Moolenbroek ((unsigned int)p >> 8) & 0xff, p & 0xff);
906040ec644SDavid van Moolenbroek break;
907040ec644SDavid van Moolenbroek case AF_INET6:
908040ec644SDavid van Moolenbroek e = -1;
909040ec644SDavid van Moolenbroek u.sin6.sin6_scope_id = 0;
910040ec644SDavid van Moolenbroek if (getnameinfo(&u.sa, l,
911040ec644SDavid van Moolenbroek hname, (socklen_t)sizeof(hname),
912040ec644SDavid van Moolenbroek NULL, 0, NI_NUMERICHOST) == 0) {
913040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname,
914040ec644SDavid van Moolenbroek htons(u.sin6.sin6_port));
915040ec644SDavid van Moolenbroek if (e == -1)
916040ec644SDavid van Moolenbroek goto ouch;
917040ec644SDavid van Moolenbroek }
918040ec644SDavid van Moolenbroek if (e != FTP_OK) {
919040ec644SDavid van Moolenbroek uint8_t aa[sizeof(u.sin6.sin6_addr)];
920040ec644SDavid van Moolenbroek memcpy(aa, &u.sin6.sin6_addr, sizeof(aa));
921040ec644SDavid van Moolenbroek p = ntohs(u.sin6.sin6_port);
922040ec644SDavid van Moolenbroek e = ftp_cmd(conn,
923040ec644SDavid van Moolenbroek "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n",
924040ec644SDavid van Moolenbroek 6, 16,
925040ec644SDavid van Moolenbroek aa[ 0], aa[ 1], aa[ 2], aa[ 3],
926040ec644SDavid van Moolenbroek aa[ 4], aa[ 5], aa[ 6], aa[ 7],
927040ec644SDavid van Moolenbroek aa[ 8], aa[ 9], aa[10], aa[11],
928040ec644SDavid van Moolenbroek aa[12], aa[13], aa[14], aa[15],
929040ec644SDavid van Moolenbroek 2,
930040ec644SDavid van Moolenbroek ((unsigned int)p >> 8) & 0xff, p & 0xff);
931040ec644SDavid van Moolenbroek }
932040ec644SDavid van Moolenbroek break;
933040ec644SDavid van Moolenbroek default:
934040ec644SDavid van Moolenbroek e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
935040ec644SDavid van Moolenbroek goto ouch;
936040ec644SDavid van Moolenbroek }
937040ec644SDavid van Moolenbroek if (e != FTP_OK)
938040ec644SDavid van Moolenbroek goto ouch;
939040ec644SDavid van Moolenbroek
940040ec644SDavid van Moolenbroek /* seek to required offset */
941040ec644SDavid van Moolenbroek if (offset)
942040ec644SDavid van Moolenbroek if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK)
943040ec644SDavid van Moolenbroek goto sysouch;
944040ec644SDavid van Moolenbroek
945040ec644SDavid van Moolenbroek /* make the server initiate the transfer */
946040ec644SDavid van Moolenbroek if (verbose)
947040ec644SDavid van Moolenbroek fetch_info("initiating transfer");
948040ec644SDavid van Moolenbroek if (op_arg)
949040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg);
950040ec644SDavid van Moolenbroek else
951040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "%s %.*s\r\n", oper,
952*0a6a1f1dSLionel Sambuc (int)filenamelen, filename);
953040ec644SDavid van Moolenbroek if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
954040ec644SDavid van Moolenbroek goto ouch;
955040ec644SDavid van Moolenbroek
956040ec644SDavid van Moolenbroek /* accept the incoming connection and go to town */
957040ec644SDavid van Moolenbroek if ((d = accept(sd, NULL, NULL)) == -1)
958040ec644SDavid van Moolenbroek goto sysouch;
959040ec644SDavid van Moolenbroek close(sd);
960040ec644SDavid van Moolenbroek sd = d;
961040ec644SDavid van Moolenbroek }
962040ec644SDavid van Moolenbroek
963040ec644SDavid van Moolenbroek if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL)
964040ec644SDavid van Moolenbroek goto sysouch;
965040ec644SDavid van Moolenbroek return (df);
966040ec644SDavid van Moolenbroek
967040ec644SDavid van Moolenbroek sysouch:
968040ec644SDavid van Moolenbroek fetch_syserr();
969040ec644SDavid van Moolenbroek if (sd >= 0)
970040ec644SDavid van Moolenbroek close(sd);
971040ec644SDavid van Moolenbroek return (NULL);
972040ec644SDavid van Moolenbroek
973040ec644SDavid van Moolenbroek ouch:
974040ec644SDavid van Moolenbroek if (e != -1)
975040ec644SDavid van Moolenbroek ftp_seterr(e);
976040ec644SDavid van Moolenbroek if (sd >= 0)
977040ec644SDavid van Moolenbroek close(sd);
978040ec644SDavid van Moolenbroek return (NULL);
979040ec644SDavid van Moolenbroek }
980040ec644SDavid van Moolenbroek
981040ec644SDavid van Moolenbroek /*
982040ec644SDavid van Moolenbroek * Authenticate
983040ec644SDavid van Moolenbroek */
984040ec644SDavid van Moolenbroek static int
ftp_authenticate(conn_t * conn,struct url * url,struct url * purl)985040ec644SDavid van Moolenbroek ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
986040ec644SDavid van Moolenbroek {
987040ec644SDavid van Moolenbroek const char *user, *pwd, *login_name;
988040ec644SDavid van Moolenbroek char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1];
989040ec644SDavid van Moolenbroek int e, len;
990040ec644SDavid van Moolenbroek
991040ec644SDavid van Moolenbroek /* XXX FTP_AUTH, and maybe .netrc */
992040ec644SDavid van Moolenbroek
993040ec644SDavid van Moolenbroek /* send user name and password */
994040ec644SDavid van Moolenbroek if (url->user[0] == '\0')
995040ec644SDavid van Moolenbroek fetch_netrc_auth(url);
996040ec644SDavid van Moolenbroek user = url->user;
997040ec644SDavid van Moolenbroek if (*user == '\0')
998040ec644SDavid van Moolenbroek user = getenv("FTP_LOGIN");
999040ec644SDavid van Moolenbroek if (user == NULL || *user == '\0')
1000040ec644SDavid van Moolenbroek user = FTP_ANONYMOUS_USER;
1001040ec644SDavid van Moolenbroek if (purl && url->port == fetch_default_port(url->scheme))
1002040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host);
1003040ec644SDavid van Moolenbroek else if (purl)
1004040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port);
1005040ec644SDavid van Moolenbroek else
1006040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "USER %s\r\n", user);
1007040ec644SDavid van Moolenbroek
1008040ec644SDavid van Moolenbroek /* did the server request a password? */
1009040ec644SDavid van Moolenbroek if (e == FTP_NEED_PASSWORD) {
1010040ec644SDavid van Moolenbroek pwd = url->pwd;
1011040ec644SDavid van Moolenbroek if (*pwd == '\0')
1012040ec644SDavid van Moolenbroek pwd = getenv("FTP_PASSWORD");
1013040ec644SDavid van Moolenbroek if (pwd == NULL || *pwd == '\0') {
1014040ec644SDavid van Moolenbroek if ((login_name = getlogin()) == 0)
1015040ec644SDavid van Moolenbroek login_name = FTP_ANONYMOUS_USER;
1016040ec644SDavid van Moolenbroek if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0)
1017040ec644SDavid van Moolenbroek len = 0;
1018040ec644SDavid van Moolenbroek else if (len > URL_USERLEN + 1)
1019040ec644SDavid van Moolenbroek len = URL_USERLEN + 1;
1020040ec644SDavid van Moolenbroek gethostname(pbuf + len, sizeof(pbuf) - len);
1021040ec644SDavid van Moolenbroek /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */
1022040ec644SDavid van Moolenbroek pbuf[sizeof(pbuf) - 1] = '\0';
1023040ec644SDavid van Moolenbroek pwd = pbuf;
1024040ec644SDavid van Moolenbroek }
1025040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "PASS %s\r\n", pwd);
1026040ec644SDavid van Moolenbroek }
1027040ec644SDavid van Moolenbroek
1028040ec644SDavid van Moolenbroek return (e);
1029040ec644SDavid van Moolenbroek }
1030040ec644SDavid van Moolenbroek
1031040ec644SDavid van Moolenbroek /*
1032040ec644SDavid van Moolenbroek * Log on to FTP server
1033040ec644SDavid van Moolenbroek */
1034040ec644SDavid van Moolenbroek static conn_t *
ftp_connect(struct url * url,struct url * purl,const char * flags)1035040ec644SDavid van Moolenbroek ftp_connect(struct url *url, struct url *purl, const char *flags)
1036040ec644SDavid van Moolenbroek {
1037040ec644SDavid van Moolenbroek conn_t *conn;
1038040ec644SDavid van Moolenbroek int e, direct, verbose;
1039040ec644SDavid van Moolenbroek #ifdef INET6
1040040ec644SDavid van Moolenbroek int af = AF_UNSPEC;
1041040ec644SDavid van Moolenbroek #else
1042040ec644SDavid van Moolenbroek int af = AF_INET;
1043040ec644SDavid van Moolenbroek #endif
1044040ec644SDavid van Moolenbroek
1045040ec644SDavid van Moolenbroek direct = CHECK_FLAG('d');
1046040ec644SDavid van Moolenbroek verbose = CHECK_FLAG('v');
1047040ec644SDavid van Moolenbroek if (CHECK_FLAG('4'))
1048040ec644SDavid van Moolenbroek af = AF_INET;
1049040ec644SDavid van Moolenbroek else if (CHECK_FLAG('6'))
1050040ec644SDavid van Moolenbroek af = AF_INET6;
1051040ec644SDavid van Moolenbroek
1052040ec644SDavid van Moolenbroek if (direct)
1053040ec644SDavid van Moolenbroek purl = NULL;
1054040ec644SDavid van Moolenbroek
1055040ec644SDavid van Moolenbroek /* check for proxy */
1056040ec644SDavid van Moolenbroek if (purl) {
1057040ec644SDavid van Moolenbroek /* XXX proxy authentication! */
1058040ec644SDavid van Moolenbroek /* XXX connetion caching */
1059040ec644SDavid van Moolenbroek if (!purl->port)
1060040ec644SDavid van Moolenbroek purl->port = fetch_default_port(purl->scheme);
1061040ec644SDavid van Moolenbroek
1062040ec644SDavid van Moolenbroek conn = fetch_connect(purl, af, verbose);
1063040ec644SDavid van Moolenbroek } else {
1064040ec644SDavid van Moolenbroek /* no proxy, go straight to target */
1065040ec644SDavid van Moolenbroek if (!url->port)
1066040ec644SDavid van Moolenbroek url->port = fetch_default_port(url->scheme);
1067040ec644SDavid van Moolenbroek
1068040ec644SDavid van Moolenbroek while ((conn = fetch_cache_get(url, af)) != NULL) {
1069040ec644SDavid van Moolenbroek e = ftp_cmd(conn, "NOOP\r\n");
1070040ec644SDavid van Moolenbroek if (e == FTP_OK)
1071040ec644SDavid van Moolenbroek return conn;
1072040ec644SDavid van Moolenbroek fetch_close(conn);
1073040ec644SDavid van Moolenbroek }
1074040ec644SDavid van Moolenbroek conn = fetch_connect(url, af, verbose);
1075040ec644SDavid van Moolenbroek purl = NULL;
1076040ec644SDavid van Moolenbroek }
1077040ec644SDavid van Moolenbroek
1078040ec644SDavid van Moolenbroek /* check connection */
1079040ec644SDavid van Moolenbroek if (conn == NULL)
1080040ec644SDavid van Moolenbroek /* fetch_connect() has already set an error code */
1081040ec644SDavid van Moolenbroek return (NULL);
1082040ec644SDavid van Moolenbroek
1083040ec644SDavid van Moolenbroek /* expect welcome message */
1084040ec644SDavid van Moolenbroek if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY)
1085040ec644SDavid van Moolenbroek goto fouch;
1086040ec644SDavid van Moolenbroek
1087040ec644SDavid van Moolenbroek /* authenticate */
1088040ec644SDavid van Moolenbroek if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
1089040ec644SDavid van Moolenbroek goto fouch;
1090040ec644SDavid van Moolenbroek
1091040ec644SDavid van Moolenbroek /* TODO: Request extended features supported, if any (RFC 3659). */
1092040ec644SDavid van Moolenbroek
1093040ec644SDavid van Moolenbroek /* done */
1094040ec644SDavid van Moolenbroek return (conn);
1095040ec644SDavid van Moolenbroek
1096040ec644SDavid van Moolenbroek fouch:
1097040ec644SDavid van Moolenbroek if (e != -1)
1098040ec644SDavid van Moolenbroek ftp_seterr(e);
1099040ec644SDavid van Moolenbroek fetch_close(conn);
1100040ec644SDavid van Moolenbroek return (NULL);
1101040ec644SDavid van Moolenbroek }
1102040ec644SDavid van Moolenbroek
1103040ec644SDavid van Moolenbroek /*
1104040ec644SDavid van Moolenbroek * Check the proxy settings
1105040ec644SDavid van Moolenbroek */
1106040ec644SDavid van Moolenbroek static struct url *
ftp_get_proxy(struct url * url,const char * flags)1107040ec644SDavid van Moolenbroek ftp_get_proxy(struct url * url, const char *flags)
1108040ec644SDavid van Moolenbroek {
1109040ec644SDavid van Moolenbroek struct url *purl;
1110040ec644SDavid van Moolenbroek char *p, *fp, *FP, *hp, *HP;
1111040ec644SDavid van Moolenbroek
1112040ec644SDavid van Moolenbroek if (flags != NULL && strchr(flags, 'd') != NULL)
1113040ec644SDavid van Moolenbroek return NULL;
1114040ec644SDavid van Moolenbroek if (fetch_no_proxy_match(url->host))
1115040ec644SDavid van Moolenbroek return NULL;
1116040ec644SDavid van Moolenbroek
1117040ec644SDavid van Moolenbroek FP = getenv("FTP_PROXY");
1118040ec644SDavid van Moolenbroek fp = getenv("ftp_proxy");
1119040ec644SDavid van Moolenbroek HP = getenv("HTTP_PROXY");
1120040ec644SDavid van Moolenbroek hp = getenv("http_proxy");
1121040ec644SDavid van Moolenbroek
1122040ec644SDavid van Moolenbroek if ((((p = FP) || (p = fp) || (p = HP) || (p = hp))) &&
1123040ec644SDavid van Moolenbroek *p && (purl = fetchParseURL(p)) != NULL) {
1124040ec644SDavid van Moolenbroek if (!*purl->scheme) {
1125040ec644SDavid van Moolenbroek if (fp || FP)
1126040ec644SDavid van Moolenbroek strcpy(purl->scheme, SCHEME_FTP);
1127040ec644SDavid van Moolenbroek else
1128040ec644SDavid van Moolenbroek strcpy(purl->scheme, SCHEME_HTTP);
1129040ec644SDavid van Moolenbroek }
1130040ec644SDavid van Moolenbroek if (!purl->port)
1131040ec644SDavid van Moolenbroek purl->port = fetch_default_proxy_port(purl->scheme);
1132040ec644SDavid van Moolenbroek if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
1133040ec644SDavid van Moolenbroek strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
1134040ec644SDavid van Moolenbroek return purl;
1135040ec644SDavid van Moolenbroek fetchFreeURL(purl);
1136040ec644SDavid van Moolenbroek }
1137040ec644SDavid van Moolenbroek return NULL;
1138040ec644SDavid van Moolenbroek }
1139040ec644SDavid van Moolenbroek
1140040ec644SDavid van Moolenbroek /*
1141040ec644SDavid van Moolenbroek * Process an FTP request
1142040ec644SDavid van Moolenbroek */
1143040ec644SDavid van Moolenbroek fetchIO *
ftp_request(struct url * url,const char * op,const char * op_arg,struct url_stat * us,struct url * purl,const char * flags)1144040ec644SDavid van Moolenbroek ftp_request(struct url *url, const char *op, const char *op_arg,
1145040ec644SDavid van Moolenbroek struct url_stat *us, struct url *purl, const char *flags)
1146040ec644SDavid van Moolenbroek {
1147040ec644SDavid van Moolenbroek fetchIO *f;
1148040ec644SDavid van Moolenbroek char *path;
1149040ec644SDavid van Moolenbroek conn_t *conn;
1150040ec644SDavid van Moolenbroek int if_modified_since, oflag;
1151040ec644SDavid van Moolenbroek struct url_stat local_us;
1152040ec644SDavid van Moolenbroek
1153040ec644SDavid van Moolenbroek /* check if we should use HTTP instead */
1154040ec644SDavid van Moolenbroek if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
1155040ec644SDavid van Moolenbroek if (strcmp(op, "STAT") == 0)
1156040ec644SDavid van Moolenbroek return (http_request(url, "HEAD", us, purl, flags));
1157040ec644SDavid van Moolenbroek else if (strcmp(op, "RETR") == 0)
1158040ec644SDavid van Moolenbroek return (http_request(url, "GET", us, purl, flags));
1159040ec644SDavid van Moolenbroek /*
1160040ec644SDavid van Moolenbroek * Our HTTP code doesn't support PUT requests yet, so try
1161040ec644SDavid van Moolenbroek * a direct connection.
1162040ec644SDavid van Moolenbroek */
1163040ec644SDavid van Moolenbroek }
1164040ec644SDavid van Moolenbroek
1165040ec644SDavid van Moolenbroek /* connect to server */
1166040ec644SDavid van Moolenbroek conn = ftp_connect(url, purl, flags);
1167040ec644SDavid van Moolenbroek if (purl)
1168040ec644SDavid van Moolenbroek fetchFreeURL(purl);
1169040ec644SDavid van Moolenbroek if (conn == NULL)
1170040ec644SDavid van Moolenbroek return (NULL);
1171040ec644SDavid van Moolenbroek
1172040ec644SDavid van Moolenbroek if ((path = fetchUnquotePath(url)) == NULL) {
1173040ec644SDavid van Moolenbroek fetch_syserr();
1174040ec644SDavid van Moolenbroek return NULL;
1175040ec644SDavid van Moolenbroek }
1176040ec644SDavid van Moolenbroek
1177040ec644SDavid van Moolenbroek /* change directory */
1178040ec644SDavid van Moolenbroek if (ftp_cwd(conn, path, op_arg != NULL) == -1) {
1179040ec644SDavid van Moolenbroek free(path);
1180040ec644SDavid van Moolenbroek return (NULL);
1181040ec644SDavid van Moolenbroek }
1182040ec644SDavid van Moolenbroek
1183040ec644SDavid van Moolenbroek if_modified_since = CHECK_FLAG('i');
1184040ec644SDavid van Moolenbroek if (if_modified_since && us == NULL)
1185040ec644SDavid van Moolenbroek us = &local_us;
1186040ec644SDavid van Moolenbroek
1187040ec644SDavid van Moolenbroek /* stat file */
1188040ec644SDavid van Moolenbroek if (us && ftp_stat(conn, path, us) == -1
1189040ec644SDavid van Moolenbroek && fetchLastErrCode != FETCH_PROTO
1190040ec644SDavid van Moolenbroek && fetchLastErrCode != FETCH_UNAVAIL) {
1191040ec644SDavid van Moolenbroek free(path);
1192040ec644SDavid van Moolenbroek return (NULL);
1193040ec644SDavid van Moolenbroek }
1194040ec644SDavid van Moolenbroek
1195040ec644SDavid van Moolenbroek if (if_modified_since && url->last_modified > 0 &&
1196040ec644SDavid van Moolenbroek url->last_modified >= us->mtime) {
1197040ec644SDavid van Moolenbroek free(path);
1198040ec644SDavid van Moolenbroek fetchLastErrCode = FETCH_UNCHANGED;
1199040ec644SDavid van Moolenbroek snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged");
1200040ec644SDavid van Moolenbroek return NULL;
1201040ec644SDavid van Moolenbroek }
1202040ec644SDavid van Moolenbroek
1203040ec644SDavid van Moolenbroek /* just a stat */
1204040ec644SDavid van Moolenbroek if (strcmp(op, "STAT") == 0) {
1205040ec644SDavid van Moolenbroek free(path);
1206040ec644SDavid van Moolenbroek return fetchIO_unopen(NULL, NULL, NULL, NULL);
1207040ec644SDavid van Moolenbroek }
1208040ec644SDavid van Moolenbroek if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0)
1209040ec644SDavid van Moolenbroek oflag = O_WRONLY;
1210040ec644SDavid van Moolenbroek else
1211040ec644SDavid van Moolenbroek oflag = O_RDONLY;
1212040ec644SDavid van Moolenbroek
1213040ec644SDavid van Moolenbroek /* initiate the transfer */
1214040ec644SDavid van Moolenbroek f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags));
1215040ec644SDavid van Moolenbroek free(path);
1216040ec644SDavid van Moolenbroek return f;
1217040ec644SDavid van Moolenbroek }
1218040ec644SDavid van Moolenbroek
1219040ec644SDavid van Moolenbroek /*
1220040ec644SDavid van Moolenbroek * Get and stat file
1221040ec644SDavid van Moolenbroek */
1222040ec644SDavid van Moolenbroek fetchIO *
fetchXGetFTP(struct url * url,struct url_stat * us,const char * flags)1223040ec644SDavid van Moolenbroek fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
1224040ec644SDavid van Moolenbroek {
1225040ec644SDavid van Moolenbroek return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags));
1226040ec644SDavid van Moolenbroek }
1227040ec644SDavid van Moolenbroek
1228040ec644SDavid van Moolenbroek /*
1229040ec644SDavid van Moolenbroek * Get file
1230040ec644SDavid van Moolenbroek */
1231040ec644SDavid van Moolenbroek fetchIO *
fetchGetFTP(struct url * url,const char * flags)1232040ec644SDavid van Moolenbroek fetchGetFTP(struct url *url, const char *flags)
1233040ec644SDavid van Moolenbroek {
1234040ec644SDavid van Moolenbroek return (fetchXGetFTP(url, NULL, flags));
1235040ec644SDavid van Moolenbroek }
1236040ec644SDavid van Moolenbroek
1237040ec644SDavid van Moolenbroek /*
1238040ec644SDavid van Moolenbroek * Put file
1239040ec644SDavid van Moolenbroek */
1240040ec644SDavid van Moolenbroek fetchIO *
fetchPutFTP(struct url * url,const char * flags)1241040ec644SDavid van Moolenbroek fetchPutFTP(struct url *url, const char *flags)
1242040ec644SDavid van Moolenbroek {
1243040ec644SDavid van Moolenbroek return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL,
1244040ec644SDavid van Moolenbroek ftp_get_proxy(url, flags), flags));
1245040ec644SDavid van Moolenbroek }
1246040ec644SDavid van Moolenbroek
1247040ec644SDavid van Moolenbroek /*
1248040ec644SDavid van Moolenbroek * Get file stats
1249040ec644SDavid van Moolenbroek */
1250040ec644SDavid van Moolenbroek int
fetchStatFTP(struct url * url,struct url_stat * us,const char * flags)1251040ec644SDavid van Moolenbroek fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
1252040ec644SDavid van Moolenbroek {
1253040ec644SDavid van Moolenbroek fetchIO *f;
1254040ec644SDavid van Moolenbroek
1255040ec644SDavid van Moolenbroek f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags);
1256040ec644SDavid van Moolenbroek if (f == NULL)
1257040ec644SDavid van Moolenbroek return (-1);
1258040ec644SDavid van Moolenbroek fetchIO_close(f);
1259040ec644SDavid van Moolenbroek return (0);
1260040ec644SDavid van Moolenbroek }
1261040ec644SDavid van Moolenbroek
1262040ec644SDavid van Moolenbroek /*
1263040ec644SDavid van Moolenbroek * List a directory
1264040ec644SDavid van Moolenbroek */
1265040ec644SDavid van Moolenbroek int
fetchListFTP(struct url_list * ue,struct url * url,const char * pattern,const char * flags)1266040ec644SDavid van Moolenbroek fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags)
1267040ec644SDavid van Moolenbroek {
1268040ec644SDavid van Moolenbroek fetchIO *f;
1269040ec644SDavid van Moolenbroek char buf[2 * PATH_MAX], *eol, *eos;
1270040ec644SDavid van Moolenbroek ssize_t len;
1271040ec644SDavid van Moolenbroek size_t cur_off;
1272040ec644SDavid van Moolenbroek int ret;
1273040ec644SDavid van Moolenbroek
1274040ec644SDavid van Moolenbroek /* XXX What about proxies? */
1275040ec644SDavid van Moolenbroek if (pattern == NULL || strcmp(pattern, "*") == 0)
1276040ec644SDavid van Moolenbroek pattern = "";
1277040ec644SDavid van Moolenbroek f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags);
1278040ec644SDavid van Moolenbroek if (f == NULL)
1279040ec644SDavid van Moolenbroek return -1;
1280040ec644SDavid van Moolenbroek
1281040ec644SDavid van Moolenbroek cur_off = 0;
1282040ec644SDavid van Moolenbroek ret = 0;
1283040ec644SDavid van Moolenbroek
1284040ec644SDavid van Moolenbroek while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) {
1285040ec644SDavid van Moolenbroek cur_off += len;
1286040ec644SDavid van Moolenbroek while ((eol = memchr(buf, '\n', cur_off)) != NULL) {
1287040ec644SDavid van Moolenbroek if (len == eol - buf)
1288040ec644SDavid van Moolenbroek break;
1289040ec644SDavid van Moolenbroek if (eol != buf) {
1290040ec644SDavid van Moolenbroek if (eol[-1] == '\r')
1291040ec644SDavid van Moolenbroek eos = eol - 1;
1292040ec644SDavid van Moolenbroek else
1293040ec644SDavid van Moolenbroek eos = eol;
1294040ec644SDavid van Moolenbroek *eos = '\0';
1295040ec644SDavid van Moolenbroek ret = fetch_add_entry(ue, url, buf, 0);
1296040ec644SDavid van Moolenbroek if (ret)
1297040ec644SDavid van Moolenbroek break;
1298040ec644SDavid van Moolenbroek cur_off -= eol - buf + 1;
1299040ec644SDavid van Moolenbroek memmove(buf, eol + 1, cur_off);
1300040ec644SDavid van Moolenbroek }
1301040ec644SDavid van Moolenbroek }
1302040ec644SDavid van Moolenbroek if (ret)
1303040ec644SDavid van Moolenbroek break;
1304040ec644SDavid van Moolenbroek }
1305040ec644SDavid van Moolenbroek if (cur_off != 0 || len < 0) {
1306040ec644SDavid van Moolenbroek /* Not RFC conform, bail out. */
1307040ec644SDavid van Moolenbroek fetchIO_close(f);
1308040ec644SDavid van Moolenbroek return -1;
1309040ec644SDavid van Moolenbroek }
1310040ec644SDavid van Moolenbroek fetchIO_close(f);
1311040ec644SDavid van Moolenbroek return ret;
1312040ec644SDavid van Moolenbroek }
1313