From 3bdf9aae5f7f4c5f47fc8f807ae4409dfe3bdd58 Mon Sep 17 00:00:00 2001 From: lpleahy Date: Thu, 9 Feb 2012 19:16:44 +0000 Subject: [PATCH] Merged socket development branch: * Fixed bug report (Duane Voth: Python sockets test application not working) by starting the receive operations when a connection is established! * Increased performance by extending the idle loop into the network stack with the Poll call. * Added support for TCPv6 (SOCK_STREAM) and UDPv6 (SOCK_DGRAM). * Added support for getaddrinfo and getnameinfo calls. * Moved application PCD values into AppPkg Signed-off-by: lpleahy git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13002 6f19259b-4bc3-4df7-8a09-765794883524 --- StdLib/BsdSocketLib/BsdSocketLib.inf | 9 +- StdLib/BsdSocketLib/getaddrinfo.c | 1964 +++++++++++++++++++++ StdLib/BsdSocketLib/getnameinfo.c | 567 ++++++ StdLib/EfiSocketLib/EfiSocketLib.inf | 8 + StdLib/EfiSocketLib/Ip4.c | 1 + StdLib/EfiSocketLib/Socket.c | 103 +- StdLib/EfiSocketLib/Socket.h | 125 ++ StdLib/EfiSocketLib/Tcp4.c | 1 + StdLib/EfiSocketLib/Tcp6.c | 2329 +++++++++++++++++++++++++ StdLib/EfiSocketLib/Udp4.c | 1 + StdLib/EfiSocketLib/Udp6.c | 1094 ++++++++++++ StdLib/EfiSocketLib/UseEfiSocketLib.c | 31 +- StdLib/Include/Efi/EfiSocketLib.h | 5 + StdLib/Include/net/if_dl.h | 4 +- StdLib/Include/net/servent.h | 60 + StdLib/Include/nsswitch.h | 237 +++ StdLib/Include/resolv.h | 4 + StdLib/SocketDxe/EntryUnload.c | 28 +- StdLib/StdLib.dec | 5 - 19 files changed, 6551 insertions(+), 25 deletions(-) create mode 100644 StdLib/BsdSocketLib/getaddrinfo.c create mode 100644 StdLib/BsdSocketLib/getnameinfo.c create mode 100644 StdLib/EfiSocketLib/Tcp6.c create mode 100644 StdLib/EfiSocketLib/Udp6.c create mode 100644 StdLib/Include/net/servent.h create mode 100644 StdLib/Include/nsswitch.h diff --git a/StdLib/BsdSocketLib/BsdSocketLib.inf b/StdLib/BsdSocketLib/BsdSocketLib.inf index 1d95f0e8f3..d095e5337d 100644 --- a/StdLib/BsdSocketLib/BsdSocketLib.inf +++ b/StdLib/BsdSocketLib/BsdSocketLib.inf @@ -31,12 +31,14 @@ bind.c close.c connect.c + getaddrinfo.c gethostbydns.c gethostbyht.c gethostbynis.c gethostname.c gethostnamadr.c gethostbynis.c + getnameinfo.c getnetbydns.c getnetbynis.c getnetbyht.c @@ -51,16 +53,9 @@ getsockname.c getsockopt.c herror.c -# inet_addr.c -# inet_lnaof.c -# inet_makeaddr.c inet_net_ntop.c inet_net_pton.c inet_neta.c -# inet_netof.c -# inet_network.c -# inet_ntoa.c -# inet_ntop.c inet_pton.c listen.c map_v4v6.c diff --git a/StdLib/BsdSocketLib/getaddrinfo.c b/StdLib/BsdSocketLib/getaddrinfo.c new file mode 100644 index 0000000000..11489421d8 --- /dev/null +++ b/StdLib/BsdSocketLib/getaddrinfo.c @@ -0,0 +1,1964 @@ +/* $NetBSD: getaddrinfo.c,v 1.91.6.1 2009/01/26 00:27:34 snj Exp $ */ +/* $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS 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 PROJECT OR 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. + */ + +/* + * Issues to be discussed: + * - Return values. There are nonstandard return values defined and used + * in the source code. This is because RFC2553 is silent about which error + * code must be returned for which situation. + * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2 + * says to use inet_aton() to convert IPv4 numeric to binary (alows + * classful form as a result). + * current code - disallow classful form for IPv4 (due to use of inet_pton). + * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is + * invalid. + * current code - SEGV on freeaddrinfo(NULL) + * Note: + * - The code filters out AFs that are not supported by the kernel, + * when globbing NULL hostname (to loopback, or wildcard). Is it the right + * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG + * in ai_flags? + * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. + * (1) what should we do against numeric hostname (2) what should we do + * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? + * non-loopback address configured? global address configured? + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getaddrinfo.c,v 1.91.6.1 2009/01/26 00:27:34 snj Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#define INET6 1 + +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//#ifdef YP +//#include +//#include +//#include +//#endif + +#include + +#define endservent_r(svd) endservent() +#define nsdispatch(pResult,dtab,database,routine,files,hostname,pai) NS_NOTFOUND +#define res_nmkquery(state,op,dname,class,type,data,datalen,newrr_in,buf,buflen) res_mkquery( op, dname, class, type, data, datalen, newrr_in, buf, buflen ) +#define res_nsend(state,buf,buflen,ans,anssiz) res_send ( buf, buflen, ans, anssiz ) + +/* Things involving an internal (static) resolver context. */ +__BEGIN_DECLS +#define __res_get_state() (( 0 != _res.nscount ) ? &_res : NULL ) +#define __res_put_state(state) +#define __res_state() _res +__END_DECLS + +#ifdef __weak_alias +__weak_alias(getaddrinfo,_getaddrinfo) +__weak_alias(freeaddrinfo,_freeaddrinfo) +__weak_alias(gai_strerror,_gai_strerror) +#endif + +#define SUCCESS 0 +#define ANY 0 +#define YES 1 +#define NO 0 + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in_loopback[] = { 127, 0, 0, 1 }; +#ifdef INET6 +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; +#endif + +static const struct afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; + int a_scoped; +} afdl [] = { +#ifdef INET6 + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback, 1}, +#endif + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback, 0}, + {0, 0, 0, 0, NULL, NULL, 0}, +}; + +struct explore { + int e_af; + int e_socktype; + int e_protocol; + const char *e_protostr; + int e_wild; +#define WILD_AF(ex) ((ex)->e_wild & 0x01) +#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) +#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) +}; + +static const struct explore explore[] = { +#if 0 + { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, +#endif +#ifdef INET6 + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, +#endif + { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, + { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, + { -1, 0, 0, NULL, 0 }, +}; + +#ifdef INET6 +#define PTON_MAX 16 +#else +#define PTON_MAX 4 +#endif + +static const ns_src default_dns_files[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NSSRC_DNS, NS_SUCCESS }, + { 0, 0 } +}; + +#define MAXPACKET (64*1024) + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +struct res_target { + struct res_target *next; + const char *name; /* domain name */ + int qclass, qtype; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer buffer */ + int n; /* result length */ +}; + +static int str2number(const char *); +static int explore_fqdn(const struct addrinfo *, const char *, + const char *, struct addrinfo **, struct servent_data *); +static int explore_null(const struct addrinfo *, + const char *, struct addrinfo **, struct servent_data *); +static int explore_numeric(const struct addrinfo *, const char *, + const char *, struct addrinfo **, const char *, struct servent_data *); +static int explore_numeric_scope(const struct addrinfo *, const char *, + const char *, struct addrinfo **, struct servent_data *); +static int get_canonname(const struct addrinfo *, + struct addrinfo *, const char *); +static struct addrinfo *get_ai(const struct addrinfo *, + const struct afd *, const char *); +static int get_portmatch(const struct addrinfo *, const char *, + struct servent_data *); +static int get_port(const struct addrinfo *, const char *, int, + struct servent_data *); +static const struct afd *find_afd(int); +#ifdef INET6 +static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); +#endif + +static struct addrinfo *getanswer(const querybuf *, int, const char *, int, + const struct addrinfo *); +static void aisort(struct addrinfo *s, res_state res); +static int _dns_getaddrinfo(void *, void *, va_list); +static void _sethtent(FILE **); +static void _endhtent(FILE **); +static struct addrinfo *_gethtent(FILE **, const char *, + const struct addrinfo *); +static int _files_getaddrinfo(void *, void *, va_list); +#ifdef YP +static struct addrinfo *_yphostent(char *, const struct addrinfo *); +static int _yp_getaddrinfo(void *, void *, va_list); +#endif + +static int res_queryN(const char *, struct res_target *, res_state); +static int res_searchN(const char *, struct res_target *, res_state); +static int res_querydomainN(const char *, const char *, + struct res_target *, res_state); + +static const char * const ai_errlist[] = { + "Success", + "Address family for hostname not supported", /* EAI_ADDRFAMILY */ + "Temporary failure in name resolution", /* EAI_AGAIN */ + "Invalid value for ai_flags", /* EAI_BADFLAGS */ + "Non-recoverable failure in name resolution", /* EAI_FAIL */ + "ai_family not supported", /* EAI_FAMILY */ + "Memory allocation failure", /* EAI_MEMORY */ + "No address associated with hostname", /* EAI_NODATA */ + "hostname nor servname provided, or not known", /* EAI_NONAME */ + "servname not supported for ai_socktype", /* EAI_SERVICE */ + "ai_socktype not supported", /* EAI_SOCKTYPE */ + "System error returned in errno", /* EAI_SYSTEM */ + "Invalid value for hints", /* EAI_BADHINTS */ + "Resolved protocol is unknown", /* EAI_PROTOCOL */ + "Argument buffer overflow", /* EAI_OVERFLOW */ + "Unknown error", /* EAI_MAX */ +}; + +/* XXX macros that make external reference is BAD. */ + +#define GET_AI(ai, afd, addr) \ +do { \ + /* external reference: pai, error, and label free */ \ + (ai) = get_ai(pai, (afd), (addr)); \ + if ((ai) == NULL) { \ + error = EAI_MEMORY; \ + goto free; \ + } \ +} while (/*CONSTCOND*/0) + +#define GET_PORT(ai, serv, svd) \ +do { \ + /* external reference: error and label free */ \ + error = get_port((ai), (serv), 0, (svd)); \ + if (error != 0) \ + goto free; \ +} while (/*CONSTCOND*/0) + +#define GET_CANONNAME(ai, str) \ +do { \ + /* external reference: pai, error and label free */ \ + error = get_canonname(pai, (ai), (str)); \ + if (error != 0) \ + goto free; \ +} while (/*CONSTCOND*/0) + +#define ERR(err) \ +do { \ + /* external reference: error, and label bad */ \ + error = (err); \ + goto bad; \ + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) + +#define MATCH_FAMILY(x, y, w) \ + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || \ + (y) == PF_UNSPEC))) +#define MATCH(x, y, w) \ + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) + +const char * +gai_strerror(int ecode) +{ + if (ecode < 0 || ecode > EAI_MAX) + ecode = EAI_MAX; + return ai_errlist[ecode]; +} + +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + _DIAGASSERT(ai != NULL); + + do { + next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + ai = next; + } while (ai); +} + +static int +str2number(const char *p) +{ + char *ep; + unsigned long v; + + _DIAGASSERT(p != NULL); + + if (*p == '\0') + return -1; + ep = NULL; + errno = 0; + v = strtoul(p, &ep, 10); + if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) + return v; + else + return -1; +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct addrinfo sentinel; + struct addrinfo *cur; + int error = 0; + struct addrinfo ai; + struct addrinfo ai0; + struct addrinfo *pai; + const struct explore *ex; + struct servent_data svd; + + /* hostname is allowed to be NULL */ + /* servname is allowed to be NULL */ + /* hints is allowed to be NULL */ + _DIAGASSERT(res != NULL); + + (void)memset(&svd, 0, sizeof(svd)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + memset(&ai, 0, sizeof(ai)); + pai = &ai; + pai->ai_flags = 0; + pai->ai_family = PF_UNSPEC; + pai->ai_socktype = ANY; + pai->ai_protocol = ANY; + pai->ai_addrlen = 0; + pai->ai_canonname = NULL; + pai->ai_addr = NULL; + pai->ai_next = NULL; + + if (hostname == NULL && servname == NULL) + return EAI_NONAME; + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + ERR(EAI_BADHINTS); /* xxx */ + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: +#ifdef INET6 + case PF_INET6: +#endif + break; + default: + ERR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + + /* + * if both socktype/protocol are specified, check if they + * are meaningful combination. + */ + if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { + for (ex = explore; ex->e_af >= 0; ex++) { + if (pai->ai_family != ex->e_af) + continue; + if (ex->e_socktype == ANY) + continue; + if (ex->e_protocol == ANY) + continue; + if (pai->ai_socktype == ex->e_socktype + && pai->ai_protocol != ex->e_protocol) { + ERR(EAI_BADHINTS); + } + } + } + } + + /* + * check for special cases. (1) numeric servname is disallowed if + * socktype/protocol are left unspecified. (2) servname is disallowed + * for raw and other inet{,6} sockets. + */ + if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) +#ifdef PF_INET6 + || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) +#endif + ) { + ai0 = *pai; /* backup *pai */ + + if (pai->ai_family == PF_UNSPEC) { +#ifdef PF_INET6 + pai->ai_family = PF_INET6; +#else + pai->ai_family = PF_INET; +#endif + } + error = get_portmatch(pai, servname, &svd); + if (error) + ERR(error); + + *pai = ai0; + } + + ai0 = *pai; + + /* NULL hostname, or numeric hostname */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + /* PF_UNSPEC entries are prepared for DNS queries only */ + if (ex->e_af == PF_UNSPEC) + continue; + + if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) + continue; + if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) + continue; + if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) + continue; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = ex->e_af; + if (pai->ai_socktype == ANY && ex->e_socktype != ANY) + pai->ai_socktype = ex->e_socktype; + if (pai->ai_protocol == ANY && ex->e_protocol != ANY) + pai->ai_protocol = ex->e_protocol; + + if (hostname == NULL) + error = explore_null(pai, servname, &cur->ai_next, + &svd); + else + error = explore_numeric_scope(pai, hostname, servname, + &cur->ai_next, &svd); + + if (error) + goto free; + + while (cur->ai_next) + cur = cur->ai_next; + } + + /* + * XXX + * If numeric representation of AF1 can be interpreted as FQDN + * representation of AF2, we need to think again about the code below. + */ + if (sentinel.ai_next) + goto good; + + if (hostname == NULL) + ERR(EAI_NODATA); + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + + /* + * hostname as alphabetical name. + * we would like to prefer AF_INET6 than AF_INET, so we'll make a + * outer loop by AFs. + */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + /* require exact match for family field */ + if (pai->ai_family != ex->e_af) + continue; + + if (!MATCH(pai->ai_socktype, ex->e_socktype, + WILD_SOCKTYPE(ex))) { + continue; + } + if (!MATCH(pai->ai_protocol, ex->e_protocol, + WILD_PROTOCOL(ex))) { + continue; + } + + if (pai->ai_socktype == ANY && ex->e_socktype != ANY) + pai->ai_socktype = ex->e_socktype; + if (pai->ai_protocol == ANY && ex->e_protocol != ANY) + pai->ai_protocol = ex->e_protocol; + + error = explore_fqdn(pai, hostname, servname, &cur->ai_next, + &svd); + + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + /* XXX */ + if (sentinel.ai_next) + error = 0; + + if (error) + goto free; + + if (sentinel.ai_next) { + good: + endservent_r(&svd); + *res = sentinel.ai_next; + return SUCCESS; + } else + error = EAI_FAIL; + free: + bad: + endservent_r(&svd); + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + *res = NULL; + return error; +} + +/* + * FQDN hostname, DNS lookup + */ +static int +explore_fqdn(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res, struct servent_data *svd) +{ + struct addrinfo *result; + struct addrinfo *cur; + int error = 0; + static const ns_dtab dtab[] = { + NS_FILES_CB(_files_getaddrinfo, NULL) + { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ + NS_NIS_CB(_yp_getaddrinfo, NULL) + NS_NULL_CB + }; + + _DIAGASSERT(pai != NULL); + /* hostname may be NULL */ + /* servname may be NULL */ + _DIAGASSERT(res != NULL); + + result = NULL; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname, svd) != 0) + return 0; + + switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", + default_dns_files, hostname, pai)) { + case NS_TRYAGAIN: + error = EAI_AGAIN; + goto free; + case NS_UNAVAIL: + error = EAI_FAIL; + goto free; + case NS_NOTFOUND: + error = EAI_NODATA; + goto free; + case NS_SUCCESS: + error = 0; + for (cur = result; cur; cur = cur->ai_next) { + GET_PORT(cur, servname, svd); + /* canonname should be filled already */ + } + break; + } + + *res = result; + + return 0; + +free: + if (result) + freeaddrinfo(result); + return error; +} + +/* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ +static int +explore_null(const struct addrinfo *pai, const char *servname, + struct addrinfo **res, struct servent_data *svd) +{ + int s; + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + + _DIAGASSERT(pai != NULL); + /* servname may be NULL */ + _DIAGASSERT(res != NULL); + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = socket(pai->ai_family, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname, svd) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, afd, afd->a_addrany); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + */ + GET_PORT(cur->ai_next, servname, svd); + } else { + GET_AI(cur->ai_next, afd, afd->a_loopback); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + GET_PORT(cur->ai_next, servname, svd); + } + cur = cur->ai_next; + + *res = sentinel.ai_next; + return 0; + +free: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * numeric hostname + */ +static int +explore_numeric(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res, const char *canonname, + struct servent_data *svd) +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + char pton[PTON_MAX]; + + _DIAGASSERT(pai != NULL); + /* hostname may be NULL */ + /* servname may be NULL */ + _DIAGASSERT(res != NULL); + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname, svd) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + switch (afd->a_af) { +#if 0 /*X/Open spec*/ + case AF_INET: + if (inet_aton(hostname, (struct in_addr *)pton) == 1) { + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname, svd); + if ((pai->ai_flags & AI_CANONNAME)) { + /* + * Set the numeric address itself as + * the canonical name, based on a + * clarification in rfc2553bis-03. + */ + GET_CANONNAME(cur->ai_next, canonname); + } + while (cur && cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /*xxx*/ + } + break; +#endif + default: + if (inet_pton(afd->a_af, hostname, pton) == 1) { + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname, svd); + if ((pai->ai_flags & AI_CANONNAME)) { + /* + * Set the numeric address itself as + * the canonical name, based on a + * clarification in rfc2553bis-03. + */ + GET_CANONNAME(cur->ai_next, canonname); + } + while (cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /*xxx*/ + } + break; + } + + *res = sentinel.ai_next; + return 0; + +free: +bad: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * numeric hostname with scope + */ +static int +explore_numeric_scope(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res, struct servent_data *svd) +{ +#if !defined(SCOPE_DELIMITER) || !defined(INET6) + return explore_numeric(pai, hostname, servname, res, hostname, svd); +#else + const struct afd *afd; + struct addrinfo *cur; + int error; + char *cp, *hostname2 = NULL, *scope, *addr; + struct sockaddr_in6 *sin6; + + _DIAGASSERT(pai != NULL); + /* hostname may be NULL */ + /* servname may be NULL */ + _DIAGASSERT(res != NULL); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname, svd) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (!afd->a_scoped) + return explore_numeric(pai, hostname, servname, res, hostname, + svd); + + cp = strchr(hostname, SCOPE_DELIMITER); + if (cp == NULL) + return explore_numeric(pai, hostname, servname, res, hostname, + svd); + + /* + * Handle special case of + */ + hostname2 = strdup(hostname); + if (hostname2 == NULL) + return EAI_MEMORY; + /* terminate at the delimiter */ + hostname2[cp - hostname] = '\0'; + addr = hostname2; + scope = cp + 1; + + error = explore_numeric(pai, addr, servname, res, hostname, svd); + if (error == 0) { + u_int32_t scopeid; + + for (cur = *res; cur; cur = cur->ai_next) { + if (cur->ai_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; + if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { + free(hostname2); + return(EAI_NODATA); /* XXX: is return OK? */ + } + sin6->sin6_scope_id = scopeid; + } + } + + free(hostname2); + + return error; +#endif +} + +static int +get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) +{ + + _DIAGASSERT(pai != NULL); + _DIAGASSERT(ai != NULL); + _DIAGASSERT(str != NULL); + + if ((pai->ai_flags & AI_CANONNAME) != 0) { + ai->ai_canonname = strdup(str); + if (ai->ai_canonname == NULL) + return EAI_MEMORY; + } + return 0; +} + +static struct addrinfo * +get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) +{ + char *p; + struct addrinfo *ai; + + _DIAGASSERT(pai != NULL); + _DIAGASSERT(afd != NULL); + _DIAGASSERT(addr != NULL); + + ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + + (afd->a_socklen)); + if (ai == NULL) + return NULL; + + memcpy(ai, pai, sizeof(struct addrinfo)); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memset(ai->ai_addr, 0, (size_t)afd->a_socklen); + ai->ai_addr->sa_len = (uint8_t)afd->a_socklen; + ai->ai_addrlen = afd->a_socklen; + ai->ai_family = afd->a_af; + ai->ai_addr->sa_family = (sa_family_t)ai->ai_family; + p = (char *)(void *)(ai->ai_addr); + memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); + return ai; +} + +static int +get_portmatch(const struct addrinfo *ai, const char *servname, + struct servent_data *svd) +{ + + _DIAGASSERT(ai != NULL); + /* servname may be NULL */ + + return get_port(ai, servname, 1, svd); +} + +static int +get_port(const struct addrinfo *ai, const char *servname, int matchonly, + struct servent_data *svd) +{ + const char *proto; + struct servent *sp; + int port; + int allownumeric; + + _DIAGASSERT(ai != NULL); + /* servname may be NULL */ + + if (servname == NULL) + return 0; + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + break; + default: + return 0; + } + + switch (ai->ai_socktype) { + case SOCK_RAW: + return EAI_SERVICE; + case SOCK_DGRAM: + case SOCK_STREAM: + allownumeric = 1; + break; + case ANY: + /* + * This was 0. It is now 1 so that queries specifying + * a NULL hint, or hint without socktype (but, hopefully, + * with protocol) and numeric address actually work. + */ + allownumeric = 1; + break; + default: + return EAI_SOCKTYPE; + } + + port = str2number(servname); + if (port >= 0) { + if (!allownumeric) + return EAI_SERVICE; + if (port < 0 || port > 65535) + return EAI_SERVICE; + port = htons(port); + } else { +// struct servent sv; + if (ai->ai_flags & AI_NUMERICSERV) + return EAI_NONAME; + + switch (ai->ai_socktype) { + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + default: + proto = NULL; + break; + } + +// sp = getservbyname_r(servname, proto, &sv, svd); + sp = getservbyname ( servname, proto ); + if (sp == NULL) + return EAI_SERVICE; + port = sp->s_port; + } + + if (!matchonly) { + switch (ai->ai_family) { + case AF_INET: + ((struct sockaddr_in *)(void *) + ai->ai_addr)->sin_port = (in_port_t)port; + break; +#ifdef INET6 + case AF_INET6: + ((struct sockaddr_in6 *)(void *) + ai->ai_addr)->sin6_port = (in_port_t)port; + break; +#endif + } + } + + return 0; +} + +static const struct afd * +find_afd(int af) +{ + const struct afd *afd; + + if (af == PF_UNSPEC) + return NULL; + for (afd = afdl; afd->a_af; afd++) { + if (afd->a_af == af) + return afd; + } + return NULL; +} + +#ifdef INET6 +/* convert a string to a scope identifier. XXX: IPv6 specific */ +static int +ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) +{ + u_long lscopeid; + struct in6_addr *a6; + char *ep; + + _DIAGASSERT(scope != NULL); + _DIAGASSERT(sin6 != NULL); + _DIAGASSERT(scopeid != NULL); + + a6 = &sin6->sin6_addr; + + /* empty scopeid portion is invalid */ + if (*scope == '\0') + return -1; + + if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { + /* + * We currently assume a one-to-one mapping between links + * and interfaces, so we simply use interface indices for + * like-local scopes. + */ +/* + *scopeid = if_nametoindex(scope); + if (*scopeid == 0) + goto trynumeric; + return 0; +*/ + return -1; + } + + /* still unclear about literal, allow numeric only - placeholder */ + if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) + goto trynumeric; + if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) + goto trynumeric; + else + goto trynumeric; /* global */ + + /* try to convert to a numeric id as a last resort */ + trynumeric: + errno = 0; + lscopeid = strtoul(scope, &ep, 10); + *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); + if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) + return 0; + else + return -1; +} +#endif + +/* code duplicate with gethnamaddr.c */ + +static const char AskedForGot[] = + "gethostby*.getanswer: asked for \"%s\", got \"%s\""; + +static struct addrinfo * +getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, + const struct addrinfo *pai) +{ + struct addrinfo sentinel, *cur; + struct addrinfo ai; + const struct afd *afd; + char *canonname; + const HEADER *hp; + const u_char *cp; + int n; + const u_char *eom; + char *bp, *ep; + int type, class, ancount, qdcount; + int haveanswer, had_error; + char tbuf[MAXDNAME]; + int (*name_ok) (const char *); + static char hostbuf[8*1024]; + + _DIAGASSERT(answer != NULL); + _DIAGASSERT(qname != NULL); + _DIAGASSERT(pai != NULL); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + canonname = NULL; + eom = answer->buf + anslen; + switch (qtype) { + case T_A: + case T_AAAA: + case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ + name_ok = res_hnok; + break; + default: + return NULL; /* XXX should be abort(); */ + } + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = hostbuf; + ep = hostbuf + sizeof hostbuf; + cp = answer->buf + HFIXEDSZ; + if (qdcount != 1) { + h_errno = NO_RECOVERY; + return (NULL); + } + n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp)); + if ((n < 0) || !(*name_ok)(bp)) { + h_errno = NO_RECOVERY; + return (NULL); + } + cp += n + QFIXEDSZ; + if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { + /* res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = (int)strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + h_errno = NO_RECOVERY; + return (NULL); + } + canonname = bp; + bp += n; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = canonname; + } + haveanswer = 0; + had_error = 0; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp)); + if ((n < 0) || !(*name_ok)(bp)) { + had_error++; + continue; + } + cp += n; /* name */ + type = _getshort(cp); + cp += INT16SZ; /* type */ + class = _getshort(cp); + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = _getshort(cp); + cp += INT16SZ; /* len */ + if (class != C_IN) { + /* XXX - debug? syslog? */ + cp += n; + continue; /* XXX - had_error++ ? */ + } + if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && + type == T_CNAME) { + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + if ((n < 0) || !(*name_ok)(tbuf)) { + had_error++; + continue; + } + cp += n; + /* Get canonical name. */ + n = (int)strlen(tbuf) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strlcpy(bp, tbuf, (size_t)(ep - bp)); + canonname = bp; + bp += n; + continue; + } + if (qtype == T_ANY) { + if (!(type == T_A || type == T_AAAA)) { + cp += n; + continue; + } + } else if (type != qtype) { + if (type != T_KEY && type != T_SIG) { +#ifdef _ORG_FREEBSD_ + struct syslog_data sd = SYSLOG_DATA_INIT; + syslog_r(LOG_NOTICE|LOG_AUTH, &sd, + "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", + qname, p_class(C_IN), p_type(qtype), + p_type(type)); +#endif + } + cp += n; + continue; /* XXX - had_error++ ? */ + } + switch (type) { + case T_A: + case T_AAAA: + if (strcasecmp(canonname, bp) != 0) { +#ifdef _ORG_FREEBSD_ + struct syslog_data sd = SYSLOG_DATA_INIT; + syslog_r(LOG_NOTICE|LOG_AUTH, &sd, + AskedForGot, canonname, bp); +#endif + cp += n; + continue; /* XXX - had_error++ ? */ + } + if (type == T_A && n != INADDRSZ) { + cp += n; + continue; + } + if (type == T_AAAA && n != IN6ADDRSZ) { + cp += n; + continue; + } + if (type == T_AAAA) { + struct in6_addr in6; + memcpy(&in6, cp, IN6ADDRSZ); + if (IN6_IS_ADDR_V4MAPPED(&in6)) { + cp += n; + continue; + } + } + if (!haveanswer) { + int nn; + + canonname = bp; + nn = (int)strlen(bp) + 1; /* for the \0 */ + bp += nn; + } + + /* don't overwrite pai */ + ai = *pai; + ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; + afd = find_afd(ai.ai_family); + if (afd == NULL) { + cp += n; + continue; + } + cur->ai_next = get_ai(&ai, afd, (const char *)cp); + if (cur->ai_next == NULL) + had_error++; + while (cur && cur->ai_next) + cur = cur->ai_next; + cp += n; + break; + default: + abort(); + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { + if (!canonname) + (void)get_canonname(pai, sentinel.ai_next, qname); + else + (void)get_canonname(pai, sentinel.ai_next, canonname); + h_errno = NETDB_SUCCESS; + return sentinel.ai_next; + } + + h_errno = NO_RECOVERY; + return NULL; +} + +#define SORTEDADDR(p) (((struct sockaddr_in *)(void *)(p->ai_next->ai_addr))->sin_addr.s_addr) +#define SORTMATCH(p, s) ((SORTEDADDR(p) & (s).mask) == (s).addr.s_addr) + +static void +aisort(struct addrinfo *s, res_state res) +{ + struct addrinfo head, *t, *p; + int i; + + head.ai_next = NULL; + t = &head; + + for (i = 0; i < (int)res->nsort; i++) { + p = s; + while (p->ai_next) { + if ((p->ai_next->ai_family != AF_INET) + || SORTMATCH(p, res->sort_list[i])) { + t->ai_next = p->ai_next; + t = t->ai_next; + p->ai_next = p->ai_next->ai_next; + } else { + p = p->ai_next; + } + } + } + + /* add rest of list and reset s to the new list*/ + t->ai_next = s->ai_next; + s->ai_next = head.ai_next; +} + +/*ARGSUSED*/ +static int +_dns_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + struct addrinfo *ai; + querybuf *buf, *buf2; + const char *name; + const struct addrinfo *pai; + struct addrinfo sentinel, *cur; + struct res_target q, q2; + res_state res; + + name = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&q, 0, sizeof(q)); + memset(&q2, 0, sizeof(q2)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + buf = malloc(sizeof(*buf)); + if (buf == NULL) { + h_errno = NETDB_INTERNAL; + return NS_NOTFOUND; + } + buf2 = malloc(sizeof(*buf2)); + if (buf2 == NULL) { + free(buf); + h_errno = NETDB_INTERNAL; + return NS_NOTFOUND; + } + + switch (pai->ai_family) { + case AF_UNSPEC: + /* prefer IPv6 */ + q.name = name; + q.qclass = C_IN; + q.qtype = T_AAAA; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + q.next = &q2; + q2.name = name; + q2.qclass = C_IN; + q2.qtype = T_A; + q2.answer = buf2->buf; + q2.anslen = sizeof(buf2->buf); + break; + case AF_INET: + q.name = name; + q.qclass = C_IN; + q.qtype = T_A; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + break; + case AF_INET6: + q.name = name; + q.qclass = C_IN; + q.qtype = T_AAAA; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + break; + default: + free(buf); + free(buf2); + return NS_UNAVAIL; + } + + res = __res_get_state(); + if (res == NULL) { + free(buf); + free(buf2); + return NS_NOTFOUND; + } + + if (res_searchN(name, &q, res) < 0) { + __res_put_state(res); + free(buf); + free(buf2); + return NS_NOTFOUND; + } + ai = getanswer(buf, q.n, q.name, q.qtype, pai); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + if (q.next) { + ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai); + if (ai) + cur->ai_next = ai; + } + free(buf); + free(buf2); + if (sentinel.ai_next == NULL) { + __res_put_state(res); + switch (h_errno) { + case HOST_NOT_FOUND: + return NS_NOTFOUND; + case TRY_AGAIN: + return NS_TRYAGAIN; + default: + return NS_UNAVAIL; + } + } + + if (res->nsort) + aisort(&sentinel, res); + + __res_put_state(res); + + *((struct addrinfo **)rv) = sentinel.ai_next; + return NS_SUCCESS; +} + +static void +_sethtent(FILE **hostf) +{ + + if (!*hostf) + *hostf = fopen(_PATH_HOSTS, "r" ); + else + rewind(*hostf); +} + +static void +_endhtent(FILE **hostf) +{ + + if (*hostf) { + (void) fclose(*hostf); + *hostf = NULL; + } +} + +static struct addrinfo * +_gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) +{ + char *p; + char *cp, *tname, *cname; + struct addrinfo hints, *res0, *res; + int error; + const char *addr; + static char hostbuf[8*1024]; + + _DIAGASSERT(name != NULL); + _DIAGASSERT(pai != NULL); + + if (!*hostf && ( NULL == (*hostf = fopen(_PATH_HOSTS, "r" )))) + return (NULL); + again: + if ( NULL == (p = fgets(hostbuf, sizeof hostbuf, *hostf))) + return (NULL); + if (*p == '#') + goto again; + if ( NULL == (cp = strpbrk(p, "#\n"))) + goto again; + *cp = '\0'; + if ( NULL == (cp = strpbrk(p, " \t"))) + goto again; + *cp++ = '\0'; + addr = p; + /* if this is not something we're looking for, skip it. */ + cname = NULL; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (!cname) + cname = cp; + tname = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + if (strcasecmp(name, tname) == 0) + goto found; + } + goto again; + +found: + hints = *pai; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(addr, NULL, &hints, &res0); + if (error) + goto again; + for (res = res0; res; res = res->ai_next) { + /* cover it up */ + res->ai_flags = pai->ai_flags; + + if (pai->ai_flags & AI_CANONNAME) { + if (get_canonname(pai, res, cname) != 0) { + freeaddrinfo(res0); + goto again; + } + } + } + return res0; +} + +/*ARGSUSED*/ +static int +_files_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + const char *name; + const struct addrinfo *pai; + struct addrinfo sentinel, *cur; + struct addrinfo *p; +#ifndef _REENTRANT + static +#endif + FILE *hostf = NULL; + + name = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + _sethtent(&hostf); + while ((p = _gethtent(&hostf, name, pai)) != NULL) { + cur->ai_next = p; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + _endhtent(&hostf); + + *((struct addrinfo **)rv) = sentinel.ai_next; + if (sentinel.ai_next == NULL) + return NS_NOTFOUND; + return NS_SUCCESS; +} + +#ifdef YP +/*ARGSUSED*/ +static struct addrinfo * +_yphostent(char *line, const struct addrinfo *pai) +{ + struct addrinfo sentinel, *cur; + struct addrinfo hints, *res, *res0; + int error; + char *p; + const char *addr, *canonname; + char *nextline; + char *cp; + + _DIAGASSERT(line != NULL); + _DIAGASSERT(pai != NULL); + + p = line; + addr = canonname = NULL; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + +nextline: + /* terminate line */ + cp = strchr(p, '\n'); + if (cp) { + *cp++ = '\0'; + nextline = cp; + } else + nextline = NULL; + + cp = strpbrk(p, " \t"); + if (cp == NULL) { + if (canonname == NULL) + return (NULL); + else + goto done; + } + *cp++ = '\0'; + + addr = p; + + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (!canonname) + canonname = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + } + + hints = *pai; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(addr, NULL, &hints, &res0); + if (error == 0) { + for (res = res0; res; res = res->ai_next) { + /* cover it up */ + res->ai_flags = pai->ai_flags; + + if (pai->ai_flags & AI_CANONNAME) + (void)get_canonname(pai, res, canonname); + } + } else + res0 = NULL; + if (res0) { + cur->ai_next = res0; + while (cur->ai_next) + cur = cur->ai_next; + } + + if (nextline) { + p = nextline; + goto nextline; + } + +done: + return sentinel.ai_next; +} + +/*ARGSUSED*/ +static int +_yp_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + struct addrinfo sentinel, *cur; + struct addrinfo *ai = NULL; + char *ypbuf; + int ypbuflen, r; + const char *name; + const struct addrinfo *pai; + char *ypdomain; + + if (_yp_check(&ypdomain) == 0) + return NS_UNAVAIL; + + name = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + /* hosts.byname is only for IPv4 (Solaris8) */ + if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { + r = yp_match(ypdomain, "hosts.byname", name, + (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + struct addrinfo ai4; + + ai4 = *pai; + ai4.ai_family = AF_INET; + ai = _yphostent(ypbuf, &ai4); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + } + free(ypbuf); + } + + /* ipnodes.byname can hold both IPv4/v6 */ + r = yp_match(ypdomain, "ipnodes.byname", name, + (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + ai = _yphostent(ypbuf, pai); + if (ai) + cur->ai_next = ai; + free(ypbuf); + } + + if (sentinel.ai_next == NULL) { + h_errno = HOST_NOT_FOUND; + return NS_NOTFOUND; + } + *((struct addrinfo **)rv) = sentinel.ai_next; + return NS_SUCCESS; +} +#endif + +/* resolver logic */ + +/* + * Formulate a normal query, send, and await answer. + * Returned answer is placed in supplied buffer "answer". + * Perform preliminary check of answer, returning success only + * if no error is indicated and the answer count is nonzero. + * Return the size of the response on success, -1 on error. + * Error number is left in h_errno. + * + * Caller must parse answer and determine whether it answers the question. + */ +static int +res_queryN(const char *name, /* domain name */ struct res_target *target, + res_state res) +{ + static u_char buf[MAXPACKET]; + HEADER *hp; + int n; + struct res_target *t; + int rcode; + int ancount; + + _DIAGASSERT(name != NULL); + /* XXX: target may be NULL??? */ + + rcode = NOERROR; + ancount = 0; + + for (t = target; t; t = t->next) { + int class, type; + u_char *answer; + int anslen; + + hp = (HEADER *)(void *)t->answer; + hp->rcode = NOERROR; /* default */ + + /* make it easier... */ + class = t->qclass; + type = t->qtype; + answer = t->answer; + anslen = t->anslen; +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_nquery(%s, %d, %d)\n", name, class, type); +#endif + + n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, + buf, sizeof(buf)); +#ifdef RES_USE_EDNS0 + if (n > 0 && (res->options & RES_USE_EDNS0) != 0) + n = res_nopt(res, n, buf, sizeof(buf), anslen); +#endif + if (n <= 0) { +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_nquery: mkquery failed\n"); +#endif + h_errno = NO_RECOVERY; + return n; + } + n = res_nsend(res, buf, n, answer, anslen); +#if 0 + if (n < 0) { +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query: send error\n"); +#endif + h_errno = TRY_AGAIN; + return n; + } +#endif + + if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; rcode = %u, ancount=%u\n", hp->rcode, + ntohs(hp->ancount)); +#endif + continue; + } + + ancount += ntohs(hp->ancount); + + t->n = n; + } + + if (ancount == 0) { + switch (rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + break; + } + return -1; + } + return ancount; +} + +/* + * Formulate a normal query, send, and retrieve answer in supplied buffer. + * Return the size of the response on success, -1 on error. + * If enabled, implement search rules until answer or unrecoverable failure + * is detected. Error code, if any, is left in h_errno. + */ +static int +res_searchN(const char *name, struct res_target *target, res_state res) +{ + const char *cp, * const *domain; + HEADER *hp; + u_int dots; + int trailing_dot, ret, saved_herrno; + int got_nodata = 0, got_servfail = 0, tried_as_is = 0; + + _DIAGASSERT(name != NULL); + _DIAGASSERT(target != NULL); + + hp = (HEADER *)(void *)target->answer; /*XXX*/ + + errno = 0; + h_errno = HOST_NOT_FOUND; /* default, if we never query */ + dots = 0; + for (cp = name; *cp; cp++) + dots += (*cp == '.'); + trailing_dot = 0; + if (cp > name && *--cp == '.') + trailing_dot++; + + /* + * if there aren't any dots, it could be a user-level alias + */ + if (!dots && (cp = __hostalias(name)) != NULL) { + ret = res_queryN(cp, target, res); + return ret; + } + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + saved_herrno = -1; + if (dots >= res->ndots) { + ret = res_querydomainN(name, NULL, target, res); + if (ret > 0) + return (ret); + saved_herrno = h_errno; + tried_as_is++; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (res->options & RES_DEFNAMES)) || + (dots && !trailing_dot && (res->options & RES_DNSRCH))) { + int done = 0; + + for (domain = (const char * const *)res->dnsrch; + *domain && !done; + domain++) { + + ret = res_querydomainN(name, *domain, target, res); + if (ret > 0) + return ret; + + /* + * If no server present, give up. + * If name isn't found in this domain, + * keep trying higher domains in the search list + * (if that's enabled). + * On a NO_DATA error, keep trying, otherwise + * a wildcard entry of another type could keep us + * from finding this entry higher in the domain. + * If we get some other error (negative answer or + * server failure), then stop searching up, + * but try the input name below in case it's + * fully-qualified. + */ + if (errno == ECONNREFUSED) { + h_errno = TRY_AGAIN; + return -1; + } + + switch (h_errno) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) { + /* try next search element, if any */ + got_servfail++; + break; + } + /* FALLTHROUGH */ + default: + /* anything else implies that we're done */ + done++; + } + /* + * if we got here for some reason other than DNSRCH, + * we only wanted one iteration of the loop, so stop. + */ + if (!(res->options & RES_DNSRCH)) + done++; + } + } + + /* + * if we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!tried_as_is) { + ret = res_querydomainN(name, NULL, target, res); + if (ret > 0) + return ret; + } + + /* + * if we got here, we didn't satisfy the search. + * if we did an initial full query, return that query's h_errno + * (note that we wouldn't be here if that query had succeeded). + * else if we ever got a nodata, send that back as the reason. + * else send back meaningless h_errno, that being the one from + * the last DNSRCH we did. + */ + if (saved_herrno != -1) + h_errno = saved_herrno; + else if (got_nodata) + h_errno = NO_DATA; + else if (got_servfail) + h_errno = TRY_AGAIN; + return -1; +} + +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +static int +res_querydomainN(const char *name, const char *domain, + struct res_target *target, res_state res) +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; + + _DIAGASSERT(name != NULL); + /* XXX: target may be NULL??? */ + +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_querydomain(%s, %s)\n", + name, domain?domain:""); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name); + if (n + 1 > sizeof(nbuf)) { + h_errno = NO_RECOVERY; + return -1; + } + if (n > 0 && name[--n] == '.') { + strncpy(nbuf, name, n); + nbuf[n] = '\0'; + } else + longname = name; + } else { + n = strlen(name); + d = strlen(domain); + if (n + 1 + d + 1 > sizeof(nbuf)) { + h_errno = NO_RECOVERY; + return -1; + } + snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); + } + return res_queryN(longname, target, res); +} diff --git a/StdLib/BsdSocketLib/getnameinfo.c b/StdLib/BsdSocketLib/getnameinfo.c new file mode 100644 index 0000000000..fab3460dc0 --- /dev/null +++ b/StdLib/BsdSocketLib/getnameinfo.c @@ -0,0 +1,567 @@ +/* $NetBSD: getnameinfo.c,v 1.45 2006/10/15 16:14:46 christos Exp $ */ +/* $KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $ */ + +/* + * Copyright (c) 2000 Ben Harris. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS 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 PROJECT OR 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. + */ + +/* + * Issues to be discussed: + * - Thread safe-ness must be checked + * - RFC2553 says that we should raise error on short buffer. X/Open says + * we need to truncate the result. We obey RFC2553 (and X/Open should be + * modified). ipngwg rough consensus seems to follow RFC2553. + * - What is "local" in NI_FQDN? + * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. + * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if + * sin6_scope_id is filled - standardization status? + * XXX breaks backward compat for code that expects no scopeid. + * beware on merge. + */ + +#define INET6 1 + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getnameinfo.c,v 1.45 2006/10/15 16:14:46 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CLLADDR(x) ( LLADDR(x) ) +#define endservent_r(svd) endservent() +#define getservbyport_r(Port,pProto,pSv,pSvd) getservbyport(Port,pProto) + +#ifdef __weak_alias +__weak_alias(getnameinfo,_getnameinfo) +#endif + +static +int +hexname( + const u_int8_t * cp, + size_t len, + char * host, + socklen_t hostlen + ); + +static const struct afd { + int a_af; + socklen_t a_addrlen; + socklen_t a_socklen; + int a_off; +} afdl [] = { +#ifdef INET6 + {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr)}, +#endif + {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr)}, + {0, 0, 0, 0}, +}; + +struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; +}; + +static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *, + socklen_t, char *, socklen_t, int)); +#ifdef INET6 +static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, + socklen_t, int)); +static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, + int)); +#endif +static int getnameinfo_link __P((const struct sockaddr *, socklen_t, char *, + socklen_t, char *, socklen_t, int)); +static int hexname __P((const u_int8_t *, size_t, char *, socklen_t)); + +/* + * Top-level getnameinfo() code. Look at the address family, and pick an + * appropriate function to call. + */ +int +getnameinfo( + const struct sockaddr * sa, + socklen_t salen, + char * host, + socklen_t hostlen, + char * serv, + socklen_t servlen, + int flags + ) +{ + + switch (sa->sa_family) { + case AF_INET: + case AF_INET6: + return getnameinfo_inet(sa, salen, host, hostlen, + serv, servlen, flags); + case AF_LINK: + return getnameinfo_link(sa, salen, host, hostlen, + serv, servlen, flags); + default: + return EAI_FAMILY; + } +} + + +/* + * getnameinfo_inet(): + * Format an IPv4 or IPv6 sockaddr into a printable string. + */ +static +int +getnameinfo_inet( + const struct sockaddr * sa, + socklen_t salen, + char * host, + socklen_t hostlen, + char * serv, + socklen_t servlen, + int flags + ) +{ + const struct afd *afd; + struct servent *sp; + struct hostent *hp; + u_short port; + int family, i; + const char *addr; + u_int32_t v4a; + char numserv[512]; + char numaddr[512]; + + /* sa is checked below */ + /* host may be NULL */ + /* serv may be NULL */ + + if (sa == NULL) + return EAI_FAIL; + +#ifdef BSD4_4 + if (sa->sa_len != salen) + return EAI_FAIL; +#endif + + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) + if (afdl[i].a_af == family) { + afd = &afdl[i]; + goto found; + } + return EAI_FAMILY; + + found: + if (salen != afd->a_socklen) + return EAI_FAIL; + + /* network byte order */ + port = ((const struct sockinet *)(const void *)sa)->si_port; + addr = (const char *)(const void *)sa + afd->a_off; + + if (serv == NULL || servlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: rfc2553bis-03 says that serv == NULL OR + * servlen == 0 means that the caller does not want the result. + */ + } else { + if (flags & NI_NUMERICSERV) + sp = NULL; + else { + struct servent_data svd; +// struct servent sv; + + (void)memset(&svd, 0, sizeof(svd)); + sp = getservbyport_r(port, + (flags & NI_DGRAM) ? "udp" : "tcp", &sv, &svd); + endservent_r(&svd); + } + if (sp) { + if (strlen(sp->s_name) + 1 > servlen) + return EAI_MEMORY; + strlcpy(serv, sp->s_name, servlen); + } else { + snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); + if (strlen(numserv) + 1 > servlen) + return EAI_MEMORY; + strlcpy(serv, numserv, servlen); + } + } + + switch (sa->sa_family) { + case AF_INET: + v4a = (u_int32_t) + ntohl(((const struct sockaddr_in *) + (const void *)sa)->sin_addr.s_addr); + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + flags |= NI_NUMERICHOST; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0) + flags |= NI_NUMERICHOST; + break; +#ifdef INET6 + case AF_INET6: + { + const struct sockaddr_in6 *sin6; + sin6 = (const struct sockaddr_in6 *)(const void *)sa; + switch (sin6->sin6_addr.s6_addr[0]) { + case 0x00: + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + ; + else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + ; + else + flags |= NI_NUMERICHOST; + break; + default: + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + flags |= NI_NUMERICHOST; + } + else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; + break; + } + } + break; +#endif + } + if (host == NULL || hostlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: rfc2553bis-03 says that host == NULL or + * hostlen == 0 means that the caller does not want the result. + */ + } else if (flags & NI_NUMERICHOST) { + size_t numaddrlen; + + /* NUMERICHOST and NAMEREQD conflicts with each other */ + if (flags & NI_NAMEREQD) + return EAI_NONAME; + + switch(afd->a_af) { +#ifdef INET6 + case AF_INET6: + { + int error; + + if ((error = ip6_parsenumeric(sa, addr, host, + hostlen, flags)) != 0) + return(error); + break; + } +#endif + default: + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return EAI_SYSTEM; + numaddrlen = strlen(numaddr); + if (numaddrlen + 1 > hostlen) /* don't forget terminator */ + return EAI_MEMORY; + strlcpy(host, numaddr, hostlen); + break; + } + } else { + hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); + + if (hp) { +#if 0 + /* + * commented out, since "for local host" is not + * implemented here - see RFC2553 p30 + */ + if (flags & NI_NOFQDN) { + char *p; + p = strchr(hp->h_name, '.'); + if (p) + *p = '\0'; + } +#endif + if (strlen(hp->h_name) + 1 > hostlen) { + return EAI_MEMORY; + } + strlcpy(host, hp->h_name, hostlen); + } else { + if (flags & NI_NAMEREQD) + return EAI_NONAME; + switch(afd->a_af) { +#ifdef INET6 + case AF_INET6: + { + int error; + + if ((error = ip6_parsenumeric(sa, addr, host, + hostlen, + flags)) != 0) + return(error); + break; + } +#endif + default: + if (inet_ntop(afd->a_af, addr, host, + hostlen) == NULL) + return EAI_SYSTEM; + break; + } + } + } + return(0); +} + +#ifdef INET6 +static int +ip6_parsenumeric( + const struct sockaddr *sa, + const char *addr, + char *host, + socklen_t hostlen, + int flags + ) +{ + size_t numaddrlen; + char numaddr[512]; + + _DIAGASSERT(sa != NULL); + _DIAGASSERT(addr != NULL); + _DIAGASSERT(host != NULL); + + if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) + return EAI_SYSTEM; + + numaddrlen = strlen(numaddr); + if (numaddrlen + 1 > hostlen) /* don't forget terminator */ + return EAI_OVERFLOW; + strlcpy(host, numaddr, hostlen); + + if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) { + char zonebuf[MAXHOSTNAMELEN]; + int zonelen; + + zonelen = ip6_sa2str( + (const struct sockaddr_in6 *)(const void *)sa, + zonebuf, sizeof(zonebuf), flags); + if (zonelen < 0) + return EAI_OVERFLOW; + if ((size_t) zonelen + 1 + numaddrlen + 1 > hostlen) + return EAI_OVERFLOW; + /* construct */ + memcpy(host + numaddrlen + 1, zonebuf, + (size_t)zonelen); + host[numaddrlen] = SCOPE_DELIMITER; + host[numaddrlen + 1 + zonelen] = '\0'; + } + + return 0; +} + +/* ARGSUSED */ +static int +ip6_sa2str( + const struct sockaddr_in6 *sa6, + char *buf, + size_t bufsiz, + int flags + ) +{ + unsigned int ifindex; + const struct in6_addr *a6; + int n; + + _DIAGASSERT(sa6 != NULL); + _DIAGASSERT(buf != NULL); + + ifindex = (unsigned int)sa6->sin6_scope_id; + a6 = &sa6->sin6_addr; + +#ifdef NI_NUMERICSCOPE + if ((flags & NI_NUMERICSCOPE) != 0) { + n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); + if ((n < 0) || ((size_t)n >= bufsiz)) + return -1; + else + return n; + } +#endif + +#if 0 + /* if_indextoname() does not take buffer size. not a good api... */ + if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && + bufsiz >= IF_NAMESIZE) { + char *p = if_indextoname(ifindex, buf); + if (p) { + return(strlen(p)); + } + } +#endif // 0 + + /* last resort */ + n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); + if (n < 0 || (size_t) n >= bufsiz) + return -1; + else + return n; +} +#endif /* INET6 */ + + +/* + * getnameinfo_link(): + * Format a link-layer address into a printable format, paying attention to + * the interface type. + */ +/* ARGSUSED */ +static +int +getnameinfo_link ( + const struct sockaddr * sa, + socklen_t salen, + char * host, + socklen_t hostlen, + char * serv, + socklen_t servlen, + int flags + ) +{ + const struct sockaddr_dl *sdl = + (const struct sockaddr_dl *)(const void *)sa; +// const struct ieee1394_hwaddr *iha; + int n; + + if (serv != NULL && servlen > 0) + *serv = '\0'; + + if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { + n = snprintf(host, hostlen, "link#%u", sdl->sdl_index); + if (n < 0 || (socklen_t) n > hostlen) { + *host = '\0'; + return EAI_MEMORY; + } + return 0; + } + +#if 0 + switch (sdl->sdl_type) { +#ifdef IFT_ECONET + case IFT_ECONET: + if (sdl->sdl_alen < 2) + return EAI_FAMILY; + if (CLLADDR(sdl)[1] == 0) + n = snprintf(host, hostlen, "%u", CLLADDR(sdl)[0]); + else + n = snprintf(host, hostlen, "%u.%u", + CLLADDR(sdl)[1], CLLADDR(sdl)[0]); + if (n < 0 || (socklen_t) n >= hostlen) { + *host = '\0'; + return EAI_MEMORY; + } else + return 0; +#endif + case IFT_IEEE1394: + if (sdl->sdl_alen < sizeof(iha->iha_uid)) + return EAI_FAMILY; + iha = + (const struct ieee1394_hwaddr *)(const void *)CLLADDR(sdl); + return hexname(iha->iha_uid, sizeof(iha->iha_uid), + host, hostlen); + /* + * The following have zero-length addresses. + * IFT_ATM (net/if_atmsubr.c) + * IFT_FAITH (net/if_faith.c) + * IFT_GIF (net/if_gif.c) + * IFT_LOOP (net/if_loop.c) + * IFT_PPP (net/if_ppp.c, net/if_spppsubr.c) + * IFT_SLIP (net/if_sl.c, net/if_strip.c) + * IFT_STF (net/if_stf.c) + * IFT_L2VLAN (net/if_vlan.c) + * IFT_PROPVIRTUAL (net/if_bridge.h> + */ + /* + * The following use IPv4 addresses as link-layer addresses: + * IFT_OTHER (net/if_gre.c) + */ + case IFT_ARCNET: /* default below is believed correct for all these. */ + case IFT_ETHER: + case IFT_FDDI: + case IFT_HIPPI: + case IFT_ISO88025: + default: +#endif // 0 + return hexname((const u_int8_t *)CLLADDR(sdl), + (size_t)sdl->sdl_alen, host, hostlen); +// } +} + +static +int +hexname( + const u_int8_t * cp, + size_t len, + char * host, + socklen_t hostlen + ) +{ + int n; + size_t i; + char *outp = host; + + *outp = '\0'; + for (i = 0; i < len; i++) { + n = snprintf(outp, hostlen, "%s%02x", + i ? ":" : "", cp[i]); + if (n < 0 || (socklen_t) n >= hostlen) { + *host = '\0'; + return EAI_MEMORY; + } + outp += n; + hostlen -= n; + } + return 0; +} diff --git a/StdLib/EfiSocketLib/EfiSocketLib.inf b/StdLib/EfiSocketLib/EfiSocketLib.inf index df639527f6..5e6ff595a9 100644 --- a/StdLib/EfiSocketLib/EfiSocketLib.inf +++ b/StdLib/EfiSocketLib/EfiSocketLib.inf @@ -34,7 +34,9 @@ Service.c Socket.c Tcp4.c + Tcp6.c Udp4.c + Udp6.c UseEfiSocketLib.c [Packages] @@ -52,9 +54,15 @@ [Protocols] gEfiIp4ProtocolGuid gEfiIp4ServiceBindingProtocolGuid + gEfiIp6ProtocolGuid + gEfiIp6ServiceBindingProtocolGuid gEfiTcp4ProtocolGuid gEfiTcp4ServiceBindingProtocolGuid + gEfiTcp6ProtocolGuid + gEfiTcp6ServiceBindingProtocolGuid gEfiUdp4ProtocolGuid gEfiUdp4ServiceBindingProtocolGuid + gEfiUdp6ProtocolGuid + gEfiUdp6ServiceBindingProtocolGuid gEfiSocketProtocolGuid gEfiSocketServiceBindingProtocolGuid diff --git a/StdLib/EfiSocketLib/Ip4.c b/StdLib/EfiSocketLib/Ip4.c index 9f885e96bf..f295b4250e 100644 --- a/StdLib/EfiSocketLib/Ip4.c +++ b/StdLib/EfiSocketLib/Ip4.c @@ -383,6 +383,7 @@ EslIp4PortAllocate ( // pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.IPv4->Configure; pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Cancel; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.IPv4->Poll; pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Receive; pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.IPv4->Transmit; diff --git a/StdLib/EfiSocketLib/Socket.c b/StdLib/EfiSocketLib/Socket.c index 9c2d2f6844..1805fa629c 100644 --- a/StdLib/EfiSocketLib/Socket.c +++ b/StdLib/EfiSocketLib/Socket.c @@ -481,6 +481,14 @@ CONST ESL_SOCKET_BINDING cEslSocketBinding[] = { 4, // RX buffers 4, // TX buffers 4 }, // TX Oob buffers + { L"Tcp6", + &gEfiTcp6ServiceBindingProtocolGuid, + &gEfiTcp6ProtocolGuid, + &mEslTcp6ServiceGuid, + OFFSET_OF ( ESL_LAYER, pTcp6List ), + 4, // RX buffers + 4, // TX buffers + 4 }, // TX Oob buffers { L"Udp4", &gEfiUdp4ServiceBindingProtocolGuid, &gEfiUdp4ProtocolGuid, @@ -488,6 +496,14 @@ CONST ESL_SOCKET_BINDING cEslSocketBinding[] = { OFFSET_OF ( ESL_LAYER, pUdp4List ), 4, // RX buffers 4, // TX buffers + 0 }, // TX Oob buffers + { L"Udp6", + &gEfiUdp6ServiceBindingProtocolGuid, + &gEfiUdp6ProtocolGuid, + &mEslUdp6ServiceGuid, + OFFSET_OF ( ESL_LAYER, pUdp6List ), + 4, // RX buffers + 4, // TX buffers 0 } // TX Oob buffers }; @@ -516,11 +532,11 @@ CONST int cEslAfInetApiSize = DIM ( cEslAfInetApi ); **/ CONST ESL_PROTOCOL_API * cEslAfInet6Api[] = { NULL, // 0 - NULL, // SOCK_STREAM - NULL, // SOCK_DGRAM + &cEslTcp6Api, // SOCK_STREAM + &cEslUdp6Api, // SOCK_DGRAM NULL, // SOCK_RAW NULL, // SOCK_RDM - NULL // SOCK_SEQPACKET + &cEslTcp6Api // SOCK_SEQPACKET }; /** @@ -603,6 +619,7 @@ EslSocket ( // Validate the domain value // if (( AF_INET != domain ) + && ( AF_INET6 != domain ) && ( AF_LOCAL != domain )) { DEBUG (( DEBUG_ERROR | DEBUG_SOCKET, "ERROR - Invalid domain value\r\n" )); @@ -1789,6 +1806,11 @@ EslSocketConnect ( if ( EFI_NOT_READY != Status ) { if ( !EFI_ERROR ( Status )) { pSocket->State = SOCKET_STATE_CONNECTED; + + // + // Start the receive operations + // + EslSocketRxStart ( pSocket->pPortList ); } else { pSocket->State = SOCKET_STATE_BOUND; @@ -1980,7 +2002,8 @@ EslSocketGetLocalAddress ( // // Verify the socket state // - if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + if (( SOCKET_STATE_CONNECTED == pSocket->State ) + || ( SOCKET_STATE_LISTENING == pSocket->State )) { // // Verify the API // @@ -3096,7 +3119,7 @@ EslSocketPacketAllocate ( LengthInBytes, (VOID **)&pPacket ); if ( !EFI_ERROR ( Status )) { - DEBUG (( DebugFlags | DEBUG_POOL | DEBUG_INIT, + DEBUG (( DebugFlags | DEBUG_POOL, "0x%08x: Allocate pPacket, %d bytes\r\n", pPacket, LengthInBytes )); @@ -3210,6 +3233,7 @@ EslSocketPoll ( short DetectedEvents; ESL_SOCKET * pSocket; EFI_STATUS Status; + EFI_TPL TplPrevious; short ValidEvents; DEBUG (( DEBUG_POLL, "Entering SocketPoll\r\n" )); @@ -3247,6 +3271,22 @@ EslSocketPoll ( Events & ( ~ValidEvents ))); } else { + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Increase the network performance by extending the + // polling (idle) loop down into the LAN driver + // + EslSocketRxPoll ( pSocket ); + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + // // Check for pending connections // @@ -4366,6 +4406,11 @@ EslSocketReceive ( // Verify that the socket is connected // if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Poll the network to increase performance + // + EslSocketRxPoll ( pSocket ); + // // Locate the port // @@ -4847,6 +4892,49 @@ EslSocketRxComplete ( } +/** + Poll a socket for pending receive activity. + + This routine is called at elivated TPL and extends the idle + loop which polls a socket down into the LAN driver layer to + determine if there is any receive activity. + + The ::EslSocketPoll, ::EslSocketReceive and ::EslSocketTransmit + routines call this routine when there is nothing to do. + + @param [in] pSocket Address of an ::EFI_SOCKET structure. + + **/ +VOID +EslSocketRxPoll ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pPort; + + DEBUG (( DEBUG_POLL, "Entering EslSocketRxPoll\r\n" )); + + // + // Increase the network performance by extending the + // polling (idle) loop down into the LAN driver + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Poll the LAN adapter + // + pPort->pfnRxPoll ( pPort->pProtocol.v ); + + // + // Locate the next LAN adapter + // + pPort = pPort->pLinkSocket; + } + + DEBUG (( DEBUG_POLL, "Exiting EslSocketRxPoll\r\n" )); +} + + /** Start a receive operation @@ -5291,6 +5379,11 @@ EslSocketTransmit ( // RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + // + // Poll the network to increase performance + // + EslSocketRxPoll ( pSocket ); + // // Attempt to buffer the packet for transmission // diff --git a/StdLib/EfiSocketLib/Socket.h b/StdLib/EfiSocketLib/Socket.h index 50bb2d3efd..30b642000e 100644 --- a/StdLib/EfiSocketLib/Socket.h +++ b/StdLib/EfiSocketLib/Socket.h @@ -137,6 +137,26 @@ typedef struct } ESL_TCP4_TX_DATA; +/** + Receive context for SOCK_STREAM and SOCK_SEQPACKET sockets using TCPv6. +**/ +typedef struct +{ + EFI_TCP6_RECEIVE_DATA RxData; ///< Receive operation description + UINT8 Buffer[ RX_PACKET_DATA ]; ///< Data buffer +} ESL_TCP6_RX_DATA; + + +/** + Transmit context for SOCK_STREAM and SOCK_SEQPACKET sockets using TCPv6. +**/ +typedef struct +{ + EFI_TCP6_TRANSMIT_DATA TxData; ///< Transmit operation description + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_TCP6_TX_DATA; + + /** Receive context for SOCK_DGRAM sockets using UDPv4. **/ @@ -159,6 +179,28 @@ typedef struct } ESL_UDP4_TX_DATA; +/** + Receive context for SOCK_DGRAM sockets using UDPv6. +**/ +typedef struct +{ + EFI_UDP6_SESSION_DATA Session; ///< Remote network address + EFI_UDP6_RECEIVE_DATA * pRxData; ///< Receive operation description +} ESL_UDP6_RX_DATA; + + +/** + Transmit context for SOCK_DGRAM sockets using UDPv6. +**/ +typedef struct +{ + EFI_UDP6_SESSION_DATA Session; ///< Remote network address + EFI_UDP6_TRANSMIT_DATA TxData; ///< Transmit operation description + UINTN RetransmitCount; ///< Retransmit to handle ARP negotiation + UINT8 Buffer[ 1 ]; ///< Data buffer +} ESL_UDP6_TX_DATA; + + /** Network specific context for transmit and receive packets. **/ @@ -172,8 +214,12 @@ typedef struct _ESL_PACKET { ESL_IP4_TX_DATA Ip4Tx; ///< Transmit operation description ESL_TCP4_RX_DATA Tcp4Rx; ///< Receive operation description ESL_TCP4_TX_DATA Tcp4Tx; ///< Transmit operation description + ESL_TCP6_RX_DATA Tcp6Rx; ///< Receive operation description + ESL_TCP6_TX_DATA Tcp6Tx; ///< Transmit operation description ESL_UDP4_RX_DATA Udp4Rx; ///< Receive operation description ESL_UDP4_TX_DATA Udp4Tx; ///< Transmit operation description + ESL_UDP6_RX_DATA Udp6Rx; ///< Receive operation description + ESL_UDP6_TX_DATA Udp6Tx; ///< Transmit operation description } Op; ///< Network specific context } GCC_ESL_PACKET; @@ -217,8 +263,12 @@ typedef struct _ESL_IO_MGMT { EFI_IP4_COMPLETION_TOKEN Ip4Tx; ///< IP4 transmit token EFI_TCP4_IO_TOKEN Tcp4Rx; ///< TCP4 receive token EFI_TCP4_IO_TOKEN Tcp4Tx; ///< TCP4 transmit token + EFI_TCP6_IO_TOKEN Tcp6Rx; ///< TCP6 receive token + EFI_TCP6_IO_TOKEN Tcp6Tx; ///< TCP6 transmit token EFI_UDP4_COMPLETION_TOKEN Udp4Rx; ///< UDP4 receive token EFI_UDP4_COMPLETION_TOKEN Udp4Tx; ///< UDP4 transmit token + EFI_UDP6_COMPLETION_TOKEN Udp6Rx; ///< UDP6 receive token + EFI_UDP6_COMPLETION_TOKEN Udp6Tx; ///< UDP6 transmit token } Token; ///< Completion token for the network operation } GCC_IO_MGMT; @@ -256,6 +306,26 @@ typedef struct { EFI_TCP4_CLOSE_TOKEN CloseToken; ///< Close control } ESL_TCP4_CONTEXT; +/** + TCP6 context structure + + The driver uses this structure to manage the TCP6 connections. +**/ +typedef struct { + // + // TCP6 context + // + EFI_TCP6_CONFIG_DATA ConfigData; ///< TCP6 configuration data + EFI_TCP6_OPTION Option; ///< TCP6 port options + + // + // Tokens + // + EFI_TCP6_LISTEN_TOKEN ListenToken; ///< Listen control + EFI_TCP6_CONNECTION_TOKEN ConnectToken; ///< Connection control + EFI_TCP6_CLOSE_TOKEN CloseToken; ///< Close control +} ESL_TCP6_CONTEXT; + /** UDP4 context structure @@ -268,6 +338,18 @@ typedef struct { EFI_UDP4_CONFIG_DATA ConfigData; ///< UDP4 configuration data } ESL_UDP4_CONTEXT; +/** + UDP6 context structure + + The driver uses this structure to manage the UDP6 connections. +**/ +typedef struct { + // + // UDP6 context + // + EFI_UDP6_CONFIG_DATA ConfigData; ///< UDP6 configuration data +} ESL_UDP6_CONTEXT; + /** Configure the network layer. @@ -301,6 +383,21 @@ EFI_STATUS IN VOID * pToken ); +/** + Poll the LAN adapter for receive packets. + + @param [in] pProtocol Protocol structure address + @param [in] pToken Completion token address + + @return Returns EFI_SUCCESS if the operation is successfully + started. +**/ +typedef +EFI_STATUS +(* PFN_NET_POLL) ( + IN VOID * pProtocol + ); + /** Port control structure @@ -353,6 +450,7 @@ typedef struct _ESL_PORT { // Receive data management // PFN_NET_IO_START pfnRxCancel; ///< Cancel a receive on the network + PFN_NET_POLL pfnRxPoll; ///< Poll the LAN adapter for receive packets PFN_NET_IO_START pfnRxStart; ///< Start a receive on the network ESL_IO_MGMT * pRxActive; ///< Active receive operation queue ESL_IO_MGMT * pRxFree; ///< Free structure queue @@ -364,12 +462,16 @@ typedef struct _ESL_PORT { VOID * v; ///< VOID pointer EFI_IP4_PROTOCOL * IPv4; ///< IP4 protocol pointer EFI_TCP4_PROTOCOL * TCPv4; ///< TCP4 protocol pointer + EFI_TCP6_PROTOCOL * TCPv6; ///< TCP6 protocol pointer EFI_UDP4_PROTOCOL * UDPv4; ///< UDP4 protocol pointer + EFI_UDP6_PROTOCOL * UDPv6; ///< UDP6 protocol pointer } pProtocol; ///< Protocol structure address union { ESL_IP4_CONTEXT Ip4; ///< IPv4 management data ESL_TCP4_CONTEXT Tcp4; ///< TCPv4 management data + ESL_TCP6_CONTEXT Tcp6; ///< TCPv6 management data ESL_UDP4_CONTEXT Udp4; ///< UDPv4 management data + ESL_UDP6_CONTEXT Udp6; ///< UDPv6 management data } Context; ///< Network specific context }GCC_ESL_PORT; @@ -975,7 +1077,9 @@ typedef struct { // ESL_SERVICE * pIp4List; ///< List of Ip4 services ESL_SERVICE * pTcp4List; ///< List of Tcp4 services + ESL_SERVICE * pTcp6List; ///< List of Tcp6 services ESL_SERVICE * pUdp4List; ///< List of Udp4 services + ESL_SERVICE * pUdp6List; ///< List of Udp6 services // // Socket management @@ -992,8 +1096,11 @@ typedef struct { extern ESL_LAYER mEslLayer; extern CONST ESL_PROTOCOL_API cEslIp4Api; +extern CONST ESL_PROTOCOL_API cEslIp6Api; extern CONST ESL_PROTOCOL_API cEslTcp4Api; +extern CONST ESL_PROTOCOL_API cEslTcp6Api; extern CONST ESL_PROTOCOL_API cEslUdp4Api; +extern CONST ESL_PROTOCOL_API cEslUdp6Api; extern CONST EFI_SERVICE_BINDING_PROTOCOL mEfiServiceBinding; @@ -1423,6 +1530,24 @@ EslSocketRxComplete ( IN BOOLEAN bUrgent ); +/** + Poll a socket for pending receive activity. + + This routine is called at elivated TPL and extends the idle + loop which polls a socket down into the LAN driver layer to + determine if there is any receive activity. + + The ::EslSocketPoll, ::EslSocketReceive and ::EslSocketTransmit + routines call this routine when there is nothing to do. + + @param [in] pSocket Address of an ::EFI_SOCKET structure. + + **/ +VOID +EslSocketRxPoll ( + IN ESL_SOCKET * pSocket + ); + /** Start a receive operation diff --git a/StdLib/EfiSocketLib/Tcp4.c b/StdLib/EfiSocketLib/Tcp4.c index ec03b96bc1..b3ed45a716 100644 --- a/StdLib/EfiSocketLib/Tcp4.c +++ b/StdLib/EfiSocketLib/Tcp4.c @@ -1307,6 +1307,7 @@ EslTcp4PortAllocate ( // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED // pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv4->Configure; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv4->Poll; pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Receive; pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Transmit; diff --git a/StdLib/EfiSocketLib/Tcp6.c b/StdLib/EfiSocketLib/Tcp6.c new file mode 100644 index 0000000000..0ee9fb6a64 --- /dev/null +++ b/StdLib/EfiSocketLib/Tcp6.c @@ -0,0 +1,2329 @@ +/** @file + Implement the TCP6 driver support for the socket layer. + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + + \section ConnectionManagement Connection Management + + The ::EslTcp6Listen routine initially places the SOCK_STREAM or + SOCK_SEQPACKET socket into a listen state. When a remote machine + makes a connection to the socket, the TCPv6 network layer calls + ::EslTcp6ListenComplete to complete the connection processing. + EslTcp6ListenComplete manages the connections by placing them in + FIFO order in a queue to be serviced by the application. When the + number of connections exceeds the backlog (ESL_SOCKET::MaxFifoDepth), + the new connection is closed. Eventually, the application indirectly + calls ::EslTcp6Accept to remove the next connection from the queue + and get the associated socket. + +**/ + +#include "Socket.h" + + +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv6 network layer. It + configures the local TCPv6 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp6ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv6 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp6ConnectComplete. + This pair of routines walks through the list of local TCPv6 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcp6ConnectStart ( + IN ESL_SOCKET * pSocket + ); + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + The TCPv6 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ); + + +/** + Accept a network connection. + + This routine waits for a network connection to the socket and + returns the remote network address to the caller if requested. + + This routine is called by ::EslSocketAccept to handle the TCPv6 protocol + specific accept operations for SOCK_STREAM and SOCK_SEQPACKET sockets. + See the \ref ConnectionManagement section. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @param [in] pSockAddr Address of a buffer to receive the remote + network address. + + @param [in, out] pSockAddrLength Length in bytes of the address buffer. + On output specifies the length of the + remote network address. + + @retval EFI_SUCCESS Remote address is available + @retval Others Remote address not available + + **/ +EFI_STATUS +EslTcp6Accept ( + IN ESL_SOCKET * pSocket, + IN struct sockaddr * pSockAddr, + IN OUT socklen_t * pSockAddrLength + ) +{ + ESL_PORT * pPort; + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the socket length + // + pRemoteAddress = (struct sockaddr_in6 *) pSockAddr; + if (( NULL == pSockAddrLength ) + || ( sizeof ( *pRemoteAddress ) > *pSockAddrLength )) { + // + // Invalid socket address + // + Status = EFI_INVALID_PARAMETER; + pSocket->errno = EINVAL; + DEBUG (( DEBUG_ACCEPT, + "ERROR - Invalid address length\r\n" )); + } + else { + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Locate the address context + // + pPort = pSocket->pPortList; + pTcp6 = &pPort->Context.Tcp6; + + // + // Fill-in the remote address structure + // + ZeroMem ( pRemoteAddress, sizeof ( *pRemoteAddress )); + pRemoteAddress->sin6_len = sizeof ( *pRemoteAddress ); + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the remote connection completion event. + + This routine handles the completion of a connection attempt. It + releases the port (TCPv6 adapter connection) in the case of an + error and start a connection attempt on the next port. If the + connection attempt was successful then this routine releases all + of the other ports. + + This routine is called by the TCPv6 layer when a connect request + completes. It sets the ESL_SOCKET::bConnected flag to notify the + ::EslTcp6ConnectComplete routine that the connection is available. + The flag is set when the connection is established or no more ports + exist in the list. The connection status is passed via + ESL_SOCKET::ConnectStatus. + + @param [in] Event The connect completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ConnectComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + BOOLEAN bRemoveFirstPort; + BOOLEAN bRemovePorts; + ESL_PORT * pNextPort; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the TCP context + // + pSocket = pPort->pSocket; + pTcp6 = &pPort->Context.Tcp6; + + // + // Get the connection status + // + bRemoveFirstPort = FALSE; + bRemovePorts = FALSE; + Status = pTcp6->ConnectToken.CompletionToken.Status; + pSocket->ConnectStatus = Status; + if ( !EFI_ERROR ( Status )) { + // + // The connection was successful + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connected to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + + // + // Remove the rest of the ports + // + bRemovePorts = TRUE; + } + else { + // + // The connection failed + // + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port connection to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d failed, Status: %r\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort, + Status )); + + // + // Close the current port + // + Status = EslSocketPortClose ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // Try to connect using the next port + // + Status = EslTcp6ConnectStart ( pSocket ); + if ( EFI_NOT_READY != Status ) { + pSocket->ConnectStatus = Status; + bRemoveFirstPort = TRUE; + } + } + + // + // Remove the ports if necessary + // + if ( bRemoveFirstPort || bRemovePorts ) { + // + // Remove the first port if necessary + // + pPort = pSocket->pPortList; + if (( !bRemoveFirstPort ) && ( NULL != pPort )) { + pPort = pPort->pLinkSocket; + } + + // + // Remove the rest of the list + // + while ( NULL != pPort ) { + pNextPort = pPort->pLinkSocket; + EslSocketPortClose ( pPort ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port closed\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to close port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + pPort = pNextPort; + } + + // + // Notify the poll routine + // + pSocket->bConnected = TRUE; + } + + DBG_EXIT ( ); +} + + +/** + Poll for completion of the connection attempt. + + This routine polls the ESL_SOCKET::bConnected flag to determine + when the connection attempt is complete. + + This routine is called from ::EslSocketConnect to determine when + the connection is complete. The ESL_SOCKET::bConnected flag is + set by ::EslTcp6ConnectComplete when the TCPv6 layer establishes + a connection or runs out of local network adapters. This routine + gets the connection status from ESL_SOCKET::ConnectStatus. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcp6ConnectPoll ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if the connection is complete + // + if ( !pSocket->bConnected ) { + // + // Not connected + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + else { + // + // The connection processing is complete + // + pSocket->bConnected = FALSE; + + // + // Translate the connection status + // + Status = pSocket->ConnectStatus; + switch ( Status ) { + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_ABORTED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EINVAL; + break; + + case EFI_NO_MAPPING: + case EFI_NO_RESPONSE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_NO_MEDIA: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOMEM; + break; + + case EFI_SUCCESS: + pSocket->errno = 0; + pSocket->bConfigured = TRUE; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = ENOTSUP; + break; + + case 0x80000069: + pSocket->errno = ECONNRESET; + break; + } + } + + // + // Return the initialization status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Attempt to connect to a remote TCP port + + This routine starts the connection processing for a SOCK_STREAM + or SOCK_SEQPAKCET socket using the TCPv6 network layer. It + configures the local TCPv6 connection point and then attempts to + connect to a remote system. Upon completion, the + ::EslTcp6ConnectComplete routine gets called with the connection + status. + + This routine is called by ::EslSocketConnect to initiate the TCPv6 + network specific connect operations. The connection processing is + initiated by this routine and finished by ::EslTcp6ConnectComplete. + This pair of routines walks through the list of local TCPv6 + connection points until a connection to the remote system is + made. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS The connection was successfully established. + @retval EFI_NOT_READY The connection is in progress, call this routine again. + @retval Others The connection attempt failed. + + **/ +EFI_STATUS +EslTcp6ConnectStart ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pPort; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine if any more local adapters are available + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Configure the port + // + pTcp6 = &pPort->Context.Tcp6; + pTcp6->ConfigData.AccessPoint.ActiveFlag = TRUE; + pTcp6->ConfigData.TrafficClass = 0; + pTcp6->ConfigData.HopLimit = 255; + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Configure ( pTcp6Protocol, + &pTcp6->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Failed to configure the Tcp6 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + } + else { + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + + // + // Attempt the connection to the remote system + // + Status = pTcp6Protocol->Connect ( pTcp6Protocol, + &pTcp6->ConnectToken ); + if ( !EFI_ERROR ( Status )) { + // + // Connection in progress + // + pSocket->errno = EINPROGRESS; + Status = EFI_NOT_READY; + DEBUG (( DEBUG_CONNECT, + "0x%08x: Port attempting connection to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + } + else { + // + // Connection error + // + DEBUG (( DEBUG_CONNECT, + "ERROR - Port 0x%08x not connected, Status: %r\r\n", + pPort, + Status )); + // + // Determine the errno value + // + switch ( Status ) { + default: + pSocket->errno = EIO; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_TIMEOUT: + pSocket->errno = ETIMEDOUT; + break; + + case EFI_NETWORK_UNREACHABLE: + pSocket->errno = ENETDOWN; + break; + + case EFI_HOST_UNREACHABLE: + pSocket->errno = EHOSTUNREACH; + break; + + case EFI_PORT_UNREACHABLE: + case EFI_PROTOCOL_UNREACHABLE: + case EFI_CONNECTION_REFUSED: + pSocket->errno = ECONNREFUSED; + break; + + case EFI_CONNECTION_RESET: + pSocket->errno = ECONNRESET; + break; + } + } + } + } + else { + // + // No more local adapters available + // + pSocket->errno = ENETUNREACH; + Status = EFI_NO_RESPONSE; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Establish the known port to listen for network connections. + + This routine places the port into a state that enables connection + attempts. + + This routine is called by ::EslSocketListen to handle the network + specifics of the listen operation for SOCK_STREAM and SOCK_SEQPACKET + sockets. See the \ref ConnectionManagement section. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS - Socket successfully created + @retval Other - Failed to enable the socket for listen + +**/ +EFI_STATUS +EslTcp6Listen ( + IN ESL_SOCKET * pSocket + ) +{ + ESL_PORT * pNextPort; + ESL_PORT * pPort; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Verify the socket layer synchronization + // + VERIFY_TPL ( TPL_SOCKETS ); + + // + // Use for/break instead of goto + // + for ( ; ; ) { + // + // Assume no ports are available + // + pSocket->errno = EOPNOTSUPP; + Status = EFI_NOT_READY; + + // + // Walk the list of ports + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Assume success + // + pSocket->errno = 0; + + // + // Use for/break insteak of goto + // + for ( ; ; ) { + // + // Create the listen completion event + // + pTcp6 = &pPort->Context.Tcp6; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcp6ListenComplete, + pPort, + &pTcp6->ListenToken.CompletionToken.Event ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DEBUG_LISTEN, + "ERROR - Failed to create the listen completion event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_POOL, + "0x%08x: Created listen completion event\r\n", + pTcp6->ListenToken.CompletionToken.Event )); + + // + // Configure the port + // + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Configure ( pTcp6Protocol, + &pTcp6->ConfigData ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Tcp6 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Port configured\r\n", + pPort )); + pPort->bConfigured = TRUE; + + // + // Start the listen operation on the port + // + Status = pTcp6Protocol->Accept ( pTcp6Protocol, + &pTcp6->ListenToken ); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed Tcp6 accept, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NOT_STARTED: + pSocket->errno = ENETDOWN; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + } + break; + } + DEBUG (( DEBUG_LISTEN, + "0x%08x: Listen pending on Port\r\n", + pPort )); + + // + // Listen is pending on this port + // + break; + } + + // + // Get the next port + // + pNextPort = pPort->pLinkSocket; + + // + // Close the port upon error + // + if ( EFI_ERROR ( Status )) { + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine if any ports are in the listen state + // + if ( NULL == pSocket->pPortList ) { + // + // No ports in the listen state + // + pSocket->MaxFifoDepth = 0; + + // + // Return the last error detected + // + break; + } + + // + // Mark the socket as configured + // + pSocket->bConfigured = TRUE; + + // + // All done + // + DEBUG (( DEBUG_LISTEN, + "0x%08x: pSocket - Listen pending on socket\r\n", + pSocket )); + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the connection attempt + + A system has initiated a connection attempt with a socket in the + listen state. Attempt to complete the connection. + + The TCPv6 layer calls this routine when a connection is made to + the socket in the listen state. See the + \ref ConnectionManagement section. + + @param [in] Event The listen completion event + + @param [in] pPort Address of an ::ESL_PORT structure. + +**/ +VOID +EslTcp6ListenComplete ( + IN EFI_EVENT Event, + IN ESL_PORT * pPort + ) +{ + EFI_HANDLE ChildHandle; + struct sockaddr_in6 LocalAddress; + EFI_TCP6_CONFIG_DATA * pConfigData; + ESL_LAYER * pLayer; + ESL_PORT * pNewPort; + ESL_SOCKET * pNewSocket; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + EFI_HANDLE TcpPortHandle; + EFI_STATUS TempStatus; + + DBG_ENTER ( ); + VERIFY_AT_TPL ( TPL_SOCKETS ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Determine if this connection fits into the connection FIFO + // + pSocket = pPort->pSocket; + TcpPortHandle = pPort->Context.Tcp6.ListenToken.NewChildHandle; + if (( SOCKET_STATE_LISTENING == pSocket->State ) + && ( pSocket->MaxFifoDepth > pSocket->FifoDepth )) { + // + // Allocate a socket for this connection + // + ChildHandle = NULL; + pLayer = &mEslLayer; + Status = EslSocketAllocate ( &ChildHandle, + DEBUG_CONNECTION, + &pNewSocket ); + if ( !EFI_ERROR ( Status )) { + // + // Clone the socket parameters + // + pNewSocket->pApi = pSocket->pApi; + pNewSocket->Domain = pSocket->Domain; + pNewSocket->Protocol = pSocket->Protocol; + pNewSocket->Type = pSocket->Type; + + // + // Build the local address + // + pTcp6 = &pPort->Context.Tcp6; + LocalAddress.sin6_len = (uint8_t)pNewSocket->pApi->MinimumAddressLength; + LocalAddress.sin6_family = AF_INET6; + LocalAddress.sin6_port = 0; + CopyMem ( &LocalAddress.sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + &pTcp6->ConfigData.AccessPoint.StationAddress.Addr [ 0 ], + sizeof ( pTcp6->ConfigData.AccessPoint.StationAddress.Addr )); + + // + // Allocate a port for this connection + // Note in this instance Configure may not be called with NULL! + // + Status = EslSocketPortAllocate ( pNewSocket, + pPort->pService, + TcpPortHandle, + (struct sockaddr *)&LocalAddress, + FALSE, + DEBUG_CONNECTION, + &pNewPort ); + if ( !EFI_ERROR ( Status )) { + // + // Restart the listen operation on the port + // + pTcp6Protocol = pPort->pProtocol.TCPv6; + Status = pTcp6Protocol->Accept ( pTcp6Protocol, + &pTcp6->ListenToken ); + + // + // Close the TCP port using SocketClose + // + TcpPortHandle = NULL; + pTcp6 = &pNewPort->Context.Tcp6; + + // + // Check for an accept call error + // + if ( !EFI_ERROR ( Status )) { + // + // Get the port configuration + // + pNewPort->bConfigured = TRUE; + pConfigData = &pTcp6->ConfigData; + pConfigData->ControlOption = &pTcp6->Option; + pTcp6Protocol = pNewPort->pProtocol.TCPv6; + Status = pTcp6Protocol->GetModeData ( pTcp6Protocol, + NULL, + pConfigData, + NULL, + NULL, + NULL ); + if ( !EFI_ERROR ( Status )) { + // + // Add the new socket to the connection FIFO + // + if ( NULL == pSocket->pFifoTail ) { + // + // First connection + // + pSocket->pFifoHead = pNewSocket; + } + else { + // + // Add to end of list. + // + pSocket->pFifoTail->pNextConnection = pNewSocket; + } + pSocket->pFifoTail = pNewSocket; + pSocket->FifoDepth += 1; + + // + // Update the socket state + // + pNewSocket->State = SOCKET_STATE_IN_FIFO; + + // + // Log the connection + // + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Socket on port [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d connected to [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pNewSocket, + pConfigData->AccessPoint.StationAddress.Addr[0], + pConfigData->AccessPoint.StationAddress.Addr[1], + pConfigData->AccessPoint.StationAddress.Addr[2], + pConfigData->AccessPoint.StationAddress.Addr[3], + pConfigData->AccessPoint.StationAddress.Addr[4], + pConfigData->AccessPoint.StationAddress.Addr[5], + pConfigData->AccessPoint.StationAddress.Addr[6], + pConfigData->AccessPoint.StationAddress.Addr[7], + pConfigData->AccessPoint.StationAddress.Addr[8], + pConfigData->AccessPoint.StationAddress.Addr[9], + pConfigData->AccessPoint.StationAddress.Addr[10], + pConfigData->AccessPoint.StationAddress.Addr[11], + pConfigData->AccessPoint.StationAddress.Addr[12], + pConfigData->AccessPoint.StationAddress.Addr[13], + pConfigData->AccessPoint.StationAddress.Addr[14], + pConfigData->AccessPoint.StationAddress.Addr[15], + pConfigData->AccessPoint.StationPort, + pConfigData->AccessPoint.RemoteAddress.Addr[0], + pConfigData->AccessPoint.RemoteAddress.Addr[1], + pConfigData->AccessPoint.RemoteAddress.Addr[2], + pConfigData->AccessPoint.RemoteAddress.Addr[3], + pConfigData->AccessPoint.RemoteAddress.Addr[4], + pConfigData->AccessPoint.RemoteAddress.Addr[5], + pConfigData->AccessPoint.RemoteAddress.Addr[6], + pConfigData->AccessPoint.RemoteAddress.Addr[7], + pConfigData->AccessPoint.RemoteAddress.Addr[8], + pConfigData->AccessPoint.RemoteAddress.Addr[9], + pConfigData->AccessPoint.RemoteAddress.Addr[10], + pConfigData->AccessPoint.RemoteAddress.Addr[11], + pConfigData->AccessPoint.RemoteAddress.Addr[12], + pConfigData->AccessPoint.RemoteAddress.Addr[13], + pConfigData->AccessPoint.RemoteAddress.Addr[14], + pConfigData->AccessPoint.RemoteAddress.Addr[15], + pConfigData->AccessPoint.RemotePort )); + DEBUG (( DEBUG_CONNECTION | DEBUG_INFO, + "0x%08x: Listen socket adding socket 0x%08x to FIFO, depth: %d\r\n", + pSocket, + pNewSocket, + pSocket->FifoDepth )); + + // + // Start the receive operation + // + EslSocketRxStart ( pNewPort ); + } + else { + DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO, + "ERROR - GetModeData failed on port 0x%08x, Status: %r\r\n", + pNewPort, + Status )); + } + } + else { + // + // The listen failed on this port + // + DEBUG (( DEBUG_LISTEN | DEBUG_INFO, + "ERROR - Listen failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + + // + // Close the listening port + // + EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN ); + } + } + + // + // Done with the socket if necessary + // + if ( EFI_ERROR ( Status )) { + TempStatus = EslSocketCloseStart ( &pNewSocket->SocketProtocol, + TRUE, + &pSocket->errno ); + ASSERT ( EFI_SUCCESS == TempStatus ); + } + } + } + else { + DEBUG (( DEBUG_CONNECTION, + "0x%08x: Socket FIFO full, connection refused\r\n", + pSocket )); + + // + // The FIFO is full or the socket is in the wrong state + // + Status = EFI_BUFFER_TOO_SMALL; + } + + // + // Close the connection if necessary + // + if (( EFI_ERROR ( Status )) + && ( NULL == TcpPortHandle )) { + // + // TODO: Finish this code path + // The new connection does not fit into the connection FIFO + // + // Process: + // Call close + // Release the resources + + } + + DBG_EXIT ( ); +} + + +/** + Get the local socket address. + + This routine returns the IPv6 address and TCP port number associated + with the local socket. + + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_STREAM or SOCK_SEQPACKET socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pSockAddr Network address to receive the local system address + +**/ +VOID +EslTcp6LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in6 * pLocalAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the local address + // + pTcp6 = &pPort->Context.Tcp6; + pLocalAddress = (struct sockaddr_in6 *)pSockAddr; + pLocalAddress->sin6_family = AF_INET6; + pLocalAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.StationPort ); + CopyMem ( &pLocalAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the local port address. + + This routine sets the local port address. + + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv6 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv6 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslTcp6LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_TCP6_ACCESS_POINT * pAccessPoint; + CONST struct sockaddr_in6 * pIpAddress; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Validate the address + // + pIpAddress = (struct sockaddr_in6 *)pSockAddr; +// +// TODO: Fix the following check +// +/* + if ( INADDR_BROADCAST == pIpAddress->sin6_addr.s_addr ) { + // + // The local address must not be the broadcast address + // + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EADDRNOTAVAIL; + } + else { +*/ +{ + // + // Set the local address + // + pAccessPoint = &pPort->Context.Tcp6.ConfigData.AccessPoint; + CopyMem ( &pAccessPoint->StationAddress.Addr[0], + &pIpAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + sizeof ( pIpAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ])); + + // + // Validate the IP address + // + pAccessPoint->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pAccessPoint->StationPort = SwapBytes16 ( pIpAddress->sin6_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local Tcp6 Address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pAccessPoint->StationAddress.Addr[0], + pAccessPoint->StationAddress.Addr[1], + pAccessPoint->StationAddress.Addr[2], + pAccessPoint->StationAddress.Addr[3], + pAccessPoint->StationAddress.Addr[4], + pAccessPoint->StationAddress.Addr[5], + pAccessPoint->StationAddress.Addr[6], + pAccessPoint->StationAddress.Addr[7], + pAccessPoint->StationAddress.Addr[8], + pAccessPoint->StationAddress.Addr[9], + pAccessPoint->StationAddress.Addr[10], + pAccessPoint->StationAddress.Addr[11], + pAccessPoint->StationAddress.Addr[12], + pAccessPoint->StationAddress.Addr[13], + pAccessPoint->StationAddress.Addr[14], + pAccessPoint->StationAddress.Addr[15], + pAccessPoint->StationPort )); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a receive packet + + This routine performs the network specific operations necessary + to free a receive packet. + + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes + +**/ +VOID +EslTcp6PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + *pRxBytes -= pPacket->Op.Tcp6Rx.RxData.DataLength; + DBG_EXIT ( ); +} + + +/** + Initialize the network specific portions of an ::ESL_PORT structure. + + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the TCPv6 protocol. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslTcp6PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_TCP6_ACCESS_POINT * pAccessPoint; + ESL_SOCKET * pSocket; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Use for/break instead of goto + for ( ; ; ) { + // + // Allocate the close event + // + pSocket = pPort->pSocket; + pTcp6 = &pPort->Context.Tcp6; + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslSocketPortCloseComplete, + pPort, + &pTcp6->CloseToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the close event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created close event\r\n", + pTcp6->CloseToken.CompletionToken.Event )); + + // + // Allocate the connection event + // + Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, + TPL_SOCKETS, + (EFI_EVENT_NOTIFY)EslTcp6ConnectComplete, + pPort, + &pTcp6->ConnectToken.CompletionToken.Event); + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to create the connect event, Status: %r\r\n", + Status )); + pSocket->errno = ENOMEM; + break; + } + DEBUG (( DEBUG_CLOSE | DEBUG_POOL, + "0x%08x: Created connect event\r\n", + pTcp6->ConnectToken.CompletionToken.Event )); + + // + // Initialize the port + // + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Tcp6Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Tcp6Tx.CompletionToken.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_TCP6_IO_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv6->Configure; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv6->Poll; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv6->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv6->Transmit; + + // + // Set the configuration flags + // + pAccessPoint = &pPort->Context.Tcp6.ConfigData.AccessPoint; + pAccessPoint->ActiveFlag = FALSE; + pTcp6->ConfigData.TrafficClass = 0; + pTcp6->ConfigData.HopLimit = 255; + break; + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Close a Tcp6 port. + + This routine releases the network specific resources allocated by + ::EslTcp6PortAllocate. + + This routine is called by ::EslSocketPortClose. + See the \ref PortCloseStateMachine section. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @retval EFI_SUCCESS The port is closed + @retval other Port close error + +**/ +EFI_STATUS +EslTcp6PortClose ( + IN ESL_PORT * pPort + ) +{ + UINTN DebugFlags; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the port in the socket list + // + Status = EFI_SUCCESS; + DebugFlags = pPort->DebugFlags; + pTcp6 = &pPort->Context.Tcp6; + + // + // Done with the connect event + // + if ( NULL != pTcp6->ConnectToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->ConnectToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed connect event\r\n", + pTcp6->ConnectToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the connect event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the close event + // + if ( NULL != pTcp6->CloseToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->CloseToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed close event\r\n", + pTcp6->CloseToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the close event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Done with the listen completion event + // + if ( NULL != pTcp6->ListenToken.CompletionToken.Event ) { + Status = gBS->CloseEvent ( pTcp6->ListenToken.CompletionToken.Event ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( DebugFlags | DEBUG_POOL, + "0x%08x: Closed listen completion event\r\n", + pTcp6->ListenToken.CompletionToken.Event )); + } + else { + DEBUG (( DEBUG_ERROR | DebugFlags, + "ERROR - Failed to close the listen completion event, Status: %r\r\n", + Status )); + ASSERT ( EFI_SUCCESS == Status ); + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Perform the network specific close operation on the port. + + This routine performs a cancel operations on the TCPv6 port to + shutdown the receive operations on the port. + + This routine is called by the ::EslSocketPortCloseTxDone + routine after the port completes all of the transmission. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @retval EFI_SUCCESS The port is closed, not normally returned + @retval EFI_NOT_READY The port is still closing + @retval EFI_ALREADY_STARTED Error, the port is in the wrong state, + most likely the routine was called already. + +**/ +EFI_STATUS +EslTcp6PortCloseOp ( + IN ESL_PORT * pPort + ) +{ + ESL_TCP6_CONTEXT * pTcp6; + EFI_TCP6_PROTOCOL * pTcp6Protocol; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Close the configured port + // + Status = EFI_SUCCESS; + pTcp6 = &pPort->Context.Tcp6; + pTcp6Protocol = pPort->pProtocol.TCPv6; + pTcp6->CloseToken.AbortOnClose = pPort->bCloseNow; + Status = pTcp6Protocol->Close ( pTcp6Protocol, + &pTcp6->CloseToken ); + if ( !EFI_ERROR ( Status )) { + DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "0x%08x: Port close started\r\n", + pPort )); + } + else { + DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO, + "ERROR - Close failed on port 0x%08x, Status: %r\r\n", + pPort, + Status )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. + + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_STREAM and SOCK_SEQPACKET + sockets. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [out] pSkipBytes Address to receive the number of bytes skipped + + @return Returns the address of the next free byte in the buffer. + + **/ +UINT8 * +EslTcp6Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + OUT size_t * pSkipBytes + ) +{ + size_t DataLength; + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + pTcp6 = &pPort->Context.Tcp6; + DEBUG (( DEBUG_RX, + "Getting packet remote address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[1], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[2], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[3], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[4], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[5], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[6], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[7], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[8], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[9], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[10], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[11], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[12], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[13], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[14], + pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[15], + pTcp6->ConfigData.AccessPoint.RemotePort )); + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + CopyMem ( &pRemoteAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + } + + // + // Determine the amount of received data + // + DataLength = pPacket->ValidBytes; + if ( BufferLength < DataLength ) { + DataLength = BufferLength; + } + + // + // Move the data into the buffer + // + DEBUG (( DEBUG_RX, + "0x%08x: Port copy packet 0x%08x data into 0x%08x, 0x%08x bytes\r\n", + pPort, + pPacket, + pBuffer, + DataLength )); + CopyMem ( pBuffer, pPacket->pBuffer, DataLength ); + + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { + // + // Account for the bytes consumed + // + pPacket->pBuffer += DataLength; + pPacket->ValidBytes -= DataLength; + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataLength )); + + // + // Determine if the entire packet was consumed + // + if (( 0 == pPacket->ValidBytes ) + || ( SOCK_STREAM != pPort->pSocket->Type )) { + // + // All done with this packet + // Account for any discarded data + // + *pSkipBytes = pPacket->ValidBytes; + } + else + { + // + // More data to consume later + // + *pbConsumePacket = FALSE; + } + } + + // + // Return the data length and the buffer address + // + *pDataLength = DataLength; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; +} + + +/** + Get the remote socket address. + + This routine returns the address of the remote connection point + associated with the SOCK_STREAM or SOCK_SEQPACKET socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the TCPv6 address and por number associated with the network adapter. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address + +**/ +VOID +EslTcp6RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pTcp6 = &pPort->Context.Tcp6; + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pTcp6->ConfigData.AccessPoint.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr, + &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the remote address + + This routine sets the remote address in the port. + + This routine is called by ::EslSocketConnect to specify the + remote network address. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslTcp6RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + CONST struct sockaddr_in6 * pRemoteAddress; + ESL_TCP6_CONTEXT * pTcp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pTcp6 = &pPort->Context.Tcp6; + pRemoteAddress = (struct sockaddr_in6 *)pSockAddr; + CopyMem ( &pTcp6->ConfigData.AccessPoint.RemoteAddress.Addr [ 0 ], + &pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 [ 0 ], + sizeof ( pRemoteAddress->sin6_addr.__u6_addr.__u6_addr8 )); + pTcp6->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pRemoteAddress->sin6_port ); + Status = EFI_SUCCESS; + +// +// TODO: Fix the following check +// +/* + if ( INADDR_BROADCAST == pRemoteAddress->sin6_addr.s_addr ) { + DEBUG (( DEBUG_CONNECT, + "ERROR - Invalid remote address\r\n" )); + Status = EFI_INVALID_PARAMETER; + pPort->pSocket->errno = EAFNOSUPPORT; + } +*/ + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + This routine queues the data in FIFO order in either the urgent + or normal data queues depending upon the type of data received. + See the \ref ReceiveEngine section. + + This routine is called by the TCPv6 driver when some data is + received. + + Buffer the data that was just received. + + @param [in] Event The receive completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslTcp6RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + BOOLEAN bUrgent; + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + Status = pIo->Token.Tcp6Rx.CompletionToken.Status; + + // + // +--------------------+ +---------------------------+ + // | ESL_IO_MGMT | | ESL_PACKET | + // | | | | + // | +---------------+ +-----------------------+ | + // | | Token | | EFI_Tcp6_RECEIVE_DATA | | + // | | RxData --> | | | + // | | | +-----------------------+---+ + // | | Event | | Data Buffer | + // +----+---------------+ | | + // | | + // +---------------------------+ + // + // + // Duplicate the buffer address and length for use by the + // buffer handling code in EslTcp6Receive. These fields are + // used when a partial read is done of the data from the + // packet. + // + pPacket = pIo->pPacket; + pPacket->pBuffer = pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentBuffer; + LengthInBytes = pPacket->Op.Tcp6Rx.RxData.DataLength; + pPacket->ValidBytes = LengthInBytes; + + // + // Get the data type so that it may be linked to the + // correct receive buffer list on the ESL_SOCKET structure + // + bUrgent = pPacket->Op.Tcp6Rx.RxData.UrgentFlag; + + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, bUrgent ); + DBG_EXIT ( ); +} + + +/** + Start a receive operation + + This routine posts a receive buffer to the TCPv6 driver. + See the \ref ReceiveEngine section. + + This support routine is called by EslSocketRxStart. + + @param [in] pPort Address of an ::ESL_PORT structure. + @param [in] pIo Address of an ::ESL_IO_MGMT structure. + + **/ +VOID +EslTcp6RxStart ( + IN ESL_PORT * pPort, + IN ESL_IO_MGMT * pIo + ) +{ + ESL_PACKET * pPacket; + + DBG_ENTER ( ); + + // + // Initialize the buffer for receive + // + pPacket = pIo->pPacket; + pIo->Token.Tcp6Rx.Packet.RxData = &pPacket->Op.Tcp6Rx.RxData; + pPacket->Op.Tcp6Rx.RxData.DataLength = sizeof ( pPacket->Op.Tcp6Rx.Buffer ); + pPacket->Op.Tcp6Rx.RxData.FragmentCount = 1; + pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentLength = pPacket->Op.Tcp6Rx.RxData.DataLength; + pPacket->Op.Tcp6Rx.RxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp6Rx.Buffer[0]; + + DBG_EXIT ( ); +} + + +/** + Determine if the socket is configured. + + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + + This routine is called by EslSocketIsConfigured to verify + that the socket has been configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure. + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslTcp6SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Determine the socket configuration status + // + Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; + + // + // Return the port connected state. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Buffer data for transmission over a network connection. + + This routine buffers data for the transmit engine in one of two + queues, one for urgent (out-of-band) data and the other for normal + data. The urgent data is provided to TCP as soon as it is available, + allowing the TCP layer to schedule transmission of the urgent data + between packets of normal data. + + This routine is called by ::EslSocketTransmit to buffer + data for transmission. When the \ref TransmitEngine has resources, + this routine will start the transmission of the next buffer on + the network connection. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully buffered + + **/ +EFI_STATUS +EslTcp6TxBuffer ( + IN ESL_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength + ) +{ + BOOLEAN bUrgent; + BOOLEAN bUrgentQueue; + ESL_PACKET * pPacket; + ESL_IO_MGMT ** ppActive; + ESL_IO_MGMT ** ppFree; + ESL_PORT * pPort; + ESL_PACKET ** ppQueueHead; + ESL_PACKET ** ppQueueTail; + ESL_PACKET * pPreviousPacket; + ESL_TCP6_CONTEXT * pTcp6; + size_t * pTxBytes; + EFI_TCP6_TRANSMIT_DATA * pTxData; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + *pDataLength = 0; + + // + // Verify that the socket is connected + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pTcp6 = &pPort->Context.Tcp6; + bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB )); + bUrgentQueue = bUrgent + && ( !pSocket->bOobInLine ) + && pSocket->pApi->bOobSupported; + if ( bUrgentQueue ) { + ppQueueHead = &pSocket->pTxOobPacketListHead; + ppQueueTail = &pSocket->pTxOobPacketListTail; + ppActive = &pPort->pTxOobActive; + ppFree = &pPort->pTxOobFree; + pTxBytes = &pSocket->TxOobBytes; + } + else { + ppQueueHead = &pSocket->pTxPacketListHead; + ppQueueTail = &pSocket->pTxPacketListTail; + ppActive = &pPort->pTxActive; + ppFree = &pPort->pTxFree; + pTxBytes = &pSocket->TxBytes; + } + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + if ( pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n0x%08x: pPort, TX flow control released, Max bytes: %d > %d bufferred bytes\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = FALSE; + } + + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Tcp6Tx ) + - sizeof ( pPacket->Op.Tcp6Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Tcp6Tx.TxData; + pTxData->Push = TRUE || bUrgent; + pTxData->Urgent = bUrgent; + pTxData->DataLength = (UINT32) BufferLength; + pTxData->FragmentCount = 1; + pTxData->FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp6Tx.Buffer[0]; + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Tcp6Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + bUrgent ? L"urgent" : L"normal", + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = *ppQueueTail; + if ( NULL == pPreviousPacket ) { + *ppQueueHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + *ppQueueTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on %s transmit list\r\n", + pPacket, + bUrgentQueue ? L"urgent" : L"normal" )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL != *ppFree ) { + EslSocketTxStart ( pPort, + ppQueueHead, + ppQueueTail, + ppActive, + ppFree ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + if ( !pPort->bTxFlowControl ) { + DEBUG (( DEBUG_TX, + "0x%08x: pPort, TX flow control applied, Max bytes %d <= %d bufferred bytes\r\nTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n", + pPort, + pSocket->MaxTxBuf, + *pTxBytes )); + pPort->bTxFlowControl = TRUE; + } + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the normal data transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for normal data. + + This routine is called by the TCPv6 network layer when a + normal data transmit request completes. + + @param [in] Event The normal transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address + +**/ +VOID +EslTcp6TxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Tcp6Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Tcp6Tx.CompletionToken.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Normal ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Process the urgent data transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for urgent data. + + This routine is called by the TCPv6 network layer when a + urgent data transmit request completes. + + @param [in] Event The urgent transmit completion event + + @param [in] pIo The ESL_IO_MGMT structure address + +**/ +VOID +EslTcp6TxOobComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PACKET * pPacket; + ESL_PORT * pPort; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Tcp6Tx.TxData.DataLength; + pSocket->TxOobBytes -= LengthInBytes; + Status = pIo->Token.Tcp6Tx.CompletionToken.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "Urgent ", + &pSocket->pTxOobPacketListHead, + &pSocket->pTxOobPacketListTail, + &pPort->pTxOobActive, + &pPort->pTxOobFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_STREAM and SOCK_SEQPACKET sockets + over TCPv6. +**/ +CONST ESL_PROTOCOL_API cEslTcp6Api = { + "TCPv6", + IPPROTO_TCP, + OFFSET_OF ( ESL_PORT, Context.Tcp6.ConfigData ), + OFFSET_OF ( ESL_LAYER, pTcp6List ), + sizeof ( struct sockaddr_in6 ), + sizeof ( struct sockaddr_in6 ), + AF_INET6, + sizeof (((ESL_PACKET *)0 )->Op.Tcp6Rx ), + OFFSET_OF ( ESL_PACKET, Op.Tcp6Rx.Buffer ) - OFFSET_OF ( ESL_PACKET, Op ), + OFFSET_OF ( ESL_IO_MGMT, Token.Tcp6Rx.Packet.RxData ), + TRUE, + EADDRINUSE, + EslTcp6Accept, + EslTcp6ConnectPoll, + EslTcp6ConnectStart, + EslTcp6SocketIsConfigured, + EslTcp6LocalAddressGet, + EslTcp6LocalAddressSet, + EslTcp6Listen, + NULL, // OptionGet + NULL, // OptionSet + EslTcp6PacketFree, + EslTcp6PortAllocate, + EslTcp6PortClose, + EslTcp6PortCloseOp, + FALSE, + EslTcp6Receive, + EslTcp6RemoteAddressGet, + EslTcp6RemoteAddressSet, + EslTcp6RxComplete, + EslTcp6RxStart, + EslTcp6TxBuffer, + EslTcp6TxComplete, + EslTcp6TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/Udp4.c b/StdLib/EfiSocketLib/Udp4.c index e1500d3f9f..6625c078d0 100644 --- a/StdLib/EfiSocketLib/Udp4.c +++ b/StdLib/EfiSocketLib/Udp4.c @@ -253,6 +253,7 @@ EslUdp4PortAllocate ( // pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv4->Configure; pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Cancel; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.UDPv4->Poll; pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Receive; pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Transmit; diff --git a/StdLib/EfiSocketLib/Udp6.c b/StdLib/EfiSocketLib/Udp6.c new file mode 100644 index 0000000000..187f0ad641 --- /dev/null +++ b/StdLib/EfiSocketLib/Udp6.c @@ -0,0 +1,1094 @@ +/** @file + Implement the UDP4 driver support for the socket layer. + + Copyright (c) 2011, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Socket.h" + + +/** + Get the local socket address + + This routine returns the IPv6 address and UDP port number associated + with the local socket. + + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_DGRAM socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pSockAddr Network address to receive the local system address + +**/ +VOID +EslUdp6LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in6 * pLocalAddress; + ESL_UDP6_CONTEXT * pUdp6; + + DBG_ENTER ( ); + + // + // Return the local address + // + pUdp6 = &pPort->Context.Udp6; + pLocalAddress = (struct sockaddr_in6 *)pSockAddr; + pLocalAddress->sin6_family = AF_INET6; + pLocalAddress->sin6_port = SwapBytes16 ( pUdp6->ConfigData.StationPort ); + CopyMem ( &pLocalAddress->sin6_addr, + &pUdp6->ConfigData.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the local port address. + + This routine sets the local port address. + + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv6 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv6 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslUdp6LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_UDP6_CONFIG_DATA * pConfig; + CONST struct sockaddr_in6 * pIpAddress; + CONST UINT8 * pIPv6Address; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the local address + // + pIpAddress = (struct sockaddr_in6 *)pSockAddr; + pIPv6Address = (UINT8 *)&pIpAddress->sin6_addr; + pConfig = &pPort->Context.Udp6.ConfigData; + CopyMem ( &pConfig->StationAddress, + pIPv6Address, + sizeof ( pConfig->StationAddress )); + + // + // Validate the IP address + // + pConfig->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pConfig->StationPort = SwapBytes16 ( pIpAddress->sin6_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local UDP6 Address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfig->StationAddress.Addr[0], + pConfig->StationAddress.Addr[1], + pConfig->StationAddress.Addr[2], + pConfig->StationAddress.Addr[3], + pConfig->StationAddress.Addr[4], + pConfig->StationAddress.Addr[5], + pConfig->StationAddress.Addr[6], + pConfig->StationAddress.Addr[7], + pConfig->StationAddress.Addr[8], + pConfig->StationAddress.Addr[9], + pConfig->StationAddress.Addr[10], + pConfig->StationAddress.Addr[11], + pConfig->StationAddress.Addr[12], + pConfig->StationAddress.Addr[13], + pConfig->StationAddress.Addr[14], + pConfig->StationAddress.Addr[15], + pConfig->StationPort )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a receive packet + + This routine performs the network specific operations necessary + to free a receive packet. + + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes + +**/ +VOID +EslUdp6PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + EFI_UDP6_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + pRxData = pPacket->Op.Udp6Rx.pRxData; + *pRxBytes -= pRxData->DataLength; + + // + // Disconnect the buffer from the packet + // + pPacket->Op.Udp6Rx.pRxData = NULL; + + // + // Return the buffer to the UDP6 driver + // + gBS->SignalEvent ( pRxData->RecycleSignal ); + DBG_EXIT ( ); +} + + +/** + Initialize the network specific portions of an ::ESL_PORT structure. + + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the UDPv4 protocol. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslUdp6PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_UDP6_CONFIG_DATA * pConfig; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Initialize the port + // + pSocket = pPort->pSocket; + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Udp6Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Udp6Tx.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_UDP6_COMPLETION_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv6->Configure; + pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Cancel; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.UDPv6->Poll; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Transmit; + + // + // Do not drop packets + // + pConfig = &pPort->Context.Udp6.ConfigData; + pConfig->ReceiveTimeout = 0; + pConfig->ReceiveTimeout = pConfig->ReceiveTimeout; + + // + // Set the configuration flags + // + pConfig->AllowDuplicatePort = TRUE; + pConfig->AcceptAnyPort = FALSE; + pConfig->AcceptPromiscuous = FALSE; + pConfig->HopLimit = 255; + pConfig->TrafficClass = 0; + + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. + + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_DGRAM sockets. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [out] pSkipBytes Address to receive the number of bytes skipped + + @return Returns the address of the next free byte in the buffer. + + **/ +UINT8 * +EslUdp6Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + OUT size_t * pSkipBytes + ) +{ + size_t DataBytes; + struct sockaddr_in6 * pRemoteAddress; + EFI_UDP6_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + pRxData = pPacket->Op.Udp6Rx.pRxData; + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + DEBUG (( DEBUG_RX, + "Getting packet remote address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pRxData->UdpSession.SourceAddress.Addr[0], + pRxData->UdpSession.SourceAddress.Addr[1], + pRxData->UdpSession.SourceAddress.Addr[2], + pRxData->UdpSession.SourceAddress.Addr[3], + pRxData->UdpSession.SourceAddress.Addr[4], + pRxData->UdpSession.SourceAddress.Addr[5], + pRxData->UdpSession.SourceAddress.Addr[6], + pRxData->UdpSession.SourceAddress.Addr[7], + pRxData->UdpSession.SourceAddress.Addr[8], + pRxData->UdpSession.SourceAddress.Addr[9], + pRxData->UdpSession.SourceAddress.Addr[10], + pRxData->UdpSession.SourceAddress.Addr[11], + pRxData->UdpSession.SourceAddress.Addr[12], + pRxData->UdpSession.SourceAddress.Addr[13], + pRxData->UdpSession.SourceAddress.Addr[14], + pRxData->UdpSession.SourceAddress.Addr[15], + pRxData->UdpSession.SourcePort )); + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + CopyMem ( &pRemoteAddress->sin6_addr, + &pRxData->UdpSession.SourceAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + pRemoteAddress->sin6_port = SwapBytes16 ( pRxData->UdpSession.SourcePort ); + } + + // + // Copy the received data + // + pBuffer = EslSocketCopyFragmentedBuffer ( pRxData->FragmentCount, + (EFI_IP4_FRAGMENT_DATA *)&pRxData->FragmentTable[0], + BufferLength, + pBuffer, + &DataBytes ); + + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { + // + // Display for the bytes consumed + // + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataBytes )); + + // + // Account for any discarded data + // + *pSkipBytes = pRxData->DataLength - DataBytes; + } + + // + // Return the data length and the buffer address + // + *pDataLength = DataBytes; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; +} + + +/** + Get the remote socket address + + This routine returns the address of the remote connection point + associated with the SOCK_DGRAM socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the UDPv4 address and port number associated with the network adapter. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address + +**/ +VOID +EslUdp6RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pUdp6 = &pPort->Context.Udp6; + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pUdp6->ConfigData.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr, + &pUdp6->ConfigData.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the remote address + + This routine sets the remote address in the port. + + This routine is called by ::EslSocketConnect to specify the + remote network address. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslUdp6RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + CONST struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pUdp6 = &pPort->Context.Udp6; + pRemoteAddress = (struct sockaddr_in6 *)pSockAddr; + CopyMem ( &pUdp6->ConfigData.RemoteAddress, + &pRemoteAddress->sin6_addr, + sizeof ( pUdp6->ConfigData.RemoteAddress )); + pUdp6->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin6_port ); + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + This routine keeps the UDPv4 driver's buffer and queues it in + in FIFO order to the data queue. The UDP6 driver's buffer will + be returned by either ::EslUdp6Receive or ::EslSocketPortCloseTxDone. + See the \ref ReceiveEngine section. + + This routine is called by the UDPv4 driver when data is + received. + + @param [in] Event The receive completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslUdp6RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_UDP6_RECEIVE_DATA * pRxData; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + Status = pIo->Token.Udp6Rx.Status; + + // + // Get the packet length + // + pRxData = pIo->Token.Udp6Rx.Packet.RxData; + LengthInBytes = pRxData->DataLength; + + // + // +--------------------+ +-----------------------+ + // | ESL_IO_MGMT | | Data Buffer | + // | | | (Driver owned) | + // | +---------------+ +-----------------------+ + // | | Token | ^ + // | | Rx Event | | + // | | | +-----------------------+ + // | | RxData --> | EFI_UDP6_RECEIVE_DATA | + // +----+---------------+ | (Driver owned) | + // +-----------------------+ + // +--------------------+ ^ + // | ESL_PACKET | . + // | | . + // | +---------------+ . + // | | pRxData --> NULL ....... + // +----+---------------+ + // + // + // Save the data in the packet + // + pPacket = pIo->pPacket; + pPacket->Op.Udp6Rx.pRxData = pRxData; + + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, FALSE ); + DBG_EXIT ( ); +} + + +/** + Determine if the socket is configured. + + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + This routine calls the bind and configuration routines if they + were not already called. After the port is configured, the + \ref ReceiveEngine is started. + + This routine is called by EslSocketIsConfigured to verify + that the socket is configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslUdp6SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_UDP6_CONFIG_DATA * pConfigData; + ESL_PORT * pPort; + ESL_PORT * pNextPort; + ESL_UDP6_CONTEXT * pUdp6; + EFI_UDP6_PROTOCOL * pUdp6Protocol; + EFI_STATUS Status; + struct sockaddr_in6 LocalAddress; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Configure the port if necessary + // + if ( !pSocket->bConfigured ) { + // + // Fill in the port list if necessary + // + if ( NULL == pSocket->pPortList ) { + ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); + LocalAddress.sin6_len = sizeof ( LocalAddress ); + LocalAddress.sin6_family = AF_INET6; + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin6_len, + &pSocket->errno ); + } + + // + // Walk the port list + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Attempt to configure the port + // + pNextPort = pPort->pLinkSocket; + pUdp6 = &pPort->Context.Udp6; + pUdp6Protocol = pPort->pProtocol.UDPv6; + pConfigData = &pUdp6->ConfigData; + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configuring for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d --> [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationAddress.Addr[4], + pConfigData->StationAddress.Addr[5], + pConfigData->StationAddress.Addr[6], + pConfigData->StationAddress.Addr[7], + pConfigData->StationAddress.Addr[8], + pConfigData->StationAddress.Addr[9], + pConfigData->StationAddress.Addr[10], + pConfigData->StationAddress.Addr[11], + pConfigData->StationAddress.Addr[12], + pConfigData->StationAddress.Addr[13], + pConfigData->StationAddress.Addr[14], + pConfigData->StationAddress.Addr[15], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemoteAddress.Addr[4], + pConfigData->RemoteAddress.Addr[5], + pConfigData->RemoteAddress.Addr[6], + pConfigData->RemoteAddress.Addr[7], + pConfigData->RemoteAddress.Addr[8], + pConfigData->RemoteAddress.Addr[9], + pConfigData->RemoteAddress.Addr[10], + pConfigData->RemoteAddress.Addr[11], + pConfigData->RemoteAddress.Addr[12], + pConfigData->RemoteAddress.Addr[13], + pConfigData->RemoteAddress.Addr[14], + pConfigData->RemoteAddress.Addr[15], + pConfigData->RemotePort )); + Status = pUdp6Protocol->Configure ( pUdp6Protocol, + pConfigData ); + if ( !EFI_ERROR ( Status )) { + // + // Update the configuration data + // + Status = pUdp6Protocol->GetModeData ( pUdp6Protocol, + pConfigData, + NULL, + NULL, + NULL ); + } + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Udp6 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + } + else { + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configured for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d --> [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationAddress.Addr[4], + pConfigData->StationAddress.Addr[5], + pConfigData->StationAddress.Addr[6], + pConfigData->StationAddress.Addr[7], + pConfigData->StationAddress.Addr[8], + pConfigData->StationAddress.Addr[9], + pConfigData->StationAddress.Addr[10], + pConfigData->StationAddress.Addr[11], + pConfigData->StationAddress.Addr[12], + pConfigData->StationAddress.Addr[13], + pConfigData->StationAddress.Addr[14], + pConfigData->StationAddress.Addr[15], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemoteAddress.Addr[4], + pConfigData->RemoteAddress.Addr[5], + pConfigData->RemoteAddress.Addr[6], + pConfigData->RemoteAddress.Addr[7], + pConfigData->RemoteAddress.Addr[8], + pConfigData->RemoteAddress.Addr[9], + pConfigData->RemoteAddress.Addr[10], + pConfigData->RemoteAddress.Addr[11], + pConfigData->RemoteAddress.Addr[12], + pConfigData->RemoteAddress.Addr[13], + pConfigData->RemoteAddress.Addr[14], + pConfigData->RemoteAddress.Addr[15], + pConfigData->RemotePort )); + pPort->bConfigured = TRUE; + + // + // Start the first read on the port + // + EslSocketRxStart ( pPort ); + + // + // The socket is connected + // + pSocket->State = SOCKET_STATE_CONNECTED; + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine the configuration status + // + if ( NULL != pSocket->pPortList ) { + pSocket->bConfigured = TRUE; + } + } + + // + // Determine the socket configuration status + // + if ( !EFI_ERROR ( Status )) { + Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; + } + + // + // Return the port connected state. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Buffer data for transmission over a network connection. + + This routine buffers data for the transmit engine in the normal + data queue. When the \ref TransmitEngine has resources, this + routine will start the transmission of the next buffer on the + network connection. + + This routine is called by ::EslSocketTransmit to buffer + data for transmission. The data is copied into a local buffer + freeing the application buffer for reuse upon return. When + necessary, this routine starts the transmit engine that + performs the data transmission on the network connection. The + transmit engine transmits the data a packet at a time over the + network connection. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully buffered + +**/ +EFI_STATUS +EslUdp6TxBuffer ( + IN ESL_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength + ) +{ + ESL_PACKET * pPacket; + ESL_PACKET * pPreviousPacket; + ESL_PORT * pPort; + const struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + size_t * pTxBytes; + ESL_UDP6_TX_DATA * pTxData; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + *pDataLength = 0; + + // + // Verify that the socket is connected + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pUdp6 = &pPort->Context.Udp6; + pTxBytes = &pSocket->TxBytes; + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Udp6Tx ) + - sizeof ( pPacket->Op.Udp6Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Udp6Tx; + pTxData->TxData.UdpSessionData = NULL; + pTxData->TxData.DataLength = (UINT32) BufferLength; + pTxData->TxData.FragmentCount = 1; + pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Udp6Tx.Buffer[0]; + + // + // Set the remote system address if necessary + // + pTxData->TxData.UdpSessionData = NULL; + if ( NULL != pAddress ) { + pRemoteAddress = (const struct sockaddr_in6 *)pAddress; + CopyMem ( &pTxData->Session.SourceAddress, + &pUdp6->ConfigData.StationAddress, + sizeof ( pTxData->Session.SourceAddress )); + pTxData->Session.SourcePort = 0; + CopyMem ( &pTxData->Session.DestinationAddress, + &pRemoteAddress->sin6_addr, + sizeof ( pTxData->Session.DestinationAddress )); + pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin6_port ); + + // + // Use the remote system address when sending this packet + // + pTxData->TxData.UdpSessionData = &pTxData->Session; + } + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Udp6Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = pSocket->pTxPacketListTail; + if ( NULL == pPreviousPacket ) { + pSocket->pTxPacketListHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + pSocket->pTxPacketListTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on transmit list\r\n", + pPacket )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL != pPort->pTxFree ) { + EslSocketTxStart ( pPort, + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for data packets. + + This routine is called by the UDPv4 network layer when a data + transmit request completes. + + @param [in] Event The normal transmit completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslUdp6TxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PORT * pPort; + ESL_PACKET * pPacket; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Udp6Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Udp6Tx.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "UDP ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_DGRAM sockets over UDPv4. +**/ +CONST ESL_PROTOCOL_API cEslUdp6Api = { + "UDPv6", + IPPROTO_UDP, + OFFSET_OF ( ESL_PORT, Context.Udp6.ConfigData ), + OFFSET_OF ( ESL_LAYER, pUdp6List ), + sizeof ( struct sockaddr_in6 ), + sizeof ( struct sockaddr_in6 ), + AF_INET6, + sizeof (((ESL_PACKET *)0 )->Op.Udp6Rx ), + sizeof (((ESL_PACKET *)0 )->Op.Udp6Rx ), + OFFSET_OF ( ESL_IO_MGMT, Token.Udp6Rx.Packet.RxData ), + FALSE, + EADDRINUSE, + NULL, // Accept + NULL, // ConnectPoll + NULL, // ConnectStart + EslUdp6SocketIsConfigured, + EslUdp6LocalAddressGet, + EslUdp6LocalAddressSet, + NULL, // Listen + NULL, // OptionGet + NULL, // OptionSet + EslUdp6PacketFree, + EslUdp6PortAllocate, + NULL, // PortClose, + NULL, // PortCloseOp + TRUE, + EslUdp6Receive, + EslUdp6RemoteAddressGet, + EslUdp6RemoteAddressSet, + EslUdp6RxComplete, + NULL, // RxStart + EslUdp6TxBuffer, + EslUdp6TxComplete, + NULL // TxOobComplete +}; diff --git a/StdLib/EfiSocketLib/UseEfiSocketLib.c b/StdLib/EfiSocketLib/UseEfiSocketLib.c index 1c122af650..ed72e8e763 100644 --- a/StdLib/EfiSocketLib/UseEfiSocketLib.c +++ b/StdLib/EfiSocketLib/UseEfiSocketLib.c @@ -28,6 +28,14 @@ CONST EFI_GUID mEslIp4ServiceGuid = { }; +/** + Tag GUID - IPv6 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslIp6ServiceGuid = { + 0xc51b2761, 0xc476, 0x45fe, { 0xbe, 0x61, 0xba, 0x4b, 0xcc, 0x32, 0xf2, 0x34 } +}; + + /** Tag GUID - TCPv4 in use by an application using EfiSocketLib **/ @@ -36,6 +44,14 @@ CONST EFI_GUID mEslTcp4ServiceGuid = { }; +/** + Tag GUID - TCPv6 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslTcp6ServiceGuid = { + 0x279858a4, 0x4e9e, 0x4e53, { 0x93, 0x22, 0xf2, 0x54, 0xe0, 0x7e, 0xef, 0xd4 } +}; + + /** Tag GUID - UDPv4 in use by an application using EfiSocketLib **/ @@ -44,6 +60,14 @@ CONST EFI_GUID mEslUdp4ServiceGuid = { }; +/** + Tag GUID - UDPv6 in use by an application using EfiSocketLib +**/ +CONST EFI_GUID mEslUdp6ServiceGuid = { + 0xaa4af677, 0x6efe, 0x477c, { 0x96, 0x68, 0xe8, 0x13, 0x9d, 0x2, 0xfd, 0x9b } +}; + + /** Connect to the EFI socket library @@ -252,10 +276,8 @@ EslServiceNetworkDisconnect ( NULL, &HandleCount, &pHandles ); - if ( EFI_ERROR ( Status )) { - break; - } - if ( NULL != pHandles ) { + if (( !EFI_ERROR ( Status )) + && ( NULL != pHandles )) { // // Attempt to disconnect from this network adapter // @@ -277,6 +299,7 @@ EslServiceNetworkDisconnect ( // Set the next network protocol // pSocketBinding += 1; + Status = EFI_SUCCESS; } // diff --git a/StdLib/Include/Efi/EfiSocketLib.h b/StdLib/Include/Efi/EfiSocketLib.h index 802fbca40b..f71653fa16 100644 --- a/StdLib/Include/Efi/EfiSocketLib.h +++ b/StdLib/Include/Efi/EfiSocketLib.h @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include @@ -154,8 +156,11 @@ typedef struct { //------------------------------------------------------------------------------ extern CONST EFI_GUID mEslIp4ServiceGuid; ///< Tag GUID for the IPv4 layer +extern CONST EFI_GUID mEslIp6ServiceGuid; ///< Tag GUID for the IPv6 layer extern CONST EFI_GUID mEslTcp4ServiceGuid; ///< Tag GUID for the TCPv4 layer +extern CONST EFI_GUID mEslTcp6ServiceGuid; ///< Tag GUID for the TCPv6 layer extern CONST EFI_GUID mEslUdp4ServiceGuid; ///< Tag GUID for the UDPv4 layer +extern CONST EFI_GUID mEslUdp6ServiceGuid; ///< Tag GUID for the UDPv6 layer //------------------------------------------------------------------------------ // Data diff --git a/StdLib/Include/net/if_dl.h b/StdLib/Include/net/if_dl.h index 3935551097..3a2104d461 100644 --- a/StdLib/Include/net/if_dl.h +++ b/StdLib/Include/net/if_dl.h @@ -77,8 +77,8 @@ struct sockaddr_dl { #include __BEGIN_DECLS -void link_addr __P((const char *, struct sockaddr_dl *)); -char *link_ntoa __P((const struct sockaddr_dl *)); +void link_addr (const char *, struct sockaddr_dl *); +char *link_ntoa (const struct sockaddr_dl *); __END_DECLS #endif /* !KERNEL */ diff --git a/StdLib/Include/net/servent.h b/StdLib/Include/net/servent.h new file mode 100644 index 0000000000..09931f69d4 --- /dev/null +++ b/StdLib/Include/net/servent.h @@ -0,0 +1,60 @@ +/* $NetBSD: servent.h,v 1.3 2008/04/28 20:23:00 martin Exp $ */ + +/*- + * Copyright (c) 2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS 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 FOUNDATION OR 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. + */ + +#include + +struct servent_data { + void *db; + struct servent serv; + char **aliases; + size_t maxaliases; + int flags; +#define _SV_STAYOPEN 1 +#define _SV_DB 2 +#define _SV_FIRST 4 + char *line; + void *dummy; +}; + +#if 0 +struct servent *getservent_r(struct servent *, struct servent_data *); +struct servent *getservbyname_r(const char *, const char *, + struct servent *, struct servent_data *); +struct servent *getservbyport_r(int, const char *, + struct servent *, struct servent_data *); +void setservent_r(int, struct servent_data *); +void endservent_r(struct servent_data *); +#endif // 0 + +int _servent_open(struct servent_data *); +void _servent_close(struct servent_data *); +int _servent_getline(struct servent_data *); +struct servent *_servent_parseline(struct servent_data *, struct servent *); diff --git a/StdLib/Include/nsswitch.h b/StdLib/Include/nsswitch.h new file mode 100644 index 0000000000..89e3a3e68b --- /dev/null +++ b/StdLib/Include/nsswitch.h @@ -0,0 +1,237 @@ +/* $NetBSD: nsswitch.h,v 1.20 2008/04/28 20:22:54 martin Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS 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 FOUNDATION OR 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. + */ + +#ifndef _NSSWITCH_H +#define _NSSWITCH_H 1 + +/* + * Don't use va_list in prototypes. va_list is typedef'd in two places + * ( and ), so if we include one of + * them here we may collide with the utility's includes. It's unreasonable + * for utilities to have to include one of them to include nsswitch.h, so + * we get _BSD_VA_LIST_ from and use it. + */ +#include +#include + +#define NSS_MODULE_INTERFACE_VERSION 0 + +#ifndef _PATH_NS_CONF +#define _PATH_NS_CONF "/etc/nsswitch.conf" +#endif + +#define NS_CONTINUE 0 +#define NS_RETURN 1 + +/* + * Layout of: + * uint32_t ns_src.flags + */ + /* nsswitch.conf status codes and nsdispatch(3) return values */ +#define NS_SUCCESS (1<<0) /* entry was found */ +#define NS_UNAVAIL (1<<1) /* source not responding, or corrupt */ +#define NS_NOTFOUND (1<<2) /* source responded 'no such entry' */ +#define NS_TRYAGAIN (1<<3) /* source busy, may respond to retrys */ +#define NS_STATUSMASK 0x000000ff /* bitmask to get the status flags */ + + /* internal nsdispatch(3) flags; not settable in nsswitch.conf(5) */ +#define NS_FORCEALL (1<<8) /* force all methods to be invoked; */ + +/* + * Currently implemented sources. + */ +#define NSSRC_FILES "files" /* local files */ +#define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */ +#define NSSRC_NIS "nis" /* YP/NIS */ +#define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */ + +/* + * Currently implemented databases. + */ +#define NSDB_HOSTS "hosts" +#define NSDB_GROUP "group" +#define NSDB_GROUP_COMPAT "group_compat" +#define NSDB_NETGROUP "netgroup" +#define NSDB_NETWORKS "networks" +#define NSDB_PASSWD "passwd" +#define NSDB_PASSWD_COMPAT "passwd_compat" +#define NSDB_SHELLS "shells" + +/* + * Suggested databases to implement. + */ +#define NSDB_ALIASES "aliases" +#define NSDB_AUTH "auth" +#define NSDB_AUTOMOUNT "automount" +#define NSDB_BOOTPARAMS "bootparams" +#define NSDB_ETHERS "ethers" +#define NSDB_EXPORTS "exports" +#define NSDB_NETMASKS "netmasks" +#define NSDB_PHONES "phones" +#define NSDB_PRINTCAP "printcap" +#define NSDB_PROTOCOLS "protocols" +#define NSDB_REMOTE "remote" +#define NSDB_RPC "rpc" +#define NSDB_SENDMAILVARS "sendmailvars" +#define NSDB_SERVICES "services" +#define NSDB_TERMCAP "termcap" +#define NSDB_TTYS "ttys" + +/* + * ns_dtab `callback' function signature. + */ +typedef int (*nss_method)(void *, void *, _BSD_VA_LIST_); + +/* + * ns_dtab - `nsswitch dispatch table' + * Contains an entry for each source and the appropriate function to call. + */ +typedef struct { + const char *src; + nss_method callback; + void *cb_data; +} ns_dtab; + +/* + * Macros to help build an ns_dtab[] + */ +#define NS_FILES_CB(F,C) { NSSRC_FILES, F, __UNCONST(C) }, +#define NS_COMPAT_CB(F,C) { NSSRC_COMPAT, F, __UNCONST(C) }, + +#ifdef HESIOD +# define NS_DNS_CB(F,C) { NSSRC_DNS, F, __UNCONST(C) }, +#else +# define NS_DNS_CB(F,C) +#endif + +#ifdef YP +# define NS_NIS_CB(F,C) { NSSRC_NIS, F, __UNCONST(C) }, +#else +# define NS_NIS_CB(F,C) +#endif +#define NS_NULL_CB { NULL, NULL, NULL }, + +/* + * ns_src - `nsswitch source' + * Used by the nsparser routines to store a mapping between a source + * and its dispatch control flags for a given database. + */ +typedef struct { + const char *name; + uint32_t flags; +} ns_src; + + +/* + * Default sourcelists (if nsswitch.conf is missing, corrupt, + * or the requested database doesn't have an entry) + */ +extern const ns_src __nsdefaultsrc[]; +extern const ns_src __nsdefaultcompat[]; +extern const ns_src __nsdefaultcompat_forceall[]; +extern const ns_src __nsdefaultfiles[]; +extern const ns_src __nsdefaultfiles_forceall[]; +extern const ns_src __nsdefaultnis[]; +extern const ns_src __nsdefaultnis_forceall[]; + + +/* + * ns_mtab - `nsswitch method table' + * An nsswitch module provides a mapping from (database name, method name) + * tuples to the nss_method and associated callback data. Effectively, + * ns_dtab, but used for dynamically loaded modules. + */ +typedef struct { + const char *database; + const char *name; + nss_method method; + void *mdata; +} ns_mtab; + +/* + * nss_module_register_fn - module registration function + * called at module load + * nss_module_unregister_fn - module un-registration function + * called at module unload + */ +typedef void (*nss_module_unregister_fn)(ns_mtab *, u_int); +typedef ns_mtab *(*nss_module_register_fn)(const char *, u_int *, + nss_module_unregister_fn *); + +#ifdef _NS_PRIVATE + +/* + * Private data structures for back-end nsswitch implementation. + */ + +/* + * ns_dbt - `nsswitch database thang' + * For each database in /etc/nsswitch.conf there is a ns_dbt, with its + * name and a list of ns_src's containing the source information. + */ +typedef struct { + const char *name; /* name of database */ + ns_src *srclist; /* list of sources */ + u_int srclistsize; /* size of srclist */ +} ns_dbt; + +/* + * ns_mod - `nsswitch module' + */ +typedef struct { + const char *name; /* module name */ + void *handle; /* handle from dlopen() */ + ns_mtab *mtab; /* method table */ + u_int mtabsize; /* size of mtab */ + /* called to unload module */ + nss_module_unregister_fn unregister; +} ns_mod; + +#endif /* _NS_PRIVATE */ + + +#include + +__BEGIN_DECLS +int nsdispatch(void *, const ns_dtab [], const char *, + const char *, const ns_src [], ...); + +#ifdef _NS_PRIVATE +int _nsdbtaddsrc(ns_dbt *, const ns_src *); +void _nsdbtdump(const ns_dbt *); +int _nsdbtput(const ns_dbt *); +void _nsyyerror(const char *); +int _nsyylex(void); +#endif /* _NS_PRIVATE */ + +__END_DECLS + +#endif /* !_NSSWITCH_H */ diff --git a/StdLib/Include/resolv.h b/StdLib/Include/resolv.h index 818a0ea07f..d94b3cfb7f 100644 --- a/StdLib/Include/resolv.h +++ b/StdLib/Include/resolv.h @@ -112,6 +112,8 @@ struct __res_state { char pad[72]; /* on an i386 this means 512b total */ }; +typedef struct __res_state *res_state; + /* * Resolver options (keep these in synch with res_debug.c, please) */ @@ -201,6 +203,7 @@ extern const struct res_sym __p_type_syms[]; #define hostalias __hostalias #define putlong __putlong #define putshort __putshort +uint16_t _getshort(const u_char *); #define p_class __p_class #define p_time __p_time #define p_type __p_type @@ -288,6 +291,7 @@ int res_mkupdate __P((ns_updrec *, u_char *, int)); ns_updrec * res_mkupdrec __P((int, const char *, u_int, u_int, u_long)); void res_freeupdrec __P((ns_updrec *)); #endif + __END_DECLS #endif /* !_RESOLV_H_ */ diff --git a/StdLib/SocketDxe/EntryUnload.c b/StdLib/SocketDxe/EntryUnload.c index 1f550c95b3..edd991b407 100644 --- a/StdLib/SocketDxe/EntryUnload.c +++ b/StdLib/SocketDxe/EntryUnload.c @@ -29,11 +29,27 @@ CONST EFI_GUID mEslIp4ServiceGuid = { }; +/** + Tag GUID - IPv6 in use by SocketDxe +**/ +CONST EFI_GUID mEslIp6ServiceGuid = { + 0x2fc3b2d3, 0x6eba, 0x42b0, { 0xa4, 0xa7, 0x14, 0xc7, 0xa8, 0x4b, 0x5d, 0x22 } +}; + + /** Tag GUID - TCPv4 in use by SocketDxe **/ CONST EFI_GUID mEslTcp4ServiceGuid = { - 0x4dcaab0a, 0x1990, 0x4352, { 0x8d, 0x2f, 0x2d, 0x8f, 0x13, 0x55, 0x98, 0xa5 } + 0x4dcaab0a, 0x1990, 0x4352, { 0x8d, 0x2f, 0x2d, 0x8f, 0x13, 0x55, 0x98, 0xa5 } +}; + + +/** + Tag GUID - TCPv6 in use by SocketDxe +**/ +CONST EFI_GUID mEslTcp6ServiceGuid = { + 0xdd455a69, 0xec75, 0x456c, { 0x84, 0xd2, 0x95, 0xca, 0xe7, 0xd3, 0xc6, 0xd3 } }; @@ -41,7 +57,15 @@ CONST EFI_GUID mEslTcp4ServiceGuid = { Tag GUID - UDPv4 in use by SocketDxe **/ CONST EFI_GUID mEslUdp4ServiceGuid = { - 0x43a110ce, 0x9ccd, 0x402b, { 0x8c, 0x29, 0x4a, 0x6d, 0x8a, 0xf7, 0x79, 0x90 } + 0x43a110ce, 0x9ccd, 0x402b, { 0x8c, 0x29, 0x4a, 0x6d, 0x8a, 0xf7, 0x79, 0x90 } +}; + + +/** + Tag GUID - UDPv6 in use by SocketDxe +**/ +CONST EFI_GUID mEslUdp6ServiceGuid = { + 0x32ff59cd, 0xc33, 0x48d0, { 0xa2, 0x44, 0x4b, 0xb8, 0x11, 0x33, 0x64, 0x3 } }; diff --git a/StdLib/StdLib.dec b/StdLib/StdLib.dec index b31469c4a1..7a0b5513e9 100644 --- a/StdLib/StdLib.dec +++ b/StdLib/StdLib.dec @@ -40,11 +40,6 @@ gStdLibTokenSpaceGuid = { 0x447559f0, 0xd02e, 0x4cf1, { 0x99, 0xbc, 0xca, 0x11, 0x65, 0x40, 0x54, 0xc2 }} -[PcdsFixedAtBuild] - gStdLibTokenSpaceGuid.DataSource_Port|1234|UINT16|0 - gStdLibTokenSpaceGuid.WebServer_HttpPort|80|UINT16|1 - - [Protocols] gEfiSocketProtocolGuid = { 0x58e6ed63, 0x1694, 0x440b, { 0x93, 0x88, 0xe9, 0x8f, 0xed, 0x6b, 0x65, 0xaf } } gEfiSocketServiceBindingProtocolGuid = { 0x8aaedb2a, 0xa6bb, 0x47c6, { 0x94, 0xce, 0x1b, 0x80, 0x96, 0x42, 0x3f, 0x2a } } -- 2.39.2