libfetch: merge some features from FreeBSD:

- Supports HTTP/1.1 308 redirect.
- SSLv[23] HTTPS connections are forbidden by default.
- TLS client certificate validation thru OpenSSL.
- Fixes for user/password encoding, misc.
This commit is contained in:
Juan RP 2015-10-24 07:52:30 +02:00
parent 38e8192f26
commit 90eb1d9433
8 changed files with 641 additions and 58 deletions

7
NEWS
View File

@ -1,5 +1,12 @@
xbps-0.48 (???): xbps-0.48 (???):
* libfetch: merge some features from FreeBSD:
- Supports HTTP/1.1 308 redirect.
- SSLv[23] HTTPS connections are forbidden by default.
- TLS client certificate validation thru OpenSSL.
- Fixes for user/password encoding, misc.
* lixbps: use a sane umask if the pkgdb file needs to created for the first * lixbps: use a sane umask if the pkgdb file needs to created for the first
time. Thanks to Wolfgang Draxinger (https://github.com/voidlinux/xbps/pull/108). time. Thanks to Wolfgang Draxinger (https://github.com/voidlinux/xbps/pull/108).

View File

@ -1,7 +1,9 @@
/* $FreeBSD: rev 288217 $ */
/* $NetBSD: common.c,v 1.29 2014/01/08 20:25:34 joerg Exp $ */ /* $NetBSD: common.c,v 1.29 2014/01/08 20:25:34 joerg Exp $ */
/*- /*-
* Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav * Copyright (c) 1998-2014 Dag-Erling Smorgrav
* Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org> * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
* Copyright (c) 2013 Michael Gmelin <freebsd@grem.de>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -26,8 +28,6 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
*/ */
#include "compat.h" #include "compat.h"
@ -58,6 +58,10 @@
#include <signal.h> #include <signal.h>
#endif #endif
#ifdef WITH_SSL
#include <openssl/x509v3.h>
#endif
#include <pthread.h> #include <pthread.h>
#include "fetch.h" #include "fetch.h"
@ -438,6 +442,495 @@ fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
pthread_mutex_unlock(&cache_mtx); pthread_mutex_unlock(&cache_mtx);
} }
#ifdef WITH_SSL
/*
* Find the first occurrence of find in s, where the search is limited to the
* first slen characters of s.
*/
static char *
strnstr(const char *s, const char *find, size_t slen)
{
char c, sc;
size_t len;
if ((c = *find++) != '\0') {
len = strlen(find);
do {
do {
if (slen-- < 1 || (sc = *s++) == '\0')
return (NULL);
} while (sc != c);
if (len > slen)
return (NULL);
} while (strncmp(s, find, len) != 0);
s--;
}
return ((char *)__UNCONST(s));
}
/*
* Convert characters A-Z to lowercase (intentionally avoid any locale
* specific conversions).
*/
static char
fetch_ssl_tolower(char in)
{
if (in >= 'A' && in <= 'Z')
return (in + 32);
else
return (in);
}
/*
* isalpha implementation that intentionally avoids any locale specific
* conversions.
*/
static int
fetch_ssl_isalpha(char in)
{
return ((in >= 'A' && in <= 'Z') || (in >= 'a' && in <= 'z'));
}
/*
* Check if passed hostnames a and b are equal.
*/
static int
fetch_ssl_hname_equal(const char *a, size_t alen, const char *b,
size_t blen)
{
size_t i;
if (alen != blen)
return (0);
for (i = 0; i < alen; ++i) {
if (fetch_ssl_tolower(a[i]) != fetch_ssl_tolower(b[i]))
return (0);
}
return (1);
}
/*
* Check if domain label is traditional, meaning that only A-Z, a-z, 0-9
* and '-' (hyphen) are allowed. Hyphens have to be surrounded by alpha-
* numeric characters. Double hyphens (like they're found in IDN a-labels
* 'xn--') are not allowed. Empty labels are invalid.
*/
static int
fetch_ssl_is_trad_domain_label(const char *l, size_t len, int wcok)
{
size_t i;
if (!len || l[0] == '-' || l[len-1] == '-')
return (0);
for (i = 0; i < len; ++i) {
if (!isdigit(l[i]) &&
!fetch_ssl_isalpha(l[i]) &&
!(l[i] == '*' && wcok) &&
!(l[i] == '-' && l[i - 1] != '-'))
return (0);
}
return (1);
}
/*
* Check if host name consists only of numbers. This might indicate an IP
* address, which is not a good idea for CN wildcard comparison.
*/
static int
fetch_ssl_hname_is_only_numbers(const char *hostname, size_t len)
{
size_t i;
for (i = 0; i < len; ++i) {
if (!((hostname[i] >= '0' && hostname[i] <= '9') ||
hostname[i] == '.'))
return (0);
}
return (1);
}
/*
* Check if the host name h passed matches the pattern passed in m which
* is usually part of subjectAltName or CN of a certificate presented to
* the client. This includes wildcard matching. The algorithm is based on
* RFC6125, sections 6.4.3 and 7.2, which clarifies RFC2818 and RFC3280.
*/
static int
fetch_ssl_hname_match(const char *h, size_t hlen, const char *m,
size_t mlen)
{
int delta, hdotidx, mdot1idx, wcidx;
const char *hdot, *mdot1, *mdot2;
const char *wc; /* wildcard */
if (!(h && *h && m && *m))
return (0);
if ((wc = strnstr(m, "*", mlen)) == NULL)
return (fetch_ssl_hname_equal(h, hlen, m, mlen));
wcidx = wc - m;
/* hostname should not be just dots and numbers */
if (fetch_ssl_hname_is_only_numbers(h, hlen))
return (0);
/* only one wildcard allowed in pattern */
if (strnstr(wc + 1, "*", mlen - wcidx - 1) != NULL)
return (0);
/*
* there must be at least two more domain labels and
* wildcard has to be in the leftmost label (RFC6125)
*/
mdot1 = strnstr(m, ".", mlen);
if (mdot1 == NULL || mdot1 < wc || (mlen - (mdot1 - m)) < 4)
return (0);
mdot1idx = mdot1 - m;
mdot2 = strnstr(mdot1 + 1, ".", mlen - mdot1idx - 1);
if (mdot2 == NULL || (mlen - (mdot2 - m)) < 2)
return (0);
/* hostname must contain a dot and not be the 1st char */
hdot = strnstr(h, ".", hlen);
if (hdot == NULL || hdot == h)
return (0);
hdotidx = hdot - h;
/*
* host part of hostname must be at least as long as
* pattern it's supposed to match
*/
if (hdotidx < mdot1idx)
return (0);
/*
* don't allow wildcards in non-traditional domain names
* (IDN, A-label, U-label...)
*/
if (!fetch_ssl_is_trad_domain_label(h, hdotidx, 0) ||
!fetch_ssl_is_trad_domain_label(m, mdot1idx, 1))
return (0);
/* match domain part (part after first dot) */
if (!fetch_ssl_hname_equal(hdot, hlen - hdotidx, mdot1,
mlen - mdot1idx))
return (0);
/* match part left of wildcard */
if (!fetch_ssl_hname_equal(h, wcidx, m, wcidx))
return (0);
/* match part right of wildcard */
delta = mdot1idx - wcidx - 1;
if (!fetch_ssl_hname_equal(hdot - delta, delta,
mdot1 - delta, delta))
return (0);
/* all tests succeded, it's a match */
return (1);
}
/*
* Get numeric host address info - returns NULL if host was not an IP
* address. The caller is responsible for deallocation using
* freeaddrinfo(3).
*/
static struct addrinfo *
fetch_ssl_get_numeric_addrinfo(const char *hostname, size_t len)
{
struct addrinfo hints, *res;
char *host;
host = (char *)malloc(len + 1);
memcpy(host, hostname, len);
host[len] = '\0';
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_NUMERICHOST;
/* port is not relevant for this purpose */
if (getaddrinfo(host, "443", &hints, &res) != 0)
return NULL;
free(host);
return res;
}
/*
* Compare ip address in addrinfo with address passes.
*/
static int
fetch_ssl_ipaddr_match_bin(const struct addrinfo *lhost, const char *rhost,
size_t rhostlen)
{
const void *left;
if (lhost->ai_family == AF_INET && rhostlen == 4) {
left = (void *)&((struct sockaddr_in*)(void *)
lhost->ai_addr)->sin_addr.s_addr;
#ifdef INET6
} else if (lhost->ai_family == AF_INET6 && rhostlen == 16) {
left = (void *)&((struct sockaddr_in6 *)(void *)
lhost->ai_addr)->sin6_addr;
#endif
} else
return (0);
return (!memcmp(left, (const void *)rhost, rhostlen) ? 1 : 0);
}
/*
* Compare ip address in addrinfo with host passed. If host is not an IP
* address, comparison will fail.
*/
static int
fetch_ssl_ipaddr_match(const struct addrinfo *laddr, const char *r,
size_t rlen)
{
struct addrinfo *raddr;
int ret;
char *rip;
ret = 0;
if ((raddr = fetch_ssl_get_numeric_addrinfo(r, rlen)) == NULL)
return 0; /* not a numeric host */
if (laddr->ai_family == raddr->ai_family) {
if (laddr->ai_family == AF_INET) {
rip = (char *)&((struct sockaddr_in *)(void *)
raddr->ai_addr)->sin_addr.s_addr;
ret = fetch_ssl_ipaddr_match_bin(laddr, rip, 4);
#ifdef INET6
} else if (laddr->ai_family == AF_INET6) {
rip = (char *)&((struct sockaddr_in6 *)(void *)
raddr->ai_addr)->sin6_addr;
ret = fetch_ssl_ipaddr_match_bin(laddr, rip, 16);
#endif
}
}
freeaddrinfo(raddr);
return (ret);
}
/*
* Verify server certificate by subjectAltName.
*/
static int
fetch_ssl_verify_altname(STACK_OF(GENERAL_NAME) *altnames,
const char *host, struct addrinfo *ip)
{
const GENERAL_NAME *name;
size_t nslen;
int i;
const char *ns;
for (i = 0; i < sk_GENERAL_NAME_num(altnames); ++i) {
name = sk_GENERAL_NAME_value(altnames, i);
ns = (const char *)ASN1_STRING_data(name->d.ia5);
nslen = (size_t)ASN1_STRING_length(name->d.ia5);
if (name->type == GEN_DNS && ip == NULL &&
fetch_ssl_hname_match(host, strlen(host), ns, nslen))
return (1);
else if (name->type == GEN_IPADD && ip != NULL &&
fetch_ssl_ipaddr_match_bin(ip, ns, nslen))
return (1);
}
return (0);
}
/*
* Verify server certificate by CN.
*/
static int
fetch_ssl_verify_cn(X509_NAME *subject, const char *host,
struct addrinfo *ip)
{
ASN1_STRING *namedata;
X509_NAME_ENTRY *nameentry;
int cnlen, lastpos, loc, ret;
unsigned char *cn;
ret = 0;
lastpos = -1;
loc = -1;
cn = NULL;
/* get most specific CN (last entry in list) and compare */
while ((lastpos = X509_NAME_get_index_by_NID(subject,
NID_commonName, lastpos)) != -1)
loc = lastpos;
if (loc > -1) {
nameentry = X509_NAME_get_entry(subject, loc);
namedata = X509_NAME_ENTRY_get_data(nameentry);
cnlen = ASN1_STRING_to_UTF8(&cn, namedata);
if (ip == NULL &&
fetch_ssl_hname_match(host, strlen(host), (const char *)cn, cnlen))
ret = 1;
else if (ip != NULL && fetch_ssl_ipaddr_match(ip, (const char *)cn, cnlen))
ret = 1;
OPENSSL_free(cn);
}
return (ret);
}
/*
* Verify that server certificate subjectAltName/CN matches
* hostname. First check, if there are alternative subject names. If yes,
* those have to match. Only if those don't exist it falls back to
* checking the subject's CN.
*/
static int
fetch_ssl_verify_hname(X509 *cert, const char *host)
{
struct addrinfo *ip;
STACK_OF(GENERAL_NAME) *altnames;
X509_NAME *subject;
int ret;
ret = 0;
ip = fetch_ssl_get_numeric_addrinfo(host, strlen(host));
altnames = X509_get_ext_d2i(cert, NID_subject_alt_name,
NULL, NULL);
if (altnames != NULL) {
ret = fetch_ssl_verify_altname(altnames, host, ip);
} else {
subject = X509_get_subject_name(cert);
if (subject != NULL)
ret = fetch_ssl_verify_cn(subject, host, ip);
}
if (ip != NULL)
freeaddrinfo(ip);
if (altnames != NULL)
GENERAL_NAMES_free(altnames);
return (ret);
}
/*
* Configure transport security layer based on environment.
*/
static void
fetch_ssl_setup_transport_layer(SSL_CTX *ctx, int verbose)
{
long ssl_ctx_options;
ssl_ctx_options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET;
if (getenv("SSL_ALLOW_SSL3") == NULL)
ssl_ctx_options |= SSL_OP_NO_SSLv3;
if (getenv("SSL_NO_TLS1") != NULL)
ssl_ctx_options |= SSL_OP_NO_TLSv1;
if (getenv("SSL_NO_TLS1_1") != NULL)
ssl_ctx_options |= SSL_OP_NO_TLSv1_1;
if (getenv("SSL_NO_TLS1_2") != NULL)
ssl_ctx_options |= SSL_OP_NO_TLSv1_2;
if (verbose)
fetch_info("SSL options: %lx", ssl_ctx_options);
SSL_CTX_set_options(ctx, ssl_ctx_options);
}
/*
* Configure peer verification based on environment.
*/
static int
fetch_ssl_setup_peer_verification(SSL_CTX *ctx, int verbose)
{
X509_LOOKUP *crl_lookup;
X509_STORE *crl_store;
const char *ca_cert_file, *ca_cert_path, *crl_file;
if (getenv("SSL_NO_VERIFY_PEER") == NULL) {
ca_cert_file = getenv("SSL_CA_CERT_FILE") != NULL ?
getenv("SSL_CA_CERT_FILE") : "/etc/ssl/cert.pem";
ca_cert_path = getenv("SSL_CA_CERT_PATH");
if (verbose) {
fetch_info("Peer verification enabled");
if (ca_cert_file != NULL)
fetch_info("Using CA cert file: %s",
ca_cert_file);
if (ca_cert_path != NULL)
fetch_info("Using CA cert path: %s",
ca_cert_path);
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER,
fetch_ssl_cb_verify_crt);
SSL_CTX_load_verify_locations(ctx, ca_cert_file,
ca_cert_path);
if ((crl_file = getenv("SSL_CRL_FILE")) != NULL) {
if (verbose)
fetch_info("Using CRL file: %s", crl_file);
crl_store = SSL_CTX_get_cert_store(ctx);
crl_lookup = X509_STORE_add_lookup(crl_store,
X509_LOOKUP_file());
if (crl_lookup == NULL ||
!X509_load_crl_file(crl_lookup, crl_file,
X509_FILETYPE_PEM)) {
fprintf(stderr,
"Could not load CRL file %s\n",
crl_file);
return (0);
}
X509_STORE_set_flags(crl_store,
X509_V_FLAG_CRL_CHECK |
X509_V_FLAG_CRL_CHECK_ALL);
}
}
return (1);
}
/*
* Configure client certificate based on environment.
*/
static int
fetch_ssl_setup_client_certificate(SSL_CTX *ctx, int verbose)
{
const char *client_cert_file, *client_key_file;
if ((client_cert_file = getenv("SSL_CLIENT_CERT_FILE")) != NULL) {
client_key_file = getenv("SSL_CLIENT_KEY_FILE") != NULL ?
getenv("SSL_CLIENT_KEY_FILE") : client_cert_file;
if (verbose) {
fetch_info("Using client cert file: %s",
client_cert_file);
fetch_info("Using client key file: %s",
client_key_file);
}
if (SSL_CTX_use_certificate_chain_file(ctx,
client_cert_file) != 1) {
fprintf(stderr,
"Could not load client certificate %s\n",
client_cert_file);
return (0);
}
if (SSL_CTX_use_PrivateKey_file(ctx, client_key_file,
SSL_FILETYPE_PEM) != 1) {
fprintf(stderr,
"Could not load client key %s\n",
client_key_file);
return (0);
}
}
return (1);
}
/*
* Callback for SSL certificate verification, this is called on server
* cert verification. It takes no decision, but informs the user in case
* verification failed.
*/
int
fetch_ssl_cb_verify_crt(int verified, X509_STORE_CTX *ctx)
{
X509 *crt;
X509_NAME *name;
char *str;
str = NULL;
if (!verified) {
if ((crt = X509_STORE_CTX_get_current_cert(ctx)) != NULL &&
(name = X509_get_subject_name(crt)) != NULL)
str = X509_NAME_oneline(name, 0, 0);
fprintf(stderr, "Certificate verification failed for %s\n",
str != NULL ? str : "no relevant certificate");
OPENSSL_free(str);
}
return (verified);
}
#endif
/* /*
* Enable SSL on a connection. * Enable SSL on a connection.
*/ */
@ -447,6 +940,8 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
#ifdef WITH_SSL #ifdef WITH_SSL
int ret; int ret;
X509_NAME *name;
char *str;
/* Init the SSL library and context */ /* Init the SSL library and context */
if (!SSL_library_init()){ if (!SSL_library_init()){
@ -460,8 +955,14 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
fetch_ssl_setup_transport_layer(conn->ssl_ctx, verbose);
if (!fetch_ssl_setup_peer_verification(conn->ssl_ctx, verbose))
return (-1);
if (!fetch_ssl_setup_client_certificate(conn->ssl_ctx, verbose))
return (-1);
conn->ssl = SSL_new(conn->ssl_ctx); conn->ssl = SSL_new(conn->ssl_ctx);
if (conn->ssl == NULL){ if (conn->ssl == NULL) {
fprintf(stderr, "SSL context creation failed\n"); fprintf(stderr, "SSL context creation failed\n");
return (-1); return (-1);
} }
@ -483,21 +984,36 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
return (-1); return (-1);
} }
if (verbose) { conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
X509_NAME *name;
char *str;
fprintf(stderr, "SSL connection established using %s\n", if (conn->ssl_cert == NULL) {
SSL_get_cipher(conn->ssl)); fprintf(stderr, "No server SSL certificate\n");
return (-1);
}
if (getenv("SSL_NO_VERIFY_HOSTNAME") == NULL) {
if (verbose)
fetch_info("Verify hostname");
if (!fetch_ssl_verify_hname(conn->ssl_cert, URL->host)) {
fprintf(stderr,
"SSL certificate subject doesn't match host %s\n",
URL->host);
return (-1);
}
}
if (verbose) {
fetch_info("%s connection established using %s",
SSL_get_version(conn->ssl), SSL_get_cipher(conn->ssl));
conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
name = X509_get_subject_name(conn->ssl_cert); name = X509_get_subject_name(conn->ssl_cert);
str = X509_NAME_oneline(name, 0, 0); str = X509_NAME_oneline(name, 0, 0);
printf("Certificate subject: %s\n", str); fetch_info("Certificate subject: %s", str);
free(str); OPENSSL_free(str);
name = X509_get_issuer_name(conn->ssl_cert); name = X509_get_issuer_name(conn->ssl_cert);
str = X509_NAME_oneline(name, 0, 0); str = X509_NAME_oneline(name, 0, 0);
printf("Certificate issuer: %s\n", str); fetch_info("Certificate issuer: %s", str);
free(str); OPENSSL_free(str);
} }
return (0); return (0);
@ -744,7 +1260,17 @@ fetch_close(conn_t *conn)
#ifdef WITH_SSL #ifdef WITH_SSL
if (conn->ssl) { if (conn->ssl) {
SSL_shutdown(conn->ssl); SSL_shutdown(conn->ssl);
SSL_set_connect_state(conn->ssl);
SSL_free(conn->ssl); SSL_free(conn->ssl);
conn->ssl = NULL;
}
if (conn->ssl_ctx) {
SSL_CTX_free(conn->ssl_ctx);
conn->ssl_ctx = NULL;
}
if (conn->ssl_cert) {
X509_free(conn->ssl_cert);
conn->ssl_cert = NULL;
} }
#endif #endif
ret = close(conn->sd); ret = close(conn->sd);

View File

@ -1,6 +1,7 @@
/* $FreeBSD: rev 267133 $ */
/* $NetBSD: common.h,v 1.23 2014/01/08 20:25:34 joerg Exp $ */ /* $NetBSD: common.h,v 1.23 2014/01/08 20:25:34 joerg Exp $ */
/*- /*-
* Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav * Copyright (c) 1998-2014 Dag-Erling Smorgrav
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -25,8 +26,6 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: common.h,v 1.30 2007/12/18 11:03:07 des Exp $
*/ */
#ifndef _COMMON_H_INCLUDED #ifndef _COMMON_H_INCLUDED
@ -106,6 +105,9 @@ conn_t *fetch_cache_get(const struct url *, int);
void fetch_cache_put(conn_t *, int (*)(conn_t *)); void fetch_cache_put(conn_t *, int (*)(conn_t *));
conn_t *fetch_connect(struct url *, int, int); conn_t *fetch_connect(struct url *, int, int);
conn_t *fetch_reopen(int); conn_t *fetch_reopen(int);
#ifdef WITH_SSL
int fetch_ssl_cb_verify_crt(int, X509_STORE_CTX*);
#endif
int fetch_ssl(conn_t *, const struct url *, int); int fetch_ssl(conn_t *, const struct url *, int);
ssize_t fetch_read(conn_t *, char *, size_t); ssize_t fetch_read(conn_t *, char *, size_t);
int fetch_getln(conn_t *); int fetch_getln(conn_t *);
@ -144,4 +146,8 @@ fetchIO *ftp_request(struct url *, const char *, const char *,
*/ */
#define CHECK_FLAG(x) (flags && strchr(flags, (x))) #define CHECK_FLAG(x) (flags && strchr(flags, (x)))
#ifndef __UNCONST
#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
#endif
#endif #endif

View File

@ -1,3 +1,4 @@
/* $FreeBSD: rev 252375 $ */
/* $NetBSD: fetch.c,v 1.19 2009/08/11 20:48:06 joerg Exp $ */ /* $NetBSD: fetch.c,v 1.19 2009/08/11 20:48:06 joerg Exp $ */
/*- /*-
* Copyright (c) 1998-2004 Dag-Erling Coïdan Smørav * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørav
@ -26,8 +27,6 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: fetch.c,v 1.41 2007/12/19 00:26:36 des Exp $
*/ */
#include "compat.h" #include "compat.h"
@ -292,6 +291,48 @@ fetchMakeURL(const char *scheme, const char *host, int port, const char *doc,
return (u); return (u);
} }
/*
* Return value of the given hex digit.
*/
static int
fetch_hexval(char ch)
{
if (ch >= '0' && ch <= '9')
return (ch - '0');
else if (ch >= 'a' && ch <= 'f')
return (ch - 'a' + 10);
else if (ch >= 'A' && ch <= 'F')
return (ch - 'A' + 10);
return (-1);
}
/*
* Decode percent-encoded URL component from src into dst, stopping at end
* of string, or at @ or : separators. Returns a pointer to the unhandled
* part of the input string (null terminator, @, or :). No terminator is
* written to dst (it is the caller's responsibility).
*/
static const char *
fetch_pctdecode(char *dst, const char *src, size_t dlen)
{
int d1, d2;
char c;
const char *s;
for (s = src; *s != '\0' && *s != '@' && *s != ':'; s++) {
if (s[0] == '%' && (d1 = fetch_hexval(s[1])) >= 0 &&
(d2 = fetch_hexval(s[2])) >= 0 && (d1 > 0 || d2 > 0)) {
c = d1 << 4 | d2;
s += 2;
} else {
c = *s;
}
if (dlen-- > 0)
*dst++ = c;
}
return (s);
}
int int
fetch_urlpath_safe(char x) fetch_urlpath_safe(char x)
{ {
@ -426,17 +467,10 @@ find_user:
p = strpbrk(URL, "/@"); p = strpbrk(URL, "/@");
if (p != NULL && *p == '@') { if (p != NULL && *p == '@') {
/* username */ /* username */
for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++) { q = fetch_pctdecode(u->user, URL, URL_USERLEN);
if (i < URL_USERLEN)
u->user[i++] = *q;
}
/* password */ /* password */
if (*q == ':') { if (*q == ':')
for (q++, i = 0; (*q != '@'); q++) q = fetch_pctdecode(u->pwd, q + 1, URL_PWDLEN);
if (i < URL_PWDLEN)
u->pwd[i++] = *q;
}
p++; p++;
} else { } else {

View File

@ -1,6 +1,6 @@
/* $NetBSD: fetch.h,v 1.16 2010/01/22 13:21:09 joerg Exp $ */ /* $NetBSD: fetch.h,v 1.16 2010/01/22 13:21:09 joerg Exp $ */
/*- /*-
* Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav * Copyright (c) 1998-2014 Dag-Erling Smorgrav
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -25,8 +25,6 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: fetch.h,v 1.26 2004/09/21 18:35:20 des Exp $
*/ */
#ifndef _FETCH_H_INCLUDED #ifndef _FETCH_H_INCLUDED
@ -36,7 +34,7 @@
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#define _LIBFETCH_VER "libfetch/2.0" #define _LIBFETCH_VER "xbps/2.0"
#define URL_HOSTLEN 255 #define URL_HOSTLEN 255
#define URL_SCHEMELEN 16 #define URL_SCHEMELEN 16

View File

@ -1,6 +1,6 @@
/* $NetBSD: ftp.c,v 1.46 2014/06/11 13:12:12 joerg Exp $ */ /* $NetBSD: ftp.c,v 1.46 2014/06/11 13:12:12 joerg Exp $ */
/*- /*-
* Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav * Copyright (c) 1998-2004 Dag-Erling Smorgrav
* Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
* All rights reserved. * All rights reserved.
* *
@ -42,7 +42,7 @@
* *
* Major Changelog: * Major Changelog:
* *
* Dag-Erling CoýÅan Smgrav * Dag-Erling Smograv
* 9 Jun 1998 * 9 Jun 1998
* *
* Incorporated into libfetch * Incorporated into libfetch

View File

@ -1,6 +1,7 @@
/* $FreeBSD: rev 267127 $ */
/* $NetBSD: http.c,v 1.37 2014/06/11 13:12:12 joerg Exp $ */ /* $NetBSD: http.c,v 1.37 2014/06/11 13:12:12 joerg Exp $ */
/*- /*-
* Copyright (c) 2000-2004 Dag-Erling CoýÅan Smgrav * Copyright (c) 2000-2014 Dag-Erling Smorgrav
* Copyright (c) 2003 Thomas Klausner <wiz@NetBSD.org> * Copyright (c) 2003 Thomas Klausner <wiz@NetBSD.org>
* Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org> * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>
* All rights reserved. * All rights reserved.
@ -27,8 +28,6 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: http.c,v 1.83 2008/02/06 11:39:55 des Exp $
*/ */
/* /*
@ -63,12 +62,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#if defined(__linux__) || defined(__MINT__) || defined(__FreeBSD_kernel__) #if defined(__linux__)
/* Keep this down to Linux or MiNT, it can create surprises elsewhere. */
/*
__FreeBSD_kernel__ is defined for GNU/kFreeBSD.
See http://glibc-bsd.alioth.debian.org/porting/PORTING .
*/
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
@ -106,7 +100,9 @@
#define HTTP_MOVED_TEMP 302 #define HTTP_MOVED_TEMP 302
#define HTTP_SEE_OTHER 303 #define HTTP_SEE_OTHER 303
#define HTTP_NOT_MODIFIED 304 #define HTTP_NOT_MODIFIED 304
#define HTTP_USE_PROXY 305
#define HTTP_TEMP_REDIRECT 307 #define HTTP_TEMP_REDIRECT 307
#define HTTP_PERM_REDIRECT 308
#define HTTP_NEED_AUTH 401 #define HTTP_NEED_AUTH 401
#define HTTP_NEED_PROXY_AUTH 407 #define HTTP_NEED_PROXY_AUTH 407
#define HTTP_BAD_RANGE 416 #define HTTP_BAD_RANGE 416
@ -115,6 +111,7 @@
#define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \
|| (xyz) == HTTP_MOVED_TEMP \ || (xyz) == HTTP_MOVED_TEMP \
|| (xyz) == HTTP_TEMP_REDIRECT \ || (xyz) == HTTP_TEMP_REDIRECT \
|| (xyz) == HTTP_USE_PROXY \
|| (xyz) == HTTP_SEE_OTHER) || (xyz) == HTTP_SEE_OTHER)
#define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)
@ -517,6 +514,12 @@ http_parse_mtime(const char *p, time_t *mtime)
locale[sizeof(locale)-1] = '\0'; locale[sizeof(locale)-1] = '\0';
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");
r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);
/*
* Some proxies use UTC in response, but it should still be
* parsed. RFC2616 states GMT and UTC are exactly equal for HTTP.
*/
if (r == NULL)
r = strptime(p, "%a, %d %b %Y %H:%M:%S UTC", &tm);
/* XXX should add support for date-2 and date-3 */ /* XXX should add support for date-2 and date-3 */
setlocale(LC_TIME, locale); setlocale(LC_TIME, locale);
if (r == NULL) if (r == NULL)
@ -698,6 +701,7 @@ http_authorize(conn_t *conn, const char *hdr, const char *p)
static conn_t * static conn_t *
http_connect(struct url *URL, struct url *purl, const char *flags, int *cached) http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
{ {
struct url *curl;
conn_t *conn; conn_t *conn;
int af, verbose; int af, verbose;
#ifdef TCP_NOPUSH #ifdef TCP_NOPUSH
@ -718,22 +722,25 @@ http_connect(struct url *URL, struct url *purl, const char *flags, int *cached)
af = AF_INET6; af = AF_INET6;
#endif #endif
if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) { curl = (purl != NULL) ? purl : URL;
URL = purl;
} else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
/* can't talk http to an ftp server */
/* XXX should set an error code */
return (NULL);
}
if ((conn = fetch_cache_get(URL, af)) != NULL) { if ((conn = fetch_cache_get(curl, af)) != NULL) {
*cached = 1; *cached = 1;
return (conn); return (conn);
} }
if ((conn = fetch_connect(URL, af, verbose)) == NULL) if ((conn = fetch_connect(curl, af, verbose)) == NULL)
/* fetch_connect() has already set an error code */ /* fetch_connect() has already set an error code */
return (NULL); return (NULL);
if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && purl) {
http_cmd(conn, "CONNECT %s:%d HTTP/1.1",
URL->host, URL->port);
if (http_get_reply(conn) != HTTP_OK) {
fetch_close(conn);
return (NULL);
}
http_get_reply(conn);
}
if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 &&
fetch_ssl(conn, URL, verbose) == -1) { fetch_ssl(conn, URL, verbose) == -1) {
fetch_close(conn); fetch_close(conn);
@ -888,7 +895,7 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
if (verbose) if (verbose)
fetch_info("requesting %s://%s%s", fetch_info("requesting %s://%s%s",
url->scheme, host, url->doc); url->scheme, host, url->doc);
if (purl) { if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) {
http_cmd(conn, "%s %s://%s%s HTTP/1.1\r\n", http_cmd(conn, "%s %s://%s%s HTTP/1.1\r\n",
op, url->scheme, host, url->doc); op, url->scheme, host, url->doc);
} else { } else {
@ -933,10 +940,14 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
else else
http_cmd(conn, "Referer: %s\r\n", p); http_cmd(conn, "Referer: %s\r\n", p);
} }
if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0') if ((p = getenv("HTTP_USER_AGENT")) != NULL) {
http_cmd(conn, "User-Agent: %s\r\n", p); /* no User-Agent if defined but empty */
else if (*p != '\0')
http_cmd(conn, "User-Agent: %s\r\n", p);
} else {
/* default User-Agent */
http_cmd(conn, "User-Agent: %s\r\n", _LIBFETCH_VER); http_cmd(conn, "User-Agent: %s\r\n", _LIBFETCH_VER);
}
/* /*
* Some servers returns 406 (Not Acceptable) if the Accept field is not * Some servers returns 406 (Not Acceptable) if the Accept field is not
@ -975,6 +986,7 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
case HTTP_MOVED_PERM: case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP: case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER: case HTTP_SEE_OTHER:
case HTTP_USE_PROXY:
/* /*
* Not so fine, but we still have to read the * Not so fine, but we still have to read the
* headers to get the new location. * headers to get the new location.

View File

@ -1,5 +1,4 @@
# $FreeBSD: http.errors,v 1.5 2001/05/23 18:52:02 des Exp $ # $FreeBSD$ revision 241840
# $NetBSD: http.errors,v 1.3 2009/02/05 16:59:45 joerg Exp $
# #
# This list is taken from RFC 2068. # This list is taken from RFC 2068.
# #
@ -19,6 +18,7 @@
304 UNCHANGED Not Modified 304 UNCHANGED Not Modified
305 INFO Use Proxy 305 INFO Use Proxy
307 MOVED Temporary Redirect 307 MOVED Temporary Redirect
308 MOVED Permanent Redirect
400 PROTO Bad Request 400 PROTO Bad Request
401 AUTH Unauthorized 401 AUTH Unauthorized
402 AUTH Payment Required 402 AUTH Payment Required