xref: /haiku/src/kits/network/libnetapi/NetworkAddressResolver.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 
15 static bool
16 strip_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 
25 	if (separator != -1) {
26 		// looks like there is a port
27 		host.CopyInto(port, separator + 1, -1);
28 		host.Truncate(separator);
29 
30 		return true;
31 	}
32 
33 	return false;
34 }
35 
36 
37 // #pragma mark -
38 
39 
40 BNetworkAddressResolver::BNetworkAddressResolver()
41 	:
42 	BReferenceable(),
43 	fInfo(NULL),
44 	fStatus(B_NO_INIT)
45 {
46 }
47 
48 
49 BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
50 	uint16 port, uint32 flags)
51 	:
52 	BReferenceable(),
53 	fInfo(NULL),
54 	fStatus(B_NO_INIT)
55 {
56 	SetTo(address, port, flags);
57 }
58 
59 BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
60 	const char* service, uint32 flags)
61 	:
62 	BReferenceable(),
63 	fInfo(NULL),
64 	fStatus(B_NO_INIT)
65 {
66 	SetTo(address, service, flags);
67 }
68 
69 
70 BNetworkAddressResolver::BNetworkAddressResolver(int family,
71 	const char* address, uint16 port, uint32 flags)
72 	:
73 	BReferenceable(),
74 	fInfo(NULL),
75 	fStatus(B_NO_INIT)
76 {
77 	SetTo(family, address, port, flags);
78 }
79 
80 
81 BNetworkAddressResolver::BNetworkAddressResolver(int family,
82 	const char* address, const char* service, uint32 flags)
83 	:
84 	BReferenceable(),
85 	fInfo(NULL),
86 	fStatus(B_NO_INIT)
87 {
88 	SetTo(family, address, service, flags);
89 }
90 
91 
92 BNetworkAddressResolver::~BNetworkAddressResolver()
93 {
94 	Unset();
95 }
96 
97 
98 status_t
99 BNetworkAddressResolver::InitCheck() const
100 {
101 	return fStatus;
102 }
103 
104 
105 void
106 BNetworkAddressResolver::Unset()
107 {
108 	if (fInfo != NULL) {
109 		freeaddrinfo(fInfo);
110 		fInfo = NULL;
111 	}
112 	fStatus = B_NO_INIT;
113 }
114 
115 
116 status_t
117 BNetworkAddressResolver::SetTo(const char* address, uint16 port, uint32 flags)
118 {
119 	return SetTo(AF_UNSPEC, address, port, flags);
120 }
121 
122 
123 status_t
124 BNetworkAddressResolver::SetTo(const char* address, const char* service,
125 	uint32 flags)
126 {
127 	return SetTo(AF_UNSPEC, address, service, flags);
128 }
129 
130 
131 status_t
132 BNetworkAddressResolver::SetTo(int family, const char* address, uint16 port,
133 	uint32 flags)
134 {
135 	BString service;
136 	service << port;
137 
138 	return SetTo(family, address, port != 0 ? service.String() : NULL, flags);
139 }
140 
141 
142 status_t
143 BNetworkAddressResolver::SetTo(int family, const char* host,
144 	const char* service, uint32 flags)
145 {
146 	Unset();
147 
148 	// Check if the address contains a port
149 
150 	BString hostString(host);
151 
152 	BString portString;
153 	if (!strip_port(hostString, portString) && service != NULL)
154 		portString = service;
155 
156 	// Resolve address
157 
158 	addrinfo hint = {0};
159 	hint.ai_family = family;
160 	if ((flags & B_NO_ADDRESS_RESOLUTION) != 0)
161 		hint.ai_flags |= AI_NUMERICHOST;
162 	else if ((flags & B_UNCONFIGURED_ADDRESS_FAMILIES) == 0)
163 		hint.ai_flags |= AI_ADDRCONFIG;
164 
165 	if (host == NULL && portString.Length() == 0) {
166 		portString = "0";
167 		hint.ai_flags |= AI_PASSIVE;
168 	}
169 
170 	int status = getaddrinfo(host != NULL ? hostString.String() : NULL,
171 		portString.Length() != 0 ? portString.String() : NULL, &hint, &fInfo);
172 	if (status == 0)
173 		return fStatus = B_OK;
174 
175 	// Map errors
176 	// TODO: improve error reporting, maybe add specific error codes?
177 
178 	switch (status) {
179 		case EAI_ADDRFAMILY:
180 		case EAI_BADFLAGS:
181 		case EAI_PROTOCOL:
182 		case EAI_BADHINTS:
183 		case EAI_SOCKTYPE:
184 		case EAI_SERVICE:
185 		case EAI_NONAME:
186 		case EAI_FAMILY:
187 			fStatus = B_BAD_VALUE;
188 			break;
189 
190 		case EAI_SYSTEM:
191 			fStatus = errno;
192 			break;
193 
194 		case EAI_OVERFLOW:
195 		case EAI_MEMORY:
196 			fStatus = B_NO_MEMORY;
197 			break;
198 
199 		case EAI_AGAIN:
200 			// TODO: better error code to denote temporary failure?
201 			fStatus = B_TIMED_OUT;
202 			break;
203 
204 		default:
205 			fStatus = B_ERROR;
206 			break;
207 	}
208 
209 	return fStatus;
210 }
211 
212 
213 status_t
214 BNetworkAddressResolver::GetNextAddress(uint32* cookie,
215 	BNetworkAddress& address) const
216 {
217 	if (fStatus != B_OK)
218 		return fStatus;
219 
220 	// Skip previous info entries
221 
222 	addrinfo* info = fInfo;
223 	int32 first = *cookie;
224 	for (int32 index = 0; index < first && info != NULL; index++) {
225 		info = info->ai_next;
226 	}
227 
228 	if (info == NULL)
229 		return B_BAD_VALUE;
230 
231 	// Return current
232 
233 	address.SetTo(*info->ai_addr, info->ai_addrlen);
234 	(*cookie)++;
235 
236 	return B_OK;
237 }
238 
239 
240 status_t
241 BNetworkAddressResolver::GetNextAddress(int family, uint32* cookie,
242 	BNetworkAddress& address) const
243 {
244 	if (fStatus != B_OK)
245 		return fStatus;
246 
247 	// Skip previous info entries, and those that have a non-matching family
248 
249 	addrinfo* info = fInfo;
250 	int32 first = *cookie;
251 	for (int32 index = 0; index < first && info != NULL; index++)
252 		info = info->ai_next;
253 
254 	while (info != NULL && info->ai_family != family)
255 		info = info->ai_next;
256 
257 	if (info == NULL)
258 		return B_BAD_VALUE;
259 
260 	// Return current
261 
262 	address.SetTo(*info->ai_addr, info->ai_addrlen);
263 	(*cookie)++;
264 
265 	return B_OK;
266 }
267 
268 
269 /*static*/ BReference<const BNetworkAddressResolver>
270 BNetworkAddressResolver::Resolve(const char* address, const char* service,
271 	uint32 flags)
272 {
273 	return Resolve(AF_UNSPEC, address, service, flags);
274 }
275 
276 
277 /*static*/ BReference<const BNetworkAddressResolver>
278 BNetworkAddressResolver::Resolve(const char* address, uint16 port, uint32 flags)
279 {
280 	return Resolve(AF_UNSPEC, address, port, flags);
281 }
282 
283 
284 /*static*/ BReference<const BNetworkAddressResolver>
285 BNetworkAddressResolver::Resolve(int family, const char* address,
286 	uint16 port, uint32 flags)
287 {
288 	BString service;
289 	service << port;
290 
291 	return Resolve(family, address, port == 0 ? NULL : service.String(), flags);
292 }
293 
294 
295 /*static*/ BReference<const BNetworkAddressResolver>
296 BNetworkAddressResolver::Resolve(int family, const char* address,
297 	const char* service, uint32 flags)
298 {
299 	// TODO it may be faster to use an hash map to have faster lookup of the
300 	// cache. However, we also need to access the cache by LRU, and for that
301 	// a doubly-linked list is better. We should have these two share the same
302 	// items, so it's easy to remove the LRU from the map, or insert a new
303 	// item in both structures.
304 	for (int i = 0; i < sCacheMap.CountItems(); i++) {
305 		CacheEntry* entry = sCacheMap.ItemAt(i);
306 		if (entry->Matches(family, address, service, flags)) {
307 			// This entry is now the MRU, move to end of list.
308 			// TODO if the item is old (more than 1 minute), it should be
309 			// dropped and a new request made.
310 			sCacheMap.MoveItem(i, sCacheMap.CountItems());
311 			return entry->fResolver;
312 		}
313 	}
314 
315 	BNetworkAddressResolver* resolver = new(std::nothrow)
316 		BNetworkAddressResolver(family, address, service, flags);
317 
318 	if (resolver != NULL && resolver->InitCheck() == B_OK) {
319 		// TODO adjust capacity. Chrome uses 256 entries with a timeout of
320 		// 1 minute, IE uses 1000 entries with a timeout of 30 seconds.
321 		if (sCacheMap.CountItems() > 255)
322 			delete sCacheMap.RemoveItemAt(0);
323 
324 		CacheEntry* entry = new(std::nothrow) CacheEntry(family, address,
325 			service, flags, resolver);
326 		if (entry)
327 			sCacheMap.AddItem(entry, sCacheMap.CountItems());
328 	}
329 
330 	return BReference<const BNetworkAddressResolver>(resolver, true);
331 }
332 
333 
334 BObjectList<BNetworkAddressResolver::CacheEntry>
335 	BNetworkAddressResolver::sCacheMap;
336