| @@ -1,1002 +1,1003 @@ | | | @@ -1,1002 +1,1003 @@ |
1 | /* $NetBSD: ftp.c,v 1.16 2008/04/17 08:35:20 joerg Exp $ */ | | 1 | /* $NetBSD: ftp.c,v 1.17 2008/04/17 19:04:12 joerg Exp $ */ |
2 | /*- | | 2 | /*- |
3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav | | 3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav |
| | | 4 | * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org> |
4 | * All rights reserved. | | 5 | * All rights reserved. |
5 | * | | 6 | * |
6 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
8 | * are met: | | 9 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer | | 11 | * notice, this list of conditions and the following disclaimer |
11 | * in this position and unchanged. | | 12 | * in this position and unchanged. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 14 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 15 | * documentation and/or other materials provided with the distribution. |
15 | * 3. The name of the author may not be used to endorse or promote products | | 16 | * 3. The name of the author may not be used to endorse or promote products |
16 | * derived from this software without specific prior written permission | | 17 | * derived from this software without specific prior written permission |
17 | * | | 18 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | | 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | 24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | | 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | * | | 29 | * |
29 | * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $ | | 30 | * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $ |
30 | */ | | 31 | */ |
31 | | | 32 | |
32 | /* | | 33 | /* |
33 | * Portions of this code were taken from or based on ftpio.c: | | 34 | * Portions of this code were taken from or based on ftpio.c: |
34 | * | | 35 | * |
35 | * ---------------------------------------------------------------------------- | | 36 | * ---------------------------------------------------------------------------- |
36 | * "THE BEER-WARE LICENSE" (Revision 42): | | 37 | * "THE BEER-WARE LICENSE" (Revision 42): |
37 | * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you | | 38 | * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you |
38 | * can do whatever you want with this stuff. If we meet some day, and you think | | 39 | * can do whatever you want with this stuff. If we meet some day, and you think |
39 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp | | 40 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
40 | * ---------------------------------------------------------------------------- | | 41 | * ---------------------------------------------------------------------------- |
41 | * | | 42 | * |
42 | * Major Changelog: | | 43 | * Major Changelog: |
43 | * | | 44 | * |
44 | * Dag-Erling Coïdan Smørgrav | | 45 | * Dag-Erling Coïdan Smørgrav |
45 | * 9 Jun 1998 | | 46 | * 9 Jun 1998 |
46 | * | | 47 | * |
47 | * Incorporated into libfetch | | 48 | * Incorporated into libfetch |
48 | * | | 49 | * |
49 | * Jordan K. Hubbard | | 50 | * Jordan K. Hubbard |
50 | * 17 Jan 1996 | | 51 | * 17 Jan 1996 |
51 | * | | 52 | * |
52 | * Turned inside out. Now returns xfers as new file ids, not as a special | | 53 | * Turned inside out. Now returns xfers as new file ids, not as a special |
53 | * `state' of FTP_t | | 54 | * `state' of FTP_t |
54 | * | | 55 | * |
55 | * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ | | 56 | * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ |
56 | * | | 57 | * |
57 | */ | | 58 | */ |
58 | | | 59 | |
59 | #if HAVE_CONFIG_H | | 60 | #if HAVE_CONFIG_H |
60 | #include "config.h" | | 61 | #include "config.h" |
61 | #endif | | 62 | #endif |
62 | #include <nbcompat.h> | | 63 | #include <nbcompat.h> |
63 | | | 64 | |
64 | #include <sys/types.h> | | 65 | #include <sys/types.h> |
65 | #include <sys/socket.h> | | 66 | #include <sys/socket.h> |
66 | #include <netinet/in.h> | | 67 | #include <netinet/in.h> |
67 | | | 68 | |
68 | #include <ctype.h> | | 69 | #include <ctype.h> |
69 | #include <errno.h> | | 70 | #include <errno.h> |
70 | #include <fcntl.h> | | 71 | #include <fcntl.h> |
71 | #include <inttypes.h> | | 72 | #include <inttypes.h> |
72 | #include <netdb.h> | | 73 | #include <netdb.h> |
73 | #include <stdarg.h> | | 74 | #include <stdarg.h> |
74 | #ifndef NETBSD | | 75 | #ifndef NETBSD |
75 | #include <nbcompat/stdio.h> | | 76 | #include <nbcompat/stdio.h> |
76 | #else | | 77 | #else |
77 | #include <stdio.h> | | 78 | #include <stdio.h> |
78 | #endif | | 79 | #endif |
79 | #include <stdlib.h> | | 80 | #include <stdlib.h> |
80 | #include <string.h> | | 81 | #include <string.h> |
81 | #include <time.h> | | 82 | #include <time.h> |
82 | #include <unistd.h> | | 83 | #include <unistd.h> |
83 | | | 84 | |
84 | #include "fetch.h" | | 85 | #include "fetch.h" |
85 | #include "common.h" | | 86 | #include "common.h" |
86 | #include "ftperr.h" | | 87 | #include "ftperr.h" |
87 | | | 88 | |
88 | #define FTP_ANONYMOUS_USER "anonymous" | | 89 | #define FTP_ANONYMOUS_USER "anonymous" |
89 | | | 90 | |
90 | #define FTP_CONNECTION_ALREADY_OPEN 125 | | 91 | #define FTP_CONNECTION_ALREADY_OPEN 125 |
91 | #define FTP_OPEN_DATA_CONNECTION 150 | | 92 | #define FTP_OPEN_DATA_CONNECTION 150 |
92 | #define FTP_OK 200 | | 93 | #define FTP_OK 200 |
93 | #define FTP_FILE_STATUS 213 | | 94 | #define FTP_FILE_STATUS 213 |
94 | #define FTP_SERVICE_READY 220 | | 95 | #define FTP_SERVICE_READY 220 |
95 | #define FTP_TRANSFER_COMPLETE 226 | | 96 | #define FTP_TRANSFER_COMPLETE 226 |
96 | #define FTP_PASSIVE_MODE 227 | | 97 | #define FTP_PASSIVE_MODE 227 |
97 | #define FTP_LPASSIVE_MODE 228 | | 98 | #define FTP_LPASSIVE_MODE 228 |
98 | #define FTP_EPASSIVE_MODE 229 | | 99 | #define FTP_EPASSIVE_MODE 229 |
99 | #define FTP_LOGGED_IN 230 | | 100 | #define FTP_LOGGED_IN 230 |
100 | #define FTP_FILE_ACTION_OK 250 | | 101 | #define FTP_FILE_ACTION_OK 250 |
101 | #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ | | 102 | #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ |
102 | #define FTP_FILE_CREATED 257 /* multiple meanings */ | | 103 | #define FTP_FILE_CREATED 257 /* multiple meanings */ |
103 | #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ | | 104 | #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ |
104 | #define FTP_NEED_PASSWORD 331 | | 105 | #define FTP_NEED_PASSWORD 331 |
105 | #define FTP_NEED_ACCOUNT 332 | | 106 | #define FTP_NEED_ACCOUNT 332 |
106 | #define FTP_FILE_OK 350 | | 107 | #define FTP_FILE_OK 350 |
107 | #define FTP_SYNTAX_ERROR 500 | | 108 | #define FTP_SYNTAX_ERROR 500 |
108 | #define FTP_PROTOCOL_ERROR 999 | | 109 | #define FTP_PROTOCOL_ERROR 999 |
109 | | | 110 | |
110 | static struct url cached_host; | | 111 | static struct url cached_host; |
111 | static conn_t *cached_connection; | | 112 | static conn_t *cached_connection; |
112 | | | 113 | |
113 | #define isftpreply(foo) \ | | 114 | #define isftpreply(foo) \ |
114 | (isdigit((unsigned char)foo[0]) && \ | | 115 | (isdigit((unsigned char)foo[0]) && \ |
115 | isdigit((unsigned char)foo[1]) && \ | | 116 | isdigit((unsigned char)foo[1]) && \ |
116 | isdigit((unsigned char)foo[2]) && \ | | 117 | isdigit((unsigned char)foo[2]) && \ |
117 | (foo[3] == ' ' || foo[3] == '\0')) | | 118 | (foo[3] == ' ' || foo[3] == '\0')) |
118 | #define isftpinfo(foo) \ | | 119 | #define isftpinfo(foo) \ |
119 | (isdigit((unsigned char)foo[0]) && \ | | 120 | (isdigit((unsigned char)foo[0]) && \ |
120 | isdigit((unsigned char)foo[1]) && \ | | 121 | isdigit((unsigned char)foo[1]) && \ |
121 | isdigit((unsigned char)foo[2]) && \ | | 122 | isdigit((unsigned char)foo[2]) && \ |
122 | foo[3] == '-') | | 123 | foo[3] == '-') |
123 | | | 124 | |
124 | /* | | 125 | /* |
125 | * Translate IPv4 mapped IPv6 address to IPv4 address | | 126 | * Translate IPv4 mapped IPv6 address to IPv4 address |
126 | */ | | 127 | */ |
127 | static void | | 128 | static void |
128 | unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len) | | 129 | unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len) |
129 | { | | 130 | { |
130 | struct sockaddr_in *sin4; | | 131 | struct sockaddr_in *sin4; |
131 | uint32_t addr; | | 132 | uint32_t addr; |
132 | int port; | | 133 | int port; |
133 | | | 134 | |
134 | if (sin6->sin6_family != AF_INET6 || | | 135 | if (sin6->sin6_family != AF_INET6 || |
135 | !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) | | 136 | !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) |
136 | return; | | 137 | return; |
137 | sin4 = (struct sockaddr_in *)sin6; | | 138 | sin4 = (struct sockaddr_in *)sin6; |
138 | addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12]; | | 139 | addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12]; |
139 | port = sin6->sin6_port; | | 140 | port = sin6->sin6_port; |
140 | memset(sin4, 0, sizeof(struct sockaddr_in)); | | 141 | memset(sin4, 0, sizeof(struct sockaddr_in)); |
141 | sin4->sin_addr.s_addr = addr; | | 142 | sin4->sin_addr.s_addr = addr; |
142 | sin4->sin_port = port; | | 143 | sin4->sin_port = port; |
143 | sin4->sin_family = AF_INET; | | 144 | sin4->sin_family = AF_INET; |
144 | *len = sizeof(struct sockaddr_in); | | 145 | *len = sizeof(struct sockaddr_in); |
145 | #ifdef HAVE_SA_LEN | | 146 | #ifdef HAVE_SA_LEN |
146 | sin4->sin_len = sizeof(struct sockaddr_in); | | 147 | sin4->sin_len = sizeof(struct sockaddr_in); |
147 | #endif | | 148 | #endif |
148 | } | | 149 | } |
149 | | | 150 | |
150 | /* | | 151 | /* |
151 | * Get server response | | 152 | * Get server response |
152 | */ | | 153 | */ |
153 | static int | | 154 | static int |
154 | ftp_chkerr(conn_t *conn) | | 155 | ftp_chkerr(conn_t *conn) |
155 | { | | 156 | { |
156 | if (fetch_getln(conn) == -1) { | | 157 | if (fetch_getln(conn) == -1) { |
157 | fetch_syserr(); | | 158 | fetch_syserr(); |
158 | return (-1); | | 159 | return (-1); |
159 | } | | 160 | } |
160 | if (isftpinfo(conn->buf)) { | | 161 | if (isftpinfo(conn->buf)) { |
161 | while (conn->buflen && !isftpreply(conn->buf)) { | | 162 | while (conn->buflen && !isftpreply(conn->buf)) { |
162 | if (fetch_getln(conn) == -1) { | | 163 | if (fetch_getln(conn) == -1) { |
163 | fetch_syserr(); | | 164 | fetch_syserr(); |
164 | return (-1); | | 165 | return (-1); |
165 | } | | 166 | } |
166 | } | | 167 | } |
167 | } | | 168 | } |
168 | | | 169 | |
169 | while (conn->buflen && | | 170 | while (conn->buflen && |
170 | isspace((unsigned char)conn->buf[conn->buflen - 1])) | | 171 | isspace((unsigned char)conn->buf[conn->buflen - 1])) |
171 | conn->buflen--; | | 172 | conn->buflen--; |
172 | conn->buf[conn->buflen] = '\0'; | | 173 | conn->buf[conn->buflen] = '\0'; |
173 | | | 174 | |
174 | if (!isftpreply(conn->buf)) { | | 175 | if (!isftpreply(conn->buf)) { |
175 | ftp_seterr(FTP_PROTOCOL_ERROR); | | 176 | ftp_seterr(FTP_PROTOCOL_ERROR); |
176 | return (-1); | | 177 | return (-1); |
177 | } | | 178 | } |
178 | | | 179 | |
179 | conn->err = (conn->buf[0] - '0') * 100 | | 180 | conn->err = (conn->buf[0] - '0') * 100 |
180 | + (conn->buf[1] - '0') * 10 | | 181 | + (conn->buf[1] - '0') * 10 |
181 | + (conn->buf[2] - '0'); | | 182 | + (conn->buf[2] - '0'); |
182 | | | 183 | |
183 | return (conn->err); | | 184 | return (conn->err); |
184 | } | | 185 | } |
185 | | | 186 | |
186 | /* | | 187 | /* |
187 | * Send a command and check reply | | 188 | * Send a command and check reply |
188 | */ | | 189 | */ |
189 | static int | | 190 | static int |
190 | ftp_cmd(conn_t *conn, const char *fmt, ...) | | 191 | ftp_cmd(conn_t *conn, const char *fmt, ...) |
191 | { | | 192 | { |
192 | va_list ap; | | 193 | va_list ap; |
193 | size_t len; | | 194 | size_t len; |
194 | char *msg; | | 195 | char *msg; |
195 | int r; | | 196 | int r; |
196 | | | 197 | |
197 | va_start(ap, fmt); | | 198 | va_start(ap, fmt); |
198 | len = vasprintf(&msg, fmt, ap); | | 199 | len = vasprintf(&msg, fmt, ap); |
199 | va_end(ap); | | 200 | va_end(ap); |
200 | | | 201 | |
201 | if (msg == NULL) { | | 202 | if (msg == NULL) { |
202 | errno = ENOMEM; | | 203 | errno = ENOMEM; |
203 | fetch_syserr(); | | 204 | fetch_syserr(); |
204 | return (-1); | | 205 | return (-1); |
205 | } | | 206 | } |
206 | | | 207 | |
207 | r = fetch_putln(conn, msg, len); | | 208 | r = fetch_putln(conn, msg, len); |
208 | free(msg); | | 209 | free(msg); |
209 | | | 210 | |
210 | if (r == -1) { | | 211 | if (r == -1) { |
211 | fetch_syserr(); | | 212 | fetch_syserr(); |
212 | return (-1); | | 213 | return (-1); |
213 | } | | 214 | } |
214 | | | 215 | |
215 | return (ftp_chkerr(conn)); | | 216 | return (ftp_chkerr(conn)); |
216 | } | | 217 | } |
217 | | | 218 | |
218 | /* | | 219 | /* |
219 | * Return a pointer to the filename part of a path | | 220 | * Return a pointer to the filename part of a path |
220 | */ | | 221 | */ |
221 | static const char * | | 222 | static const char * |
222 | ftp_filename(const char *file, int *len, int *type, int subdir) | | 223 | ftp_filename(const char *file, int *len, int *type, int subdir) |
223 | { | | 224 | { |
224 | const char *s; | | 225 | const char *s; |
225 | | | 226 | |
226 | if ((s = strrchr(file, '/')) == NULL || subdir) | | 227 | if ((s = strrchr(file, '/')) == NULL || subdir) |
227 | s = file; | | 228 | s = file; |
228 | else | | 229 | else |
229 | s = s + 1; | | 230 | s = s + 1; |
230 | *len = strlen(s); | | 231 | *len = strlen(s); |
231 | if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { | | 232 | if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { |
232 | *type = s[*len - 1]; | | 233 | *type = s[*len - 1]; |
233 | *len -= 7; | | 234 | *len -= 7; |
234 | } else { | | 235 | } else { |
235 | *type = '\0'; | | 236 | *type = '\0'; |
236 | } | | 237 | } |
237 | return (s); | | 238 | return (s); |
238 | } | | 239 | } |
239 | | | 240 | |
240 | /* | | 241 | /* |
241 | * Get current working directory from the reply to a CWD, PWD or CDUP | | 242 | * Get current working directory from the reply to a CWD, PWD or CDUP |
242 | * command. | | 243 | * command. |
243 | */ | | 244 | */ |
244 | static int | | 245 | static int |
245 | ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen) | | 246 | ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen) |
246 | { | | 247 | { |
247 | char *src, *dst, *end; | | 248 | char *src, *dst, *end; |
248 | int q; | | 249 | int q; |
249 | | | 250 | |
250 | if (conn->err != FTP_WORKING_DIRECTORY && | | 251 | if (conn->err != FTP_WORKING_DIRECTORY && |
251 | conn->err != FTP_FILE_ACTION_OK) | | 252 | conn->err != FTP_FILE_ACTION_OK) |
252 | return (FTP_PROTOCOL_ERROR); | | 253 | return (FTP_PROTOCOL_ERROR); |
253 | end = conn->buf + conn->buflen; | | 254 | end = conn->buf + conn->buflen; |
254 | src = conn->buf + 4; | | 255 | src = conn->buf + 4; |
255 | if (src >= end || *src++ != '"') | | 256 | if (src >= end || *src++ != '"') |
256 | return (FTP_PROTOCOL_ERROR); | | 257 | return (FTP_PROTOCOL_ERROR); |
257 | for (q = 0, dst = pwd; src < end && pwdlen--; ++src) { | | 258 | for (q = 0, dst = pwd; src < end && pwdlen--; ++src) { |
258 | if (!q && *src == '"') | | 259 | if (!q && *src == '"') |
259 | q = 1; | | 260 | q = 1; |
260 | else if (q && *src != '"') | | 261 | else if (q && *src != '"') |
261 | break; | | 262 | break; |
262 | else if (q) | | 263 | else if (q) |
263 | *dst++ = '"', q = 0; | | 264 | *dst++ = '"', q = 0; |
264 | else | | 265 | else |
265 | *dst++ = *src; | | 266 | *dst++ = *src; |
266 | } | | 267 | } |
267 | if (!pwdlen) | | 268 | if (!pwdlen) |
268 | return (FTP_PROTOCOL_ERROR); | | 269 | return (FTP_PROTOCOL_ERROR); |
269 | *dst = '\0'; | | 270 | *dst = '\0'; |
270 | return (FTP_OK); | | 271 | return (FTP_OK); |
271 | } | | 272 | } |
272 | | | 273 | |
273 | /* | | 274 | /* |
274 | * Change working directory to the directory that contains the specified | | 275 | * Change working directory to the directory that contains the specified |
275 | * file. | | 276 | * file. |
276 | */ | | 277 | */ |
277 | static int | | 278 | static int |
278 | ftp_cwd(conn_t *conn, const char *file, int subdir) | | 279 | ftp_cwd(conn_t *conn, const char *file, int subdir) |
279 | { | | 280 | { |
280 | const char *beg, *end; | | 281 | const char *beg, *end; |
281 | char pwd[PATH_MAX]; | | 282 | char pwd[PATH_MAX]; |
282 | int e, i, len; | | 283 | int e, i, len; |
283 | | | 284 | |
284 | /* If no slashes in name, no need to change dirs. */ | | 285 | /* If no slashes in name, no need to change dirs. */ |
285 | if (subdir) | | 286 | if (subdir) |
286 | end = file + strlen(file); | | 287 | end = file + strlen(file); |
287 | else if ((end = strrchr(file, '/')) == NULL) | | 288 | else if ((end = strrchr(file, '/')) == NULL) |
288 | return (0); | | 289 | return (0); |
289 | if ((e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || | | 290 | if ((e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || |
290 | (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { | | 291 | (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { |
291 | ftp_seterr(e); | | 292 | ftp_seterr(e); |
292 | return (-1); | | 293 | return (-1); |
293 | } | | 294 | } |
294 | for (;;) { | | 295 | for (;;) { |
295 | len = strlen(pwd); | | 296 | len = strlen(pwd); |
296 | | | 297 | |
297 | /* Look for a common prefix between PWD and dir to fetch. */ | | 298 | /* Look for a common prefix between PWD and dir to fetch. */ |
298 | for (i = 0; i <= len && i <= end - file; ++i) | | 299 | for (i = 0; i <= len && i <= end - file; ++i) |
299 | if (pwd[i] != file[i]) | | 300 | if (pwd[i] != file[i]) |
300 | break; | | 301 | break; |
301 | /* Keep going up a dir until we have a matching prefix. */ | | 302 | /* Keep going up a dir until we have a matching prefix. */ |
302 | if (strcmp(pwd, "/") == 0) | | 303 | if (strcmp(pwd, "/") == 0) |
303 | break; | | 304 | break; |
304 | if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/')) | | 305 | if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/')) |
305 | break; | | 306 | break; |
306 | if ((e = ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK || | | 307 | if ((e = ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK || |
307 | (e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || | | 308 | (e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || |
308 | (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { | | 309 | (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { |
309 | ftp_seterr(e); | | 310 | ftp_seterr(e); |
310 | return (-1); | | 311 | return (-1); |
311 | } | | 312 | } |
312 | } | | 313 | } |
313 | | | 314 | |
314 | #ifdef FTP_COMBINE_CWDS | | 315 | #ifdef FTP_COMBINE_CWDS |
315 | /* Skip leading slashes, even "////". */ | | 316 | /* Skip leading slashes, even "////". */ |
316 | for (beg = file + i; beg < end && *beg == '/'; ++beg, ++i) | | 317 | for (beg = file + i; beg < end && *beg == '/'; ++beg, ++i) |
317 | /* nothing */ ; | | 318 | /* nothing */ ; |
318 | | | 319 | |
319 | /* If there is no trailing dir, we're already there. */ | | 320 | /* If there is no trailing dir, we're already there. */ |
320 | if (beg >= end) | | 321 | if (beg >= end) |
321 | return (0); | | 322 | return (0); |
322 | | | 323 | |
323 | /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ | | 324 | /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ |
324 | e = ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg); | | 325 | e = ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg); |
325 | if (e == FTP_FILE_ACTION_OK) | | 326 | if (e == FTP_FILE_ACTION_OK) |
326 | return (0); | | 327 | return (0); |
327 | #endif /* FTP_COMBINE_CWDS */ | | 328 | #endif /* FTP_COMBINE_CWDS */ |
328 | | | 329 | |
329 | /* That didn't work so go back to legacy behavior (multiple CWDs). */ | | 330 | /* That didn't work so go back to legacy behavior (multiple CWDs). */ |
330 | for (beg = file + i; beg < end; beg = file + i + 1) { | | 331 | for (beg = file + i; beg < end; beg = file + i + 1) { |
331 | while (*beg == '/') | | 332 | while (*beg == '/') |
332 | ++beg, ++i; | | 333 | ++beg, ++i; |
333 | for (++i; file + i < end && file[i] != '/'; ++i) | | 334 | for (++i; file + i < end && file[i] != '/'; ++i) |
334 | /* nothing */ ; | | 335 | /* nothing */ ; |
335 | e = ftp_cmd(conn, "CWD %.*s", file + i - beg, beg); | | 336 | e = ftp_cmd(conn, "CWD %.*s", file + i - beg, beg); |
336 | if (e != FTP_FILE_ACTION_OK) { | | 337 | if (e != FTP_FILE_ACTION_OK) { |
337 | ftp_seterr(e); | | 338 | ftp_seterr(e); |
338 | return (-1); | | 339 | return (-1); |
339 | } | | 340 | } |
340 | } | | 341 | } |
341 | return (0); | | 342 | return (0); |
342 | } | | 343 | } |
343 | | | 344 | |
344 | /* | | 345 | /* |
345 | * Set transfer mode and data type | | 346 | * Set transfer mode and data type |
346 | */ | | 347 | */ |
347 | static int | | 348 | static int |
348 | ftp_mode_type(conn_t *conn, int mode, int type) | | 349 | ftp_mode_type(conn_t *conn, int mode, int type) |
349 | { | | 350 | { |
350 | int e; | | 351 | int e; |
351 | | | 352 | |
352 | switch (mode) { | | 353 | switch (mode) { |
353 | case 0: | | 354 | case 0: |
354 | case 's': | | 355 | case 's': |
355 | mode = 'S'; | | 356 | mode = 'S'; |
356 | case 'S': | | 357 | case 'S': |
357 | break; | | 358 | break; |
358 | default: | | 359 | default: |
359 | return (FTP_PROTOCOL_ERROR); | | 360 | return (FTP_PROTOCOL_ERROR); |
360 | } | | 361 | } |
361 | if ((e = ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) { | | 362 | if ((e = ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) { |
362 | if (mode == 'S') { | | 363 | if (mode == 'S') { |
363 | /* | | 364 | /* |
364 | * Stream mode is supposed to be the default - so | | 365 | * Stream mode is supposed to be the default - so |
365 | * much so that some servers not only do not | | 366 | * much so that some servers not only do not |
366 | * support any other mode, but do not support the | | 367 | * support any other mode, but do not support the |
367 | * MODE command at all. | | 368 | * MODE command at all. |
368 | * | | 369 | * |
369 | * If "MODE S" fails, it is unlikely that we | | 370 | * If "MODE S" fails, it is unlikely that we |
370 | * previously succeeded in setting a different | | 371 | * previously succeeded in setting a different |
371 | * mode. Therefore, we simply hope that the | | 372 | * mode. Therefore, we simply hope that the |
372 | * server is already in the correct mode, and | | 373 | * server is already in the correct mode, and |
373 | * silently ignore the failure. | | 374 | * silently ignore the failure. |
374 | */ | | 375 | */ |
375 | } else { | | 376 | } else { |
376 | return (e); | | 377 | return (e); |
377 | } | | 378 | } |
378 | } | | 379 | } |
379 | | | 380 | |
380 | switch (type) { | | 381 | switch (type) { |
381 | case 0: | | 382 | case 0: |
382 | case 'i': | | 383 | case 'i': |
383 | type = 'I'; | | 384 | type = 'I'; |
384 | case 'I': | | 385 | case 'I': |
385 | break; | | 386 | break; |
386 | case 'a': | | 387 | case 'a': |
387 | type = 'A'; | | 388 | type = 'A'; |
388 | case 'A': | | 389 | case 'A': |
389 | break; | | 390 | break; |
390 | case 'd': | | 391 | case 'd': |
391 | type = 'D'; | | 392 | type = 'D'; |
392 | case 'D': | | 393 | case 'D': |
393 | /* can't handle yet */ | | 394 | /* can't handle yet */ |
394 | default: | | 395 | default: |
395 | return (FTP_PROTOCOL_ERROR); | | 396 | return (FTP_PROTOCOL_ERROR); |
396 | } | | 397 | } |
397 | if ((e = ftp_cmd(conn, "TYPE %c", type)) != FTP_OK) | | 398 | if ((e = ftp_cmd(conn, "TYPE %c", type)) != FTP_OK) |
398 | return (e); | | 399 | return (e); |
399 | | | 400 | |
400 | return (FTP_OK); | | 401 | return (FTP_OK); |
401 | } | | 402 | } |
402 | | | 403 | |
403 | /* | | 404 | /* |
404 | * Request and parse file stats | | 405 | * Request and parse file stats |
405 | */ | | 406 | */ |
406 | static int | | 407 | static int |
407 | ftp_stat(conn_t *conn, const char *file, struct url_stat *us) | | 408 | ftp_stat(conn_t *conn, const char *file, struct url_stat *us) |
408 | { | | 409 | { |
409 | char *ln; | | 410 | char *ln; |
410 | const char *filename; | | 411 | const char *filename; |
411 | int filenamelen, type; | | 412 | int filenamelen, type; |
412 | struct tm tm; | | 413 | struct tm tm; |
413 | time_t t; | | 414 | time_t t; |
414 | int e; | | 415 | int e; |
415 | | | 416 | |
416 | us->size = -1; | | 417 | us->size = -1; |
417 | us->atime = us->mtime = 0; | | 418 | us->atime = us->mtime = 0; |
418 | | | 419 | |
419 | filename = ftp_filename(file, &filenamelen, &type, 0); | | 420 | filename = ftp_filename(file, &filenamelen, &type, 0); |
420 | | | 421 | |
421 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { | | 422 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { |
422 | ftp_seterr(e); | | 423 | ftp_seterr(e); |
423 | return (-1); | | 424 | return (-1); |
424 | } | | 425 | } |
425 | | | 426 | |
426 | e = ftp_cmd(conn, "SIZE %.*s", filenamelen, filename); | | 427 | e = ftp_cmd(conn, "SIZE %.*s", filenamelen, filename); |
427 | if (e != FTP_FILE_STATUS) { | | 428 | if (e != FTP_FILE_STATUS) { |
428 | ftp_seterr(e); | | 429 | ftp_seterr(e); |
429 | return (-1); | | 430 | return (-1); |
430 | } | | 431 | } |
431 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) | | 432 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) |
432 | /* nothing */ ; | | 433 | /* nothing */ ; |
433 | for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) | | 434 | for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) |
434 | us->size = us->size * 10 + *ln - '0'; | | 435 | us->size = us->size * 10 + *ln - '0'; |
435 | if (*ln && !isspace((unsigned char)*ln)) { | | 436 | if (*ln && !isspace((unsigned char)*ln)) { |
436 | ftp_seterr(FTP_PROTOCOL_ERROR); | | 437 | ftp_seterr(FTP_PROTOCOL_ERROR); |
437 | us->size = -1; | | 438 | us->size = -1; |
438 | return (-1); | | 439 | return (-1); |
439 | } | | 440 | } |
440 | if (us->size == 0) | | 441 | if (us->size == 0) |
441 | us->size = -1; | | 442 | us->size = -1; |
442 | | | 443 | |
443 | e = ftp_cmd(conn, "MDTM %.*s", filenamelen, filename); | | 444 | e = ftp_cmd(conn, "MDTM %.*s", filenamelen, filename); |
444 | if (e != FTP_FILE_STATUS) { | | 445 | if (e != FTP_FILE_STATUS) { |
445 | ftp_seterr(e); | | 446 | ftp_seterr(e); |
446 | return (-1); | | 447 | return (-1); |
447 | } | | 448 | } |
448 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) | | 449 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) |
449 | /* nothing */ ; | | 450 | /* nothing */ ; |
450 | switch (strspn(ln, "0123456789")) { | | 451 | switch (strspn(ln, "0123456789")) { |
451 | case 14: | | 452 | case 14: |
452 | break; | | 453 | break; |
453 | case 15: | | 454 | case 15: |
454 | ln++; | | 455 | ln++; |
455 | ln[0] = '2'; | | 456 | ln[0] = '2'; |
456 | ln[1] = '0'; | | 457 | ln[1] = '0'; |
457 | break; | | 458 | break; |
458 | default: | | 459 | default: |
459 | ftp_seterr(FTP_PROTOCOL_ERROR); | | 460 | ftp_seterr(FTP_PROTOCOL_ERROR); |
460 | return (-1); | | 461 | return (-1); |
461 | } | | 462 | } |
462 | if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", | | 463 | if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", |
463 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, | | 464 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, |
464 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { | | 465 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { |
465 | ftp_seterr(FTP_PROTOCOL_ERROR); | | 466 | ftp_seterr(FTP_PROTOCOL_ERROR); |
466 | return (-1); | | 467 | return (-1); |
467 | } | | 468 | } |
468 | tm.tm_mon--; | | 469 | tm.tm_mon--; |
469 | tm.tm_year -= 1900; | | 470 | tm.tm_year -= 1900; |
470 | tm.tm_isdst = -1; | | 471 | tm.tm_isdst = -1; |
471 | t = timegm(&tm); | | 472 | t = timegm(&tm); |
472 | if (t == (time_t)-1) | | 473 | if (t == (time_t)-1) |
473 | t = time(NULL); | | 474 | t = time(NULL); |
474 | us->mtime = t; | | 475 | us->mtime = t; |
475 | us->atime = t; | | 476 | us->atime = t; |
476 | | | 477 | |
477 | return (0); | | 478 | return (0); |
478 | } | | 479 | } |
479 | | | 480 | |
480 | /* | | 481 | /* |
481 | * I/O functions for FTP | | 482 | * I/O functions for FTP |
482 | */ | | 483 | */ |
483 | struct ftpio { | | 484 | struct ftpio { |
484 | conn_t *cconn; /* Control connection */ | | 485 | conn_t *cconn; /* Control connection */ |
485 | conn_t *dconn; /* Data connection */ | | 486 | conn_t *dconn; /* Data connection */ |
486 | int dir; /* Direction */ | | 487 | int dir; /* Direction */ |
487 | int eof; /* EOF reached */ | | 488 | int eof; /* EOF reached */ |
488 | int err; /* Error code */ | | 489 | int err; /* Error code */ |
489 | }; | | 490 | }; |
490 | | | 491 | |
491 | static ssize_t ftp_readfn(void *, void *, size_t); | | 492 | static ssize_t ftp_readfn(void *, void *, size_t); |
492 | static ssize_t ftp_writefn(void *, const void *, size_t); | | 493 | static ssize_t ftp_writefn(void *, const void *, size_t); |
493 | static void ftp_closefn(void *); | | 494 | static void ftp_closefn(void *); |
494 | | | 495 | |
495 | static ssize_t | | 496 | static ssize_t |
496 | ftp_readfn(void *v, void *buf, size_t len) | | 497 | ftp_readfn(void *v, void *buf, size_t len) |
497 | { | | 498 | { |
498 | struct ftpio *io; | | 499 | struct ftpio *io; |
499 | int r; | | 500 | int r; |
500 | | | 501 | |
501 | io = (struct ftpio *)v; | | 502 | io = (struct ftpio *)v; |
502 | if (io == NULL) { | | 503 | if (io == NULL) { |
503 | errno = EBADF; | | 504 | errno = EBADF; |
504 | return (-1); | | 505 | return (-1); |
505 | } | | 506 | } |
506 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { | | 507 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { |
507 | errno = EBADF; | | 508 | errno = EBADF; |
508 | return (-1); | | 509 | return (-1); |
509 | } | | 510 | } |
510 | if (io->err) { | | 511 | if (io->err) { |
511 | errno = io->err; | | 512 | errno = io->err; |
512 | return (-1); | | 513 | return (-1); |
513 | } | | 514 | } |
514 | if (io->eof) | | 515 | if (io->eof) |
515 | return (0); | | 516 | return (0); |
516 | r = fetch_read(io->dconn, buf, len); | | 517 | r = fetch_read(io->dconn, buf, len); |
517 | if (r > 0) | | 518 | if (r > 0) |
518 | return (r); | | 519 | return (r); |
519 | if (r == 0) { | | 520 | if (r == 0) { |
520 | io->eof = 1; | | 521 | io->eof = 1; |
521 | return (0); | | 522 | return (0); |
522 | } | | 523 | } |
523 | if (errno != EINTR) | | 524 | if (errno != EINTR) |
524 | io->err = errno; | | 525 | io->err = errno; |
525 | return (-1); | | 526 | return (-1); |
526 | } | | 527 | } |
527 | | | 528 | |
528 | static ssize_t | | 529 | static ssize_t |
529 | ftp_writefn(void *v, const void *buf, size_t len) | | 530 | ftp_writefn(void *v, const void *buf, size_t len) |
530 | { | | 531 | { |
531 | struct ftpio *io; | | 532 | struct ftpio *io; |
532 | int w; | | 533 | int w; |
533 | | | 534 | |
534 | io = (struct ftpio *)v; | | 535 | io = (struct ftpio *)v; |
535 | if (io == NULL) { | | 536 | if (io == NULL) { |
536 | errno = EBADF; | | 537 | errno = EBADF; |
537 | return (-1); | | 538 | return (-1); |
538 | } | | 539 | } |
539 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { | | 540 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { |
540 | errno = EBADF; | | 541 | errno = EBADF; |
541 | return (-1); | | 542 | return (-1); |
542 | } | | 543 | } |
543 | if (io->err) { | | 544 | if (io->err) { |
544 | errno = io->err; | | 545 | errno = io->err; |
545 | return (-1); | | 546 | return (-1); |
546 | } | | 547 | } |
547 | w = fetch_write(io->dconn, buf, len); | | 548 | w = fetch_write(io->dconn, buf, len); |
548 | if (w >= 0) | | 549 | if (w >= 0) |
549 | return (w); | | 550 | return (w); |
550 | if (errno != EINTR) | | 551 | if (errno != EINTR) |
551 | io->err = errno; | | 552 | io->err = errno; |
552 | return (-1); | | 553 | return (-1); |
553 | } | | 554 | } |
554 | | | 555 | |
555 | static void | | 556 | static void |
556 | ftp_closefn(void *v) | | 557 | ftp_closefn(void *v) |
557 | { | | 558 | { |
558 | struct ftpio *io; | | 559 | struct ftpio *io; |
559 | int r; | | 560 | int r; |
560 | | | 561 | |
561 | io = (struct ftpio *)v; | | 562 | io = (struct ftpio *)v; |
562 | if (io == NULL) { | | 563 | if (io == NULL) { |
563 | errno = EBADF; | | 564 | errno = EBADF; |
564 | return; | | 565 | return; |
565 | } | | 566 | } |
566 | if (io->dir == -1) | | 567 | if (io->dir == -1) |
567 | return; | | 568 | return; |
568 | if (io->cconn == NULL || io->dconn == NULL) { | | 569 | if (io->cconn == NULL || io->dconn == NULL) { |
569 | errno = EBADF; | | 570 | errno = EBADF; |
570 | return; | | 571 | return; |
571 | } | | 572 | } |
572 | fetch_close(io->dconn); | | 573 | fetch_close(io->dconn); |
573 | io->dir = -1; | | 574 | io->dir = -1; |
574 | io->dconn = NULL; | | 575 | io->dconn = NULL; |
575 | r = ftp_chkerr(io->cconn); | | 576 | r = ftp_chkerr(io->cconn); |
576 | if (io->cconn == cached_connection && io->cconn->ref == 1) | | 577 | if (io->cconn == cached_connection && io->cconn->ref == 1) |
577 | cached_connection = NULL; | | 578 | cached_connection = NULL; |
578 | fetch_close(io->cconn); | | 579 | fetch_close(io->cconn); |
579 | free(io); | | 580 | free(io); |
580 | return; | | 581 | return; |
581 | } | | 582 | } |
582 | | | 583 | |
583 | static fetchIO * | | 584 | static fetchIO * |
584 | ftp_setup(conn_t *cconn, conn_t *dconn, int mode) | | 585 | ftp_setup(conn_t *cconn, conn_t *dconn, int mode) |
585 | { | | 586 | { |
586 | struct ftpio *io; | | 587 | struct ftpio *io; |
587 | fetchIO *f; | | 588 | fetchIO *f; |
588 | | | 589 | |
589 | if (cconn == NULL || dconn == NULL) | | 590 | if (cconn == NULL || dconn == NULL) |
590 | return (NULL); | | 591 | return (NULL); |
591 | if ((io = malloc(sizeof(*io))) == NULL) | | 592 | if ((io = malloc(sizeof(*io))) == NULL) |
592 | return (NULL); | | 593 | return (NULL); |
593 | io->cconn = cconn; | | 594 | io->cconn = cconn; |
594 | io->dconn = dconn; | | 595 | io->dconn = dconn; |
595 | io->dir = mode; | | 596 | io->dir = mode; |
596 | io->eof = io->err = 0; | | 597 | io->eof = io->err = 0; |
597 | f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); | | 598 | f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); |
598 | if (f == NULL) | | 599 | if (f == NULL) |
599 | free(io); | | 600 | free(io); |
600 | return (f); | | 601 | return (f); |
601 | } | | 602 | } |
602 | | | 603 | |
603 | /* | | 604 | /* |
604 | * Transfer file | | 605 | * Transfer file |
605 | */ | | 606 | */ |
606 | static fetchIO * | | 607 | static fetchIO * |
607 | ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, | | 608 | ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, |
608 | int mode, off_t offset, const char *flags) | | 609 | int mode, off_t offset, const char *flags) |
609 | { | | 610 | { |
610 | struct sockaddr_storage sa; | | 611 | struct sockaddr_storage sa; |
611 | struct sockaddr_in6 *sin6; | | 612 | struct sockaddr_in6 *sin6; |
612 | struct sockaddr_in *sin4; | | 613 | struct sockaddr_in *sin4; |
613 | const char *bindaddr; | | 614 | const char *bindaddr; |
614 | const char *filename; | | 615 | const char *filename; |
615 | int filenamelen, type; | | 616 | int filenamelen, type; |
616 | int low, pasv, verbose; | | 617 | int low, pasv, verbose; |
617 | int e, sd = -1; | | 618 | int e, sd = -1; |
618 | socklen_t l; | | 619 | socklen_t l; |
619 | char *s; | | 620 | char *s; |
620 | fetchIO *df; | | 621 | fetchIO *df; |
621 | | | 622 | |
622 | /* check flags */ | | 623 | /* check flags */ |
623 | low = CHECK_FLAG('l'); | | 624 | low = CHECK_FLAG('l'); |
624 | pasv = CHECK_FLAG('p'); | | 625 | pasv = CHECK_FLAG('p'); |
625 | verbose = CHECK_FLAG('v'); | | 626 | verbose = CHECK_FLAG('v'); |
626 | | | 627 | |
627 | /* passive mode */ | | 628 | /* passive mode */ |
628 | if (!pasv) | | 629 | if (!pasv) |
629 | pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && | | 630 | pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && |
630 | strncasecmp(s, "no", 2) != 0); | | 631 | strncasecmp(s, "no", 2) != 0); |
631 | | | 632 | |
632 | /* isolate filename */ | | 633 | /* isolate filename */ |
633 | filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); | | 634 | filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); |
634 | | | 635 | |
635 | /* set transfer mode and data type */ | | 636 | /* set transfer mode and data type */ |
636 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) | | 637 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) |
637 | goto ouch; | | 638 | goto ouch; |
638 | | | 639 | |
639 | /* find our own address, bind, and listen */ | | 640 | /* find our own address, bind, and listen */ |
640 | l = sizeof(sa); | | 641 | l = sizeof(sa); |
641 | if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) | | 642 | if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) |
642 | goto sysouch; | | 643 | goto sysouch; |
643 | if (sa.ss_family == AF_INET6) | | 644 | if (sa.ss_family == AF_INET6) |
644 | unmappedaddr((struct sockaddr_in6 *)&sa, &l); | | 645 | unmappedaddr((struct sockaddr_in6 *)&sa, &l); |
645 | | | 646 | |
646 | /* open data socket */ | | 647 | /* open data socket */ |
647 | if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { | | 648 | if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { |
648 | fetch_syserr(); | | 649 | fetch_syserr(); |
649 | return (NULL); | | 650 | return (NULL); |
650 | } | | 651 | } |
651 | | | 652 | |
652 | if (pasv) { | | 653 | if (pasv) { |
653 | unsigned char addr[64]; | | 654 | unsigned char addr[64]; |
654 | char *ln, *p; | | 655 | char *ln, *p; |
655 | unsigned int i; | | 656 | unsigned int i; |
656 | int port; | | 657 | int port; |
657 | | | 658 | |
658 | /* send PASV command */ | | 659 | /* send PASV command */ |
659 | if (verbose) | | 660 | if (verbose) |
660 | fetch_info("setting passive mode"); | | 661 | fetch_info("setting passive mode"); |
661 | switch (sa.ss_family) { | | 662 | switch (sa.ss_family) { |
662 | case AF_INET: | | 663 | case AF_INET: |
663 | if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) | | 664 | if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) |
664 | goto ouch; | | 665 | goto ouch; |
665 | break; | | 666 | break; |
666 | case AF_INET6: | | 667 | case AF_INET6: |
667 | if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { | | 668 | if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { |
668 | if (e == -1) | | 669 | if (e == -1) |
669 | goto ouch; | | 670 | goto ouch; |
670 | if ((e = ftp_cmd(conn, "LPSV")) != | | 671 | if ((e = ftp_cmd(conn, "LPSV")) != |
671 | FTP_LPASSIVE_MODE) | | 672 | FTP_LPASSIVE_MODE) |
672 | goto ouch; | | 673 | goto ouch; |
673 | } | | 674 | } |
674 | break; | | 675 | break; |
675 | default: | | 676 | default: |
676 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | | 677 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ |
677 | goto ouch; | | 678 | goto ouch; |
678 | } | | 679 | } |
679 | | | 680 | |
680 | /* | | 681 | /* |
681 | * Find address and port number. The reply to the PASV command | | 682 | * Find address and port number. The reply to the PASV command |
682 | * is IMHO the one and only weak point in the FTP protocol. | | 683 | * is IMHO the one and only weak point in the FTP protocol. |
683 | */ | | 684 | */ |
684 | ln = conn->buf; | | 685 | ln = conn->buf; |
685 | switch (e) { | | 686 | switch (e) { |
686 | case FTP_PASSIVE_MODE: | | 687 | case FTP_PASSIVE_MODE: |
687 | case FTP_LPASSIVE_MODE: | | 688 | case FTP_LPASSIVE_MODE: |
688 | for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) | | 689 | for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) |
689 | /* nothing */ ; | | 690 | /* nothing */ ; |
690 | if (!*p) { | | 691 | if (!*p) { |
691 | e = FTP_PROTOCOL_ERROR; | | 692 | e = FTP_PROTOCOL_ERROR; |
692 | goto ouch; | | 693 | goto ouch; |
693 | } | | 694 | } |
694 | l = (e == FTP_PASSIVE_MODE ? 6 : 21); | | 695 | l = (e == FTP_PASSIVE_MODE ? 6 : 21); |
695 | for (i = 0; *p && i < l; i++, p++) | | 696 | for (i = 0; *p && i < l; i++, p++) |
696 | addr[i] = strtol(p, &p, 10); | | 697 | addr[i] = strtol(p, &p, 10); |
697 | if (i < l) { | | 698 | if (i < l) { |
698 | e = FTP_PROTOCOL_ERROR; | | 699 | e = FTP_PROTOCOL_ERROR; |
699 | goto ouch; | | 700 | goto ouch; |
700 | } | | 701 | } |
701 | break; | | 702 | break; |
702 | case FTP_EPASSIVE_MODE: | | 703 | case FTP_EPASSIVE_MODE: |
703 | for (p = ln + 3; *p && *p != '('; p++) | | 704 | for (p = ln + 3; *p && *p != '('; p++) |
704 | /* nothing */ ; | | 705 | /* nothing */ ; |
705 | if (!*p) { | | 706 | if (!*p) { |
706 | e = FTP_PROTOCOL_ERROR; | | 707 | e = FTP_PROTOCOL_ERROR; |
707 | goto ouch; | | 708 | goto ouch; |
708 | } | | 709 | } |
709 | ++p; | | 710 | ++p; |
710 | if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], | | 711 | if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], |
711 | &port, &addr[3]) != 5 || | | 712 | &port, &addr[3]) != 5 || |
712 | addr[0] != addr[1] || | | 713 | addr[0] != addr[1] || |
713 | addr[0] != addr[2] || addr[0] != addr[3]) { | | 714 | addr[0] != addr[2] || addr[0] != addr[3]) { |
714 | e = FTP_PROTOCOL_ERROR; | | 715 | e = FTP_PROTOCOL_ERROR; |
715 | goto ouch; | | 716 | goto ouch; |
716 | } | | 717 | } |
717 | break; | | 718 | break; |
718 | } | | 719 | } |
719 | | | 720 | |
720 | /* seek to required offset */ | | 721 | /* seek to required offset */ |
721 | if (offset) | | 722 | if (offset) |
722 | if (ftp_cmd(conn, "REST %lu", (unsigned long)offset) != FTP_FILE_OK) | | 723 | if (ftp_cmd(conn, "REST %lu", (unsigned long)offset) != FTP_FILE_OK) |
723 | goto sysouch; | | 724 | goto sysouch; |
724 | | | 725 | |
725 | /* construct sockaddr for data socket */ | | 726 | /* construct sockaddr for data socket */ |
726 | l = sizeof(sa); | | 727 | l = sizeof(sa); |
727 | if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) | | 728 | if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) |
728 | goto sysouch; | | 729 | goto sysouch; |
729 | if (sa.ss_family == AF_INET6) | | 730 | if (sa.ss_family == AF_INET6) |
730 | unmappedaddr((struct sockaddr_in6 *)&sa, &l); | | 731 | unmappedaddr((struct sockaddr_in6 *)&sa, &l); |
731 | switch (sa.ss_family) { | | 732 | switch (sa.ss_family) { |
732 | case AF_INET6: | | 733 | case AF_INET6: |
733 | sin6 = (struct sockaddr_in6 *)&sa; | | 734 | sin6 = (struct sockaddr_in6 *)&sa; |
734 | if (e == FTP_EPASSIVE_MODE) | | 735 | if (e == FTP_EPASSIVE_MODE) |
735 | sin6->sin6_port = htons(port); | | 736 | sin6->sin6_port = htons(port); |
736 | else { | | 737 | else { |
737 | memcpy(&sin6->sin6_addr, addr + 2, 16); | | 738 | memcpy(&sin6->sin6_addr, addr + 2, 16); |
738 | memcpy(&sin6->sin6_port, addr + 19, 2); | | 739 | memcpy(&sin6->sin6_port, addr + 19, 2); |
739 | } | | 740 | } |
740 | break; | | 741 | break; |
741 | case AF_INET: | | 742 | case AF_INET: |
742 | sin4 = (struct sockaddr_in *)&sa; | | 743 | sin4 = (struct sockaddr_in *)&sa; |
743 | if (e == FTP_EPASSIVE_MODE) | | 744 | if (e == FTP_EPASSIVE_MODE) |
744 | sin4->sin_port = htons(port); | | 745 | sin4->sin_port = htons(port); |
745 | else { | | 746 | else { |
746 | memcpy(&sin4->sin_addr, addr, 4); | | 747 | memcpy(&sin4->sin_addr, addr, 4); |
747 | memcpy(&sin4->sin_port, addr + 4, 2); | | 748 | memcpy(&sin4->sin_port, addr + 4, 2); |
748 | } | | 749 | } |
749 | break; | | 750 | break; |
750 | default: | | 751 | default: |
751 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | | 752 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ |
752 | break; | | 753 | break; |
753 | } | | 754 | } |
754 | | | 755 | |
755 | /* connect to data port */ | | 756 | /* connect to data port */ |
756 | if (verbose) | | 757 | if (verbose) |
757 | fetch_info("opening data connection"); | | 758 | fetch_info("opening data connection"); |
758 | bindaddr = getenv("FETCH_BIND_ADDRESS"); | | 759 | bindaddr = getenv("FETCH_BIND_ADDRESS"); |
759 | if (bindaddr != NULL && *bindaddr != '\0' && | | 760 | if (bindaddr != NULL && *bindaddr != '\0' && |
760 | fetch_bind(sd, sa.ss_family, bindaddr) != 0) | | 761 | fetch_bind(sd, sa.ss_family, bindaddr) != 0) |
761 | goto sysouch; | | 762 | goto sysouch; |
762 | if (connect(sd, (struct sockaddr *)&sa, l) == -1) | | 763 | if (connect(sd, (struct sockaddr *)&sa, l) == -1) |
763 | goto sysouch; | | 764 | goto sysouch; |
764 | | | 765 | |
765 | /* make the server initiate the transfer */ | | 766 | /* make the server initiate the transfer */ |
766 | if (verbose) | | 767 | if (verbose) |
767 | fetch_info("initiating transfer"); | | 768 | fetch_info("initiating transfer"); |
768 | if (op_arg) | | 769 | if (op_arg) |
769 | e = ftp_cmd(conn, "%s%s%s", oper, *op_arg ? " " : "", op_arg); | | 770 | e = ftp_cmd(conn, "%s%s%s", oper, *op_arg ? " " : "", op_arg); |
770 | else | | 771 | else |
771 | e = ftp_cmd(conn, "%s %.*s", oper, | | 772 | e = ftp_cmd(conn, "%s %.*s", oper, |
772 | filenamelen, filename); | | 773 | filenamelen, filename); |
773 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | | 774 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) |
774 | goto ouch; | | 775 | goto ouch; |
775 | | | 776 | |
776 | } else { | | 777 | } else { |
777 | uint32_t a; | | 778 | uint32_t a; |
778 | uint16_t p; | | 779 | uint16_t p; |
779 | #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) | | 780 | #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) |
780 | int arg; | | 781 | int arg; |
781 | #endif | | 782 | #endif |
782 | int d; | | 783 | int d; |
783 | char *ap; | | 784 | char *ap; |
784 | char hname[INET6_ADDRSTRLEN]; | | 785 | char hname[INET6_ADDRSTRLEN]; |
785 | | | 786 | |
786 | switch (sa.ss_family) { | | 787 | switch (sa.ss_family) { |
787 | case AF_INET6: | | 788 | case AF_INET6: |
788 | ((struct sockaddr_in6 *)&sa)->sin6_port = 0; | | 789 | ((struct sockaddr_in6 *)&sa)->sin6_port = 0; |
789 | #ifdef IPV6_PORTRANGE | | 790 | #ifdef IPV6_PORTRANGE |
790 | arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; | | 791 | arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; |
791 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, | | 792 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, |
792 | (char *)&arg, sizeof(arg)) == -1) | | 793 | (char *)&arg, sizeof(arg)) == -1) |
793 | goto sysouch; | | 794 | goto sysouch; |
794 | #endif | | 795 | #endif |
795 | break; | | 796 | break; |
796 | case AF_INET: | | 797 | case AF_INET: |
797 | ((struct sockaddr_in *)&sa)->sin_port = 0; | | 798 | ((struct sockaddr_in *)&sa)->sin_port = 0; |
798 | #ifdef IP_PORTRANGE | | 799 | #ifdef IP_PORTRANGE |
799 | arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; | | 800 | arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; |
800 | if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, | | 801 | if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, |
801 | (char *)&arg, sizeof(arg)) == -1) | | 802 | (char *)&arg, sizeof(arg)) == -1) |
802 | goto sysouch; | | 803 | goto sysouch; |
803 | #endif | | 804 | #endif |
804 | break; | | 805 | break; |
805 | } | | 806 | } |
806 | if (verbose) | | 807 | if (verbose) |
807 | fetch_info("binding data socket"); | | 808 | fetch_info("binding data socket"); |
808 | if (bind(sd, (struct sockaddr *)&sa, l) == -1) | | 809 | if (bind(sd, (struct sockaddr *)&sa, l) == -1) |
809 | goto sysouch; | | 810 | goto sysouch; |
810 | if (listen(sd, 1) == -1) | | 811 | if (listen(sd, 1) == -1) |
811 | goto sysouch; | | 812 | goto sysouch; |
812 | | | 813 | |
813 | /* find what port we're on and tell the server */ | | 814 | /* find what port we're on and tell the server */ |
814 | if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) | | 815 | if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) |
815 | goto sysouch; | | 816 | goto sysouch; |
816 | switch (sa.ss_family) { | | 817 | switch (sa.ss_family) { |
817 | case AF_INET: | | 818 | case AF_INET: |
818 | sin4 = (struct sockaddr_in *)&sa; | | 819 | sin4 = (struct sockaddr_in *)&sa; |
819 | a = ntohl(sin4->sin_addr.s_addr); | | 820 | a = ntohl(sin4->sin_addr.s_addr); |
820 | p = ntohs(sin4->sin_port); | | 821 | p = ntohs(sin4->sin_port); |
821 | e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", | | 822 | e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", |
822 | (a >> 24) & 0xff, (a >> 16) & 0xff, | | 823 | (a >> 24) & 0xff, (a >> 16) & 0xff, |
823 | (a >> 8) & 0xff, a & 0xff, | | 824 | (a >> 8) & 0xff, a & 0xff, |
824 | (p >> 8) & 0xff, p & 0xff); | | 825 | (p >> 8) & 0xff, p & 0xff); |
825 | break; | | 826 | break; |
826 | case AF_INET6: | | 827 | case AF_INET6: |
827 | #define UC(b) (((int)b)&0xff) | | 828 | #define UC(b) (((int)b)&0xff) |
828 | e = -1; | | 829 | e = -1; |
829 | sin6 = (struct sockaddr_in6 *)&sa; | | 830 | sin6 = (struct sockaddr_in6 *)&sa; |
830 | sin6->sin6_scope_id = 0; | | 831 | sin6->sin6_scope_id = 0; |
831 | if (getnameinfo((struct sockaddr *)&sa, l, | | 832 | if (getnameinfo((struct sockaddr *)&sa, l, |
832 | hname, sizeof(hname), | | 833 | hname, sizeof(hname), |
833 | NULL, 0, NI_NUMERICHOST) == 0) { | | 834 | NULL, 0, NI_NUMERICHOST) == 0) { |
834 | e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, | | 835 | e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, |
835 | htons(sin6->sin6_port)); | | 836 | htons(sin6->sin6_port)); |
836 | if (e == -1) | | 837 | if (e == -1) |
837 | goto ouch; | | 838 | goto ouch; |
838 | } | | 839 | } |
839 | if (e != FTP_OK) { | | 840 | if (e != FTP_OK) { |
840 | ap = (char *)&sin6->sin6_addr; | | 841 | ap = (char *)&sin6->sin6_addr; |
841 | e = ftp_cmd(conn, | | 842 | e = ftp_cmd(conn, |
842 | "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", | | 843 | "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", |
843 | 6, 16, | | 844 | 6, 16, |
844 | UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), | | 845 | UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), |
845 | UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), | | 846 | UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), |
846 | UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), | | 847 | UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), |
847 | UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), | | 848 | UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), |
848 | 2, | | 849 | 2, |
849 | (ntohs(sin6->sin6_port) >> 8) & 0xff, | | 850 | (ntohs(sin6->sin6_port) >> 8) & 0xff, |
850 | ntohs(sin6->sin6_port) & 0xff); | | 851 | ntohs(sin6->sin6_port) & 0xff); |
851 | } | | 852 | } |
852 | break; | | 853 | break; |
853 | default: | | 854 | default: |
854 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | | 855 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ |
855 | goto ouch; | | 856 | goto ouch; |
856 | } | | 857 | } |
857 | if (e != FTP_OK) | | 858 | if (e != FTP_OK) |
858 | goto ouch; | | 859 | goto ouch; |
859 | | | 860 | |
860 | /* seek to required offset */ | | 861 | /* seek to required offset */ |
861 | if (offset) | | 862 | if (offset) |
862 | if (ftp_cmd(conn, "REST %llu", (unsigned long long)offset) != FTP_FILE_OK) | | 863 | if (ftp_cmd(conn, "REST %llu", (unsigned long long)offset) != FTP_FILE_OK) |
863 | goto sysouch; | | 864 | goto sysouch; |
864 | | | 865 | |
865 | /* make the server initiate the transfer */ | | 866 | /* make the server initiate the transfer */ |
866 | if (verbose) | | 867 | if (verbose) |
867 | fetch_info("initiating transfer"); | | 868 | fetch_info("initiating transfer"); |
868 | if (op_arg) | | 869 | if (op_arg) |
869 | e = ftp_cmd(conn, "%s%s%s", oper, *op_arg ? " " : "", op_arg); | | 870 | e = ftp_cmd(conn, "%s%s%s", oper, *op_arg ? " " : "", op_arg); |
870 | else | | 871 | else |
871 | e = ftp_cmd(conn, "%s %.*s", oper, | | 872 | e = ftp_cmd(conn, "%s %.*s", oper, |
872 | filenamelen, filename); | | 873 | filenamelen, filename); |
873 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | | 874 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) |
874 | goto ouch; | | 875 | goto ouch; |
875 | | | 876 | |
876 | /* accept the incoming connection and go to town */ | | 877 | /* accept the incoming connection and go to town */ |
877 | if ((d = accept(sd, NULL, NULL)) == -1) | | 878 | if ((d = accept(sd, NULL, NULL)) == -1) |
878 | goto sysouch; | | 879 | goto sysouch; |
879 | close(sd); | | 880 | close(sd); |
880 | sd = d; | | 881 | sd = d; |
881 | } | | 882 | } |
882 | | | 883 | |
883 | if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) | | 884 | if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) |
884 | goto sysouch; | | 885 | goto sysouch; |
885 | return (df); | | 886 | return (df); |
886 | | | 887 | |
887 | sysouch: | | 888 | sysouch: |
888 | fetch_syserr(); | | 889 | fetch_syserr(); |
889 | if (sd >= 0) | | 890 | if (sd >= 0) |
890 | close(sd); | | 891 | close(sd); |
891 | return (NULL); | | 892 | return (NULL); |
892 | | | 893 | |
893 | ouch: | | 894 | ouch: |
894 | if (e != -1) | | 895 | if (e != -1) |
895 | ftp_seterr(e); | | 896 | ftp_seterr(e); |
896 | if (sd >= 0) | | 897 | if (sd >= 0) |
897 | close(sd); | | 898 | close(sd); |
898 | return (NULL); | | 899 | return (NULL); |
899 | } | | 900 | } |
900 | | | 901 | |
901 | /* | | 902 | /* |
902 | * Authenticate | | 903 | * Authenticate |
903 | */ | | 904 | */ |
904 | static int | | 905 | static int |
905 | ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) | | 906 | ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) |
906 | { | | 907 | { |
907 | const char *user, *pwd, *login_name; | | 908 | const char *user, *pwd, *login_name; |
908 | char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1]; | | 909 | char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1]; |
909 | int e, len; | | 910 | int e, len; |
910 | | | 911 | |
911 | /* XXX FTP_AUTH, and maybe .netrc */ | | 912 | /* XXX FTP_AUTH, and maybe .netrc */ |
912 | | | 913 | |
913 | /* send user name and password */ | | 914 | /* send user name and password */ |
914 | if (url->user[0] == '\0') | | 915 | if (url->user[0] == '\0') |
915 | fetch_netrc_auth(url); | | 916 | fetch_netrc_auth(url); |
916 | user = url->user; | | 917 | user = url->user; |
917 | if (*user == '\0') | | 918 | if (*user == '\0') |
918 | user = getenv("FTP_LOGIN"); | | 919 | user = getenv("FTP_LOGIN"); |
919 | if (user == NULL || *user == '\0') | | 920 | if (user == NULL || *user == '\0') |
920 | user = FTP_ANONYMOUS_USER; | | 921 | user = FTP_ANONYMOUS_USER; |
921 | if (purl && url->port == fetch_default_port(url->scheme)) | | 922 | if (purl && url->port == fetch_default_port(url->scheme)) |
922 | e = ftp_cmd(conn, "USER %s@%s", user, url->host); | | 923 | e = ftp_cmd(conn, "USER %s@%s", user, url->host); |
923 | else if (purl) | | 924 | else if (purl) |
924 | e = ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port); | | 925 | e = ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port); |
925 | else | | 926 | else |
926 | e = ftp_cmd(conn, "USER %s", user); | | 927 | e = ftp_cmd(conn, "USER %s", user); |
927 | | | 928 | |
928 | /* did the server request a password? */ | | 929 | /* did the server request a password? */ |
929 | if (e == FTP_NEED_PASSWORD) { | | 930 | if (e == FTP_NEED_PASSWORD) { |
930 | pwd = url->pwd; | | 931 | pwd = url->pwd; |
931 | if (*pwd == '\0') | | 932 | if (*pwd == '\0') |
932 | pwd = getenv("FTP_PASSWORD"); | | 933 | pwd = getenv("FTP_PASSWORD"); |
933 | if (pwd == NULL || *pwd == '\0') { | | 934 | if (pwd == NULL || *pwd == '\0') { |
934 | if ((login_name = getlogin()) == 0) | | 935 | if ((login_name = getlogin()) == 0) |
935 | login_name = FTP_ANONYMOUS_USER; | | 936 | login_name = FTP_ANONYMOUS_USER; |
936 | if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0) | | 937 | if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0) |
937 | len = 0; | | 938 | len = 0; |
938 | else if (len > URL_USERLEN + 1) | | 939 | else if (len > URL_USERLEN + 1) |
939 | len = URL_USERLEN + 1; | | 940 | len = URL_USERLEN + 1; |
940 | gethostname(pbuf + len, sizeof(pbuf) - len); | | 941 | gethostname(pbuf + len, sizeof(pbuf) - len); |
941 | /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */ | | 942 | /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */ |
942 | pbuf[sizeof(pbuf) - 1] = '\0'; | | 943 | pbuf[sizeof(pbuf) - 1] = '\0'; |
943 | pwd = pbuf; | | 944 | pwd = pbuf; |
944 | } | | 945 | } |
945 | e = ftp_cmd(conn, "PASS %s", pwd); | | 946 | e = ftp_cmd(conn, "PASS %s", pwd); |
946 | } | | 947 | } |
947 | | | 948 | |
948 | return (e); | | 949 | return (e); |
949 | } | | 950 | } |
950 | | | 951 | |
951 | /* | | 952 | /* |
952 | * Log on to FTP server | | 953 | * Log on to FTP server |
953 | */ | | 954 | */ |
954 | static conn_t * | | 955 | static conn_t * |
955 | ftp_connect(struct url *url, struct url *purl, const char *flags) | | 956 | ftp_connect(struct url *url, struct url *purl, const char *flags) |
956 | { | | 957 | { |
957 | conn_t *conn; | | 958 | conn_t *conn; |
958 | int e, direct, verbose; | | 959 | int e, direct, verbose; |
959 | #ifdef INET6 | | 960 | #ifdef INET6 |
960 | int af = AF_UNSPEC; | | 961 | int af = AF_UNSPEC; |
961 | #else | | 962 | #else |
962 | int af = AF_INET; | | 963 | int af = AF_INET; |
963 | #endif | | 964 | #endif |
964 | | | 965 | |
965 | direct = CHECK_FLAG('d'); | | 966 | direct = CHECK_FLAG('d'); |
966 | verbose = CHECK_FLAG('v'); | | 967 | verbose = CHECK_FLAG('v'); |
967 | if (CHECK_FLAG('4')) | | 968 | if (CHECK_FLAG('4')) |
968 | af = AF_INET; | | 969 | af = AF_INET; |
969 | else if (CHECK_FLAG('6')) | | 970 | else if (CHECK_FLAG('6')) |
970 | af = AF_INET6; | | 971 | af = AF_INET6; |
971 | | | 972 | |
972 | if (direct) | | 973 | if (direct) |
973 | purl = NULL; | | 974 | purl = NULL; |
974 | | | 975 | |
975 | /* check for proxy */ | | 976 | /* check for proxy */ |
976 | if (purl) { | | 977 | if (purl) { |
977 | /* XXX proxy authentication! */ | | 978 | /* XXX proxy authentication! */ |
978 | conn = fetch_connect(purl->host, purl->port, af, verbose); | | 979 | conn = fetch_connect(purl->host, purl->port, af, verbose); |
979 | } else { | | 980 | } else { |
980 | /* no proxy, go straight to target */ | | 981 | /* no proxy, go straight to target */ |
981 | conn = fetch_connect(url->host, url->port, af, verbose); | | 982 | conn = fetch_connect(url->host, url->port, af, verbose); |
982 | purl = NULL; | | 983 | purl = NULL; |
983 | } | | 984 | } |
984 | | | 985 | |
985 | /* check connection */ | | 986 | /* check connection */ |
986 | if (conn == NULL) | | 987 | if (conn == NULL) |
987 | /* fetch_connect() has already set an error code */ | | 988 | /* fetch_connect() has already set an error code */ |
988 | return (NULL); | | 989 | return (NULL); |
989 | | | 990 | |
990 | /* expect welcome message */ | | 991 | /* expect welcome message */ |
991 | if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) | | 992 | if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) |
992 | goto fouch; | | 993 | goto fouch; |
993 | | | 994 | |
994 | /* authenticate */ | | 995 | /* authenticate */ |
995 | if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) | | 996 | if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) |
996 | goto fouch; | | 997 | goto fouch; |
997 | | | 998 | |
998 | /* TODO: Request extended features supported, if any (RFC 3659). */ | | 999 | /* TODO: Request extended features supported, if any (RFC 3659). */ |
999 | | | 1000 | |
1000 | /* done */ | | 1001 | /* done */ |
1001 | return (conn); | | 1002 | return (conn); |
1002 | | | 1003 | |