/* * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif /* * VC configurations may define UNICODE, to indicate to the C RTL that * WCHAR functions are preferred. * This affects functions like gai_strerror(), which is implemented as * an alias macro for gai_strerrorA() (which returns a const char *) or * gai_strerrorW() (which returns a const WCHAR *). This source file * assumes POSIX declarations, so prefer the non-UNICODE definitions. */ #undef UNICODE #include #include #include "bio_local.h" #include #ifndef OPENSSL_NO_SOCK #include #include #include "internal/thread_once.h" CRYPTO_RWLOCK *bio_lookup_lock; static CRYPTO_ONCE bio_lookup_init = CRYPTO_ONCE_STATIC_INIT; /* * Throughout this file and bio_local.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 = OPENSSL_zalloc(sizeof(*ret)); if (ret == NULL) return NULL; ret->sa.sa_family = AF_UNSPEC; return ret; } void BIO_ADDR_free(BIO_ADDR *ap) { OPENSSL_free(ap); } int BIO_ADDR_copy(BIO_ADDR *dst, const BIO_ADDR *src) { if (dst == NULL || src == NULL) return 0; if (src->sa.sa_family == AF_UNSPEC) { BIO_ADDR_clear(dst); return 1; } return BIO_ADDR_make(dst, &src->sa); } BIO_ADDR *BIO_ADDR_dup(const BIO_ADDR *ap) { BIO_ADDR *ret = NULL; if (ap != NULL) { ret = BIO_ADDR_new(); if (ret != NULL && !BIO_ADDR_copy(ret, ap)) { BIO_ADDR_free(ret); ret = NULL; } } return ret; } void BIO_ADDR_clear(BIO_ADDR *ap) { memset(ap, 0, sizeof(*ap)); ap->sa.sa_family = AF_UNSPEC; } /* * 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) { memcpy(&(ap->s_in), sa, sizeof(struct sockaddr_in)); return 1; } #if OPENSSL_USE_IPV6 if (sa->sa_family == AF_INET6) { memcpy(&(ap->s_in6), sa, sizeof(struct sockaddr_in6)); return 1; } #endif #ifndef OPENSSL_NO_UNIX_SOCK if (sa->sa_family == AF_UNIX) { memcpy(&(ap->s_un), sa, sizeof(struct sockaddr_un)); return 1; } #endif return 0; } int BIO_ADDR_rawmake(BIO_ADDR *ap, int family, const void *where, size_t wherelen, unsigned short port) { #ifndef OPENSSL_NO_UNIX_SOCK if (family == AF_UNIX) { if (wherelen + 1 > sizeof(ap->s_un.sun_path)) return 0; memset(&ap->s_un, 0, sizeof(ap->s_un)); ap->s_un.sun_family = family; strncpy(ap->s_un.sun_path, where, sizeof(ap->s_un.sun_path) - 1); return 1; } #endif if (family == AF_INET) { if (wherelen != sizeof(struct in_addr)) return 0; memset(&ap->s_in, 0, sizeof(ap->s_in)); ap->s_in.sin_family = family; ap->s_in.sin_port = port; ap->s_in.sin_addr = *(struct in_addr *)where; return 1; } #if OPENSSL_USE_IPV6 if (family == AF_INET6) { if (wherelen != sizeof(struct in6_addr)) return 0; memset(&ap->s_in6, 0, sizeof(ap->s_in6)); ap->s_in6.sin6_family = family; ap->s_in6.sin6_port = port; ap->s_in6.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->s_in.sin_addr); addrptr = &ap->s_in.sin_addr; } #if OPENSSL_USE_IPV6 else if (ap->sa.sa_family == AF_INET6) { len = sizeof(ap->s_in6.sin6_addr); addrptr = &ap->s_in6.sin6_addr; } #endif #ifndef OPENSSL_NO_UNIX_SOCK else if (ap->sa.sa_family == AF_UNIX) { len = strlen(ap->s_un.sun_path); addrptr = &ap->s_un.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->s_in.sin_port; #if OPENSSL_USE_IPV6 if (ap->sa.sa_family == AF_INET6) return ap->s_in6.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 * hostname 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 (BIO_sock_init() != 1) return 0; 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) { ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(), "calling getnameinfo()"); } else # endif { ERR_raise_data(ERR_LIB_BIO, ERR_R_SYS_LIB, gai_strerror(ret)); } return 0; } /* VMS getnameinfo() has a bug, it doesn't fill in serv, which * leaves it with whatever garbage that happens to be there. * However, we initialise serv with the empty string (serv[0] * is therefore NUL), so it gets real easy to detect when things * didn't go the way one might expect. */ if (serv[0] == '\0') { BIO_snprintf(serv, sizeof(serv), "%d", ntohs(BIO_ADDR_rawport(ap))); } if (hostname != NULL) *hostname = OPENSSL_strdup(host); if (service != NULL) *service = OPENSSL_strdup(serv); } else { #endif if (hostname != NULL) *hostname = OPENSSL_strdup(inet_ntoa(ap->s_in.sin_addr)); if (service != NULL) { char serv[6]; /* port is 16 bits => max 5 decimal digits */ BIO_snprintf(serv, sizeof(serv), "%d", ntohs(ap->s_in.sin_port)); *service = OPENSSL_strdup(serv); } } if ((hostname != NULL && *hostname == NULL) || (service != NULL && *service == NULL)) { if (hostname != NULL) { OPENSSL_free(*hostname); *hostname = NULL; } if (service != NULL) { OPENSSL_free(*service); *service = NULL; } return 0; } 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) { #ifndef OPENSSL_NO_UNIX_SOCK if (ap->sa.sa_family == AF_UNIX) return OPENSSL_strdup(ap->s_un.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->s_in); #if OPENSSL_USE_IPV6 if (ap->sa.sa_family == AF_INET6) return sizeof(ap->s_in6); #endif #ifndef OPENSSL_NO_UNIX_SOCK if (ap->sa.sa_family == AF_UNIX) return sizeof(ap->s_un); #endif return sizeof(*ap); } /********************************************************************** * * Address info 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) { if (bai->bai_protocol != 0) return bai->bai_protocol; #ifndef OPENSSL_NO_UNIX_SOCK if (bai->bai_family == AF_UNIX) return 0; #endif switch (bai->bai_socktype) { case SOCK_STREAM: return IPPROTO_TCP; case SOCK_DGRAM: return IPPROTO_UDP; default: break; } } 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 # ifndef OPENSSL_NO_UNIX_SOCK # 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 (p != NULL && 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) return 0; } } if (p != NULL && service != NULL) { if (pl == 0 || (pl == 1 && p[0] == '*')) { *service = NULL; } else { *service = OPENSSL_strndup(p, pl); if (*service == NULL) return 0; } } return 1; amb_err: ERR_raise(ERR_LIB_BIO, BIO_R_AMBIGUOUS_HOST_OR_SERVICE); return 0; spec_err: ERR_raise(ERR_LIB_BIO, BIO_R_MALFORMED_HOST_OR_SERVICE); 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 occurred. */ static int addrinfo_wrap(int family, int socktype, const void *where, size_t wherelen, unsigned short port, BIO_ADDRINFO **bai) { if ((*bai = OPENSSL_zalloc(sizeof(**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; #ifndef OPENSSL_NO_UNIX_SOCK 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; } DEFINE_RUN_ONCE_STATIC(do_bio_lookup_init) { bio_lookup_lock = CRYPTO_THREAD_lock_new(); return bio_lookup_lock != NULL; } int BIO_lookup(const char *host, const char *service, enum BIO_lookup_type lookup_type, int family, int socktype, BIO_ADDRINFO **res) { return BIO_lookup_ex(host, service, lookup_type, family, socktype, 0, res); } /*- * BIO_lookup_ex - look up the host and service you want to connect to. * @host: the host (or node, in case family == AF_UNIX) 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. * @protocol: The protocol to use, e.g. IPPROTO_TCP or IPPROTO_UDP or 0 for all. * Note that some platforms may not return IPPROTO_SCTP without * explicitly requesting it (i.e. IPPROTO_SCTP may not be returned * with 0 for the protocol) * @res: Storage place for the resulting list of returned addresses * * This will do a lookup of the host 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_ex(const char *host, const char *service, int lookup_type, int family, int socktype, int protocol, BIO_ADDRINFO **res) { int ret = 0; /* Assume failure */ switch (family) { case AF_INET: #if OPENSSL_USE_IPV6 case AF_INET6: #endif #ifndef OPENSSL_NO_UNIX_SOCK case AF_UNIX: #endif #ifdef AF_UNSPEC case AF_UNSPEC: #endif break; default: ERR_raise(ERR_LIB_BIO, BIO_R_UNSUPPORTED_PROTOCOL_FAMILY); return 0; } #ifndef OPENSSL_NO_UNIX_SOCK if (family == AF_UNIX) { if (addrinfo_wrap(family, socktype, host, strlen(host), 0, res)) return 1; else ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB); return 0; } #endif if (BIO_sock_init() != 1) return 0; if (1) { #ifdef AI_PASSIVE int gai_ret = 0, old_ret = 0; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_protocol = protocol; # ifdef AI_ADDRCONFIG # ifdef AF_UNSPEC if (host != NULL && family == AF_UNSPEC) # endif hints.ai_flags |= AI_ADDRCONFIG; # endif 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_local.h */ # if defined(AI_ADDRCONFIG) && defined(AI_NUMERICHOST) retry: # endif switch ((gai_ret = getaddrinfo(host, service, &hints, res))) { # ifdef EAI_SYSTEM case EAI_SYSTEM: ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(), "calling getaddrinfo()"); ERR_raise(ERR_LIB_BIO, ERR_R_SYS_LIB); break; # endif # ifdef EAI_MEMORY case EAI_MEMORY: ERR_raise_data(ERR_LIB_BIO, ERR_R_SYS_LIB, gai_strerror(old_ret ? old_ret : gai_ret)); break; # endif case 0: ret = 1; /* Success */ break; default: # if defined(AI_ADDRCONFIG) && defined(AI_NUMERICHOST) if (hints.ai_flags & AI_ADDRCONFIG) { hints.ai_flags &= ~AI_ADDRCONFIG; hints.ai_flags |= AI_NUMERICHOST; old_ret = gai_ret; goto retry; } # endif ERR_raise_data(ERR_LIB_BIO, ERR_R_SYS_LIB, gai_strerror(old_ret ? old_ret : gai_ret)); break; } } else { #endif const struct hostent *he; /* * Because struct hostent is defined for 32-bit pointers only with * VMS C, we need to make sure that '&he_fallback_address' and * '&he_fallback_addresses' are 32-bit pointers */ #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma pointer_size save # pragma pointer_size 32 #endif /* Windows doesn't seem to have in_addr_t */ #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) static uint32_t he_fallback_address; static const char *he_fallback_addresses[] = { (char *)&he_fallback_address, NULL }; #else static in_addr_t he_fallback_address; static const char *he_fallback_addresses[] = { (char *)&he_fallback_address, NULL }; #endif static const struct hostent he_fallback = { NULL, NULL, AF_INET, sizeof(he_fallback_address), (char **)&he_fallback_addresses }; #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma pointer_size restore #endif struct servent *se; /* Apparently, 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 if (!RUN_ONCE(&bio_lookup_init, do_bio_lookup_init)) { /* Should this be raised inside do_bio_lookup_init()? */ ERR_raise(ERR_LIB_BIO, ERR_R_CRYPTO_LIB); ret = 0; goto err; } if (!CRYPTO_THREAD_write_lock(bio_lookup_lock)) { ret = 0; goto err; } he_fallback_address = INADDR_ANY; 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: /* We forgot to handle a lookup type! */ assert("We forgot to handle a lookup type!" == NULL); ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR); ret = 0; goto err; } } else { he = gethostbyname(host); if (he == NULL) { #ifndef OPENSSL_SYS_WINDOWS /* * This might be misleading, because h_errno is used as if * it was errno. To minimize mixup add 1000. Underlying * reason for this is that hstrerror is declared obsolete, * not to mention that a) h_errno is not always guaranteed * to be meaningless; b) hstrerror can reside in yet another * library, linking for sake of hstrerror is an overkill; * c) this path is not executed on contemporary systems * anyway [above getaddrinfo/gai_strerror is]. We just let * system administrator figure this out... */ # if defined(OPENSSL_SYS_VXWORKS) /* h_errno doesn't exist on VxWorks */ ERR_raise_data(ERR_LIB_SYS, 1000, "calling gethostbyname()"); # else ERR_raise_data(ERR_LIB_SYS, 1000 + h_errno, "calling gethostbyname()"); # endif #else ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(), "calling gethostbyname()"); #endif ret = 0; goto err; } } if (service == NULL) { se_fallback.s_port = 0; se_fallback.s_proto = NULL; se = &se_fallback; } else { char *endp = NULL; long portnum = strtol(service, &endp, 10); /* * Because struct servent is defined for 32-bit pointers only with * VMS C, we need to make sure that 'proto' is a 32-bit pointer. */ #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma pointer_size save # pragma pointer_size 32 #endif char *proto = NULL; #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma pointer_size restore #endif switch (socktype) { case SOCK_STREAM: proto = "tcp"; break; case SOCK_DGRAM: proto = "udp"; break; } if (endp != service && *endp == '\0' && portnum > 0 && portnum < 65536) { se_fallback.s_port = htons((unsigned short)portnum); se_fallback.s_proto = proto; se = &se_fallback; } else if (endp == service) { se = getservbyname(service, proto); if (se == NULL) { ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(), "calling getservbyname()"); goto err; } } else { ERR_raise(ERR_LIB_BIO, BIO_R_MALFORMED_HOST_OR_SERVICE); goto err; } } *res = NULL; { /* * Because hostent::h_addr_list is an array of 32-bit pointers with VMS C, * we must make sure our iterator designates the same element type, hence * the pointer size dance. */ #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma pointer_size save # pragma pointer_size 32 #endif char **addrlistp; #if defined(OPENSSL_SYS_VMS) && defined(__DECC) # pragma pointer_size restore #endif 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_wrap_err; tmp_bai->bai_next = *res; *res = tmp_bai; continue; addrinfo_wrap_err: BIO_ADDRINFO_free(*res); *res = NULL; ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB); ret = 0; goto err; } ret = 1; } err: CRYPTO_THREAD_unlock(bio_lookup_lock); } return ret; } #endif /* OPENSSL_NO_SOCK */