1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2006,2008 Oracle.  All rights reserved.
5 *
6 * $Id: os_addrinfo.c,v 1.11 2008/01/08 20:58:43 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#define	__INCLUDE_NETWORKING	1
12#include "db_int.h"
13
14/*
15 * __os_getaddrinfo and __os_freeaddrinfo wrap the getaddrinfo and freeaddrinfo
16 * calls, as well as the associated platform dependent error handling, mapping
17 * the error return to a ANSI C/POSIX error return.
18 */
19
20/*
21 * __os_getaddrinfo --
22 *
23 * PUBLIC: #if defined(HAVE_REPLICATION_THREADS)
24 * PUBLIC: int __os_getaddrinfo __P((ENV *, const char *, u_int,
25 * PUBLIC:    const char *, const ADDRINFO *, ADDRINFO **));
26 * PUBLIC: #endif
27 */
28int
29__os_getaddrinfo(env, nodename, port, servname, hints, res)
30	ENV *env;
31	const char *nodename, *servname;
32	u_int port;
33	const ADDRINFO *hints;
34	ADDRINFO **res;
35{
36#ifdef HAVE_GETADDRINFO
37	int ret;
38
39	if ((ret = getaddrinfo(nodename, servname, hints, res)) == 0)
40		return (0);
41
42	__db_errx(env, "%s(%u): host lookup failed: %s",
43	    nodename == NULL ? "" : nodename, port,
44#ifdef DB_WIN32
45	    gai_strerrorA(ret));
46#else
47	    gai_strerror(ret));
48#endif
49	return (__os_posix_err(ret));
50#else
51	ADDRINFO *answer;
52	struct hostent *hostaddr;
53	struct sockaddr_in sin;
54	u_int32_t tmpaddr;
55	int ret;
56
57	COMPQUIET(hints, NULL);
58	COMPQUIET(servname, NULL);
59
60	/* INADDR_NONE is not defined on Solaris 2.6, 2.7 or 2.8. */
61#ifndef	INADDR_NONE
62#define	INADDR_NONE	((u_long)0xffffffff)
63#endif
64
65	/*
66	 * Basic implementation of IPv4 component of getaddrinfo.
67	 * Limited to the functionality used by repmgr.
68	 */
69	memset(&sin, 0, sizeof(sin));
70	sin.sin_family = AF_INET;
71	if (nodename) {
72		if (nodename[0] == '\0')
73			sin.sin_addr.s_addr = htonl(INADDR_ANY);
74		else if ((tmpaddr = inet_addr(nodename)) != INADDR_NONE) {
75			sin.sin_addr.s_addr = tmpaddr;
76		} else {
77			hostaddr = gethostbyname(nodename);
78			if (hostaddr == NULL) {
79#ifdef DB_WIN32
80				ret = __os_get_neterr();
81				__db_syserr(env, ret,
82				    "%s(%u): host lookup failed",
83				    nodename == NULL ? "" : nodename, port);
84				return (__os_posix_err(ret));
85#else
86				/*
87				 * Historic UNIX systems used the h_errno
88				 * global variable to return gethostbyname
89				 * errors.  The only function we currently
90				 * use that needs h_errno is gethostbyname,
91				 * so we deal with it here.
92				 *
93				 * hstrerror is not available on Solaris 2.6
94				 * (it is in libresolv but is a private,
95				 * unexported symbol).
96				 */
97#ifdef HAVE_HSTRERROR
98				__db_errx(env,
99				    "%s(%u): host lookup failed: %s",
100				    nodename == NULL ? "" : nodename, port,
101				    hstrerror(h_errno));
102#else
103				__db_errx(env,
104				    "%s(%u): host lookup failed: %d",
105				    nodename == NULL ? "" : nodename, port,
106				    h_errno);
107#endif
108				switch (h_errno) {
109				case HOST_NOT_FOUND:
110				case NO_DATA:
111					return (EHOSTUNREACH);
112				case TRY_AGAIN:
113					return (EAGAIN);
114				case NO_RECOVERY:
115				default:
116					return (EFAULT);
117				}
118				/* NOTREACHED */
119#endif
120			}
121			memcpy(&(sin.sin_addr),
122			    hostaddr->h_addr, (size_t)hostaddr->h_length);
123		}
124	} else					/* No host specified. */
125		sin.sin_addr.s_addr = htonl(INADDR_ANY);
126	sin.sin_port = htons((u_int16_t)port);
127
128	if ((ret = __os_calloc(env, 1, sizeof(ADDRINFO), &answer)) != 0)
129		return (ret);
130	if ((ret = __os_malloc(env, sizeof(sin), &answer->ai_addr)) != 0) {
131		__os_free(env, answer);
132		return (ret);
133	}
134
135	answer->ai_family = AF_INET;
136	answer->ai_protocol = IPPROTO_TCP;
137	answer->ai_socktype = SOCK_STREAM;
138	answer->ai_addrlen = sizeof(sin);
139	memcpy(answer->ai_addr, &sin, sizeof(sin));
140	*res = answer;
141
142	return (0);
143#endif /* HAVE_GETADDRINFO */
144}
145
146/*
147 * __os_freeaddrinfo --
148 *
149 * PUBLIC: #if defined(HAVE_REPLICATION_THREADS)
150 * PUBLIC: void __os_freeaddrinfo __P((ENV *, ADDRINFO *));
151 * PUBLIC: #endif
152 */
153void
154__os_freeaddrinfo(env, ai)
155	ENV *env;
156	ADDRINFO *ai;
157{
158#ifdef HAVE_GETADDRINFO
159	COMPQUIET(env, NULL);
160
161	freeaddrinfo(ai);
162#else
163	ADDRINFO *next, *tmpaddr;
164
165	for (next = ai; next != NULL; next = tmpaddr) {
166		if (next->ai_canonname != NULL)
167			__os_free(env, next->ai_canonname);
168
169		if (next->ai_addr != NULL)
170			__os_free(env, next->ai_addr);
171
172		tmpaddr = next->ai_next;
173		__os_free(env, next);
174	}
175#endif
176}
177