1 |
|
|
/* $OpenBSD: fetch.c,v 1.147 2016/05/27 15:16:16 jsing Exp $ */ |
2 |
|
|
/* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */ |
3 |
|
|
|
4 |
|
|
/*- |
5 |
|
|
* Copyright (c) 1997 The NetBSD Foundation, Inc. |
6 |
|
|
* All rights reserved. |
7 |
|
|
* |
8 |
|
|
* This code is derived from software contributed to The NetBSD Foundation |
9 |
|
|
* by Jason Thorpe and Luke Mewburn. |
10 |
|
|
* |
11 |
|
|
* Redistribution and use in source and binary forms, with or without |
12 |
|
|
* modification, are permitted provided that the following conditions |
13 |
|
|
* are met: |
14 |
|
|
* 1. Redistributions of source code must retain the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer. |
16 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
17 |
|
|
* notice, this list of conditions and the following disclaimer in the |
18 |
|
|
* documentation and/or other materials provided with the distribution. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 |
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 |
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 |
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 |
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 |
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 |
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 |
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 |
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 |
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 |
|
|
* POSSIBILITY OF SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
/* |
34 |
|
|
* FTP User Program -- Command line file retrieval |
35 |
|
|
*/ |
36 |
|
|
|
37 |
|
|
#include <sys/types.h> |
38 |
|
|
#include <sys/socket.h> |
39 |
|
|
#include <sys/stat.h> |
40 |
|
|
|
41 |
|
|
#include <netinet/in.h> |
42 |
|
|
|
43 |
|
|
#include <arpa/ftp.h> |
44 |
|
|
#include <arpa/inet.h> |
45 |
|
|
|
46 |
|
|
#include <ctype.h> |
47 |
|
|
#include <err.h> |
48 |
|
|
#include <libgen.h> |
49 |
|
|
#include <netdb.h> |
50 |
|
|
#include <fcntl.h> |
51 |
|
|
#include <signal.h> |
52 |
|
|
#include <stdio.h> |
53 |
|
|
#include <stdarg.h> |
54 |
|
|
#include <errno.h> |
55 |
|
|
#include <stdlib.h> |
56 |
|
|
#include <string.h> |
57 |
|
|
#include <unistd.h> |
58 |
|
|
#include <util.h> |
59 |
|
|
#include <resolv.h> |
60 |
|
|
|
61 |
|
|
#ifndef SMALL |
62 |
|
|
#include <tls.h> |
63 |
|
|
#else /* !SMALL */ |
64 |
|
|
struct tls; |
65 |
|
|
#endif /* !SMALL */ |
66 |
|
|
|
67 |
|
|
#include "ftp_var.h" |
68 |
|
|
#include "cmds.h" |
69 |
|
|
|
70 |
|
|
static int url_get(const char *, const char *, const char *); |
71 |
|
|
void aborthttp(int); |
72 |
|
|
void abortfile(int); |
73 |
|
|
char hextochar(const char *); |
74 |
|
|
char *urldecode(const char *); |
75 |
|
|
char *recode_credentials(const char *_userinfo); |
76 |
|
|
int ftp_printf(FILE *, struct tls *, const char *, ...) __attribute__((format(printf, 3, 4))); |
77 |
|
|
char *ftp_readline(FILE *, struct tls *, size_t *); |
78 |
|
|
size_t ftp_read(FILE *, struct tls *, char *, size_t); |
79 |
|
|
#ifndef SMALL |
80 |
|
|
int proxy_connect(int, char *, char *); |
81 |
|
|
int SSL_vprintf(struct tls *, const char *, va_list); |
82 |
|
|
char *SSL_readline(struct tls *, size_t *); |
83 |
|
|
#endif /* !SMALL */ |
84 |
|
|
|
85 |
|
|
#define FTP_URL "ftp://" /* ftp URL prefix */ |
86 |
|
|
#define HTTP_URL "http://" /* http URL prefix */ |
87 |
|
|
#define HTTPS_URL "https://" /* https URL prefix */ |
88 |
|
|
#define FILE_URL "file:" /* file URL prefix */ |
89 |
|
|
#define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ |
90 |
|
|
#define HTTP_PROXY "http_proxy" /* env var with http proxy location */ |
91 |
|
|
|
92 |
|
|
#define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) |
93 |
|
|
|
94 |
|
|
static const char at_encoding_warning[] = |
95 |
|
|
"Extra `@' characters in usernames and passwords should be encoded as %%40"; |
96 |
|
|
|
97 |
|
|
jmp_buf httpabort; |
98 |
|
|
|
99 |
|
|
static int redirect_loop; |
100 |
|
|
|
101 |
|
|
/* |
102 |
|
|
* Determine whether the character needs encoding, per RFC1738: |
103 |
|
|
* - No corresponding graphic US-ASCII. |
104 |
|
|
* - Unsafe characters. |
105 |
|
|
*/ |
106 |
|
|
static int |
107 |
|
|
unsafe_char(const char *c0) |
108 |
|
|
{ |
109 |
|
|
const char *unsafe_chars = " <>\"#{}|\\^~[]`"; |
110 |
|
|
const unsigned char *c = (const unsigned char *)c0; |
111 |
|
|
|
112 |
|
|
/* |
113 |
|
|
* No corresponding graphic US-ASCII. |
114 |
|
|
* Control characters and octets not used in US-ASCII. |
115 |
|
|
*/ |
116 |
|
|
return (iscntrl(*c) || !isascii(*c) || |
117 |
|
|
|
118 |
|
|
/* |
119 |
|
|
* Unsafe characters. |
120 |
|
|
* '%' is also unsafe, if is not followed by two |
121 |
|
|
* hexadecimal digits. |
122 |
|
|
*/ |
123 |
|
|
strchr(unsafe_chars, *c) != NULL || |
124 |
|
|
(*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c)))); |
125 |
|
|
} |
126 |
|
|
|
127 |
|
|
/* |
128 |
|
|
* Encode given URL, per RFC1738. |
129 |
|
|
* Allocate and return string to the caller. |
130 |
|
|
*/ |
131 |
|
|
static char * |
132 |
|
|
url_encode(const char *path) |
133 |
|
|
{ |
134 |
|
|
size_t i, length, new_length; |
135 |
|
|
char *epath, *epathp; |
136 |
|
|
|
137 |
|
|
length = new_length = strlen(path); |
138 |
|
|
|
139 |
|
|
/* |
140 |
|
|
* First pass: |
141 |
|
|
* Count unsafe characters, and determine length of the |
142 |
|
|
* final URL. |
143 |
|
|
*/ |
144 |
|
|
for (i = 0; i < length; i++) |
145 |
|
|
if (unsafe_char(path + i)) |
146 |
|
|
new_length += 2; |
147 |
|
|
|
148 |
|
|
epath = epathp = malloc(new_length + 1); /* One more for '\0'. */ |
149 |
|
|
if (epath == NULL) |
150 |
|
|
err(1, "Can't allocate memory for URL encoding"); |
151 |
|
|
|
152 |
|
|
/* |
153 |
|
|
* Second pass: |
154 |
|
|
* Encode, and copy final URL. |
155 |
|
|
*/ |
156 |
|
|
for (i = 0; i < length; i++) |
157 |
|
|
if (unsafe_char(path + i)) { |
158 |
|
|
snprintf(epathp, 4, "%%" "%02x", |
159 |
|
|
(unsigned char)path[i]); |
160 |
|
|
epathp += 3; |
161 |
|
|
} else |
162 |
|
|
*(epathp++) = path[i]; |
163 |
|
|
|
164 |
|
|
*epathp = '\0'; |
165 |
|
|
return (epath); |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
/* |
169 |
|
|
* Retrieve URL, via the proxy in $proxyvar if necessary. |
170 |
|
|
* Modifies the string argument given. |
171 |
|
|
* Returns -1 on failure, 0 on success |
172 |
|
|
*/ |
173 |
|
|
static int |
174 |
|
|
url_get(const char *origline, const char *proxyenv, const char *outfile) |
175 |
|
|
{ |
176 |
|
|
char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4]; |
177 |
|
|
char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL; |
178 |
|
|
char *epath, *redirurl, *loctail, *h, *p; |
179 |
|
|
int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1; |
180 |
|
|
struct addrinfo hints, *res0, *res, *ares = NULL; |
181 |
|
|
const char * volatile savefile; |
182 |
|
|
char * volatile proxyurl = NULL; |
183 |
|
|
char *credentials = NULL; |
184 |
|
|
volatile int s = -1, out; |
185 |
|
|
volatile sig_t oldintr, oldinti; |
186 |
|
|
FILE *fin = NULL; |
187 |
|
|
off_t hashbytes; |
188 |
|
|
const char *errstr; |
189 |
|
|
ssize_t len, wlen; |
190 |
|
|
char *proxyhost = NULL; |
191 |
|
|
#ifndef SMALL |
192 |
|
|
char *sslpath = NULL, *sslhost = NULL; |
193 |
|
|
char *locbase, *full_host = NULL; |
194 |
|
|
const char *scheme; |
195 |
|
|
int ishttpurl = 0, ishttpsurl = 0; |
196 |
|
|
#endif /* !SMALL */ |
197 |
|
|
struct tls *tls = NULL; |
198 |
|
|
int status; |
199 |
|
|
int save_errno; |
200 |
|
|
const size_t buflen = 128 * 1024; |
201 |
|
|
|
202 |
|
|
direction = "received"; |
203 |
|
|
|
204 |
|
|
newline = strdup(origline); |
205 |
|
|
if (newline == NULL) |
206 |
|
|
errx(1, "Can't allocate memory to parse URL"); |
207 |
|
|
if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { |
208 |
|
|
host = newline + sizeof(HTTP_URL) - 1; |
209 |
|
|
#ifndef SMALL |
210 |
|
|
ishttpurl = 1; |
211 |
|
|
scheme = HTTP_URL; |
212 |
|
|
#endif /* !SMALL */ |
213 |
|
|
} else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) { |
214 |
|
|
host = newline + sizeof(FTP_URL) - 1; |
215 |
|
|
isftpurl = 1; |
216 |
|
|
#ifndef SMALL |
217 |
|
|
scheme = FTP_URL; |
218 |
|
|
#endif /* !SMALL */ |
219 |
|
|
} else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) { |
220 |
|
|
host = newline + sizeof(FILE_URL) - 1; |
221 |
|
|
isfileurl = 1; |
222 |
|
|
#ifndef SMALL |
223 |
|
|
scheme = FILE_URL; |
224 |
|
|
} else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) { |
225 |
|
|
host = newline + sizeof(HTTPS_URL) - 1; |
226 |
|
|
ishttpsurl = 1; |
227 |
|
|
scheme = HTTPS_URL; |
228 |
|
|
#endif /* !SMALL */ |
229 |
|
|
} else |
230 |
|
|
errx(1, "url_get: Invalid URL '%s'", newline); |
231 |
|
|
|
232 |
|
|
if (isfileurl) { |
233 |
|
|
path = host; |
234 |
|
|
} else { |
235 |
|
|
path = strchr(host, '/'); /* Find path */ |
236 |
|
|
if (EMPTYSTRING(path)) { |
237 |
|
|
if (outfile) { /* No slash, but */ |
238 |
|
|
path=strchr(host,'\0'); /* we have outfile. */ |
239 |
|
|
goto noslash; |
240 |
|
|
} |
241 |
|
|
if (isftpurl) |
242 |
|
|
goto noftpautologin; |
243 |
|
|
warnx("No `/' after host (use -o): %s", origline); |
244 |
|
|
goto cleanup_url_get; |
245 |
|
|
} |
246 |
|
|
*path++ = '\0'; |
247 |
|
|
if (EMPTYSTRING(path) && !outfile) { |
248 |
|
|
if (isftpurl) |
249 |
|
|
goto noftpautologin; |
250 |
|
|
warnx("No filename after host (use -o): %s", origline); |
251 |
|
|
goto cleanup_url_get; |
252 |
|
|
} |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
noslash: |
256 |
|
|
|
257 |
|
|
#ifndef SMALL |
258 |
|
|
/* |
259 |
|
|
* Look for auth header in host, since now host does not |
260 |
|
|
* contain the path. Basic auth from RFC 2617, valid |
261 |
|
|
* characters for path are in RFC 3986 section 3.3. |
262 |
|
|
*/ |
263 |
|
|
if (proxyenv == NULL && (ishttpurl || ishttpsurl)) { |
264 |
|
|
if ((p = strchr(host, '@')) != NULL) { |
265 |
|
|
*p = '\0'; |
266 |
|
|
credentials = recode_credentials(host); |
267 |
|
|
host = p + 1; |
268 |
|
|
} |
269 |
|
|
} |
270 |
|
|
#endif /* SMALL */ |
271 |
|
|
|
272 |
|
|
if (outfile) |
273 |
|
|
savefile = outfile; |
274 |
|
|
else { |
275 |
|
|
if (path[strlen(path) - 1] == '/') /* Consider no file */ |
276 |
|
|
savefile = NULL; /* after dir invalid. */ |
277 |
|
|
else |
278 |
|
|
savefile = basename(path); |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
if (EMPTYSTRING(savefile)) { |
282 |
|
|
if (isftpurl) |
283 |
|
|
goto noftpautologin; |
284 |
|
|
warnx("No filename after directory (use -o): %s", origline); |
285 |
|
|
goto cleanup_url_get; |
286 |
|
|
} |
287 |
|
|
|
288 |
|
|
#ifndef SMALL |
289 |
|
|
if (resume && pipeout) { |
290 |
|
|
warnx("can't append to stdout"); |
291 |
|
|
goto cleanup_url_get; |
292 |
|
|
} |
293 |
|
|
#endif /* !SMALL */ |
294 |
|
|
|
295 |
|
|
if (!isfileurl && proxyenv != NULL) { /* use proxy */ |
296 |
|
|
#ifndef SMALL |
297 |
|
|
if (ishttpsurl) { |
298 |
|
|
sslpath = strdup(path); |
299 |
|
|
sslhost = strdup(host); |
300 |
|
|
if (! sslpath || ! sslhost) |
301 |
|
|
errx(1, "Can't allocate memory for https path/host."); |
302 |
|
|
} |
303 |
|
|
#endif /* !SMALL */ |
304 |
|
|
proxyhost = strdup(host); |
305 |
|
|
if (proxyhost == NULL) |
306 |
|
|
errx(1, "Can't allocate memory for proxy host."); |
307 |
|
|
proxyurl = strdup(proxyenv); |
308 |
|
|
if (proxyurl == NULL) |
309 |
|
|
errx(1, "Can't allocate memory for proxy URL."); |
310 |
|
|
if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) |
311 |
|
|
host = proxyurl + sizeof(HTTP_URL) - 1; |
312 |
|
|
else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0) |
313 |
|
|
host = proxyurl + sizeof(FTP_URL) - 1; |
314 |
|
|
else { |
315 |
|
|
warnx("Malformed proxy URL: %s", proxyenv); |
316 |
|
|
goto cleanup_url_get; |
317 |
|
|
} |
318 |
|
|
if (EMPTYSTRING(host)) { |
319 |
|
|
warnx("Malformed proxy URL: %s", proxyenv); |
320 |
|
|
goto cleanup_url_get; |
321 |
|
|
} |
322 |
|
|
if (*--path == '\0') |
323 |
|
|
*path = '/'; /* add / back to real path */ |
324 |
|
|
path = strchr(host, '/'); /* remove trailing / on host */ |
325 |
|
|
if (!EMPTYSTRING(path)) |
326 |
|
|
*path++ = '\0'; /* i guess this ++ is useless */ |
327 |
|
|
|
328 |
|
|
path = strchr(host, '@'); /* look for credentials in proxy */ |
329 |
|
|
if (!EMPTYSTRING(path)) { |
330 |
|
|
*path = '\0'; |
331 |
|
|
if (strchr(host, ':') == NULL) { |
332 |
|
|
warnx("Malformed proxy URL: %s", proxyenv); |
333 |
|
|
goto cleanup_url_get; |
334 |
|
|
} |
335 |
|
|
credentials = recode_credentials(host); |
336 |
|
|
*path = '@'; /* restore @ in proxyurl */ |
337 |
|
|
|
338 |
|
|
/* |
339 |
|
|
* This removes the password from proxyurl, |
340 |
|
|
* filling with stars |
341 |
|
|
*/ |
342 |
|
|
for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@'; |
343 |
|
|
host++) |
344 |
|
|
*host = '*'; |
345 |
|
|
|
346 |
|
|
host = path + 1; |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
path = newline; |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
if (isfileurl) { |
353 |
|
|
struct stat st; |
354 |
|
|
|
355 |
|
|
s = open(path, O_RDONLY); |
356 |
|
|
if (s == -1) { |
357 |
|
|
warn("Can't open file %s", path); |
358 |
|
|
goto cleanup_url_get; |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
if (fstat(s, &st) == -1) |
362 |
|
|
filesize = -1; |
363 |
|
|
else |
364 |
|
|
filesize = st.st_size; |
365 |
|
|
|
366 |
|
|
/* Open the output file. */ |
367 |
|
|
if (!pipeout) { |
368 |
|
|
#ifndef SMALL |
369 |
|
|
if (resume) |
370 |
|
|
out = open(savefile, O_CREAT | O_WRONLY | |
371 |
|
|
O_APPEND, 0666); |
372 |
|
|
|
373 |
|
|
else |
374 |
|
|
#endif /* !SMALL */ |
375 |
|
|
out = open(savefile, O_CREAT | O_WRONLY | |
376 |
|
|
O_TRUNC, 0666); |
377 |
|
|
if (out < 0) { |
378 |
|
|
warn("Can't open %s", savefile); |
379 |
|
|
goto cleanup_url_get; |
380 |
|
|
} |
381 |
|
|
} else |
382 |
|
|
out = fileno(stdout); |
383 |
|
|
|
384 |
|
|
#ifndef SMALL |
385 |
|
|
if (resume) { |
386 |
|
|
if (fstat(out, &st) == -1) { |
387 |
|
|
warn("Can't fstat %s", savefile); |
388 |
|
|
goto cleanup_url_get; |
389 |
|
|
} |
390 |
|
|
if (lseek(s, st.st_size, SEEK_SET) == -1) { |
391 |
|
|
warn("Can't lseek %s", path); |
392 |
|
|
goto cleanup_url_get; |
393 |
|
|
} |
394 |
|
|
restart_point = st.st_size; |
395 |
|
|
} |
396 |
|
|
#endif /* !SMALL */ |
397 |
|
|
|
398 |
|
|
/* Trap signals */ |
399 |
|
|
oldintr = NULL; |
400 |
|
|
oldinti = NULL; |
401 |
|
|
if (setjmp(httpabort)) { |
402 |
|
|
if (oldintr) |
403 |
|
|
(void)signal(SIGINT, oldintr); |
404 |
|
|
if (oldinti) |
405 |
|
|
(void)signal(SIGINFO, oldinti); |
406 |
|
|
goto cleanup_url_get; |
407 |
|
|
} |
408 |
|
|
oldintr = signal(SIGINT, abortfile); |
409 |
|
|
|
410 |
|
|
bytes = 0; |
411 |
|
|
hashbytes = mark; |
412 |
|
|
progressmeter(-1, path); |
413 |
|
|
|
414 |
|
|
if ((buf = malloc(buflen)) == NULL) |
415 |
|
|
errx(1, "Can't allocate memory for transfer buffer"); |
416 |
|
|
|
417 |
|
|
/* Finally, suck down the file. */ |
418 |
|
|
i = 0; |
419 |
|
|
oldinti = signal(SIGINFO, psummary); |
420 |
|
|
while ((len = read(s, buf, buflen)) > 0) { |
421 |
|
|
bytes += len; |
422 |
|
|
for (cp = buf; len > 0; len -= i, cp += i) { |
423 |
|
|
if ((i = write(out, cp, len)) == -1) { |
424 |
|
|
warn("Writing %s", savefile); |
425 |
|
|
signal(SIGINFO, oldinti); |
426 |
|
|
goto cleanup_url_get; |
427 |
|
|
} |
428 |
|
|
else if (i == 0) |
429 |
|
|
break; |
430 |
|
|
} |
431 |
|
|
if (hash && !progress) { |
432 |
|
|
while (bytes >= hashbytes) { |
433 |
|
|
(void)putc('#', ttyout); |
434 |
|
|
hashbytes += mark; |
435 |
|
|
} |
436 |
|
|
(void)fflush(ttyout); |
437 |
|
|
} |
438 |
|
|
} |
439 |
|
|
signal(SIGINFO, oldinti); |
440 |
|
|
if (hash && !progress && bytes > 0) { |
441 |
|
|
if (bytes < mark) |
442 |
|
|
(void)putc('#', ttyout); |
443 |
|
|
(void)putc('\n', ttyout); |
444 |
|
|
(void)fflush(ttyout); |
445 |
|
|
} |
446 |
|
|
if (len != 0) { |
447 |
|
|
warn("Reading from file"); |
448 |
|
|
goto cleanup_url_get; |
449 |
|
|
} |
450 |
|
|
progressmeter(1, NULL); |
451 |
|
|
if (verbose) |
452 |
|
|
ptransfer(0); |
453 |
|
|
(void)signal(SIGINT, oldintr); |
454 |
|
|
|
455 |
|
|
rval = 0; |
456 |
|
|
goto cleanup_url_get; |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && |
460 |
|
|
(hosttail[1] == '\0' || hosttail[1] == ':')) { |
461 |
|
|
host++; |
462 |
|
|
*hosttail++ = '\0'; |
463 |
|
|
#ifndef SMALL |
464 |
|
|
if (asprintf(&full_host, "[%s]", host) == -1) |
465 |
|
|
errx(1, "Cannot allocate memory for hostname"); |
466 |
|
|
#endif /* !SMALL */ |
467 |
|
|
} else |
468 |
|
|
hosttail = host; |
469 |
|
|
|
470 |
|
|
portnum = strrchr(hosttail, ':'); /* find portnum */ |
471 |
|
|
if (portnum != NULL) |
472 |
|
|
*portnum++ = '\0'; |
473 |
|
|
|
474 |
|
|
#ifndef SMALL |
475 |
|
|
if (full_host == NULL) |
476 |
|
|
if ((full_host = strdup(host)) == NULL) |
477 |
|
|
errx(1, "Cannot allocate memory for hostname"); |
478 |
|
|
if (debug) |
479 |
|
|
fprintf(ttyout, "host %s, port %s, path %s, " |
480 |
|
|
"save as %s, auth %s.\n", |
481 |
|
|
host, portnum, path, savefile, credentials); |
482 |
|
|
#endif /* !SMALL */ |
483 |
|
|
|
484 |
|
|
memset(&hints, 0, sizeof(hints)); |
485 |
|
|
hints.ai_family = family; |
486 |
|
|
hints.ai_socktype = SOCK_STREAM; |
487 |
|
|
#ifndef SMALL |
488 |
|
|
port = portnum ? portnum : (ishttpsurl ? httpsport : httpport); |
489 |
|
|
#else /* !SMALL */ |
490 |
|
|
port = portnum ? portnum : httpport; |
491 |
|
|
#endif /* !SMALL */ |
492 |
|
|
error = getaddrinfo(host, port, &hints, &res0); |
493 |
|
|
/* |
494 |
|
|
* If the services file is corrupt/missing, fall back |
495 |
|
|
* on our hard-coded defines. |
496 |
|
|
*/ |
497 |
|
|
if (error == EAI_SERVICE && port == httpport) { |
498 |
|
|
snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); |
499 |
|
|
error = getaddrinfo(host, pbuf, &hints, &res0); |
500 |
|
|
#ifndef SMALL |
501 |
|
|
} else if (error == EAI_SERVICE && port == httpsport) { |
502 |
|
|
snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); |
503 |
|
|
error = getaddrinfo(host, pbuf, &hints, &res0); |
504 |
|
|
#endif /* !SMALL */ |
505 |
|
|
} |
506 |
|
|
if (error) { |
507 |
|
|
warnx("%s: %s", host, gai_strerror(error)); |
508 |
|
|
goto cleanup_url_get; |
509 |
|
|
} |
510 |
|
|
|
511 |
|
|
#ifndef SMALL |
512 |
|
|
if (srcaddr) { |
513 |
|
|
hints.ai_flags |= AI_NUMERICHOST; |
514 |
|
|
error = getaddrinfo(srcaddr, NULL, &hints, &ares); |
515 |
|
|
if (error) { |
516 |
|
|
warnx("%s: %s", srcaddr, gai_strerror(error)); |
517 |
|
|
goto cleanup_url_get; |
518 |
|
|
} |
519 |
|
|
} |
520 |
|
|
#endif /* !SMALL */ |
521 |
|
|
|
522 |
|
|
/* ensure consistent order of the output */ |
523 |
|
|
if (verbose) |
524 |
|
|
setvbuf(ttyout, NULL, _IOLBF, 0); |
525 |
|
|
|
526 |
|
|
s = -1; |
527 |
|
|
for (res = res0; res; res = res->ai_next) { |
528 |
|
|
if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, |
529 |
|
|
sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) |
530 |
|
|
strlcpy(hbuf, "(unknown)", sizeof(hbuf)); |
531 |
|
|
if (verbose) |
532 |
|
|
fprintf(ttyout, "Trying %s...\n", hbuf); |
533 |
|
|
|
534 |
|
|
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); |
535 |
|
|
if (s == -1) { |
536 |
|
|
cause = "socket"; |
537 |
|
|
continue; |
538 |
|
|
} |
539 |
|
|
|
540 |
|
|
#ifndef SMALL |
541 |
|
|
if (srcaddr) { |
542 |
|
|
if (ares->ai_family != res->ai_family) { |
543 |
|
|
close(s); |
544 |
|
|
s = -1; |
545 |
|
|
errno = EINVAL; |
546 |
|
|
cause = "bind"; |
547 |
|
|
continue; |
548 |
|
|
} |
549 |
|
|
if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) { |
550 |
|
|
save_errno = errno; |
551 |
|
|
close(s); |
552 |
|
|
errno = save_errno; |
553 |
|
|
s = -1; |
554 |
|
|
cause = "bind"; |
555 |
|
|
continue; |
556 |
|
|
} |
557 |
|
|
} |
558 |
|
|
#endif /* !SMALL */ |
559 |
|
|
|
560 |
|
|
again: |
561 |
|
|
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { |
562 |
|
|
if (errno == EINTR) |
563 |
|
|
goto again; |
564 |
|
|
save_errno = errno; |
565 |
|
|
close(s); |
566 |
|
|
errno = save_errno; |
567 |
|
|
s = -1; |
568 |
|
|
cause = "connect"; |
569 |
|
|
continue; |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
/* get port in numeric */ |
573 |
|
|
if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0, |
574 |
|
|
pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0) |
575 |
|
|
port = pbuf; |
576 |
|
|
else |
577 |
|
|
port = NULL; |
578 |
|
|
|
579 |
|
|
#ifndef SMALL |
580 |
|
|
if (proxyenv && sslhost) |
581 |
|
|
proxy_connect(s, sslhost, credentials); |
582 |
|
|
#endif /* !SMALL */ |
583 |
|
|
break; |
584 |
|
|
} |
585 |
|
|
freeaddrinfo(res0); |
586 |
|
|
#ifndef SMALL |
587 |
|
|
if (srcaddr) |
588 |
|
|
freeaddrinfo(ares); |
589 |
|
|
#endif /* !SMALL */ |
590 |
|
|
if (s < 0) { |
591 |
|
|
warn("%s", cause); |
592 |
|
|
goto cleanup_url_get; |
593 |
|
|
} |
594 |
|
|
|
595 |
|
|
#ifndef SMALL |
596 |
|
|
if (ishttpsurl) { |
597 |
|
|
if (proxyenv && sslpath) { |
598 |
|
|
ishttpsurl = 0; |
599 |
|
|
proxyurl = NULL; |
600 |
|
|
path = sslpath; |
601 |
|
|
} |
602 |
|
|
if (sslhost == NULL) { |
603 |
|
|
sslhost = strdup(host); |
604 |
|
|
if (sslhost == NULL) |
605 |
|
|
errx(1, "Can't allocate memory for https host."); |
606 |
|
|
} |
607 |
|
|
if ((tls = tls_client()) == NULL) { |
608 |
|
|
fprintf(ttyout, "failed to create SSL client\n"); |
609 |
|
|
goto cleanup_url_get; |
610 |
|
|
} |
611 |
|
|
if (tls_configure(tls, tls_config) != 0) { |
612 |
|
|
fprintf(ttyout, "SSL configuration failure: %s\n", |
613 |
|
|
tls_error(tls)); |
614 |
|
|
goto cleanup_url_get; |
615 |
|
|
} |
616 |
|
|
if (tls_connect_socket(tls, s, sslhost) != 0) { |
617 |
|
|
fprintf(ttyout, "SSL failure: %s\n", tls_error(tls)); |
618 |
|
|
goto cleanup_url_get; |
619 |
|
|
} |
620 |
|
|
} else { |
621 |
|
|
fin = fdopen(s, "r+"); |
622 |
|
|
} |
623 |
|
|
#else /* !SMALL */ |
624 |
|
|
fin = fdopen(s, "r+"); |
625 |
|
|
#endif /* !SMALL */ |
626 |
|
|
|
627 |
|
|
if (verbose) |
628 |
|
|
fprintf(ttyout, "Requesting %s", origline); |
629 |
|
|
|
630 |
|
|
/* |
631 |
|
|
* Construct and send the request. Proxy requests don't want leading /. |
632 |
|
|
*/ |
633 |
|
|
#ifndef SMALL |
634 |
|
|
cookie_get(host, path, ishttpsurl, &buf); |
635 |
|
|
#endif /* !SMALL */ |
636 |
|
|
|
637 |
|
|
epath = url_encode(path); |
638 |
|
|
if (proxyurl) { |
639 |
|
|
if (verbose) |
640 |
|
|
fprintf(ttyout, " (via %s)\n", proxyurl); |
641 |
|
|
/* |
642 |
|
|
* Host: directive must use the destination host address for |
643 |
|
|
* the original URI (path). |
644 |
|
|
*/ |
645 |
|
|
if (credentials) |
646 |
|
|
ftp_printf(fin, tls, "GET %s HTTP/1.0\r\n" |
647 |
|
|
"Proxy-Authorization: Basic %s\r\n" |
648 |
|
|
"Host: %s\r\n%s%s\r\n\r\n", |
649 |
|
|
epath, credentials, |
650 |
|
|
proxyhost, buf ? buf : "", httpuseragent); |
651 |
|
|
else |
652 |
|
|
ftp_printf(fin, tls, "GET %s HTTP/1.0\r\n" |
653 |
|
|
"Host: %s\r\n%s%s\r\n\r\n", |
654 |
|
|
epath, proxyhost, buf ? buf : "", httpuseragent); |
655 |
|
|
} else { |
656 |
|
|
#ifndef SMALL |
657 |
|
|
if (resume) { |
658 |
|
|
struct stat stbuf; |
659 |
|
|
|
660 |
|
|
if (stat(savefile, &stbuf) == 0) |
661 |
|
|
restart_point = stbuf.st_size; |
662 |
|
|
else |
663 |
|
|
restart_point = 0; |
664 |
|
|
} |
665 |
|
|
if (credentials) { |
666 |
|
|
ftp_printf(fin, tls, |
667 |
|
|
"GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ", |
668 |
|
|
epath, restart_point ? |
669 |
|
|
"HTTP/1.1\r\nConnection: close" : "HTTP/1.0", |
670 |
|
|
credentials); |
671 |
|
|
free(credentials); |
672 |
|
|
credentials = NULL; |
673 |
|
|
} else |
674 |
|
|
#endif /* SMALL */ |
675 |
|
|
ftp_printf(fin, tls, "GET /%s %s\r\nHost: ", epath, |
676 |
|
|
#ifndef SMALL |
677 |
|
|
restart_point ? "HTTP/1.1\r\nConnection: close" : |
678 |
|
|
#endif /* !SMALL */ |
679 |
|
|
"HTTP/1.0"); |
680 |
|
|
if (proxyhost) { |
681 |
|
|
ftp_printf(fin, tls, "%s", proxyhost); |
682 |
|
|
port = NULL; |
683 |
|
|
} else if (strchr(host, ':')) { |
684 |
|
|
/* |
685 |
|
|
* strip off scoped address portion, since it's |
686 |
|
|
* local to node |
687 |
|
|
*/ |
688 |
|
|
h = strdup(host); |
689 |
|
|
if (h == NULL) |
690 |
|
|
errx(1, "Can't allocate memory."); |
691 |
|
|
if ((p = strchr(h, '%')) != NULL) |
692 |
|
|
*p = '\0'; |
693 |
|
|
ftp_printf(fin, tls, "[%s]", h); |
694 |
|
|
free(h); |
695 |
|
|
} else |
696 |
|
|
ftp_printf(fin, tls, "%s", host); |
697 |
|
|
|
698 |
|
|
/* |
699 |
|
|
* Send port number only if it's specified and does not equal |
700 |
|
|
* 80. Some broken HTTP servers get confused if you explicitly |
701 |
|
|
* send them the port number. |
702 |
|
|
*/ |
703 |
|
|
#ifndef SMALL |
704 |
|
|
if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0) |
705 |
|
|
ftp_printf(fin, tls, ":%s", port); |
706 |
|
|
if (restart_point) |
707 |
|
|
ftp_printf(fin, tls, "\r\nRange: bytes=%lld-", |
708 |
|
|
(long long)restart_point); |
709 |
|
|
#else /* !SMALL */ |
710 |
|
|
if (port && strcmp(port, "80") != 0) |
711 |
|
|
ftp_printf(fin, tls, ":%s", port); |
712 |
|
|
#endif /* !SMALL */ |
713 |
|
|
ftp_printf(fin, tls, "\r\n%s%s\r\n\r\n", |
714 |
|
|
buf ? buf : "", httpuseragent); |
715 |
|
|
if (verbose) |
716 |
|
|
fprintf(ttyout, "\n"); |
717 |
|
|
} |
718 |
|
|
free(epath); |
719 |
|
|
|
720 |
|
|
#ifndef SMALL |
721 |
|
|
free(buf); |
722 |
|
|
#endif /* !SMALL */ |
723 |
|
|
buf = NULL; |
724 |
|
|
|
725 |
|
|
if (fin != NULL && fflush(fin) == EOF) { |
726 |
|
|
warn("Writing HTTP request"); |
727 |
|
|
goto cleanup_url_get; |
728 |
|
|
} |
729 |
|
|
if ((buf = ftp_readline(fin, tls, &len)) == NULL) { |
730 |
|
|
warn("Receiving HTTP reply"); |
731 |
|
|
goto cleanup_url_get; |
732 |
|
|
} |
733 |
|
|
|
734 |
|
|
while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) |
735 |
|
|
buf[--len] = '\0'; |
736 |
|
|
#ifndef SMALL |
737 |
|
|
if (debug) |
738 |
|
|
fprintf(ttyout, "received '%s'\n", buf); |
739 |
|
|
#endif /* !SMALL */ |
740 |
|
|
|
741 |
|
|
cp = strchr(buf, ' '); |
742 |
|
|
if (cp == NULL) |
743 |
|
|
goto improper; |
744 |
|
|
else |
745 |
|
|
cp++; |
746 |
|
|
|
747 |
|
|
strlcpy(ststr, cp, sizeof(ststr)); |
748 |
|
|
status = strtonum(ststr, 200, 416, &errstr); |
749 |
|
|
if (errstr) { |
750 |
|
|
warnx("Error retrieving file: %s", cp); |
751 |
|
|
goto cleanup_url_get; |
752 |
|
|
} |
753 |
|
|
|
754 |
|
|
switch (status) { |
755 |
|
|
case 200: /* OK */ |
756 |
|
|
#ifndef SMALL |
757 |
|
|
/* |
758 |
|
|
* When we request a partial file, and we receive an HTTP 200 |
759 |
|
|
* it is a good indication that the server doesn't support |
760 |
|
|
* range requests, and is about to send us the entire file. |
761 |
|
|
* If the restart_point == 0, then we are not actually |
762 |
|
|
* requesting a partial file, and an HTTP 200 is appropriate. |
763 |
|
|
*/ |
764 |
|
|
if (resume && restart_point != 0) { |
765 |
|
|
warnx("Server does not support resume."); |
766 |
|
|
restart_point = resume = 0; |
767 |
|
|
} |
768 |
|
|
/* FALLTHROUGH */ |
769 |
|
|
case 206: /* Partial Content */ |
770 |
|
|
#endif /* !SMALL */ |
771 |
|
|
break; |
772 |
|
|
case 301: /* Moved Permanently */ |
773 |
|
|
case 302: /* Found */ |
774 |
|
|
case 303: /* See Other */ |
775 |
|
|
case 307: /* Temporary Redirect */ |
776 |
|
|
isredirect++; |
777 |
|
|
if (redirect_loop++ > 10) { |
778 |
|
|
warnx("Too many redirections requested"); |
779 |
|
|
goto cleanup_url_get; |
780 |
|
|
} |
781 |
|
|
break; |
782 |
|
|
#ifndef SMALL |
783 |
|
|
case 416: /* Requested Range Not Satisfiable */ |
784 |
|
|
warnx("File is already fully retrieved."); |
785 |
|
|
goto cleanup_url_get; |
786 |
|
|
#endif /* !SMALL */ |
787 |
|
|
default: |
788 |
|
|
warnx("Error retrieving file: %s", cp); |
789 |
|
|
goto cleanup_url_get; |
790 |
|
|
} |
791 |
|
|
|
792 |
|
|
/* |
793 |
|
|
* Read the rest of the header. |
794 |
|
|
*/ |
795 |
|
|
free(buf); |
796 |
|
|
filesize = -1; |
797 |
|
|
|
798 |
|
|
for (;;) { |
799 |
|
|
if ((buf = ftp_readline(fin, tls, &len)) == NULL) { |
800 |
|
|
warn("Receiving HTTP reply"); |
801 |
|
|
goto cleanup_url_get; |
802 |
|
|
} |
803 |
|
|
|
804 |
|
|
while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) |
805 |
|
|
buf[--len] = '\0'; |
806 |
|
|
if (len == 0) |
807 |
|
|
break; |
808 |
|
|
#ifndef SMALL |
809 |
|
|
if (debug) |
810 |
|
|
fprintf(ttyout, "received '%s'\n", buf); |
811 |
|
|
#endif /* !SMALL */ |
812 |
|
|
|
813 |
|
|
/* Look for some headers */ |
814 |
|
|
cp = buf; |
815 |
|
|
#define CONTENTLEN "Content-Length: " |
816 |
|
|
if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) { |
817 |
|
|
size_t s; |
818 |
|
|
cp += sizeof(CONTENTLEN) - 1; |
819 |
|
|
if ((s = strcspn(cp, " \t"))) |
820 |
|
|
*(cp+s) = 0; |
821 |
|
|
filesize = strtonum(cp, 0, LLONG_MAX, &errstr); |
822 |
|
|
if (errstr != NULL) |
823 |
|
|
goto improper; |
824 |
|
|
#ifndef SMALL |
825 |
|
|
if (restart_point) |
826 |
|
|
filesize += restart_point; |
827 |
|
|
#endif /* !SMALL */ |
828 |
|
|
#define LOCATION "Location: " |
829 |
|
|
} else if (isredirect && |
830 |
|
|
strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) { |
831 |
|
|
cp += sizeof(LOCATION) - 1; |
832 |
|
|
/* |
833 |
|
|
* If there is a colon before the first slash, this URI |
834 |
|
|
* is not relative. RFC 3986 4.2 |
835 |
|
|
*/ |
836 |
|
|
if (cp[strcspn(cp, ":/")] != ':') { |
837 |
|
|
#ifdef SMALL |
838 |
|
|
errx(1, "Relative redirect not supported"); |
839 |
|
|
#else /* SMALL */ |
840 |
|
|
/* XXX doesn't handle protocol-relative URIs */ |
841 |
|
|
if (*cp == '/') { |
842 |
|
|
locbase = NULL; |
843 |
|
|
cp++; |
844 |
|
|
} else { |
845 |
|
|
locbase = strdup(path); |
846 |
|
|
if (locbase == NULL) |
847 |
|
|
errx(1, "Can't allocate memory" |
848 |
|
|
" for location base"); |
849 |
|
|
loctail = strchr(locbase, '#'); |
850 |
|
|
if (loctail != NULL) |
851 |
|
|
*loctail = '\0'; |
852 |
|
|
loctail = strchr(locbase, '?'); |
853 |
|
|
if (loctail != NULL) |
854 |
|
|
*loctail = '\0'; |
855 |
|
|
loctail = strrchr(locbase, '/'); |
856 |
|
|
if (loctail == NULL) { |
857 |
|
|
free(locbase); |
858 |
|
|
locbase = NULL; |
859 |
|
|
} else |
860 |
|
|
loctail[1] = '\0'; |
861 |
|
|
} |
862 |
|
|
/* Contruct URL from relative redirect */ |
863 |
|
|
if (asprintf(&redirurl, "%s%s%s%s/%s%s", |
864 |
|
|
scheme, full_host, |
865 |
|
|
portnum ? ":" : "", |
866 |
|
|
portnum ? portnum : "", |
867 |
|
|
locbase ? locbase : "", |
868 |
|
|
cp) == -1) |
869 |
|
|
errx(1, "Cannot build " |
870 |
|
|
"redirect URL"); |
871 |
|
|
free(locbase); |
872 |
|
|
#endif /* SMALL */ |
873 |
|
|
} else if ((redirurl = strdup(cp)) == NULL) |
874 |
|
|
errx(1, "Cannot allocate memory for URL"); |
875 |
|
|
loctail = strchr(redirurl, '#'); |
876 |
|
|
if (loctail != NULL) |
877 |
|
|
*loctail = '\0'; |
878 |
|
|
if (verbose) |
879 |
|
|
fprintf(ttyout, "Redirected to %s\n", redirurl); |
880 |
|
|
if (fin != NULL) |
881 |
|
|
fclose(fin); |
882 |
|
|
else if (s != -1) |
883 |
|
|
close(s); |
884 |
|
|
rval = url_get(redirurl, proxyenv, savefile); |
885 |
|
|
free(redirurl); |
886 |
|
|
goto cleanup_url_get; |
887 |
|
|
} |
888 |
|
|
free(buf); |
889 |
|
|
} |
890 |
|
|
|
891 |
|
|
/* Open the output file. */ |
892 |
|
|
if (!pipeout) { |
893 |
|
|
#ifndef SMALL |
894 |
|
|
if (resume) |
895 |
|
|
out = open(savefile, O_CREAT | O_WRONLY | O_APPEND, |
896 |
|
|
0666); |
897 |
|
|
else |
898 |
|
|
#endif /* !SMALL */ |
899 |
|
|
out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, |
900 |
|
|
0666); |
901 |
|
|
if (out < 0) { |
902 |
|
|
warn("Can't open %s", savefile); |
903 |
|
|
goto cleanup_url_get; |
904 |
|
|
} |
905 |
|
|
} else |
906 |
|
|
out = fileno(stdout); |
907 |
|
|
|
908 |
|
|
/* Trap signals */ |
909 |
|
|
oldintr = NULL; |
910 |
|
|
oldinti = NULL; |
911 |
|
|
if (setjmp(httpabort)) { |
912 |
|
|
if (oldintr) |
913 |
|
|
(void)signal(SIGINT, oldintr); |
914 |
|
|
if (oldinti) |
915 |
|
|
(void)signal(SIGINFO, oldinti); |
916 |
|
|
goto cleanup_url_get; |
917 |
|
|
} |
918 |
|
|
oldintr = signal(SIGINT, aborthttp); |
919 |
|
|
|
920 |
|
|
bytes = 0; |
921 |
|
|
hashbytes = mark; |
922 |
|
|
progressmeter(-1, path); |
923 |
|
|
|
924 |
|
|
free(buf); |
925 |
|
|
|
926 |
|
|
/* Finally, suck down the file. */ |
927 |
|
|
if ((buf = malloc(buflen)) == NULL) |
928 |
|
|
errx(1, "Can't allocate memory for transfer buffer"); |
929 |
|
|
i = 0; |
930 |
|
|
len = 1; |
931 |
|
|
oldinti = signal(SIGINFO, psummary); |
932 |
|
|
while (len > 0) { |
933 |
|
|
len = ftp_read(fin, tls, buf, buflen); |
934 |
|
|
bytes += len; |
935 |
|
|
for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { |
936 |
|
|
if ((i = write(out, cp, wlen)) == -1) { |
937 |
|
|
warn("Writing %s", savefile); |
938 |
|
|
signal(SIGINFO, oldinti); |
939 |
|
|
goto cleanup_url_get; |
940 |
|
|
} |
941 |
|
|
else if (i == 0) |
942 |
|
|
break; |
943 |
|
|
} |
944 |
|
|
if (hash && !progress) { |
945 |
|
|
while (bytes >= hashbytes) { |
946 |
|
|
(void)putc('#', ttyout); |
947 |
|
|
hashbytes += mark; |
948 |
|
|
} |
949 |
|
|
(void)fflush(ttyout); |
950 |
|
|
} |
951 |
|
|
} |
952 |
|
|
signal(SIGINFO, oldinti); |
953 |
|
|
if (hash && !progress && bytes > 0) { |
954 |
|
|
if (bytes < mark) |
955 |
|
|
(void)putc('#', ttyout); |
956 |
|
|
(void)putc('\n', ttyout); |
957 |
|
|
(void)fflush(ttyout); |
958 |
|
|
} |
959 |
|
|
if (len != 0) { |
960 |
|
|
warn("Reading from socket"); |
961 |
|
|
goto cleanup_url_get; |
962 |
|
|
} |
963 |
|
|
progressmeter(1, NULL); |
964 |
|
|
if ( |
965 |
|
|
#ifndef SMALL |
966 |
|
|
!resume && |
967 |
|
|
#endif /* !SMALL */ |
968 |
|
|
filesize != -1 && len == 0 && bytes != filesize) { |
969 |
|
|
if (verbose) |
970 |
|
|
fputs("Read short file.\n", ttyout); |
971 |
|
|
goto cleanup_url_get; |
972 |
|
|
} |
973 |
|
|
|
974 |
|
|
if (verbose) |
975 |
|
|
ptransfer(0); |
976 |
|
|
(void)signal(SIGINT, oldintr); |
977 |
|
|
|
978 |
|
|
rval = 0; |
979 |
|
|
goto cleanup_url_get; |
980 |
|
|
|
981 |
|
|
noftpautologin: |
982 |
|
|
warnx( |
983 |
|
|
"Auto-login using ftp URLs isn't supported when using $ftp_proxy"); |
984 |
|
|
goto cleanup_url_get; |
985 |
|
|
|
986 |
|
|
improper: |
987 |
|
|
warnx("Improper response from %s", host); |
988 |
|
|
|
989 |
|
|
cleanup_url_get: |
990 |
|
|
#ifndef SMALL |
991 |
|
|
if (tls != NULL) { |
992 |
|
|
tls_close(tls); |
993 |
|
|
tls_free(tls); |
994 |
|
|
} |
995 |
|
|
free(full_host); |
996 |
|
|
free(sslhost); |
997 |
|
|
#endif /* !SMALL */ |
998 |
|
|
if (fin != NULL) |
999 |
|
|
fclose(fin); |
1000 |
|
|
else if (s != -1) |
1001 |
|
|
close(s); |
1002 |
|
|
free(buf); |
1003 |
|
|
free(proxyhost); |
1004 |
|
|
free(proxyurl); |
1005 |
|
|
free(newline); |
1006 |
|
|
free(credentials); |
1007 |
|
|
return (rval); |
1008 |
|
|
} |
1009 |
|
|
|
1010 |
|
|
/* |
1011 |
|
|
* Abort a http retrieval |
1012 |
|
|
*/ |
1013 |
|
|
/* ARGSUSED */ |
1014 |
|
|
void |
1015 |
|
|
aborthttp(int signo) |
1016 |
|
|
{ |
1017 |
|
|
|
1018 |
|
|
alarmtimer(0); |
1019 |
|
|
fputs("\nhttp fetch aborted.\n", ttyout); |
1020 |
|
|
(void)fflush(ttyout); |
1021 |
|
|
longjmp(httpabort, 1); |
1022 |
|
|
} |
1023 |
|
|
|
1024 |
|
|
/* |
1025 |
|
|
* Abort a http retrieval |
1026 |
|
|
*/ |
1027 |
|
|
/* ARGSUSED */ |
1028 |
|
|
void |
1029 |
|
|
abortfile(int signo) |
1030 |
|
|
{ |
1031 |
|
|
|
1032 |
|
|
alarmtimer(0); |
1033 |
|
|
fputs("\nfile fetch aborted.\n", ttyout); |
1034 |
|
|
(void)fflush(ttyout); |
1035 |
|
|
longjmp(httpabort, 1); |
1036 |
|
|
} |
1037 |
|
|
|
1038 |
|
|
/* |
1039 |
|
|
* Retrieve multiple files from the command line, transferring |
1040 |
|
|
* files of the form "host:path", "ftp://host/path" using the |
1041 |
|
|
* ftp protocol, and files of the form "http://host/path" using |
1042 |
|
|
* the http protocol. |
1043 |
|
|
* If path has a trailing "/", then return (-1); |
1044 |
|
|
* the path will be cd-ed into and the connection remains open, |
1045 |
|
|
* and the function will return -1 (to indicate the connection |
1046 |
|
|
* is alive). |
1047 |
|
|
* If an error occurs the return value will be the offset+1 in |
1048 |
|
|
* argv[] of the file that caused a problem (i.e, argv[x] |
1049 |
|
|
* returns x+1) |
1050 |
|
|
* Otherwise, 0 is returned if all files retrieved successfully. |
1051 |
|
|
*/ |
1052 |
|
|
int |
1053 |
|
|
auto_fetch(int argc, char *argv[], char *outfile) |
1054 |
|
|
{ |
1055 |
|
|
char *xargv[5]; |
1056 |
|
|
char *cp, *url, *host, *dir, *file, *portnum; |
1057 |
|
|
char *username, *pass, *pathstart; |
1058 |
|
|
char *ftpproxy, *httpproxy; |
1059 |
|
|
int rval, xargc; |
1060 |
|
|
volatile int argpos; |
1061 |
|
|
int dirhasglob, filehasglob, oautologin; |
1062 |
|
|
char rempath[PATH_MAX]; |
1063 |
|
|
|
1064 |
|
|
argpos = 0; |
1065 |
|
|
|
1066 |
|
|
if (setjmp(toplevel)) { |
1067 |
|
|
if (connected) |
1068 |
|
|
disconnect(0, NULL); |
1069 |
|
|
return (argpos + 1); |
1070 |
|
|
} |
1071 |
|
|
(void)signal(SIGINT, (sig_t)intr); |
1072 |
|
|
(void)signal(SIGPIPE, (sig_t)lostpeer); |
1073 |
|
|
|
1074 |
|
|
if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0') |
1075 |
|
|
ftpproxy = NULL; |
1076 |
|
|
if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0') |
1077 |
|
|
httpproxy = NULL; |
1078 |
|
|
|
1079 |
|
|
/* |
1080 |
|
|
* Loop through as long as there's files to fetch. |
1081 |
|
|
*/ |
1082 |
|
|
username = pass = NULL; |
1083 |
|
|
for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) { |
1084 |
|
|
if (strchr(argv[argpos], ':') == NULL) |
1085 |
|
|
break; |
1086 |
|
|
|
1087 |
|
|
free(username); |
1088 |
|
|
free(pass); |
1089 |
|
|
host = dir = file = portnum = username = pass = NULL; |
1090 |
|
|
|
1091 |
|
|
/* |
1092 |
|
|
* We muck with the string, so we make a copy. |
1093 |
|
|
*/ |
1094 |
|
|
url = strdup(argv[argpos]); |
1095 |
|
|
if (url == NULL) |
1096 |
|
|
errx(1, "Can't allocate memory for auto-fetch."); |
1097 |
|
|
|
1098 |
|
|
/* |
1099 |
|
|
* Try HTTP URL-style arguments first. |
1100 |
|
|
*/ |
1101 |
|
|
if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || |
1102 |
|
|
#ifndef SMALL |
1103 |
|
|
/* even if we compiled without SSL, url_get will check */ |
1104 |
|
|
strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 || |
1105 |
|
|
#endif /* !SMALL */ |
1106 |
|
|
strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) { |
1107 |
|
|
redirect_loop = 0; |
1108 |
|
|
if (url_get(url, httpproxy, outfile) == -1) |
1109 |
|
|
rval = argpos + 1; |
1110 |
|
|
continue; |
1111 |
|
|
} |
1112 |
|
|
|
1113 |
|
|
/* |
1114 |
|
|
* Try FTP URL-style arguments next. If ftpproxy is |
1115 |
|
|
* set, use url_get() instead of standard ftp. |
1116 |
|
|
* Finally, try host:file. |
1117 |
|
|
*/ |
1118 |
|
|
host = url; |
1119 |
|
|
if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) { |
1120 |
|
|
char *passend, *passagain, *userend; |
1121 |
|
|
|
1122 |
|
|
if (ftpproxy) { |
1123 |
|
|
if (url_get(url, ftpproxy, outfile) == -1) |
1124 |
|
|
rval = argpos + 1; |
1125 |
|
|
continue; |
1126 |
|
|
} |
1127 |
|
|
host += sizeof(FTP_URL) - 1; |
1128 |
|
|
dir = strchr(host, '/'); |
1129 |
|
|
|
1130 |
|
|
/* Look for [user:pass@]host[:port] */ |
1131 |
|
|
|
1132 |
|
|
/* check if we have "user:pass@" */ |
1133 |
|
|
userend = strchr(host, ':'); |
1134 |
|
|
passend = strchr(host, '@'); |
1135 |
|
|
if (passend && userend && userend < passend && |
1136 |
|
|
(!dir || passend < dir)) { |
1137 |
|
|
username = host; |
1138 |
|
|
pass = userend + 1; |
1139 |
|
|
host = passend + 1; |
1140 |
|
|
*userend = *passend = '\0'; |
1141 |
|
|
passagain = strchr(host, '@'); |
1142 |
|
|
if (strchr(pass, '@') != NULL || |
1143 |
|
|
(passagain != NULL && passagain < dir)) { |
1144 |
|
|
warnx(at_encoding_warning); |
1145 |
|
|
username = pass = NULL; |
1146 |
|
|
goto bad_ftp_url; |
1147 |
|
|
} |
1148 |
|
|
|
1149 |
|
|
if (EMPTYSTRING(username)) { |
1150 |
|
|
bad_ftp_url: |
1151 |
|
|
warnx("Invalid URL: %s", argv[argpos]); |
1152 |
|
|
rval = argpos + 1; |
1153 |
|
|
username = pass = NULL; |
1154 |
|
|
continue; |
1155 |
|
|
} |
1156 |
|
|
username = urldecode(username); |
1157 |
|
|
pass = urldecode(pass); |
1158 |
|
|
} |
1159 |
|
|
|
1160 |
|
|
/* check [host]:port, or [host] */ |
1161 |
|
|
if (host[0] == '[') { |
1162 |
|
|
cp = strchr(host, ']'); |
1163 |
|
|
if (cp && (!dir || cp < dir)) { |
1164 |
|
|
if (cp + 1 == dir || cp[1] == ':') { |
1165 |
|
|
host++; |
1166 |
|
|
*cp++ = '\0'; |
1167 |
|
|
} else |
1168 |
|
|
cp = NULL; |
1169 |
|
|
} else |
1170 |
|
|
cp = host; |
1171 |
|
|
} else |
1172 |
|
|
cp = host; |
1173 |
|
|
|
1174 |
|
|
/* split off host[:port] if there is */ |
1175 |
|
|
if (cp) { |
1176 |
|
|
portnum = strchr(cp, ':'); |
1177 |
|
|
pathstart = strchr(cp, '/'); |
1178 |
|
|
/* : in path is not a port # indicator */ |
1179 |
|
|
if (portnum && pathstart && |
1180 |
|
|
pathstart < portnum) |
1181 |
|
|
portnum = NULL; |
1182 |
|
|
|
1183 |
|
|
if (!portnum) |
1184 |
|
|
; |
1185 |
|
|
else { |
1186 |
|
|
if (!dir) |
1187 |
|
|
; |
1188 |
|
|
else if (portnum + 1 < dir) { |
1189 |
|
|
*portnum++ = '\0'; |
1190 |
|
|
/* |
1191 |
|
|
* XXX should check if portnum |
1192 |
|
|
* is decimal number |
1193 |
|
|
*/ |
1194 |
|
|
} else { |
1195 |
|
|
/* empty portnum */ |
1196 |
|
|
goto bad_ftp_url; |
1197 |
|
|
} |
1198 |
|
|
} |
1199 |
|
|
} else |
1200 |
|
|
portnum = NULL; |
1201 |
|
|
} else { /* classic style `host:file' */ |
1202 |
|
|
dir = strchr(host, ':'); |
1203 |
|
|
} |
1204 |
|
|
if (EMPTYSTRING(host)) { |
1205 |
|
|
rval = argpos + 1; |
1206 |
|
|
continue; |
1207 |
|
|
} |
1208 |
|
|
|
1209 |
|
|
/* |
1210 |
|
|
* If dir is NULL, the file wasn't specified |
1211 |
|
|
* (URL looked something like ftp://host) |
1212 |
|
|
*/ |
1213 |
|
|
if (dir != NULL) |
1214 |
|
|
*dir++ = '\0'; |
1215 |
|
|
|
1216 |
|
|
/* |
1217 |
|
|
* Extract the file and (if present) directory name. |
1218 |
|
|
*/ |
1219 |
|
|
if (!EMPTYSTRING(dir)) { |
1220 |
|
|
cp = strrchr(dir, '/'); |
1221 |
|
|
if (cp != NULL) { |
1222 |
|
|
*cp++ = '\0'; |
1223 |
|
|
file = cp; |
1224 |
|
|
} else { |
1225 |
|
|
file = dir; |
1226 |
|
|
dir = NULL; |
1227 |
|
|
} |
1228 |
|
|
} |
1229 |
|
|
#ifndef SMALL |
1230 |
|
|
if (debug) |
1231 |
|
|
fprintf(ttyout, |
1232 |
|
|
"user %s:%s host %s port %s dir %s file %s\n", |
1233 |
|
|
username, pass ? "XXXX" : NULL, host, portnum, |
1234 |
|
|
dir, file); |
1235 |
|
|
#endif /* !SMALL */ |
1236 |
|
|
|
1237 |
|
|
/* |
1238 |
|
|
* Set up the connection. |
1239 |
|
|
*/ |
1240 |
|
|
if (connected) |
1241 |
|
|
disconnect(0, NULL); |
1242 |
|
|
xargv[0] = __progname; |
1243 |
|
|
xargv[1] = host; |
1244 |
|
|
xargv[2] = NULL; |
1245 |
|
|
xargc = 2; |
1246 |
|
|
if (!EMPTYSTRING(portnum)) { |
1247 |
|
|
xargv[2] = portnum; |
1248 |
|
|
xargv[3] = NULL; |
1249 |
|
|
xargc = 3; |
1250 |
|
|
} |
1251 |
|
|
oautologin = autologin; |
1252 |
|
|
if (username == NULL) |
1253 |
|
|
anonftp = 1; |
1254 |
|
|
else { |
1255 |
|
|
anonftp = 0; |
1256 |
|
|
autologin = 0; |
1257 |
|
|
} |
1258 |
|
|
setpeer(xargc, xargv); |
1259 |
|
|
autologin = oautologin; |
1260 |
|
|
if (connected == 0 || |
1261 |
|
|
(connected == 1 && autologin && (username == NULL || |
1262 |
|
|
!ftp_login(host, username, pass)))) { |
1263 |
|
|
warnx("Can't connect or login to host `%s'", host); |
1264 |
|
|
rval = argpos + 1; |
1265 |
|
|
continue; |
1266 |
|
|
} |
1267 |
|
|
|
1268 |
|
|
/* Always use binary transfers. */ |
1269 |
|
|
setbinary(0, NULL); |
1270 |
|
|
|
1271 |
|
|
dirhasglob = filehasglob = 0; |
1272 |
|
|
if (doglob) { |
1273 |
|
|
if (!EMPTYSTRING(dir) && |
1274 |
|
|
strpbrk(dir, "*?[]{}") != NULL) |
1275 |
|
|
dirhasglob = 1; |
1276 |
|
|
if (!EMPTYSTRING(file) && |
1277 |
|
|
strpbrk(file, "*?[]{}") != NULL) |
1278 |
|
|
filehasglob = 1; |
1279 |
|
|
} |
1280 |
|
|
|
1281 |
|
|
/* Change directories, if necessary. */ |
1282 |
|
|
if (!EMPTYSTRING(dir) && !dirhasglob) { |
1283 |
|
|
xargv[0] = "cd"; |
1284 |
|
|
xargv[1] = dir; |
1285 |
|
|
xargv[2] = NULL; |
1286 |
|
|
cd(2, xargv); |
1287 |
|
|
if (!dirchange) { |
1288 |
|
|
rval = argpos + 1; |
1289 |
|
|
continue; |
1290 |
|
|
} |
1291 |
|
|
} |
1292 |
|
|
|
1293 |
|
|
if (EMPTYSTRING(file)) { |
1294 |
|
|
#ifndef SMALL |
1295 |
|
|
rval = -1; |
1296 |
|
|
#else /* !SMALL */ |
1297 |
|
|
recvrequest("NLST", "-", NULL, "w", 0, 0); |
1298 |
|
|
rval = 0; |
1299 |
|
|
#endif /* !SMALL */ |
1300 |
|
|
continue; |
1301 |
|
|
} |
1302 |
|
|
|
1303 |
|
|
if (verbose) |
1304 |
|
|
fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file); |
1305 |
|
|
|
1306 |
|
|
if (dirhasglob) { |
1307 |
|
|
snprintf(rempath, sizeof(rempath), "%s/%s", dir, file); |
1308 |
|
|
file = rempath; |
1309 |
|
|
} |
1310 |
|
|
|
1311 |
|
|
/* Fetch the file(s). */ |
1312 |
|
|
xargc = 2; |
1313 |
|
|
xargv[0] = "get"; |
1314 |
|
|
xargv[1] = file; |
1315 |
|
|
xargv[2] = NULL; |
1316 |
|
|
if (dirhasglob || filehasglob) { |
1317 |
|
|
int ointeractive; |
1318 |
|
|
|
1319 |
|
|
ointeractive = interactive; |
1320 |
|
|
interactive = 0; |
1321 |
|
|
xargv[0] = "mget"; |
1322 |
|
|
#ifndef SMALL |
1323 |
|
|
if (resume) { |
1324 |
|
|
xargc = 3; |
1325 |
|
|
xargv[1] = "-c"; |
1326 |
|
|
xargv[2] = file; |
1327 |
|
|
xargv[3] = NULL; |
1328 |
|
|
} |
1329 |
|
|
#endif /* !SMALL */ |
1330 |
|
|
mget(xargc, xargv); |
1331 |
|
|
interactive = ointeractive; |
1332 |
|
|
} else { |
1333 |
|
|
if (outfile != NULL) { |
1334 |
|
|
xargv[2] = outfile; |
1335 |
|
|
xargv[3] = NULL; |
1336 |
|
|
xargc++; |
1337 |
|
|
} |
1338 |
|
|
#ifndef SMALL |
1339 |
|
|
if (resume) |
1340 |
|
|
reget(xargc, xargv); |
1341 |
|
|
else |
1342 |
|
|
#endif /* !SMALL */ |
1343 |
|
|
get(xargc, xargv); |
1344 |
|
|
} |
1345 |
|
|
|
1346 |
|
|
if ((code / 100) != COMPLETE) |
1347 |
|
|
rval = argpos + 1; |
1348 |
|
|
} |
1349 |
|
|
if (connected && rval != -1) |
1350 |
|
|
disconnect(0, NULL); |
1351 |
|
|
return (rval); |
1352 |
|
|
} |
1353 |
|
|
|
1354 |
|
|
char * |
1355 |
|
|
urldecode(const char *str) |
1356 |
|
|
{ |
1357 |
|
|
char *ret, c; |
1358 |
|
|
int i, reallen; |
1359 |
|
|
|
1360 |
|
|
if (str == NULL) |
1361 |
|
|
return NULL; |
1362 |
|
|
if ((ret = malloc(strlen(str)+1)) == NULL) |
1363 |
|
|
err(1, "Can't allocate memory for URL decoding"); |
1364 |
|
|
for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) { |
1365 |
|
|
c = str[i]; |
1366 |
|
|
if (c == '+') { |
1367 |
|
|
*ret = ' '; |
1368 |
|
|
continue; |
1369 |
|
|
} |
1370 |
|
|
|
1371 |
|
|
/* Cannot use strtol here because next char |
1372 |
|
|
* after %xx may be a digit. |
1373 |
|
|
*/ |
1374 |
|
|
if (c == '%' && isxdigit((unsigned char)str[i+1]) && |
1375 |
|
|
isxdigit((unsigned char)str[i+2])) { |
1376 |
|
|
*ret = hextochar(&str[i+1]); |
1377 |
|
|
i+=2; |
1378 |
|
|
continue; |
1379 |
|
|
} |
1380 |
|
|
*ret = c; |
1381 |
|
|
} |
1382 |
|
|
*ret = '\0'; |
1383 |
|
|
|
1384 |
|
|
return ret-reallen; |
1385 |
|
|
} |
1386 |
|
|
|
1387 |
|
|
char * |
1388 |
|
|
recode_credentials(const char *userinfo) |
1389 |
|
|
{ |
1390 |
|
|
char *ui, *creds; |
1391 |
|
|
size_t ulen, credsize; |
1392 |
|
|
|
1393 |
|
|
/* url-decode the user and pass */ |
1394 |
|
|
ui = urldecode(userinfo); |
1395 |
|
|
|
1396 |
|
|
ulen = strlen(ui); |
1397 |
|
|
credsize = (ulen + 2) / 3 * 4 + 1; |
1398 |
|
|
creds = malloc(credsize); |
1399 |
|
|
if (creds == NULL) |
1400 |
|
|
errx(1, "out of memory"); |
1401 |
|
|
if (b64_ntop(ui, ulen, creds, credsize) == -1) |
1402 |
|
|
errx(1, "error in base64 encoding"); |
1403 |
|
|
free(ui); |
1404 |
|
|
return (creds); |
1405 |
|
|
} |
1406 |
|
|
|
1407 |
|
|
char |
1408 |
|
|
hextochar(const char *str) |
1409 |
|
|
{ |
1410 |
|
|
unsigned char c, ret; |
1411 |
|
|
|
1412 |
|
|
c = str[0]; |
1413 |
|
|
ret = c; |
1414 |
|
|
if (isalpha(c)) |
1415 |
|
|
ret -= isupper(c) ? 'A' - 10 : 'a' - 10; |
1416 |
|
|
else |
1417 |
|
|
ret -= '0'; |
1418 |
|
|
ret *= 16; |
1419 |
|
|
|
1420 |
|
|
c = str[1]; |
1421 |
|
|
ret += c; |
1422 |
|
|
if (isalpha(c)) |
1423 |
|
|
ret -= isupper(c) ? 'A' - 10 : 'a' - 10; |
1424 |
|
|
else |
1425 |
|
|
ret -= '0'; |
1426 |
|
|
return ret; |
1427 |
|
|
} |
1428 |
|
|
|
1429 |
|
|
int |
1430 |
|
|
isurl(const char *p) |
1431 |
|
|
{ |
1432 |
|
|
|
1433 |
|
|
if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || |
1434 |
|
|
strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || |
1435 |
|
|
#ifndef SMALL |
1436 |
|
|
strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 || |
1437 |
|
|
#endif /* !SMALL */ |
1438 |
|
|
strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || |
1439 |
|
|
strstr(p, ":/")) |
1440 |
|
|
return (1); |
1441 |
|
|
return (0); |
1442 |
|
|
} |
1443 |
|
|
|
1444 |
|
|
char * |
1445 |
|
|
ftp_readline(FILE *fp, struct tls *tls, size_t *lenp) |
1446 |
|
|
{ |
1447 |
|
|
if (fp != NULL) |
1448 |
|
|
return fparseln(fp, lenp, NULL, "\0\0\0", 0); |
1449 |
|
|
#ifndef SMALL |
1450 |
|
|
else if (tls != NULL) |
1451 |
|
|
return SSL_readline(tls, lenp); |
1452 |
|
|
#endif /* !SMALL */ |
1453 |
|
|
else |
1454 |
|
|
return NULL; |
1455 |
|
|
} |
1456 |
|
|
|
1457 |
|
|
size_t |
1458 |
|
|
ftp_read(FILE *fp, struct tls *tls, char *buf, size_t len) |
1459 |
|
|
{ |
1460 |
|
|
ssize_t tls_ret; |
1461 |
|
|
size_t ret = 0; |
1462 |
|
|
|
1463 |
|
|
if (fp != NULL) |
1464 |
|
|
ret = fread(buf, sizeof(char), len, fp); |
1465 |
|
|
#ifndef SMALL |
1466 |
|
|
else if (tls != NULL) { |
1467 |
|
|
if ((tls_ret = tls_read(tls, buf, len)) >= 0) |
1468 |
|
|
ret = (size_t)tls_ret; |
1469 |
|
|
} |
1470 |
|
|
#endif /* !SMALL */ |
1471 |
|
|
return (ret); |
1472 |
|
|
} |
1473 |
|
|
|
1474 |
|
|
int |
1475 |
|
|
ftp_printf(FILE *fp, struct tls *tls, const char *fmt, ...) |
1476 |
|
|
{ |
1477 |
|
|
int ret; |
1478 |
|
|
va_list ap; |
1479 |
|
|
|
1480 |
|
|
va_start(ap, fmt); |
1481 |
|
|
|
1482 |
|
|
if (fp != NULL) |
1483 |
|
|
ret = vfprintf(fp, fmt, ap); |
1484 |
|
|
#ifndef SMALL |
1485 |
|
|
else if (tls != NULL) |
1486 |
|
|
ret = SSL_vprintf(tls, fmt, ap); |
1487 |
|
|
#endif /* !SMALL */ |
1488 |
|
|
else |
1489 |
|
|
ret = 0; |
1490 |
|
|
|
1491 |
|
|
va_end(ap); |
1492 |
|
|
#ifndef SMALL |
1493 |
|
|
if (debug) { |
1494 |
|
|
va_start(ap, fmt); |
1495 |
|
|
ret = vfprintf(ttyout, fmt, ap); |
1496 |
|
|
va_end(ap); |
1497 |
|
|
} |
1498 |
|
|
#endif /* !SMALL */ |
1499 |
|
|
return (ret); |
1500 |
|
|
} |
1501 |
|
|
|
1502 |
|
|
#ifndef SMALL |
1503 |
|
|
int |
1504 |
|
|
SSL_vprintf(struct tls *tls, const char *fmt, va_list ap) |
1505 |
|
|
{ |
1506 |
|
|
char *string, *buf; |
1507 |
|
|
size_t len; |
1508 |
|
|
int ret; |
1509 |
|
|
|
1510 |
|
|
if ((ret = vasprintf(&string, fmt, ap)) == -1) |
1511 |
|
|
return ret; |
1512 |
|
|
buf = string; |
1513 |
|
|
len = ret; |
1514 |
|
|
while (len > 0) { |
1515 |
|
|
ret = tls_write(tls, buf, len); |
1516 |
|
|
if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) |
1517 |
|
|
continue; |
1518 |
|
|
if (ret < 0) |
1519 |
|
|
break; |
1520 |
|
|
buf += ret; |
1521 |
|
|
len -= ret; |
1522 |
|
|
} |
1523 |
|
|
free(string); |
1524 |
|
|
return ret; |
1525 |
|
|
} |
1526 |
|
|
|
1527 |
|
|
char * |
1528 |
|
|
SSL_readline(struct tls *tls, size_t *lenp) |
1529 |
|
|
{ |
1530 |
|
|
size_t i, len; |
1531 |
|
|
char *buf, *q, c; |
1532 |
|
|
int ret; |
1533 |
|
|
|
1534 |
|
|
len = 128; |
1535 |
|
|
if ((buf = malloc(len)) == NULL) |
1536 |
|
|
errx(1, "Can't allocate memory for transfer buffer"); |
1537 |
|
|
for (i = 0; ; i++) { |
1538 |
|
|
if (i >= len - 1) { |
1539 |
|
|
if ((q = reallocarray(buf, len, 2)) == NULL) |
1540 |
|
|
errx(1, "Can't expand transfer buffer"); |
1541 |
|
|
buf = q; |
1542 |
|
|
len *= 2; |
1543 |
|
|
} |
1544 |
|
|
again: |
1545 |
|
|
ret = tls_read(tls, &c, 1); |
1546 |
|
|
if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) |
1547 |
|
|
goto again; |
1548 |
|
|
if (ret < 0) |
1549 |
|
|
errx(1, "SSL read error: %s", tls_error(tls)); |
1550 |
|
|
|
1551 |
|
|
buf[i] = c; |
1552 |
|
|
if (c == '\n') { |
1553 |
|
|
buf[i] = '\0'; |
1554 |
|
|
break; |
1555 |
|
|
} |
1556 |
|
|
} |
1557 |
|
|
*lenp = i; |
1558 |
|
|
return (buf); |
1559 |
|
|
} |
1560 |
|
|
|
1561 |
|
|
int |
1562 |
|
|
proxy_connect(int socket, char *host, char *cookie) |
1563 |
|
|
{ |
1564 |
|
|
int l; |
1565 |
|
|
char buf[1024]; |
1566 |
|
|
char *connstr, *hosttail, *port; |
1567 |
|
|
|
1568 |
|
|
if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && |
1569 |
|
|
(hosttail[1] == '\0' || hosttail[1] == ':')) { |
1570 |
|
|
host++; |
1571 |
|
|
*hosttail++ = '\0'; |
1572 |
|
|
} else |
1573 |
|
|
hosttail = host; |
1574 |
|
|
|
1575 |
|
|
port = strrchr(hosttail, ':'); /* find portnum */ |
1576 |
|
|
if (port != NULL) |
1577 |
|
|
*port++ = '\0'; |
1578 |
|
|
if (!port) |
1579 |
|
|
port = "443"; |
1580 |
|
|
|
1581 |
|
|
if (cookie) { |
1582 |
|
|
l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n" |
1583 |
|
|
"Proxy-Authorization: Basic %s\r\n%s\r\n\r\n", |
1584 |
|
|
host, port, cookie, HTTP_USER_AGENT); |
1585 |
|
|
} else { |
1586 |
|
|
l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n", |
1587 |
|
|
host, port, HTTP_USER_AGENT); |
1588 |
|
|
} |
1589 |
|
|
|
1590 |
|
|
if (l == -1) |
1591 |
|
|
errx(1, "Could not allocate memory to assemble connect string!"); |
1592 |
|
|
#ifndef SMALL |
1593 |
|
|
if (debug) |
1594 |
|
|
printf("%s", connstr); |
1595 |
|
|
#endif /* !SMALL */ |
1596 |
|
|
if (write(socket, connstr, l) != l) |
1597 |
|
|
err(1, "Could not send connect string"); |
1598 |
|
|
read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */ |
1599 |
|
|
free(connstr); |
1600 |
|
|
return(200); |
1601 |
|
|
} |
1602 |
|
|
#endif /* !SMALL */ |