xref: /haiku/src/kits/network/libnetapi/NetworkAddressResolver.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
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 	fInfo(NULL),
43 	fStatus(B_NO_INIT)
44 {
45 }
46 
47 
48 BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
49 	uint16 port, uint32 flags)
50 	:
51 	fInfo(NULL),
52 	fStatus(B_NO_INIT)
53 {
54 	SetTo(address, port, flags);
55 }
56 
57 BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
58 	const char* service, uint32 flags)
59 	:
60 	fInfo(NULL),
61 	fStatus(B_NO_INIT)
62 {
63 	SetTo(address, service, flags);
64 }
65 
66 
67 BNetworkAddressResolver::BNetworkAddressResolver(int family,
68 	const char* address, uint16 port, uint32 flags)
69 	:
70 	fInfo(NULL),
71 	fStatus(B_NO_INIT)
72 {
73 	SetTo(family, address, port, flags);
74 }
75 
76 
77 BNetworkAddressResolver::BNetworkAddressResolver(int family,
78 	const char* address, const char* service, uint32 flags)
79 	:
80 	fInfo(NULL),
81 	fStatus(B_NO_INIT)
82 {
83 	SetTo(family, address, service, flags);
84 }
85 
86 
87 BNetworkAddressResolver::~BNetworkAddressResolver()
88 {
89 	Unset();
90 }
91 
92 
93 status_t
94 BNetworkAddressResolver::InitCheck() const
95 {
96 	return fStatus;
97 }
98 
99 
100 void
101 BNetworkAddressResolver::Unset()
102 {
103 	if (fInfo != NULL) {
104 		freeaddrinfo(fInfo);
105 		fInfo = NULL;
106 	}
107 	fStatus = B_NO_INIT;
108 }
109 
110 
111 status_t
112 BNetworkAddressResolver::SetTo(const char* address, uint16 port, uint32 flags)
113 {
114 	return SetTo(AF_UNSPEC, address, port, flags);
115 }
116 
117 
118 status_t
119 BNetworkAddressResolver::SetTo(const char* address, const char* service,
120 	uint32 flags)
121 {
122 	return SetTo(AF_UNSPEC, address, service, flags);
123 }
124 
125 
126 status_t
127 BNetworkAddressResolver::SetTo(int family, const char* address, uint16 port,
128 	uint32 flags)
129 {
130 	BString service;
131 	service << port;
132 
133 	return SetTo(family, address, port != 0 ? service.String() : NULL, flags);
134 }
135 
136 
137 status_t
138 BNetworkAddressResolver::SetTo(int family, const char* host,
139 	const char* service, uint32 flags)
140 {
141 	Unset();
142 
143 	// Check if the address contains a port
144 
145 	BString hostString(host);
146 
147 	BString portString;
148 	if (!strip_port(hostString, portString) && service != NULL)
149 		portString = service;
150 
151 	// Resolve address
152 
153 	addrinfo hint = {0};
154 	hint.ai_family = family;
155 	if ((flags & B_NO_ADDRESS_RESOLUTION) != 0)
156 		hint.ai_flags |= AI_NUMERICHOST;
157 	else if ((flags & B_UNCONFIGURED_ADDRESS_FAMILIES) == 0)
158 		hint.ai_flags |= AI_ADDRCONFIG;
159 
160 	if (host == NULL && portString.Length() == 0) {
161 		portString = "0";
162 		hint.ai_flags |= AI_PASSIVE;
163 	}
164 
165 	int status = getaddrinfo(host != NULL ? hostString.String() : NULL,
166 		portString.Length() != 0 ? portString.String() : NULL, &hint, &fInfo);
167 	if (status == 0)
168 		return fStatus = B_OK;
169 
170 	// Map errors
171 	// TODO: improve error reporting, maybe add specific error codes?
172 
173 	switch (status) {
174 		case EAI_ADDRFAMILY:
175 		case EAI_BADFLAGS:
176 		case EAI_PROTOCOL:
177 		case EAI_BADHINTS:
178 		case EAI_SOCKTYPE:
179 		case EAI_SERVICE:
180 		case EAI_NONAME:
181 		case EAI_FAMILY:
182 			fStatus = B_BAD_VALUE;
183 			break;
184 
185 		case EAI_SYSTEM:
186 			fStatus = errno;
187 			break;
188 
189 		case EAI_OVERFLOW:
190 		case EAI_MEMORY:
191 			fStatus = B_NO_MEMORY;
192 			break;
193 
194 		case EAI_AGAIN:
195 			// TODO: better error code to denote temporary failure?
196 			fStatus = B_TIMED_OUT;
197 			break;
198 
199 		default:
200 			fStatus = B_ERROR;
201 			break;
202 	}
203 
204 	return fStatus;
205 }
206 
207 
208 status_t
209 BNetworkAddressResolver::GetNextAddress(uint32* cookie,
210 	BNetworkAddress& address) const
211 {
212 	if (fStatus != B_OK)
213 		return fStatus;
214 
215 	// Skip previous info entries
216 
217 	addrinfo* info = fInfo;
218 	int32 first = *cookie;
219 	for (int32 index = 0; index < first && info != NULL; index++) {
220 		info = info->ai_next;
221 	}
222 
223 	if (info == NULL)
224 		return B_BAD_VALUE;
225 
226 	// Return current
227 
228 	address.SetTo(*info->ai_addr, info->ai_addrlen);
229 	(*cookie)++;
230 
231 	return B_OK;
232 }
233 
234 
235 status_t
236 BNetworkAddressResolver::GetNextAddress(int family, uint32* cookie,
237 	BNetworkAddress& address) const
238 {
239 	if (fStatus != B_OK)
240 		return fStatus;
241 
242 	// Skip previous info entries, and those that have a non-matching family
243 
244 	addrinfo* info = fInfo;
245 	int32 first = *cookie;
246 	for (int32 index = 0; index < first && info != NULL; index++) {
247 		while (info != NULL && info->ai_family != family)
248 			info = info->ai_next;
249 	}
250 
251 	if (info == NULL)
252 		return B_BAD_VALUE;
253 
254 	// Return current
255 
256 	address.SetTo(*info->ai_addr, info->ai_addrlen);
257 	(*cookie)++;
258 
259 	return B_OK;
260 }
261