view sock.c @ 806:12a3139a2baf

sock: use atomic_cas_ptr correctly This bug was caused by incorrect documentation (already fixed). It resulted in us freeing the name on successful CAS and caching the freed pointer. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Fri, 10 Apr 2020 12:27:10 -0400
parents 32d261e8b47f
children de55ae9a8839
line wrap: on
line source

/*
 * Copyright (c) 2016-2020 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 <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;
}