view sock.c @ 883:2f57e0de2892 default tip

Added signature for changeset ee2327236830 Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sun, 12 May 2024 11:28:48 -0400
parents de55ae9a8839
children
line wrap: on
line source

/*
 * Copyright (c) 2016-2020,2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>

#include <jeffpc/sock.h>
#include <jeffpc/atomic.h>
#include <jeffpc/config.h>

const char *xgethostname(void)
{
	static const char *cached;
	char *name;
	ssize_t len;
	int ret;

	if (cached)
		return cached;

#if defined(HOST_NAME_MAX)
	len = HOST_NAME_MAX;
#elif defined(MAXHOSTNAMELEN)
	len = MAXHOSTNAMELEN;
#else
	len = sysconf(_SC_HOST_NAME_MAX);
	if (len < 0)
		goto unknown;
#endif

	len++; /* space for the trailing nul */

	name = malloc(len);
	if (!name)
		goto unknown;

	ret = gethostname(name, len);
	if (ret) {
		free(name);
		goto unknown;
	}

	if (atomic_cas_ptr(&cached, NULL, name) != NULL)
		free(name);

	return cached;

unknown:
	return "<unknown>";
}

int connect_ip(const char *host, uint16_t port, bool v4, bool v6, enum ip_type type)
{
	struct addrinfo hints, *res, *p;
	char strport[6];
	int sock;

	if (!host || !port || (!v4 && !v6) || (type != IP_TCP))
		return -EINVAL;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;

	snprintf(strport, sizeof(strport), "%hu", port);

	switch (getaddrinfo(host, strport, &hints, &res)) {
		case 0:
			/* success */
			break;
#ifdef JEFFPC_HAVE_EAI_ADDRFAMILY
		case EAI_ADDRFAMILY:
#endif
		case EAI_FAMILY:
		case EAI_SERVICE:
		case EAI_SOCKTYPE:
			return -ENOTSUP;
		case EAI_AGAIN:
			return -EAGAIN;
		case EAI_BADFLAGS:
			return -EINVAL;
		case EAI_MEMORY:
			return -ENOMEM;
#ifdef JEFFPC_HAVE_EAI_NODATA
		case EAI_NODATA:
#endif
		case EAI_NONAME:
			return -ENOENT;
		case EAI_OVERFLOW:
			return -EOVERFLOW;
		case EAI_SYSTEM:
			return -errno;
		default:
			/* TODO: is this the best errno we can return? */
			return -ENOENT;
	}

	sock = -ENOENT;

	for (p = res; p; p = p->ai_next) {
		switch (p->ai_family) {
			case AF_INET:
				if (!v4)
					continue;
				break;
			case AF_INET6:
				if (!v6)
					continue;
				break;
			default:
				continue;
		}

		sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
		if (sock == -1) {
			sock = -errno;
			continue;
		}

		if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
			close(sock);
			sock = -errno;
			continue;
		}

		/* success! */
		break;
	}

	freeaddrinfo(res);

	return sock;
}

const char *xsockaddr_ntop(const struct sockaddr *sa, char *str, size_t strlen)
{
	const union xsockaddr *u = (const union xsockaddr *) sa;

	switch (u->sa.sa_family) {
		case AF_INET:
			return inet_ntop(AF_INET, &u->inet.sin_addr, str,
					 strlen);
		case AF_INET6:
			return inet_ntop(AF_INET6, &u->inet6.sin6_addr, str,
					 strlen);
	}

	panic("Unknown address family %d", u->sa.sa_family);
}

bool xsockaddr_cmp(struct sockaddr *sa1, struct sockaddr *sa2)
{
	if (sa1->sa_family != sa2->sa_family)
		return false;

	/*
	 * NOTE: Some systems (e.g., FreeBSD) have sa_len which we could
	 * sanity check, but other systems (e.g., Linux) do not have it.  We
	 * could work around this with conditional compilation, but that's
	 * too much effort with no obvious benefit.  So, just imagine that
	 * we assert here that sa1->sa_len == sa2->sa_len.
	 */

	switch (sa1->sa_family) {
		case AF_INET: {
			struct sockaddr_in *in1 = (struct sockaddr_in *) sa1;
			struct sockaddr_in *in2 = (struct sockaddr_in *) sa2;

			return in1->sin_addr.s_addr == in2->sin_addr.s_addr;
		}
		case AF_INET6: {
			struct sockaddr_in6 *in1 = (struct sockaddr_in6 *) sa1;
			struct sockaddr_in6 *in2 = (struct sockaddr_in6 *) sa2;

			return IN6_ARE_ADDR_EQUAL(&in1->sin6_addr,
						  &in2->sin6_addr);
		}
	}

	panic("Unknown address family %d", sa1->sa_family);
}

void xsockaddr_copy(union xsockaddr *u, struct sockaddr *sa)
{
	size_t len;

	switch (sa->sa_family) {
		case AF_INET:
			len = sizeof(u->inet);
			break;
		case AF_INET6:
			len = sizeof(u->inet6);
			break;
		default:
			panic("Unknown address family %d", sa->sa_family);
	}

	memcpy(u, sa, len);
}