| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Copyright (C) 1993-2016 Free Software Foundation, Inc. | 
|  | 2 | This file is part of the GNU C Library. | 
|  | 3 | Contributed by David Mosberger (davidm@azstarnet.com). | 
|  | 4 |  | 
|  | 5 | The GNU C Library is free software; you can redistribute it and/or | 
|  | 6 | modify it under the terms of the GNU Lesser General Public | 
|  | 7 | License as published by the Free Software Foundation; either | 
|  | 8 | version 2.1 of the License, or (at your option) any later version. | 
|  | 9 |  | 
|  | 10 | The GNU C Library is distributed in the hope that it will be useful, | 
|  | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 13 | Lesser General Public License for more details. | 
|  | 14 |  | 
|  | 15 | You should have received a copy of the GNU Lesser General Public | 
|  | 16 | License along with the GNU C Library; if not, see | 
|  | 17 | <http://www.gnu.org/licenses/>.  */ | 
|  | 18 |  | 
|  | 19 | /* This file provides a Linux /etc/host.conf compatible front end to | 
|  | 20 | the various name resolvers (/etc/hosts, named, NIS server, etc.). | 
|  | 21 | Though mostly compatibly, the following differences exist compared | 
|  | 22 | to the original implementation: | 
|  | 23 |  | 
|  | 24 | - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK | 
|  | 25 | environment variable (i.e., `off', `nowarn', or `warn'). | 
|  | 26 |  | 
|  | 27 | - line comments can appear anywhere (not just at the beginning of | 
|  | 28 | a line) | 
|  | 29 | */ | 
|  | 30 |  | 
|  | 31 | #include <assert.h> | 
|  | 32 | #include <errno.h> | 
|  | 33 | #include <ctype.h> | 
|  | 34 | #include <libintl.h> | 
|  | 35 | #include <memory.h> | 
|  | 36 | #include <stdio.h> | 
|  | 37 | #include <stdio_ext.h> | 
|  | 38 | #include <stdlib.h> | 
|  | 39 | #include <string.h> | 
|  | 40 | #include <net/if.h> | 
|  | 41 | #include <sys/ioctl.h> | 
|  | 42 | #include <unistd.h> | 
|  | 43 | #include <netinet/in.h> | 
|  | 44 | #include <libc-lock.h> | 
|  | 45 | #include "ifreq.h" | 
|  | 46 | #include "res_hconf.h" | 
|  | 47 | #include <wchar.h> | 
|  | 48 | #include <atomic.h> | 
|  | 49 |  | 
|  | 50 | #if IS_IN (libc) | 
|  | 51 | # define fgets_unlocked __fgets_unlocked | 
|  | 52 | #endif | 
|  | 53 |  | 
|  | 54 | #define _PATH_HOSTCONF	"/etc/host.conf" | 
|  | 55 |  | 
|  | 56 | /* Environment vars that all user to override default behavior:  */ | 
|  | 57 | #define ENV_HOSTCONF	"RESOLV_HOST_CONF" | 
|  | 58 | #define ENV_SPOOF	"RESOLV_SPOOF_CHECK" | 
|  | 59 | #define ENV_TRIM_OVERR	"RESOLV_OVERRIDE_TRIM_DOMAINS" | 
|  | 60 | #define ENV_TRIM_ADD	"RESOLV_ADD_TRIM_DOMAINS" | 
|  | 61 | #define ENV_MULTI	"RESOLV_MULTI" | 
|  | 62 | #define ENV_REORDER	"RESOLV_REORDER" | 
|  | 63 |  | 
|  | 64 | enum parse_cbs | 
|  | 65 | { | 
|  | 66 | CB_none, | 
|  | 67 | CB_arg_trimdomain_list, | 
|  | 68 | CB_arg_spoof, | 
|  | 69 | CB_arg_bool | 
|  | 70 | }; | 
|  | 71 |  | 
|  | 72 | static const struct cmd | 
|  | 73 | { | 
|  | 74 | const char name[11]; | 
|  | 75 | uint8_t cb; | 
|  | 76 | unsigned int arg; | 
|  | 77 | } cmd[] = | 
|  | 78 | { | 
|  | 79 | {"order",		CB_none,		0}, | 
|  | 80 | {"trim",		CB_arg_trimdomain_list,	0}, | 
|  | 81 | {"spoof",		CB_arg_spoof,		0}, | 
|  | 82 | {"multi",		CB_arg_bool,		HCONF_FLAG_MULTI}, | 
|  | 83 | {"nospoof",		CB_arg_bool,		HCONF_FLAG_SPOOF}, | 
|  | 84 | {"spoofalert",	CB_arg_bool,		HCONF_FLAG_SPOOFALERT}, | 
|  | 85 | {"reorder",		CB_arg_bool,		HCONF_FLAG_REORDER} | 
|  | 86 | }; | 
|  | 87 |  | 
|  | 88 | /* Structure containing the state.  */ | 
|  | 89 | struct hconf _res_hconf; | 
|  | 90 |  | 
|  | 91 | /* Skip white space.  */ | 
|  | 92 | static const char * | 
|  | 93 | skip_ws (const char *str) | 
|  | 94 | { | 
|  | 95 | while (isspace (*str)) ++str; | 
|  | 96 | return str; | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 |  | 
|  | 100 | /* Skip until whitespace, comma, end of line, or comment character.  */ | 
|  | 101 | static const char * | 
|  | 102 | skip_string (const char *str) | 
|  | 103 | { | 
|  | 104 | while (*str && !isspace (*str) && *str != '#' && *str != ',') | 
|  | 105 | ++str; | 
|  | 106 | return str; | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 |  | 
|  | 110 | static const char * | 
|  | 111 | arg_trimdomain_list (const char *fname, int line_num, const char *args) | 
|  | 112 | { | 
|  | 113 | const char * start; | 
|  | 114 | size_t len; | 
|  | 115 |  | 
|  | 116 | do | 
|  | 117 | { | 
|  | 118 | start = args; | 
|  | 119 | args = skip_string (args); | 
|  | 120 | len = args - start; | 
|  | 121 |  | 
|  | 122 | if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX) | 
|  | 123 | { | 
|  | 124 | char *buf; | 
|  | 125 |  | 
|  | 126 | if (__asprintf (&buf, _("\ | 
|  | 127 | %s: line %d: cannot specify more than %d trim domains"), | 
|  | 128 | fname, line_num, TRIMDOMAINS_MAX) < 0) | 
|  | 129 | return 0; | 
|  | 130 |  | 
|  | 131 | __fxprintf (NULL, "%s", buf); | 
|  | 132 |  | 
|  | 133 | free (buf); | 
|  | 134 | return 0; | 
|  | 135 | } | 
|  | 136 | _res_hconf.trimdomain[_res_hconf.num_trimdomains++] = | 
|  | 137 | __strndup (start, len); | 
|  | 138 | args = skip_ws (args); | 
|  | 139 | switch (*args) | 
|  | 140 | { | 
|  | 141 | case ',': case ';': case ':': | 
|  | 142 | args = skip_ws (++args); | 
|  | 143 | if (!*args || *args == '#') | 
|  | 144 | { | 
|  | 145 | char *buf; | 
|  | 146 |  | 
|  | 147 | if (__asprintf (&buf, _("\ | 
|  | 148 | %s: line %d: list delimiter not followed by domain"), | 
|  | 149 | fname, line_num) < 0) | 
|  | 150 | return 0; | 
|  | 151 |  | 
|  | 152 | __fxprintf (NULL, "%s", buf); | 
|  | 153 |  | 
|  | 154 | free (buf); | 
|  | 155 | return 0; | 
|  | 156 | } | 
|  | 157 | default: | 
|  | 158 | break; | 
|  | 159 | } | 
|  | 160 | } | 
|  | 161 | while (*args && *args != '#'); | 
|  | 162 | return args; | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 |  | 
|  | 166 | static const char * | 
|  | 167 | arg_spoof (const char *fname, int line_num, const char *args) | 
|  | 168 | { | 
|  | 169 | const char *start = args; | 
|  | 170 | size_t len; | 
|  | 171 |  | 
|  | 172 | args = skip_string (args); | 
|  | 173 | len = args - start; | 
|  | 174 |  | 
|  | 175 | if (len == 3 && __strncasecmp (start, "off", len) == 0) | 
|  | 176 | _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT); | 
|  | 177 | else | 
|  | 178 | { | 
|  | 179 | _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT); | 
|  | 180 | if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0) | 
|  | 181 | || !(len == 4 && __strncasecmp (start, "warn", len) == 0)) | 
|  | 182 | _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT; | 
|  | 183 | } | 
|  | 184 | return args; | 
|  | 185 | } | 
|  | 186 |  | 
|  | 187 |  | 
|  | 188 | static const char * | 
|  | 189 | arg_bool (const char *fname, int line_num, const char *args, unsigned flag) | 
|  | 190 | { | 
|  | 191 | if (__strncasecmp (args, "on", 2) == 0) | 
|  | 192 | { | 
|  | 193 | args += 2; | 
|  | 194 | _res_hconf.flags |= flag; | 
|  | 195 | } | 
|  | 196 | else if (__strncasecmp (args, "off", 3) == 0) | 
|  | 197 | { | 
|  | 198 | args += 3; | 
|  | 199 | _res_hconf.flags &= ~flag; | 
|  | 200 | } | 
|  | 201 | else | 
|  | 202 | { | 
|  | 203 | char *buf; | 
|  | 204 |  | 
|  | 205 | if (__asprintf (&buf, | 
|  | 206 | _("%s: line %d: expected `on' or `off', found `%s'\n"), | 
|  | 207 | fname, line_num, args) < 0) | 
|  | 208 | return 0; | 
|  | 209 |  | 
|  | 210 | __fxprintf (NULL, "%s", buf); | 
|  | 211 |  | 
|  | 212 | free (buf); | 
|  | 213 | return 0; | 
|  | 214 | } | 
|  | 215 | return args; | 
|  | 216 | } | 
|  | 217 |  | 
|  | 218 |  | 
|  | 219 | static void | 
|  | 220 | parse_line (const char *fname, int line_num, const char *str) | 
|  | 221 | { | 
|  | 222 | const char *start; | 
|  | 223 | const struct cmd *c = 0; | 
|  | 224 | size_t len; | 
|  | 225 | size_t i; | 
|  | 226 |  | 
|  | 227 | str = skip_ws (str); | 
|  | 228 |  | 
|  | 229 | /* skip line comment and empty lines: */ | 
|  | 230 | if (*str == '\0' || *str == '#') return; | 
|  | 231 |  | 
|  | 232 | start = str; | 
|  | 233 | str = skip_string (str); | 
|  | 234 | len = str - start; | 
|  | 235 |  | 
|  | 236 | for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i) | 
|  | 237 | { | 
|  | 238 | if (__strncasecmp (start, cmd[i].name, len) == 0 | 
|  | 239 | && strlen (cmd[i].name) == len) | 
|  | 240 | { | 
|  | 241 | c = &cmd[i]; | 
|  | 242 | break; | 
|  | 243 | } | 
|  | 244 | } | 
|  | 245 | if (c == NULL) | 
|  | 246 | { | 
|  | 247 | char *buf; | 
|  | 248 |  | 
|  | 249 | if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"), | 
|  | 250 | fname, line_num, start) < 0) | 
|  | 251 | return; | 
|  | 252 |  | 
|  | 253 | __fxprintf (NULL, "%s", buf); | 
|  | 254 |  | 
|  | 255 | free (buf); | 
|  | 256 | return; | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | /* process args: */ | 
|  | 260 | str = skip_ws (str); | 
|  | 261 |  | 
|  | 262 | if (c->cb == CB_arg_trimdomain_list) | 
|  | 263 | str = arg_trimdomain_list (fname, line_num, str); | 
|  | 264 | else if (c->cb == CB_arg_spoof) | 
|  | 265 | str = arg_spoof (fname, line_num, str); | 
|  | 266 | else if (c->cb == CB_arg_bool) | 
|  | 267 | str = arg_bool (fname, line_num, str, c->arg); | 
|  | 268 | else | 
|  | 269 | /* Ignore the line.  */ | 
|  | 270 | return; | 
|  | 271 |  | 
|  | 272 | if (!str) | 
|  | 273 | return; | 
|  | 274 |  | 
|  | 275 | /* rest of line must contain white space or comment only: */ | 
|  | 276 | while (*str) | 
|  | 277 | { | 
|  | 278 | if (!isspace (*str)) { | 
|  | 279 | if (*str != '#') | 
|  | 280 | { | 
|  | 281 | char *buf; | 
|  | 282 |  | 
|  | 283 | if (__asprintf (&buf, | 
|  | 284 | _("%s: line %d: ignoring trailing garbage `%s'\n"), | 
|  | 285 | fname, line_num, str) < 0) | 
|  | 286 | break; | 
|  | 287 |  | 
|  | 288 | __fxprintf (NULL, "%s", buf); | 
|  | 289 |  | 
|  | 290 | free (buf); | 
|  | 291 | } | 
|  | 292 | break; | 
|  | 293 | } | 
|  | 294 | ++str; | 
|  | 295 | } | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 |  | 
|  | 299 | static void | 
|  | 300 | do_init (void) | 
|  | 301 | { | 
|  | 302 | const char *hconf_name; | 
|  | 303 | int line_num = 0; | 
|  | 304 | char buf[256], *envval; | 
|  | 305 | FILE *fp; | 
|  | 306 |  | 
|  | 307 | memset (&_res_hconf, '\0', sizeof (_res_hconf)); | 
|  | 308 |  | 
|  | 309 | hconf_name = getenv (ENV_HOSTCONF); | 
|  | 310 | if (hconf_name == NULL) | 
|  | 311 | hconf_name = _PATH_HOSTCONF; | 
|  | 312 |  | 
|  | 313 | fp = fopen (hconf_name, "rce"); | 
|  | 314 | if (fp) | 
|  | 315 | { | 
|  | 316 | /* No threads using this stream.  */ | 
|  | 317 | __fsetlocking (fp, FSETLOCKING_BYCALLER); | 
|  | 318 |  | 
|  | 319 | while (fgets_unlocked (buf, sizeof (buf), fp)) | 
|  | 320 | { | 
|  | 321 | ++line_num; | 
|  | 322 | *__strchrnul (buf, '\n') = '\0'; | 
|  | 323 | parse_line (hconf_name, line_num, buf); | 
|  | 324 | } | 
|  | 325 | fclose (fp); | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | envval = getenv (ENV_SPOOF); | 
|  | 329 | if (envval) | 
|  | 330 | arg_spoof (ENV_SPOOF, 1, envval); | 
|  | 331 |  | 
|  | 332 | envval = getenv (ENV_MULTI); | 
|  | 333 | if (envval) | 
|  | 334 | arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI); | 
|  | 335 |  | 
|  | 336 | envval = getenv (ENV_REORDER); | 
|  | 337 | if (envval) | 
|  | 338 | arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER); | 
|  | 339 |  | 
|  | 340 | envval = getenv (ENV_TRIM_ADD); | 
|  | 341 | if (envval) | 
|  | 342 | arg_trimdomain_list (ENV_TRIM_ADD, 1, envval); | 
|  | 343 |  | 
|  | 344 | envval = getenv (ENV_TRIM_OVERR); | 
|  | 345 | if (envval) | 
|  | 346 | { | 
|  | 347 | _res_hconf.num_trimdomains = 0; | 
|  | 348 | arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval); | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | _res_hconf.initialized = 1; | 
|  | 352 | } | 
|  | 353 |  | 
|  | 354 |  | 
|  | 355 | /* Initialize hconf datastructure by reading host.conf file and | 
|  | 356 | environment variables.  */ | 
|  | 357 | void | 
|  | 358 | _res_hconf_init (void) | 
|  | 359 | { | 
|  | 360 | __libc_once_define (static, once); | 
|  | 361 |  | 
|  | 362 | __libc_once (once, do_init); | 
|  | 363 | } | 
|  | 364 |  | 
|  | 365 |  | 
|  | 366 | #if IS_IN (libc) | 
|  | 367 | # if defined SIOCGIFCONF && defined SIOCGIFNETMASK | 
|  | 368 | /* List of known interfaces.  */ | 
|  | 369 | libc_freeres_ptr ( | 
|  | 370 | static struct netaddr | 
|  | 371 | { | 
|  | 372 | int addrtype; | 
|  | 373 | union | 
|  | 374 | { | 
|  | 375 | struct | 
|  | 376 | { | 
|  | 377 | u_int32_t	addr; | 
|  | 378 | u_int32_t	mask; | 
|  | 379 | } ipv4; | 
|  | 380 | } u; | 
|  | 381 | } *ifaddrs); | 
|  | 382 | # endif | 
|  | 383 |  | 
|  | 384 | /* Reorder addresses returned in a hostent such that the first address | 
|  | 385 | is an address on the local subnet, if there is such an address. | 
|  | 386 | Otherwise, nothing is changed. | 
|  | 387 |  | 
|  | 388 | Note that this function currently only handles IPv4 addresses.  */ | 
|  | 389 |  | 
|  | 390 | void | 
|  | 391 | _res_hconf_reorder_addrs (struct hostent *hp) | 
|  | 392 | { | 
|  | 393 | #if defined SIOCGIFCONF && defined SIOCGIFNETMASK | 
|  | 394 | int i, j; | 
|  | 395 | /* Number of interfaces.  Also serves as a flag for the | 
|  | 396 | double-checked locking idiom.  */ | 
|  | 397 | static int num_ifs = -1; | 
|  | 398 | /* Local copy of num_ifs, for non-atomic access.  */ | 
|  | 399 | int num_ifs_local; | 
|  | 400 | /* We need to protect the dynamic buffer handling.  The lock is only | 
|  | 401 | acquired during initialization.  Afterwards, a positive num_ifs | 
|  | 402 | value indicates completed initialization.  */ | 
|  | 403 | __libc_lock_define_initialized (static, lock); | 
|  | 404 |  | 
|  | 405 | /* Only reorder if we're supposed to.  */ | 
|  | 406 | if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0) | 
|  | 407 | return; | 
|  | 408 |  | 
|  | 409 | /* Can't deal with anything but IPv4 for now...  */ | 
|  | 410 | if (hp->h_addrtype != AF_INET) | 
|  | 411 | return; | 
|  | 412 |  | 
|  | 413 | /* This load synchronizes with the release MO store in the | 
|  | 414 | initialization block below.  */ | 
|  | 415 | num_ifs_local = atomic_load_acquire (&num_ifs); | 
|  | 416 | if (num_ifs_local <= 0) | 
|  | 417 | { | 
|  | 418 | struct ifreq *ifr, *cur_ifr; | 
|  | 419 | int sd, num, i; | 
|  | 420 | /* Save errno.  */ | 
|  | 421 | int save = errno; | 
|  | 422 |  | 
|  | 423 | /* Initialize interface table.  */ | 
|  | 424 |  | 
|  | 425 | /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket.  */ | 
|  | 426 | sd = __socket (AF_INET, SOCK_DGRAM, 0); | 
|  | 427 | if (sd < 0) | 
|  | 428 | return; | 
|  | 429 |  | 
|  | 430 | /* Get lock.  */ | 
|  | 431 | __libc_lock_lock (lock); | 
|  | 432 |  | 
|  | 433 | /* Recheck, somebody else might have done the work by now.  No | 
|  | 434 | ordering is required for the load because we have the lock, | 
|  | 435 | and num_ifs is only updated under the lock.  Also see (3) in | 
|  | 436 | the analysis below.  */ | 
|  | 437 | num_ifs_local = atomic_load_relaxed (&num_ifs); | 
|  | 438 | if (num_ifs_local <= 0) | 
|  | 439 | { | 
|  | 440 | /* This is the only block which writes to num_ifs.  It can | 
|  | 441 | be executed several times (sequentially) if | 
|  | 442 | initialization does not yield any interfaces, and num_ifs | 
|  | 443 | remains zero.  However, once we stored a positive value | 
|  | 444 | in num_ifs below, this block cannot be entered again due | 
|  | 445 | to the condition above.  */ | 
|  | 446 | int new_num_ifs = 0; | 
|  | 447 |  | 
|  | 448 | /* Get a list of interfaces.  */ | 
|  | 449 | __ifreq (&ifr, &num, sd); | 
|  | 450 | if (!ifr) | 
|  | 451 | goto cleanup; | 
|  | 452 |  | 
|  | 453 | ifaddrs = malloc (num * sizeof (ifaddrs[0])); | 
|  | 454 | if (!ifaddrs) | 
|  | 455 | goto cleanup1; | 
|  | 456 |  | 
|  | 457 | /* Copy usable interfaces in ifaddrs structure.  */ | 
|  | 458 | for (cur_ifr = ifr, i = 0; i < num; | 
|  | 459 | cur_ifr = __if_nextreq (cur_ifr), ++i) | 
|  | 460 | { | 
|  | 461 | union | 
|  | 462 | { | 
|  | 463 | struct sockaddr sa; | 
|  | 464 | struct sockaddr_in sin; | 
|  | 465 | } ss; | 
|  | 466 |  | 
|  | 467 | if (cur_ifr->ifr_addr.sa_family != AF_INET) | 
|  | 468 | continue; | 
|  | 469 |  | 
|  | 470 | ifaddrs[new_num_ifs].addrtype = AF_INET; | 
|  | 471 | ss.sa = cur_ifr->ifr_addr; | 
|  | 472 | ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr; | 
|  | 473 |  | 
|  | 474 | if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0) | 
|  | 475 | continue; | 
|  | 476 |  | 
|  | 477 | ss.sa = cur_ifr->ifr_netmask; | 
|  | 478 | ifaddrs[new_num_ifs].u.ipv4.mask = ss.sin.sin_addr.s_addr; | 
|  | 479 |  | 
|  | 480 | /* Now we're committed to this entry.  */ | 
|  | 481 | ++new_num_ifs; | 
|  | 482 | } | 
|  | 483 | /* Just keep enough memory to hold all the interfaces we want.  */ | 
|  | 484 | ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0])); | 
|  | 485 | assert (ifaddrs != NULL); | 
|  | 486 |  | 
|  | 487 | cleanup1: | 
|  | 488 | __if_freereq (ifr, num); | 
|  | 489 |  | 
|  | 490 | cleanup: | 
|  | 491 | /* Release lock, preserve error value, and close socket.  */ | 
|  | 492 | errno = save; | 
|  | 493 |  | 
|  | 494 | /* Advertise successful initialization if new_num_ifs is | 
|  | 495 | positive (and no updates to ifaddrs are permitted after | 
|  | 496 | that).  Otherwise, num_ifs remains unchanged, at zero. | 
|  | 497 | This store synchronizes with the initial acquire MO | 
|  | 498 | load.  */ | 
|  | 499 | atomic_store_release (&num_ifs, new_num_ifs); | 
|  | 500 | /* Keep the local copy current, to save another load.  */ | 
|  | 501 | num_ifs_local = new_num_ifs; | 
|  | 502 | } | 
|  | 503 |  | 
|  | 504 | __libc_lock_unlock (lock); | 
|  | 505 |  | 
|  | 506 | __close (sd); | 
|  | 507 | } | 
|  | 508 |  | 
|  | 509 | /* num_ifs_local cannot be negative because the if statement above | 
|  | 510 | covered this case.  It can still be zero if we just performed | 
|  | 511 | initialization, but could not find any interfaces.  */ | 
|  | 512 | if (num_ifs_local == 0) | 
|  | 513 | return; | 
|  | 514 |  | 
|  | 515 | /* The code below accesses ifaddrs, so we need to ensure that the | 
|  | 516 | initialization happens-before this point. | 
|  | 517 |  | 
|  | 518 | The actual initialization is sequenced-before the release store | 
|  | 519 | to num_ifs, and sequenced-before the end of the critical section. | 
|  | 520 |  | 
|  | 521 | This means there are three possible executions: | 
|  | 522 |  | 
|  | 523 | (1) The thread that initialized the data also uses it, so | 
|  | 524 | sequenced-before is sufficient to ensure happens-before. | 
|  | 525 |  | 
|  | 526 | (2) The release MO store of num_ifs synchronizes-with the acquire | 
|  | 527 | MO load, and the acquire MO load is sequenced before the use | 
|  | 528 | of the initialized data below. | 
|  | 529 |  | 
|  | 530 | (3) We enter the critical section, and the relaxed MO load of | 
|  | 531 | num_ifs yields a positive value.  The write to ifaddrs is | 
|  | 532 | sequenced-before leaving the critical section.  Leaving the | 
|  | 533 | critical section happens-before we entered the critical | 
|  | 534 | section ourselves, which means that the write to ifaddrs | 
|  | 535 | happens-before this point. | 
|  | 536 |  | 
|  | 537 | Consequently, all potential writes to ifaddrs (and the data it | 
|  | 538 | points to) happens-before this point.  */ | 
|  | 539 |  | 
|  | 540 | /* Find an address for which we have a direct connection.  */ | 
|  | 541 | for (i = 0; hp->h_addr_list[i]; ++i) | 
|  | 542 | { | 
|  | 543 | struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i]; | 
|  | 544 |  | 
|  | 545 | for (j = 0; j < num_ifs_local; ++j) | 
|  | 546 | { | 
|  | 547 | u_int32_t if_addr    = ifaddrs[j].u.ipv4.addr; | 
|  | 548 | u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask; | 
|  | 549 |  | 
|  | 550 | if (((haddr->s_addr ^ if_addr) & if_netmask) == 0) | 
|  | 551 | { | 
|  | 552 | void *tmp; | 
|  | 553 |  | 
|  | 554 | tmp = hp->h_addr_list[i]; | 
|  | 555 | hp->h_addr_list[i] = hp->h_addr_list[0]; | 
|  | 556 | hp->h_addr_list[0] = tmp; | 
|  | 557 | return; | 
|  | 558 | } | 
|  | 559 | } | 
|  | 560 | } | 
|  | 561 | #endif /* defined(SIOCGIFCONF) && ... */ | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 |  | 
|  | 565 | /* If HOSTNAME has a postfix matching any of the trimdomains, trim away | 
|  | 566 | that postfix.  Notice that HOSTNAME is modified inplace.  Also, the | 
|  | 567 | original code applied all trimdomains in order, meaning that the | 
|  | 568 | same domainname could be trimmed multiple times.  I believe this | 
|  | 569 | was unintentional.  */ | 
|  | 570 | void | 
|  | 571 | _res_hconf_trim_domain (char *hostname) | 
|  | 572 | { | 
|  | 573 | size_t hostname_len, trim_len; | 
|  | 574 | int i; | 
|  | 575 |  | 
|  | 576 | hostname_len = strlen (hostname); | 
|  | 577 |  | 
|  | 578 | for (i = 0; i < _res_hconf.num_trimdomains; ++i) | 
|  | 579 | { | 
|  | 580 | const char *trim = _res_hconf.trimdomain[i]; | 
|  | 581 |  | 
|  | 582 | trim_len = strlen (trim); | 
|  | 583 | if (hostname_len > trim_len | 
|  | 584 | && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0) | 
|  | 585 | { | 
|  | 586 | hostname[hostname_len - trim_len] = '\0'; | 
|  | 587 | break; | 
|  | 588 | } | 
|  | 589 | } | 
|  | 590 | } | 
|  | 591 |  | 
|  | 592 |  | 
|  | 593 | /* Trim all hostnames/aliases in HP according to the trimdomain list. | 
|  | 594 | Notice that HP is modified inplace!  */ | 
|  | 595 | void | 
|  | 596 | _res_hconf_trim_domains (struct hostent *hp) | 
|  | 597 | { | 
|  | 598 | int i; | 
|  | 599 |  | 
|  | 600 | if (_res_hconf.num_trimdomains == 0) | 
|  | 601 | return; | 
|  | 602 |  | 
|  | 603 | _res_hconf_trim_domain (hp->h_name); | 
|  | 604 | for (i = 0; hp->h_aliases[i]; ++i) | 
|  | 605 | _res_hconf_trim_domain (hp->h_aliases[i]); | 
|  | 606 | } | 
|  | 607 | #endif |