packages feed

hpqtypes-1.2.2: libpqtypes/src/network.c

/*
 * network.c
 *   Type handler for the network data types.
 *
 * Copyright (c) 2011 eSilo, LLC. All rights reserved.
 * This is free software; see the source for copying conditions.  There is
 * NO warranty; not even for MERCHANTABILITY or  FITNESS FOR A  PARTICULAR
 * PURPOSE.
 */

#include "libpqtypes-int.h"

#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
	!defined(HAVE_GETADDRINFO))
#	include "getaddrinfo.h"
#endif

#ifndef PGSQL_AF_INET
#	define PGSQL_AF_INET	(AF_INET + 0)
#endif

#ifndef PGSQL_AF_INET6
#	define PGSQL_AF_INET6	(AF_INET + 1)
#endif

#ifndef AF_INET6
#warning NO AF_INET6 SUPPORT!
#endif

/* Some platforms don't define this, like AIX 4.3 */
#ifndef AI_NUMERICHOST
#	define AI_NUMERICHOST 0x04
#endif

/* handles cidr as well */
int
pqt_put_inet(PGtypeArgs *args)
{
	unsigned char *b = (unsigned char *)args->put.out;
	PGinet *inet = va_arg(args->ap, PGinet *);
	int family;

	PUTNULLCHK(args, inet);

	family = ((struct sockaddr *)inet->sa_buf)->sa_family;

	if (family == AF_INET)
	{
		struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf;
		*b++ = (unsigned char) PGSQL_AF_INET;
		*b++ = (unsigned char) inet->mask;
		*b++ = (unsigned char) (inet->is_cidr ? 1 : 0);
		*b++ = (unsigned char) 4;
		memcpy(b, &sa->sin_addr, 4);
		b += 4;
	}
#ifdef AF_INET6
	else if (family == AF_INET6)
	{
		struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf;
		*b++ = (unsigned char) PGSQL_AF_INET6;
		*b++ = (unsigned char) inet->mask;
		*b++ = (unsigned char) (inet->is_cidr ? 1 : 0);
		*b++ = (unsigned char) 16;
		memcpy(b, &sa->sin6_addr, 16);
		b += 16;
	}
#endif
	else
	{
		return args->errorf(args, "Unknown inet address family %d", family);
	}

	return (int) ((char *) b - args->put.out);
}

static int
get_inet2(PGtypeArgs *args, int is_cidr)
{
	DECLVALUE(args);
	unsigned short family;
	PGinet *inet = va_arg(args->ap, PGinet *);

	CHKGETVALS(args, inet);

	if (args->format == TEXTFMT)
	{
		int r;
		char *p;
		char ipstr[80];
		struct addrinfo *ai = NULL;
		struct addrinfo hints;

		pqt_strcpy(ipstr, sizeof(ipstr), value);
		if ((p = strrchr(ipstr, '/')))
		{
			*p = 0;
			inet->mask = atoi(p+1);
		}
		else
		{
			inet->mask = 32;
		}

		inet->is_cidr = is_cidr;

		/* suppress hostname lookups */
		memset(&hints, 0, sizeof(hints));
		hints.ai_flags = AI_NUMERICHOST;

		/* Without this, windows chokes with WSAHOST_NOT_FOUND */
#ifdef PQT_WIN32
		hints.ai_family = AF_INET;
#endif

		if ((r = getaddrinfo(ipstr, NULL, &hints, &ai)) || !ai)
		{
			if(r == EAI_BADFLAGS)
			{
				hints.ai_flags = 0;
				r = getaddrinfo(ipstr, NULL, &hints, &ai);
			}
			/* Another WSAHOST_NOT_FOUND work around, but for IPv6 */
#if defined(PQT_WIN32) && defined(AF_INET6)
			else if(r == WSAHOST_NOT_FOUND)
			{
				hints.ai_flags = 0;
				hints.ai_family = AF_INET6;
				r = getaddrinfo(ipstr, NULL, &hints, &ai);
			}
#endif

			if(r)
				RERR_STR2INT(args);
		}

		inet->sa_buf_len = (int) ai->ai_addrlen;
		memcpy(inet->sa_buf, ai->ai_addr, inet->sa_buf_len);

		/* Some platforms, lika AIX 4.3, do not zero this structure properly.
     * The family and port are dirty, so set the first 4 bytes to 0 and
     * then re-set the family.  I saw "0x1002" as the first 2 bytes of
     * this structure (dumb getaddrinfo), it should be "0x0002" for AF_INET.
     */
		memset(inet->sa_buf, 0, 4);
		((struct sockaddr *)inet->sa_buf)->sa_family = ai->ai_addr->sa_family;

		/* Uninitialized memory, postgres inet/cidr types don't store this.
     * Make sure its set to 0.  Another AIX problem (maybe other platforms).
		 */
#ifdef AF_INET6
		if (ai->ai_addr->sa_family == AF_INET6)
			((struct sockaddr_in6 *)inet->sa_buf)->sin6_flowinfo = 0;
#endif

		freeaddrinfo(ai);
		return 0;
	}

	family = (unsigned short) *value++;
	if (family == PGSQL_AF_INET)
	{
		struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf;
		sa->sin_family = AF_INET;
		inet->mask = (unsigned char) *value++;
		inet->is_cidr = *value++;
		memcpy(&sa->sin_addr, value + 1, *value);
		inet->sa_buf_len = (int) sizeof(struct sockaddr_in);
	}
#ifdef AF_INET6
	else if (family == PGSQL_AF_INET6)
	{
		struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf;
		sa->sin6_family = AF_INET6;
		inet->mask = (unsigned char) *value++;
		inet->is_cidr = *value++;
		memcpy(&sa->sin6_addr, value + 1, *value);
		inet->sa_buf_len = (int) sizeof(struct sockaddr_in6);
	}
#endif
	else
	{
		return args->errorf(args, "Unknown inet address family %d", family);
	}

	return 0;
}

int
pqt_get_inet(PGtypeArgs *args)
{
	return get_inet2(args, 0);
}

int
pqt_get_cidr(PGtypeArgs *args)
{
	return get_inet2(args, 1);
}

int
pqt_put_macaddr(PGtypeArgs *args)
{
	PGmacaddr *mac = va_arg(args->ap, PGmacaddr *);

	PUTNULLCHK(args, mac);

	args->put.out[0] = (unsigned char) mac->a;
	args->put.out[1] = (unsigned char) mac->b;
	args->put.out[2] = (unsigned char) mac->c;
	args->put.out[3] = (unsigned char) mac->d;
	args->put.out[4] = (unsigned char) mac->e;
	args->put.out[5] = (unsigned char) mac->f;
	return 6;
}

int
pqt_get_macaddr(PGtypeArgs *args)
{
	DECLVALUE(args);
	unsigned char *p;
	PGmacaddr *mac = va_arg(args->ap, PGmacaddr *);

	CHKGETVALS(args, mac);

	if (args->format == TEXTFMT)
	{
		int a,b,c,d,e,f;
		int count = sscanf(value, "%x:%x:%x:%x:%x:%x", &a,&b,&c,&d,&e,&f);

		if (count != 6 || (a < 0) || (a > 255) || (b < 0) || (b > 255) ||
				(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
				(e < 0) || (e > 255) || (f < 0) || (f > 255))
			RERR_STR2INT(args);

		mac->a = a;
		mac->b = b;
		mac->c = c;
		mac->d = d;
		mac->e = e;
		mac->f = f;
		return 0;
	}

	p = (unsigned char *) value;
	mac->a = *p++;
	mac->b = *p++;
	mac->c = *p++;
	mac->d = *p++;
	mac->e = *p++;
	mac->f = *p;
	return 0;
}