Deleted Added
sdiff udiff text old ( 40939 ) new ( 40975 )
full compact
1/*-
2 * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $Id: ftp.c,v 1.6 1998/11/05 19:48:17 des Exp $
29 */
30
31/*
32 * Portions of this code were taken from or based on ftpio.c:
33 *
34 * ----------------------------------------------------------------------------
35 * "THE BEER-WARE LICENSE" (Revision 42):
36 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
37 * can do whatever you want with this stuff. If we meet some day, and you think
38 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
39 * ----------------------------------------------------------------------------
40 *
41 * Major Changelog:
42 *
43 * Dag-Erling Co�dan Sm�rgrav
44 * 9 Jun 1998
45 *
46 * Incorporated into libfetch
47 *
48 * Jordan K. Hubbard
49 * 17 Jan 1996
50 *
51 * Turned inside out. Now returns xfers as new file ids, not as a special
52 * `state' of FTP_t
53 *
54 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
55 *
56 */
57
58#include <sys/types.h>
59#include <sys/socket.h>
60#include <netinet/in.h>
61#include <sys/errno.h>
62
63#include <ctype.h>
64#include <errno.h>
65#include <netdb.h>
66#include <stdarg.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71
72#include "fetch.h"
73#include "common.h"
74#include "ftperr.inc"
75
76#define FTP_DEFAULT_TO_ANONYMOUS
77#define FTP_ANONYMOUS_USER "ftp"
78#define FTP_ANONYMOUS_PASSWORD "ftp"
79#define FTP_DEFAULT_PORT 21
80
81#define FTP_OPEN_DATA_CONNECTION 150
82#define FTP_OK 200
83#define FTP_PASSIVE_MODE 227
84#define FTP_LOGGED_IN 230
85#define FTP_FILE_ACTION_OK 250
86#define FTP_NEED_PASSWORD 331
87#define FTP_NEED_ACCOUNT 332
88
89#define ENDL "\r\n"
90
91static struct url cached_host;
92static FILE *cached_socket;
93
94static char *_ftp_last_reply;
95
96/*
97 * Get server response, check that first digit is a '2'
98 */
99static int
100_ftp_chkerr(FILE *s, int *e)
101{
102 char *line;
103 size_t len;
104 int err;
105
106 if (e)
107 *e = 0;
108
109 do {
110 if (((line = fgetln(s, &len)) == NULL) || (len < 4)) {
111 _fetch_syserr();
112 return -1;
113 }
114 } while (line[3] == '-');
115
116 _ftp_last_reply = line;
117
118#ifndef NDEBUG
119 fprintf(stderr, "\033[1m<<< ");
120 fprintf(stderr, "%*.*s", (int)len, (int)len, line);
121 fprintf(stderr, "\033[m");
122#endif
123
124 if (!isdigit(line[1]) || !isdigit(line[1])
125 || !isdigit(line[2]) || (line[3] != ' ')) {
126 _ftp_seterr(0);
127 return -1;
128 }
129
130 err = (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0');
131 _ftp_seterr(err);
132
133 if (e)
134 *e = err;
135
136 return (line[0] == '2') - 1;
137}
138
139/*
140 * Send a command and check reply
141 */
142static int
143_ftp_cmd(FILE *f, char *fmt, ...)
144{
145 va_list ap;
146 int e;
147
148 va_start(ap, fmt);
149 vfprintf(f, fmt, ap);
150#ifndef NDEBUG
151 fprintf(stderr, "\033[1m>>> ");
152 vfprintf(stderr, fmt, ap);
153 fprintf(stderr, "\033[m");
154#endif
155 va_end(ap);
156
157 _ftp_chkerr(f, &e);
158 return e;
159}
160
161/*
162 * Transfer file
163 */
164static FILE *
165_ftp_transfer(FILE *cf, char *oper, char *file, char *mode, int pasv)
166{
167 struct sockaddr_in sin;
168 int sd = -1, l;
169 char *s;
170 FILE *df;
171
172 /* change directory */
173 if (((s = strrchr(file, '/')) != NULL) && (s != file)) {
174 *s = 0;
175 if (_ftp_cmd(cf, "CWD %s" ENDL, file) != FTP_FILE_ACTION_OK) {
176 *s = '/';
177 return NULL;
178 }
179 *s++ = '/';
180 } else {
181 if (_ftp_cmd(cf, "CWD /" ENDL) != FTP_FILE_ACTION_OK)
182 return NULL;
183 }
184
185 /* s now points to file name */
186
187 /* open data socket */
188 if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
189 _fetch_syserr();
190 return NULL;
191 }
192
193 if (pasv) {
194 u_char addr[6];
195 char *ln, *p;
196 int i;
197
198 /* send PASV command */
199 if (_ftp_cmd(cf, "PASV" ENDL) != FTP_PASSIVE_MODE)
200 goto ouch;
201
202 /* find address and port number. The reply to the PASV command
203 is IMHO the one and only weak point in the FTP protocol. */
204 ln = _ftp_last_reply;
205 for (p = ln + 3; !isdigit(*p); p++)
206 /* nothing */ ;
207 for (p--, i = 0; i < 6; i++) {
208 p++; /* skip the comma */
209 addr[i] = strtol(p, &p, 10);
210 }
211
212 /* construct sockaddr for data socket */
213 l = sizeof(sin);
214 if (getpeername(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
215 goto sysouch;
216 bcopy(addr, (char *)&sin.sin_addr, 4);
217 bcopy(addr + 4, (char *)&sin.sin_port, 2);
218
219 /* connect to data port */
220 if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
221 goto sysouch;
222
223 /* make the server initiate the transfer */
224 if (_ftp_cmd(cf, "%s %s" ENDL, oper, s) != FTP_OPEN_DATA_CONNECTION)
225 goto ouch;
226
227 } else {
228 u_int32_t a;
229 u_short p;
230 int d;
231
232 /* find our own address, bind, and listen */
233 l = sizeof(sin);
234 if (getsockname(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
235 goto sysouch;
236 sin.sin_port = 0;
237 if (bind(sd, (struct sockaddr *)&sin, l) == -1)
238 goto sysouch;
239 if (listen(sd, 1) == -1)
240 goto sysouch;
241
242 /* find what port we're on and tell the server */
243 if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
244 goto sysouch;
245 a = ntohl(sin.sin_addr.s_addr);
246 p = ntohs(sin.sin_port);
247 if (_ftp_cmd(cf, "PORT %d,%d,%d,%d,%d,%d" ENDL,
248 (a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff,
249 (p >> 8) & 0xff, p & 0xff) != FTP_OK)
250 goto ouch;
251
252 /* make the server initiate the transfer */
253 if (_ftp_cmd(cf, "%s %s" ENDL, oper, s) != FTP_OPEN_DATA_CONNECTION)
254 goto ouch;
255
256 /* accept the incoming connection and go to town */
257 if ((d = accept(sd, NULL, NULL)) == -1)
258 goto sysouch;
259 close(sd);
260 sd = d;
261 }
262
263 if ((df = fdopen(sd, mode)) == NULL)
264 goto sysouch;
265 return df;
266
267sysouch:
268 _fetch_syserr();
269ouch:
270 close(sd);
271 return NULL;
272}
273
274/*
275 * Log on to FTP server
276 */
277static FILE *
278_ftp_connect(char *host, int port, char *user, char *pwd)
279{
280 int sd, e, pp = FTP_DEFAULT_PORT;
281 char *p, *q;
282 FILE *f;
283
284 /* check for proxy */
285 if ((p = getenv("FTP_PROXY")) != NULL) {
286 if ((q = strchr(p, ':')) != NULL) {
287 /* XXX check that it's a valid number */
288 pp = atoi(q+1);
289 }
290 if (q)
291 *q = 0;
292 sd = fetchConnect(p, pp);
293 if (q)
294 *q = ':';
295 } else {
296 /* no proxy, go straight to target */
297 sd = fetchConnect(host, port);
298 }
299
300 /* check connection */
301 if (sd == -1) {
302 _fetch_syserr();
303 return NULL;
304 }
305
306 /* streams make life easier */
307 if ((f = fdopen(sd, "r+")) == NULL) {
308 _fetch_syserr();
309 goto ouch;
310 }
311
312 /* expect welcome message */
313 if (_ftp_chkerr(f, NULL) == -1)
314 goto fouch;
315
316 /* send user name and password */
317 if (!user || !*user)
318 user = FTP_ANONYMOUS_USER;
319 e = p ? _ftp_cmd(f, "USER %s@%s@%d" ENDL, user, host, port)
320 : _ftp_cmd(f, "USER %s" ENDL, user);
321
322 /* did the server request a password? */
323 if (e == FTP_NEED_PASSWORD) {
324 if (!pwd || !*pwd)
325 pwd = FTP_ANONYMOUS_PASSWORD;
326 e = _ftp_cmd(f, "PASS %s" ENDL, pwd);
327 }
328
329 /* did the server request an account? */
330 if (e == FTP_NEED_ACCOUNT)
331 /* help! */ ;
332
333 /* we should be done by now */
334 if (e != FTP_LOGGED_IN)
335 goto fouch;
336
337 /* might as well select mode and type at once */
338#ifdef FTP_FORCE_STREAM_MODE
339 if (_ftp_cmd(f, "MODE S" ENDL) != FTP_OK) /* default is S */
340 goto ouch;
341#endif
342 if (_ftp_cmd(f, "TYPE I" ENDL) != FTP_OK) /* default is A */
343 goto ouch;
344
345 /* done */
346 return f;
347
348ouch:
349 close(sd);
350 return NULL;
351fouch:
352 fclose(f);
353 return NULL;
354}
355
356/*
357 * Disconnect from server
358 */
359static void
360_ftp_disconnect(FILE *f)
361{
362 _ftp_cmd(f, "QUIT" ENDL);
363 fclose(f);
364}
365
366/*
367 * Check if we're already connected
368 */
369static int
370_ftp_isconnected(struct url *url)
371{
372 return (cached_socket
373 && (strcmp(url->host, cached_host.host) == 0)
374 && (strcmp(url->user, cached_host.user) == 0)
375 && (strcmp(url->pwd, cached_host.pwd) == 0)
376 && (url->port == cached_host.port));
377}
378
379/*
380 * FTP session
381 */
382static FILE *
383fetchXxxFTP(struct url *url, char *oper, char *mode, char *flags)
384{
385 FILE *cf = NULL;
386 int e;
387
388 /* set default port */
389 if (!url->port)
390 url->port = FTP_DEFAULT_PORT;
391
392 /* try to use previously cached connection; there should be a 226 waiting */
393 if (_ftp_isconnected(url)) {
394 _ftp_chkerr(cached_socket, &e);
395 if (e > 0)
396 cf = cached_socket;
397 }
398
399 /* connect to server */
400 if (!cf) {
401 cf = _ftp_connect(url->host, url->port, url->user, url->pwd);
402 if (!cf)
403 return NULL;
404 if (cached_socket)
405 _ftp_disconnect(cached_socket);
406 cached_socket = cf;
407 memcpy(&cached_host, url, sizeof(struct url));
408 }
409
410 /* initiate the transfer */
411 return _ftp_transfer(cf, oper, url->doc, mode, (flags && strchr(flags, 'p')));
412}
413
414/*
415 * Itsy bitsy teeny weenie
416 */
417FILE *
418fetchGetFTP(struct url *url, char *flags)
419{
420 return fetchXxxFTP(url, "RETR", "r", flags);
421}
422
423FILE *
424fetchPutFTP(struct url *url, char *flags)
425{
426 if (flags && strchr(flags, 'a'))
427 return fetchXxxFTP(url, "APPE", "w", flags);
428 else return fetchXxxFTP(url, "STOR", "w", flags);
429}
430
431extern void warnx(char *fmt, ...);
432int
433fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
434{
435 warnx("fetchStatFTP(): not implemented");
436 return -1;
437}
438