2 * Copyright 2010-2011, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include <NetworkAddressResolver.h>
12 #include <NetworkAddress.h>
16 strip_port(BString
& host
, BString
& port
)
18 int32 first
= host
.FindFirst(':');
19 int32 separator
= host
.FindLast(':');
20 if (separator
!= first
21 && (separator
== 0 || host
.ByteAt(separator
- 1) != ']')) {
25 if (separator
!= -1) {
26 // looks like there is a port
27 host
.CopyInto(port
, separator
+ 1, -1);
28 host
.Truncate(separator
);
40 BNetworkAddressResolver::BNetworkAddressResolver()
49 BNetworkAddressResolver::BNetworkAddressResolver(const char* address
,
50 uint16 port
, uint32 flags
)
56 SetTo(address
, port
, flags
);
59 BNetworkAddressResolver::BNetworkAddressResolver(const char* address
,
60 const char* service
, uint32 flags
)
66 SetTo(address
, service
, flags
);
70 BNetworkAddressResolver::BNetworkAddressResolver(int family
,
71 const char* address
, uint16 port
, uint32 flags
)
77 SetTo(family
, address
, port
, flags
);
81 BNetworkAddressResolver::BNetworkAddressResolver(int family
,
82 const char* address
, const char* service
, uint32 flags
)
88 SetTo(family
, address
, service
, flags
);
92 BNetworkAddressResolver::~BNetworkAddressResolver()
99 BNetworkAddressResolver::InitCheck() const
106 BNetworkAddressResolver::Unset()
117 BNetworkAddressResolver::SetTo(const char* address
, uint16 port
, uint32 flags
)
119 return SetTo(AF_UNSPEC
, address
, port
, flags
);
124 BNetworkAddressResolver::SetTo(const char* address
, const char* service
,
127 return SetTo(AF_UNSPEC
, address
, service
, flags
);
132 BNetworkAddressResolver::SetTo(int family
, const char* address
, uint16 port
,
138 return SetTo(family
, address
, port
!= 0 ? service
.String() : NULL
, flags
);
143 BNetworkAddressResolver::SetTo(int family
, const char* host
,
144 const char* service
, uint32 flags
)
148 // Check if the address contains a port
150 BString
hostString(host
);
153 if (!strip_port(hostString
, portString
) && service
!= NULL
)
154 portString
= service
;
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
;
165 if (host
== NULL
&& portString
.Length() == 0) {
167 hint
.ai_flags
|= AI_PASSIVE
;
170 int status
= getaddrinfo(host
!= NULL
? hostString
.String() : NULL
,
171 portString
.Length() != 0 ? portString
.String() : NULL
, &hint
, &fInfo
);
173 return fStatus
= B_OK
;
176 // TODO: improve error reporting, maybe add specific error codes?
187 fStatus
= B_BAD_VALUE
;
196 fStatus
= B_NO_MEMORY
;
200 // TODO: better error code to denote temporary failure?
201 fStatus
= B_TIMED_OUT
;
214 BNetworkAddressResolver::GetNextAddress(uint32
* cookie
,
215 BNetworkAddress
& address
) const
220 // Skip previous info entries
222 addrinfo
* info
= fInfo
;
223 int32 first
= *cookie
;
224 for (int32 index
= 0; index
< first
&& info
!= NULL
; index
++) {
225 info
= info
->ai_next
;
233 address
.SetTo(*info
->ai_addr
, info
->ai_addrlen
);
241 BNetworkAddressResolver::GetNextAddress(int family
, uint32
* cookie
,
242 BNetworkAddress
& address
) const
247 // Skip previous info entries, and those that have a non-matching family
249 addrinfo
* info
= fInfo
;
250 int32 first
= *cookie
;
251 for (int32 index
= 0; index
< first
&& info
!= NULL
; index
++)
252 info
= info
->ai_next
;
254 while (info
!= NULL
&& info
->ai_family
!= family
)
255 info
= info
->ai_next
;
262 address
.SetTo(*info
->ai_addr
, info
->ai_addrlen
);
269 /*static*/ BReference
<const BNetworkAddressResolver
>
270 BNetworkAddressResolver::Resolve(const char* address
, const char* service
,
273 return Resolve(AF_UNSPEC
, address
, service
, flags
);
277 /*static*/ BReference
<const BNetworkAddressResolver
>
278 BNetworkAddressResolver::Resolve(const char* address
, uint16 port
, uint32 flags
)
280 return Resolve(AF_UNSPEC
, address
, port
, flags
);
284 /*static*/ BReference
<const BNetworkAddressResolver
>
285 BNetworkAddressResolver::Resolve(int family
, const char* address
,
286 uint16 port
, uint32 flags
)
291 return Resolve(family
, address
, port
== 0 ? NULL
: service
.String(), flags
);
295 /*static*/ BReference
<const BNetworkAddressResolver
>
296 BNetworkAddressResolver::Resolve(int family
, const char* address
,
297 const char* service
, uint32 flags
)
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
;
315 BNetworkAddressResolver
* resolver
= new(std::nothrow
)
316 BNetworkAddressResolver(family
, address
, service
, flags
);
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);
324 CacheEntry
* entry
= new(std::nothrow
) CacheEntry(family
, address
,
325 service
, flags
, resolver
);
327 sCacheMap
.AddItem(entry
, sCacheMap
.CountItems());
330 return BReference
<const BNetworkAddressResolver
>(resolver
, true);
334 BObjectList
<BNetworkAddressResolver::CacheEntry
>
335 BNetworkAddressResolver::sCacheMap
;