getaddrinfo.c 9.18 KB
Newer Older
1 2 3 4 5
/*-------------------------------------------------------------------------
 *
 * getaddrinfo.c
 *	  Support getaddrinfo() on platforms that don't have it.
 *
6
 * We also supply getnameinfo() here, assuming that the platform will have
Bruce Momjian's avatar
Bruce Momjian committed
7
 * it if and only if it has getaddrinfo().	If this proves false on some
8 9
 * platform, we'll need to split this file and provide a separate configure
 * test for getnameinfo().
10
 *
11 12 13 14
 * Windows may or may not have these routines, so we handle Windows special
 * by dynamically checking for their existence.  If they already exist, we
 * use the Windows native routines, but if not, we use our own.
 *
15
 *
16
 * Copyright (c) 2003-2005, PostgreSQL Global Development Group
17 18
 *
 * IDENTIFICATION
19
 *	  $PostgreSQL: pgsql/src/port/getaddrinfo.c,v 1.22 2005/12/08 17:52:11 momjian Exp $
20 21 22 23 24
 *
 *-------------------------------------------------------------------------
 */

/* This is intended to be used in both frontend and backend, so use c.h */
25
#include "c.h"
26

27
#ifndef WIN32_CLIENT_ONLY
28
#include <sys/socket.h>
29
#include <netdb.h>
30 31
#include <netinet/in.h>
#include <arpa/inet.h>
32
#endif
33

34 35
#include "getaddrinfo.h"

36 37 38 39 40 41 42

#ifdef WIN32

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

43
#if !defined(WIN32_CLIENT_ONLY)
44 45 46 47 48 49
/*
 * The native routines may or may not exist on the Windows platform we are on,
 * so we dynamically look up the routines, and call them via function pointers.
 * Here we need to declare what the function pointers look like
 */
typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename,
50 51 52
														 const char *servname,
											   const struct addrinfo * hints,
													 struct addrinfo ** res);
53 54 55 56

typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai);

typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa,
57 58 59 60
														 int salen,
													 char *host, int hostlen,
													 char *serv, int servlen,
														 int flags);
61 62 63 64 65 66 67 68 69 70 71

/* static pointers to the native routines, so we only do the lookup once. */
static getaddrinfo_ptr_t getaddrinfo_ptr = NULL;
static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL;
static getnameinfo_ptr_t getnameinfo_ptr = NULL;


static bool
haveNativeWindowsIPv6routines(void)
{
	void	   *hLibrary = NULL;
72
	static bool alreadyLookedForIpv6routines = false;
73 74 75 76 77

	if (alreadyLookedForIpv6routines)
		return (getaddrinfo_ptr != NULL);

	/*
78 79
	 * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines
	 * are present in the WinSock 2 library (ws2_32.dll). Try that first
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
	 */

	hLibrary = LoadLibraryA("ws2_32");

	if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL)
	{
		/*
		 * Well, ws2_32 doesn't exist, or more likely doesn't have
		 * getaddrinfo.
		 */
		if (hLibrary != NULL)
			FreeLibrary(hLibrary);

		/*
		 * In Windows 2000, there was only the IPv6 Technology Preview look in
		 * the IPv6 WinSock library (wship6.dll).
		 */

		hLibrary = LoadLibraryA("wship6");
	}

	/* If hLibrary is null, we couldn't find a dll with functions */
	if (hLibrary != NULL)
	{
		/* We found a dll, so now get the addresses of the routines */

106 107 108
		getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary,
															 "getaddrinfo");
		freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary,
109
															 "freeaddrinfo");
110 111
		getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary,
															 "getnameinfo");
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

		/*
		 * If any one of the routines is missing, let's play it safe and
		 * ignore them all
		 */
		if (getaddrinfo_ptr == NULL ||
			freeaddrinfo_ptr == NULL ||
			getnameinfo_ptr == NULL)
		{
			FreeLibrary(hLibrary);
			hLibrary = NULL;
			getaddrinfo_ptr = NULL;
			freeaddrinfo_ptr = NULL;
			getnameinfo_ptr = NULL;
		}
	}

129
	alreadyLookedForIpv6routines = true;
130 131 132
	return (getaddrinfo_ptr != NULL);
}
#endif
133
#endif
134 135


Bruce Momjian's avatar
Bruce Momjian committed
136 137 138 139 140 141 142 143
/*
 * get address info for ipv4 sockets.
 *
 *	Bugs:	- only one addrinfo is set even though hintp is NULL or
 *		  ai_socktype is 0
 *		- AI_CANONNAME is not supported.
 *		- servname can only be a number, not text.
 */
144 145
int
getaddrinfo(const char *node, const char *service,
Bruce Momjian's avatar
Bruce Momjian committed
146 147
			const struct addrinfo * hintp,
			struct addrinfo ** res)
148
{
Bruce Momjian's avatar
Bruce Momjian committed
149 150 151 152
	struct addrinfo *ai;
	struct sockaddr_in sin,
			   *psin;
	struct addrinfo hints;
Bruce Momjian's avatar
Bruce Momjian committed
153

154
#if defined(WIN32) && !defined(WIN32_CLIENT_ONLY)
155

156 157 158 159 160 161 162 163
	/*
	 * If Windows has native IPv6 support, use the native Windows routine.
	 * Otherwise, fall through and use our own code.
	 */
	if (haveNativeWindowsIPv6routines())
		return (*getaddrinfo_ptr) (node, service, hintp, res);
#endif

Bruce Momjian's avatar
Bruce Momjian committed
164
	if (hintp == NULL)
Bruce Momjian's avatar
Bruce Momjian committed
165 166 167 168 169 170 171
	{
		memset(&hints, 0, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
	}
	else
		memcpy(&hints, hintp, sizeof(hints));
172

Bruce Momjian's avatar
Bruce Momjian committed
173
	if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
174 175
		return EAI_FAMILY;

Bruce Momjian's avatar
Bruce Momjian committed
176 177
	if (hints.ai_socktype == 0)
		hints.ai_socktype = SOCK_STREAM;
178 179 180 181

	if (!node && !service)
		return EAI_NONAME;

182 183 184 185
	memset(&sin, 0, sizeof(sin));

	sin.sin_family = AF_INET;

186 187 188 189
	if (node)
	{
		if (node[0] == '\0')
			sin.sin_addr.s_addr = htonl(INADDR_ANY);
Bruce Momjian's avatar
Bruce Momjian committed
190
		else if (hints.ai_flags & AI_NUMERICHOST)
191
		{
Bruce Momjian's avatar
Bruce Momjian committed
192 193
			if (!inet_aton(node, &sin.sin_addr))
				return EAI_FAIL;
194 195 196 197
		}
		else
		{
			struct hostent *hp;
Bruce Momjian's avatar
Bruce Momjian committed
198

199 200
#ifdef FRONTEND
			struct hostent hpstr;
201
			char		buf[BUFSIZ];
Bruce Momjian's avatar
Bruce Momjian committed
202
			int			herrno = 0;
203

204
			pqGethostbyname(node, &hpstr, buf, sizeof(buf),
Bruce Momjian's avatar
Bruce Momjian committed
205
							&hp, &herrno);
206
#else
207
			hp = gethostbyname(node);
208
#endif
209 210 211 212 213 214
			if (hp == NULL)
			{
				switch (h_errno)
				{
					case HOST_NOT_FOUND:
					case NO_DATA:
Bruce Momjian's avatar
Bruce Momjian committed
215
						return EAI_NONAME;
216 217 218 219 220 221 222 223
					case TRY_AGAIN:
						return EAI_AGAIN;
					case NO_RECOVERY:
					default:
						return EAI_FAIL;
				}
			}
			if (hp->h_addrtype != AF_INET)
Bruce Momjian's avatar
Bruce Momjian committed
224
				return EAI_FAIL;
225

226
			memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
227 228 229 230
		}
	}
	else
	{
Bruce Momjian's avatar
Bruce Momjian committed
231
		if (hints.ai_flags & AI_PASSIVE)
232 233
			sin.sin_addr.s_addr = htonl(INADDR_ANY);
		else
234 235 236 237
			sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	}

	if (service)
238
		sin.sin_port = htons((unsigned short) atoi(service));
239 240 241

#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
	sin.sin_len = sizeof(sin);
Bruce Momjian's avatar
Bruce Momjian committed
242
#endif
243 244 245 246

	ai = malloc(sizeof(*ai));
	if (!ai)
		return EAI_MEMORY;
Bruce Momjian's avatar
Bruce Momjian committed
247

248 249 250 251 252 253 254
	psin = malloc(sizeof(*psin));
	if (!psin)
	{
		free(ai);
		return EAI_MEMORY;
	}

255
	memcpy(psin, &sin, sizeof(*psin));
256

Bruce Momjian's avatar
Bruce Momjian committed
257
	ai->ai_flags = 0;
258
	ai->ai_family = AF_INET;
Bruce Momjian's avatar
Bruce Momjian committed
259 260
	ai->ai_socktype = hints.ai_socktype;
	ai->ai_protocol = hints.ai_protocol;
261 262 263 264 265 266 267 268 269 270 271 272
	ai->ai_addrlen = sizeof(*psin);
	ai->ai_addr = (struct sockaddr *) psin;
	ai->ai_canonname = NULL;
	ai->ai_next = NULL;

	*res = ai;

	return 0;
}


void
Bruce Momjian's avatar
Bruce Momjian committed
273
freeaddrinfo(struct addrinfo * res)
274 275 276
{
	if (res)
	{
277
#if defined(WIN32) && !defined(WIN32_CLIENT_ONLY)
278

279 280 281 282 283 284
		/*
		 * If Windows has native IPv6 support, use the native Windows routine.
		 * Otherwise, fall through and use our own code.
		 */
		if (haveNativeWindowsIPv6routines())
		{
285
			(*freeaddrinfo_ptr) (res);
286 287 288 289
			return;
		}
#endif

290 291 292 293 294 295 296
		if (res->ai_addr)
			free(res->ai_addr);
		free(res);
	}
}


297
const char *
298 299
gai_strerror(int errcode)
{
300
#ifdef HAVE_HSTRERROR
Bruce Momjian's avatar
Bruce Momjian committed
301
	int			hcode;
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

	switch (errcode)
	{
		case EAI_NONAME:
			hcode = HOST_NOT_FOUND;
			break;
		case EAI_AGAIN:
			hcode = TRY_AGAIN;
			break;
		case EAI_FAIL:
		default:
			hcode = NO_RECOVERY;
			break;
	}

	return hstrerror(hcode);
318
#else							/* !HAVE_HSTRERROR */
319 320 321 322 323 324 325

	switch (errcode)
	{
		case EAI_NONAME:
			return "Unknown host";
		case EAI_AGAIN:
			return "Host name lookup failure";
326
			/* Errors below are probably WIN32 only */
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
#ifdef EAI_BADFLAGS
		case EAI_BADFLAGS:
			return "Invalid argument";
#endif
#ifdef EAI_FAMILY
		case EAI_FAMILY:
			return "Address family not supported";
#endif
#ifdef EAI_MEMORY
		case EAI_MEMORY:
			return "Not enough memory";
#endif
#ifdef EAI_NODATA
		case EAI_NODATA:
			return "No host data of that type was found";
#endif
#ifdef EAI_SERVICE
		case EAI_SERVICE:
			return "Class type not found";
#endif
#ifdef EAI_SOCKTYPE
		case EAI_SOCKTYPE:
			return "Socket type not supported";
#endif
351 352 353
		default:
			return "Unknown server error";
	}
Bruce Momjian's avatar
Bruce Momjian committed
354
#endif   /* HAVE_HSTRERROR */
355
}
Bruce Momjian's avatar
Bruce Momjian committed
356 357

/*
358
 * Convert an ipv4 address to a hostname.
Bruce Momjian's avatar
Bruce Momjian committed
359
 *
Bruce Momjian's avatar
Bruce Momjian committed
360 361 362 363 364
 * Bugs:	- Only supports NI_NUMERICHOST and NI_NUMERICSERV
 *		  It will never resolv a hostname.
 *		- No IPv6 support.
 */
int
Bruce Momjian's avatar
Bruce Momjian committed
365
getnameinfo(const struct sockaddr * sa, int salen,
366 367
			char *node, int nodelen,
			char *service, int servicelen, int flags)
Bruce Momjian's avatar
Bruce Momjian committed
368
{
369
#if defined(WIN32) && !defined(WIN32_CLIENT_ONLY)
370

371 372 373 374 375 376 377 378 379
	/*
	 * If Windows has native IPv6 support, use the native Windows routine.
	 * Otherwise, fall through and use our own code.
	 */
	if (haveNativeWindowsIPv6routines())
		return (*getnameinfo_ptr) (sa, salen, node, nodelen,
								   service, servicelen, flags);
#endif

Bruce Momjian's avatar
Bruce Momjian committed
380 381 382 383 384 385 386 387 388 389
	/* Invalid arguments. */
	if (sa == NULL || (node == NULL && service == NULL))
		return EAI_FAIL;

	/* We don't support those. */
	if ((node && !(flags & NI_NUMERICHOST))
		|| (service && !(flags & NI_NUMERICSERV)))
		return EAI_FAIL;

#ifdef	HAVE_IPV6
390
	if (sa->sa_family == AF_INET6)
Bruce Momjian's avatar
Bruce Momjian committed
391
		return EAI_FAMILY;
Bruce Momjian's avatar
Bruce Momjian committed
392 393
#endif

394
	if (node)
Bruce Momjian's avatar
Bruce Momjian committed
395
	{
Bruce Momjian's avatar
Bruce Momjian committed
396
		int			ret = -1;
397

398
		if (sa->sa_family == AF_INET)
Bruce Momjian's avatar
Bruce Momjian committed
399
		{
Bruce Momjian's avatar
Bruce Momjian committed
400 401 402
			char	   *p;

			p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr);
403
			ret = snprintf(node, nodelen, "%s", p);
Bruce Momjian's avatar
Bruce Momjian committed
404
		}
405
		if (ret == -1 || ret > nodelen)
Bruce Momjian's avatar
Bruce Momjian committed
406 407 408
			return EAI_MEMORY;
	}

409
	if (service)
Bruce Momjian's avatar
Bruce Momjian committed
410
	{
Bruce Momjian's avatar
Bruce Momjian committed
411
		int			ret = -1;
412

413
		if (sa->sa_family == AF_INET)
Bruce Momjian's avatar
Bruce Momjian committed
414
		{
415
			ret = snprintf(service, servicelen, "%d",
Bruce Momjian's avatar
Bruce Momjian committed
416
						   ntohs(((struct sockaddr_in *) sa)->sin_port));
Bruce Momjian's avatar
Bruce Momjian committed
417
		}
418
		if (ret == -1 || ret > servicelen)
Bruce Momjian's avatar
Bruce Momjian committed
419 420 421 422 423
			return EAI_MEMORY;
	}

	return 0;
}