xref: /haiku/src/kits/network/libnetapi/NetworkAddressResolver.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 	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 
39 BNetworkAddressResolver::BNetworkAddressResolver()
40 	:
41 	fInfo(NULL),
42 	fStatus(B_NO_INIT)
43 {
44 }
45 
46 
47 BNetworkAddressResolver::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 
56 BNetworkAddressResolver::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 
66 BNetworkAddressResolver::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 
76 BNetworkAddressResolver::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 
86 BNetworkAddressResolver::~BNetworkAddressResolver()
87 {
88 	Unset();
89 }
90 
91 
92 status_t
93 BNetworkAddressResolver::InitCheck() const
94 {
95 	return fStatus;
96 }
97 
98 
99 void
100 BNetworkAddressResolver::Unset()
101 {
102 	if (fInfo != NULL) {
103 		freeaddrinfo(fInfo);
104 		fInfo = NULL;
105 	}
106 	fStatus = B_NO_INIT;
107 }
108 
109 
110 status_t
111 BNetworkAddressResolver::SetTo(const char* address, uint16 port, uint32 flags)
112 {
113 	return SetTo(AF_UNSPEC, address, port, flags);
114 }
115 
116 
117 status_t
118 BNetworkAddressResolver::SetTo(const char* address, const char* service,
119 	uint32 flags)
120 {
121 	return SetTo(AF_UNSPEC, address, service, flags);
122 }
123 
124 
125 status_t
126 BNetworkAddressResolver::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 
136 status_t
137 BNetworkAddressResolver::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 
207 status_t
208 BNetworkAddressResolver::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 
234 status_t
235 BNetworkAddressResolver::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