diff options
-rw-r--r-- | StdLib/BsdSocketLib/BsdSocketLib.inf | 9 | ||||
-rw-r--r-- | StdLib/BsdSocketLib/getaddrinfo.c | 1964 | ||||
-rw-r--r-- | StdLib/BsdSocketLib/getnameinfo.c | 567 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/EfiSocketLib.inf | 8 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Ip4.c | 1 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Socket.c | 103 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Socket.h | 125 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Tcp4.c | 1 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Tcp6.c | 2329 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Udp4.c | 1 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/Udp6.c | 1094 | ||||
-rw-r--r-- | StdLib/EfiSocketLib/UseEfiSocketLib.c | 31 | ||||
-rw-r--r-- | StdLib/Include/Efi/EfiSocketLib.h | 5 | ||||
-rw-r--r-- | StdLib/Include/net/if_dl.h | 4 | ||||
-rw-r--r-- | StdLib/Include/net/servent.h | 60 | ||||
-rw-r--r-- | StdLib/Include/nsswitch.h | 237 | ||||
-rw-r--r-- | StdLib/Include/resolv.h | 4 | ||||
-rw-r--r-- | StdLib/SocketDxe/EntryUnload.c | 28 | ||||
-rw-r--r-- | StdLib/StdLib.dec | 5 |
19 files changed, 6551 insertions, 25 deletions
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 <sys/cdefs.h>
+#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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <stdarg.h>
+#include <nsswitch.h>
+#include <resolv.h>
+
+//#ifdef YP
+//#include <rpc/rpc.h>
+//#include <rpcsvc/yp_prot.h>
+//#include <rpcsvc/ypclnt.h>
+//#endif
+
+#include <net/servent.h>
+
+#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 <scoped_address><delimiter><scope id>
+ */
+ 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:"<Nil>");
+#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 <sys/cdefs.h>
+#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 <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+//#include <net/if_ieee1394.h>
+//#include <net/if_types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <assert.h>
+#include <limits.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <net/servent.h>
+
+#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 <numeric-addr><delim><zoneid> */
+ 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" ));
@@ -3248,6 +3272,22 @@ EslSocketPoll ( }
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
//
if ( 0 != pSocket->FifoDepth ) {
@@ -4367,6 +4407,11 @@ EslSocketReceive ( //
if ( SOCKET_STATE_CONNECTED == pSocket->State ) {
//
+ // Poll the network to increase performance
+ //
+ EslSocketRxPoll ( pSocket );
+
+ //
// Locate the port
//
pPort = pSocket->pPortList;
@@ -4848,6 +4893,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
This routine posts a receive buffer to the network adapter.
@@ -5292,6 +5380,11 @@ EslSocketTransmit ( RAISE_TPL ( TplPrevious, TPL_SOCKETS );
//
+ // Poll the network to increase performance
+ //
+ EslSocketRxPoll ( pSocket );
+
+ //
// Attempt to buffer the packet for transmission
//
Status = pSocket->pApi->pfnTransmit ( pSocket,
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 @@ -138,6 +138,26 @@ typedef struct /**
+ 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.
**/
typedef struct
@@ -160,6 +180,28 @@ typedef struct /**
+ 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.
**/
typedef struct _ESL_PACKET {
@@ -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;
@@ -257,6 +307,26 @@ typedef struct { } 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
The driver uses this structure to manage the UDP4 connections.
@@ -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.
@@ -302,6 +384,21 @@ EFI_STATUS );
/**
+ 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
The driver uses this structure to manager the socket's connection
@@ -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;
@@ -1424,6 +1531,24 @@ 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
+ );
+
+/**
Start a receive operation
This routine posts a receive buffer to the network adapter.
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 @@ -29,6 +29,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
**/
CONST EFI_GUID mEslTcp4ServiceGuid = {
@@ -37,6 +45,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
**/
CONST EFI_GUID mEslUdp4ServiceGuid = {
@@ -45,6 +61,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
This routine creates the ::ESL_SOCKET structure and returns
@@ -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 <Protocol/EfiSocket.h>
#include <Protocol/ServiceBinding.h>
#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
#include <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
#include <sys/time.h>
@@ -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 <sys/EfiCdefs.h>
__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 <stdio.h>
+
+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
+ * (<machine/varargs.h> and <machine/stdarg.h>), 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 <machine/ansi.h> and use it.
+ */
+#include <machine/ansi.h>
+#include <sys/types.h>
+
+#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 <sys/cdefs.h>
+
+__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 @@ -30,10 +30,26 @@ 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 } }
|