From 28a0841bf58e3813b2e07ad22f19484308e2f70a Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Tue, 2 Feb 2016 15:54:57 +0100 Subject: [PATCH] Refactoring BIO: add wrappers around sockaddr et al Because different platforms have different levels of support for IPv6, different kinds of sockaddr variants, and some have getaddrinfo et al while others don't, we could end up with a mess if ifdefs, duplicate code and other maintainance nightmares. Instead, we're introducing wrappers around the common form for socket communication: BIO_ADDR, closely related to struct sockaddr and some of its variants. BIO_ADDRINFO, closely related to struct addrinfo. With that comes support routines, both convenient creators and accessors, plus a few utility functions: BIO_parse_hostserv, takes a string of the form host:service and splits it into host and service. It checks for * in both parts, and converts any [ipv6-address] syntax to ust the IPv6 address. BIO_lookup, looks up information on a host. All routines handle IPv4 (AF_INET) and IPv6 (AF_INET6) addresses, and there is support for local sockets (AF_UNIX) as well. Reviewed-by: Kurt Roeckx --- crypto/bio/Makefile.in | 4 +- crypto/bio/b_addr.c | 795 ++++++++++++++++++++++++++++++ crypto/bio/b_sock.c | 11 +- crypto/bio/bio_lcl.h | 78 +++ doc/crypto/BIO_ADDR.pod | 109 ++++ doc/crypto/BIO_ADDRINFO.pod | 82 +++ doc/crypto/BIO_parse_hostserv.pod | 62 +++ e_os.h | 2 + include/openssl/bio.h | 32 ++ 9 files changed, 1165 insertions(+), 10 deletions(-) create mode 100644 crypto/bio/b_addr.c create mode 100644 doc/crypto/BIO_ADDR.pod create mode 100644 doc/crypto/BIO_ADDRINFO.pod create mode 100644 doc/crypto/BIO_parse_hostserv.pod diff --git a/crypto/bio/Makefile.in b/crypto/bio/Makefile.in index e4ba2552ea..9bcc17eef8 100644 --- a/crypto/bio/Makefile.in +++ b/crypto/bio/Makefile.in @@ -18,14 +18,14 @@ LIB=$(TOP)/libcrypto.a LIBSRC= bio_lib.c bio_cb.c bio_err.c \ bss_mem.c bss_null.c bss_fd.c \ bss_file.c bss_sock.c bss_conn.c \ - bf_null.c bf_buff.c b_print.c b_dump.c \ + bf_null.c bf_buff.c b_print.c b_dump.c b_addr.c \ b_sock.c bss_acpt.c bf_nbio.c bss_log.c bss_bio.c \ bss_dgram.c # bf_lbuf.c LIBOBJ= bio_lib.o bio_cb.o bio_err.o \ bss_mem.o bss_null.o bss_fd.o \ bss_file.o bss_sock.o bss_conn.o \ - bf_null.o bf_buff.o b_print.o b_dump.o \ + bf_null.o bf_buff.o b_print.o b_dump.o b_addr.o \ b_sock.o bss_acpt.o bf_nbio.o bss_log.o bss_bio.o \ bss_dgram.o # bf_lbuf.o diff --git a/crypto/bio/b_addr.c b/crypto/bio/b_addr.c new file mode 100644 index 0000000000..ed267e96f6 --- /dev/null +++ b/crypto/bio/b_addr.c @@ -0,0 +1,795 @@ +/* ==================================================================== + * Copyright (c) 2015 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include + +#include "bio_lcl.h" + +#include +#include + +/* + * Throughout this file and bio_lcl.h, the existence of the macro + * AI_PASSIVE is used to detect the availability of struct addrinfo, + * getnameinfo() and getaddrinfo(). If that macro doesn't exist, + * we use our own implementation instead, using gethostbyname, + * getservbyname and a few other. + */ + +/********************************************************************** + * + * Address structure + * + */ + +BIO_ADDR *BIO_ADDR_new(void) +{ + BIO_ADDR *ret = (BIO_ADDR *)OPENSSL_zalloc(sizeof(BIO_ADDR)); + return ret; +} + +void BIO_ADDR_free(BIO_ADDR *ap) +{ + OPENSSL_free(ap); +} + +/* + * BIO_ADDR_make - non-public routine to fill a BIO_ADDR with the contents + * of a struct sockaddr. + */ +int BIO_ADDR_make(BIO_ADDR *ap, const struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + ap->sin = *(const struct sockaddr_in *)sa; + return 1; + } +#ifdef AF_INET6 + if (sa->sa_family == AF_INET6) { + ap->sin6 = *(const struct sockaddr_in6 *)sa; + return 1; + } +#endif +#ifdef AF_UNIX + if (ap->sa.sa_family == AF_UNIX) { + ap->sun = *(const struct sockaddr_un *)sa; + return 1; + } +#endif + + return 0; +} + +int BIO_ADDR_rawmake(BIO_ADDR *ap, int family, + const void *where, size_t wherelen, + unsigned short port) +{ +#ifdef AF_UNIX + if (family == AF_UNIX) { + if (wherelen + 1 > sizeof(ap->sun.sun_path)) + return 0; + memset(&ap->sun, 0, sizeof(ap->sun)); + ap->sun.sun_family = family; + strncpy(ap->sun.sun_path, where, sizeof(ap->sun.sun_path) - 1); + return 1; + } +#endif + if (family == AF_INET) { + if (wherelen != sizeof(struct in_addr)) + return 0; + memset(&ap->sin, 0, sizeof(ap->sin)); + ap->sin.sin_family = family; + ap->sin.sin_port = port; + ap->sin.sin_addr = *(struct in_addr *)where; + return 1; + } +#ifdef AF_INET6 + if (family == AF_INET6) { + if (wherelen != sizeof(struct in6_addr)) + return 0; + memset(&ap->sin6, 0, sizeof(ap->sin6)); + ap->sin6.sin6_family = family; + ap->sin6.sin6_port = port; + ap->sin6.sin6_addr = *(struct in6_addr *)where; + return 1; + } +#endif + + return 0; +} + +int BIO_ADDR_family(const BIO_ADDR *ap) +{ + return ap->sa.sa_family; +} + +int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l) +{ + size_t len = 0; + const void *addrptr = NULL; + + if (ap->sa.sa_family == AF_INET) { + len = sizeof(ap->sin.sin_addr); + addrptr = &ap->sin.sin_addr; + } +#ifdef AF_INET6 + else if (ap->sa.sa_family == AF_INET6) { + len = sizeof(ap->sin6.sin6_addr); + addrptr = &ap->sin6.sin6_addr; + } +#endif +#ifdef AF_UNIX + else if (ap->sa.sa_family == AF_UNIX) { + len = strlen(ap->sun.sun_path); + addrptr = &ap->sun.sun_path; + } +#endif + + if (addrptr == NULL) + return 0; + + if (p != NULL) { + memcpy(p, addrptr, len); + } + if (l != NULL) + *l = len; + + return 1; +} + +unsigned short BIO_ADDR_rawport(const BIO_ADDR *ap) +{ + if (ap->sa.sa_family == AF_INET) + return ap->sin.sin_port; +#ifdef AF_INET6 + if (ap->sa.sa_family == AF_INET6) + return ap->sin6.sin6_port; +#endif + return 0; +} + +/*- + * addr_strings - helper function to get host and service names + * @ap: the BIO_ADDR that has the input info + * @numeric: 0 if actual names should be returned, 1 if the numeric + * representation should be returned. + * @hostname: a pointer to a pointer to a memory area to store the + * host name or numeric representation. Unused if NULL. + * @service: a pointer to a pointer to a memory area to store the + * service name or numeric representation. Unused if NULL. + * + * The return value is 0 on failure, with the error code in the error + * stack, and 1 on success. + */ +static int addr_strings(const BIO_ADDR *ap, int numeric, + char **hostname, char **service) +{ + if (1) { +#ifdef AI_PASSIVE + int ret = 0; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + int flags = 0; + + if (numeric) + flags |= NI_NUMERICHOST | NI_NUMERICSERV; + + if ((ret = getnameinfo(BIO_ADDR_sockaddr(ap), + BIO_ADDR_sockaddr_size(ap), + host, sizeof(host), serv, sizeof(serv), + flags)) != 0) { +# ifdef EAI_SYSTEM + if (ret == EAI_SYSTEM) { + SYSerr(SYS_F_GETNAMEINFO, get_last_socket_error()); + BIOerr(BIO_F_ADDR_STRINGS, ERR_R_SYS_LIB); + } else +# endif + { + BIOerr(BIO_F_ADDR_STRINGS, ERR_R_SYS_LIB); + ERR_add_error_data(1, gai_strerror(ret)); + } + return 0; + } + if (hostname) + *hostname = OPENSSL_strdup(host); + if (service) + *service = OPENSSL_strdup(serv); + } else { +#endif + if (hostname) + *hostname = OPENSSL_strdup(inet_ntoa(ap->sin.sin_addr)); + if (service) { + char serv[6]; /* port is 16 bits => max 5 decimal digits */ + BIO_snprintf(serv, sizeof(serv), "%d", ntohs(ap->sin.sin_port)); + *service = OPENSSL_strdup(serv); + } + } + + return 1; +} + +char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) +{ + char *hostname = NULL; + + if (addr_strings(ap, numeric, &hostname, NULL)) + return hostname; + + return NULL; +} + +char *BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric) +{ + char *service = NULL; + + if (addr_strings(ap, numeric, NULL, &service)) + return service; + + return NULL; +} + +char *BIO_ADDR_path_string(const BIO_ADDR *ap) +{ +#ifdef AF_UNIX + if (ap->sa.sa_family == AF_UNIX) + return OPENSSL_strdup(ap->sun.sun_path); +#endif + return NULL; +} + +/* + * BIO_ADDR_sockaddr - non-public routine to return the struct sockaddr + * for a given BIO_ADDR. In reality, this is simply a type safe cast. + * The returned struct sockaddr is const, so it can't be tampered with. + */ +const struct sockaddr *BIO_ADDR_sockaddr(const BIO_ADDR *ap) +{ + return &(ap->sa); +} + +/* + * BIO_ADDR_sockaddr_noconst - non-public function that does the same + * as BIO_ADDR_sockaddr, but returns a non-const. USE WITH CARE, as + * it allows you to tamper with the data (and thereby the contents + * of the input BIO_ADDR). + */ +struct sockaddr *BIO_ADDR_sockaddr_noconst(BIO_ADDR *ap) +{ + return &(ap->sa); +} + +/* + * BIO_ADDR_sockaddr_size - non-public function that returns the size + * of the struct sockaddr the BIO_ADDR is using. If the protocol family + * isn't set or is something other than AF_INET, AF_INET6 or AF_UNIX, + * the size of the BIO_ADDR type is returned. + */ +socklen_t BIO_ADDR_sockaddr_size(const BIO_ADDR *ap) +{ + if (ap->sa.sa_family == AF_INET) + return sizeof(ap->sin); +#ifdef AF_INET6 + if (ap->sa.sa_family == AF_INET6) + return sizeof(ap->sin6); +#endif +#ifdef AF_UNIX + if (ap->sa.sa_family == AF_UNIX) + return sizeof(ap->sun); +#endif + return sizeof(*ap); +} + +/********************************************************************** + * + * Address into database + * + */ + +const BIO_ADDRINFO *BIO_ADDRINFO_next(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return bai->bai_next; + return NULL; +} + +int BIO_ADDRINFO_family(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return bai->bai_family; + return 0; +} + +int BIO_ADDRINFO_socktype(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return bai->bai_socktype; + return 0; +} + +int BIO_ADDRINFO_protocol(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return bai->bai_protocol; + return 0; +} + +/* + * BIO_ADDRINFO_sockaddr_size - non-public function that returns the size + * of the struct sockaddr inside the BIO_ADDRINFO. + */ +socklen_t BIO_ADDRINFO_sockaddr_size(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return bai->bai_addrlen; + return 0; +} + +/* + * BIO_ADDRINFO_sockaddr - non-public function that returns bai_addr + * as the struct sockaddr it is. + */ +const struct sockaddr *BIO_ADDRINFO_sockaddr(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return bai->bai_addr; + return NULL; +} + +const BIO_ADDR *BIO_ADDRINFO_address(const BIO_ADDRINFO *bai) +{ + if (bai != NULL) + return (BIO_ADDR *)bai->bai_addr; + return NULL; +} + +void BIO_ADDRINFO_free(BIO_ADDRINFO *bai) +{ + if (bai == NULL) + return; + +#ifdef AI_PASSIVE +# ifdef AF_UNIX +# define _cond bai->bai_family != AF_UNIX +# else +# define _cond 1 +# endif + if (_cond) { + freeaddrinfo(bai); + return; + } +#endif + + /* Free manually when we know that addrinfo_wrap() was used. + * See further comment above addrinfo_wrap() + */ + while (bai != NULL) { + BIO_ADDRINFO *next = bai->bai_next; + OPENSSL_free(bai->bai_addr); + OPENSSL_free(bai); + bai = next; + } +} + +/********************************************************************** + * + * Service functions + * + */ + +/*- + * The specs in hostserv can take these forms: + * + * host:service => *host = "host", *service = "service" + * host:* => *host = "host", *service = NULL + * host: => *host = "host", *service = NULL + * :service => *host = NULL, *service = "service" + * *:service => *host = NULL, *service = "service" + * + * in case no : is present in the string, the result depends on + * hostserv_prio, as follows: + * + * when hostserv_prio == BIO_PARSE_PRIO_HOST + * host => *host = "host", *service untouched + * + * when hostserv_prio == BIO_PARSE_PRIO_SERV + * service => *host untouched, *service = "service" + * + */ +int BIO_parse_hostserv(const char *hostserv, char **host, char **service, + enum BIO_hostserv_priorities hostserv_prio) +{ + const char *h = NULL; size_t hl = 0; + const char *p = NULL; size_t pl = 0; + + if (*hostserv == '[') { + if ((p = strchr(hostserv, ']')) == NULL) + goto spec_err; + h = hostserv + 1; + hl = p - h; + p++; + if (*p == '\0') + p = NULL; + else if (*p != ':') + goto spec_err; + else { + p++; + pl = strlen(p); + } + } else { + const char *p2 = strrchr(hostserv, ':'); + p = strchr(hostserv, ':'); + + /*- + * Check for more than one colon. There are three possible + * interpretations: + * 1. IPv6 address with port number, last colon being separator. + * 2. IPv6 address only. + * 3. IPv6 address only if hostserv_prio == BIO_PARSE_PRIO_HOST, + * IPv6 address and port number if hostserv_prio == BIO_PARSE_PRIO_SERV + * Because of this ambiguity, we currently choose to make it an + * error. + */ + if (p != p2) + goto amb_err; + + if (p != NULL) { + h = hostserv; + hl = p - h; + p++; + pl = strlen(p); + } else if (hostserv_prio == BIO_PARSE_PRIO_HOST) { + h = hostserv; + hl = strlen(h); + } else { + p = hostserv; + pl = strlen(p); + } + } + + if (strchr(p, ':')) + goto spec_err; + + if (h != NULL && host != NULL) { + if (hl == 0 + || (hl == 1 && h[0] == '*')) { + *host = NULL; + } else { + *host = OPENSSL_strndup(h, hl); + if (*host == NULL) + goto memerr; + } + } + if (p != NULL && service != NULL) { + if (pl == 0 + || (pl == 1 && p[0] == '*')) { + *service = NULL; + } else { + *service = OPENSSL_strndup(p, pl); + if (*service == NULL) + goto memerr; + } + } + + return 1; + amb_err: + BIOerr(BIO_F_BIO_PARSE_HOSTSERV, BIO_R_AMBIGUOUS_HOST_OR_SERVICE); + return 0; + spec_err: + BIOerr(BIO_F_BIO_PARSE_HOSTSERV, BIO_R_MALFORMED_HOST_OR_SERVICE); + return 0; + memerr: + BIOerr(BIO_F_BIO_PARSE_HOSTSERV, ERR_R_MALLOC_FAILURE); + return 0; +} + +/* addrinfo_wrap is used to build our own addrinfo "chain". + * (it has only one entry, so calling it a chain may be a stretch) + * It should ONLY be called when getaddrinfo() and friends + * aren't available, OR when dealing with a non IP protocol + * family, such as AF_UNIX + * + * the return value is 1 on success, or 0 on failure, which + * only happens if a memory allocation error occured. + */ +static int addrinfo_wrap(int family, int socktype, + const void *where, size_t wherelen, + unsigned short port, + BIO_ADDRINFO **bai) +{ + OPENSSL_assert(bai != NULL); + + *bai = (BIO_ADDRINFO *)OPENSSL_zalloc(sizeof(**bai)); + + if (*bai == NULL) + return 0; + (*bai)->bai_family = family; + (*bai)->bai_socktype = socktype; + if (socktype == SOCK_STREAM) + (*bai)->bai_protocol = IPPROTO_TCP; + if (socktype == SOCK_DGRAM) + (*bai)->bai_protocol = IPPROTO_UDP; +#ifdef AF_UNIX + if (family == AF_UNIX) + (*bai)->bai_protocol = 0; +#endif + { + /* Magic: We know that BIO_ADDR_sockaddr_noconst is really + just an advanced cast of BIO_ADDR* to struct sockaddr * + by the power of union, so while it may seem that we're + creating a memory leak here, we are not. It will be + all right. */ + BIO_ADDR *addr = BIO_ADDR_new(); + if (addr != NULL) { + BIO_ADDR_rawmake(addr, family, where, wherelen, port); + (*bai)->bai_addr = BIO_ADDR_sockaddr_noconst(addr); + } + } + (*bai)->bai_next = NULL; + if ((*bai)->bai_addr == NULL) { + BIO_ADDRINFO_free(*bai); + *bai = NULL; + return 0; + } + return 1; +} + +/*- + * BIO_lookup - look up the node and service you want to connect to. + * @node: the node you want to connect to. + * @service: the service you want to connect to. + * @lookup_type: declare intent with the result, client or server. + * @family: the address family you want to use. Use AF_UNSPEC for any, or + * AF_INET, AF_INET6 or AF_UNIX. + * @socktype: The socket type you want to use. Can be SOCK_STREAM, SOCK_DGRAM + * or 0 for all. + * @res: Storage place for the resulting list of returned addresses + * + * This will do a lookup of the node and service that you want to connect to. + * It returns a linked list of different addresses you can try to connect to. + * + * When no longer needed you should call BIO_ADDRINFO_free() to free the result. + * + * The return value is 1 on success or 0 in case of error. + */ +int BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res) +{ + int ret = 0; /* Assume failure */ + + switch(family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif +#ifdef AF_UNIX + case AF_UNIX: +#endif +#ifdef AF_UNSPEC + case AF_UNSPEC: +#endif + break; + default: + BIOerr(BIO_F_BIO_LOOKUP, BIO_R_UNSUPPORTED_PROTOCOL_FAMILY); + return 0; + } + +#ifdef AF_UNIX + if (family == AF_UNIX) { + if (addrinfo_wrap(family, socktype, host, strlen(host), 0, res)) + return 1; + else + BIOerr(BIO_F_BIO_LOOKUP, ERR_R_MALLOC_FAILURE); + return 0; + } +#endif + + if (1) { +#ifdef AI_PASSIVE + struct addrinfo hints; + + hints.ai_flags = 0; +# ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +# endif + hints.ai_family = family; + hints.ai_socktype = socktype; + hints.ai_protocol = 0; + hints.ai_addrlen = 0; + hints.ai_addr = NULL; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + + if (lookup_type == BIO_LOOKUP_SERVER) + hints.ai_flags |= AI_PASSIVE; + + /* Note that |res| SHOULD be a 'struct addrinfo **' thanks to + * macro magic in bio_lcl.h + */ + switch (getaddrinfo(host, service, &hints, res)) { +# ifdef EAI_SYSTEM + case EAI_SYSTEM: + SYSerr(SYS_F_GETADDRINFO, get_last_socket_error()); + BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB); + break; +# endif + case 0: + ret = 1; /* Success */ + break; + default: + BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB); + ERR_add_error_data(1, gai_strerror(ret)); + break; + } + } else { +#endif + struct hostent *he; + /* Windows doesn't seem to have in_addr_t */ +#ifdef OPENSSL_SYS_WINDOWS + uint32_t he_fallback_address = INADDR_ANY; + uint32_t *he_fallback_addresses[] = { &he_fallback_address, NULL }; +#else + in_addr_t he_fallback_address = INADDR_ANY; + in_addr_t *he_fallback_addresses[] = { &he_fallback_address, NULL }; +#endif + struct hostent he_fallback = { NULL, NULL, AF_INET, + sizeof(he_fallback_address), + (char **)&he_fallback_addresses }; + struct servent *se; + /* Apprently, on WIN64, s_proto and s_port have traded places... */ +#ifdef _WIN64 + struct servent se_fallback = { NULL, NULL, NULL, 0 }; +#else + struct servent se_fallback = { NULL, NULL, 0, NULL }; +#endif + char *proto = NULL; + + CRYPTO_w_lock(CRYPTO_LOCK_GETHOSTBYNAME); + CRYPTO_w_lock(CRYPTO_LOCK_GETSERVBYNAME); + if (host == NULL) { + he = &he_fallback; + switch(lookup_type) { + case BIO_LOOKUP_CLIENT: + he_fallback_address = INADDR_LOOPBACK; + break; + case BIO_LOOKUP_SERVER: + he_fallback_address = INADDR_ANY; + break; + default: + OPENSSL_assert(("We forgot to handle a lookup type!" == 0)); + break; + } + } else { + he = gethostbyname(host); + + if (he == NULL) { + BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB); + ERR_add_error_data(1, hstrerror(h_errno)); + ret = 0; + goto err; + } + } + + if (service == NULL) { + se_fallback.s_port = 0; + se_fallback.s_proto = proto; + se = &se_fallback; + } else { + char *endp = NULL; + long portnum = strtol(service, &endp, 10); + + if (endp != service && *endp == '\0' + && portnum > 0 && portnum < 65536) { + se_fallback.s_port = htons(portnum); + se_fallback.s_proto = proto; + se = &se_fallback; + } else if (endp == service) { + switch (socktype) { + case SOCK_STREAM: + proto = "tcp"; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + } + se = getservbyname(service, proto); + + if (se == NULL) { + BIOerr(BIO_F_BIO_LOOKUP, ERR_R_SYS_LIB); + ERR_add_error_data(1, hstrerror(h_errno)); + goto err; + } + } else { + BIOerr(BIO_F_BIO_LOOKUP, BIO_R_MALFORMED_HOST_OR_SERVICE); + goto err; + } + } + + *res = NULL; + + { + char **addrlistp; + size_t addresses; + BIO_ADDRINFO *tmp_bai = NULL; + + /* The easiest way to create a linked list from an + array is to start from the back */ + for(addrlistp = he->h_addr_list; *addrlistp != NULL; + addrlistp++) + ; + + for(addresses = addrlistp - he->h_addr_list; + addrlistp--, addresses-- > 0; ) { + if (!addrinfo_wrap(he->h_addrtype, socktype, + *addrlistp, he->h_length, + se->s_port, &tmp_bai)) + goto addrinfo_malloc_err; + tmp_bai->bai_next = *res; + *res = tmp_bai; + continue; + addrinfo_malloc_err: + BIO_ADDRINFO_free(*res); + *res = NULL; + BIOerr(BIO_F_BIO_LOOKUP, ERR_R_MALLOC_FAILURE); + ret = 0; + goto err; + } + + ret = 1; + } + err: + CRYPTO_w_unlock(CRYPTO_LOCK_GETSERVBYNAME); + CRYPTO_w_unlock(CRYPTO_LOCK_GETHOSTBYNAME); + } + + return ret; +} diff --git a/crypto/bio/b_sock.c b/crypto/bio/b_sock.c index a7a6aab328..5a502514da 100644 --- a/crypto/bio/b_sock.c +++ b/crypto/bio/b_sock.c @@ -58,15 +58,10 @@ #include #include #include -#define USE_SOCKETS -#include "internal/cryptlib.h" -#include -#if defined(OPENSSL_SYS_NETWARE) && defined(NETWARE_BSDSOCK) -# include -# if defined(NETWARE_CLIB) -# include +#include "bio_lcl.h" +#if defined(NETWARE_CLIB) +# include NETDB_DEFINE_CONTEXT -# endif #endif #ifndef OPENSSL_NO_SOCK # include diff --git a/crypto/bio/bio_lcl.h b/crypto/bio/bio_lcl.h index 741884da84..19bfe53318 100644 --- a/crypto/bio/bio_lcl.h +++ b/crypto/bio/bio_lcl.h @@ -1,5 +1,81 @@ +#define USE_SOCKETS +#include "e_os.h" + +/* BEGIN BIO_ADDRINFO/BIO_ADDR stuff. */ + +#ifndef OPENSSL_NO_SOCK +/* + * Throughout this file and b_addr.c, the existence of the macro + * AI_PASSIVE is used to detect the availability of struct addrinfo, + * getnameinfo() and getaddrinfo(). If that macro doesn't exist, + * we use our own implementation instead. + */ + +/* + * It's imperative that these macros get defined before openssl/bio.h gets + * included. Otherwise, the AI_PASSIVE hack will not work properly. + * For clarity, we check for internal/cryptlib.h since it's a common header + * that also includes bio.h. + */ +# ifdef HEADER_CRYPTLIB_H +# error internal/cryptlib.h included before bio_lcl.h +# endif +# ifdef HEADER_BIO_H +# error openssl/bio.h included before bio_lcl.h +# endif + +/* + * Undefine AF_UNIX on systems that define it but don't support it. + */ +# if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_VMS) +# undef AF_UNIX +# endif + +# ifdef AI_PASSIVE +# define bio_addrinfo_st addrinfo +# define bai_family ai_family +# define bai_socktype ai_socktype +# define bai_protocol ai_protocol +# define bai_addrlen ai_addrlen +# define bai_addr ai_addr +# define bai_next ai_next +# else +struct bio_addrinfo_st { + int bai_family; + int bai_socktype; + int bai_protocol; + size_t bai_addrlen; + struct sockaddr *bai_addr; + struct bio_addrinfo_st *bai_next; +}; +# endif + +union bio_addr_st { + struct sockaddr sa; +# ifdef AF_INET6 + struct sockaddr_in6 sin6; +# endif + struct sockaddr_in sin; +# ifdef AF_UNIX + struct sockaddr_un sun; +# endif +}; +#endif + +/* END BIO_ADDRINFO/BIO_ADDR stuff. */ + +#include "internal/cryptlib.h" #include +#ifndef OPENSSL_NO_SOCK +int BIO_ADDR_make(BIO_ADDR *ap, const struct sockaddr *sa); +const struct sockaddr *BIO_ADDR_sockaddr(const BIO_ADDR *ap); +struct sockaddr *BIO_ADDR_sockaddr_noconst(BIO_ADDR *ap); +socklen_t BIO_ADDR_sockaddr_size(const BIO_ADDR *ap); +socklen_t BIO_ADDRINFO_sockaddr_size(const BIO_ADDRINFO *bai); +const struct sockaddr *BIO_ADDRINFO_sockaddr(const BIO_ADDRINFO *bai); +#endif + #if BIO_FLAGS_UPLINK==0 /* Shortcut UPLINK calls on most platforms... */ # define UP_stdin stdin @@ -33,4 +109,6 @@ # define UP_lseek lseek # define UP_close close # endif + #endif + diff --git a/doc/crypto/BIO_ADDR.pod b/doc/crypto/BIO_ADDR.pod new file mode 100644 index 0000000000..cec7dddd7e --- /dev/null +++ b/doc/crypto/BIO_ADDR.pod @@ -0,0 +1,109 @@ +=pod + +=head1 NAME + +BIO_ADDR, BIO_ADDR_new, BIO_ADDR_free, BIO_ADDR_rawmake, +BIO_ADDR_family, BIO_ADDR_rawaddress, BIO_ADDR_rawport, +BIO_ADDR_hostname_string, BIO_ADDR_service_string, +BIO_ADDR_path_string - BIO_ADDR routines + +=head1 SYNOPSIS + + #include + #include + + typedef union bio_addr_st BIO_ADDR; + + BIO_ADDR *BIO_ADDR_new(void); + void BIO_ADDR_free(BIO_ADDR *); + int BIO_ADDR_rawmake(BIO_ADDR *ap, int family, + const void *where, size_t wherelen, unsigned short port); + int BIO_ADDR_family(const BIO_ADDR *ap); + int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l); + unsigned short BIO_ADDR_rawport(const BIO_ADDR *ap); + char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric); + char *BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric); + char *BIO_ADDR_path_string(const BIO_ADDR *ap); + +=head1 DESCRIPTION + +The B type is a wrapper around all types of socket +addresses that OpenSSL deals with, currently transparently +supporting AF_INET, AF_INET6 and AF_UNIX according to what's +available on the platform at hand. + +BIO_ADDR_new() creates a new unfilled B, to be used +with routines that will fill it with information, such as +BIO_accept_ex(). + +BIO_ADDR_free() frees a B created with BIO_ADDR_new(). + +BIO_ADDR_rawmake() takes a protocol B, an byte array of +size B with an address in network byte order pointed at +by B and a port number in network byte order in B (except +for the B protocol family, where B is meaningless and +therefore ignored) and populates the given B with them. +In case this creates a B B, B is expected +to be the length of the path string (not including the terminating +NUL, such as the result of a call to strlen()). +I below>. + +BIO_ADDR_family() returns the protocol family of the given +B. The possible non-error results are one of the +constants AF_INET, AF_INET6 and AF_UNIX. + +BIO_ADDR_rawaddress() will write the raw address of the given +B in the area pointed at by B

if B

is non-NULL, +and will set B<*l> to be the amount of bytes the raw address +takes up if B is non-NULL. +A technique to only find out the size of the address is a call +with B

set to B. The raw address will be in network byte +order, most significant byte first. +In case this is a B B, B gets the length of the +path string (not including the terminating NUL, such as the result of +a call to strlen()). +I below>. + +BIO_ADDR_rawport() returns the raw port of the given B. +The raw port will be in network byte order. + +BIO_ADDR_hostname_string() returns a character string with the +hostname of the given B. If B is 1, the string +will contain the numerical form of the address. This only works for +B of the protocol families AF_INET and AF_INET6. The +returned string has been allocated on the heap and must be freed +with OPENSSL_free(). + +BIO_ADDR_service_string() returns a character string with the +service name of the port of the given B. If B +is 1, the string will contain the port number. This only works +for B of the protocol families AF_INET and AF_INET6. The +returned string has been allocated on the heap and must be freed +with OPENSSL_free(). + +BIO_ADDR_path_string() returns a character string with the path +of the given B. This only works for B of the +protocol family AF_UNIX. The returned string has been allocated +on the heap and must be freed with OPENSSL_free(). + +=head1 RAW ADDRESSES + +Both BIO_ADDR_rawmake() and BIO_ADDR_rawaddress() take a pointer to a +network byte order address of a specific site. Internally, those are +treated as a pointer to B (for B), B (for B) or B (for B), all +depending on the protocol family the address is for. + +=head1 RETURN VALUES + +The string producing functions BIO_ADDR_hostname_string(), +BIO_ADDR_service_string() and BIO_ADDR_path_string() will +return B on error and leave an error indication on the +OpenSSL error stack. + +All other functions described here return 0 or B when the +information they should return isn't available. + +=head1 SEE ALSO + +L, L diff --git a/doc/crypto/BIO_ADDRINFO.pod b/doc/crypto/BIO_ADDRINFO.pod new file mode 100644 index 0000000000..85652add9b --- /dev/null +++ b/doc/crypto/BIO_ADDRINFO.pod @@ -0,0 +1,82 @@ +=pod + +=head1 NAME + +BIO_ADDRINFO, BIO_ADDRINFO_lookup, BIO_ADDRINFO_next, BIO_ADDRINFO_free, +BIO_ADDRINFO_family, BIO_ADDRINFO_socktype, BIO_ADDRINFO_protocol, +BIO_ADDRINFO_sockaddr, BIO_ADDRINFO_sockaddr_size, BIO_ADDRINFO_address +- BIO_ADDRINFO type and routines + +=head1 SYNOPSIS + + #include + #include + + typedef union bio_addrinfo_st BIO_ADDRINFO; + + enum BIO_lookup_type { + BIO_LOOKUP_CLIENT, BIO_LOOKUP_SERVER + }; + int BIO_lookup(const char *node, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res); + + const BIO_ADDRINFO *BIO_ADDRINFO_next(const BIO_ADDRINFO *bai); + int BIO_ADDRINFO_family(const BIO_ADDRINFO *bai); + int BIO_ADDRINFO_socktype(const BIO_ADDRINFO *bai); + int BIO_ADDRINFO_protocol(const BIO_ADDRINFO *bai); + const BIO_ADDR *BIO_ADDRINFO_address(const BIO_ADDRINFO *bai); + void BIO_ADDRINFO_free(BIO_ADDRINFO *bai); + +=head1 DESCRIPTION + +The B type is a wrapper for address information +types provided on your platform. + +B normally forms a chain of several that can be +picked at one by one. + +BIO_lookup() looks up a specified B and B, and +uses B to determine what the default address should +be if B is B. B, B are used to +determine what protocol family and protocol should be used for +the lookup. B can be any of AF_INET, AF_INET6, AF_UNIX and +AF_UNSPEC, and B can be SOCK_STREAM or SOCK_DGRAM. +B points at a pointer to hold the start of a B +chain. +For the family B, BIO_lookup() will ignore the B +parameter and expects the B parameter to hold the path to the +socket file. + +BIO_ADDRINFO_family() returns the family of the given +B. The result will be one of the constants +AF_INET, AF_INET6 and AF_UNIX. + +BIO_ADDRINFO_socktype() returns the socket type of the given +B. The result will be one of the constants +SOCK_STREAM and SOCK_DGRAM. + +BIO_ADDRINFO_protocol() returns the protocol id of the given +B. The result will be one of the constants +IPPROTO_TCP and IPPROTO_UDP. + +BIO_ADDRINFO_address() returns the underlying B +of the given B. + +BIO_ADDRINFO_next() returns the next B in the chain +from the given one. + +BIO_ADDRINFO_free() frees the chain of B starting +with the given one. + +=head1 RETURN VALUES + +BIO_lookup() returns 1 on success and 0 when an error occured, and +will leave an error indicaton on the OpenSSL error stack in that case. + +All other functions described here return 0 or B when the +information they should return isn't available. + +=head1 SEE ALSO + +L diff --git a/doc/crypto/BIO_parse_hostserv.pod b/doc/crypto/BIO_parse_hostserv.pod new file mode 100644 index 0000000000..0c9cfbe1a3 --- /dev/null +++ b/doc/crypto/BIO_parse_hostserv.pod @@ -0,0 +1,62 @@ +=pod + +=head1 NAME + +BIO_parse_hostserv - utility routines to parse a standard host and service +string + +=head1 SYNOPSIS + + #include + + enum BIO_hostserv_priorities { + BIO_PARSE_PRIO_HOST, BIO_PARSE_PRIO_SERV + }; + int BIO_parse_hostserv(const char *hostserv, char **host, char **service, + enum BIO_hostserv_priorities hostserv_prio); + +=head1 DESCRIPTION + +BIO_parse_hostserv() will parse the information given in B, +create strings with the host name and service name and give those +back via B and B. Those will need to be freed after +they are used. B helps determine if B shall +be interpreted primarly as a host name or a service name in ambiguous +cases. + +The syntax the BIO_parse_hostserv() recognises is: + + host + ':' + service + host + ':' + '*' + host + ':' + ':' + service + '*' + ':' + service + host + service + +The host part can be a name or an IP address. If it's a IPv6 +address, it MUST be enclosed in brackets, such as '[::1]'. + +The service part can be a service name or its port number. + +The returned values will depend on the given B string +and B, as follows: + + host + ':' + service => *host = "host", *service = "service" + host + ':' + '*' => *host = "host", *service = NULL + host + ':' => *host = "host", *service = NULL + ':' + service => *host = NULL, *service = "service" + '*' + ':' + service => *host = NULL, *service = "service" + + in case no ':' is present in the string, the result depends on + hostserv_prio, as follows: + + when hostserv_prio == BIO_PARSE_PRIO_HOST + host => *host = "host", *service untouched + + when hostserv_prio == BIO_PARSE_PRIO_SERV + service => *host untouched, *service = "service" + +=head1 SEE ALSO + +L diff --git a/e_os.h b/e_os.h index 9b9bb4f765..4456b7e51c 100644 --- a/e_os.h +++ b/e_os.h @@ -497,6 +497,7 @@ struct servent *PASCAL getservbyname(const char *, const char *); * configured for BSD */ # if defined(NETWARE_BSDSOCK) +# include # include # include # include @@ -545,6 +546,7 @@ struct servent *PASCAL getservbyname(const char *, const char *); # endif # include # include +# include # endif # ifdef OPENSSL_SYS_AIX diff --git a/include/openssl/bio.h b/include/openssl/bio.h index 9b398ee51b..999e573ae2 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -221,6 +221,9 @@ extern "C" { */ # define BIO_FLAGS_MEM_RDONLY 0x200 +typedef union bio_addr_st BIO_ADDR; +typedef struct bio_addrinfo_st BIO_ADDRINFO; + void BIO_set_flags(BIO *b, int flags); int BIO_test_flags(const BIO *b, int flags); void BIO_clear_flags(BIO *b, int flags); @@ -699,6 +702,35 @@ int BIO_dump_indent_fp(FILE *fp, const char *s, int len, int indent); int BIO_hex_string(BIO *out, int indent, int width, unsigned char *data, int datalen); +BIO_ADDR *BIO_ADDR_new(void); +int BIO_ADDR_rawmake(BIO_ADDR *ap, int family, + const void *where, size_t wherelen, unsigned short port); +void BIO_ADDR_free(BIO_ADDR *); +int BIO_ADDR_family(const BIO_ADDR *ap); +int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l); +unsigned short BIO_ADDR_rawport(const BIO_ADDR *ap); +char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric); +char *BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric); +char *BIO_ADDR_path_string(const BIO_ADDR *ap); + +const BIO_ADDRINFO *BIO_ADDRINFO_next(const BIO_ADDRINFO *bai); +int BIO_ADDRINFO_family(const BIO_ADDRINFO *bai); +int BIO_ADDRINFO_socktype(const BIO_ADDRINFO *bai); +int BIO_ADDRINFO_protocol(const BIO_ADDRINFO *bai); +const BIO_ADDR *BIO_ADDRINFO_address(const BIO_ADDRINFO *bai); +void BIO_ADDRINFO_free(BIO_ADDRINFO *bai); + +enum BIO_hostserv_priorities { + BIO_PARSE_PRIO_HOST, BIO_PARSE_PRIO_SERV +}; +int BIO_parse_hostserv(const char *hostserv, char **host, char **service, + enum BIO_hostserv_priorities hostserv_prio); +enum BIO_lookup_type { + BIO_LOOKUP_CLIENT, BIO_LOOKUP_SERVER +}; +int BIO_lookup(const char *host, const char *service, + enum BIO_lookup_type lookup_type, + int family, int socktype, BIO_ADDRINFO **res); struct hostent *BIO_gethostbyname(const char *name); /*- * We might want a thread-safe interface too: -- 2.25.1