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