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