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