1/* 2PostgreSQL Database Management System 3(formerly known as Postgres, then as Postgres95) 4 5Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group 6 7Portions Copyright (c) 1994, The Regents of the University of California 8 9Permission to use, copy, modify, and distribute this software and its 10documentation for any purpose, without fee, and without a written agreement 11is hereby granted, provided that the above copyright notice and this paragraph 12and the following two paragraphs appear in all copies. 13 14IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 15DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 16LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 17EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 18SUCH DAMAGE. 19 20THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 21INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 22AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 23ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS 24TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 25 26*/ 27 28/*------------------------------------------------------------------------- 29 * 30 * getaddrinfo.c 31 * Support getaddrinfo() on platforms that don't have it. 32 * 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(). 37 * 38 * Copyright (c) 2003-2007, PostgreSQL Global Development Group 39 * 40 * Copyright (C) 2007 Jeremy Allison. 41 * Modified to return multiple IPv4 addresses for Samba. 42 * 43 *------------------------------------------------------------------------- 44 */ 45 46#include "replace.h" 47#include "system/network.h" 48 49#ifndef SMB_MALLOC 50#define SMB_MALLOC(s) malloc(s) 51#endif 52 53#ifndef SMB_STRDUP 54#define SMB_STRDUP(s) strdup(s) 55#endif 56 57static int check_hostent_err(struct hostent *hp) 58{ 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; 69 } 70 } 71 if (!hp->h_name || hp->h_addrtype != AF_INET) { 72 return EAI_FAIL; 73 } 74 return 0; 75} 76 77static char *canon_name_from_hostent(struct hostent *hp, 78 int *perr) 79{ 80 char *ret = NULL; 81 82 *perr = check_hostent_err(hp); 83 if (*perr) { 84 return NULL; 85 } 86 ret = SMB_STRDUP(hp->h_name); 87 if (!ret) { 88 *perr = EAI_MEMORY; 89 } 90 return ret; 91} 92 93static char *get_my_canon_name(int *perr) 94{ 95 char name[HOST_NAME_MAX+1]; 96 97 if (gethostname(name, HOST_NAME_MAX) == -1) { 98 *perr = EAI_FAIL; 99 return NULL; 100 } 101 /* Ensure null termination. */ 102 name[HOST_NAME_MAX] = '\0'; 103 return canon_name_from_hostent(gethostbyname(name), perr); 104} 105 106static char *get_canon_name_from_addr(struct in_addr ip, 107 int *perr) 108{ 109 return canon_name_from_hostent( 110 gethostbyaddr(&ip, sizeof(ip), AF_INET), 111 perr); 112} 113 114static struct addrinfo *alloc_entry(const struct addrinfo *hints, 115 struct in_addr ip, 116 unsigned short port) 117{ 118 struct sockaddr_in *psin = NULL; 119 struct addrinfo *ai = SMB_MALLOC(sizeof(*ai)); 120 121 if (!ai) { 122 return NULL; 123 } 124 memset(ai, '\0', sizeof(*ai)); 125 126 psin = SMB_MALLOC(sizeof(*psin)); 127 if (!psin) { 128 free(ai); 129 return NULL; 130 } 131 132 memset(psin, '\0', sizeof(*psin)); 133 134 psin->sin_family = AF_INET; 135 psin->sin_port = htons(port); 136 psin->sin_addr = ip; 137 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; 146 147 return ai; 148} 149 150/* 151 * get address info for a single ipv4 address. 152 * 153 * Bugs: - servname can only be a number, not text. 154 */ 155 156static int getaddr_info_single_addr(const char *service, 157 uint32_t addr, 158 const struct addrinfo *hints, 159 struct addrinfo **res) 160{ 161 162 struct addrinfo *ai = NULL; 163 struct in_addr ip; 164 unsigned short port = 0; 165 166 if (service) { 167 port = (unsigned short)atoi(service); 168 } 169 ip.s_addr = htonl(addr); 170 171 ai = alloc_entry(hints, ip, port); 172 if (!ai) { 173 return EAI_MEMORY; 174 } 175 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); 186 } 187 if (ai->ai_canonname == NULL) { 188 freeaddrinfo(ai); 189 return err; 190 } 191 } 192 193 *res = ai; 194 return 0; 195} 196 197/* 198 * get address info for multiple ipv4 addresses. 199 * 200 * Bugs: - servname can only be a number, not text. 201 */ 202 203static int getaddr_info_name(const char *node, 204 const char *service, 205 const struct addrinfo *hints, 206 struct addrinfo **res) 207{ 208 struct addrinfo *listp = NULL, *prevp = NULL; 209 char **pptr = NULL; 210 int err; 211 struct hostent *hp = NULL; 212 unsigned short port = 0; 213 214 if (service) { 215 port = (unsigned short)atoi(service); 216 } 217 218 hp = gethostbyname(node); 219 err = check_hostent_err(hp); 220 if (err) { 221 return err; 222 } 223 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); 227 228 if (!ai) { 229 freeaddrinfo(listp); 230 return EAI_MEMORY; 231 } 232 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; 240 } 241 } else { 242 prevp->ai_next = ai; 243 prevp = ai; 244 } 245 } 246 *res = listp; 247 return 0; 248} 249 250/* 251 * get address info for ipv4 sockets. 252 * 253 * Bugs: - servname can only be a number, not text. 254 */ 255 256int rep_getaddrinfo(const char *node, 257 const char *service, 258 const struct addrinfo * hintp, 259 struct addrinfo ** res) 260{ 261 struct addrinfo hints; 262 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)); 270 } 271 272 if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) { 273 return EAI_FAMILY; 274 } 275 276 if (hints.ai_socktype == 0) { 277 hints.ai_socktype = SOCK_STREAM; 278 } 279 280 if (!node && !service) { 281 return EAI_NONAME; 282 } 283 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; 294 } 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); 304 } 305 } else if (hints.ai_flags & AI_PASSIVE) { 306 return getaddr_info_single_addr(service, 307 INADDR_ANY, 308 &hints, 309 res); 310 } 311 return getaddr_info_single_addr(service, 312 INADDR_LOOPBACK, 313 &hints, 314 res); 315} 316 317 318void rep_freeaddrinfo(struct addrinfo *res) 319{ 320 struct addrinfo *next = NULL; 321 322 for (;res; res = next) { 323 next = res->ai_next; 324 if (res->ai_canonname) { 325 free(res->ai_canonname); 326 } 327 if (res->ai_addr) { 328 free(res->ai_addr); 329 } 330 free(res); 331 } 332} 333 334 335const char *rep_gai_strerror(int errcode) 336{ 337#ifdef HAVE_HSTRERROR 338 int hcode; 339 340 switch (errcode) 341 { 342 case EAI_NONAME: 343 hcode = HOST_NOT_FOUND; 344 break; 345 case EAI_AGAIN: 346 hcode = TRY_AGAIN; 347 break; 348 case EAI_FAIL: 349 default: 350 hcode = NO_RECOVERY; 351 break; 352 } 353 354 return hstrerror(hcode); 355#else /* !HAVE_HSTRERROR */ 356 357 switch (errcode) 358 { 359 case EAI_NONAME: 360 return "Unknown host"; 361 case EAI_AGAIN: 362 return "Host name lookup failure"; 363#ifdef EAI_BADFLAGS 364 case EAI_BADFLAGS: 365 return "Invalid argument"; 366#endif 367#ifdef EAI_FAMILY 368 case EAI_FAMILY: 369 return "Address family not supported"; 370#endif 371#ifdef EAI_MEMORY 372 case EAI_MEMORY: 373 return "Not enough memory"; 374#endif 375#ifdef EAI_NODATA 376 case EAI_NODATA: 377 return "No host data of that type was found"; 378#endif 379#ifdef EAI_SERVICE 380 case EAI_SERVICE: 381 return "Class type not found"; 382#endif 383#ifdef EAI_SOCKTYPE 384 case EAI_SOCKTYPE: 385 return "Socket type not supported"; 386#endif 387 default: 388 return "Unknown server error"; 389 } 390#endif /* HAVE_HSTRERROR */ 391} 392 393static int gethostnameinfo(const struct sockaddr *sa, 394 char *node, 395 size_t nodelen, 396 int flags) 397{ 398 int ret = -1; 399 char *p = NULL; 400 401 if (!(flags & NI_NUMERICHOST)) { 402 struct hostent *hp = gethostbyaddr( 403 &((struct sockaddr_in *)sa)->sin_addr, 404 sizeof(struct in_addr), 405 sa->sa_family); 406 ret = check_hostent_err(hp); 407 if (ret == 0) { 408 /* Name looked up successfully. */ 409 ret = snprintf(node, nodelen, "%s", hp->h_name); 410 if (ret < 0 || (size_t)ret >= nodelen) { 411 return EAI_MEMORY; 412 } 413 if (flags & NI_NOFQDN) { 414 p = strchr(node,'.'); 415 if (p) { 416 *p = '\0'; 417 } 418 } 419 return 0; 420 } 421 422 if (flags & NI_NAMEREQD) { 423 /* If we require a name and didn't get one, 424 * automatically fail. */ 425 return ret; 426 } 427 /* Otherwise just fall into the numeric host code... */ 428 } 429 p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr); 430 ret = snprintf(node, nodelen, "%s", p); 431 if (ret < 0 || (size_t)ret >= nodelen) { 432 return EAI_MEMORY; 433 } 434 return 0; 435} 436 437static int getservicenameinfo(const struct sockaddr *sa, 438 char *service, 439 size_t servicelen, 440 int flags) 441{ 442 int ret = -1; 443 int port = ntohs(((struct sockaddr_in *)sa)->sin_port); 444 445 if (!(flags & NI_NUMERICSERV)) { 446 struct servent *se = getservbyport( 447 port, 448 (flags & NI_DGRAM) ? "udp" : "tcp"); 449 if (se && se->s_name) { 450 /* Service name looked up successfully. */ 451 ret = snprintf(service, servicelen, "%s", se->s_name); 452 if (ret < 0 || (size_t)ret >= servicelen) { 453 return EAI_MEMORY; 454 } 455 return 0; 456 } 457 /* Otherwise just fall into the numeric service code... */ 458 } 459 ret = snprintf(service, servicelen, "%d", port); 460 if (ret < 0 || (size_t)ret >= servicelen) { 461 return EAI_MEMORY; 462 } 463 return 0; 464} 465 466/* 467 * Convert an ipv4 address to a hostname. 468 * 469 * Bugs: - No IPv6 support. 470 */ 471int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen, 472 char *node, size_t nodelen, 473 char *service, size_t servicelen, int flags) 474{ 475 476 /* Invalid arguments. */ 477 if (sa == NULL || (node == NULL && service == NULL)) { 478 return EAI_FAIL; 479 } 480 481 if (sa->sa_family != AF_INET) { 482 return EAI_FAIL; 483 } 484 485 if (salen < sizeof(struct sockaddr_in)) { 486 return EAI_FAIL; 487 } 488 489 if (node) { 490 return gethostnameinfo(sa, node, nodelen, flags); 491 } 492 493 if (service) { 494 return getservicenameinfo(sa, service, servicelen, flags); 495 } 496 return 0; 497} 498