glibc/sysdeps/posix/getaddrinfo.c
Ulrich Drepper 11bf311edc [BZ #2510, BZ #2830, BZ #3137, BZ #3313, BZ #3426, BZ #3465, BZ #3480, BZ #3483, BZ #3493, BZ #3514, BZ #3515, BZ #3664, BZ #3673, BZ #3674]
2007-01-11  Jakub Jelinek  <jakub@redhat.com>
	* sysdeps/i386/soft-fp/sfp-machine.h: Remove.
	* sysdeps/x86_64/soft-fp/sfp-machine.h: Likewise.
2007-01-10  Ulrich Drepper  <drepper@redhat.com>
	* io/fts.c: Make sure fts_cur is always valid after return from
	fts_read.
	Patch by Miloslav Trmac <mitr@redhat.com>.
2006-10-27  Richard Sandiford  <richard@codesourcery.com>
	* elf/elf.h (R_MIPS_GLOB_DAT): Define.
	(R_MIPS_NUM): Bump by 1.
2007-01-03  Jakub Jelinek  <jakub@redhat.com>
	* posix/execvp.c: Include alloca.h.
	(allocate_scripts_argv): Renamed to...
	(scripts_argv): ... this.  Don't allocate buffer here nor count
	arguments.
	(execvp): Use alloca if possible.
	* posix/Makefile: Add rules to build and run tst-vfork3 test.
	* posix/tst-vfork3.c: New test.
	* stdlib/Makefile (tst-strtod3-ENV): Define.
2007-01-02  Ulrich Drepper  <drepper@redhat.com>
	* posix/getconf.c: Update copyright year.
	* nss/getent.c: Likewise.
	* iconv/iconvconfig.c: Likewise.
	* iconv/iconv_prog.c: Likewise.
	* elf/ldconfig.c: Likewise.
	* catgets/gencat.c: Likewise.
	* csu/version.c: Likewise.
	* elf/ldd.bash.in: Likewise.
	* elf/sprof.c (print_version): Likewise.
	* locale/programs/locale.c: Likewise.
	* locale/programs/localedef.c: Likewise.
	* nscd/nscd.c (print_version): Likewise.
	* debug/xtrace.sh: Likewise.
	* malloc/memusage.sh: Likewise.
	* malloc/mtrace.pl: Likewise.
	* debug/catchsegv.sh: Likewise.
2006-12-24  Ulrich Drepper  <drepper@redhat.com>
	* malloc/malloc.c (sYSMALLOc): Remove some unnecessary alignment
	attempts.
2006-12-23  Ulrich Drepper  <drepper@redhat.com>
	* posix/wordexp.c: Remove some unnecessary tests.
2006-12-20  SUGIOKA Toshinobu  <sugioka@itonet.co.jp>

	* sysdeps/unix/sysv/linux/sh/bits/shm.h: New file.

	* nss/getXXbyYY_r.c: Include atomic.h.
	(INTERNAL (REENTRANT_NAME)): Write startp after start_fct,
	add atomic_write_barrier () in between.

2006-11-28  Jakub Jelinek  <jakub@redhat.com>
	* elf/dl-support.c: Include dl-procinfo.h.
	* sysdeps/powerpc/dl-procinfo.h (PPC_PLATFORM_POWER4,
	PPC_PLATFORM_PPC970, PPC_PLATFORM_POWER5, PPC_PLATFORM_POWER5_PLUS,
	PPC_PLATFORM_POWER6, PPC_PLATFORM_CELL_BE, PPC_PLATFORM_POWER6X):
	Define.
	(_dl_string_platform): Use PPC_PLATFORM_* macros instead of
	hardcoded constants.
	* sysdeps/powerpc/dl-procinfo.c (_dl_powerpc_platform): Use
	PPC_PLATFORM_* macros for array designators.
2006-11-11  Steven Munroe  <sjmunroe@us.ibm.com>
	* sysdeps/powerpc/dl-procinfo.c (_dl_powerpc_cap_flags): Add 3 new cap
	names to the beginning.
	(_dl_powerpc_platforms): Add "power6x".
	* sysdeps/powerpc/dl-procinfo.h (_DL_HWCAP_FIRST): Decrease.
	(HWCAP_IMPORTANT): Add PPC_FEATURE_HAS_DFP.
	(_DL_PLATFORMS_COUNT): Increase.
	(_dl_string_platform): Handle power6x case.
	* sysdeps/powerpc/sysdep.h (PPC_FEATURE_PA6T, PPC_FEATURE_HAS_DFP,
	PPC_FEATURE_POWER6_EXT): Define.
	(PPC_FEATURE_POWER5, PPC_FEATURE_POWER5_PLUS): Correct Comment.
	[-2^31 .. 2^31) range.
	* sysdeps/unix/sysv/linux/bits/statvfs.h: Define ST_RELATIME.
	* sysdeps/unix/sysv/linux/internal_statvfs.c (__statvfs_getflags):
	Handle relatime mount option.

2006-12-13  Jakub Jelinek  <jakub@redhat.com>
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/setcontext.S: Include
	kernel-features.h.

2006-12-11  Ulrich Drepper  <drepper@redhat.com>

	* stdlib/strtod_l.c (____STRTOF_INTERNAL): Parse thousand
	separators also if no non-zero digits found.
	* stdlib/Makefile (tests): Add tst-strtod3.
	[BZ #3664]
	* stdlib/strtod_l.c (____STRTOF_INTERNAL): Fix test to recognize
	empty parsed strings.
	* stdlib/Makefile (tests): Add tst-strtod2.
	* stdlib/tst-strtod2.c: New file.

	[BZ #3673]
	* stdlib/strtod_l.c (____STRTOF_INTERNAL): Fix exp_limit
	computation.
	* stdlib/Makefile (tests): Add tst-atof2.
	* stdlib/tst-atof2.c: New file.

	[BZ #3674]
	* stdlib/strtod_l.c (____STRTOF_INTERNAL): Adjust exponent value
	correctly if removing trailing zero of hex-float.
	* stdlib/Makefile (tests): Add tst-atof1.
	* stdlib/tst-atof1.c: New file.

	* misc/mntent_r.c (__hasmntopt): Check p[optlen] even when p == rest.
	Start searching for next comma at p rather than rest.
	* misc/Makefile (tests): Add tst-mntent2.
	* misc/tst-mntent2.c: New test.

2006-12-08  Ulrich Drepper  <drepper@redhat.com>
	* malloc/memusage.c: Handle realloc with new size of zero and
	non-NULL pointer correctly.
	(me): Really write first record twice.
	(struct entry): Make format bi-arch safe.
	(dest): Write out more realloc statistics.
	* malloc/memusagestat.c (struct entry): Make format bi-arch safe.
2006-12-05  Jakub Jelinek  <jakub@redhat.com>
	* nis/nis_subr.c (nis_getnames): Revert last change.
2006-12-03  Kaz Kojima  <kkojima@rr.iij4u.or.jp>
	* sysdeps/unix/sysv/linux/sh/sys/io.h: Removed.
2006-11-30  H.J. Lu  <hongjiu.lu@intel.com>
	* sysdeps/i386/i686/memcmp.S: Use jump table as the base of
	jump table entries.

2006-11-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* sysdeps/unix/sysv/linux/i386/clone.S: Provide CFI for the outermost
	`clone' function to ensure proper unwinding stop of gdb.
	* sysdeps/unix/sysv/linux/x86_64/clone.S: Likewise.

2006-12-01  Ulrich Drepper  <drepper@redhat.com>

	* nscd/nscd.init: Remove obsolete and commented-out -S option
	handling.

2006-11-23  Jakub Jelinek  <jakub@redhat.com>

	[BZ #3514]
	* manual/string.texi (strncmp): Fix pastos from wcscmp description.

	[BZ #3515]
	* manual/string.texi (strtok): Remove duplicate paragraph.

2006-12-01  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* sysdeps/unix/sysv/linux/x86_64/sigaction.c: Fix compatibility with
	libgcc not supporting `rflags' unwinding (register # >= 17).

2006-11-30  Jakub Jelinek  <jakub@redhat.com>

	* sunrpc/svc_run.c (svc_run): Set my_pollfd to new_pollfd if realloc
	succeeded.

2006-11-29  Daniel Jacobowitz  <dan@codesourcery.com>
	    Jakub Jelinek  <jakub@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* sysdeps/unix/sysv/linux/x86_64/sigaction.c (restore_rt): Add correct
	unwind information.
	* sysdeps/unix/sysv/linux/x86_64/Makefile: Provide symbols for
	'restore_rt' even in the 'signal' directory.
	* sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym: Extend the regs list.
	malloc crashed.  Don't allocate memory unnecessarily in each
	loop.
2006-10-21  Jakub Jelinek  <jakub@redhat.com>
	* resolv/mapv4v6addr.h (map_v4v6_address): Fix last change.
2006-11-20  Ulrich Drepper  <drepper@redhat.com>
	* resolv/mapv4v6addr.h (map_v4v6_address): Optimize a bit.
2006-11-18  Bruno Haible  <bruno@clisp.org>
	* sysdeps/unix/sysv/linux/i386/getgroups.c (__getgroups): Invoke
	__sysconf only after having tried to call getgroups32.
2006-11-19  Ulrich Drepper  <drepper@redhat.com>
	* nss/nss_files/files-hosts.c (LINE_PARSER): Support IPv6-style
	addresses for IPv4 queries if they can be mapped.
2006-11-16  Jakub Jelinek  <jakub@redhat.com>
	* sysdeps/x86_64/fpu/s_copysignf.S (__copysignf): Switch to .text.
	* sysdeps/x86_64/fpu/s_copysign.S (__copysign): Likewise.
	(signmask): Add .size directive.
	(othermask): Add .type directive.
2006-11-14  Ulrich Drepper  <drepper@redhat.com>
	* po/nl.po: Update from translation team.
	* timezone/zdump.c: Redo fix for BZ #3137.
2006-11-14  Jakub Jelinek  <jakub@redhat.com>
	* nss/nss_files/files-alias.c (get_next_alias): Set line back
	to first_unused after parsing :include: file.
	* timezone/africa: Update from tzdata2006o.
	* timezone/antarctica: Likewise.
	* timezone/asia: Likewise.
	* timezone/australasia: Likewise.
	* timezone/backward: Likewise.
	* timezone/europe: Likewise.
	* timezone/iso3166.tab: Likewise.
	* timezone/northamerica: Likewise.
	* timezone/southamerica: Likewise.
	* timezone/zone.tab: Likewise.

	* time/tzfile.c (__tzfile_read): Extend to handle new file format
	on machines with 64-bit time_t.

	* timezone/checktab.awk: Update from tzcode2006o.
	* timezone/ialloc.c: Likewise.
	* timezone/private.h: Likewise.
	* timezone/scheck.c: Likewise.
	* timezone/tzfile.h: Likewise.
	* timezone/tzselect.ksh: Likewise.
	* timezone/zdump.c: Likewise.
	* timezone/zic.c: Likewise.

	[BZ #3483]
	* elf/ldconfig.c (main): Call setlocale and textdomain.
	Patch mostly by Benno Schulenberg <bensberg@justemail.net>.

	[BZ #3480]
	* manual/argp.texi: Fix typos.
	* manual/charset.texi: Likewise.
	* manual/errno.texi: Likewise.
	* manual/filesys.texi: Likewise.
	* manual/lang.texi: Likewise.
	* manual/maint.texi: Likewise.
	* manual/memory.texi: Likewise.
	* manual/message.texi: Likewise.
	* manual/resource.texi: Likewise.
	* manual/search.texi: Likewise.
	* manual/signal.texi: Likewise.
	* manual/startup.texi: Likewise.
	* manual/stdio.texi: Likewise.
	* manual/sysinfo.texi: Likewise.
	* manual/syslog.texi: Likewise.
	* manual/time.texi: Likewise.
	Patch by Ralf Wildenhues <Ralf.Wildenhues@gmx.de>.

	[BZ #3465]
	* sunrpc/clnt_raw.c: Minimal message improvements.
	* sunrpc/pm_getmaps.c: Likewise.
	* nis/nss_nisplus/nisplus-publickey.c: Likewise.
	* nis/nis_print_group_entry.c: Likewise.
	* locale/programs/repertoire.c: Likewise.
	* locale/programs/charmap.c: Likewise.
	* malloc/memusage.sh: Likewise.
	* elf/dl-deps.c: Likewise.
	* locale/programs/ld-collate.c: Likewise.
	* libio/vswprintf.c: Likewise.
	* malloc/memusagestat.c: Likewise.
	* sunrpc/auth_unix.c: Likewise.
	* sunrpc/rpc_main.c: Likewise.
	* nscd/cache.c: Likewise.
	* locale/programs/repertoire.c: Unify output messages.
	* locale/programs/charmap.c: Likewise.
	* locale/programs/ld-ctype.c: Likewise.
	* locale/programs/ld-monetary.c: Likewise.
	* locale/programs/ld-numeric.c: Likewise.
	* locale/programs/ld-time.c: Likewise.
	* elf/ldconfig.c: Likewise.
	* nscd/selinux.c: Likewise.
	* elf/cache.c: Likewise.
	Patch mostly by Benno Schulenberg <bensberg@justemail.net>.

2006-11-10  Jakub Jelinek  <jakub@redhat.com>

	* string/strxfrm_l.c (STRXFRM): Fix trailing \1 optimization
	if N is one bigger than return value.
	* string/tst-strxfrm2.c (do_test): Also test strxfrm with l1 + 1
	and l1 last arguments, if buf is defined, verify the return value
	equals to strlen (buf) and verify no byte beyond passed length
	is modified.

2006-11-10  Ulrich Drepper  <drepper@redhat.com>

	* po/sv.po: Update from translation team.

	* sysdeps/gnu/siglist.c (__old_sys_siglist, __old_sys_sigabbrev):
	Use __new_sys_siglist instead of _sys_siglist_internal as
	second macro argument.
	(_old_sys_siglist): Use declare_symbol_alias macro instead of
	strong_alias.
2006-11-09  Ulrich Drepper  <drepper@redhat.com>

	[BZ #3493]
	* posix/unistd.h (sysconf): Remove const attribute.

	* sysdeps/posix/getaddrinfo.c (getaddrinfo): Fix test for
	temporary or deprecated addresses.
	Patch by Sridhar Samudrala <sri@us.ibm.com>.

	* string/Makefile (tests): Add tst-strxfrm2.
	* string/tst-strxfrm2.c: New file.

2006-10-09  Jakub Jelinek  <jakub@redhat.com>

	* elf/dl-debug.c (_dl_debug_initialize): Check r->r_map for 0
	rather than r->r_brk.
	* string/strxfrm_l.c (STRXFRM): Do the trailing \1 removal
	optimization even if needed > n.

2006-11-07  Jakub Jelinek  <jakub@redhat.com>

	* include/libc-symbols.h (declare_symbol): Rename to...
	(declare_symbol_alias): ... this.  Add ORIGINAL argument, imply
	strong_alias (ORIGINAL, SYMBOL) in asm to make sure it preceedes
	.size directive.
	* sysdeps/gnu/errlist-compat.awk: Adjust for declare_symbol_alias
	changes.
	* sysdeps/gnu/siglist.c: Likewise.

2006-11-03  Steven Munroe  <sjmunroe@us.ibm.com>

	* sysdeps/powerpc/fpu/bits/mathinline.h
	[__LIBC_INTERNAL_MATH_INLINES]: Moved to ...
	* sysdeps/powerpc/fpu/math_private.h: ...here.  New file.

2006-11-05  Ulrich Drepper  <drepper@redhat.com>

	* sysdeps/unix/sysv/linux/i386/sysconf.c (intel_check_word):
	Update handling of cache descriptor 0x49 for new models.
	* sysdeps/unix/sysv/linux/x86_64/sysconf.c (intel_check_word):
	Likewise.

2006-11-02  Ulrich Drepper  <drepper@redhat.com>

	* configure.in: Work around ld --help change and avoid -z relro
	test completely if the architecture doesn't care about security.

2006-11-01  Ulrich Drepper  <drepper@redhat.com>

	* po/sv.po: Update from translation team.

2006-10-31  Ulrich Drepper  <drepper@redhat.com>

	* stdlib/atexit.c (atexit): Don't mark as hidden when used to
	generate compatibility version.

2006-10-29  Ulrich Drepper  <drepper@redhat.com>

	* configure.in: Relax -z relro requirement a bit.

	* po/sv.po: Update from translation team.

2006-10-29  Jakub Jelinek  <jakub@redhat.com>

	* elf/dl-sym.c (do_sym): Use RTLD_SINGLE_THREAD_P.
	* elf/dl-runtime.c (_dl_fixup, _dl_profile_fixup): Likewise.
	* elf/dl-close.c (_dl_close_worker): Likewise.
	* elf/dl-open.c (_dl_open_worker): Likewise.
	* sysdeps/generic/sysdep-cancel.h (RTLD_SINGLE_THREAD_P): Define.

	* configure.in: Require assembler support for visibility, compiler
	support for visibility and aliases, linker support for various -z
	options.
	* Makeconfig: Remove conditional code which now is unnecessary.
	* config.h.in: Likewise.
	* config.make.in: Likewise.
	* dlfcn/Makefile: Likewise.
	* elf/Makefile: Likewise.
	* elf/dl-load.c: Likewise.
	* elf/rtld.c: Likewise.
	* include/libc-symbols.h: Likewise.
	* include/stdio.h: Likewise.
	* io/Makefile: Likewise.
	* io/fstat.c: Likewise.
	* io/fstat64.c: Likewise.
	* io/fstatat.c: Likewise.
	* io/fstatat64.c: Likewise.
	* io/lstat.c: Likewise.
	* io/lstat64.c: Likewise.
	* io/mknod.c: Likewise.
	* io/mknodat.c: Likewise.
	* io/stat.c: Likewise.
	* io/stat64.c: Likewise.
	* libio/stdio.c: Likewise.
	* nscd/Makefile: Likewise.
	* stdlib/Makefile: Likewise.
	* stdlib/atexit.c: Likewise.
	* sysdeps/generic/ldsodefs.h: Likewise.
	* sysdeps/i386/dl-machine.h: Likewise.
	* sysdeps/i386/sysdep.h: Likewise.
	* sysdeps/i386/i686/memcmp.S: Likewise.
	* sysdeps/powerpc/powerpc32/sysdep.h: Likewise.
	* sysdeps/unix/sysv/linux/i386/sigaction.c: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/sigaction.c: Likewise.

	* Makerules: USE_TLS support is now default.
	* tls.make.c: Likewise.
	* csu/Versions: Likewise.
	* csu/libc-start.c: Likewise.
	* csu/libc-tls.c: Likewise.
	* csu/version.c: Likewise.
	* dlfcn/dlinfo.c: Likewise.
	* elf/dl-addr.c: Likewise.
	* elf/dl-cache.c: Likewise.
	* elf/dl-close.c: Likewise.
	* elf/dl-iteratephdr.c: Likewise.
	* elf/dl-load.c: Likewise.
	* elf/dl-lookup.c: Likewise.
	* elf/dl-object.c: Likewise.
	* elf/dl-open.c: Likewise.
	* elf/dl-reloc.c: Likewise.
	* elf/dl-support.c: Likewise.
	* elf/dl-sym.c: Likewise.
	* elf/dl-sysdep.c: Likewise.
	* elf/dl-tls.c: Likewise.
	* elf/ldconfig.c: Likewise.
	* elf/rtld.c: Likewise.
	* elf/tst-tls-dlinfo.c: Likewise.
	* elf/tst-tls1.c: Likewise.
	* elf/tst-tls10.h: Likewise.
	* elf/tst-tls14.c: Likewise.
	* elf/tst-tls2.c: Likewise.
	* elf/tst-tls3.c: Likewise.
	* elf/tst-tls4.c: Likewise.
	* elf/tst-tls5.c: Likewise.
	* elf/tst-tls6.c: Likewise.
	* elf/tst-tls7.c: Likewise.
	* elf/tst-tls8.c: Likewise.
	* elf/tst-tls9.c: Likewise.
	* elf/tst-tlsmod1.c: Likewise.
	* elf/tst-tlsmod13.c: Likewise.
	* elf/tst-tlsmod13a.c: Likewise.
	* elf/tst-tlsmod14a.c: Likewise.
	* elf/tst-tlsmod2.c: Likewise.
	* elf/tst-tlsmod3.c: Likewise.
	* elf/tst-tlsmod4.c: Likewise.
	* elf/tst-tlsmod5.c: Likewise.
	* elf/tst-tlsmod6.c: Likewise.
	* include/errno.h: Likewise.
	* include/link.h: Likewise.
	* include/tls.h: Likewise.
	* locale/global-locale.c: Likewise.
	* locale/localeinfo.h: Likewise.
	* malloc/arena.c: Likewise.
	* malloc/hooks.c: Likewise.
	* malloc/malloc.c: Likewise.
	* resolv/Versions: Likewise.
	* sysdeps/alpha/dl-machine.h: Likewise.
	* sysdeps/alpha/libc-tls.c: Likewise.
	* sysdeps/generic/ldsodefs.h: Likewise.
	* sysdeps/generic/tls.h: Likewise.
	* sysdeps/i386/dl-machine.h: Likewise.
	* sysdeps/ia64/dl-machine.h: Likewise.
	* sysdeps/ia64/libc-tls.c: Likewise.
	* sysdeps/mach/hurd/fork.c: Likewise.
	* sysdeps/mach/hurd/i386/tls.h: Likewise.
	* sysdeps/powerpc/powerpc32/dl-machine.c: Likwise.
	* sysdeps/powerpc/powerpc32/dl-machine.h: Likewise.
	* sysdeps/powerpc/powerpc64/dl-machine.h: Likewise.
	* sysdeps/s390/libc-tls.c: Likewise.
	* sysdeps/s390/s390-32/dl-machine.h: Likewise.
	* sysdeps/s390/s390-64/dl-machine.h: Likewise.
	* sysdeps/sh/dl-machine.h: Likewise.
	* sysdeps/sparc/sparc32/dl-machine.h: Likewise.
	* sysdeps/sparc/sparc64/dl-machine.h: Likewise.
	* sysdeps/x86_64/dl-machine.h: Likewise.

	[BZ #3426]
	* stdlib/stdlib.h: Adjust comment for canonicalize_file_name to
	reality.

2006-10-27  Jakub Jelinek  <jakub@redhat.com>

	* elf/dl-lookup.c (_dl_debug_bindings): Remove unused symbol_scope
	argument.
	(_dl_lookup_symbol_x): Adjust caller.

	* sysdeps/generic/ldsodefs.h (struct link_namespaces): Remove
	_ns_global_scope.
	* elf/rtld.c (dl_main): Don't initialize _ns_global_scope.

	* elf/dl-libc.c: Revert l_scope name changes.
	* elf/dl-load.c: Likewise.
	* elf/dl-object.c: Likewise.
	* elf/rtld.c: Likewise.
	* elf/dl-close.c (_dl_close): Likewise.
	* elf/dl-open.c (dl_open_worker): Likewise.  If not SINGLE_THREAD_P,
	always use __rtld_mrlock_{change,done}.  Always free old scope list
	here if not l_scope_mem.
	* elf/dl-runtime.c (_dl_fixup, _dl_profile_fixup): Revert l_scope name
	change.  Never free scope list here.  Just __rtld_mrlock_lock before
	the lookup and __rtld_mrlock_unlock it after the lookup.
	* elf/dl-sym.c: Likewise.
	* include/link.h (struct r_scoperec): Remove.
	(struct link_map): Replace l_scoperec with l_scope, l_scoperec_mem
	with l_scope_mem and l_scoperec_lock with l_scope_lock.

2006-10-25  Ulrich Drepper  <drepper@redhat.com>

	* sysdeps/gnu/netinet/tcp.h: Define TCP_CONGESTION.

2006-10-18  Ulrich Drepper  <drepper@redhat.com>

	* configure.in: Disable building profile libraries by default.

2006-10-18  Ulrich Drepper  <drepper@redhat.com>

	* elf/dl-lookup.c (_dl_lookup_symbol_x): Add warning to
	_dl_lookup_symbol_x code.

2006-10-17  Jakub Jelinek  <jakub@redhat.com>

	* elf/dl-runtime.c: Include sysdep-cancel.h.
	(_dl_fixup, _dl_profile_fixup): Use __rtld_mrlock_* and
	scoperec->nusers only if !SINGLE_THREAD_P.  Use atomic_*
	instead of catomic_* macros.
	* elf/dl-sym.c: Include sysdep-cancel.h.
	(do_sym): Use __rtld_mrlock_* and scoperec->nusers only
	if !SINGLE_THREAD_P.  Use atomic_* instead of catomic_* macros.
	* elf/dl-close.c: Include sysdep-cancel.h.
	(_dl_close): Use __rtld_mrlock_* and scoperec->nusers only
	if !SINGLE_THREAD_P.  Use atomic_* instead of catomic_* macros.
	* elf/dl-open.c: Include sysdep-cancel.h.
	(dl_open_worker): Use __rtld_mrlock_* and scoperec->nusers only
	if !SINGLE_THREAD_P.  Use atomic_* instead of catomic_* macros.

2006-10-17  Jakub Jelinek  <jakub@redhat.com>

	[BZ #3313]
	* malloc/malloc.c (malloc_consolidate): Set maxfb to address of last
	fastbin rather than end of fastbin array.

2006-10-18  Ulrich Drepper  <drepper@redhat.com>

	* sysdeps/i386/i486/bits/atomic.h (catomic_decrement): Use correct
	body macro.
	* sysdeps/x86_64/bits/atomic.h
	(__arch_c_compare_and_exchange_val_64_acq): Add missing casts.
	(catomic_decrement): Use correct body macro.

2006-10-17  Jakub Jelinek  <jakub@redhat.com>

	* include/atomic.h: Add a unique prefix to all local variables
	in macros.
	* csu/tst-atomic.c (do_test): Test also catomic_* macros.

2006-10-14  Ulrich Drepper  <drepper@redhat.com>

	* resolv/arpa/nameser.h: Document that ns_t_a6 is deprecated.

	[BZ #3313]
	* malloc/malloc.c (malloc_consolidate): Don't use get_fast_max to
	determine highest fast bin to consolidate, always look into all of
	them.
	(do_check_malloc_state): Only require for empty bins for large
	sizes in main arena.

	* libio/stdio.h: Add more __wur attributes.

2006-11-12  Andreas Jaeger  <aj@suse.de>

	[BZ #2510]
	* manual/search.texi (Hash Search Function): Clarify.
	(Array Search Function): Clarify.

2006-11-12  Joseph Myers  <joseph@codesourcery.com>

	[BZ #2830]
	* math/atest-exp.c (main): Cast hex value to mp_limb_t before
	shifting.
	* math/atest-exp2.c (read_mpn_hex): Likewise.
	* math/atest-sincos.c (main): Likewise.

	* sysdeps/unix/sysv/linux/syscalls.list: Add epoll_pwait.
	* sysdeps/unix/sysv/linux/sys/epoll.h: Declare epoll_pwait.
	* sysdeps/unix/sysv/linux/Versions (libc): Add epoll_pwait for
	version GLIBC_2.6.
	* Versions.def: Add GLIBC_2.6 for libc.

	* sysdeps/i386/i486/bits/atomic.h: Add catomic_* support.

2006-10-11  Jakub Jelinek  <jakub@redhat.com>

	* malloc/malloc.c (_int_malloc): Remove unused any_larger variable.

	* nis/nis_defaults.c (__nis_default_access): Don't call getenv twice.

	* nis/nis_subr.c (nis_getnames): Use __secure_getenv instead of getenv.
	* sysdeps/generic/unsecvars.h: Add NIS_PATH.

2006-10-11  Ulrich Drepper  <drepper@redhat.com>

	* include/atomic.c: Define catomic_* operations.
	* sysdeps/x86_64/bits/atomic.h: Likewise.  Fix a few minor problems.
	* stdlib/cxa_finalize.c: Use catomic_* operations instead of atomic_*.
	* malloc/memusage.c: Likewise.
	* gmon/mcount.c: Likewise.
	* elf/dl-close.c: Likewise.
	* elf/dl-open.c: Likewise.
	* elf/dl-profile.c: Likewise.
	* elf/dl-sym.c: Likewise.
	* elf/dl-runtime.c: Likewise.
	* elf/dl-fptr.c: Likewise.
	* resolv/res_libc.c: Likewise.

2006-10-10  Roland McGrath  <roland@frob.com>
	* sysdeps/mach/hurd/utimes.c: Use a union to avoid an improper cast.
	* sysdeps/mach/hurd/futimes.c: Likewise.
	* sysdeps/mach/hurd/lutimes.c: Likewise.

2006-10-09  Ulrich Drepper  <drepper@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	Implement reference counting of scope records.
	* elf/dl-close.c (_dl_close): Remove all scopes from removed objects
	from the list in objects which remain.  Always allocate new scope
	record.
	* elf/dl-open.c (dl_open_worker): When growing array for scopes,
	don't resize, allocate a new one.
	* elf/dl-runtime.c: Update reference counters before using a scope
	array.
	* elf/dl-sym.c: Likewise.
	* elf/dl-libc.c: Adjust for l_scope name change.
	* elf/dl-load.c: Likewise.
	* elf/dl-object.c: Likewise.
	* elf/rtld.c: Likewise.
	* include/link.h: Include <rtld-lowlevel.h>.  Define struct
	r_scoperec.  Replace r_scope with pointer to r_scoperec structure.
	Add l_scoperec_lock.
	* sysdeps/generic/ldsodefs.h: Include <rtld-lowlevel.h>.
	* sysdeps/generic/rtld-lowlevel.h: New file.

	* include/atomic.h: Rename atomic_and to atomic_and_val and
	atomic_or to atomic_or_val.  Define new macros atomic_and and
	atomic_or which do not return values.
	* sysdeps/x86_64/bits/atomic.h: Define atomic_and and atomic_or.
	Various cleanups.
	* sysdeps/i386/i486/bits/atomic.h: Likewise.

	* po/sv.po: Update from translation team.

2006-10-07  Ulrich Drepper  <drepper@redhat.com>

	* Versions.def: Add GLIBC_2.6 to libpthread.

	* include/shlib-compat.h (SHLIB_COMPAT): Expand parameters before use.
	(versioned_symbol): Likewise.
	(compat_symbol): Likewise.

	* po/tr.po: Update from translation team.
	* nis/Banner: Removed.  It's been integral part forever and the
	author info is incomplete anyway.
	* libio/Banner: Likewise.

2006-10-06  Ulrich Drepper  <drepper@redhat.com>

	* version.h (VERSION): Bump to 2.5.90 for new development tree.
2007-01-11 21:51:07 +00:00

2165 lines
55 KiB
C

/* The Inner Net License, Version 2.00
The author(s) grant permission for redistribution and use in source and
binary forms, with or without modification, of the software and documentation
provided that the following conditions are met:
0. If you receive a version of the software that is specifically labelled
as not being for redistribution (check the version message and/or README),
you are not permitted to redistribute that version of the software in any
way or form.
1. All terms of the all other applicable copyrights and licenses must be
followed.
2. Redistributions of source code must retain the authors' copyright
notice(s), this list of conditions, and the following disclaimer.
3. Redistributions in binary form must reproduce the authors' copyright
notice(s), this list of conditions, and the following disclaimer in the
documentation and/or other materials provided with the distribution.
4. [The copyright holder has authorized the removal of this clause.]
5. Neither the name(s) of the author(s) nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
If these license terms cause you a real problem, contact the author. */
/* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <resolv.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <nsswitch.h>
#include <bits/libc-lock.h>
#include <not-cancel.h>
#include <nscd/nscd-client.h>
#include <nscd/nscd_proto.h>
#ifdef HAVE_LIBIDN
extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
extern int __idna_to_unicode_lzlz (const char *input, char **output,
int flags);
# include <libidn/idna.h>
#endif
#define GAIH_OKIFUNSPEC 0x0100
#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
#ifndef UNIX_PATH_MAX
# define UNIX_PATH_MAX 108
#endif
struct gaih_service
{
const char *name;
int num;
};
struct gaih_servtuple
{
struct gaih_servtuple *next;
int socktype;
int protocol;
int port;
};
static const struct gaih_servtuple nullserv;
struct gaih_addrtuple
{
struct gaih_addrtuple *next;
char *name;
int family;
uint32_t addr[4];
uint32_t scopeid;
};
struct gaih_typeproto
{
int socktype;
int protocol;
char name[4];
int protoflag;
};
/* Values for `protoflag'. */
#define GAI_PROTO_NOSERVICE 1
#define GAI_PROTO_PROTOANY 2
static const struct gaih_typeproto gaih_inet_typeproto[] =
{
{ 0, 0, "", 0 },
{ SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
{ SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
{ SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
{ 0, 0, "", 0 }
};
struct gaih
{
int family;
int (*gaih)(const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai,
unsigned int *naddrs);
};
static const struct addrinfo default_hints =
{
.ai_flags = AI_DEFAULT,
.ai_family = PF_UNSPEC,
.ai_socktype = 0,
.ai_protocol = 0,
.ai_addrlen = 0,
.ai_addr = NULL,
.ai_canonname = NULL,
.ai_next = NULL
};
#if 0
/* Using Unix sockets this way is a security risk. */
static int
gaih_local (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai)
{
struct utsname utsname;
if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
return GAIH_OKIFUNSPEC | -EAI_NONAME;
if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
if (uname (&utsname) < 0)
return -EAI_SYSTEM;
if (name != NULL)
{
if (strcmp(name, "localhost") &&
strcmp(name, "local") &&
strcmp(name, "unix") &&
strcmp(name, utsname.nodename))
return GAIH_OKIFUNSPEC | -EAI_NONAME;
}
if (req->ai_protocol || req->ai_socktype)
{
const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
while (tp->name[0]
&& ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
|| (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
|| (req->ai_protocol != 0
&& !(tp->protoflag & GAI_PROTO_PROTOANY)
&& req->ai_protocol != tp->protocol)))
++tp;
if (! tp->name[0])
{
if (req->ai_socktype)
return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
else
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
*pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
+ ((req->ai_flags & AI_CANONNAME)
? (strlen(utsname.nodename) + 1): 0));
if (*pai == NULL)
return -EAI_MEMORY;
(*pai)->ai_next = NULL;
(*pai)->ai_flags = req->ai_flags;
(*pai)->ai_family = AF_LOCAL;
(*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
(*pai)->ai_protocol = req->ai_protocol;
(*pai)->ai_addrlen = sizeof (struct sockaddr_un);
(*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
#ifdef _HAVE_SA_LEN
((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
sizeof (struct sockaddr_un);
#endif /* _HAVE_SA_LEN */
((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
if (service)
{
struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
if (strchr (service->name, '/') != NULL)
{
if (strlen (service->name) >= sizeof (sunp->sun_path))
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
strcpy (sunp->sun_path, service->name);
}
else
{
if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
sizeof (sunp->sun_path))
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
__stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
}
}
else
{
/* This is a dangerous use of the interface since there is a time
window between the test for the file and the actual creation
(done by the caller) in which a file with the same name could
be created. */
char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
0) != 0
|| __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
return -EAI_SYSTEM;
}
if (req->ai_flags & AI_CANONNAME)
(*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
+ sizeof (struct sockaddr_un),
utsname.nodename);
else
(*pai)->ai_canonname = NULL;
return 0;
}
#endif /* 0 */
static int
gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
const struct addrinfo *req, struct gaih_servtuple *st)
{
struct servent *s;
size_t tmpbuflen = 1024;
struct servent ts;
char *tmpbuf;
int r;
do
{
tmpbuf = __alloca (tmpbuflen);
r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
&s);
if (r != 0 || s == NULL)
{
if (r == ERANGE)
tmpbuflen *= 2;
else
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
while (r);
st->next = NULL;
st->socktype = tp->socktype;
st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
? req->ai_protocol : tp->protocol);
st->port = s->s_port;
return 0;
}
#define gethosts(_family, _type) \
{ \
int i; \
int herrno; \
struct hostent th; \
struct hostent *h; \
char *localcanon = NULL; \
no_data = 0; \
while (1) { \
rc = 0; \
status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen, \
&rc, &herrno, NULL, &localcanon)); \
if (rc != ERANGE || herrno != NETDB_INTERNAL) \
break; \
tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
} \
if (status == NSS_STATUS_SUCCESS && rc == 0) \
h = &th; \
else \
h = NULL; \
if (rc != 0) \
{ \
if (herrno == NETDB_INTERNAL) \
{ \
__set_h_errno (herrno); \
return -EAI_SYSTEM; \
} \
if (herrno == TRY_AGAIN) \
no_data = EAI_AGAIN; \
else \
no_data = herrno == NO_DATA; \
} \
else if (h != NULL) \
{ \
for (i = 0; h->h_addr_list[i]; i++) \
{ \
if (*pat == NULL) \
{ \
*pat = __alloca (sizeof (struct gaih_addrtuple)); \
(*pat)->scopeid = 0; \
} \
uint32_t *addr = (*pat)->addr; \
(*pat)->next = NULL; \
(*pat)->name = i == 0 ? strdupa (h->h_name) : NULL; \
if (_family == AF_INET && req->ai_family == AF_INET6) \
{ \
(*pat)->family = AF_INET6; \
addr[3] = *(uint32_t *) h->h_addr_list[i]; \
addr[2] = htonl (0xffff); \
addr[1] = 0; \
addr[0] = 0; \
} \
else \
{ \
(*pat)->family = _family; \
memcpy (addr, h->h_addr_list[i], sizeof(_type)); \
} \
pat = &((*pat)->next); \
} \
\
if (localcanon != NULL && canon == NULL) \
canon = strdupa (localcanon); \
\
if (_family == AF_INET6 && i > 0) \
got_ipv6 = true; \
} \
}
typedef enum nss_status (*nss_gethostbyname3_r)
(const char *name, int af, struct hostent *host,
char *buffer, size_t buflen, int *errnop,
int *h_errnop, int32_t *ttlp, char **canonp);
typedef enum nss_status (*nss_getcanonname_r)
(const char *name, char *buffer, size_t buflen, char **result,
int *errnop, int *h_errnop);
extern service_user *__nss_hosts_database attribute_hidden;
static int
gaih_inet (const char *name, const struct gaih_service *service,
const struct addrinfo *req, struct addrinfo **pai,
unsigned int *naddrs)
{
const struct gaih_typeproto *tp = gaih_inet_typeproto;
struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
struct gaih_addrtuple *at = NULL;
int rc;
bool got_ipv6 = false;
const char *canon = NULL;
const char *orig_name = name;
if (req->ai_protocol || req->ai_socktype)
{
++tp;
while (tp->name[0]
&& ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
|| (req->ai_protocol != 0
&& !(tp->protoflag & GAI_PROTO_PROTOANY)
&& req->ai_protocol != tp->protocol)))
++tp;
if (! tp->name[0])
{
if (req->ai_socktype)
return GAIH_OKIFUNSPEC | -EAI_SOCKTYPE;
else
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
int port = 0;
if (service != NULL)
{
if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
if (service->num < 0)
{
if (tp->name[0])
{
st = (struct gaih_servtuple *)
__alloca (sizeof (struct gaih_servtuple));
if ((rc = gaih_inet_serv (service->name, tp, req, st)))
return rc;
}
else
{
struct gaih_servtuple **pst = &st;
for (tp++; tp->name[0]; tp++)
{
struct gaih_servtuple *newp;
if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
continue;
if (req->ai_socktype != 0
&& req->ai_socktype != tp->socktype)
continue;
if (req->ai_protocol != 0
&& !(tp->protoflag & GAI_PROTO_PROTOANY)
&& req->ai_protocol != tp->protocol)
continue;
newp = (struct gaih_servtuple *)
__alloca (sizeof (struct gaih_servtuple));
if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
{
if (rc & GAIH_OKIFUNSPEC)
continue;
return rc;
}
*pst = newp;
pst = &(newp->next);
}
if (st == (struct gaih_servtuple *) &nullserv)
return GAIH_OKIFUNSPEC | -EAI_SERVICE;
}
}
else
{
port = htons (service->num);
goto got_port;
}
}
else
{
got_port:
if (req->ai_socktype || req->ai_protocol)
{
st = __alloca (sizeof (struct gaih_servtuple));
st->next = NULL;
st->socktype = tp->socktype;
st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
? req->ai_protocol : tp->protocol);
st->port = port;
}
else
{
/* Neither socket type nor protocol is set. Return all socket types
we know about. */
struct gaih_servtuple **lastp = &st;
for (++tp; tp->name[0]; ++tp)
{
struct gaih_servtuple *newp;
newp = __alloca (sizeof (struct gaih_servtuple));
newp->next = NULL;
newp->socktype = tp->socktype;
newp->protocol = tp->protocol;
newp->port = port;
*lastp = newp;
lastp = &newp->next;
}
}
}
if (name != NULL)
{
at = __alloca (sizeof (struct gaih_addrtuple));
at->family = AF_UNSPEC;
at->scopeid = 0;
at->next = NULL;
#ifdef HAVE_LIBIDN
if (req->ai_flags & AI_IDN)
{
int idn_flags = 0;
if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
idn_flags |= IDNA_ALLOW_UNASSIGNED;
if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
idn_flags |= IDNA_USE_STD3_ASCII_RULES;
char *p = NULL;
rc = __idna_to_ascii_lz (name, &p, idn_flags);
if (rc != IDNA_SUCCESS)
{
if (rc == IDNA_MALLOC_ERROR)
return -EAI_MEMORY;
if (rc == IDNA_DLOPEN_ERROR)
return -EAI_SYSTEM;
return -EAI_IDN_ENCODE;
}
/* In case the output string is the same as the input string
no new string has been allocated. */
if (p != name)
{
name = strdupa (p);
free (p);
}
}
#endif
if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
{
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
at->family = AF_INET;
else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED))
{
at->addr[3] = at->addr[0];
at->addr[2] = htonl (0xffff);
at->addr[1] = 0;
at->addr[0] = 0;
at->family = AF_INET6;
}
else
return -EAI_ADDRFAMILY;
if (req->ai_flags & AI_CANONNAME)
canon = name;
}
else if (at->family == AF_UNSPEC)
{
char *namebuf = (char *) name;
char *scope_delim = strchr (name, SCOPE_DELIMITER);
if (__builtin_expect (scope_delim != NULL, 0))
{
namebuf = alloca (scope_delim - name + 1);
*((char *) __mempcpy (namebuf, name, scope_delim - name)) = '\0';
}
if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
{
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
at->family = AF_INET6;
else if (req->ai_family == AF_INET
&& IN6_IS_ADDR_V4MAPPED (at->addr))
{
at->addr[0] = at->addr[3];
at->family = AF_INET;
}
else
return -EAI_ADDRFAMILY;
if (scope_delim != NULL)
{
int try_numericscope = 0;
if (IN6_IS_ADDR_LINKLOCAL (at->addr)
|| IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
{
at->scopeid = if_nametoindex (scope_delim + 1);
if (at->scopeid == 0)
try_numericscope = 1;
}
else
try_numericscope = 1;
if (try_numericscope != 0)
{
char *end;
assert (sizeof (uint32_t) <= sizeof (unsigned long));
at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
10);
if (*end != '\0')
return GAIH_OKIFUNSPEC | -EAI_NONAME;
}
}
if (req->ai_flags & AI_CANONNAME)
canon = name;
}
}
if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
{
struct gaih_addrtuple **pat = &at;
int no_data = 0;
int no_inet6_data = 0;
service_user *nip = NULL;
enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
enum nss_status status = NSS_STATUS_UNAVAIL;
int no_more;
int old_res_options;
/* If we do not have to look for IPv4 and IPv6 together, use
the simple, old functions. */
if (req->ai_family == AF_INET
|| (req->ai_family == AF_INET6
&& ((req->ai_flags & AI_V4MAPPED) == 0
|| (req->ai_flags & AI_ALL) == 0)))
{
int family = req->ai_family;
size_t tmpbuflen = 512;
char *tmpbuf = alloca (tmpbuflen);
int rc;
struct hostent th;
struct hostent *h;
int herrno;
simple_again:
while (1)
{
rc = __gethostbyname2_r (name, family, &th, tmpbuf,
tmpbuflen, &h, &herrno);
if (rc != ERANGE || herrno != NETDB_INTERNAL)
break;
tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
}
if (rc == 0)
{
if (h == NULL)
{
if (req->ai_family == AF_INET6
&& (req->ai_flags & AI_V4MAPPED)
&& family == AF_INET6)
{
/* Try again, this time looking for IPv4
addresses. */
family = AF_INET;
goto simple_again;
}
}
else
{
/* We found data, now convert it into the list. */
for (int i = 0; h->h_addr_list[i]; ++i)
{
if (*pat == NULL)
{
*pat = __alloca (sizeof (struct gaih_addrtuple));
(*pat)->scopeid = 0;
}
(*pat)->next = NULL;
(*pat)->family = req->ai_family;
if (family == req->ai_family)
memcpy ((*pat)->addr, h->h_addr_list[i],
h->h_length);
else
{
uint32_t *addr = (uint32_t *) (*pat)->addr;
addr[3] = *(uint32_t *) h->h_addr_list[i];
addr[2] = htonl (0xffff);
addr[1] = 0;
addr[0] = 0;
}
pat = &((*pat)->next);
}
}
}
else
{
if (herrno == NETDB_INTERNAL)
{
__set_h_errno (herrno);
return -EAI_SYSTEM;
}
if (herrno == TRY_AGAIN)
{
return -EAI_AGAIN;
}
/* We made requests but they turned out no data.
The name is known, though. */
return GAIH_OKIFUNSPEC | -EAI_NODATA;
}
goto process_list;
}
#ifdef USE_NSCD
if (__nss_not_use_nscd_hosts > 0
&& ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
__nss_not_use_nscd_hosts = 0;
if (!__nss_not_use_nscd_hosts)
{
/* Try to use nscd. */
struct nscd_ai_result *air = NULL;
int herrno;
int err = __nscd_getai (name, &air, &herrno);
if (air != NULL)
{
/* Transform into gaih_addrtuple list. */
bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
char *addrs = air->addrs;
for (int i = 0; i < air->naddrs; ++i)
{
socklen_t size = (air->family[i] == AF_INET
? INADDRSZ : IN6ADDRSZ);
if (*pat == NULL)
{
*pat = __alloca (sizeof (struct gaih_addrtuple));
(*pat)->scopeid = 0;
}
uint32_t *pataddr = (*pat)->addr;
(*pat)->next = NULL;
if (added_canon || air->canon == NULL)
(*pat)->name = NULL;
else
canon = (*pat)->name = strdupa (air->canon);
if (air->family[i] == AF_INET
&& req->ai_family == AF_INET6
&& (req->ai_flags & AI_V4MAPPED))
{
(*pat)->family = AF_INET6;
pataddr[3] = *(uint32_t *) addrs;
pataddr[2] = htonl (0xffff);
pataddr[1] = 0;
pataddr[0] = 0;
pat = &((*pat)->next);
added_canon = true;
}
else if (req->ai_family == AF_UNSPEC
|| air->family[i] == req->ai_family)
{
(*pat)->family = air->family[i];
memcpy (pataddr, addrs, size);
pat = &((*pat)->next);
added_canon = true;
if (air->family[i] == AF_INET6)
got_ipv6 = true;
}
addrs += size;
}
free (air);
if (at->family == AF_UNSPEC)
return GAIH_OKIFUNSPEC | -EAI_NONAME;
goto process_list;
}
else if (err != 0 && __nss_not_use_nscd_hosts == 0)
{
if (herrno == NETDB_INTERNAL && errno == ENOMEM)
return -EAI_MEMORY;
if (herrno == TRY_AGAIN)
return -EAI_AGAIN;
return -EAI_SYSTEM;
}
}
#endif
if (__nss_hosts_database != NULL)
{
no_more = 0;
nip = __nss_hosts_database;
}
else
no_more = __nss_database_lookup ("hosts", NULL,
"dns [!UNAVAIL=return] files",
&nip);
if (__res_maybe_init (&_res, 0) == -1)
no_more = 1;
/* If we are looking for both IPv4 and IPv6 address we don't
want the lookup functions to automatically promote IPv4
addresses to IPv6 addresses. Currently this is decided
by setting the RES_USE_INET6 bit in _res.options. */
old_res_options = _res.options;
_res.options &= ~RES_USE_INET6;
size_t tmpbuflen = 512;
char *tmpbuf = alloca (tmpbuflen);
while (!no_more)
{
nss_gethostbyname3_r fct = NULL;
if (req->ai_flags & AI_CANONNAME)
/* No need to use this function if we do not look for
the canonical name. The function does not exist in
all NSS modules and therefore the lookup would
often fail. */
fct = __nss_lookup_function (nip, "gethostbyname3_r");
if (fct == NULL)
/* We are cheating here. The gethostbyname2_r function does
not have the same interface as gethostbyname3_r but the
extra arguments the latter takes are added at the end.
So the gethostbyname2_r code will just ignore them. */
fct = __nss_lookup_function (nip, "gethostbyname2_r");
if (fct != NULL)
{
if (req->ai_family == AF_INET6
|| req->ai_family == AF_UNSPEC)
{
gethosts (AF_INET6, struct in6_addr);
no_inet6_data = no_data;
inet6_status = status;
}
if (req->ai_family == AF_INET
|| req->ai_family == AF_UNSPEC
|| (req->ai_family == AF_INET6
&& (req->ai_flags & AI_V4MAPPED)
/* Avoid generating the mapped addresses if we
know we are not going to need them. */
&& ((req->ai_flags & AI_ALL) || !got_ipv6)))
{
gethosts (AF_INET, struct in_addr);
if (req->ai_family == AF_INET)
{
no_inet6_data = no_data;
inet6_status = status;
}
}
/* If we found one address for AF_INET or AF_INET6,
don't continue the search. */
if (inet6_status == NSS_STATUS_SUCCESS
|| status == NSS_STATUS_SUCCESS)
{
if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
{
/* If we need the canonical name, get it
from the same service as the result. */
nss_getcanonname_r cfct;
int herrno;
cfct = __nss_lookup_function (nip, "getcanonname_r");
if (cfct != NULL)
{
const size_t max_fqdn_len = 256;
char *buf = alloca (max_fqdn_len);
char *s;
if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
max_fqdn_len, &s, &rc,
&herrno))
== NSS_STATUS_SUCCESS)
canon = s;
else
/* Set to name now to avoid using
gethostbyaddr. */
canon = name;
}
}
break;
}
/* We can have different states for AF_INET and
AF_INET6. Try to find a useful one for both. */
if (inet6_status == NSS_STATUS_TRYAGAIN)
status = NSS_STATUS_TRYAGAIN;
else if (status == NSS_STATUS_UNAVAIL
&& inet6_status != NSS_STATUS_UNAVAIL)
status = inet6_status;
}
if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
break;
if (nip->next == NULL)
no_more = -1;
else
nip = nip->next;
}
_res.options = old_res_options;
if (no_data != 0 && no_inet6_data != 0)
{
/* If both requests timed out report this. */
if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
return -EAI_AGAIN;
/* We made requests but they turned out no data. The name
is known, though. */
return GAIH_OKIFUNSPEC | -EAI_NODATA;
}
}
process_list:
if (at->family == AF_UNSPEC)
return GAIH_OKIFUNSPEC | -EAI_NONAME;
}
else
{
struct gaih_addrtuple *atr;
atr = at = __alloca (sizeof (struct gaih_addrtuple));
memset (at, '\0', sizeof (struct gaih_addrtuple));
if (req->ai_family == AF_UNSPEC)
{
at->next = __alloca (sizeof (struct gaih_addrtuple));
memset (at->next, '\0', sizeof (struct gaih_addrtuple));
}
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
{
at->family = AF_INET6;
if ((req->ai_flags & AI_PASSIVE) == 0)
memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
atr = at->next;
}
if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
{
atr->family = AF_INET;
if ((req->ai_flags & AI_PASSIVE) == 0)
atr->addr[0] = htonl (INADDR_LOOPBACK);
}
}
if (pai == NULL)
return 0;
{
struct gaih_servtuple *st2;
struct gaih_addrtuple *at2 = at;
size_t socklen;
sa_family_t family;
/*
buffer is the size of an unformatted IPv6 address in printable format.
*/
while (at2 != NULL)
{
/* Only the first entry gets the canonical name. */
if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0)
{
if (canon == NULL)
{
struct hostent *h = NULL;
int herrno;
struct hostent th;
size_t tmpbuflen = 512;
char *tmpbuf = NULL;
do
{
tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
rc = __gethostbyaddr_r (at2->addr,
((at2->family == AF_INET6)
? sizeof (struct in6_addr)
: sizeof (struct in_addr)),
at2->family, &th, tmpbuf,
tmpbuflen, &h, &herrno);
}
while (rc == ERANGE && herrno == NETDB_INTERNAL);
if (rc != 0 && herrno == NETDB_INTERNAL)
{
__set_h_errno (herrno);
return -EAI_SYSTEM;
}
if (h != NULL)
canon = h->h_name;
else
{
assert (orig_name != NULL);
/* If the canonical name cannot be determined, use
the passed in string. */
canon = orig_name;
}
}
#ifdef HAVE_LIBIDN
if (req->ai_flags & AI_CANONIDN)
{
int idn_flags = 0;
if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
idn_flags |= IDNA_ALLOW_UNASSIGNED;
if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
idn_flags |= IDNA_USE_STD3_ASCII_RULES;
char *out;
int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
if (rc != IDNA_SUCCESS)
{
if (rc == IDNA_MALLOC_ERROR)
return -EAI_MEMORY;
if (rc == IDNA_DLOPEN_ERROR)
return -EAI_SYSTEM;
return -EAI_IDN_ENCODE;
}
/* In case the output string is the same as the input
string no new string has been allocated. Otherwise
make a copy. */
if (out == canon)
goto make_copy;
}
else
#endif
{
#ifdef HAVE_LIBIDN
make_copy:
#endif
canon = strdup (canon);
if (canon == NULL)
return -EAI_MEMORY;
}
}
family = at2->family;
if (family == AF_INET6)
{
socklen = sizeof (struct sockaddr_in6);
/* If we looked up IPv4 mapped address discard them here if
the caller isn't interested in all address and we have
found at least one IPv6 address. */
if (got_ipv6
&& (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
&& IN6_IS_ADDR_V4MAPPED (at2->addr))
goto ignore;
}
else
socklen = sizeof (struct sockaddr_in);
for (st2 = st; st2 != NULL; st2 = st2->next)
{
struct addrinfo *ai;
ai = *pai = malloc (sizeof (struct addrinfo) + socklen);
if (ai == NULL)
{
free ((char *) canon);
return -EAI_MEMORY;
}
ai->ai_flags = req->ai_flags;
ai->ai_family = family;
ai->ai_socktype = st2->socktype;
ai->ai_protocol = st2->protocol;
ai->ai_addrlen = socklen;
ai->ai_addr = (void *) (ai + 1);
/* We only add the canonical name once. */
ai->ai_canonname = (char *) canon;
canon = NULL;
#ifdef _HAVE_SA_LEN
ai->ai_addr->sa_len = socklen;
#endif /* _HAVE_SA_LEN */
ai->ai_addr->sa_family = family;
/* In case of an allocation error the list must be NULL
terminated. */
ai->ai_next = NULL;
if (family == AF_INET6)
{
struct sockaddr_in6 *sin6p =
(struct sockaddr_in6 *) ai->ai_addr;
sin6p->sin6_port = st2->port;
sin6p->sin6_flowinfo = 0;
memcpy (&sin6p->sin6_addr,
at2->addr, sizeof (struct in6_addr));
sin6p->sin6_scope_id = at2->scopeid;
}
else
{
struct sockaddr_in *sinp =
(struct sockaddr_in *) ai->ai_addr;
sinp->sin_port = st2->port;
memcpy (&sinp->sin_addr,
at2->addr, sizeof (struct in_addr));
memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
}
pai = &(ai->ai_next);
}
++*naddrs;
ignore:
at2 = at2->next;
}
}
return 0;
}
#if 0
static const struct gaih gaih[] =
{
{ PF_INET6, gaih_inet },
{ PF_INET, gaih_inet },
#if 0
{ PF_LOCAL, gaih_local },
#endif
{ PF_UNSPEC, NULL }
};
#endif
struct sort_result
{
struct addrinfo *dest_addr;
struct sockaddr_storage source_addr;
uint8_t source_addr_len;
bool got_source_addr;
uint8_t source_addr_flags;
};
static int
get_scope (const struct sockaddr_storage *ss)
{
int scope;
if (ss->ss_family == PF_INET6)
{
const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss;
if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
{
if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr))
scope = 2;
else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr))
scope = 5;
else
/* XXX Is this the correct default behavior? */
scope = 14;
}
else
scope = in6->sin6_addr.s6_addr[1] & 0xf;
}
else if (ss->ss_family == PF_INET)
{
const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
const uint8_t *addr = (const uint8_t *) &in->sin_addr;
/* RFC 3484 specifies how to map IPv6 addresses to scopes.
169.254/16 and 127/8 are link-local. */
if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127)
scope = 2;
else if (addr[0] == 10 || (addr[0] == 172 && addr[1] == 16)
|| (addr[0] == 192 && addr[1] == 168))
scope = 5;
else
scope = 14;
}
else
/* XXX What is a good default? */
scope = 15;
return scope;
}
struct prefixentry
{
struct in6_addr prefix;
unsigned int bits;
int val;
};
/* The label table. */
static const struct prefixentry *labels;
/* Default labels. */
static const struct prefixentry default_labels[] =
{
/* See RFC 3484 for the details. */
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
128, 0 },
{ { .in6_u
= { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
16, 2 },
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
96, 3 },
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
96, 4 },
/* The next two entries differ from RFC 3484. We need to treat
IPv6 site-local addresses special because they are never NATed,
unlike site-locale IPv4 addresses. If this would not happen, on
machines which have only IPv4 and IPv6 site-local addresses, the
sorting would prefer the IPv6 site-local addresses, causing
unnecessary delays when trying to connect to a global IPv6 address
through a site-local IPv6 address. */
{ { .in6_u
= { .u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
10, 5 },
{ { .in6_u
= { .u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
7, 6 },
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
0, 1 }
};
/* The precedence table. */
static const struct prefixentry *precedence;
/* The default precedences. */
static const struct prefixentry default_precedence[] =
{
/* See RFC 3484 for the details. */
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
128, 50 },
{ { .in6_u
= { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
16, 30 },
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
96, 20 },
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
96, 10 },
{ { .in6_u
= { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
0, 40 }
};
static int
match_prefix (const struct sockaddr_storage *ss,
const struct prefixentry *list, int default_val)
{
int idx;
struct sockaddr_in6 in6_mem;
const struct sockaddr_in6 *in6;
if (ss->ss_family == PF_INET6)
in6 = (const struct sockaddr_in6 *) ss;
else if (ss->ss_family == PF_INET)
{
const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
/* Convert to IPv6 address. */
in6_mem.sin6_family = PF_INET6;
in6_mem.sin6_port = in->sin_port;
in6_mem.sin6_flowinfo = 0;
if (in->sin_addr.s_addr == htonl (0x7f000001))
in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
else
{
/* Construct a V4-to-6 mapped address. */
memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
in6_mem.sin6_addr.s6_addr16[5] = 0xffff;
in6_mem.sin6_addr.s6_addr32[3] = in->sin_addr.s_addr;
in6_mem.sin6_scope_id = 0;
}
in6 = &in6_mem;
}
else
return default_val;
for (idx = 0; ; ++idx)
{
unsigned int bits = list[idx].bits;
const uint8_t *mask = list[idx].prefix.s6_addr;
const uint8_t *val = in6->sin6_addr.s6_addr;
while (bits >= 8)
{
if (*mask != *val)
break;
++mask;
++val;
bits -= 8;
}
if (bits < 8)
{
if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits)))
/* Match! */
break;
}
}
return list[idx].val;
}
static int
get_label (const struct sockaddr_storage *ss)
{
/* XXX What is a good default value? */
return match_prefix (ss, labels, INT_MAX);
}
static int
get_precedence (const struct sockaddr_storage *ss)
{
/* XXX What is a good default value? */
return match_prefix (ss, precedence, 0);
}
/* Find last bit set in a word. */
static int
fls (uint32_t a)
{
uint32_t mask;
int n = 0;
for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n)
if ((a & mask) != 0)
break;
return n;
}
static int
rfc3484_sort (const void *p1, const void *p2)
{
const struct sort_result *a1 = (const struct sort_result *) p1;
const struct sort_result *a2 = (const struct sort_result *) p2;
/* Rule 1: Avoid unusable destinations.
We have the got_source_addr flag set if the destination is reachable. */
if (a1->got_source_addr && ! a2->got_source_addr)
return -1;
if (! a1->got_source_addr && a2->got_source_addr)
return 1;
/* Rule 2: Prefer matching scope. Only interesting if both
destination addresses are IPv6. */
int a1_dst_scope
= get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
int a2_dst_scope
= get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
if (a1->got_source_addr)
{
int a1_src_scope = get_scope (&a1->source_addr);
int a2_src_scope = get_scope (&a2->source_addr);
if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope)
return -1;
if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope)
return 1;
}
/* Rule 3: Avoid deprecated addresses. */
if (a1->got_source_addr)
{
if (!(a1->source_addr_flags & in6ai_deprecated)
&& (a2->source_addr_flags & in6ai_deprecated))
return -1;
if ((a1->source_addr_flags & in6ai_deprecated)
&& !(a2->source_addr_flags & in6ai_deprecated))
return 1;
}
/* Rule 4: Prefer home addresses. */
if (a1->got_source_addr)
{
if (!(a1->source_addr_flags & in6ai_homeaddress)
&& (a2->source_addr_flags & in6ai_homeaddress))
return 1;
if ((a1->source_addr_flags & in6ai_homeaddress)
&& !(a2->source_addr_flags & in6ai_homeaddress))
return -1;
}
/* Rule 5: Prefer matching label. */
if (a1->got_source_addr)
{
int a1_dst_label
= get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
int a1_src_label = get_label (&a1->source_addr);
int a2_dst_label
= get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
int a2_src_label = get_label (&a2->source_addr);
if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label)
return -1;
if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label)
return 1;
}
/* Rule 6: Prefer higher precedence. */
int a1_prec
= get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
int a2_prec
= get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
if (a1_prec > a2_prec)
return -1;
if (a1_prec < a2_prec)
return 1;
/* Rule 7: Prefer native transport. */
if (a1->got_source_addr)
{
if (!(a1->source_addr_flags & in6ai_temporary)
&& (a2->source_addr_flags & in6ai_temporary))
return -1;
if ((a1->source_addr_flags & in6ai_temporary)
&& !(a2->source_addr_flags & in6ai_temporary))
return 1;
/* XXX Do we need to check anything beside temporary addresses? */
}
/* Rule 8: Prefer smaller scope. */
if (a1_dst_scope < a2_dst_scope)
return -1;
if (a1_dst_scope > a2_dst_scope)
return 1;
/* Rule 9: Use longest matching prefix. */
if (a1->got_source_addr
&& a1->dest_addr->ai_family == a2->dest_addr->ai_family)
{
int bit1 = 0;
int bit2 = 0;
if (a1->dest_addr->ai_family == PF_INET)
{
assert (a1->source_addr.ss_family == PF_INET);
assert (a2->source_addr.ss_family == PF_INET);
struct sockaddr_in *in1_dst;
struct sockaddr_in *in1_src;
struct sockaddr_in *in2_dst;
struct sockaddr_in *in2_src;
in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
in1_src = (struct sockaddr_in *) &a1->source_addr;
in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
in2_src = (struct sockaddr_in *) &a2->source_addr;
bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
^ in1_src->sin_addr.s_addr));
bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
^ in2_src->sin_addr.s_addr));
}
else if (a1->dest_addr->ai_family == PF_INET6)
{
assert (a1->source_addr.ss_family == PF_INET6);
assert (a2->source_addr.ss_family == PF_INET6);
struct sockaddr_in6 *in1_dst;
struct sockaddr_in6 *in1_src;
struct sockaddr_in6 *in2_dst;
struct sockaddr_in6 *in2_src;
in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr;
in1_src = (struct sockaddr_in6 *) &a1->source_addr;
in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr;
in2_src = (struct sockaddr_in6 *) &a2->source_addr;
int i;
for (i = 0; i < 4; ++i)
if (in1_dst->sin6_addr.s6_addr32[i]
!= in1_src->sin6_addr.s6_addr32[i]
|| (in2_dst->sin6_addr.s6_addr32[i]
!= in2_src->sin6_addr.s6_addr32[i]))
break;
if (i < 4)
{
bit1 = fls (ntohl (in1_dst->sin6_addr.s6_addr32[i]
^ in1_src->sin6_addr.s6_addr32[i]));
bit2 = fls (ntohl (in2_dst->sin6_addr.s6_addr32[i]
^ in2_src->sin6_addr.s6_addr32[i]));
}
}
if (bit1 > bit2)
return -1;
if (bit1 < bit2)
return 1;
}
/* Rule 10: Otherwise, leave the order unchanged. */
return 0;
}
static int
in6aicmp (const void *p1, const void *p2)
{
struct in6addrinfo *a1 = (struct in6addrinfo *) p1;
struct in6addrinfo *a2 = (struct in6addrinfo *) p2;
return memcmp (a1->addr, a2->addr, sizeof (a1->addr));
}
/* Name of the config file for RFC 3484 sorting (for now). */
#define GAICONF_FNAME "/etc/gai.conf"
/* Nozero if we are supposed to reload the config file automatically
whenever it changed. */
static int gaiconf_reload_flag;
/* Last modification time. */
static struct timespec gaiconf_mtime;
libc_freeres_fn(fini)
{
if (labels != default_labels)
{
const struct prefixentry *old = labels;
labels = default_labels;
free ((void *) old);
}
if (precedence != default_precedence)
{
const struct prefixentry *old = precedence;
precedence = default_precedence;
free ((void *) old);
}
}
struct prefixlist
{
struct prefixentry entry;
struct prefixlist *next;
};
static void
free_prefixlist (struct prefixlist *list)
{
while (list != NULL)
{
struct prefixlist *oldp = list;
list = list->next;
free (oldp);
}
}
static int
prefixcmp (const void *p1, const void *p2)
{
const struct prefixentry *e1 = (const struct prefixentry *) p1;
const struct prefixentry *e2 = (const struct prefixentry *) p2;
if (e1->bits < e2->bits)
return 1;
if (e1->bits == e2->bits)
return 0;
return -1;
}
static void
gaiconf_init (void)
{
struct prefixlist *labellist = NULL;
size_t nlabellist = 0;
bool labellist_nullbits = false;
struct prefixlist *precedencelist = NULL;
size_t nprecedencelist = 0;
bool precedencelist_nullbits = false;
FILE *fp = fopen (GAICONF_FNAME, "rc");
if (fp != NULL)
{
struct stat64 st;
if (__fxstat64 (_STAT_VER, fileno (fp), &st) != 0)
{
fclose (fp);
goto no_file;
}
char *line = NULL;
size_t linelen = 0;
__fsetlocking (fp, FSETLOCKING_BYCALLER);
while (!feof_unlocked (fp))
{
ssize_t n = __getline (&line, &linelen, fp);
if (n <= 0)
break;
/* Handle comments. No escaping possible so this is easy. */
char *cp = strchr (line, '#');
if (cp != NULL)
*cp = '\0';
cp = line;
while (isspace (*cp))
++cp;
char *cmd = cp;
while (*cp != '\0' && !isspace (*cp))
++cp;
size_t cmdlen = cp - cmd;
if (*cp != '\0')
*cp++ = '\0';
while (isspace (*cp))
++cp;
char *val1 = cp;
while (*cp != '\0' && !isspace (*cp))
++cp;
size_t val1len = cp - cmd;
/* We always need at least two values. */
if (val1len == 0)
continue;
if (*cp != '\0')
*cp++ = '\0';
while (isspace (*cp))
++cp;
char *val2 = cp;
while (*cp != '\0' && !isspace (*cp))
++cp;
/* Ignore the rest of the line. */
*cp = '\0';
struct prefixlist **listp;
size_t *lenp;
bool *nullbitsp;
switch (cmdlen)
{
case 5:
if (strcmp (cmd, "label") == 0)
{
struct in6_addr prefix;
unsigned long int bits;
unsigned long int val;
char *endp;
listp = &labellist;
lenp = &nlabellist;
nullbitsp = &labellist_nullbits;
new_elem:
bits = 128;
__set_errno (0);
cp = strchr (val1, '/');
if (cp != NULL)
*cp++ = '\0';
if (inet_pton (AF_INET6, val1, &prefix)
&& (cp == NULL
|| (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
|| errno != ERANGE)
&& *endp == '\0'
&& bits <= INT_MAX
&& ((val = strtoul (val2, &endp, 10)) != ULONG_MAX
|| errno != ERANGE)
&& *endp == '\0'
&& val <= INT_MAX)
{
struct prefixlist *newp = malloc (sizeof (*newp));
if (newp == NULL)
{
free (line);
fclose (fp);
goto no_file;
}
memcpy (&newp->entry.prefix, &prefix, sizeof (prefix));
newp->entry.bits = bits;
newp->entry.val = val;
newp->next = *listp;
*listp = newp;
++*lenp;
*nullbitsp |= bits == 0;
}
}
break;
case 6:
if (strcmp (cmd, "reload") == 0)
gaiconf_reload_flag = strcmp (val1, "yes") == 0;
break;
case 10:
if (strcmp (cmd, "precedence") == 0)
{
listp = &precedencelist;
lenp = &nprecedencelist;
nullbitsp = &precedencelist_nullbits;
goto new_elem;
}
break;
}
}
free (line);
fclose (fp);
/* Create the array for the labels. */
struct prefixentry *new_labels;
if (nlabellist > 0)
{
if (!labellist_nullbits)
++nlabellist;
new_labels = malloc (nlabellist * sizeof (*new_labels));
if (new_labels == NULL)
goto no_file;
int i = nlabellist;
if (!labellist_nullbits)
{
--i;
memset (&new_labels[i].prefix, '\0', sizeof (struct in6_addr));
new_labels[i].bits = 0;
new_labels[i].val = 1;
}
struct prefixlist *l = labellist;
while (i-- > 0)
{
new_labels[i] = l->entry;
l = l->next;
}
free_prefixlist (labellist);
/* Sort the entries so that the most specific ones are at
the beginning. */
qsort (new_labels, nlabellist, sizeof (*new_labels), prefixcmp);
}
else
new_labels = (struct prefixentry *) default_labels;
struct prefixentry *new_precedence;
if (nprecedencelist > 0)
{
if (!precedencelist_nullbits)
++nprecedencelist;
new_precedence = malloc (nprecedencelist * sizeof (*new_precedence));
if (new_precedence == NULL)
{
if (new_labels != default_labels)
free (new_labels);
goto no_file;
}
int i = nprecedencelist;
if (!precedencelist_nullbits)
{
--i;
memset (&new_precedence[i].prefix, '\0',
sizeof (struct in6_addr));
new_precedence[i].bits = 0;
new_precedence[i].val = 40;
}
struct prefixlist *l = precedencelist;
while (i-- > 0)
{
new_precedence[i] = l->entry;
l = l->next;
}
free_prefixlist (precedencelist);
/* Sort the entries so that the most specific ones are at
the beginning. */
qsort (new_precedence, nprecedencelist, sizeof (*new_labels),
prefixcmp);
}
else
new_precedence = (struct prefixentry *) default_precedence;
/* Now we are ready to replace the values. */
const struct prefixentry *old = labels;
labels = new_labels;
if (old != default_labels)
free ((void *) old);
old = precedence;
precedence = new_precedence;
if (old != default_precedence)
free ((void *) old);
gaiconf_mtime = st.st_mtim;
}
else
{
no_file:
free_prefixlist (labellist);
free_prefixlist (precedencelist);
/* If we previously read the file but it is gone now, free the
old data and use the builtin one. Leave the reload flag
alone. */
fini ();
}
}
static void
gaiconf_reload (void)
{
struct stat64 st;
if (__xstat64 (_STAT_VER, GAICONF_FNAME, &st) != 0
|| memcmp (&st.st_mtim, &gaiconf_mtime, sizeof (gaiconf_mtime)) != 0)
gaiconf_init ();
}
int
getaddrinfo (const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
{
int i = 0, last_i = 0;
int nresults = 0;
struct addrinfo *p = NULL;
struct gaih_service gaih_service, *pservice;
struct addrinfo local_hints;
if (name != NULL && name[0] == '*' && name[1] == 0)
name = NULL;
if (service != NULL && service[0] == '*' && service[1] == 0)
service = NULL;
if (name == NULL && service == NULL)
return EAI_NONAME;
if (hints == NULL)
hints = &default_hints;
if (hints->ai_flags
& ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
#ifdef HAVE_LIBIDN
|AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED
|AI_IDN_USE_STD3_ASCII_RULES
#endif
|AI_NUMERICSERV|AI_ALL))
return EAI_BADFLAGS;
if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
return EAI_BADFLAGS;
struct in6addrinfo *in6ai = NULL;
size_t in6ailen;
bool seen_ipv4 = false;
bool seen_ipv6 = false;
/* We might need information about what kind of interfaces are available.
But even if AI_ADDRCONFIG is not used, if the user requested IPv6
addresses we have to know whether an address is deprecated or
temporary. */
if ((hints->ai_flags & AI_ADDRCONFIG) || hints->ai_family == PF_UNSPEC
|| hints->ai_family == PF_INET6)
/* Determine whether we have IPv4 or IPv6 interfaces or both. We
cannot cache the results since new interfaces could be added at
any time. */
__check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
if (hints->ai_flags & AI_ADDRCONFIG)
{
/* Now make a decision on what we return, if anything. */
if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6))
{
/* If we haven't seen both IPv4 and IPv6 interfaces we can
narrow down the search. */
if (! seen_ipv4 || ! seen_ipv6)
{
local_hints = *hints;
local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6;
hints = &local_hints;
}
}
else if ((hints->ai_family == PF_INET && ! seen_ipv4)
|| (hints->ai_family == PF_INET6 && ! seen_ipv6))
{
/* We cannot possibly return a valid answer. */
free (in6ai);
return EAI_NONAME;
}
}
if (service && service[0])
{
char *c;
gaih_service.name = service;
gaih_service.num = strtoul (gaih_service.name, &c, 10);
if (*c != '\0')
{
if (hints->ai_flags & AI_NUMERICSERV)
{
free (in6ai);
return EAI_NONAME;
}
gaih_service.num = -1;
}
pservice = &gaih_service;
}
else
pservice = NULL;
struct addrinfo **end;
if (pai)
end = &p;
else
end = NULL;
unsigned int naddrs = 0;
#if 0
/* If we would support more protocols than just IPv4 and IPv6 we
would iterate over a table with appropriate callback functions.
Since we currently only handle IPv4 and IPv6 this is not
necessary. */
const struct gaih *g = gaih;
const struct gaih *pg = NULL;
int j = 0;
while (g->gaih)
{
if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
{
j++;
if (pg == NULL || pg->gaih != g->gaih)
{
pg = g;
i = g->gaih (name, pservice, hints, end, &naddrs);
if (i != 0)
{
/* EAI_NODATA is a more specific result as it says that
we found a result but it is not usable. */
if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
last_i = i;
if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
{
++g;
continue;
}
freeaddrinfo (p);
free (in6ai);
return -(i & GAIH_EAI);
}
if (end)
while (*end)
{
end = &((*end)->ai_next);
++nresults;
}
}
}
++g;
}
if (j == 0)
{
free (in6ai);
return EAI_FAMILY;
}
#else
if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET
|| hints->ai_family == AF_INET6)
{
last_i = gaih_inet (name, pservice, hints, end, &naddrs);
if (last_i != 0)
{
freeaddrinfo (p);
free (in6ai);
return -(last_i & GAIH_EAI);
}
if (end)
while (*end)
{
end = &((*end)->ai_next);
++nresults;
}
}
else
{
free (in6ai);
return EAI_FAMILY;
}
#endif
if (naddrs > 1)
{
/* Read the config file. */
__libc_once_define (static, once);
__typeof (once) old_once = once;
__libc_once (once, gaiconf_init);
if (old_once && gaiconf_reload_flag)
gaiconf_reload ();
/* Sort results according to RFC 3484. */
struct sort_result results[nresults];
struct addrinfo *q;
struct addrinfo *last = NULL;
char *canonname = NULL;
/* If we have information about deprecated and temporary address
sort the array now. */
if (in6ai != NULL)
qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp);
for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
{
results[i].dest_addr = q;
results[i].got_source_addr = false;
/* If we just looked up the address for a different
protocol, reuse the result. */
if (last != NULL && last->ai_addrlen == q->ai_addrlen
&& memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0)
{
memcpy (&results[i].source_addr, &results[i - 1].source_addr,
results[i - 1].source_addr_len);
results[i].source_addr_len = results[i - 1].source_addr_len;
results[i].got_source_addr = results[i - 1].got_source_addr;
results[i].source_addr_flags = results[i - 1].source_addr_flags;
}
else
{
results[i].source_addr_flags = 0;
/* We overwrite the type with SOCK_DGRAM since we do not
want connect() to connect to the other side. If we
cannot determine the source address remember this
fact. */
int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
socklen_t sl = sizeof (results[i].source_addr);
if (fd != -1
&& __connect (fd, q->ai_addr, q->ai_addrlen) == 0
&& __getsockname (fd,
(struct sockaddr *) &results[i].source_addr,
&sl) == 0)
{
results[i].source_addr_len = sl;
results[i].got_source_addr = true;
if (q->ai_family == PF_INET6 && in6ai != NULL)
{
/* See whether the source address is the list of
deprecated or temporary addresses. */
struct in6addrinfo tmp;
struct sockaddr_in6 *sin6p
= (struct sockaddr_in6 *) &results[i].source_addr;
memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ);
struct in6addrinfo *found
= bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
in6aicmp);
if (found != NULL)
results[i].source_addr_flags = found->flags;
}
}
else
/* Just make sure that if we have to process the same
address again we do not copy any memory. */
results[i].source_addr_len = 0;
if (fd != -1)
close_not_cancel_no_status (fd);
}
/* Remember the canonical name. */
if (q->ai_canonname != NULL)
{
assert (canonname == NULL);
canonname = q->ai_canonname;
q->ai_canonname = NULL;
}
}
/* We got all the source addresses we can get, now sort using
the information. */
qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
/* Queue the results up as they come out of sorting. */
q = p = results[0].dest_addr;
for (i = 1; i < nresults; ++i)
q = q->ai_next = results[i].dest_addr;
q->ai_next = NULL;
/* Fill in the canonical name into the new first entry. */
p->ai_canonname = canonname;
}
free (in6ai);
if (p)
{
*pai = p;
return 0;
}
if (pai == NULL && last_i == 0)
return 0;
return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
}
libc_hidden_def (getaddrinfo)
static_link_warning (getaddrinfo)
void
freeaddrinfo (struct addrinfo *ai)
{
struct addrinfo *p;
while (ai != NULL)
{
p = ai;
ai = ai->ai_next;
free (p->ai_canonname);
free (p);
}
}
libc_hidden_def (freeaddrinfo)