summaryrefslogtreecommitdiff
path: root/src/os/os_addrinfo.c
blob: 205f41ec0fa8d22c437b1114bc2c57d50538d64c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2006, 2012 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#include "db_config.h"

#include "db_int.h"

/*
 * __os_getaddrinfo and __os_freeaddrinfo wrap the getaddrinfo and freeaddrinfo
 * calls, as well as the associated platform dependent error handling, mapping
 * the error return to a ANSI C/POSIX error return.
 */

/*
 * __os_getaddrinfo --
 *
 * PUBLIC: #if defined(HAVE_REPLICATION_THREADS)
 * PUBLIC: int __os_getaddrinfo __P((ENV *, const char *, u_int,
 * PUBLIC:    const char *, const ADDRINFO *, ADDRINFO **));
 * PUBLIC: #endif
 */
int
__os_getaddrinfo(env, nodename, port, servname, hints, res)
	ENV *env;
	const char *nodename, *servname;
	u_int port;
	const ADDRINFO *hints;
	ADDRINFO **res;
{
#ifdef HAVE_GETADDRINFO
	int ret;

	if ((ret = getaddrinfo(nodename, servname, hints, res)) == 0)
		return (0);

	__db_errx(env, DB_STR_A("0153",
	    "%s(%u): host lookup failed: %s", "%s %u %s"),
	    nodename == NULL ? "" : nodename, port,
#ifdef DB_WIN32
	    gai_strerrorA(ret));
#else
	    gai_strerror(ret));
#endif
	return (__os_posix_err(ret));
#else
	ADDRINFO *answer;
	struct hostent *hostaddr;
	struct sockaddr_in sin;
	u_int32_t tmpaddr;
	int ret;

	COMPQUIET(hints, NULL);
	COMPQUIET(servname, NULL);

	/* INADDR_NONE is not defined on Solaris 2.6, 2.7 or 2.8. */
#ifndef	INADDR_NONE
#define	INADDR_NONE	((u_long)0xffffffff)
#endif

	/*
	 * Basic implementation of IPv4 component of getaddrinfo.
	 * Limited to the functionality used by repmgr.
	 */
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	if (nodename) {
		if (nodename[0] == '\0')
			sin.sin_addr.s_addr = htonl(INADDR_ANY);
		else if ((tmpaddr = inet_addr(CHAR_STAR_CAST nodename)) !=
		    INADDR_NONE) {
			sin.sin_addr.s_addr = tmpaddr;
		} else {
			hostaddr = gethostbyname(nodename);
			if (hostaddr == NULL) {
#ifdef DB_WIN32
				ret = __os_get_neterr();
				__db_syserr(env, ret, DB_STR_A("0154",
				    "%s(%u): host lookup failed", "%s %u"),
				    nodename == NULL ? "" : nodename, port);
				return (__os_posix_err(ret));
#else
				/*
				 * Historic UNIX systems used the h_errno
				 * global variable to return gethostbyname
				 * errors.  The only function we currently
				 * use that needs h_errno is gethostbyname,
				 * so we deal with it here.
				 *
				 * hstrerror is not available on Solaris 2.6
				 * (it is in libresolv but is a private,
				 * unexported symbol).
				 */
#ifdef HAVE_HSTRERROR
				__db_errx(env, DB_STR_A("0155",
				    "%s(%u): host lookup failed: %s",
				    "%s %u %s"),
				    nodename == NULL ? "" : nodename, port,
				    hstrerror(h_errno));
#else
				__db_errx(env, DB_STR_A("0156",
				    "%s(%u): host lookup failed: %d",
				    "%s %u %d"),
				    nodename == NULL ? "" : nodename, port,
				    h_errno);
#endif
				switch (h_errno) {
				case HOST_NOT_FOUND:
				case NO_DATA:
					return (EHOSTUNREACH);
				case TRY_AGAIN:
					return (EAGAIN);
				case NO_RECOVERY:
				default:
					return (EFAULT);
				}
				/* NOTREACHED */
#endif
			}
			memcpy(&(sin.sin_addr),
			    hostaddr->h_addr, (size_t)hostaddr->h_length);
		}
	} else					/* No host specified. */
		sin.sin_addr.s_addr = htonl(INADDR_ANY);
	sin.sin_port = htons((u_int16_t)port);

	if ((ret = __os_calloc(env, 1, sizeof(ADDRINFO), &answer)) != 0)
		return (ret);
	if ((ret = __os_malloc(env, sizeof(sin), &answer->ai_addr)) != 0) {
		__os_free(env, answer);
		return (ret);
	}

	answer->ai_family = AF_INET;
	answer->ai_protocol = IPPROTO_TCP;
	answer->ai_socktype = SOCK_STREAM;
	answer->ai_addrlen = sizeof(sin);
	memcpy(answer->ai_addr, &sin, sizeof(sin));
	*res = answer;

	return (0);
#endif /* HAVE_GETADDRINFO */
}

/*
 * __os_freeaddrinfo --
 *
 * PUBLIC: #if defined(HAVE_REPLICATION_THREADS)
 * PUBLIC: void __os_freeaddrinfo __P((ENV *, ADDRINFO *));
 * PUBLIC: #endif
 */
void
__os_freeaddrinfo(env, ai)
	ENV *env;
	ADDRINFO *ai;
{
#ifdef HAVE_GETADDRINFO
	COMPQUIET(env, NULL);

	freeaddrinfo(ai);
#else
	ADDRINFO *next, *tmpaddr;

	for (next = ai; next != NULL; next = tmpaddr) {
		if (next->ai_canonname != NULL)
			__os_free(env, next->ai_canonname);

		if (next->ai_addr != NULL)
			__os_free(env, next->ai_addr);

		tmpaddr = next->ai_next;
		__os_free(env, next);
	}
#endif
}