1/*
2 * Copyright 2010-2011, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <NetworkAddressResolver.h>
8
9#include <errno.h>
10#include <netdb.h>
11
12#include <NetworkAddress.h>
13
14
15static bool
16strip_port(BString& host, BString& port)
17{
18	int32 first = host.FindFirst(':');
19	int32 separator = host.FindLast(':');
20	if (separator != first
21		&& (separator == 0 || host.ByteAt(separator - 1) != ']'))
22		return false;
23
24	if (separator != -1) {
25		// looks like there is a port
26		host.CopyInto(port, separator + 1, -1);
27		host.Truncate(separator);
28
29		return true;
30	}
31
32	return false;
33}
34
35
36// #pragma mark -
37
38
39BNetworkAddressResolver::BNetworkAddressResolver()
40	:
41	fInfo(NULL),
42	fStatus(B_NO_INIT)
43{
44}
45
46
47BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
48	uint16 port, uint32 flags)
49	:
50	fInfo(NULL),
51	fStatus(B_NO_INIT)
52{
53	SetTo(address, port, flags);
54}
55
56BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
57	const char* service, uint32 flags)
58	:
59	fInfo(NULL),
60	fStatus(B_NO_INIT)
61{
62	SetTo(address, service, flags);
63}
64
65
66BNetworkAddressResolver::BNetworkAddressResolver(int family,
67	const char* address, uint16 port, uint32 flags)
68	:
69	fInfo(NULL),
70	fStatus(B_NO_INIT)
71{
72	SetTo(family, address, port, flags);
73}
74
75
76BNetworkAddressResolver::BNetworkAddressResolver(int family,
77	const char* address, const char* service, uint32 flags)
78	:
79	fInfo(NULL),
80	fStatus(B_NO_INIT)
81{
82	SetTo(family, address, service, flags);
83}
84
85
86BNetworkAddressResolver::~BNetworkAddressResolver()
87{
88	Unset();
89}
90
91
92status_t
93BNetworkAddressResolver::InitCheck() const
94{
95	return fStatus;
96}
97
98
99void
100BNetworkAddressResolver::Unset()
101{
102	if (fInfo != NULL) {
103		freeaddrinfo(fInfo);
104		fInfo = NULL;
105	}
106	fStatus = B_NO_INIT;
107}
108
109
110status_t
111BNetworkAddressResolver::SetTo(const char* address, uint16 port, uint32 flags)
112{
113	return SetTo(AF_UNSPEC, address, port, flags);
114}
115
116
117status_t
118BNetworkAddressResolver::SetTo(const char* address, const char* service,
119	uint32 flags)
120{
121	return SetTo(AF_UNSPEC, address, service, flags);
122}
123
124
125status_t
126BNetworkAddressResolver::SetTo(int family, const char* address, uint16 port,
127	uint32 flags)
128{
129	BString service;
130	service << port;
131
132	return SetTo(family, address, port != 0 ? service.String() : NULL, flags);
133}
134
135
136status_t
137BNetworkAddressResolver::SetTo(int family, const char* host,
138	const char* service, uint32 flags)
139{
140	Unset();
141
142	// Check if the address contains a port
143
144	BString hostString(host);
145
146	BString portString;
147	if (!strip_port(hostString, portString) && service != NULL)
148		portString = service;
149
150	// Resolve address
151
152	addrinfo hint = {0};
153	hint.ai_family = family;
154	if ((flags & B_NO_ADDRESS_RESOLUTION) != 0)
155		hint.ai_flags |= AI_NUMERICHOST;
156	else if ((flags & B_UNCONFIGURED_ADDRESS_FAMILIES) == 0)
157		hint.ai_flags |= AI_ADDRCONFIG;
158
159	if (host == NULL && portString.Length() == 0) {
160		portString = "0";
161		hint.ai_flags |= AI_PASSIVE;
162	}
163
164	int status = getaddrinfo(host != NULL ? hostString.String() : NULL,
165		portString.Length() != 0 ? portString.String() : NULL, &hint, &fInfo);
166	if (status == 0)
167		return fStatus = B_OK;
168
169	// Map errors
170	// TODO: improve error reporting, maybe add specific error codes?
171
172	switch (status) {
173		case EAI_ADDRFAMILY:
174		case EAI_BADFLAGS:
175		case EAI_PROTOCOL:
176		case EAI_BADHINTS:
177		case EAI_SOCKTYPE:
178		case EAI_SERVICE:
179		case EAI_NONAME:
180		case EAI_FAMILY:
181			fStatus = B_BAD_VALUE;
182			break;
183
184		case EAI_SYSTEM:
185			fStatus = errno;
186			break;
187
188		case EAI_OVERFLOW:
189		case EAI_MEMORY:
190			fStatus = B_NO_MEMORY;
191			break;
192
193		case EAI_AGAIN:
194			// TODO: better error code to denote temporary failure?
195			fStatus = B_TIMED_OUT;
196			break;
197
198		default:
199			fStatus = B_ERROR;
200			break;
201	}
202
203	return fStatus;
204}
205
206
207status_t
208BNetworkAddressResolver::GetNextAddress(uint32* cookie,
209	BNetworkAddress& address) const
210{
211	if (fStatus != B_OK)
212		return fStatus;
213
214	// Skip previous info entries
215
216	addrinfo* info = fInfo;
217	int32 first = *cookie;
218	for (int32 index = 0; index < first && info != NULL; index++) {
219		info = info->ai_next;
220	}
221
222	if (info == NULL)
223		return B_BAD_VALUE;
224
225	// Return current
226
227	address.SetTo(*info->ai_addr, info->ai_addrlen);
228	(*cookie)++;
229
230	return B_OK;
231}
232
233
234status_t
235BNetworkAddressResolver::GetNextAddress(int family, uint32* cookie,
236	BNetworkAddress& address) const
237{
238	if (fStatus != B_OK)
239		return fStatus;
240
241	// Skip previous info entries, and those that have a non-matching family
242
243	addrinfo* info = fInfo;
244	int32 first = *cookie;
245	for (int32 index = 0; index < first && info != NULL; index++) {
246		while (info != NULL && info->ai_family != family)
247			info = info->ai_next;
248	}
249
250	if (info == NULL)
251		return B_BAD_VALUE;
252
253	// Return current
254
255	address.SetTo(*info->ai_addr, info->ai_addrlen);
256	(*cookie)++;
257
258	return B_OK;
259}
260