dsdb: Fix CID 1034719 Evaluation order violation
[Samba.git] / lib / replace / getaddrinfo.c
blob8440d8e6f3e9d50e292a40b4e69931505ac1b905
1 /*
2 PostgreSQL Database Management System
3 (formerly known as Postgres, then as Postgres95)
5 Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
7 Portions Copyright (c) 1994, The Regents of the University of California
9 Permission to use, copy, modify, and distribute this software and its
10 documentation for any purpose, without fee, and without a written agreement
11 is hereby granted, provided that the above copyright notice and this paragraph
12 and the following two paragraphs appear in all copies.
14 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
15 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
16 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
17 EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
18 SUCH DAMAGE.
20 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
24 TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
28 /*-------------------------------------------------------------------------
30 * getaddrinfo.c
31 * Support getaddrinfo() on platforms that don't have it.
33 * We also supply getnameinfo() here, assuming that the platform will have
34 * it if and only if it has getaddrinfo(). If this proves false on some
35 * platform, we'll need to split this file and provide a separate configure
36 * test for getnameinfo().
38 * Copyright (c) 2003-2007, PostgreSQL Global Development Group
40 * Copyright (C) 2007 Jeremy Allison.
41 * Modified to return multiple IPv4 addresses for Samba.
43 *-------------------------------------------------------------------------
46 #include "replace.h"
47 #include "system/network.h"
49 #ifndef SMB_MALLOC
50 #define SMB_MALLOC(s) malloc(s)
51 #endif
53 #ifndef SMB_STRDUP
54 #define SMB_STRDUP(s) strdup(s)
55 #endif
57 static int check_hostent_err(struct hostent *hp)
59 if (!hp) {
60 switch (h_errno) {
61 case HOST_NOT_FOUND:
62 case NO_DATA:
63 return EAI_NONAME;
64 case TRY_AGAIN:
65 return EAI_AGAIN;
66 case NO_RECOVERY:
67 default:
68 return EAI_FAIL;
71 if (!hp->h_name || hp->h_addrtype != AF_INET) {
72 return EAI_FAIL;
74 return 0;
77 static char *canon_name_from_hostent(struct hostent *hp,
78 int *perr)
80 char *ret = NULL;
82 *perr = check_hostent_err(hp);
83 if (*perr) {
84 return NULL;
86 ret = SMB_STRDUP(hp->h_name);
87 if (!ret) {
88 *perr = EAI_MEMORY;
90 return ret;
93 static char *get_my_canon_name(int *perr)
95 char name[HOST_NAME_MAX+1];
97 if (gethostname(name, HOST_NAME_MAX) == -1) {
98 *perr = EAI_FAIL;
99 return NULL;
101 /* Ensure null termination. */
102 name[HOST_NAME_MAX] = '\0';
103 return canon_name_from_hostent(gethostbyname(name), perr);
106 static char *get_canon_name_from_addr(struct in_addr ip,
107 int *perr)
109 return canon_name_from_hostent(
110 gethostbyaddr(&ip, sizeof(ip), AF_INET),
111 perr);
114 static struct addrinfo *alloc_entry(const struct addrinfo *hints,
115 struct in_addr ip,
116 unsigned short port)
118 struct sockaddr_in *psin = NULL;
119 struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
121 if (!ai) {
122 return NULL;
124 memset(ai, '\0', sizeof(*ai));
126 psin = SMB_MALLOC(sizeof(*psin));
127 if (!psin) {
128 free(ai);
129 return NULL;
132 memset(psin, '\0', sizeof(*psin));
134 psin->sin_family = AF_INET;
135 psin->sin_port = htons(port);
136 psin->sin_addr = ip;
138 ai->ai_flags = 0;
139 ai->ai_family = AF_INET;
140 ai->ai_socktype = hints->ai_socktype;
141 ai->ai_protocol = hints->ai_protocol;
142 ai->ai_addrlen = sizeof(*psin);
143 ai->ai_addr = (struct sockaddr *) psin;
144 ai->ai_canonname = NULL;
145 ai->ai_next = NULL;
147 return ai;
151 * get address info for a single ipv4 address.
153 * Bugs: - servname can only be a number, not text.
156 static int getaddr_info_single_addr(const char *service,
157 uint32_t addr,
158 const struct addrinfo *hints,
159 struct addrinfo **res)
162 struct addrinfo *ai = NULL;
163 struct in_addr ip;
164 unsigned short port = 0;
166 if (service) {
167 port = (unsigned short)atoi(service);
169 ip.s_addr = htonl(addr);
171 ai = alloc_entry(hints, ip, port);
172 if (!ai) {
173 return EAI_MEMORY;
176 /* If we're asked for the canonical name,
177 * make sure it returns correctly. */
178 if (!(hints->ai_flags & AI_NUMERICSERV) &&
179 hints->ai_flags & AI_CANONNAME) {
180 int err;
181 if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
182 ai->ai_canonname = get_my_canon_name(&err);
183 } else {
184 ai->ai_canonname =
185 get_canon_name_from_addr(ip,&err);
187 if (ai->ai_canonname == NULL) {
188 freeaddrinfo(ai);
189 return err;
193 *res = ai;
194 return 0;
198 * get address info for multiple ipv4 addresses.
200 * Bugs: - servname can only be a number, not text.
203 static int getaddr_info_name(const char *node,
204 const char *service,
205 const struct addrinfo *hints,
206 struct addrinfo **res)
208 struct addrinfo *listp = NULL, *prevp = NULL;
209 char **pptr = NULL;
210 int err;
211 struct hostent *hp = NULL;
212 unsigned short port = 0;
214 if (service) {
215 port = (unsigned short)atoi(service);
218 hp = gethostbyname(node);
219 err = check_hostent_err(hp);
220 if (err) {
221 return err;
224 for(pptr = hp->h_addr_list; *pptr; pptr++) {
225 struct in_addr ip = *(struct in_addr *)*pptr;
226 struct addrinfo *ai = alloc_entry(hints, ip, port);
228 if (!ai) {
229 freeaddrinfo(listp);
230 return EAI_MEMORY;
233 if (!listp) {
234 listp = ai;
235 prevp = ai;
236 ai->ai_canonname = SMB_STRDUP(hp->h_name);
237 if (!ai->ai_canonname) {
238 freeaddrinfo(listp);
239 return EAI_MEMORY;
241 } else {
242 prevp->ai_next = ai;
243 prevp = ai;
246 *res = listp;
247 return 0;
251 * get address info for ipv4 sockets.
253 * Bugs: - servname can only be a number, not text.
256 int rep_getaddrinfo(const char *node,
257 const char *service,
258 const struct addrinfo * hintp,
259 struct addrinfo ** res)
261 struct addrinfo hints;
263 /* Setup the hints struct. */
264 if (hintp == NULL) {
265 memset(&hints, 0, sizeof(hints));
266 hints.ai_family = AF_INET;
267 hints.ai_socktype = SOCK_STREAM;
268 } else {
269 memcpy(&hints, hintp, sizeof(hints));
272 if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
273 return EAI_FAMILY;
276 if (hints.ai_socktype == 0) {
277 hints.ai_socktype = SOCK_STREAM;
280 if (!node && !service) {
281 return EAI_NONAME;
284 if (node) {
285 if (node[0] == '\0') {
286 return getaddr_info_single_addr(service,
287 INADDR_ANY,
288 &hints,
289 res);
290 } else if (hints.ai_flags & AI_NUMERICHOST) {
291 struct in_addr ip;
292 if (!inet_aton(node, &ip)) {
293 return EAI_FAIL;
295 return getaddr_info_single_addr(service,
296 ntohl(ip.s_addr),
297 &hints,
298 res);
299 } else {
300 return getaddr_info_name(node,
301 service,
302 &hints,
303 res);
305 } else if (hints.ai_flags & AI_PASSIVE) {
306 return getaddr_info_single_addr(service,
307 INADDR_ANY,
308 &hints,
309 res);
311 return getaddr_info_single_addr(service,
312 INADDR_LOOPBACK,
313 &hints,
314 res);
318 void rep_freeaddrinfo(struct addrinfo *res)
320 struct addrinfo *next = NULL;
322 for (;res; res = next) {
323 next = res->ai_next;
324 free(res->ai_canonname);
325 free(res->ai_addr);
326 free(res);
331 const char *rep_gai_strerror(int errcode)
333 #ifdef HAVE_HSTRERROR
334 int hcode;
336 switch (errcode)
338 case EAI_NONAME:
339 hcode = HOST_NOT_FOUND;
340 break;
341 case EAI_AGAIN:
342 hcode = TRY_AGAIN;
343 break;
344 case EAI_FAIL:
345 default:
346 hcode = NO_RECOVERY;
347 break;
350 return hstrerror(hcode);
351 #else /* !HAVE_HSTRERROR */
353 switch (errcode)
355 case EAI_NONAME:
356 return "Unknown host";
357 case EAI_AGAIN:
358 return "Host name lookup failure";
359 #ifdef EAI_BADFLAGS
360 case EAI_BADFLAGS:
361 return "Invalid argument";
362 #endif
363 #ifdef EAI_FAMILY
364 case EAI_FAMILY:
365 return "Address family not supported";
366 #endif
367 #ifdef EAI_MEMORY
368 case EAI_MEMORY:
369 return "Not enough memory";
370 #endif
371 #ifdef EAI_NODATA
372 case EAI_NODATA:
373 return "No host data of that type was found";
374 #endif
375 #ifdef EAI_SERVICE
376 case EAI_SERVICE:
377 return "Class type not found";
378 #endif
379 #ifdef EAI_SOCKTYPE
380 case EAI_SOCKTYPE:
381 return "Socket type not supported";
382 #endif
383 default:
384 return "Unknown server error";
386 #endif /* HAVE_HSTRERROR */
389 static int gethostnameinfo(const struct sockaddr *sa,
390 char *node,
391 size_t nodelen,
392 int flags)
394 int ret = -1;
395 char *p = NULL;
397 if (!(flags & NI_NUMERICHOST)) {
398 struct hostent *hp = gethostbyaddr(
399 &((struct sockaddr_in *)sa)->sin_addr,
400 sizeof(struct in_addr),
401 sa->sa_family);
402 ret = check_hostent_err(hp);
403 if (ret == 0) {
404 /* Name looked up successfully. */
405 ret = snprintf(node, nodelen, "%s", hp->h_name);
406 if (ret < 0 || (size_t)ret >= nodelen) {
407 return EAI_MEMORY;
409 if (flags & NI_NOFQDN) {
410 p = strchr(node,'.');
411 if (p) {
412 *p = '\0';
415 return 0;
418 if (flags & NI_NAMEREQD) {
419 /* If we require a name and didn't get one,
420 * automatically fail. */
421 return ret;
423 /* Otherwise just fall into the numeric host code... */
425 p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
426 ret = snprintf(node, nodelen, "%s", p);
427 if (ret < 0 || (size_t)ret >= nodelen) {
428 return EAI_MEMORY;
430 return 0;
433 static int getservicenameinfo(const struct sockaddr *sa,
434 char *service,
435 size_t servicelen,
436 int flags)
438 int ret = -1;
439 int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
441 if (!(flags & NI_NUMERICSERV)) {
442 struct servent *se = getservbyport(
443 port,
444 (flags & NI_DGRAM) ? "udp" : "tcp");
445 if (se && se->s_name) {
446 /* Service name looked up successfully. */
447 ret = snprintf(service, servicelen, "%s", se->s_name);
448 if (ret < 0 || (size_t)ret >= servicelen) {
449 return EAI_MEMORY;
451 return 0;
453 /* Otherwise just fall into the numeric service code... */
455 ret = snprintf(service, servicelen, "%d", port);
456 if (ret < 0 || (size_t)ret >= servicelen) {
457 return EAI_MEMORY;
459 return 0;
463 * Convert an ipv4 address to a hostname.
465 * Bugs: - No IPv6 support.
467 int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen,
468 char *node, size_t nodelen,
469 char *service, size_t servicelen, int flags)
472 /* Invalid arguments. */
473 if (sa == NULL || (node == NULL && service == NULL)) {
474 return EAI_FAIL;
477 if (sa->sa_family != AF_INET) {
478 return EAI_FAIL;
481 if (salen < sizeof(struct sockaddr_in)) {
482 return EAI_FAIL;
485 if (node) {
486 return gethostnameinfo(sa, node, nodelen, flags);
489 if (service) {
490 return getservicenameinfo(sa, service, servicelen, flags);
492 return 0;