int getnameinfo(const struct sockaddr *sa,
		socklen_t addrlen,
		char *host,
		socklen_t hostlen,
		char *serv,
		socklen_t servlen,
		unsigned flags)
{
	int serrno = errno;
	bool ok = 0;
	struct hostent *hoste = NULL;
	char domain[256];

	if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM))
		return EAI_BADFLAGS;

	if (sa == NULL || addrlen < sizeof(sa_family_t))
		return EAI_FAMILY;

	if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
		return EAI_NONAME;

	if (sa->sa_family == AF_LOCAL) /* valid */;
#ifdef __UCLIBC_HAS_IPV4__
	else if (sa->sa_family == AF_INET) {
		if (addrlen < sizeof(struct sockaddr_in))
			return EAI_FAMILY;
	}
#endif
#ifdef __UCLIBC_HAS_IPV6__
	else if (sa->sa_family == AF_INET6) {
		if (addrlen < sizeof(struct sockaddr_in6))
			return EAI_FAMILY;
	}
#endif
	else
		return EAI_FAMILY;

	if (host != NULL && hostlen > 0)
		switch (sa->sa_family) {
		case AF_INET:
#ifdef __UCLIBC_HAS_IPV6__
		case AF_INET6:
#endif
			if (!(flags & NI_NUMERICHOST)) {
				if (0) /* nothing */;
#ifdef __UCLIBC_HAS_IPV6__
				else if (sa->sa_family == AF_INET6)
					hoste = gethostbyaddr((const void *)
						&(((const struct sockaddr_in6 *) sa)->sin6_addr),
						sizeof(struct in6_addr), AF_INET6);
#endif
#ifdef __UCLIBC_HAS_IPV4__
				else
					hoste = gethostbyaddr((const void *)
						&(((const struct sockaddr_in *)sa)->sin_addr),
						sizeof(struct in_addr), AF_INET);
#endif

				if (hoste) {
					char *c;
					if ((flags & NI_NOFQDN)
					 && (getdomainname(domain, sizeof(domain)) == 0)
					 && (c = strstr(hoste->h_name, domain)) != NULL
					 && (c != hoste->h_name) && (*(--c) == '.')
					) {
						strncpy(host, hoste->h_name,
							MIN(hostlen, (size_t) (c - hoste->h_name)));
						host[MIN(hostlen - 1, (size_t) (c - hoste->h_name))] = '\0';
					} else {
						strncpy(host, hoste->h_name, hostlen);
					}
					ok = 1;
				}
			}

			if (!ok) {
				const char *c = NULL;

				if (flags & NI_NAMEREQD) {
					errno = serrno;
					return EAI_NONAME;
				}
				if (0) /* nothing */;
#ifdef __UCLIBC_HAS_IPV6__
				else if (sa->sa_family == AF_INET6) {
					const struct sockaddr_in6 *sin6p;

					sin6p = (const struct sockaddr_in6 *) sa;
					c = inet_ntop(AF_INET6,
						(const void *) &sin6p->sin6_addr,
						host, hostlen);
#if 0
					/* Does scope id need to be supported? */
					uint32_t scopeid;
					scopeid = sin6p->sin6_scope_id;
					if (scopeid != 0) {
						/* Buffer is >= IFNAMSIZ+1.  */
						char scopebuf[IFNAMSIZ + 1];
						char *scopeptr;
						int ni_numericscope = 0;
						size_t real_hostlen = strnlen(host, hostlen);
						size_t scopelen = 0;

						scopebuf[0] = SCOPE_DELIMITER;
						scopebuf[1] = '\0';
						scopeptr = &scopebuf[1];

						if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)
						    || IN6_IS_ADDR_MC_LINKLOCAL(&sin6p->sin6_addr)) {
							if (if_indextoname(scopeid, scopeptr) == NULL)
								++ni_numericscope;
							else
								scopelen = strlen(scopebuf);
						} else {
							++ni_numericscope;
						}

						if (ni_numericscope)
							scopelen = 1 + snprintf(scopeptr,
								(scopebuf
								+ sizeof scopebuf
								- scopeptr),
								"%u", scopeid);

						if (real_hostlen + scopelen + 1 > hostlen)
							return EAI_SYSTEM;
						memcpy(host + real_hostlen, scopebuf, scopelen + 1);
					}
#endif
				}
#endif /* __UCLIBC_HAS_IPV6__ */
#if defined __UCLIBC_HAS_IPV4__
				else {
					c = inet_ntop(AF_INET, (const void *)
						&(((const struct sockaddr_in *) sa)->sin_addr),
						host, hostlen);
				}
#endif
				if (c == NULL) {
					errno = serrno;
					return EAI_SYSTEM;
				}
				ok = 1;
			}
			break;

		case AF_LOCAL:
			if (!(flags & NI_NUMERICHOST)) {
				struct utsname utsname;

				if (!uname(&utsname)) {
					strncpy(host, utsname.nodename, hostlen);
					break;
				};
			};

			if (flags & NI_NAMEREQD) {
				errno = serrno;
				return EAI_NONAME;
			}

			strncpy(host, "localhost", hostlen);
			break;
/* Already checked above
		default:
			return EAI_FAMILY;
*/
	}

	if (serv && (servlen > 0)) {
		if (sa->sa_family == AF_LOCAL) {
			strncpy(serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
		} else { /* AF_INET || AF_INET6 */
			if (!(flags & NI_NUMERICSERV)) {
				struct servent *s;
				s = getservbyport(((const struct sockaddr_in *) sa)->sin_port,
				      ((flags & NI_DGRAM) ? "udp" : "tcp"));
				if (s) {
					strncpy(serv, s->s_name, servlen);
					goto DONE;
				}
			}
			snprintf(serv, servlen, "%d",
				ntohs(((const struct sockaddr_in *) sa)->sin_port));
		}
	}
DONE:
	if (host && (hostlen > 0))
		host[hostlen-1] = 0;
	if (serv && (servlen > 0))
		serv[servlen-1] = 0;
	errno = serrno;
	return 0;
}