Annotation of src/usr.bin/ftp/fetch.c, Revision 1.124
1.124 ! lteo 1: /* $OpenBSD: fetch.c,v 1.123 2014/07/05 09:20:54 guenther Exp $ */
1.15 millert 2: /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */
1.1 millert 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
1.15 millert 23: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 millert 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/param.h>
39: #include <sys/socket.h>
1.22 deraadt 40: #include <sys/stat.h>
1.1 millert 41:
42: #include <netinet/in.h>
43:
44: #include <arpa/ftp.h>
45: #include <arpa/inet.h>
46:
47: #include <ctype.h>
48: #include <err.h>
1.17 millert 49: #include <libgen.h>
1.58 grunk 50: #include <limits.h>
1.1 millert 51: #include <netdb.h>
52: #include <fcntl.h>
1.3 millert 53: #include <signal.h>
1.1 millert 54: #include <stdio.h>
1.61 deraadt 55: #include <stdarg.h>
1.19 deraadt 56: #include <errno.h>
1.1 millert 57: #include <stdlib.h>
58: #include <string.h>
59: #include <unistd.h>
1.40 fgsch 60: #include <util.h>
1.73 drahn 61: #include <resolv.h>
1.1 millert 62:
1.61 deraadt 63: #ifndef SMALL
64: #include <openssl/ssl.h>
65: #include <openssl/err.h>
1.118 jca 66: #include <openssl/x509v3.h>
1.78 martynas 67: #else /* !SMALL */
1.61 deraadt 68: #define SSL void
1.78 martynas 69: #endif /* !SMALL */
1.61 deraadt 70:
1.1 millert 71: #include "ftp_var.h"
1.86 martynas 72: #include "cmds.h"
1.1 millert 73:
1.38 millert 74: static int url_get(const char *, const char *, const char *);
75: void aborthttp(int);
76: void abortfile(int);
1.42 deraadt 77: char hextochar(const char *);
78: char *urldecode(const char *);
1.123 guenther 79: char *recode_credentials(const char *_userinfo);
1.61 deraadt 80: int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4)));
81: char *ftp_readline(FILE *, SSL *, size_t *);
1.65 ray 82: size_t ftp_read(FILE *, SSL *, char *, size_t);
1.61 deraadt 83: #ifndef SMALL
1.81 espie 84: int proxy_connect(int, char *, char *);
1.61 deraadt 85: int SSL_vprintf(SSL *, const char *, va_list);
86: char *SSL_readline(SSL *, size_t *);
1.118 jca 87: int ssl_match_hostname(char *, char *);
88: int ssl_check_subject_altname(X509 *, char *);
89: int ssl_check_common_name(X509 *, char *);
90: int ssl_check_hostname(X509 *, char *);
1.119 jca 91: SSL_CTX *ssl_get_ssl_ctx(void);
1.78 martynas 92: #endif /* !SMALL */
1.14 millert 93:
1.1 millert 94: #define FTP_URL "ftp://" /* ftp URL prefix */
95: #define HTTP_URL "http://" /* http URL prefix */
1.61 deraadt 96: #define HTTPS_URL "https://" /* https URL prefix */
1.22 deraadt 97: #define FILE_URL "file:" /* file URL prefix */
1.6 millert 98: #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */
1.1 millert 99: #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
100:
101: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
102:
1.110 guenther 103: static const char at_encoding_warning[] =
1.53 deraadt 104: "Extra `@' characters in usernames and passwords should be encoded as %%40";
1.37 heko 105:
1.1 millert 106: jmp_buf httpabort;
107:
1.54 fgsch 108: static int redirect_loop;
109:
1.1 millert 110: /*
1.95 martynas 111: * Determine whether the character needs encoding, per RFC1738:
112: * - No corresponding graphic US-ASCII.
113: * - Unsafe characters.
114: */
115: static int
116: unsafe_char(const char *c)
117: {
118: const char *unsafe_chars = " <>\"#{}|\\^~[]`";
119:
120: /*
121: * No corresponding graphic US-ASCII.
122: * Control characters and octets not used in US-ASCII.
123: */
124: return (iscntrl(*c) || !isascii(*c) ||
125:
126: /*
127: * Unsafe characters.
128: * '%' is also unsafe, if is not followed by two
129: * hexadecimal digits.
130: */
131: strchr(unsafe_chars, *c) != NULL ||
132: (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c))));
133: }
134:
135: /*
136: * Encode given URL, per RFC1738.
137: * Allocate and return string to the caller.
138: */
139: static char *
140: url_encode(const char *path)
141: {
142: size_t i, length, new_length;
143: char *epath, *epathp;
144:
145: length = new_length = strlen(path);
146:
147: /*
148: * First pass:
149: * Count unsafe characters, and determine length of the
150: * final URL.
151: */
152: for (i = 0; i < length; i++)
153: if (unsafe_char(path + i))
154: new_length += 2;
155:
156: epath = epathp = malloc(new_length + 1); /* One more for '\0'. */
157: if (epath == NULL)
1.102 halex 158: err(1, "Can't allocate memory for URL encoding");
1.95 martynas 159:
160: /*
161: * Second pass:
162: * Encode, and copy final URL.
163: */
164: for (i = 0; i < length; i++)
165: if (unsafe_char(path + i)) {
166: snprintf(epathp, 4, "%%" "%02x", path[i]);
167: epathp += 3;
168: } else
169: *(epathp++) = path[i];
170:
171: *epathp = '\0';
172: return (epath);
173: }
174:
1.118 jca 175: #ifndef SMALL
176: int
177: ssl_match_hostname(char *cert_hostname, char *hostname)
178: {
179: if (strcasecmp(cert_hostname, hostname) == 0)
180: return 0;
181:
182: /* wildcard match? */
183: if (cert_hostname[0] == '*') {
184: char *cert_domain, *domain;
185:
186: cert_domain = &cert_hostname[1];
187: if (cert_domain[0] != '.')
188: return -1;
189: if (strlen(cert_domain) == 1)
190: return -1;
191:
192: domain = strchr(hostname, '.');
193: /* no wildcard match against a hostname with no domain part */
194: if (domain == NULL || strlen(domain) == 1)
195: return -1;
196:
197: if (strcasecmp(cert_domain, domain) == 0)
198: return 0;
199: }
200:
201: return -1;
202: }
203:
204: int
205: ssl_check_subject_altname(X509 *cert, char *host)
206: {
207: STACK_OF(GENERAL_NAME) *altname_stack = NULL;
208: union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
209: int addrlen, type;
210: int count, i;
211: int rv = -1;
212:
213: altname_stack =
214: X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
215: if (altname_stack == NULL)
216: return -1;
217:
218: if (inet_pton(AF_INET, host, &addrbuf) == 1) {
219: type = GEN_IPADD;
220: addrlen = 4;
221: } else if (inet_pton(AF_INET6, host, &addrbuf) == 1) {
222: type = GEN_IPADD;
223: addrlen = 16;
224: } else
225: type = GEN_DNS;
226:
227: count = sk_GENERAL_NAME_num(altname_stack);
228: for (i = 0; i < count; i++) {
229: GENERAL_NAME *altname;
230:
231: altname = sk_GENERAL_NAME_value(altname_stack, i);
232:
233: if (altname->type != type)
234: continue;
235:
236: if (type == GEN_DNS) {
237: unsigned char *data;
238: int format;
239:
240: format = ASN1_STRING_type(altname->d.dNSName);
241: if (format == V_ASN1_IA5STRING) {
242: data = ASN1_STRING_data(altname->d.dNSName);
243:
244: if (ASN1_STRING_length(altname->d.dNSName) !=
245: (int)strlen(data)) {
246: fprintf(ttyout, "%s: NUL byte in "
247: "subjectAltName, probably a "
248: "malicious certificate.\n",
249: getprogname());
250: rv = -2;
251: break;
252: }
253:
254: if (ssl_match_hostname(data, host) == 0) {
255: rv = 0;
256: break;
257: }
258: } else
259: fprintf(ttyout, "%s: unhandled subjectAltName "
260: "dNSName encoding (%d)\n", getprogname(),
261: format);
262:
263: } else if (type == GEN_IPADD) {
264: unsigned char *data;
265: int datalen;
266:
267: datalen = ASN1_STRING_length(altname->d.iPAddress);
268: data = ASN1_STRING_data(altname->d.iPAddress);
269:
270: if (datalen == addrlen &&
271: memcmp(data, &addrbuf, addrlen) == 0) {
272: rv = 0;
273: break;
274: }
275: }
276: }
277:
278: sk_GENERAL_NAME_free(altname_stack);
279: return rv;
280: }
281:
282: int
283: ssl_check_common_name(X509 *cert, char *host)
284: {
285: X509_NAME *name;
286: char *common_name = NULL;
287: int common_name_len;
288: int rv = -1;
289:
290: name = X509_get_subject_name(cert);
291: if (name == NULL)
292: goto out;
293:
294: common_name_len = X509_NAME_get_text_by_NID(name, NID_commonName,
295: NULL, 0);
296: if (common_name_len < 0)
297: goto out;
298:
299: common_name = calloc(common_name_len + 1, 1);
300: if (common_name == NULL)
301: goto out;
302:
303: X509_NAME_get_text_by_NID(name, NID_commonName, common_name,
304: common_name_len + 1);
305:
306: /* NUL bytes in CN? */
307: if (common_name_len != (int)strlen(common_name)) {
308: fprintf(ttyout, "%s: NUL byte in Common Name field, "
309: "probably a malicious certificate.\n", getprogname());
310: rv = -2;
311: goto out;
312: }
313:
314: if (ssl_match_hostname(common_name, host) == 0)
315: rv = 0;
316: out:
317: free(common_name);
318: return rv;
319: }
320:
321: int
322: ssl_check_hostname(X509 *cert, char *host)
323: {
324: int rv;
325:
326: rv = ssl_check_subject_altname(cert, host);
327: if (rv == 0 || rv == -2)
328: return rv;
329:
330: return ssl_check_common_name(cert, host);
331: }
1.119 jca 332:
333: SSL_CTX *
334: ssl_get_ssl_ctx(void)
335: {
1.120 jca 336: static SSL_CTX *ssl_ctx = NULL;
337: static int libssl_loaded = 0;
1.119 jca 338:
339: if (ssl_ctx != NULL)
340: return ssl_ctx;
341:
342: if (!libssl_loaded) {
343: SSL_library_init();
344: SSL_load_error_strings();
345: libssl_loaded = 1;
346: }
347:
348: ssl_ctx = SSL_CTX_new(SSLv23_client_method());
349: if (ssl_ctx == NULL)
350: goto err;
351:
352: if (ssl_verify) {
353: if (ssl_ca_file == NULL && ssl_ca_path == NULL)
354: ssl_ca_file = _PATH_SSL_CAFILE;
355:
356: if (SSL_CTX_load_verify_locations(ssl_ctx,
357: ssl_ca_file, ssl_ca_path) != 1)
358: goto err;
359:
360: SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
361: if (ssl_verify_depth != -1)
362: SSL_CTX_set_verify_depth(ssl_ctx,
363: ssl_verify_depth);
364: }
365:
366: if (ssl_ciphers != NULL &&
367: SSL_CTX_set_cipher_list(ssl_ctx, ssl_ciphers) == -1)
368: goto err;
369:
370: return ssl_ctx;
371: err:
372: if (ssl_ctx != NULL) {
373: SSL_CTX_free(ssl_ctx);
374: ssl_ctx = NULL;
375: }
376: return NULL;
377: }
1.118 jca 378: #endif
379:
1.95 martynas 380: /*
1.6 millert 381: * Retrieve URL, via the proxy in $proxyvar if necessary.
1.1 millert 382: * Modifies the string argument given.
383: * Returns -1 on failure, 0 on success
384: */
1.14 millert 385: static int
1.50 deraadt 386: url_get(const char *origline, const char *proxyenv, const char *outfile)
1.1 millert 387: {
1.69 jsg 388: char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4];
1.62 ray 389: char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
1.106 haesbaer 390: char *epath, *redirurl, *loctail, *h, *p;
1.53 deraadt 391: int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
1.105 haesbaer 392: struct addrinfo hints, *res0, *res, *ares = NULL;
1.34 millert 393: const char * volatile savefile;
1.62 ray 394: char * volatile proxyurl = NULL;
1.123 guenther 395: char *credentials = NULL;
1.53 deraadt 396: volatile int s = -1, out;
1.97 martynas 397: volatile sig_t oldintr, oldinti;
1.53 deraadt 398: FILE *fin = NULL;
1.1 millert 399: off_t hashbytes;
1.58 grunk 400: const char *errstr;
1.114 tedu 401: ssize_t len, wlen;
1.61 deraadt 402: #ifndef SMALL
403: char *sslpath = NULL, *sslhost = NULL;
1.123 guenther 404: char *locbase, *full_host = NULL;
1.102 halex 405: const char *scheme;
1.123 guenther 406: int ishttpurl = 0, ishttpsurl = 0;
1.61 deraadt 407: SSL_CTX *ssl_ctx = NULL;
1.78 martynas 408: #endif /* !SMALL */
1.61 deraadt 409: SSL *ssl = NULL;
1.70 deraadt 410: int status;
1.105 haesbaer 411: int save_errno;
1.113 tedu 412: const size_t buflen = 128 * 1024;
1.14 millert 413:
1.97 martynas 414: direction = "received";
415:
1.62 ray 416: newline = strdup(origline);
417: if (newline == NULL)
1.14 millert 418: errx(1, "Can't allocate memory to parse URL");
1.102 halex 419: if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
1.62 ray 420: host = newline + sizeof(HTTP_URL) - 1;
1.102 halex 421: #ifndef SMALL
1.123 guenther 422: ishttpurl = 1;
1.102 halex 423: scheme = HTTP_URL;
424: #endif /* !SMALL */
425: } else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.62 ray 426: host = newline + sizeof(FTP_URL) - 1;
1.14 millert 427: isftpurl = 1;
1.102 halex 428: #ifndef SMALL
429: scheme = FTP_URL;
430: #endif /* !SMALL */
1.62 ray 431: } else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
432: host = newline + sizeof(FILE_URL) - 1;
1.22 deraadt 433: isfileurl = 1;
1.61 deraadt 434: #ifndef SMALL
1.102 halex 435: scheme = FILE_URL;
1.62 ray 436: } else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) {
437: host = newline + sizeof(HTTPS_URL) - 1;
1.61 deraadt 438: ishttpsurl = 1;
1.102 halex 439: scheme = HTTPS_URL;
1.78 martynas 440: #endif /* !SMALL */
1.14 millert 441: } else
1.62 ray 442: errx(1, "url_get: Invalid URL '%s'", newline);
1.6 millert 443:
1.22 deraadt 444: if (isfileurl) {
445: path = host;
446: } else {
1.93 martynas 447: path = strchr(host, '/'); /* Find path */
1.22 deraadt 448: if (EMPTYSTRING(path)) {
1.93 martynas 449: if (outfile) { /* No slash, but */
450: path=strchr(host,'\0'); /* we have outfile. */
451: goto noslash;
452: }
1.22 deraadt 453: if (isftpurl)
454: goto noftpautologin;
1.94 martynas 455: warnx("No `/' after host (use -o): %s", origline);
1.22 deraadt 456: goto cleanup_url_get;
457: }
458: *path++ = '\0';
1.93 martynas 459: if (EMPTYSTRING(path) && !outfile) {
1.22 deraadt 460: if (isftpurl)
461: goto noftpautologin;
1.94 martynas 462: warnx("No filename after host (use -o): %s", origline);
1.22 deraadt 463: goto cleanup_url_get;
464: }
1.14 millert 465: }
1.1 millert 466:
1.93 martynas 467: noslash:
1.106 haesbaer 468:
469: #ifndef SMALL
470: /*
471: * Look for auth header in host, since now host does not
472: * contain the path. Basic auth from RFC 2617, valid
473: * characters for path are in RFC 3986 section 3.3.
474: */
1.123 guenther 475: if (proxyenv == NULL && (ishttpurl || ishttpsurl)) {
1.106 haesbaer 476: if ((p = strchr(host, '@')) != NULL) {
1.123 guenther 477: *p = '\0';
478: credentials = recode_credentials(host);
1.106 haesbaer 479: host = p + 1;
480: }
481: }
482: #endif /* SMALL */
483:
1.17 millert 484: if (outfile)
485: savefile = outfile;
1.93 martynas 486: else {
487: if (path[strlen(path) - 1] == '/') /* Consider no file */
488: savefile = NULL; /* after dir invalid. */
489: else
490: savefile = basename(path);
1.75 martynas 491: }
492:
1.14 millert 493: if (EMPTYSTRING(savefile)) {
494: if (isftpurl)
495: goto noftpautologin;
1.94 martynas 496: warnx("No filename after directory (use -o): %s", origline);
1.6 millert 497: goto cleanup_url_get;
1.14 millert 498: }
1.1 millert 499:
1.93 martynas 500: #ifndef SMALL
1.100 halex 501: if (resume && pipeout) {
1.93 martynas 502: warnx("can't append to stdout");
503: goto cleanup_url_get;
504: }
505: #endif /* !SMALL */
506:
1.59 uwe 507: if (!isfileurl && proxyenv != NULL) { /* use proxy */
1.61 deraadt 508: #ifndef SMALL
509: if (ishttpsurl) {
510: sslpath = strdup(path);
511: sslhost = strdup(host);
512: if (! sslpath || ! sslhost)
513: errx(1, "Can't allocate memory for https path/host.");
514: }
1.78 martynas 515: #endif /* !SMALL */
1.62 ray 516: proxyurl = strdup(proxyenv);
517: if (proxyurl == NULL)
1.14 millert 518: errx(1, "Can't allocate memory for proxy URL.");
1.62 ray 519: if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
520: host = proxyurl + sizeof(HTTP_URL) - 1;
521: else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0)
522: host = proxyurl + sizeof(FTP_URL) - 1;
1.6 millert 523: else {
1.14 millert 524: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 525: goto cleanup_url_get;
526: }
1.14 millert 527: if (EMPTYSTRING(host)) {
528: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 529: goto cleanup_url_get;
1.14 millert 530: }
1.93 martynas 531: if (*--path == '\0')
532: *path = '/'; /* add / back to real path */
1.1 millert 533: path = strchr(host, '/'); /* remove trailing / on host */
1.42 deraadt 534: if (!EMPTYSTRING(path))
1.73 drahn 535: *path++ = '\0'; /* i guess this ++ is useless */
536:
537: path = strchr(host, '@'); /* look for credentials in proxy */
538: if (!EMPTYSTRING(path)) {
1.81 espie 539: *path = '\0';
1.123 guenther 540: if (strchr(host, ':') == NULL) {
1.73 drahn 541: warnx("Malformed proxy URL: %s", proxyenv);
542: goto cleanup_url_get;
543: }
1.123 guenther 544: credentials = recode_credentials(host);
1.81 espie 545: *path = '@'; /* restore @ in proxyurl */
1.123 guenther 546:
1.73 drahn 547: /*
1.81 espie 548: * This removes the password from proxyurl,
1.73 drahn 549: * filling with stars
550: */
1.81 espie 551: for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@';
1.73 drahn 552: host++)
553: *host = '*';
554:
1.81 espie 555: host = path + 1;
1.73 drahn 556: }
1.123 guenther 557:
1.62 ray 558: path = newline;
1.1 millert 559: }
560:
1.22 deraadt 561: if (isfileurl) {
562: struct stat st;
563:
564: s = open(path, O_RDONLY);
565: if (s == -1) {
566: warn("Can't open file %s", path);
567: goto cleanup_url_get;
568: }
569:
570: if (fstat(s, &st) == -1)
571: filesize = -1;
572: else
573: filesize = st.st_size;
574:
1.83 martynas 575: /* Open the output file. */
1.100 halex 576: if (!pipeout) {
1.75 martynas 577: #ifndef SMALL
578: if (resume)
1.82 deraadt 579: out = open(savefile, O_CREAT | O_WRONLY |
580: O_APPEND, 0666);
581:
1.75 martynas 582: else
1.78 martynas 583: #endif /* !SMALL */
1.75 martynas 584: out = open(savefile, O_CREAT | O_WRONLY |
585: O_TRUNC, 0666);
1.22 deraadt 586: if (out < 0) {
587: warn("Can't open %s", savefile);
588: goto cleanup_url_get;
589: }
590: } else
591: out = fileno(stdout);
592:
1.75 martynas 593: #ifndef SMALL
594: if (resume) {
595: if (fstat(out, &st) == -1) {
596: warn("Can't fstat %s", savefile);
597: goto cleanup_url_get;
598: }
599: if (lseek(s, st.st_size, SEEK_SET) == -1) {
600: warn("Can't lseek %s", path);
601: goto cleanup_url_get;
602: }
603: restart_point = st.st_size;
604: }
1.78 martynas 605: #endif /* !SMALL */
1.75 martynas 606:
1.22 deraadt 607: /* Trap signals */
608: oldintr = NULL;
1.97 martynas 609: oldinti = NULL;
1.22 deraadt 610: if (setjmp(httpabort)) {
611: if (oldintr)
612: (void)signal(SIGINT, oldintr);
1.97 martynas 613: if (oldinti)
614: (void)signal(SIGINFO, oldinti);
1.22 deraadt 615: goto cleanup_url_get;
616: }
617: oldintr = signal(SIGINT, abortfile);
1.42 deraadt 618:
1.22 deraadt 619: bytes = 0;
620: hashbytes = mark;
1.84 martynas 621: progressmeter(-1, path);
1.40 fgsch 622:
1.113 tedu 623: if ((buf = malloc(buflen)) == NULL)
1.47 deraadt 624: errx(1, "Can't allocate memory for transfer buffer");
1.42 deraadt 625:
1.22 deraadt 626: /* Finally, suck down the file. */
627: i = 0;
1.97 martynas 628: oldinti = signal(SIGINFO, psummary);
1.113 tedu 629: while ((len = read(s, buf, buflen)) > 0) {
1.22 deraadt 630: bytes += len;
631: for (cp = buf; len > 0; len -= i, cp += i) {
632: if ((i = write(out, cp, len)) == -1) {
633: warn("Writing %s", savefile);
1.97 martynas 634: signal(SIGINFO, oldinti);
1.22 deraadt 635: goto cleanup_url_get;
636: }
637: else if (i == 0)
638: break;
639: }
640: if (hash && !progress) {
641: while (bytes >= hashbytes) {
642: (void)putc('#', ttyout);
643: hashbytes += mark;
644: }
645: (void)fflush(ttyout);
646: }
647: }
1.97 martynas 648: signal(SIGINFO, oldinti);
1.22 deraadt 649: if (hash && !progress && bytes > 0) {
650: if (bytes < mark)
651: (void)putc('#', ttyout);
652: (void)putc('\n', ttyout);
653: (void)fflush(ttyout);
654: }
655: if (len != 0) {
656: warn("Reading from file");
657: goto cleanup_url_get;
658: }
1.84 martynas 659: progressmeter(1, NULL);
1.22 deraadt 660: if (verbose)
1.97 martynas 661: ptransfer(0);
1.22 deraadt 662: (void)signal(SIGINT, oldintr);
1.42 deraadt 663:
1.40 fgsch 664: rval = 0;
665: goto cleanup_url_get;
1.22 deraadt 666: }
667:
1.28 itojun 668: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
669: (hosttail[1] == '\0' || hosttail[1] == ':')) {
670: host++;
671: *hosttail++ = '\0';
1.102 halex 672: #ifndef SMALL
673: if (asprintf(&full_host, "[%s]", host) == -1)
674: errx(1, "Cannot allocate memory for hostname");
675: #endif /* !SMALL */
1.28 itojun 676: } else
677: hosttail = host;
678:
679: portnum = strrchr(hosttail, ':'); /* find portnum */
1.1 millert 680: if (portnum != NULL)
681: *portnum++ = '\0';
682:
1.80 martynas 683: #ifndef SMALL
1.102 halex 684: if (full_host == NULL)
685: if ((full_host = strdup(host)) == NULL)
686: errx(1, "Cannot allocate memory for hostname");
1.1 millert 687: if (debug)
1.106 haesbaer 688: fprintf(ttyout, "host %s, port %s, path %s, "
689: "save as %s, auth %s.\n",
1.123 guenther 690: host, portnum, path, savefile, credentials);
1.80 martynas 691: #endif /* !SMALL */
1.1 millert 692:
1.25 itojun 693: memset(&hints, 0, sizeof(hints));
1.39 deraadt 694: hints.ai_family = family;
1.25 itojun 695: hints.ai_socktype = SOCK_STREAM;
1.61 deraadt 696: #ifndef SMALL
697: port = portnum ? portnum : (ishttpsurl ? httpsport : httpport);
1.78 martynas 698: #else /* !SMALL */
1.25 itojun 699: port = portnum ? portnum : httpport;
1.78 martynas 700: #endif /* !SMALL */
1.25 itojun 701: error = getaddrinfo(host, port, &hints, &res0);
1.61 deraadt 702: /*
703: * If the services file is corrupt/missing, fall back
704: * on our hard-coded defines.
705: */
1.30 deraadt 706: if (error == EAI_SERVICE && port == httpport) {
707: snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT);
708: error = getaddrinfo(host, pbuf, &hints, &res0);
1.61 deraadt 709: #ifndef SMALL
710: } else if (error == EAI_SERVICE && port == httpsport) {
711: snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT);
712: error = getaddrinfo(host, pbuf, &hints, &res0);
1.78 martynas 713: #endif /* !SMALL */
1.30 deraadt 714: }
1.25 itojun 715: if (error) {
1.122 guenther 716: warnx("%s: %s", host, gai_strerror(error));
1.25 itojun 717: goto cleanup_url_get;
1.1 millert 718: }
719:
1.105 haesbaer 720: #ifndef SMALL
721: if (srcaddr) {
722: hints.ai_flags |= AI_NUMERICHOST;
723: error = getaddrinfo(srcaddr, NULL, &hints, &ares);
724: if (error) {
1.122 guenther 725: warnx("%s: %s", srcaddr, gai_strerror(error));
1.105 haesbaer 726: goto cleanup_url_get;
727: }
728: }
729: #endif /* !SMALL */
730:
1.25 itojun 731: s = -1;
732: for (res = res0; res; res = res->ai_next) {
1.44 itojun 733: if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
734: sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
735: strlcpy(hbuf, "(unknown)", sizeof(hbuf));
1.41 deraadt 736: if (verbose)
1.44 itojun 737: fprintf(ttyout, "Trying %s...\n", hbuf);
1.14 millert 738:
1.25 itojun 739: s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
740: if (s == -1) {
741: cause = "socket";
742: continue;
1.1 millert 743: }
744:
1.105 haesbaer 745: #ifndef SMALL
746: if (srcaddr) {
747: if (ares->ai_family != res->ai_family) {
748: close(s);
749: s = -1;
750: errno = EINVAL;
751: cause = "bind";
752: continue;
753: }
754: if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) {
755: save_errno = errno;
756: close(s);
757: errno = save_errno;
758: s = -1;
759: cause = "bind";
760: continue;
761: }
762: }
763: #endif /* !SMALL */
764:
1.25 itojun 765: again:
766: if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
767: if (errno == EINTR)
768: goto again;
1.57 otto 769: save_errno = errno;
1.25 itojun 770: close(s);
1.57 otto 771: errno = save_errno;
1.25 itojun 772: s = -1;
773: cause = "connect";
1.19 deraadt 774: continue;
775: }
1.25 itojun 776:
1.29 itojun 777: /* get port in numeric */
778: if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0,
779: pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0)
780: port = pbuf;
781: else
782: port = NULL;
783:
1.61 deraadt 784: #ifndef SMALL
785: if (proxyenv && sslhost)
1.123 guenther 786: proxy_connect(s, sslhost, credentials);
1.78 martynas 787: #endif /* !SMALL */
1.25 itojun 788: break;
789: }
790: freeaddrinfo(res0);
1.105 haesbaer 791: #ifndef SMALL
792: if (srcaddr)
793: freeaddrinfo(ares);
794: #endif /* !SMALL */
1.25 itojun 795: if (s < 0) {
1.33 millert 796: warn("%s", cause);
1.6 millert 797: goto cleanup_url_get;
1.1 millert 798: }
799:
1.61 deraadt 800: #ifndef SMALL
801: if (ishttpsurl) {
1.117 jca 802: union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
803:
1.61 deraadt 804: if (proxyenv && sslpath) {
805: ishttpsurl = 0;
1.62 ray 806: proxyurl = NULL;
1.61 deraadt 807: path = sslpath;
808: }
1.119 jca 809: ssl_ctx = ssl_get_ssl_ctx();
1.112 jca 810: if (ssl_ctx == NULL) {
1.116 jca 811: ERR_print_errors_fp(ttyout);
812: goto cleanup_url_get;
813: }
1.61 deraadt 814: ssl = SSL_new(ssl_ctx);
1.112 jca 815: if (ssl == NULL) {
1.61 deraadt 816: ERR_print_errors_fp(ttyout);
817: goto cleanup_url_get;
818: }
819: if (SSL_set_fd(ssl, s) == 0) {
820: ERR_print_errors_fp(ttyout);
821: goto cleanup_url_get;
1.117 jca 822: }
823: /*
824: * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
825: * permitted in "HostName".
826: */
827: if (inet_pton(AF_INET, host, &addrbuf) != 1 &&
828: inet_pton(AF_INET6, host, &addrbuf) != 1) {
829: if (SSL_set_tlsext_host_name(ssl, host) == 0) {
830: ERR_print_errors_fp(ttyout);
831: goto cleanup_url_get;
832: }
1.61 deraadt 833: }
834: if (SSL_connect(ssl) <= 0) {
835: ERR_print_errors_fp(ttyout);
1.72 ray 836: goto cleanup_url_get;
1.118 jca 837: }
838: if (ssl_verify) {
839: X509 *cert;
840:
841: cert = SSL_get_peer_certificate(ssl);
842: if (cert == NULL) {
843: fprintf(ttyout, "%s: no server certificate\n",
844: getprogname());
845: goto cleanup_url_get;
846: }
847:
848: if (ssl_check_hostname(cert, host) != 0) {
1.121 jca 849: X509_free(cert);
1.118 jca 850: fprintf(ttyout, "%s: host `%s' not present in"
851: " server certificate\n",
852: getprogname(), host);
853: goto cleanup_url_get;
854: }
855:
856: X509_free(cert);
1.61 deraadt 857: }
858: } else {
859: fin = fdopen(s, "r+");
860: }
1.78 martynas 861: #else /* !SMALL */
1.40 fgsch 862: fin = fdopen(s, "r+");
1.78 martynas 863: #endif /* !SMALL */
1.40 fgsch 864:
1.55 fgsch 865: if (verbose)
866: fprintf(ttyout, "Requesting %s", origline);
1.95 martynas 867:
1.1 millert 868: /*
1.40 fgsch 869: * Construct and send the request. Proxy requests don't want leading /.
1.1 millert 870: */
1.74 pyr 871: #ifndef SMALL
872: cookie_get(host, path, ishttpsurl, &buf);
1.78 martynas 873: #endif /* !SMALL */
1.95 martynas 874:
875: epath = url_encode(path);
1.62 ray 876: if (proxyurl) {
1.55 fgsch 877: if (verbose)
1.81 espie 878: fprintf(ttyout, " (via %s)\n", proxyurl);
1.32 itojun 879: /*
880: * Host: directive must use the destination host address for
881: * the original URI (path). We do not attach it at this moment.
882: */
1.123 guenther 883: if (credentials)
1.73 drahn 884: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n"
1.74 pyr 885: "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n",
1.123 guenther 886: epath, credentials, buf ? buf : "",
1.124 ! lteo 887: httpuseragent);
1.73 drahn 888: else
1.74 pyr 889: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n",
1.124 ! lteo 890: epath, buf ? buf : "", httpuseragent);
1.73 drahn 891:
1.28 itojun 892: } else {
1.90 martynas 893: #ifndef SMALL
894: if (resume) {
895: struct stat stbuf;
896:
897: if (stat(savefile, &stbuf) == 0)
898: restart_point = stbuf.st_size;
899: else
900: restart_point = 0;
901: }
1.123 guenther 902: if (credentials) {
1.106 haesbaer 903: ftp_printf(fin, ssl,
904: "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ",
905: epath, restart_point ?
906: "HTTP/1.1\r\nConnection: close" : "HTTP/1.0",
1.123 guenther 907: credentials);
908: free(credentials);
909: credentials = NULL;
1.106 haesbaer 910: } else
911: #endif /* SMALL */
1.107 haesbaer 912: ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath,
913: #ifndef SMALL
914: restart_point ? "HTTP/1.1\r\nConnection: close" :
915: #endif /* !SMALL */
916: "HTTP/1.0");
1.32 itojun 917: if (strchr(host, ':')) {
1.55 fgsch 918: /*
919: * strip off scoped address portion, since it's
920: * local to node
921: */
1.32 itojun 922: h = strdup(host);
923: if (h == NULL)
924: errx(1, "Can't allocate memory.");
925: if ((p = strchr(h, '%')) != NULL)
926: *p = '\0';
1.61 deraadt 927: ftp_printf(fin, ssl, "[%s]", h);
1.32 itojun 928: free(h);
1.55 fgsch 929: } else
1.61 deraadt 930: ftp_printf(fin, ssl, "%s", host);
1.55 fgsch 931:
932: /*
933: * Send port number only if it's specified and does not equal
934: * 80. Some broken HTTP servers get confused if you explicitly
935: * send them the port number.
936: */
1.61 deraadt 937: #ifndef SMALL
938: if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
939: ftp_printf(fin, ssl, ":%s", port);
1.90 martynas 940: if (restart_point)
1.75 martynas 941: ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
942: (long long)restart_point);
1.78 martynas 943: #else /* !SMALL */
1.55 fgsch 944: if (port && strcmp(port, "80") != 0)
1.61 deraadt 945: ftp_printf(fin, ssl, ":%s", port);
1.78 martynas 946: #endif /* !SMALL */
1.74 pyr 947: ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n",
1.124 ! lteo 948: buf ? buf : "", httpuseragent);
1.55 fgsch 949: if (verbose)
950: fprintf(ttyout, "\n");
1.28 itojun 951: }
1.95 martynas 952: free(epath);
1.74 pyr 953:
954: #ifndef SMALL
955: free(buf);
1.78 martynas 956: #endif /* !SMALL */
1.74 pyr 957: buf = NULL;
958:
1.61 deraadt 959: if (fin != NULL && fflush(fin) == EOF) {
1.14 millert 960: warn("Writing HTTP request");
1.6 millert 961: goto cleanup_url_get;
1.1 millert 962: }
1.61 deraadt 963: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 964: warn("Receiving HTTP reply");
965: goto cleanup_url_get;
1.1 millert 966: }
1.40 fgsch 967:
968: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
969: buf[--len] = '\0';
1.80 martynas 970: #ifndef SMALL
1.40 fgsch 971: if (debug)
972: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 973: #endif /* !SMALL */
1.40 fgsch 974:
1.1 millert 975: cp = strchr(buf, ' ');
976: if (cp == NULL)
977: goto improper;
978: else
979: cp++;
1.69 jsg 980:
981: strlcpy(ststr, cp, sizeof(ststr));
1.75 martynas 982: status = strtonum(ststr, 200, 416, &errstr);
1.69 jsg 983: if (errstr) {
984: warnx("Error retrieving file: %s", cp);
985: goto cleanup_url_get;
986: }
987:
988: switch (status) {
989: case 200: /* OK */
1.75 martynas 990: #ifndef SMALL
1.109 sthen 991: /*
1.98 phessler 992: * When we request a partial file, and we receive an HTTP 200
993: * it is a good indication that the server doesn't support
994: * range requests, and is about to send us the entire file.
995: * If the restart_point == 0, then we are not actually
996: * requesting a partial file, and an HTTP 200 is appropriate.
997: */
998: if (resume && restart_point != 0) {
999: warnx("Server does not support resume.");
1000: restart_point = resume = 0;
1001: }
1.101 halex 1002: /* FALLTHROUGH */
1.75 martynas 1003: case 206: /* Partial Content */
1.91 halex 1004: #endif /* !SMALL */
1.69 jsg 1005: break;
1006: case 301: /* Moved Permanently */
1007: case 302: /* Found */
1008: case 303: /* See Other */
1009: case 307: /* Temporary Redirect */
1.40 fgsch 1010: isredirect++;
1.54 fgsch 1011: if (redirect_loop++ > 10) {
1012: warnx("Too many redirections requested");
1013: goto cleanup_url_get;
1014: }
1.69 jsg 1015: break;
1.75 martynas 1016: #ifndef SMALL
1017: case 416: /* Requested Range Not Satisfiable */
1018: warnx("File is already fully retrieved.");
1019: goto cleanup_url_get;
1.78 martynas 1020: #endif /* !SMALL */
1.69 jsg 1021: default:
1.1 millert 1022: warnx("Error retrieving file: %s", cp);
1.6 millert 1023: goto cleanup_url_get;
1.1 millert 1024: }
1025:
1026: /*
1027: * Read the rest of the header.
1028: */
1.40 fgsch 1029: free(buf);
1030: filesize = -1;
1031:
1.62 ray 1032: for (;;) {
1.61 deraadt 1033: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 1034: warn("Receiving HTTP reply");
1035: goto cleanup_url_get;
1036: }
1.61 deraadt 1037:
1.40 fgsch 1038: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
1039: buf[--len] = '\0';
1040: if (len == 0)
1.1 millert 1041: break;
1.80 martynas 1042: #ifndef SMALL
1.40 fgsch 1043: if (debug)
1044: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 1045: #endif /* !SMALL */
1.1 millert 1046:
1.40 fgsch 1047: /* Look for some headers */
1048: cp = buf;
1.1 millert 1049: #define CONTENTLEN "Content-Length: "
1.40 fgsch 1050: if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
1.104 sthen 1051: size_t s;
1.40 fgsch 1052: cp += sizeof(CONTENTLEN) - 1;
1.111 deraadt 1053: if ((s = strcspn(cp, " \t")))
1.104 sthen 1054: *(cp+s) = 0;
1.58 grunk 1055: filesize = strtonum(cp, 0, LLONG_MAX, &errstr);
1056: if (errstr != NULL)
1.40 fgsch 1057: goto improper;
1.75 martynas 1058: #ifndef SMALL
1.90 martynas 1059: if (restart_point)
1.75 martynas 1060: filesize += restart_point;
1.78 martynas 1061: #endif /* !SMALL */
1.40 fgsch 1062: #define LOCATION "Location: "
1063: } else if (isredirect &&
1064: strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
1065: cp += sizeof(LOCATION) - 1;
1.102 halex 1066: if (strstr(cp, "://") == NULL) {
1067: #ifdef SMALL
1068: errx(1, "Relative redirect not supported");
1069: #else /* SMALL */
1070: if (*cp == '/') {
1071: locbase = NULL;
1072: cp++;
1073: } else {
1074: locbase = strdup(path);
1075: if (locbase == NULL)
1076: errx(1, "Can't allocate memory"
1077: " for location base");
1078: loctail = strchr(locbase, '#');
1079: if (loctail != NULL)
1080: *loctail = '\0';
1081: loctail = strchr(locbase, '?');
1082: if (loctail != NULL)
1083: *loctail = '\0';
1084: loctail = strrchr(locbase, '/');
1085: if (loctail == NULL) {
1086: free(locbase);
1087: locbase = NULL;
1088: } else
1089: loctail[1] = '\0';
1090: }
1091: /* Contruct URL from relative redirect */
1092: if (asprintf(&redirurl, "%s%s%s%s/%s%s",
1093: scheme, full_host,
1094: portnum ? ":" : "",
1095: portnum ? portnum : "",
1096: locbase ? locbase : "",
1097: cp) == -1)
1098: errx(1, "Cannot build "
1099: "redirect URL");
1100: free(locbase);
1101: #endif /* SMALL */
1102: } else if ((redirurl = strdup(cp)) == NULL)
1103: errx(1, "Cannot allocate memory for URL");
1104: loctail = strchr(redirurl, '#');
1105: if (loctail != NULL)
1106: *loctail = '\0';
1.40 fgsch 1107: if (verbose)
1.102 halex 1108: fprintf(ttyout, "Redirected to %s\n", redirurl);
1.40 fgsch 1109: if (fin != NULL)
1110: fclose(fin);
1111: else if (s != -1)
1112: close(s);
1.102 halex 1113: rval = url_get(redirurl, proxyenv, savefile);
1114: free(redirurl);
1115: goto cleanup_url_get;
1.40 fgsch 1116: }
1.108 tobias 1117: free(buf);
1.1 millert 1118: }
1119:
1.17 millert 1120: /* Open the output file. */
1.100 halex 1121: if (!pipeout) {
1.75 martynas 1122: #ifndef SMALL
1123: if (resume)
1.83 martynas 1124: out = open(savefile, O_CREAT | O_WRONLY | O_APPEND,
1125: 0666);
1.75 martynas 1126: else
1.78 martynas 1127: #endif /* !SMALL */
1.75 martynas 1128: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC,
1129: 0666);
1.10 deraadt 1130: if (out < 0) {
1131: warn("Can't open %s", savefile);
1132: goto cleanup_url_get;
1133: }
1134: } else
1.17 millert 1135: out = fileno(stdout);
1.1 millert 1136:
1137: /* Trap signals */
1138: oldintr = NULL;
1.97 martynas 1139: oldinti = NULL;
1.1 millert 1140: if (setjmp(httpabort)) {
1141: if (oldintr)
1.2 millert 1142: (void)signal(SIGINT, oldintr);
1.97 martynas 1143: if (oldinti)
1144: (void)signal(SIGINFO, oldinti);
1.6 millert 1145: goto cleanup_url_get;
1.1 millert 1146: }
1147: oldintr = signal(SIGINT, aborthttp);
1148:
1149: bytes = 0;
1150: hashbytes = mark;
1.84 martynas 1151: progressmeter(-1, path);
1.43 millert 1152:
1153: free(buf);
1.1 millert 1154:
1155: /* Finally, suck down the file. */
1.113 tedu 1156: if ((buf = malloc(buflen)) == NULL)
1.47 deraadt 1157: errx(1, "Can't allocate memory for transfer buffer");
1.1 millert 1158: i = 0;
1.61 deraadt 1159: len = 1;
1.97 martynas 1160: oldinti = signal(SIGINFO, psummary);
1.61 deraadt 1161: while (len > 0) {
1.113 tedu 1162: len = ftp_read(fin, ssl, buf, buflen);
1.1 millert 1163: bytes += len;
1.61 deraadt 1164: for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
1165: if ((i = write(out, cp, wlen)) == -1) {
1.1 millert 1166: warn("Writing %s", savefile);
1.97 martynas 1167: signal(SIGINFO, oldinti);
1.6 millert 1168: goto cleanup_url_get;
1.1 millert 1169: }
1170: else if (i == 0)
1171: break;
1172: }
1173: if (hash && !progress) {
1174: while (bytes >= hashbytes) {
1.10 deraadt 1175: (void)putc('#', ttyout);
1.1 millert 1176: hashbytes += mark;
1177: }
1.10 deraadt 1178: (void)fflush(ttyout);
1.1 millert 1179: }
1180: }
1.97 martynas 1181: signal(SIGINFO, oldinti);
1.1 millert 1182: if (hash && !progress && bytes > 0) {
1183: if (bytes < mark)
1.10 deraadt 1184: (void)putc('#', ttyout);
1185: (void)putc('\n', ttyout);
1186: (void)fflush(ttyout);
1.1 millert 1187: }
1188: if (len != 0) {
1189: warn("Reading from socket");
1.6 millert 1190: goto cleanup_url_get;
1.1 millert 1191: }
1.84 martynas 1192: progressmeter(1, NULL);
1.75 martynas 1193: if (
1194: #ifndef SMALL
1195: !resume &&
1.78 martynas 1196: #endif /* !SMALL */
1.75 martynas 1197: filesize != -1 && len == 0 && bytes != filesize) {
1.24 deraadt 1198: if (verbose)
1199: fputs("Read short file.\n", ttyout);
1200: goto cleanup_url_get;
1201: }
1202:
1.1 millert 1203: if (verbose)
1.97 martynas 1204: ptransfer(0);
1.2 millert 1205: (void)signal(SIGINT, oldintr);
1.1 millert 1206:
1.40 fgsch 1207: rval = 0;
1208: goto cleanup_url_get;
1.1 millert 1209:
1.14 millert 1210: noftpautologin:
1211: warnx(
1212: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
1213: goto cleanup_url_get;
1214:
1.1 millert 1215: improper:
1.8 millert 1216: warnx("Improper response from %s", host);
1.14 millert 1217:
1.6 millert 1218: cleanup_url_get:
1.61 deraadt 1219: #ifndef SMALL
1220: if (ssl) {
1221: SSL_shutdown(ssl);
1222: SSL_free(ssl);
1223: }
1.102 halex 1224: free(full_host);
1.123 guenther 1225: free(credentials);
1.78 martynas 1226: #endif /* !SMALL */
1.40 fgsch 1227: if (fin != NULL)
1228: fclose(fin);
1229: else if (s != -1)
1.1 millert 1230: close(s);
1.67 steven 1231: free(buf);
1232: free(proxyurl);
1.62 ray 1233: free(newline);
1.123 guenther 1234: free(credentials);
1.40 fgsch 1235: return (rval);
1.1 millert 1236: }
1237:
1238: /*
1239: * Abort a http retrieval
1240: */
1.51 deraadt 1241: /* ARGSUSED */
1.1 millert 1242: void
1.51 deraadt 1243: aborthttp(int signo)
1.1 millert 1244: {
1245:
1246: alarmtimer(0);
1.10 deraadt 1247: fputs("\nhttp fetch aborted.\n", ttyout);
1248: (void)fflush(ttyout);
1.1 millert 1249: longjmp(httpabort, 1);
1250: }
1251:
1252: /*
1.22 deraadt 1253: * Abort a http retrieval
1254: */
1.51 deraadt 1255: /* ARGSUSED */
1.22 deraadt 1256: void
1.51 deraadt 1257: abortfile(int signo)
1.22 deraadt 1258: {
1259:
1260: alarmtimer(0);
1261: fputs("\nfile fetch aborted.\n", ttyout);
1262: (void)fflush(ttyout);
1263: longjmp(httpabort, 1);
1264: }
1265:
1266: /*
1.1 millert 1267: * Retrieve multiple files from the command line, transferring
1268: * files of the form "host:path", "ftp://host/path" using the
1269: * ftp protocol, and files of the form "http://host/path" using
1270: * the http protocol.
1.2 millert 1271: * If path has a trailing "/", then return (-1);
1.1 millert 1272: * the path will be cd-ed into and the connection remains open,
1273: * and the function will return -1 (to indicate the connection
1274: * is alive).
1275: * If an error occurs the return value will be the offset+1 in
1276: * argv[] of the file that caused a problem (i.e, argv[x]
1277: * returns x+1)
1278: * Otherwise, 0 is returned if all files retrieved successfully.
1279: */
1280: int
1.50 deraadt 1281: auto_fetch(int argc, char *argv[], char *outfile)
1.1 millert 1282: {
1283: char *xargv[5];
1.62 ray 1284: char *cp, *url, *host, *dir, *file, *portnum;
1285: char *username, *pass, *pathstart;
1.6 millert 1286: char *ftpproxy, *httpproxy;
1.124 ! lteo 1287: #ifndef SMALL
! 1288: char *uagent = NULL;
! 1289: #endif /* !SMALL */
1.14 millert 1290: int rval, xargc;
1291: volatile int argpos;
1.49 krw 1292: int dirhasglob, filehasglob, oautologin;
1.14 millert 1293: char rempath[MAXPATHLEN];
1.1 millert 1294:
1295: argpos = 0;
1296:
1297: if (setjmp(toplevel)) {
1298: if (connected)
1299: disconnect(0, NULL);
1.2 millert 1300: return (argpos + 1);
1.1 millert 1301: }
1.3 millert 1302: (void)signal(SIGINT, (sig_t)intr);
1303: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 millert 1304:
1.45 millert 1305: if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0')
1306: ftpproxy = NULL;
1307: if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0')
1308: httpproxy = NULL;
1.6 millert 1309:
1.124 ! lteo 1310: if (httpuseragent == NULL)
! 1311: httpuseragent = HTTP_USER_AGENT;
! 1312: #ifndef SMALL
! 1313: else
! 1314: uagent = httpuseragent;
! 1315: #endif /* !SMALL */
! 1316:
1.1 millert 1317: /*
1318: * Loop through as long as there's files to fetch.
1319: */
1.123 guenther 1320: username = pass = NULL;
1.62 ray 1321: for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1.1 millert 1322: if (strchr(argv[argpos], ':') == NULL)
1323: break;
1.123 guenther 1324:
1325: free(username);
1326: free(pass);
1.62 ray 1327: host = dir = file = portnum = username = pass = NULL;
1.1 millert 1328:
1329: /*
1330: * We muck with the string, so we make a copy.
1331: */
1.62 ray 1332: url = strdup(argv[argpos]);
1333: if (url == NULL)
1.1 millert 1334: errx(1, "Can't allocate memory for auto-fetch.");
1335:
1336: /*
1337: * Try HTTP URL-style arguments first.
1338: */
1.62 ray 1339: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1340: #ifndef SMALL
1341: /* even if we compiled without SSL, url_get will check */
1.62 ray 1342: strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 ||
1.78 martynas 1343: #endif /* !SMALL */
1.62 ray 1344: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.54 fgsch 1345: redirect_loop = 0;
1.62 ray 1346: if (url_get(url, httpproxy, outfile) == -1)
1.1 millert 1347: rval = argpos + 1;
1348: continue;
1349: }
1350:
1351: /*
1.6 millert 1352: * Try FTP URL-style arguments next. If ftpproxy is
1353: * set, use url_get() instead of standard ftp.
1354: * Finally, try host:file.
1.1 millert 1355: */
1.62 ray 1356: host = url;
1357: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.37 heko 1358: char *passend, *passagain, *userend;
1.31 itojun 1359:
1.6 millert 1360: if (ftpproxy) {
1.62 ray 1361: if (url_get(url, ftpproxy, outfile) == -1)
1.6 millert 1362: rval = argpos + 1;
1363: continue;
1364: }
1.1 millert 1365: host += sizeof(FTP_URL) - 1;
1.8 millert 1366: dir = strchr(host, '/');
1.1 millert 1367:
1.8 millert 1368: /* Look for [user:pass@]host[:port] */
1.31 itojun 1369:
1370: /* check if we have "user:pass@" */
1.37 heko 1371: userend = strchr(host, ':');
1.31 itojun 1372: passend = strchr(host, '@');
1373: if (passend && userend && userend < passend &&
1374: (!dir || passend < dir)) {
1.62 ray 1375: username = host;
1.31 itojun 1376: pass = userend + 1;
1377: host = passend + 1;
1378: *userend = *passend = '\0';
1.37 heko 1379: passagain = strchr(host, '@');
1.42 deraadt 1380: if (strchr(pass, '@') != NULL ||
1.37 heko 1381: (passagain != NULL && passagain < dir)) {
1382: warnx(at_encoding_warning);
1.123 guenther 1383: username = pass = NULL;
1.37 heko 1384: goto bad_ftp_url;
1.42 deraadt 1385: }
1.31 itojun 1386:
1.77 martynas 1387: if (EMPTYSTRING(username)) {
1.11 millert 1388: bad_ftp_url:
1.31 itojun 1389: warnx("Invalid URL: %s", argv[argpos]);
1390: rval = argpos + 1;
1.123 guenther 1391: username = pass = NULL;
1.31 itojun 1392: continue;
1393: }
1.62 ray 1394: username = urldecode(username);
1.37 heko 1395: pass = urldecode(pass);
1.8 millert 1396: }
1.31 itojun 1397:
1398: #ifdef INET6
1399: /* check [host]:port, or [host] */
1400: if (host[0] == '[') {
1401: cp = strchr(host, ']');
1402: if (cp && (!dir || cp < dir)) {
1403: if (cp + 1 == dir || cp[1] == ':') {
1404: host++;
1405: *cp++ = '\0';
1406: } else
1407: cp = NULL;
1408: } else
1409: cp = host;
1.25 itojun 1410: } else
1411: cp = host;
1.31 itojun 1412: #else
1413: cp = host;
1.25 itojun 1414: #endif
1.31 itojun 1415:
1416: /* split off host[:port] if there is */
1417: if (cp) {
1418: portnum = strchr(cp, ':');
1.52 henning 1419: pathstart = strchr(cp, '/');
1420: /* : in path is not a port # indicator */
1421: if (portnum && pathstart &&
1422: pathstart < portnum)
1423: portnum = NULL;
1424:
1.31 itojun 1425: if (!portnum)
1426: ;
1427: else {
1428: if (!dir)
1429: ;
1430: else if (portnum + 1 < dir) {
1431: *portnum++ = '\0';
1432: /*
1433: * XXX should check if portnum
1434: * is decimal number
1435: */
1436: } else {
1437: /* empty portnum */
1438: goto bad_ftp_url;
1439: }
1440: }
1441: } else
1442: portnum = NULL;
1.8 millert 1443: } else { /* classic style `host:file' */
1444: dir = strchr(host, ':');
1445: }
1.1 millert 1446: if (EMPTYSTRING(host)) {
1447: rval = argpos + 1;
1448: continue;
1449: }
1450:
1451: /*
1.9 millert 1452: * If dir is NULL, the file wasn't specified
1.1 millert 1453: * (URL looked something like ftp://host)
1454: */
1.8 millert 1455: if (dir != NULL)
1456: *dir++ = '\0';
1.1 millert 1457:
1458: /*
1459: * Extract the file and (if present) directory name.
1460: */
1.42 deraadt 1461: if (!EMPTYSTRING(dir)) {
1.8 millert 1462: cp = strrchr(dir, '/');
1.1 millert 1463: if (cp != NULL) {
1464: *cp++ = '\0';
1465: file = cp;
1466: } else {
1467: file = dir;
1468: dir = NULL;
1469: }
1470: }
1.80 martynas 1471: #ifndef SMALL
1.1 millert 1472: if (debug)
1.42 deraadt 1473: fprintf(ttyout,
1474: "user %s:%s host %s port %s dir %s file %s\n",
1.76 martynas 1475: username, pass ? "XXXX" : NULL, host, portnum,
1476: dir, file);
1.80 martynas 1477: #endif /* !SMALL */
1.1 millert 1478:
1479: /*
1.49 krw 1480: * Set up the connection.
1.1 millert 1481: */
1.49 krw 1482: if (connected)
1483: disconnect(0, NULL);
1484: xargv[0] = __progname;
1485: xargv[1] = host;
1.8 millert 1486: xargv[2] = NULL;
1.49 krw 1487: xargc = 2;
1488: if (!EMPTYSTRING(portnum)) {
1489: xargv[2] = portnum;
1490: xargv[3] = NULL;
1491: xargc = 3;
1492: }
1493: oautologin = autologin;
1.88 martynas 1494: if (username == NULL)
1.89 halex 1495: anonftp = 1;
1496: else {
1497: anonftp = 0;
1.49 krw 1498: autologin = 0;
1.89 halex 1499: }
1.49 krw 1500: setpeer(xargc, xargv);
1501: autologin = oautologin;
1.87 martynas 1502: if (connected == 0 ||
1503: (connected == 1 && autologin && (username == NULL ||
1504: !ftp_login(host, username, pass)))) {
1.49 krw 1505: warnx("Can't connect or login to host `%s'", host);
1.8 millert 1506: rval = argpos + 1;
1507: continue;
1.1 millert 1508: }
1.49 krw 1509:
1510: /* Always use binary transfers. */
1511: setbinary(0, NULL);
1.1 millert 1512:
1.4 millert 1513: dirhasglob = filehasglob = 0;
1514: if (doglob) {
1.42 deraadt 1515: if (!EMPTYSTRING(dir) &&
1.4 millert 1516: strpbrk(dir, "*?[]{}") != NULL)
1517: dirhasglob = 1;
1.42 deraadt 1518: if (!EMPTYSTRING(file) &&
1.4 millert 1519: strpbrk(file, "*?[]{}") != NULL)
1520: filehasglob = 1;
1521: }
1522:
1.1 millert 1523: /* Change directories, if necessary. */
1.42 deraadt 1524: if (!EMPTYSTRING(dir) && !dirhasglob) {
1.1 millert 1525: xargv[0] = "cd";
1526: xargv[1] = dir;
1527: xargv[2] = NULL;
1528: cd(2, xargv);
1.42 deraadt 1529: if (!dirchange) {
1.1 millert 1530: rval = argpos + 1;
1531: continue;
1532: }
1533: }
1534:
1535: if (EMPTYSTRING(file)) {
1.86 martynas 1536: #ifndef SMALL
1.1 millert 1537: rval = -1;
1.86 martynas 1538: #else /* !SMALL */
1539: recvrequest("NLST", "-", NULL, "w", 0, 0);
1540: rval = 0;
1541: #endif /* !SMALL */
1.1 millert 1542: continue;
1543: }
1544:
1.21 marc 1545: if (verbose)
1.10 deraadt 1546: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1.1 millert 1547:
1.4 millert 1548: if (dirhasglob) {
1549: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1550: file = rempath;
1551: }
1552:
1553: /* Fetch the file(s). */
1.10 deraadt 1554: xargc = 2;
1.1 millert 1555: xargv[0] = "get";
1556: xargv[1] = file;
1557: xargv[2] = NULL;
1.4 millert 1558: if (dirhasglob || filehasglob) {
1559: int ointeractive;
1560:
1561: ointeractive = interactive;
1562: interactive = 0;
1563: xargv[0] = "mget";
1.78 martynas 1564: #ifndef SMALL
1565: if (resume) {
1566: xargc = 3;
1567: xargv[1] = "-c";
1568: xargv[2] = file;
1569: xargv[3] = NULL;
1570: }
1571: #endif /* !SMALL */
1.10 deraadt 1572: mget(xargc, xargv);
1.5 millert 1573: interactive = ointeractive;
1.10 deraadt 1574: } else {
1.17 millert 1575: if (outfile != NULL) {
1576: xargv[2] = outfile;
1577: xargv[3] = NULL;
1.10 deraadt 1578: xargc++;
1579: }
1.75 martynas 1580: #ifndef SMALL
1581: if (resume)
1582: reget(xargc, xargv);
1583: else
1.78 martynas 1584: #endif /* !SMALL */
1.75 martynas 1585: get(xargc, xargv);
1.10 deraadt 1586: }
1.1 millert 1587:
1.4 millert 1588: if ((code / 100) != COMPLETE)
1.1 millert 1589: rval = argpos + 1;
1590: }
1591: if (connected && rval != -1)
1592: disconnect(0, NULL);
1.124 ! lteo 1593: #ifndef SMALL
! 1594: free(uagent);
! 1595: #endif /* !SMALL */
1.1 millert 1596: return (rval);
1.37 heko 1597: }
1598:
1599: char *
1.50 deraadt 1600: urldecode(const char *str)
1.37 heko 1601: {
1.53 deraadt 1602: char *ret, c;
1603: int i, reallen;
1.37 heko 1604:
1.53 deraadt 1605: if (str == NULL)
1606: return NULL;
1607: if ((ret = malloc(strlen(str)+1)) == NULL)
1608: err(1, "Can't allocate memory for URL decoding");
1609: for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1610: c = str[i];
1611: if (c == '+') {
1612: *ret = ' ';
1613: continue;
1614: }
1.61 deraadt 1615:
1616: /* Cannot use strtol here because next char
1617: * after %xx may be a digit.
1618: */
1.53 deraadt 1619: if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) {
1620: *ret = hextochar(&str[i+1]);
1621: i+=2;
1622: continue;
1623: }
1624: *ret = c;
1625: }
1626: *ret = '\0';
1627:
1628: return ret-reallen;
1.123 guenther 1629: }
1630:
1631: char *
1632: recode_credentials(const char *userinfo)
1633: {
1634: char *ui, *creds;
1635: size_t ulen, credsize;
1636:
1637: /* url-decode the user and pass */
1638: ui = urldecode(userinfo);
1639:
1640: ulen = strlen(ui);
1641: credsize = (ulen + 2) / 3 * 4 + 1;
1642: creds = malloc(credsize);
1643: if (creds == NULL)
1644: errx(1, "out of memory");
1645: if (b64_ntop(ui, ulen, creds, credsize) == -1)
1646: errx(1, "error in base64 encoding");
1647: free(ui);
1648: return (creds);
1.37 heko 1649: }
1650:
1651: char
1.50 deraadt 1652: hextochar(const char *str)
1.37 heko 1653: {
1.53 deraadt 1654: char c, ret;
1.37 heko 1655:
1.53 deraadt 1656: c = str[0];
1657: ret = c;
1658: if (isalpha(c))
1659: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1660: else
1661: ret -= '0';
1662: ret *= 16;
1663:
1664: c = str[1];
1665: ret += c;
1666: if (isalpha(c))
1667: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1668: else
1669: ret -= '0';
1670: return ret;
1.25 itojun 1671: }
1672:
1673: int
1.50 deraadt 1674: isurl(const char *p)
1.25 itojun 1675: {
1.27 millert 1676:
1.26 deraadt 1677: if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 ||
1678: strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1679: #ifndef SMALL
1680: strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
1.78 martynas 1681: #endif /* !SMALL */
1.27 millert 1682: strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 ||
1683: strstr(p, ":/"))
1684: return (1);
1685: return (0);
1.1 millert 1686: }
1.61 deraadt 1687:
1688: char *
1689: ftp_readline(FILE *fp, SSL *ssl, size_t *lenp)
1690: {
1691: if (fp != NULL)
1692: return fparseln(fp, lenp, NULL, "\0\0\0", 0);
1693: #ifndef SMALL
1694: else if (ssl != NULL)
1695: return SSL_readline(ssl, lenp);
1.78 martynas 1696: #endif /* !SMALL */
1.61 deraadt 1697: else
1698: return NULL;
1699: }
1700:
1.65 ray 1701: size_t
1.61 deraadt 1702: ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len)
1703: {
1.65 ray 1704: size_t ret;
1.61 deraadt 1705: if (fp != NULL)
1706: ret = fread(buf, sizeof(char), len, fp);
1707: #ifndef SMALL
1.65 ray 1708: else if (ssl != NULL) {
1709: int nr;
1710:
1711: if (len > INT_MAX)
1712: len = INT_MAX;
1713: if ((nr = SSL_read(ssl, buf, (int)len)) <= 0)
1714: ret = 0;
1715: else
1716: ret = nr;
1717: }
1.78 martynas 1718: #endif /* !SMALL */
1.61 deraadt 1719: else
1720: ret = 0;
1721: return (ret);
1722: }
1723:
1724: int
1725: ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...)
1726: {
1727: int ret;
1728: va_list ap;
1729:
1730: va_start(ap, fmt);
1731:
1732: if (fp != NULL)
1733: ret = vfprintf(fp, fmt, ap);
1734: #ifndef SMALL
1735: else if (ssl != NULL)
1736: ret = SSL_vprintf((SSL*)ssl, fmt, ap);
1.78 martynas 1737: #endif /* !SMALL */
1.61 deraadt 1738: else
1.92 jsg 1739: ret = 0;
1.61 deraadt 1740:
1741: va_end(ap);
1742: return (ret);
1743: }
1744:
1745: #ifndef SMALL
1746: int
1747: SSL_vprintf(SSL *ssl, const char *fmt, va_list ap)
1748: {
1749: int ret;
1750: char *string;
1751:
1752: if ((ret = vasprintf(&string, fmt, ap)) == -1)
1753: return ret;
1.64 ray 1754: ret = SSL_write(ssl, string, ret);
1.61 deraadt 1755: free(string);
1756: return ret;
1757: }
1758:
1759: char *
1760: SSL_readline(SSL *ssl, size_t *lenp)
1761: {
1.63 ray 1762: size_t i, len;
1.61 deraadt 1763: char *buf, *q, c;
1.109 sthen 1764: int ret;
1.61 deraadt 1765:
1766: len = 128;
1767: if ((buf = malloc(len)) == NULL)
1768: errx(1, "Can't allocate memory for transfer buffer");
1769: for (i = 0; ; i++) {
1770: if (i >= len - 1) {
1771: if ((q = realloc(buf, 2 * len)) == NULL)
1772: errx(1, "Can't expand transfer buffer");
1773: buf = q;
1774: len *= 2;
1775: }
1.109 sthen 1776: again:
1777: ret = SSL_read(ssl, &c, 1);
1778: if (ret <= 0) {
1779: if (SSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ)
1780: goto again;
1781: else
1782: errx(1, "SSL_read error: %u",
1783: SSL_get_error(ssl, ret));
1784: }
1.61 deraadt 1785: buf[i] = c;
1786: if (c == '\n')
1787: break;
1788: }
1789: *lenp = i;
1790: return (buf);
1791: }
1792:
1793: int
1.81 espie 1794: proxy_connect(int socket, char *host, char *cookie)
1.61 deraadt 1795: {
1.66 ray 1796: int l;
1.61 deraadt 1797: char buf[1024];
1798: char *connstr, *hosttail, *port;
1799:
1800: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
1801: (hosttail[1] == '\0' || hosttail[1] == ':')) {
1802: host++;
1803: *hosttail++ = '\0';
1804: } else
1805: hosttail = host;
1806:
1807: port = strrchr(hosttail, ':'); /* find portnum */
1808: if (port != NULL)
1809: *port++ = '\0';
1810: if (!port)
1811: port = "443";
1812:
1.81 espie 1813: if (cookie) {
1814: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1815: "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1816: host, port, cookie, HTTP_USER_AGENT);
1817: } else {
1818: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1819: host, port, HTTP_USER_AGENT);
1820: }
1821:
1.66 ray 1822: if (l == -1)
1.61 deraadt 1823: errx(1, "Could not allocate memory to assemble connect string!");
1.80 martynas 1824: #ifndef SMALL
1.68 ray 1825: if (debug)
1826: printf("%s", connstr);
1.80 martynas 1827: #endif /* !SMALL */
1.66 ray 1828: if (write(socket, connstr, l) != l)
1.68 ray 1829: err(1, "Could not send connect string");
1.61 deraadt 1830: read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1.71 ray 1831: free(connstr);
1.61 deraadt 1832: return(200);
1833: }
1.78 martynas 1834: #endif /* !SMALL */