Index: head/contrib/bind/CHANGES =================================================================== --- head/contrib/bind/CHANGES (revision 60940) +++ head/contrib/bind/CHANGES (revision 60941) @@ -1,2591 +1,2871 @@ + --- 8.2.3-T5B (RC3) released --- + +1006. [port] Windows/NT does not have fchown(). + +1005. [bug] RD was sometimes left set, inappropriately. + +1004. [bug] cached NXT's were corrupted. + +1003. [bug] correction to #997. + +1002. [bug] file descriptor leak in res_nclose(). + +1001. [port] some builds were too fast. + + --- 8.2.3-T4B (RC2) released --- + +1000. [bug] #996 was wrongly implemented; replacement fix. + + --- 8.2.3-T3B released --- + + 999. [support] named now makes an effort to create its files with + ownership as specified by -u and -g command options. + + 998. [support] show version number in NOTIFY log messages. + + 997. [support] forwarders are now used in order by measured RTT. + + 996. [protocol] if answering ixfr with full zone, used qtype axfr. + + 995. [bug] "dig -b" was broken due to missing switch "break;" + + 994. [bug] named-xfer did not handle empty question sections. + + 993. [bug] TSIG AXFR was completely broken in DiG. + + 992. [bug] OPTION_USE_IXFR and OPTION_MAINTAIN_IXFR_BASE had + non-single-bit flag values in src/bin/named/ns_defs.h. + + 991. [protocol] send A6 glue records in xfr. + + 990. [bug] we could loose track of a bottom of zone cut is the + write buffer filled up at just the correct moment. + + 989. [bug] apply to "fetch-glue no;" to notify processing. need + to add A records that would be found this way w/ + also-notify. + + 988. [support] report expired zones when detected in maintainence + pass. + + 987. [feature] "ndc reconfig -noexpired" skip attempts to load + expired zoned when reconfiguring. + + 986. [bug] pushlev only needs to be called for axfr/zxfr not ixfr. + + --- 8.2.3-T2B released --- + + 985. [support] remove "view" command from nslookup (it used mktemp()). + + 984. [bug] always restart processing query from scratch if we + have chased a CNAME as we might still have the answer + in the cache once the CNAME has been resolved. + + 983. [support] "notify from non-master server" is now debug, not info. + + 982. [bug] rollback the compression pointers array when a + RRset/RR does not fit. + + 981. [port] decunix: typedef (u_)int#m_t + + 980. [bug] mishandled memget failure w/ TCP connections. + + 979. [bug] we were failing to call ns_stopxfrs() before calling + purge_zone() in some cases. + + 978. [port] sco50: setsockopt(SO_REUSEADDR) fails on unix domain + sockets + + 977. [bug] we should be returning notimpl for update forwarding + rather than refused. a client receiving refused + should terminate the update attempt. notimpl should + just cause the client to skip to the next server. + + 976. [bug] some stats weren't getting incremented, & added a few. + + 975. [support] SLAVE_FORWARD is now redundant and has been removed. + + 974. [port] ultrix with vendor's y2k patch explicitly desupported. + + 973. [bug] some field names added in #935 conflicted with macros. + + 972. [support] restore heartbeat notifies. + + 971. [bug] out of order updates in log. + + 970. [port] solaris: add ipv6 interface scanning support. + + 969. [bug] post process a zone load to remove any non-glue at + or below bottom of zone. + + 968. [bug] TSIGs failed to verify if the key name was compressed. + + 967. [bug] zones signed by the BIND 9 signer failed to load. + + --- 8.2.3-T1A released --- + + 966. [bug] nslookup and dig misprinted root zone in $ORIGIN. + + 965. [feature] dig's command line input buffer was rather small. + + 964. [bug] make res_nsearch() behave like res_search() of olde. + + 963. [bug] res_debug::do_section() can no longer spin all VM. + + 962. [bug] another almost-complete rewrite of IXFR from kjd (462) + + 961. [bug] acl "none" now fails to match but doesn't end search. + + 960. [bug] more hesiod library fixes from danny. + + 959. [doc] christos fixed several man page typos and brainos. + + 958. [bug] getnameinfo() should accept experimental/multicast. + + 957. [port] ultrix again. "cd" now presumed to be silent again. + + 956. [bug] multiline was not being cleared correctly. + + 955. [bug] explicit TTL on SOA records were being replaced with + soa minimum. + + 954. [bug] cannot load a signed root zone. + + 953. [bug] memory overrun in set_zone_ixfr_file(). + + 952. [bug] errs was not being correctly adjusted if the included + master file did not exist in db_load(). + + 951. [bug] contrib/dns_signer/signer: write_trim_name + array bounds write error. + + 950. [bug] hesiod: ctx->res was not being initalised. + + 949. [port] aix32: add prand_conf.h and define WCOREDUMP + + 948. [bug] fixed logic error in a number of expressions causing + res_ninit() not to be called when it should be. + + 947. [bug] sanity check in dst_read_key() wasn't. + + 946. [port] freebsd: threaded library support. + + 945. [bug] wrong file name logged in ixfr_have_log(). + + 944. [doc] add forwarders to zone types master/slave/stub in + named.conf man page. + + 943. [bug] raise CNAME and OTHER / multiple CNAME logging to + warning. + + 942. [bug] bad referrals logged for forwarders. + + 941. [bug] lame server detection wasn't checking for SOA record. + + 940. [clarity] unapproved -> denied in log messages. + + 939. [bug] reload_master and purgeandload should write the zone + if it has been updated. + + 938. [bug] update and ixfr logs could get corrupted. fseek() + before ftell() on fopen(, "a+") file. + + 937. [support] allow parallel makes to work. + + 936. [protocol] add preliminary A6 glue recognition in ns_req. + + 935. [cleanup] res_nsend() segmented into multiple functions for + readability. also fixed two file descriptor leaks. + CAN_RECONNECT is gone, keep one socket per nameserver. + + 934. [bug] Perror and Aerror where incorrect if DEBUG is not + defined. + + 933. [port] cygwin port added + + 932. [port] sco42 does not have unix domain sockets or gethostid. + + 931. [bug] eventlib was not handling unix domain sockets + correctly. + + 930. [bug] we wern't using all the potential compression pointers + in the question section. + + 929. [bug] we were accepting updates (adds) with illegal ttls. + + 928. [bug] if we manage to get a illegal ttl stored, print it + unsigened. + + 927. [port] hpux: (11.* 10.30) Makefile.set.gcc + + 926. [port] hpux10: gcc needs -D_HPUX_SOURCE and -fPIC + + 925. [protocol] when a slave loads it should notify others (RFC 1996). + + 924. [port] sunos solaris: #define NEED_SECURE_DIRECTORY to + secure the directory containing unix domain socket + rather than the socket itself. + + 923. [support] shutup "make clean" about missing threaded directories. + + 922. [bug] removing an cached zone file then performing a + "ndc reload zone" should force a zone transfer. + + 921. [bug] nsupdate: listuprec was not being initalised. + + 920. [port] aix4: Makefile.set.gcc + aix4: __P was being defined by + + 919 [port] linux: remove one level of symbolic linkage when + performing make links on port/linux/include + + 918. [bug] update prerequisite could match w/ wildcard. + + 917. [port] irix: make the current IRIX release (6.5) work by + not patching res_debug.c. see INSTALL if you have + problems with 6.3. + + 916. [bug] removing / changing a zone type could result in + Z_NOTIFY being cleared / tested against the wrong zone. + + 915. [bug] evNewWaitList() was not maintaining the prev chain. + + 914. [bug] signal EWOULDBLOCK if EV_POLL'ing with no timers. + + 913. [bug] input could get lost on the server side of a ctl sock. + + 912. [bug] nsupdate now allows explicit 0 TTL's on added RR's. + + 911. [bug] gethostbyname() should not return duplicate addresses. + + 910. [bug] address-sorting logic was exiting early. + + 909. [bug] dig wasn't respecting the +ti and +ret arguments. + + 908. [contrib] Tony Stoneley sent us an updated misc/makezones. + + 907. [port] winnt fixes from Larry at Nortel. + + 906. [bug] res_findzonecut() failed if the NS referred to a CNAME. + + 905. [doc] Minor fix to doc/man/Makefile for getnameinfo + + 904. [bug] bin/host wasn't looking up MX records if no + -t flags were passed to it. + + --- 8.2.2-P6 released --- + + 903. [bug] divide by zero bug when querying for SIG records from + a secure zone. + + 902. [support] don't attempt to set q_fzone if we won't be using it. + + 901. [support] delay notify timer setting until all zones have been + loaded. + + 900. [port] hpux10 fix call to bison; sco call bison consistenly. + + 899. [bug] dynamically allocate buffer used to display RR rather + than uses a fixed sized one. grow as needed. + + 898. [bug] if truncation caused no RR's to appear in the answer we + mis-classified the answer on a NODATA. + + 897. [support] descriptors used by named should not be inherited by + named-xfer. + + 896. [contrib] add contrib/adm/adm-nxt, an exploit for the NXT bug + in 8.2 and 8.2.1. as before, we do not recommend its + use, and we do recommend that you run the latest BIND. + --- 8.2.2-P5 released --- 895. [port] minor NT build and documentation improvements. 894. [bug] incorrect "key" statements in named.conf weren't handled properly. --- 8.2.2-P4 released --- 893. [bug] DNSSEC logic in bin/host broke -t any 892. [bug] multiple SOA on AXFR bug --- 8.2.2-P3 released --- 891. [bug] options { also-notify { ... }; }; resulted in wrong pointer being memput with the wrong size on reload. 890. [port] A/UX portability improved. 889. [port] added IPv6 portability for OpenBSD, NetBSD, FreeBSD. --- 8.2.2-P2 released (internal release) --- 888. [support] add default: all tag to top src/Makefile so that "make" will work properly in some OS'. 887. [bug] "dig ... axfr" was printing spurious "TSIG ok" msgs. 886. [support] top-level Makefile now included in all tarballs. 885. [support] IXFR improvements. 884. [bug] some deprecated NXT RR forms weren't ignored properly. 883. [support] "host" command can now try to verify dnssec signatures. 882. [contrib] dns_signer/ had some last minute problems (by author). 881. [bug] possible sprintf() overflow prevented. 880. [support] minor tweak to bin/dig/dig.c TSIG code to clarify whether res_nsend or res_nsendsigned is being used. 879. [support] add "noesw" target to top-level Makefile (for PL1). 878. [port] aix4 HAS_INET6_STRUCTS was not being set based on the existance of _IN6_ADDR_STRUCT. 877. [port] freebsd + KAME need a different Makefile.set see INSTALL notes. 876. [port] IPv6 probe for MPE/IX, NetBSD. 875. [bug] bad NAPTR RRs could be loaded from zone files. 874. [port] update irix_patch in irix port. 873. [port] add SRC/tools to sco's make [std]links. --- 8.2.2-REL released --- 872. [bug] named-xfer could free() a string twice. 871. [port] linux support for broken IPv6. 870. [port] more NT fixes and improvements from larry at bay. 869. [bug] disable client side IXFR (in named-xfer) for now. 868. [bug] updated named-bootconf to handle case insensitive parts of named.boot. added stubs support. class was not being reset. 867. [support] updated INSTALL notes. 866. [port] More NT fixes from larry at bay. 865. [port] add #include to next's port_before.h 864. [port] change solaris' Makefile.set files to use yacc and lex. also clean up install and binary paths. 863. [bug] lib/isc/ctl_srvr.c needed fcntl.h #included --- 8.2.2-T8B (RC2) released --- 862. [port] another NT infusion from larry over at bay. 861. [support] improve support for tsig'd updates. 860. [port] add IPv6 probing to: decunix hpux irix lynxos mpe netbsd qnx rhapsody sco50 859. [bug] set control sockets to close-on-exec; potential file descriptor leaks in ctl_srvr. 858. [bug] make ns_samename() and use it instead of strcasecmp(). 857. [bug] unset update-log can lead to debugging msg mishaps. --- 8.2.2-T7B (RC1) released --- 856. [bug] IXFR finally works and is reenabled. 855. [port] more win/nt changes from bay. 854. [bug] /etc/hosts lines longer than 8K can crash gethostent(). 853. [bug] another linked list bug shaken out of ns_update. 852. [bug] compiled in pathname for nslookup help file was wrong. 851. [bug] ns_update had an off by 2 bug when checking names in SRV records causing unexpected failures. 850. [bug] empty updates triggered an overambitious INSIST(). --- 8.2.2-T6B released --- 849. [support] print rcode on failed UPDATE messages. 848. [port] paths.h and port_before.h tweaks from SCO for unixware7. 847. [port] add SRC/irix_patch to make links in IRIX 846. [support] restore some diagnotics lost when #634 was done. 845. [support] WATSQ patch from Ted Rule of Flextech Television. 844. [support] added src/DNSSEC with a note about BIND-8.1.2 interop. 843. [bug] IXFR fixes. 842. [bug] pointer arithmetic on (void *) not ANSI C. 841. [port] sco50: make install: libport.a not longer exists. 840. [bug] turning on touch_timer() in ctl_clnt.c found a bug. 839. [contrib] new version of contrib/host (from author). 838. [support] improve error reporting; remove lint. 837. [bug] bin/host/host.c was not RFC2317 compliant. 836. [port] hpux portability and speed improvements. 835. [port] some shell's "cd" produce output - fix in port/systype. --- 8.2.2-T5A released --- 834. [support] massive changes to dynupd API. 833. [port] more Win/NT. 832. [feature] boolean: treat-cr-as-space. If yes, BIND will treat '\r' the same as it treats ' ' and '\t' in zone files. 831. [bugs] DNSSEC/CAIRN workshop results (in addition to #826): - invalid size passed into b64_ntop in SIG parser - Invalid TSIG keys are now logged and ignored instead of panicing. - trusted-keys didn't work if a trailing dot was present - a DST problem that occurs when one of the multiprecision integers begins with a 0 byte. - TSIG signed truncated responses were mishandled. - minor RFC2535 changes. 830. [doc] Minor updates to INSTALL 829. [support] we need to cache SOA NXDOMAIN queries if only for a clock tick. 828. [support] multiple zone warning clearer. 827. [bug] the ctl interface was clearing already-cleared timers. 826. [contrib] various improvements to contrib/dns_signer (from TIS). 825. [support] change __NAMESER and __RES to 19991006. 824. [port] sco50 needed #define __BIND_RES_TEXT in port_after.h 823. [bug] named-xfer missed a SIG text format change 822. [bug] TSIG signed truncated responses crashed the server 821. [bug] potential reference after free bugs. 820. [port] ultrix finally works again. 819. [bug] removed test for missing glue from nslookup() as it got false matches. There is no simple test for missing glue. 818. [bug] back out #790, there was no memory leak. 817. [port] Solaris needed #define BSD_COMP in port_before.h. --- 8.2.2-T4B released --- 816. [bug] you could not raise the number of available file descriptors after the first call to res_send() and make use of them. 815. [feature] report version via command line option (-v). 814. [feature] getipnodebyname, getipnodebyaddr and freehostent added. These are RFC 2553 newcomers to the RFC 2133 set. 813. [support] better diagnostics when trying to clean up old unix control socket. 812. [bug] uninitalised variable. 811. [port] sco50 make links was not linking resolv.h.diffs 810. [bug] zone transfer did not transfer all DNSSEC records at delegation points. 809. [support] res_[n]sendupdate has died before it could be used. 808. [bug] res_send() wasn't checking for EINTR after select(). 807. [support] it's now possible to send TSIG'd updates. 806. [support] ns_parserr() was uncompressing from the wrong base in a certain corner case trod on by res_findzonecut(). 805. [bug] only set SO_LINGER if required by the OS, #define DO_SO_LINGER to do so. 804. [bug] another swath of IXFR fixes. 803. [port] Compaq Tru64 UNIX 4.0B with ZK3's experimental IPv6 kit installed will at least build, but hasn't been tested. 802. [support] we no longer cache NXDOMAIN if the QTYPE was SOA. 801. [bug] our negative caching logic would log spurious errors if the response had an empty question section. 800. [bug] #764 was too aggressive in one case. 799. [port] ultrix is a still-moving target. 798. [support] QRYLOG now logs the QCLASS 797. [bug] closing a thread which had called get*by*() would leak memory. 796. [support] deallocate_on_exit now frees memory allocated by irs. 795. [port] solaris 2.4 SO_REUSEADDR generates errors on unix domain sockets. 794. [bug] ixfr_have_log() was logging wrong file name. 793. [bug] clean_cache() was not alway removing complete RRsets. 792. [bug] deallocate-on-exit caused references to freed memory. 791. [support] MEMCLUSTER_DEBUG had an array size error. 790. [bug] fix minor memory leak in ixfr code. 789. [bug] #669 was too aggressive. more than cached data was removed. 788. [bugs] improvements to tsig and dnssec. 787. [port] win/nt lint. 786. [port] IRIX and emul_ioctl(). 785. [bug] #780 broke A record update support. 784. [bugs] still trying to get IXFR working again. --- 8.2.2-T3B released --- 783. [support] make res_send() more friendly to the java scheduler. 782. [support] dangling cnames aren't errors, stop logging them. 781. [support] add -n option to ndc command, to run nonstandard named. 780. [bug] UPDATE did not support the AAAA RR. 779. [bug] miscellaneous IXFR fixes. 778. [support] don't complain to syslog about negative caching RRs. --- 8.2.2-T2B released --- 777. [bug] getword() didn't increment lineno at EOF. 776. [bug] the NOERROR_NODATA cookie overlapped a valid rcode. 775. [protocol] we weren't sending properly formated FORMERR responses. 774. [bug] UPDATE did not support the SRV RR. 773. [bug] named-xfer was calling inet_ntoa in one printf. 772. [typo] Typo in ns_parser.y on maybe_zero_port: line. 771. [lint] UNLINK now performs a INIT_LINK so explicit INIT_LINK's are nolonger needed after UNLINK. 770. [protocol] dynamic update prerequisites were inappropiately matching wildcards, at variance with RFC 2136. 769. [bug] ordering of CNAMES was driven by original query type. 768. [support] MINROOTS is now a configuration option "min-roots". 767. [clarity] adjust XFR log messages to be more clear about cause. 766. [support] add "serial-queries" option to dynamify MAXQSERIAL. 765. [feature] added evInitID() and evTestID() for NOTIFY work. 764. [bug] DNSSEC changed the semantics of match() without changing all the call sites that cared about it. 763. [bug] NOTIFY events caused by dynamic update weren't being deferred, and multiple NOTIFY events weren't being coalesced. 762. [support] don't rotate log file versions on server startup. 761. [port] named-xfer's openlog() was unconditionally using the LOG_CONS option. now it does what named does. --- 8.2.2-T1A released --- 760. [port] preliminary win/nt from baynetworks (thanks!) 759. [support] new compile time option BIND_IXFR, defaults to "off", since our testing has shown up some problems with it. 758. [feature] new "ndc reconfig" command only finds new/gone zones, doesn't stat() master files or qserial() slave SOA's. 757. [support] FORCED_RELOAD is no longer optional. 756. [support] fixed output format of hmac keys; removed DST chaff. 755. [feature] "also-notify" is now a global option. 754. [bug] the control socket was not checked for event lib compatability. 753. [feature] "ndc help" now returns one line command summaries. 752. [feature] "ndc trace" now takes an optional "level" argument. 751. [support] debugging output could segfault in ns_print.c::addstr. 750. [port] A/UX 3.1.1. 749. [port] #9 has now been done for all Makefiles. 748. [feature] "transfer-source" is now a global option. 747. [support] SORT_RESPONSE is no longer a compile time option, since the behaviour can be turned off at runtime with the "rrset_order fixed;" option. 746. [bug] don't bother rescanning the interfaces if setuid!=root. 745. [protocol] IXFR transmission was just plain wrong in some cases. 744. [support] allow the calling location of strings to be recorded. 743. [feature] $GENERATE now supports more record types, and options. 742. [port] port/sco50 was using /usr/local/etc for its ndc socket. 741. [port] HPUX needed __BIND_RES_TEXT. 740. [bug] #634 had the unfortunate side effect of disabling IXFR. 739. [port] probe for IPv6 structures, solaris openbsd freebsd 738. [bug] invalidate pointers back into linked list when element is removed. 737. [port] solaris: expr is sensitive to LC_COLLATE 736. [bug] potential single file descriptor leak opening /dev/random. 735. [bug] memory leak: having rrset-order set and reconfiguring the server results in a memory leak. 734. [port] linux only fills in as many entries as will fill the buffer with SIOCGIFCONF. 733. [bug] RD is not being set on first message to first forwarder resulting in false "Lame Server" reports and degraded service. 732. [bug] errors reading keys from master files could cause the the server to drop core. 731. [bug] highestFD was not reflecting the highest value the library could cope with. 730. [port] rand() does not modify the LSB on BSD based systems. 729. [bug] allow-query responses were dependent upon cache contents. 728. [bug] it wasn't possible to specify the flags of trusted keys in hex, which was inconvenient since dig prints hex. 727. [bug] TSIG keys weren't properly shared with named-xfer if the zone named contained a slash (/). 726. [bug] TSIG keys weren't reloaded correctly with 'ndc reload'. 725. [bug] only the first key in an acl was matched correctly. 724. [bug] "ndc restart" needed a short delay before checking for the health of a newly started name server. 723. [bug] TSIG signed zone transfer failed on especially large zones. 722. [doc] the example named.conf file had invalid TSIG usage. 721. [bug] duplicate records were tripping the cname-and-otherdata test, which wasn't necessary since they'll be ignored. 720. [port] solaris doesn't have gethostid() the way we build. 719. [lint] lots of lint fixed by bob and paul. 718. [bug] multiple CNAME support was not cycling the cnames in an RRset properly. 717. [bug] wrong /bin/ps flags in solaris prand_conf.h. minor tweak to ports/prand_conf/prand_conf.c to ensure proper flags in future ports. 716. [bug] log files are now closed/reopened on a size basis. 715. [clarity] root servers don't need to be primed. 714. [typo] extra "q" in a message in ns_maint.c. --- 8.2.1 released --- 713. [bug] don't loop on untimely eof within config file. 712. [port] hp-ux signals; aix bit types. 711. [perf] don't call find_zone() four times from within qnew(). --- 8.2.1-t7b released --- 710. [bug] can fetch zone from own address if port is different. 709. [bug] make sure zones are properly reinited when they die. 708. [bug] end marker or sizeof, but not both please. --- 8.2.1-t7a released --- 707. [port] AIX, HPUX, SunOS. 706. [feature] zone forwarding can now be applied to master, slave and stub zones as well as forward zones. 705. [bug] some zone options were not being copied. 704. [bug] very obscure problem fixed in res_update(). 703. [bug] single-zone reload was stomping freed memory. --- 8.2.1-t6b released --- 702. [port] solaris vs. enum; linux vs. IPv6. 701. [bug] NOTIFY rejection logic still wasn't correct. 700. [bug] complete #697 --- 8.2.1-t5b (rc2) released --- 699. [bug] if getting the ixfr change log fails send a axfr style response. 698. [bug] res_notify() was rejecting valid NOTIFY messages. re-organise code so that logged messages are more appropriate. 697. [port] linux. some versions define _GNU_SOURCE in features.h some version require the compiler to set the byte order when probing for IPv6 structures. 696. [bug] don't use NULL file pointer if IXFR transaction log cannot be opened due to permission errors. 695. [lint] another considerable amount of lint was removed. 694. [bug] only the last two forwarders would be used. 693. [bug] nsfwdadd() needed to continue outer loop. 692. [bug] RD was not being cleared by ns_forw(). this could cause DNS storms between lame servers. 691. [bug] We still had some leftover named-xfer ixfr tmp files. 690. [bug] return IXFR in question section of AXFR style IXFR response. 689. [bug] we now return "up to date" response to IXFR queries when required. 688. [bug] UDP IXFR now tells the client to use TCP. 687. [bug] IXFR was incorrectly reporting errors on DNSSEC RRs. 686. [port] hpux Makefile.set improvement (+O2 -> +ESlit). 685. [feature] mark recursive queries in query log. 684. [bug] named-xfer now ignores out-of-class glue. --- 8.2.1-t4b (RC1) released --- 683. [lint] considerable lint was removed. 682. [perf] another round of performance tweaks from HP (thanks!). 681. [bug] SIG wasn't being ignored when generating NOTIFY msgs. 680. [feature] delay parent reload as long as we can after removing child zone to save multiple parent reloads. 679. [port] port probe now recognizes SCO 5.0.5. 678. [doc] not all man pages were being installed. 677. [feature] lost feature "allow-recursion" added back in. 676. [bug] "100" was too small for ndc message sizes. 675. [bug] we weren't storing a (needed) extra copy of the zname. 674. [bug] SIGTERM wasn't working the first time it was sent. --- 8.2.1-t3b released --- 673. [bug] nslookup wasn't accepting _ at the beginning of names. 672. [bug] ndc was only passing the verb across the command channel and not the arguements. Reload of a single zone "really" works now. 671. [feature] you can reload multiple zones with a single ndc reload command. e.g. ndc reload zone1 zone2 ... 670. [bug] db_load did not work unless a RR had the class defined. 669. [bug] the cache is now purged when a forwarder is {re}loaded. 668. [bug] complete #652. 667. [bug] allow-query wasn't being allowed for stub zones. 666. [usability] only try to chown()/chmod() a control socket when the owner or permissions _change_ between reloads. 665. [bug] "options topology" is now possible to set. 664. [security] add important solaris-related security note to README. 663. [bug] "ndc -q" now turns off initial header and EOF printing. --- 8.2.1-t2b released --- 662. [usability] src/conf/ added, containing some of ISC's config files. 661. [protocol] we weren't sending AAAA RR's as AXFR glue. 660. [port] IRIX. 659. [contrib] author-submitted changes to dnssigner, new cider2named. 658. [protocol] print better messages wrt TSIG. add p_rcode(). remove _res_resultcodes[]. improve key handling. 657. [port] apply cpp to /usr/include/netinet/in.h to work out if struct sockaddr_in6 and struct in6_addr/inaddr6 are defined. 656. [bug] Classless IN-ADDR support was broken. 655. [bug] major overhaul of IXFR code. 654. [bug] dynamic update of non top of zone SOA now ZONEERR. 653. [feature] check-names now applied dynamic updates as if the zone was being loaded. REFUSED returned. 652. [port/bug] many operating systems allow more descriptors than their default FD_SETSIZE has room for. we catch this now, both by asking the operating system not to do this and by treating as invalid any out-of-range descriptor. 651. [protocol] any soft failures in res_send() will now cause the final return value to be TRY_AGAIN. previously the last server response received was the one returned. 650. [doc] resolver.5 man page clarified and corrected; res_init() made to do what the man page now says it does. 649. [port] make header files c++ compatible. 648. [bug] multiple options definitions of allow-query / allow-transfer / sortlist / blackist / topology are not allowed. warn rather than silently applying the last definition. 647. [bug] options max-ixfr-log-size was not being applied. 646. [feature] memcluster debugging support improved. -DRECORD_MEMCLUSTER to enable. 645. [bug] memory leaks 644. [bug] res_update() could not delete the first CNAME in a chain. 643. [bug] res_update() did not correctly handle labels with periods. 642. [port] SCO 5.0 portability improved. 641. [feature] $TTL now takes TTLs of the form 1w6d7h32m20s. 640. [bug] was returning NODATA rather than NXDOMAIN after a dynamic update removed the last RR from a childless node. 639. [bug] another fix for "rrset_order fixed". --- 8.2.1-t1a released --- 638. [bug] ixfr was still creating the wrong file names sometimes. 637. [bug] bin/dnsquery/dnsquery.c wasn't init'ing the resolver correctly befloew calling gethostbyname(). 636. [port] inet_ntoa() had to go back to being non-const for now. 635. [bug] AXFR wasn't forcing an autoincrement of SOA.SERIAL following a batch of UPDATE requests. 634. [feature] check all master soa's and use best serial, rather than trying them in order and grabbing the first one who answers with one better than the local one. 633. [port] SunOS 4.1.4 has a broken recvfrom() with non-blocking sockets. 632. [bug] res_mkupdate() signed/unsigned stupidity. 631. [bug] HMAC-MD5 fixes 630. [bug] NSTATS output was spaceless. 629. [misc] improvements to TSIG error logging. 628. [bug] "rrset_order fixed" was LIFO rather than FIFO. 627. [bug] TSIG signed zone transfers broken. 626. [bug] multiple CNAME support was broken. 625. [bug] key names are really domains so they need to be made canonical. 624. [bug] ns_name_pton() accepted domains of the form "example.." when it should have rejected them. 623. [feature] it is occasionally useful to know the local address used to perform a zone transfer. this is now logged. 622. [bug] missing check for malloc() failures in strndup(). 621. [bug] various things were wrong with nslookup's "ls -d" cmd. 620. [feature] forwarders are now retried like queries to the delegated nameservers. forward only should be more robust as a result. 619. [protocol] don't refresh TTL's from delegation information. 618. [feature] ndc is now quiet and verbose when it should be. 617. [bug] SOA counters now have minima as well as maxima. 616. [bug] needs were not always processed in a timely fashion. 615. [bug] ns_shutdown() memput() the wrong amount of memory when freeing the zones array. 614. [feature] ndc can now reload single zones including the root zone. 613. [bug] check for old unix domain socket / fifo prior to attempting to establish control channel. error message no longer just noise. 612. [port] Solaris UNIX domain sockets return different error codes and also may use FIFOs. 611. [bug] extend control timeout to 10 minutes. reloads can take a long time. 610. [bug] when reloading via the control channel we were reporting that we were about to reload after the reload was performed. Ensure message is set prior to reloading. 609. [bug] zoneTypeString() could be called with NULL pointer. 608. [bug] set various pointers to NULL after associated memory has been released to prevent accidental use. 607. [bug] finddata() was returning SIG's inappropriately. 606. [bug] fix two memory leaks in db_sec.c. 605. [feature] better error reporting from named-xfer. 604. [bug] fix a bug in the handling of $TTL's absence. 603. [port] add contributed/untested rhapsody port. 602. [bug] multiple "type hint" zones are now supported. 601. [bug] z_ftime wasn't being reset when fopen() failed. 600. [bug] gen_res_get() was initializing the wrong variable. 599. [bug] "ndc reload" exercised an uninitialized variable. 598. [bug] "nslookup reports danger" was reported ambiguously. 597. [bug] we weren't priming the cache in forward-only mode. 596. [bugs] many small bugs in DNSSEC handling were fixed. 595. [bug] nsupdate failed to support quite a few rr types: sig,key,nxt,eid,numloc,srv,atma,naptr,kx,cert 594. [proto] BADID removed per I-D. 593. [bug] mk_update() didn't support SIG. 592. [bug] lcl_pr and lcl_ho were using uninitialized bufsizes. 591. [port] linux. 590. [port] irix. 589. [doc] hesiod(3) man page contrib'd in 1996 finally put in. 588. [bug] too many lame servers at once was fatal. --- 8.2 released --- 587. [perf] uses about 5% less memory than 8.1.2 now. 586. [perf] faster at tcp, therefore less blocking on udp. 585. [misc] various releng lint. 584. [bug] IXFR wasn't doing DNSSEC RRtypes. 583. [bug] dnskeygen now fully qualifies its names; better usage. 582. [port] irix needed some patches applied during the build. 581. [bug] match_order() could dump core after "ndc reload". 580. [bug] ip_match_is_none() could dump core. 579. [bug] state names were off by one in src/lib/isc/ctl_srvr.c. 578. [misc] try without "transfer-source" if axfr connect() fails. 577. [contrib] sqlbind-8. 576. [bug] insecure updates weren't supported. 575. [doc] better documentation of key, trusted-key, zone pubkey. 574. [bug] was freeing freed memory on exit. 573. [port] nextstep. 572. [misc] centralize the name hashing logic (widen in some cases) 571. [perf] the new db_marshal() code was taking too much memory. 570. [perf] the lame server storage was taking too much memory. 569. [bug] src/lib/isc/ctl_srvr.c had an incomplete assertion. 568. [doc] Brent Baccala contributed an nsupdate man page. 567. [port] mpe, nextstep. 566. [protocol] upgrade to tsig draft 08. 565. [lint] use right relative paths for dnssafe includes in dst. 564. [bug] default security level for update rr's wasn't set. 563. [bug] debugging output in dprint_key_info() could panic us. 562. [perf] 8.2-t6b used 30% more memory on root name servers than 8.1.2 did. most of that was db_marshal hash tables. --- 8.2-T6B released --- 561. [bug] DST more graceful in handling unsupported algorithms. 560. [feature] lame server ttl now a configuration option. Re-enable lame server negative caching. 559. [bug] sysquery() was still using the child's name when it switched to using the parent's NS list causing false lame server reports. 558. [bug] disable lame server negative caching for the present. 557. [bug] undersized tcp messages are now detected early. 556. [bug] DNSSEC fine tuning. 555. [bug] the named.conf lexer was depending on two characters worth of putback buffer, ansi c guarantees one char. 554. [port] port to "next" contributed by jack bryans. 553. [contrib] added "snoof", another script kiddie toy. 552. [bug] allow-query didn't interact well with external cnames. 551. [bug] validate_zone could crash the server. 550. [lint] ns_maint was using ns_log_default, not ns_log_in_xfer. 549. [port] netbsd and openbsd improved. prand_conf improved. 548. [bug] ns_resp was using the wrong logging category. 547. [bug] dig was reinit'ing its resolver flags incorrectly. 546. [bug] nsupdate didn't handle HINFO,ISDN,TXT,X25 correctly. 545. [feature] added dnssafe back in. 544. [feature] removed DES encryption support. 543. [port] cleaned cylink of non used definitons in headerfiles. 542. [bug] include/dst no longer needed 541. [bug] CERT records are allowed to have alg == 0. 540. [doc] Removed outdated doc/secure, updated dnssigner documentation, updated dnskeygen.1 539. [bug] db_dump() was misparsing CERT records. 538. [feature] The KEY set is along with SOA, NS, A, AAAA records. 537. [bug] Multiple signatures are handled correctly. 536. [bug] SIG record expiration should be checked when the SIG is verified. 535. [bug] Queries for SIG records of non-authoritative names should not look in the cache or cache the results. 534. [bug] DNSSEC SIG records are dropped when they don't sign any data correctly. 533. [bug] SIG and NXT records are correctly handled when received in responses by named 532. [bug] dynamic update data is now always considered insecure, rather than having no security status. 531. [bug] dynamic update can again remove all data associated with a name (type ANY, class ANY). 530. [lint] downgraded "ctl: unexpected eof" from error to debug. 529. [port] unixware 7 port received. 528. [bug] timeouts could make ctl_srvr dump core. 527. [bug] we were not reliably reaping our children. 526. [bug] Cached CNAMES pointing to servers returning Type 3/4 NXDOMAIN are translated to Type 3 NODATA responses. 525. [bug] nscount could be short if we had to recurse after following a cname and we got a negative response. NS rrset got split between AU and AD sections. 524. [protocol] RFC 2308 support added. 523. [feature] mark lame servers as such and don't use them for NTTL. 522. [port] solaris 7 is now known to work. 521. [port] sunos4 should be supported now. 520. [bug] inet_pton() was allowing some bad ipv6 addresses in. 519. [bug] refuse duplicate also-notify's; optimize logging. 518. [port] hpux portability fixes. 517. [contrib] dnswalk wasn't copying with 8.* "dig" output. 516. [port] MPE portability fix. --- 8.2-T5B released --- 515. [security] lib/dnssafe code removed; now a separate patch. 514. [port] freebsd patches. 513. [bug] memory leak in res_mkupdate(). 512. [bug] $GENERATE could use an unset ttl. 511. [bug] $TTL warning test was wrong. 510. [port] bugs and things found by the netbsd folks. 509. [bug] The labels field in the SIG record may be less than the number of labels in the domain name if the owner of the SIG is a wildcard. 508. [bug] rrset ordering contained an off-by-one error 507. [bug] NXT set processing was not distinguishing between the upper and lower sets at delegation points. 506. [contrib] more script-kiddie toys, this time contrib/adm. 505. [bug] the ixfr changes to named-xfer destabilized stubs. 504. [port] some IRIX problems fixed. 503. [bug] ixfr wasn't correctly setting up its qsp. --- 8.2-T4A released --- 502. [bug] some config file parsing was still using malloc(). 501. [feature] named sets the AD bit in the header when returning authenticated data 500. [bug] dst_verify_data returns the documented error codes 499. [bug] verify_set now verifies the correct data 498. [bug] ixfr was not completely finished. 497. [bug] don't put zone 0 on the free list. 496. [bug] Losing all but last RR of RRset. 495. [port] random portability noise. 494. [bug] sysquery() should not let nlookup() change its data. 493. [feature] add "options ... rrset_order ... cyclic|random|etc". this allows round robin to be turned off selectively, or replaced with pseudorandom ordering, or whatever. 492. [bug] src/bin/named/db_sec.c was memputting objects twice. 491. [feature] add IRP (Information Retrieval Protocol) and daemon. this is functionally similar to solaris "nscd". 490. [bug] lib/isc/ctl_srvr.c couldn't overlap read and write. (also: add session context set/get.) 489. [bug] "cname and other data" was more complex than thought. 488. [port] some netbsd portability stuff. (still not working?) 487. [port] digital unix 3.2 wasn't working (4.0d was though). 486. [feature] add "sortlist", which may yet be merged/renamed into the "topology" verb. 485. [bug] do not complain about default TTLs unless a master. 484. [contrib] add contrib/z0ne, a useful tool for crackers. 483. [contrib] add contrib/query-loc[-*] to look up LOC RR's. 482. [bug] all RR's must now be of the same class as the zone. 481. [bug] outbound zone transfers are killed on any UPDATE. --- 8.2-T3A released --- 480. [bug] ns_update was corrupting TXT records 479. [bug] res_mkupdate was not handling WKS, HINFO, TXT, X25, ISDN, NSAP and LOC records. 478. [bug] name_pack could leave a bad compression pointer. 477. [port] improved support for FreeBSD 3.0. 476. [bug] BSDI contributed some fixes to the /etc/group parsing. 475. [bug] another memory leak in hesiod_resolve(). 474. [bug] SRV RR names were being compressed on output. 473. [feature] IXFR is no longer optional and has been cleaned up. 472. [bug] IXFR was disabling USE_PID_FILE. 471. [feature] add support for CERT records. 470. [bug] rrset_db_upgrade was updating the wrong cache. 469. [performance] use a free list for unused zones. 468. [feature] add getaddrinfo, courtesy of WIDE. 467. [lint] include/dst/dst.h moved to include/isc/dst.h. 466. [bug] fix core dump introduced with tsig glue. --- 8.2-T2A released --- 465. [bug] ref counting bug in ns_xfr. 464. [bug] correct cut&pasteo in IXFR config syntax. 463. [lint] clean psf files after top level "make tar". --- 8.2-T1A released --- 462. [feature] we now use randomized query id's. 461. [feature] new option "version" added. 460. [feature] add initial IXFR support from Check Point Technologies. 459. [bug] res_update() was putting debugging info on stderr. 458. [doc] add named.conf(5), improve doc/html. 457. [feature] named-bootconf is now written in /bin/sh and it is now installed in ${DESTSBIN}. 456. [bug] res->defdname[] wasn't always properly \0 terminated. 455. [bug] _PATH_MEMSTATS was never being used. 454. [doc] the html docs weren't clear about logging having to be specified first in the named.conf file. 453. [feature] add zone type "forward" for selective forwarding (sometimes called "split horizon" or "fake root"). 452. [bug] lib/irs/* was generally not coping with oversized lines and files not ending in \n. 451. [port] BSD/OS 2.* is now a separate port. 450. [Feature] added DNS key generator in bin/dnskeygen. 449. [contrib] added DNS zone signer in contrib/dns_signer. 448. [doc] sample named.conf and html documentation include examples of DNSSEC / TSIG configurations. 447. [feature] named verifies TSIG records on incoming messages, and generates TSIG records on outgoing messages. 446. [feature] res_nsendsigned, res_nfindprimary, res_nsendupdate provide TSIG aware resolver functions. 445. [feature] ns_sign and ns_verify generate/authenticate TSIG signatures on DNS messages. ns_sign_tcp, ns_sign_tcp_init, ns_verify_tcp, and ns_verify_tcp_init are used for tcp transfers. 444. [feature] acls can now include shared key names. 443. [feature] added DNSSEC verification of zone data on load and partial verification of signed data received over the wire. 442. [feature] lib/dst (TIS digital signature toolkit), lib/dnssafe, and lib/cylink added to provide functionality needed for DNSSEC and transaction signatures. 441. [bug] fixed memory leak in hesoid support. 440. [bug] support for res in lib irs was a mess. _res now controls the behaviour of get*by*() again. 439. [bug] fix *END_RESULT macros in port/solaris/port_before.h. 438. [feature] permit the install user and group to be overridden. 437. [feature] TCP truncation now reports IP address of the server. 436. [bug] memory leaks in nsupdate. 435. [doc] updated resolver.3 434. [bug] named.run was not always being created when ndc trace was run. 433. [bug] req_notify required the slave zone to have been loaded. this may not be the case when a zone has expired or is being established over a dial on demand link. 432. [feature] blackhole queries from these nets. do not use these nets to resolve queries. 431. [feature] loop breaking with UDP based well known services. 430. [bug] memory leaks in dispatch_message. 429. [feature] fast retries on host/net unreachable. 428. [bug] CNAME and other data is now a hard error. 427. [feature] support very large numbers of virtual interfaces. 426. [bug] bring named closer into line with the data ranking in RFC 2181, Section 5.4.1. 425. [bug] removed spurious debug statment that generated a lot false bug reports. 424. [bug] closed file descriptor leaks in ns_update. 423. [feature] loc_ntoa() can now accept NULL like other _ntoa's. 422. [feature] you can now specify a port on the master statement to allow transfers from a non standard port. 421. [feature] warn when the root hints do not match reality. 420. [misc] added support for bcc (bounds checking compiler). 419. [feature] bring negative caching into RFC 2308 compliance. 418. [bug] expire now behaviour now as per RFC 1034/1035. 417. [bug] updates and zone transfers weren't locking eachother. 416. [port] support added for HPUX B.11.* 415. [feature] ndc is a C program now, uses new "controls" subsystem. 414. [feature] "controls" element of named.conf now live and working. 413. [feature] octal and hexadecimal numbers now parsed in named.conf. 412. [bug] we now support 2**24-1 (16M) zones. (need namespaces!) 411. [bug] fix *END_RESULT macros in port/bsdos/port_before.h 410. [feature] added support for dial on demand links between servers. 409. [port] remove aggregious use of snprintf(). 408. [feature] add -b option to dig to set srcaddr of tcp connects. 407. [feature] added $GENERATE to generate sets of RR's that only differ by an interator. 406. [doc] added manpage for inet_cidr_ntop() inet_cidr_pton(). 405. [bug] res_nsend() closed sockets unnecessarily on timeout. handle change NS list and RES_STAYOPEN generically. 404. [bug] inet_addr/inet_aton/inet_network accepted illegal inputs as legal. Also enforce octal input. 403. [bug] inet_cidr_ntop() was not producing correct output for all possible inputs. 402. [bug] fix retry/retransmit logic in face of network errors. 401. [doc] the "transfer-source" zone option wasn't documented. 400. [bug] bin/host was dumping core - converted to use getopt. 399. [port] use time() rather than gettimeofday() in dig. 398. [bug] named could exit silently on assertion failures, now assertion failures are logged using INSIST. 397. [port] add an AIX 3.2 port (requires GNU utilities). 396. [bug] dig and nslookup allowed sscanf/sprintf overflows. 395. [bug] dig and nslookup were unable to deal with 64KB answers. 394. [feature] add RES_NOCHECKNAME and "options no-check-names" (in resolv.conf) to turn off modern host/mail name checks. 393. [bug] lib/isc/tree.c was missing a critical \ (#if DEBUG). 392. [bug] inet_aton() wasn't requiring nonterminal octets to be in the range of octets, i.e., 1.300.1.1. 391. [bug] fix bug in MAX_XFERS_RUNNING logic. 390. [bug] ns_update() was capable of renaming an open file. 389. [feature] libbind.a now has a "ctl" subsystem, which is planned to replace signals as a the communication path between "ndc" and "named". preliminary support is in "named". 388. [feature] preliminary/nonfunctional/nonstandard ZXFR support. 387. [feature] inet_cidr_pton() and inet_cidr_ntop() added. 386. [bug] inet_net_pton() was not parsing hex correctly. 385. [feature] three new options for the RES_OPTIONS environment var or for the "options" directive in /etc/resolv.conf: attempts:NN default res.retry timeout:NN default res.retrans rotate use ALL listed nameservers 384. [feature] there is now a nearly-thread-safe resolver API, with the old non-thread-safe API being a set of stubs on top of this. it is possible to program without _res. note: the documentation has not been updated. also note: IRS is a thread-ready API, get*by*() is not. (see ../contrib/manyhosts for an example application.) 383. [contrib] bsdi contributed an /etc/services.db hack, which is currently conditionalized for bsd/os but would work on any modern BSD-derived system (DB, snprintf, etc). 382. [port] bsd/os 4.0 defines its own pselect(), which differs from the one we simulated. we now simulate the right one, and use the right one. 381. [contrib] added contrib/srv, the beginnings of SRV client side. --- 8.1.2 released --- 380. [bug] Replaying the dynamic update log could trigger an INSIST. 379. [port] Updated IRIX port. 378. [bug] The declaration for res_freeupdrec() in resolv.h didn't use __P(). 377. [func] The server now sets SO_SNDBUF on UDP sockets. 376. [port] The malloc() implementation on many systems didn't like memcluster.c's 4KB block allocations, sometimes causing huge amounts of memory to be wasted. memcluster.c now allocates bigger chunks and makes its own 4KB blocks. 375. [bug] If more than (sizeof u_long) gets occurred for a particular memory bucket, an INSIST about puts < gets might have been erroneously trigged. Now total gets and outstanding gets are counted. 374. [port] SCO 3.2v4.2 doesn't have initgroups(), so we do not want to define CAN_CHANGE_ID. 373. [port] Updated LynxOS port. 372. [port] Updated SCO 3.2v5.0.x port. 371. [bug] "make install" could fail on some Linux systems because src/port/linux/include/net/Makefile didn't cope with an empty HFILES variable. 370. [bug] Trying to update an expired slave zone would cause the server to panic. 369. [bug] The Makefile for named-xfer didn't try to create ${DESTDIR}${DESTEXEC} if it didn't exist. 368. [bug] Interface scanning could get confused on BSD-like systems if the sa_len of the address was less than sizeof (struct sockaddr). 367. [func] The default value for the host-statistics option has been changed to "no". --- 8.1.2-T3B released --- 366. [bug] Z_AUTH was set on the cache zone do_reload(). 365. [security] Missing bounds checking in inverse query handling allowed an attacker to overwrite the server's stack. 364. [port] Added support for HP MPE. 363. [bug] named-xfer automatically restarts the transfer if the SOA changes during the transfer. There was no limit on the number of restarts, resulting in a lot of wasted effort if the SOA was constantly changing. The number of restarts is now limited. 362. [security] Requesting a zone transfer for a domain name which had a resource record of a certain format would cause the server to abort(). 361. [bug] named-xfer tries to close files named might have had open. On Solaris, sysconf(_SC_OPEN_MAX) can return RLIM_INFINITY, and if it did named-xfer would try to close all those files. named-xfer now applies an upper limit of FD_SETSIZE. 360. [port] Solaris 2.5 systems needed to be included in port_after.h to get rlim_t. --- 8.1.2-T3A released --- 359. [func] IRS group support is now controlled by the WANT_IRS_GR define in port_before.h. 358. [port] Updated IRIX port. 357. [port] Added support for QNX. 356. [func] Added -u (set user id), -g (set group id), and -t (chroot) command line options to 'named'. 355. [func] If getnetconf() fails because it can't create the socket used to get the interface list, the server will log an error if it is doing a periodic interface scan, and panic otherwise. Previous versions of the server always panicked. 354. [security] Bounds checking in named-xfer, dig, host, and nslookup had problems similar to those in item 293. Added a few more bounds checks to the server. 353. [port] Paths are no longer overridden in port_after.h, and are now generated from the various DEST paths in Makefile.set. 352. [bug] Because of problems with setting an infinite rlim_max for RLIMIT_NOFILE on some systems, previous versions of the server implemented "limit files unlimited" by setting the limit to the value returned by sysconf(_SC_OPEN_MAX). The server will now use RLIM_INFINITY on systems which allow it. 351. [port] Updated HP/UX 10.x port. 350. [bug] errno could be changed by certain signal handlers. These signal handlers now save errno on entry and restore it on exit. This changes eliminates the need for the SPURIOUS_ECHILD #define. 349. [bug] hesiod.h wasn't installed. 348. [port] Added support for LynxOS. 347. [bug] res_update() leaked the zone section it allocated. This leak no longer occurs on normal returns, but still occurs when there is an abnormal return. This will be addressed in a future fix. 346. [bug] Fix 303 fixed one thing and broke another, resulting in a nonfunctional grscan(). 345. [bug] Fix 328 was bad, causing the root zone to be purged every time a toplevel domain was reloaded. 344. [bug] The priming fix in change 330 erroneously called unsched() twice, causing a core dump if priming failed. The priming fix could also erroneously query [0.0.0.0].0. 343. [bug] The REQUIRE() in free_rrecp() was wrong, and was triggered by an unapproved update. 342. [port] Added support for SCO UNIX 3.2v5.0.4. --- 8.1.2-T2A released --- 341. [port] The LOG_CONS option to openlog() does not work as documented on some systems. The server will now use LOG_CONS only if USE_LOG_CONS is defined by the port. Currently the bsdos, decunix, freebsd, linux, and netbsd ports define USE_LOG_CONS. 340. [bug] The pid file was updated before the configuration file had been read. 339. [port] #define HAVE_GETRUSAGE for Solaris >= 2.5. 338. [func] 'host' can now print AAAA records. 337. [bug] rm_datum() erroneously set dp->d_next to NULL when savedpp wasn't NULL. Given a dynamic update operation that deleted more than one RR, this bug would cause all but one of the RRs to be leaked, and would prevent correct rollback if the update failed. 336. [bug] Make sure 's' isn't negative in res_send(). This shouldn't happen, but there have been some reports suggesting it can happen. 335. [lint] Cleaned up more gcc warnings. 334. [port] Added support for HP-UX 9.x. 333. [bug] db_glue.c didn't compile if DEBUG wasn't defined. 332. [bug] named-bootconf.pl didn't convert secondary lines that didn't contain a filename correctly. 331. [bug] If the server was configured with forwarders (but not in forward-only mode), and a query ran out of forwarders and had no nameservers, then the server would erroneously forward the request to [0.0.0.0].0. 330. [bug] If priming of the root servers failed, recovery could take a long time. If using forwarders to prime and the query expired, the first forwarder would always be skipped on subsequent attempts. The server complained about priming problems in forward-only mode, even though it doesn't matter. 329. [port] Some versions of Linux apparently need SPURIOUS_ECHILD. 328. [bug] purge_zone() didn't recurse if given the root zone, causing old data and new data for the root zone to be merged. 327. [func] Add log_check() and log_check_channel(). 326. [func] Add r_prev field to ns_updrec in . 325. [bug] Rollback of a failed dynamic update was done in FIFO order instead of LIFO order. 324. [bug] evTryAccept() closed the wrong fd if getsockname() failed. 323. [bug] eventlib didn't clear bits that had been serviced or deselected out of ctx->{rd,wr,ex}Last. 322. [bug] evDestroy() destroyed the files list before destroying the streams list. If there were any active streams, this would cause a double destroy of the streams' file objects, very likely triggering an 'insist'. 321. [bug] The correct error code for a failed asynchronous connect was not reported. It now is, at least on systems that have the SO_ERROR socket option. 320. [func] Allow multiple pending accepts. evTryAccept() now reports the errno if an error was queued. 319. [bug] The toplevel Makefile passed MARGS before $settings, which prevented overriding a port's Makefile.set from the command line. 318. [bug] The Solaris port_after.h checked for SUNOS_2_5_1 instead of SUNOS_5_5_1. 317. [unused] [This change number was allocated but not used.] 316. [bug] evTryAccept() didn't append to the done list correctly if connLast wasn't NULL. 315. [bug] The dynamic update code was incorrectly converted to clean up ns_updrec structures using the new clustered memory allocator, and this would cause an 'insist' to be triggered some time after a dynamic update had been processed. Instead of freeing the ns_updrec fields directly in ns_update.c, res_freeupdrec() was added to the resolver. 314. [bug] Adding and then deleting an RR in a single dynamic update request would crash the server. 313. [bug] The nameserver would only try zone transfers from the master that answered its SOA query. If a master for some reason can answer the SOA but not the AXFR, the other masters (if any) should be tried. 312. [security] Bounds checking in the resolver and dynamic update code had problems similar to those in item 293. Added more checks to ns_resp.c. 311. [bug] The s_wbuf in the qstream structure was leaked in certain zone transfer failures. 310. [bug] If the server ran out of memory in ns_xfr(), the subsequent connection cleanup could modify the z_numxfrs field of zone 0 instead of the zone being transferred, causing an 'insist' to be triggered later. 309. [bug] NAMELEN() could return a negative length. 308. [func] Don't log ECONNRESET in stream_getlen(). 307. [bug] include/isc/assertions.h and include/isc/list.h weren't installed. 306. [bug] Timewarping into the future would cause repeating timers to generate an event for every interval between the previous time and the new time. Repeating timers are now rescheduled based on the last event time, not their due time. Idle timers now use the last event time to compute the idle interval instead of the due time. 305. [bug] The BOUNDS_CHECK() for the 5 32-bit integers in the SOA RR was wrong. 304. [bug] lib/isc/assertions.c and lib/isc/memcluster.c did not follow the port_{before/after}.h convention. memcluster.c #included eventlib.h but did not need it. --- 8.1.2-T1A released --- 303. [bug] 'bp' in grscan() in lib/irs/lcl_gr.c was incorrectly validated, potentially causing corrupt data to be read. 302. [port] #define HAVE_GETRUSAGE for Solaris >= 2.5.1. 301. [port] Added support for Solaris 2.6. 300. [bug] The space for the pathname of named-xfer in the options block leaked. 299. [bug] wasn't in the include directory, and wasn't included before "port_after.h". 298. [func] Added "deallocate-on-exit" and "memstatistics-file" options. If deallocate-on-exit is "yes", the server will painstakingly deallocate every object it allocated. This is slower than letting the OS clean things up, but is helpful in detecting memory leaks. 297. [port] GNU libc 2.0 doesn't have so in the Linux port we now provide a stub nlist.h that includes the real nlist.h if GNU libc < 2.0 and does nothing if >= 2.0. 296. [bug] "make stdlinks" didn't "mkdir /var/obj" if /var/obj didn't exist. 295. [bug] Specifying a query-source with and address and port that the server was listening to didn't work. 294. [security] The server was willing to answer queries on its forwarding sockets. 293. [security] rrextract() did insufficient bounds checking which could cause it to crash the server by reading from an invalid memory location. 292. [bug] The server sometimes leaked the flushset (ns_resp.c). 291. [bug] The server did not detect oversized UDP packets, causing useless retries. 290. [bug] free_listen_info_list() leaked the IP matching lists; the leak occurred when the config file was reloaded. 289. [bug] [This bug number was allocated for something that turned out not to be a bug.] 288. [func] Add new list and assertion code to the ISC library. 287. [bug] "dig +sort" doesn't do anything, but was mentioned in dig's usage message, as well as in the man page. 286. [bug] Some systems have a default FD_SETSIZE much smaller than the number of files that can be opened. This could cause problems in the resolver and eventlib. FD_SETSIZE may now be set in port/*/include/fd_setsize.h. 285. [bug] If OS probing failed to match any of the supported ports, the build would try to continue with BSD 4.4 settings, with poor results in most situations. An error message is now printed if probing fails. 284. [func] The interface list is now doubly-linked. 283. [bug] The server would panic if binding to an interface that it had discovered failed. Simply not listening to the interface is a better solution. 282. [bug] The nslookup Makefile didn't prefix DESTHELP with DESTDIR when setting DEFS. 281. [bug] A socket() called in ns_main.c used PF_INET instead of AF_INET. 280. [bug] The sample named.conf used "clean-interval" instead of "cleaning-interval". 279. [bug] Some panic() messages in the IP matching code in ns_config.c were wrong. 278. [bug] Setting an interval to zero (e.g. interface-interval) eventually caused random timer destruction. 277. [bug] ns_panic() used "args" twice, but only called va_start() and va_end() once. 276. [bug] nslookup's "ls" command always listed all records instead of behaving the way its man page describes. 275. [bug] add_related_additional() leaked memory if the name was already in the related array. 274. [bug] If a timer was cleared while in executing its callback, and a new non-repeating timer was created afterwards (but still in the callback), the new timer was erroneously destroyed when the callback completed. 273. [func] Added transfer-source and host-statistics options. 272. [func] The zone number is now unsigned, allowing up to 65536 zones instead of the previous limit of 32768. 271. [func] Added evDefer(). 270. [bug] The meaning of the count returned by select() varies somewhat by operating system. Under certain circumstances, this confused eventlib's accounting and caused the server to spin. 269. [func] Added evLastEventTime(). 268. [bug] Connections weren't cleaned up when the eventlib context was destroyed. 267. [func] Added evTimeRW() and evUntimeRW() to control idle timer usage in the eventlib streams module. 266. [func] Added file descriptor table to ev_files.c to improve performance of evSelect() and evDeselect(). 265. [func] Added evHold(), evUnhold(), and evTryAccept(). 264. [func] Double-link many eventlib lists to allow faster removal of list elements. 263. [bug] Remember the previous non-blocking status of sockets given to evListen(). 262. [func] Added idle timers to eventlib. 261. [func] Added clustered memory allocator to eventlib; eventlib and named now use this allocator. 260. [func] The value of FD_SETSIZE that eventlib uses can be set by changing include/fd_setsize.h. 259. [bug] Notification of hosts on the also-notify list stopped after the first successful notification. --- 8.1.1 released --- 258. [bug] Setting SO_SNDLOWAT to 0 in ns_xfr() wasn't doing what it was intended to do, and could trigger a kernel bug on various systems derived from BSD 4.4. 257. [bug] In lib/irs/dns_ho.c, variable needsort was used in addrsort() before it was initialized. 256. [func] Ignore ECHILD from select() if SPURIOUS_ECHILD is defined. 255. [bug] The contents of libport.a needed to be in libbind.a. libport.a has been removed. 254. [install] Install library and .h files under /usr/local/bind instead of /usr/local. When the include files were in /usr/local/include, some compilers would automatically use them. The clients would typically not link with -lbind, causing unresolved symbols at link time. 253. [port] Removed change 216. 252. [port] Added port for UnixWare 2.0.x. 251. [doc] Added a documentation on installing to non-default locations. 250. [bug] The Makefiles for the binaries didn't create the installation target directories if they didn't exist. 249. [bug] Change HAS_SA_LEN to HAVE_SA_LEN in the AIX 4 port. 248. [security] The server now caches only those response records that are within the current query domain. 247. [bug] Forwarding of dynamic update requests sent to a slave for the zone is broken. This will be fixed in a future release, but in the meantime the server will simply refuse the request. Cleaned up the way some update code indicated that the request should be refused. --- 8.1.1-T2B released --- 246. [bug] process_prereq() could core dump if the name being processed wasn't known. 245. [bug] It was possible to evSelectFD the same event bits on the same fd more than once. 244. [bug] eventlib didn't decrement fdCount correctly if the eventmask matched in multiple descriptor sets. 243. [lint] Improved comment in stale(). 242. [port] Added port for OpenBSD. 241. [bug] evConnect() didn't evDeselect() the fd if connect() failed, which would cause us to call select() with a mask that included a closed file. select() would then return EBADF and trigger an 'insist'. 240. [bug] evCancelConn() closed the fd. 239. [port] SunOS doesn't supply RAND_MAX. 238. [bug] fakeaddr() called inet_aton() which wasn't strict enough. inet_pton() is now used. 237. [port] Added UnixWare 2.1.2 port. 236. [bug] The buffer in res_querydomain could overflow. 235. [bug] Fixed memory allocation problems in lib/irs/nis_gr.c. 234. [bug] evDeselectFD didn't restore the fd's previous nonblocking status correctly. 233. [func] Define SPURIOUS_ECHILD in Solaris port. Don't complain about getting ECHILD from recvfrom() if SPURIOUS_ECHILD is defined. 232. [func] named-bootconf.pl now supplies a commented out query-source directive and instructions to use it if there's a firewall involved. 231. [bug] Changed a few strdup() calls in rrextract() into savestr() calls. This prevents "related" checking from being turned off if the server runs out of memory. 230. [bug] If the query control structure was reset in ns_resp.c, we leaked the memory used for the previous qp->q_domain. 229. [func] Added the "dump-file" and "statistics-file" options. 228. [bug] named.conf called "statistics-interval" "stats-interval". 227. [func] demoted "zones changed" and "zones shrunk" messages in tryxfer() to debug level 3. --- 8.1.1-T1A released --- 226. [bug] evCancelConn trashed the connections list if the first element was removed. This could cause a seg fault or trigger an 'insist'. 225. [bug] In the "cannot redefine listen-on for port ..." error message, the port was not converted to host byte order before being printed. 224. [port] Added port for AIX 4. 223. [bug] The dynamic update routine findzone() didn't match class, so if you had two zones with the same name but different classes (e.g. IN and HS), then the wrong allow-update ACL could be used, and the wrong zone could be updated. 222. [bug] If a dynamic master zone was updated and then was made non-dynamic by removing the allow-update ACL or changing it to "none" before the zone had been dumped, then the master file would not reflect the update. 221. [func] added 'also-notify'. 220. [func] revised HAVE_GETRUSAGE ifdefs in ns_config.c. The "cannot set resource limits on this system" message on systems without HAVE_GETRUSAGE will now be logged once per options block, and the message severity is now "info" instead of "warning". 219. [bug] If the root name was encoded in a message using a compression pointer, dn_expand() would erroneously return "." as the name instead of "". 218. [bug] when gethostans() in dns_ho.c encountered a CNAME while processing a PTR query, it erroneously required that the CNAME target pass the res_hnok() test (i.e. that it be an RFC 952 hostname). 217. [bug] dnsquery didn't work because it tried to use the obsolete and broken p_query() call instead of fp_nquery(). 216. [port] set SH=bash in port/freebsd/Makefile.set. 215. [port] #define ts_sec and ts_nsec to tv_sec and tv_nsec respectively in port/freebsd/include/port_before.h. 214. [bug] the clarification TTL changes (see change 145 below) set the SOA minimum field to zero if the MSB was set. The server now leaves the SOA RR alone, but sets z_minimum to zero if the MSB is set. 213. [bug] if the SOA refresh or retry fields of a slave zone were 0, an 'insist' would be triggered when zone maintenance was performed. The server still leaves the SOA RR alone, but now imposes a minimum value for z_refresh and z_retry. 212. [func] added the clean-interval, interface-interval, and statistics-interval options. 211. [func] scan for new or deleted interfaces periodically. 210. [func] the _PATH_DUMPFILE default is now "named_dump.db". 209. [bug] and were #included after port_after.h. They are now #included before it, since they #include system header files. ns_lexer.h was #including and ns_parseutil.h. Now it #includes neither one. These changes required that the definition of struct timespec be moved from port_after.h to port_before.h in the ULTRIX, SunOS, and A/UX ports. 208. [port] removed HAVE_GETRUSAGE from the Solaris port, since Solaris only has it if a Berkeley compatibility package is installed. 207. [bug] abortxfer() always used SIGKILL, which didn't give named-xfer a chance to clean up after itself. Now abortxfer() does a SIGTERM first. If the SIGTERM isn't successful, it will use SIGKILL. 206. [bug] If two zones with the same name but different classes (e.g. IN and HS) were defined, then a zone transfer of whichever zone loaded first would work normally, but a zone transfer of the second would give only the NS and SOA RRs. 205. [bug] certain operating systems (notably Solaris) return error codes the server didn't expect, and thus treated as fatal to the interface. More error codes are now recognized. The server will now log unrecognized errors, but will not delete the interface. Certain error results from recvfrom() and accept() now panic the server. 204. [bug] stub zone transfers would fail if there were no NS records in the SOA response. The stub logic now works as intended and has more error checking. 203. [bug] we logged a failure of bind() in opensocket_d() twice. 202. [port] Linux defines AF_INET6 as 10, so we use that value in port/linux/include/port_after.h. 201. [bug] library Makefiles want to press on if linking of an individual module fails. The 'ld' rule was set up to do this, but the subsequent 'mv' rule was not, causing the make to stop if the 'ld' failed. Now the 'mv' is done only if the 'ld' succeeds. 200. [bug] the value of timeout.tv_sec was printed in SendRequest (bin/nslookup/send.c). select() on some systems (such as Linux) modifies the value of the timeout, so printing it is useless since it will always be 0. 199. [func] if s is too big for FD_SETSIZE in res_send, complain and try another nameserver. 198. [bug] sysnotify() was too strict in requiring an NS RR for the server named in the SOA MNAME field. RFCs 1996 and 2136 say the NS RR is optional. 197. [bug] The parser erroneously freed zone_name if a zone redefinition was attempted. This would cause the server to dump core if a zone appeared more than once in a configuration file. 196. [bug] Makefiles below port/*/include had "fi \" followed by "done" on the next line. This made bash 2.0 unhappy. The "fi" is now followed by a ";". 195. [port] ULTRIX's sh doesn't like an empty "for x in ..." list, and that was causing "make install" to fail in the src/port/ultrix/include/rpc directory. 194. [port] add SH variable to toplevel Makefile, document the need to use SH=bash on systems where /bin/sh is derived from "ash". 193. [bug] named-bootconf.pl could repeat end-of-line comments 192. [bug] ndc was being installed in DESTBIN instead of DESTSBIN. 191. [bug] block delivery of all other signals when in SIGTERM handler in named-xfer. 190. [bug] named-bootconf.pl didn't handle non-masked xfrnets correctly if the network was class B or class C. --- 8.1-REL released --- 189. [port] update to the port/sco50 directory rcvd from author. 188. [func] to avoid potentially confusing log messages, don't set Z_DYNAMIC if "allow-update { none; };" is specified in the config file. 187. [bug] a panic() in new_ip_match_mask() erroneously referred to the function as "new_ip_match_pattern". 186. [bug] transfers-in couldn't be set higher than the default. It may now be set as high as 20. 185. [doc] add a stub example to named.conf. 184. [bug] the usage message was out-of-date. 183. [port] some systems don't define AF_INET6, so we define it if necessary in all port_after.h --- 8.1-T5B released --- 182. [bug] fix the way bindname is allocated in hesiod_to_bind(). 181. [bug] MAXHOSTNAMELEN wasn't defined on Solaris. 180. [bug] a check for zptr != NULL in res_update was wrong. It should have been zptr == NULL. 179. [bug] sq_remove() and sq_done() were calling ns_freexfr() when any stream was removed, resulting in a panic when the server was reloaded. ns_freexfr() is now only called when a zone transfer stream is removed. --- 8.1-T4B released --- 178. [bug] if the server was reloaded and then a zone was deleted and the server reloaded again, all within a short period of time, then pending NOTIFY messages would cause the server to panic when they ran. 177. [lint] replaced BUFSIZ with a more appropriate size in several places. 176. [func] change MAXDATA to 2*MAXDNAME + 5*INT32SZ. 175. [security] libirs now limits hostnames to MAXHOSTNAMELEN characters. 174. [bug] we called ns_refreshtime() instead of ns_retrytime() in the Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL case in zone_maint(). 173. [bug] the server didn't clear the Z_NEED_RELOAD flag in zoneinit(). 172. [bug] if a server was a slave for a zone, and an outbound transfer ever hung or terminated unusually, regular zone maintenance would cease for the zone. 171. [port] work around a bug in the Digital UNIX 4.0B SIOCGIFCONF ioctl. 170. [func] the message logged when a zone is loaded now indicates the class of the zone. 169. [func] the message logged when a zone is removed now indicates both the type and class of the zone. 168. [bug] if a zone's type changed from master to slave on a server reload, the server erroneously deleted the new zone data as part of cleaning up the old zone data. 167. [func] when converting from wire format to printable format, represent special characters ".;\@$ by escaping them with \ instead of converting them to \DDD. 166. [bug] when a slave zone expired, it was not scheduled for immedidate maintenance. 165. [port] added port for SCO OSE 5.0.2, renamed port for SCO UNIX 3.2v4.2. 164. [func] created the "response-checks" logging category. 163. [port] don't define AF_INET6 in nameser_compat.h. 162. [bug] the server panicked if a dynamic update request was sent for a dynamic zone which had not loaded because of syntax errors in the master file. The server now returns NOTAUTH. 161. [bug] debugging messages in process_prereq() referred to process_updates() instead of process_prereq(). 160. [bug] hp was not reset after a realloc() in named-xfer.c 159. [bug] named-bootconf.pl didn't translate stub zones. 158. [lint] cast a number of "no effect" statements to void so that gcc doesn't complain when invoked with -Wall -W -Wno-unused 157. [lint] a number of uses of the %lu printf() format were converted to %u; the corresponding casts to u_long were removed. 156. [lint] converted z_deferupdcnt and z_updatecnt from int to u_int32_t. 155. [func] maint_interval is now gone; SOA sanity checking related to it is gone too. 154. [bug] in named-xfer, unsigned 32-bit integers were sometimes stored in signed 32-bit variables and then printed using a cast to u_long and printf() format %lu. This would cause problems on 64-bit systems if the MSB of the 32-bit integer was set. The variable declarations have been changed to u_int32_t, and the printf format is now %u. 153. [bug] log_open_stream() had two syslogs that said the failing function was log_vwrite() instead of log_open_stream(). 152. [lint] made class, type, and dlen in rrextract() and named-xfer.c/print_output() u_int16_t. 151. [bug] the server was incrementing nssSentFErr in the formerr: code in ns_resp.c even though it wasn't sending FORMERR to anyone. 150. [func] in "check-names response fail" mode, instead of just dropping a failing response, we now send REFUSED to the client and drop the query. 149. [bug] if there wasn't a space between the SOA minimum value and a following ')' in a master file, the server would generate an error when it tried to parse the minimum value, causing the zone load to fail. 148. [func] the list of supported syslog facilities has been increased; the following facilities may now be used, provided they're available on the system 'named' is being built on: kern, user, mail, daemon, auth, syslog, lpr, news, uucp, cron, authpriv, ftp, local[0-7]. 147. [bug] the maybe_syslog_facility, logging_opt, channel_severity, address_name, key_ref, key_stmt, acl_stmt, zone_stmt, optional_class, and size_spec rules in the parser either leaked memory or could leak memory. 146. [func] if an RR set in a reply differed from an RR set in the cache only in the TTL, we would not update the TTL of the RR set in the cache. We now update this TTL to that of the reply RR set if the reply RR set's TTL is greater. 145. [func] follow the direction of the clarification draft and treat TTLs as unsigned 32-bit integers, with a maximum value of 2^31 - 1. TTLs greater than the maximum will be converted to 0. A warning will be issued if this conversion occurs while loading a master zone or during inbound zone transfer. 144. [func] "dig version.bind. txt chaos" now returns only the version number (e.g. "8.1-T4B"). 143. [lint] fixed various mismatches between printf() format string components and their corresponding arguments. 142. [lint] SendRequest_close() in nslookup/send.c had a return type of int instead of void. 141. [port] converted bcopy() to memcpy() or memmove() as appropriate. 140. [bug] certain buffer size checking comparisons in rdata_expand() weren't working because they were checking to see if an unsigned value was < 0. 139. [func] convert a few address comparisons from == to using ina_equal(). 138. [bug] an address comparison used in marking a server as bad was done incorrectly in ns_resp.c because the comparison used = instead of ==. 137. [lint] cleaned up warnings caused by assignment used as truth-value in various source files. 136. [func] changed eventlib-related INSIST statements into INSIST_ERR, so that we can print out strerror(errno). 135. [lint] replaced _getshort() with ns_get16() and _getlong() with ns_get32() in various source files. 134. [lint] findzone() and rdata_expand() were used before they were declared in ns_update.c, and were not declared static. 133. [lint] merge_logs() was not declared in ns_func.h. 132. [lint] Linux port_after.h didn't declare daemon(). We now do so, but only if GNU libc < 2.0. 131. [lint] set_boolean_option() was not declared in ns_func.h. 130. [lint] yyparse() was not declared in ns_parser.y. 129. [lint] ns_lexer.h didn't declare lexer_end_file(). 128. [lint] db_dump.c, db_lookup.c, db_update.c, db_glue.c, db_save.c, ns_ncache.c, ns_req.c, ns_stats.c, and ns_xfr.c didn't #include 127. [lint] logging.c, ev_connects.c, ns_maint.c, ns_glue.c, ns_update.c, dig/dig.c, nslookup/list.c, nslookup/send.c, host/host.c, and dnsquery/dnsquery.c didn't #include . 126. [lint] res_update.c, heap.c, db_load.c, db_save.c, db_glue.c, ns_lexer.c, ns_forw.c, ns_maint.c, ns_req.c, ns_stats.c, ns_xfr.c, ns_glue.c, ns_config.c, ns_update.c, host/host.c, nslookup/list.c, and nslookup/getinfo.c didn't #include . 125. [lint] res_mkupdate.c, ns_update.c, nsupdate.c, ns_print.c, didn't #include . 124. [port] replaced bcmp() with memcmp(). 123. [func] while not required, it's nice to preserve the order of RRs as received when ROUND_ROBIN isn't on, so we now do so. 122. [bug] under certain improbable conditions, the server could erroneously set a maintenance timer for a master zone. When the timer went off, it would trigger the INSIST() in zone_maint(). 121. [port] replaced bzero() with memset(). 120. [func] added multiple-cnames option. 119. [bug] the timeout: code in ns_resp.c didn't clean up TCP connections. 118. [port] added port for IRIX 5.3, 6.2, 6.4 117. [bug] removed declaration of getnum_error from db_load.c, since it is now declared in ns_glob.h. 116. [bug] GNU libc 2.0 doesn't have a , so in the Linux port we now provide a stub net/route.h that includes the real if GNU libc < 2.0 and does nothing if >= 2.0. 115. [func] on Linux systems, avoid an often unnecessary 'ranlib' and the subsequent relinking of all binaries by using the 's' flag of 'ar'. 114. [bug] 'make install' didn't work on HP/UX because the path to the install script was wrong in many cases. 113. [bug] named-xfer didn't clean up properly when sent SIGTERM. 112. [bug] named-xfer didn't clean up properly if an error occured in print_output(). 111. [func] added "max-transfer-time-in" option. The server used to allow a maximum of 2 hours for an inbound zone transfer to complete. This time can now be set globally or on a per-zone basis. The parameter is the number of minutes a transfer can take. 110. [func] moved declaration of d_ns in struct databuf to improve structure alignment. 109. [bug] addname() in ns_print.c didn't write an "@" for RRs that contained a domain name which was the same as the zone origin (it wrote nothing). 108. [bug] the server didn't check for EINTR in readable() and writable() in ev_streams.c. 107. [bug] check for both EWOULDBLOCK and EAGAIN after certain system calls instead of using PORT_WOULDBLK. This fixes partial zone transfer problems reported on Sun systems. 106. [bug] db_load() couldn't read SOAs with ( ) that were only one line. 105. [bug] fixed typo in Linux Makefile.set MANROFF definition. 104. [func] move various rrset debugging messages, rm_datum, and nsfree messages to debug level 3. Moved a few rrset debugging messages to debug level 2. 103. [bug] d_rcnt could overflow; to prevent this it has been increased to 32 bits. d_mark was made unsigned and decreased to 12 bits. 102. [func] added macro DRCNTDEC to go along with DRCNTINC. 101. [bug] clean_cache() didn't count deleted RRs, so it always reported "Cleaned cache of 0 RRs". 100. [bug] heap_for_each() didn't return a status, and didn't check for a NULL context or a NULL action. heap_element() didn't set errno to EINVAL when given invalid arguments. 99. [bug] the category rule in the parser leaked memory. 98. [bug] "notify" was not recognized as a valid category name. 97. [security] zone access control wasn't applied correctly to names that didn't exist, allowing an attacker to determine whether or not a given name exists in a zone. 96. [bug] we didn't recognize certain non-fatal errno values when recvfrom() failed; this would result in us dropping an interface unnecessarily. --- 8.1-T3B released --- 95. [bug] named-bootconf.pl didn't process xfrnets correctly (if no netmask was specifed, it assumed a mask of 255.255.255.255 instead of the natural netmask for the class of the address). 94. [bug] named-bootconf.pl didn't handle lines ending in a comment. 93. [bug] if rename() failed in merge_logs(), we would return garbage instead of -1. 92. [bug] writemsg() in named-xfer.c was returning a random value instead of the number of bytes written. 91. [bug] schedretry() could set retry times in the past because it was relying on 'tt' which hadn't been updated. It now calls gettime(&tt). 90. [bug] 'tt' might not have been current when clean_cache() was called. 89. [bug] ns_lexer.h didn't #include 88. [cleanup] removed some relics of the early days of BIND 8's new logging system from the parser and ns_config.c. 87. [bug] when writing to a TCP socket, the server didn't handle errors from the write() correctly. Under the right circumstances, this will cause the server to spin. The most common trigger would be a large outbound zone transfer where the far end died. 86. [cleanup] fixed comment in dig.c that messed up font-lock mode in emacs. 85. [bug] inet_lnaof, inet_makeaddr, inet_netof, and inet_network were missing from lib/inet. 84. [func] improved log_channel creation and use by making the type more opaque. The logging API provides a more complete set of services. Added the LOG_CHANNEL_OFF flag. 83. [func] removed statistics_channel; it wasn't being used. 82. [lint] a few handler functions were declared as void (*)() instead of void (*)(void). All now have the latter declaration. 81. [port] added port for A/UX 3.1.1. 80. [port] added port for SCO UNIX 3.2v4.2. 79. [bug] when processsing slave zones during a config file reload, in the "backup file changed" (or missing) case we were calling purge_zone() and do_reload() even if we had never successfully transferred and loaded the zone. 78. [cleanup] moved writemsg() to named-xfer.c. 77. [cleanup] removed doupdate() from ns_resp.c. 76. [bug] writev() in lib/bsd would keep going if there was a partial write; this could cause incorrect output. 75. [func] added readv() to lib/bsd. 74. [bug] if evConnect() failed in tcp_send() we were aborting the server instead of just returning an error. 73. [port] automatically fix getgrgid() declaration in ULTRIX 4.5 grp.h. 72. [func] make port/*/Makefile invoke SUBDIR make in include. Add/modify include and include/sys Makefiles. 71. [port] added utimes() to lib/bsd. 70. [doc] README broken up into INSTALL, TODO, port/README. Added more info about many topics. 69. [bug] NOTIFY didn't handle an unknown NS target. E.g. if we had "test.domain NS unknown.name" and "unknown.name" was not known, NOTIFY wasn't doing an "A" query for "unknown.name". 68. [lint] tweaks to ERR() and OK() in eventlib_p.h. 67. [bug] 'ch' in main() was a char instead of an int. 66. [bug] in bin/named/Makefile, pathnames wasn't getting linked with ${LIBBIND}, ${LIBPORT}, and ${SYSLIBS}. 65. [port] automatically fix timespec in BSD/OS 2.1 includes. 64. [func] lib/isc/heap.c now includes port_before.h and port_after.h. Fix 58 (below) has been undone; with port_after.h we'll now use __ansi_realloc() from Fix 59. 63. [bug] STRIP and PS were missing from MARGS in bin/Makefile. 62. [func] RRs in the additional data section must relate to RRs in the answer and authority sections. Only certain RR types are allowed in the authority and additional data sections. 61. [bug] Dynamic update didn't understand SRV records. 60. [bug] SRV records weren't decoded properly. --- 8.1-T3A released --- 59. [bug] The IRS library also wanted an ANSI C realloc(). port/sunos now provides __ansi_realloc(). 58. [bug] SunOS didn't like heap.c doing realloc() on a NULL pointer (in ANSI C that is equivalent to malloc()), so we malloc() instead. 57. [bug] interface discovery complained about bogus interfaces on ULTRIX, SunOS, and HP/UX because SIOCGIFCONF_ADDR wasn't defined in their port_after.h. 56. [API] created lib/nameser/ns_name.c and moved a lot of the functionality from lib/resolv/res_comp.c into it. functions older than 8.1 were stubbed out, but new functions from 8.1 were just renamed/removed. 55. [bug] findzone in ns_update wasn't ignoring z_nil zones. 54. [bug] if the named-xfer exec() failed, a misleading message was printed. 53. [bug] interface discovery didn't work on NetBSD because HAVE_SA_LEN wasn't defined in port_after.h. 52. [func] log the host we got a NOTIFY message from 51. [bug] we weren't sending out NOTIFY messages if the SOA was changed as the result of a dynamic update. 50. [bug] req_notify() wasn't calling sched_zone_maint() after it called qserial_query(). 49. [bug] initial_{data,stack,core}_size and initial_num_files weren't in an #ifdef HAVE_GETRUSAGE block. 48. [func] use sysconf(_SC_OPEN_MAX) instead of getdtablesize() in all cases when USE_POSIX is defined. 47. [bug] printupdatelog() was printing the post-update serial number in the zone section instead of the pre-update serial number. 46. [bug] zp->z_serial wasn't being updated if a dynamic update changed the zone serial number. 45. [bug] the SEQ_GT test in db_update was backwards. 44. [func] merge_logs() didn't work because a 'break' wasn't removed when class and type lookups were converted to sym_ston. 43. [func] evResetTimer() added to eventlib. 42. [bug] incr_serial() doesn't need to call schedule_dump(). 41. [bug] reset_retrytimer() could clear a timer that had already been cleared. 40. [bug] some zone data structures weren't freed if the zone was removed. 39. [func] The eventlib timers module now uses a heap to implement the timer queue. 38. [bug] dynamic zones weren't dumped if they were removed from the configuration file. 37. [func] created the "load" logging category. 36. [func] find_zone now uses a hash table instead of a linear search. 35. [bug] we weren't scheduling a retry for dumps or soa serial increments that failed. 34. [func] instead of doing all NOTIFY messages five seconds after loading completes, we now spread them out over up to fifteen minutes (the maximum delay depends on how many zones there are). 33. [func] if there are too many qserials running, we'll try again in five to thirty seconds. 32. [bug] z_dumptime wasn't getting set to zero after a zone dump. 31. [func] Each zone now has a maintenance timer. sched_maint() is gone. The new programming rule: if you change zp->z_time, it's your reponsibility to ensure sched_zone_maint(zp) gets called. 30. [func] short circuit PrintTimers evPrintfs if not debugging at a level where PrintTimers would print something. 29. [bug] if a log message with a non-default category was logged to a default category channel which had print-category on, "default" was printed instead of the category name. 28. [func] the performance of the main loop has been improved. 27. [bug] NOTIFY messages weren't being delayed after a zone load. 26. [bug] the eventlib category wasn't working if the channel wasn't the default debugging channel. 25. [func] added the "maintenance" logging category. 24. [func] periodic statistics dumps are now done using an eventlib timer instead of in ns_maint(). 23. [bug] names which have multiple CNAME records are illegal, but the server was allowing them. 22. [func] convert to POSIX signals from eventlib signal handling; the eventlib API no longer provides signal support. 21. [func] converted assert() to INSIST() so that the logging system (category "insist") will be used if a consistency check fails. 20. [bug] the server could exit when it shouldn't, and without leaving a message or a core file, because it wasn't handling SIGPIPE. 19. [port] Solaris has trouble if the size of the buffer used for IP_OPTIONS processing isn't 40 bytes. 18. [bug] library Makefiles we were using 'ld' instead of ${LD}. Added LD_LIBFLAGS. 17. [bug] on at least one OS, ctime() can return NULL and this can cause problems. We now call checked_ctime() in ns_glue.c, which returns "\n" if ctime() fails. 16. [bug] some signal handlers were calling library routines which POSIX does not designate as safe for use by signal handlers. 15. [func] finished conversion to new options scheme of name checking and inbound zone transfer parameters. 14. [func] added os_change_directory(). 13. [bug] write_open() in ns_config.c wasn't checking if the file was regular before unlinking. 12. [func] added "os" logging category. 11. [bug] named-bootconf.pl used the deprecated channel name "default" instead of "default_syslog". 10. [bug] named-bootconf.pl didn't understand continuation lines. 9. [bug] remove -p from mkdep command in Makefiles for bin/named and bin/nslookup. 8. [bug] add CDEBUG to Makefiles that link using ${CC}. 7. [bug] timestamp and level were printed twice for file channels in lib/isc/logging.c. 6. [bug] off by one with on level_text subscript in lib/isc/logging.c. 5. [bug] broken channels sometimes weren't marked as broken in lib/isc/logging.c. 4. [bug] didn't set foundname=0 after try_again: in ns_resp.c. 3. [bug] update_pid_file() didn't put a newline after the pid. 2. [func] minor log message tweaks in ns_config.c. 1. [bug] zone names needed to be canonicalized in the parser. --- 8.1-T2B released --- Index: head/contrib/bind/INSTALL =================================================================== --- head/contrib/bind/INSTALL (revision 60940) +++ head/contrib/bind/INSTALL (revision 60941) @@ -1,309 +1,317 @@ Systems it is known to compile and run on: BSD/OS 3.1, 4.0.1 - FreeBSD 3.1, 3.2, 3.3 + FreeBSD 3.3, 3.4 RH Linux 5.2 (don't use "make links" when building, though) Debian GNU/Linux 2.2.9 ("unreleased") Digital UNIX 3.2C, 4.0, 5.0 NetBSD/i386 1.3.2, 1.4 SunOS 5.6 (Solaris 2.6), SunOS 5.7 (Solaris 7) SCO UnixWare 7.0, 7.0.1, 7.1 + IRIX 6.5 Systems it has been known in the past to compile and run on: AIX 4.x A/UX 3.1.1 - Digital ULTRIX 4.5 + Digital ULTRIX 4.5 (without Compaq's Y2K kit installed) HP MPE HP-UX 9.x, 10.20 IRIX 5.3, 6.2, 6.4 LynxOS + FreeBSD 3.1, 3.2 NetBSD 1.2, 1.3 OpenBSD 2.1 QNX SCO UNIX 3.2v4.2, SCO OSE 5.0.4, UnixWare 2.0.x, 2.1.2 SunOS 4.1.4 SunOS 5.5 (Solaris 2.5) See port/README for information on porting BIND 8 to other systems. Building If you do not have an ANSI/ISO C compiler, give up or get GCC. The one exception is the ULTRIX compiler, which isn't full ANSI C but it has function prototypes and BIND works around the rest. BIND 8 also wants a C library that's ANSI/ISO standard, although it can work around some common failings. If you do not have yacc, get byacc or GNU bison. If you do not have lex, get GNU flex. For information on where to get GNU software, see http://www.fsf.org/order/ftp.html. If you want to build outside the source pool, then make DST=/your/destination/here SRC=`pwd` links cd /your/destination/here If you want to use DST=/var/obj/bind, you can simply type make stdlinks Next, make sure you have no stale trash laying about make clean Then, update the Makefile dependencies: make depend NOTE: "make depend" is a NO-OP for these platforms: AIX, HPUX and NeXT. Finally, make all Installation To install, type make install This will copy binaries to the appropriate locations for your system, and install the BIND 8 library and header files under /usr/local/bind. The following variables can be used to change where things get installed: DESTDIR prefix used in front of all other DEST variables. The default is the empty prefix. (for non-root installs; not equivalent to autoconf's --prefix) DESTLIB libraries DESTINC include files DESTBIN ordinary binaries (e.g. dig, nslookup) DESTSBIN system binaries (e.g. named) DESTEXEC helper binaries (e.g. named-xfer) DESTHELP place to put nslookup's help file DESTMAN man file location DESTETC configuration file DESTRUN PID file location and "ndc" control channel location. This cannot be the same directory as DESTSBIN. These variables should be specified in the Makefile.set for your port (e.g. if you use Solaris, in src/port/solaris/Makefile.set). Before doing 'make install', you must rm .settings in the top level source directory because the build system caches these variables. Using BIND 8 Library Routines Until a method to update the system's libraries is available, applications wishing to use BIND 8 library routines must include BIND 8 .h files, and must link with libbind.a. E.g. cc -I/usr/local/bind/include -c sample.c cc -o sample -L/usr/local/bind/lib sample.o -lbind The default locations for libbind.a and .h files in BIND 8.1.1 and BIND 8.1.2 are different from those used in BIND 8.1-REL. If you did a 'make install' for BIND 8.1-REL, then you should delete the files it installed. They are: /usr/local/lib/libbind.a /usr/local/include/arpa/inet.h /usr/local/include/arpa/nameser.h /usr/local/include/arpa/nameser_compat.h /usr/local/include/netdb.h /usr/local/include/resolv.h /usr/local/include/sys/bitypes.h (if it exists) /usr/local/include/sys/cdefs.h (if it exists) Operating System Notes AIX Build problems have been reported with the AIX "make". We recommend using GNU "make" instead. FreeBSD, NetBSD, OpenBSD and BSDI The kit should compile even if you have intalled the KAME IPv6 kit. + + IRIX + build problems have been reported w/ IRIX 6.3, res_debug.c + and #include . You may need to comment out + the #include and declare + "void *malloc(size_t size)" to get named to compile on 6.3. Linux "make links" and "make stdlinks" cause problems on some Linux kernels because there are too many levels of symbolic links. QNX Read src/port/qnx/README before trying to build. SCO 5.0.x To build using gcc, copy "port/sco50/Makefile.set.gcc" to "port/sco50/Makefile.set". To go back to using SCO's compilers, copy "port/sco50/Makefile.set.sco" to "port/sco50/Makefile.set". Solaris We've tested with Sun's compilers, yacc, and lex, and also with gcc, byacc, and flex. By default, the build will try to use gcc. If you want to use the Sun compilers, simply copy "port/solaris/Makefile.set.sun" to "port/solaris/Makefile.set". To go back to using gcc, copy "port/solaris/Makefile.set.gcc" to "port/solaris/Makefile.set". If you're using a Solaris release earlier than 2.5 and you have a large number of interfaces on your system, you many need use a script to "limit descriptors N" (where 'N' is a suitably large number) before execing "named". On Solaris 2.5 and later, the server will do this itself. SunOS 4.1.4 An ANSI/ISO C compiler is required; we used gcc 2.7.2.1. NeXT Read src/port/next/README.FIRST before trying to build. Certain older versions of FreeBSD, NetBSD and BSD/OS These systems have a /bin/sh based on "ash", which doesn't handle POSIX-style quoting correctly. Using "bash" will fix the problem. Either run make with "SH=bash" on the command line, or edit src/Makefile and change "SH=sh" to "SH=bash". FD_SETSIZE The highest numbered file descriptor that the server and the resolver can utilize is determined by the FD_SETSIZE value of the system. Some systems set FD_SETSIZE much smaller than the actual number of files that can be opened. On such systems, create an "fd_setsize.h" file that sets FD_SETSIZE appropriately in the port's include directory. User and Group ID Specifying "-u" followed by a username or numeric user id on the "named" command line will cause the server to give up all privileges and become that user after the initial load of the configuation file is complete. "-g" may be used similarly to set the group id. If "-u" is specified but "-g" is not, the group used will be the given user's primary group. Here are some hints: Because the server will have no privileges after changing its user id, you must restart the server if you change the interfaces and ports that the server is listening on, or if you add an interface. If you log to files, you should create all of the log files in advance (e.g. with "touch"), and make sure they are owned by the user and group "named" will be running as. You'll have to edit "ndc" to get it to start the server with the appropriate flags. Note: this feature is still experimental. Chroot "-t" followed by a directory path on the "named" command line will cause the server to chroot() to that directory before it starts loading the configuration file. Setting up a chrooted area varies somewhat by operating system. Some experimentation may be necessary. Here are some hints: Don't forget to install named-xfer. Either don't use shared libraries when you build, or do whatever is required on your OS to allow shared libraries to be used after a chroot(). syslog() is often troublesome after chrooting. Use the "logging" statement and log to a file instead. /dev/null should be in the chroot directory hierarchy. You can usually find out the mknod parameters for a null device by looking in /dev/MAKEDEV. You'll have to edit "ndc" to get it to start the server with the appropriate flags, and to use the right pid file. Note: this feature is still experimental. Using the Server Note that /etc/named.boot is long gone. You need to make yourself an /etc/named.conf (note, that ends in "conf" rather than "boot") file. This file looks a lot like a C program or a modern gated.conf file; there are lots of {curly braces} and it takes some getting used to. You may get a lot more help from the example file (which is bin/named/named.conf) than from the documentation (see ../doc/html). You can convert your named.boot file to a named.conf file if you have Perl; see bin/named/named-bootconf.pl. All the files that used to be created in /var/tmp, e.g. named.run, will now be created in the directory specified in the options statement. If debugging is turned on using the "-d" flag on server startup, then named.run will be created in the current directory. Known Dynamic DNS Bugs If the server is master for a zone and authoritative for a child of that zone, then a dynamic update to the parent will destroy the delegation to the child when the parent zone is written to disk. This problem will be fixed in a future release. The only workaround is to not be authoritative for child zones of a dynamic zone. Slave servers do not forward update requests to the primary master correctly. This will be fixed in a future release. In the meantime, slaves will refuse dynamic updates. Shared Libraries Absolutely no support exists for editing the system's shared libraries to update the resolver. If you want to do that you probably want to look at BIND Version 4 (see http://www.isc.org/isc/) or wait a while or help out a lot. This means you probably do not want to install the library or include files into /usr/lib or /usr/include, and this kit helpfully puts everything into /usr/local/lib and /usr/local/include for that reason among others. Notes about contrib and doc The BIND 8 "doc" package includes HTML documentation as well as all the RFC's, Internet Drafts, and "man" pages we can think of. You may need to install the doc/tmac files in your nroff/troff support directory since we use the newer BSD "mandoc" system for our "man" pages. The BIND 8 "contrib" package is full of junk that you may want to take a look at. Feel free to send us more junk for future releases. Bugs Please report bugs to bind-bugs@isc.org Index: head/contrib/bind/Makefile =================================================================== --- head/contrib/bind/Makefile (revision 60940) +++ head/contrib/bind/Makefile (revision 60941) @@ -1,67 +1,68 @@ -## Copyright (c) 1996,1999 by Internet Software Consortium, Inc. +## Copyright (c) 1996,1999 by Internet Software Consortium. ## ## Permission to use, copy, modify, and distribute this software for any ## purpose with or without fee is hereby granted, provided that the above ## copyright notice and this permission notice appear in all copies. ## ## THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS ## ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES ## OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE ## CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL ## DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR ## PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ## ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ## SOFTWARE. -# $Id: Makefile,v 1.9 1999/11/05 04:52:54 vixie Exp $ +# $Id: Makefile,v 8.51 1999/11/06 03:24:02 vixie Exp $ -# This is just for making distributions. For the real Makefile, cd src. +TOP= +SUBDIRS= include port lib bin -all clean depend: FRC - @echo go to the ./src directory, you cannot make '"'$@'"' here. - @false +SH=sh -tar: bind-doc.tar.gz bind-src.tar.gz bind-contrib.tar.gz +default: all -pgp: bind-doc.tar.gz.asc bind-src.tar.gz.asc bind-contrib.tar.gz.asc +all clean depend install distclean:: FRC + @set -e; \ + version=`cat ${TOP}Version`; \ + systype=`${SH} ${TOP}port/systype ${TOP}.systype`; \ + if [ $$systype = "unknown" ]; then \ + echo "There is no BIND port for this system in this kit."; \ + else \ + settings=`${SH} ${TOP}port/settings ${TOP}.settings \ + < ${TOP}port/$$systype/Makefile.set`; \ + PATH=`pwd`/port/$$systype/bin:$$PATH; export PATH; \ + for x in ${SUBDIRS}; do \ + ( cd $$x; pwd; \ + eval "${MAKE} $$settings ${MARGS} SYSTYPE=$$systype \ + VER=$$version \ + $@"; \ + ); \ + done \ + fi -bind-doc.tar.gz: Makefile - cd doc/bog; make clean file.psf file.lst - cd doc/man; make clean all - cd doc/man; make MANROFF="groff -t" OUT_EXT=psf clean all - tar cf - Makefile doc | gzip > bind-doc.tar.gz - cd doc/man; make clean - cd doc/man; make MANROFF="groff -t" OUT_EXT=psf clean +clean distclean:: + rm -f .systype .settings -bind-src.tar.gz: Makefile - cd src; make distclean - cd src/bin/nslookup; make commands.c - cd src/bin/named; make ns_parser.c - tar cf - Makefile src | gzip > bind-src.tar.gz +links: FRC + @set -e; mkdir ${DST}; cd ${DST}; pwd; ln -s ${SRC} SRC; \ + ln -s SRC/Version .; cp SRC/Makefile .; chmod +w Makefile; \ + systype=`${SH} SRC/port/systype`; \ + for x in ${SUBDIRS} ; do \ + ( mkdir $$x; cd $$x; pwd; ln -s ../SRC/$$x SRC; \ + cp SRC/Makefile Makefile; chmod +w Makefile; \ + ${MAKE} ${MARGS} SYSTYPE=$$systype links; \ + ); \ + done -bind-contrib.tar.gz: Makefile - tar cf - Makefile contrib | gzip > bind-contrib.tar.gz +stdlinks: FRC + if [ ! -d /var/obj ]; then \ + mkdir /var/obj; \ + fi + ${MAKE} ${MARGS} SRC=`pwd` DST=/var/obj/bind links -bind-doc.tar.gz.asc: bind-doc.tar.gz - rm -f bind-doc.tar.gz.asc - pgp -u pgpkey@isc.org -sba bind-doc.tar.gz - -bind-src.tar.gz.asc: bind-src.tar.gz - rm -f bind-src.tar.gz.asc - pgp -u pgpkey@isc.org -sba bind-src.tar.gz - -bind-contrib.tar.gz.asc: bind-contrib.tar.gz - rm -f bind-contrib.tar.gz.asc - pgp -u pgpkey@isc.org -sba bind-contrib.tar.gz - -noesw: src/Version src/lib/Makefile src/lib/dst/Makefile \ - src/lib/cylink/. src/lib/dnssafe/. - perl -pi.BAK -e 's/$$/-NOESW/' src/Version - perl -pi.BAK -e 's/ cylink dnssafe//' src/lib/Makefile - perl -pi.BAK -e 's:-I../cylink::' src/lib/dst/Makefile - perl -pi.BAK -e 's:-I../dnssafe::' src/lib/dst/Makefile - perl -pi.BAK -e 's/-DCYLINK_DSS//' src/lib/dst/Makefile - perl -pi.BAK -e 's/-DDNSSAFE//' src/lib/dst/Makefile - rm -rf src/lib/cylink src/lib/dnssafe +uplinks: FRC + @set -e; systype=`${SH} ${TOP}port/systype`; pwd=`pwd`; \ + ${MAKE} ${MARGS} SRC=../`basename $$pwd` "DST=../$$systype" links FRC: Index: head/contrib/bind/README =================================================================== --- head/contrib/bind/README (revision 60940) +++ head/contrib/bind/README (revision 60941) @@ -1,213 +1,213 @@ -This is the source portion of BIND version 8.2.2, Patchlevel 5. Its -companions are "doc" and "contrib" so you are probably not missing anything. +This is the source portion of BIND version 8.2.3-T2B. Its companions +are "doc" and "contrib" so you are probably not missing anything. See the CHANGES file for a detailed listing of all changes. See the INSTALL file for information on building and installing BIND. See the SUPPORT file for information on obtaining commercial support for ISC artifacts including BIND, INN, and DHCP. SECURITY NOTE: Solaris and other pre-4.4BSD kernels do not respect ownership or protections on UNIX-domain sockets. This means that the default path for the NDC control socket (/var/run/ndc) is such that any user (root or other) on such systems can issue any NDC command except "start" and "restart". The short term fix for this is to override the default path and put such control sockets into root- owned directories which do not permit non-root to r/w/x through them. The medium term fix is for BIND to enforce this requirement internally. The long term fix is for all kernels to upgrade to 4.4BSD semantics. BIND 8.2.2 patchlevel 5 Highlights Bug in named-xfer (from patchlevel 4). Portability to IPv6 versions of FreeBSD, OpenBSD, NetBSD. Portability improvements (A/UX, AIX, IRIX, NetBSD, SCO, MPE/IX, NT). "also-notify" option could cause memory allocation errors. IXFR improvements (though client-side is still disabled). Contributed software upgraded (including TIS's "dns_signer"). Several latent denial-of-service bugs fixed (from audits, not abuse). New "make noesw" top-level target for removing encumbered components. BIND 8.2.2 Highlights Interoperability with MS-Win2K has been improved. Server-side IXFR is now known to work even under high load. Support for Windows/NT (thanks to BayNetworks). More fixes, especially to DNSSEC, TSIG, IXFR, and selective forwarding. More portability improvements and lint removal (A/UX 3.1.1, SCO 5.0). Better NOTIFY behaviour, especially with large update volume. Better UPDATE handling, including SRV RR support and RFC compliance. Fix for "ndc reload ZONENAME" (specific zone reload) problems. Fix for round robin when multiple CNAMEs are in use. New "min-roots" (MINROOTS) and "serial-queries" (MAXQSERIAL) options. Log files are no longer auto-rotated every time the server starts up. New "ndc reconfig" command only finds new/deleted zones, no stat()ing. New global options for "transfer-source" and "also-notify". $GENERATE now supports more record types, and options. BIND 8.2.1 Highlights Bug fixes, especially to DNSSEC, TSIG, IXFR, and selective forwarding. Portability improvements and lint removal. Use best SOA rather than first-better when selecting an AXFR master. $TTL now accepts symbolic time values (such as "$TTL 1h30m"). "ndc reload" now accepts a zone argument, for single-zone reloads. ndc is better behaved; is verbose or quiet when appropriate. event and error reporting improvements. BIND 8.2 Highlights RFC 2308 (Negative Caching) RFC 2181 (DNS Clarifications) RFC 2065 (DNS Security) TSIG (Transaction SIGnatures) support for multiple virtual name servers NDC uses a "control channel" now (no more signals) "Split DNS" via zone type "forward". Many bug fixes Documentation improvements Performance enhancements BIND 8.1.2 Highlights Security fixes for a number of problems including: An attacker could overwrite the stack if inverse query support was enabled. A number of denial of service attacks where malformed packets could cause the server to crash. The server was willing to answer queries on its forwarding sockets. Several memory leaks have been plugged. The server no longer panics if a periodic interface scan fails due to no file descriptors being available. Updates to a number of ports. New ports for QNX, LynxOS, HP-UX 9.x, and HP MPE. "files unlimited" now works as expected on systems where setting an infinite rlim_max for RLIMIT_NOFILE works. Adding and deleting the same record in the same dynamic update no longer crashes the server. If a dynamic update fails, rollback is now done in LIFO order instead of FIFO order. Better behavior when priming of the root servers fails. purge_zone() didn't work correctly for the root zone, allowing old data to persist after loading the zone. Improved handling of oversized UDP packets. All hosts on the also-notify list are now notified. The meaning of the count returned by select() varies somewhat by operating system, and this could cause previous releases of the server to spin. Per-host statistics may be disabled by specifying 'host-statistics no' in named.conf. The maximum number of zones has been increased from 32768 to 65536. query-source may specify an address and port that the server is already listening on. BIND 8.1.1 required that either the address or port be wild. E.g., you can now say: listen-on port 53 { 10.0.0.1; }; query-source address 10.0.0.1 port 53; The value of FD_SETSIZE to use may be specified. Experimental -u (set user id), -g (set group id), and -t (chroot) command line options. See the INSTALL file for details. BIND 8 Features -> DNS Dynamic Updates (RFC 2136) -> DNS Change Notification (RFC 1996) -> Completely new configuration syntax -> Flexible, categorized logging system -> IP-address-based access control for queries, zone transfers, and updates that may be specified on a zone-by-zone basis -> More efficient zone transfers -> Improved performance for servers with thousands of zones -> The server no longer forks for outbound zone transfers -> Many bug fixes File and Directory Overview CHANGES history of added features and fixed bugs INSTALL how to build and install README this file TODO features planned but not yet written Version the version number of this release bin/* source for executables, including the nameserver include/* public .h files lib/* the resolver and various BIND support libraries port/* ports to various operating systems Kits, Questions, Comments, and Bug Reports current non-test release latest public test kit using BIND DNS operations in general DNS standards in general gw'd to u:c.p.d.bind gw'd to u:c.p.d.std code warriors only please the BIND home page bug reports To Support the Effort Note that BIND is supported by the Internet Software Consortium, and although it is free for use and redistribution and incorporation into vendor products and export and anything else you can think of, it costs money to produce. That money comes from ISPs, hardware and software vendors, companies who make extensive use of the software, and generally kind hearted folk such as yourself. The Internet Software Consortium has also commissioned a DHCP server implementation, has taken over official support/release of the INN - system, and supports the Kerberos Version 5 effort at MIT. You can - learn more about the ISC's goals and accomplishments from the web page - at . + system, and has supported the Kerberos Version 5 effort at MIT. You + can learn more about the ISC's goals and accomplishments from the web + page at . Index: head/contrib/bind/SUPPORT =================================================================== --- head/contrib/bind/SUPPORT (revision 60940) +++ head/contrib/bind/SUPPORT (revision 60941) @@ -1,10 +1,9 @@ -> The Internet Software Consortium now offers support agreements for ISC -> software. Under these programs, organizations using BIND, DHCP or INN -> from ISC can obtain a range of on call assistance, bug fixes, training and -> consultation services. These programs are documented on our web page at: -> -> http://www.isc.org/support.html -> -> Discussion about programs to meet your needs is welcome at -> clientservices@isc.org. Please forward your questions there or call us at -> +1 650 779-7018 to speak with the Director of Client Services. +The Internet Software Consortium offers, through certified providers, support, +training, and consulting for BIND and DHCP. These programs are documented on +our web page at: + + http://www.isc.org/support.html + +Discussion about programs to meet your needs is welcome at sales@isc.org. +Please forward your questions there or call us at +1 650 779-7018 to speak +with the Director of Client Services. Index: head/contrib/bind/Version =================================================================== --- head/contrib/bind/Version (revision 60940) +++ head/contrib/bind/Version (revision 60941) @@ -1 +1 @@ -8.2.2-P5-NOESW +8.2.3-T5B Index: head/contrib/bind/bin/dig/dig.c =================================================================== --- head/contrib/bind/bin/dig/dig.c (revision 60940) +++ head/contrib/bind/bin/dig/dig.c (revision 60941) @@ -1,1634 +1,1656 @@ #ifndef lint -static const char rcsid[] = "$Id: dig.c,v 8.36 1999/11/05 05:05:14 vixie Exp $"; +static const char rcsid[] = "$Id: dig.c,v 8.41 2000/04/20 07:36:04 vixie Exp $"; #endif /* * Copyright (c) 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /*********************** Notes for the BIND 4.9 release (Paul Vixie, DEC) * dig 2.0 was written by copying sections of libresolv.a and nslookup * and modifying them to be more useful for a general lookup utility. * as of BIND 4.9, the changes needed to support dig have mostly been * incorporated into libresolv.a and nslookup; dig now links against * some of nslookup's .o files rather than #including them or maintaining * local copies of them. * * while merging dig back into the BIND release, i made a number of * structural changes. for one thing, i put all of dig's private * library routines into this file rather than maintaining them in * separate, #included, files. i don't like to #include ".c" files. * i removed all calls to "bcopy", replacing them with structure * assignments. i removed all "extern"'s of standard functions, * replacing them with #include's of standard header files. this * version of dig is probably as portable as the rest of BIND. * * i had to remove the query-time and packet-count statistics since * the current libresolv.a is a lot harder to modify to maintain these * than the 4.8 one (used in the original dig) was. for consolation, * i added a "usage" message with extensive help text. * * to save my (limited, albeit) sanity, i ran "indent" over the source. * i also added the standard berkeley/DEC copyrights, since this file now * contains a fair amount of non-USC code. note that the berkeley and * DEC copyrights do not prohibit redistribution, with or without fee; * we add them only to protect ourselves (you have to claim copyright * in order to disclaim liability and warranty). * * Paul Vixie, Palo Alto, CA, April 1993 **************************************************************************** ****************************************************************** * DiG -- Domain Information Groper * * * * dig.c - Version 2.1 (7/12/94) ("BIND takeover") * * * * Developed by: Steve Hotz & Paul Mockapetris * * USC Information Sciences Institute (USC-ISI) * * Marina del Rey, California * * 1989 * * * * dig.c - * * Version 2.0 (9/1/90) * * o renamed difftime() difftv() to avoid * * clash with ANSI C * * o fixed incorrect # args to strcmp,gettimeofday * * o incorrect length specified to strncmp * * o fixed broken -sticky -envsa -envset functions * * o print options/flags redefined & modified * * * * Version 2.0.beta (5/9/90) * * o output format - helpful to `doc` * * o minor cleanup * * o release to beta testers * * * * Version 1.1.beta (10/26/89) * * o hanging zone transer (when REFUSED) fixed * * o trailing dot added to domain names in RDATA * * o ISI internal * * * * Version 1.0.tmp (8/27/89) * * o Error in prnttime() fixed * * o no longer dumps core on large pkts * * o zone transfer (axfr) added * * o -x added for inverse queries * * (i.e. "dig -x 128.9.0.32") * * o give address of default server * * o accept broadcast to server @255.255.255.255 * * * * Version 1.0 (3/27/89) * * o original release * * * * DiG is Public Domain, and may be used for any purpose as * * long as this notice is not removed. * ******************************************************************/ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "../nslookup/res.h" /* Global. */ -#define VERSION 82 -#define VSTRING "8.2" +#define VERSION 83 +#define VSTRING "8.3" #define PRF_DEF 0x2ff9 #define PRF_MIN 0xA930 #define PRF_ZONE 0x24f9 #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif #define SAVEENV "DiG.env" #define DIG_MAXARGS 30 static int eecode = 0; static FILE * qfp; static int sockFD; static char *defsrv, *srvmsg; static char defbuf[40] = "default -- "; static char srvbuf[60]; static char myhostname[MAXHOSTNAMELEN]; static struct sockaddr_in myaddress; static u_int32_t ixfr_serial; /* stuff for nslookup modules */ struct __res_state res; FILE *filePtr; jmp_buf env; HostInfo *defaultPtr = NULL; HostInfo curHostInfo, defaultRec; int curHostValid = FALSE; int queryType, queryClass; extern int StringToClass(), StringToType(); /* subr.c */ #if defined(BSD) && BSD >= 199006 && !defined(RISCOS_BSD) FILE *yyin = NULL; void yyrestart(FILE *f) { } #endif char *pager = NULL; /* end of nslookup stuff */ /* Forward. */ static void Usage(void); static int SetOption(const char *); static void res_re_init(void); static int xstrtonum(char *); static int printZone(ns_type, const char *, const struct sockaddr_in *, ns_tsig_key *); static int print_axfr(FILE *output, const u_char *msg, size_t msglen); static struct timeval difftv(struct timeval, struct timeval); static void prnttime(struct timeval); static void stackarg(char *, char **); /* Public. */ int main(int argc, char **argv) { struct hostent *hp; short port = htons(NAMESERVER_PORT); /* Wierd stuff for SPARC alignment, hurts nothing else. */ union { HEADER header_; u_char packet_[PACKETSZ]; } packet_; #define header (packet_.header_) #define packet (packet_.packet_) u_char answer[64*1024]; int n; char doping[90]; char pingstr[50]; char *afile; char *addrc, *addrend, *addrbegin; time_t exectime; struct timeval tv1, tv2, start_time, end_time, query_time; char *srv; int anyflag = 0; int sticky = 0; int tmp; int qtypeSet; int addrflag = 0; ns_type xfr = ns_t_invalid; int bytes_out, bytes_in; - char cmd[256]; + char cmd[512]; char domain[MAXDNAME]; char msg[120], *msgptr; char **vtmp; char *args[DIG_MAXARGS]; char **ax; int once = 1, dofile = 0; /* batch -vs- interactive control */ - char fileq[100]; + char fileq[384]; int fp; int wait=0, delay; int envset=0, envsave=0; struct __res_state res_x, res_t; char *pp; ns_tsig_key key; char *keyfile = NULL, *keyname = NULL; res_ninit(&res); res.pfcode = PRF_DEF; qtypeSet = 0; memset(domain, 0, sizeof domain); gethostname(myhostname, (sizeof myhostname)); #ifdef HAVE_SA_LEN myaddress.sin_len = sizeof(struct sockaddr_in); #endif myaddress.sin_family = AF_INET; myaddress.sin_addr.s_addr = INADDR_ANY; myaddress.sin_port = 0; /*INPORT_ANY*/; defsrv = strcat(defbuf, inet_ntoa(res.nsaddr.sin_addr)); res_x = res; /* * If LOCALDEF in environment, should point to file * containing local favourite defaults. Also look for file * DiG.env (i.e. SAVEENV) in local directory. */ if ((((afile = (char *) getenv("LOCALDEF")) != (char *) NULL) && ((fp = open(afile, O_RDONLY)) > 0)) || ((fp = open(SAVEENV, O_RDONLY)) > 0)) { read(fp, (char *)&res_x, (sizeof res_x)); close(fp); res = res_x; } /* * Check for batch-mode DiG; also pre-scan for 'help'. */ vtmp = argv; ax = args; while (*vtmp != NULL) { if (strcmp(*vtmp, "-h") == 0 || strcmp(*vtmp, "-help") == 0 || strcmp(*vtmp, "-usage") == 0 || strcmp(*vtmp, "help") == 0) { Usage(); exit(0); } if (strcmp(*vtmp, "-f") == 0) { dofile++; once=0; if ((qfp = fopen(*++vtmp, "r")) == NULL) { fflush(stdout); perror("file open"); fflush(stderr); exit(10); } } else { if (ax - args == DIG_MAXARGS) { fprintf(stderr, "dig: too many arguments\n"); exit(10); } *ax++ = *vtmp; } vtmp++; } res.id = 1; gettimeofday(&tv1, NULL); /* * Main section: once if cmd-line query * while !EOF if batch mode */ *fileq = '\0'; while ((dofile && fgets(fileq, sizeof fileq, qfp) != NULL) || (!dofile && once--)) { - if (*fileq == '\n' || *fileq == '#' || *fileq==';') - continue; /* ignore blank lines & comments */ + if (*fileq == '\n' || *fileq == '#' || *fileq==';') { + printf("%s", fileq); /* echo but otherwise ignore */ + continue; /* blank lines and comments */ + } /* * "Sticky" requests that before current parsing args * return to current "working" environment (X******). */ if (sticky) { printf(";; (using sticky settings)\n"); res = res_x; } /* * Concat cmd-line and file args. */ stackarg(fileq, ax); /* defaults */ queryType = ns_t_ns; queryClass = ns_c_in; xfr = ns_t_invalid; *pingstr = 0; srv = NULL; sprintf(cmd, "\n; <<>> DiG %s <<>> ", VSTRING); argv = args; argc = ax - args; /* * More cmd-line options than anyone should ever have to * deal with .... */ while (*(++argv) != NULL && **argv != '\0') { strcat(cmd, *argv); strcat(cmd, " "); if (**argv == '@') { srv = (*argv+1); continue; } if (**argv == '%') continue; if (**argv == '+') { SetOption(*argv+1); continue; } if (**argv == '=') { ixfr_serial = strtoul(*argv+1, NULL, 0); continue; } if (strncmp(*argv, "-nost", 5) == 0) { sticky = 0; continue; } else if (strncmp(*argv, "-st", 3) == 0) { sticky++; continue; } else if (strncmp(*argv, "-envsa", 6) == 0) { envsave++; continue; } else if (strncmp(*argv, "-envse", 6) == 0) { envset++; continue; } if (**argv == '-') { switch (argv[0][1]) { case 'T': wait = atoi(*++argv); break; case 'c': if ((tmp = atoi(*++argv)) || *argv[0]=='0') { queryClass = tmp; } else if ((tmp = StringToClass(*argv, 0, NULL) ) != 0) { queryClass = tmp; } else { printf( "; invalid class specified\n" ); } break; case 't': if ((tmp = atoi(*++argv)) || *argv[0]=='0') { queryType = tmp; qtypeSet++; } else if ((tmp = StringToType(*argv, 0, NULL) ) != 0) { queryType = tmp; qtypeSet++; } else { printf( "; invalid type specified\n" ); } break; case 'x': if (!qtypeSet) { queryType = T_ANY; qtypeSet++; } if (!(addrc = *++argv)) { printf( "; no arg for -x?\n" ); break; } addrend = addrc + strlen(addrc); if (*addrend == '.') *addrend = '\0'; *domain = '\0'; while ((addrbegin = strrchr(addrc,'.'))) { strcat(domain, addrbegin+1); strcat(domain, "."); *addrbegin = '\0'; } strcat(domain, addrc); strcat(domain, ".in-addr.arpa."); break; case 'p': if (argv[0][2] != '\0') port = ntohs(atoi(argv[0]+2)); else port = htons(atoi(*++argv)); break; case 'P': if (argv[0][2] != '\0') strcpy(pingstr, argv[0]+2); else strcpy(pingstr, "ping -s"); break; case 'n': if (argv[0][2] != '\0') res.ndots = atoi(argv[0]+2); else res.ndots = atoi(*++argv); break; case 'b': { char *a, *p; if (argv[0][2] != '\0') a = argv[0]+2; else a = *++argv; if ((p = strchr(a, ':')) != NULL) { *p++ = '\0'; myaddress.sin_port = ntohs(atoi(p)); } if (!inet_aton(a,&myaddress.sin_addr)){ fprintf(stderr, ";; bad -b addr\n"); exit(1); } - } + } + break; case 'k': /* -k keydir:keyname */ if (argv[0][2] != '\0') keyfile = argv[0]+2; else keyfile = *++argv; keyname = strchr(keyfile, ':'); if (keyname == NULL) { fprintf(stderr, "key option argument should be keydir:keyname\n"); exit(1); } *keyname++='\0'; break; } /* switch - */ continue; } /* if '-' */ if ((tmp = StringToType(*argv, -1, NULL)) != -1) { if ((T_ANY == tmp) && anyflag++) { queryClass = C_ANY; continue; } if (ns_t_xfr_p(tmp) && (tmp == ns_t_axfr || (res.options & RES_USEVC) != 0) ) { res.pfcode = PRF_ZONE; xfr = (ns_type)tmp; } else { queryType = tmp; qtypeSet++; } } else if ((tmp = StringToClass(*argv, -1, NULL)) != -1) { queryClass = tmp; } else { memset(domain, 0, sizeof domain); sprintf(domain,"%s",*argv); } } /* while argv remains */ /* process key options */ if (keyfile) { #ifdef PARSE_KEYFILE int i, n1; char buf[BUFSIZ], *p; FILE *fp = NULL; int file_major, file_minor, alg; fp = fopen(keyfile, "r"); if (fp == NULL) { perror(keyfile); exit(1); } /* Now read the header info from the file. */ i = fread(buf, 1, BUFSIZ, fp); if (i < 5) { fclose(fp); exit(1); } fclose(fp); p = buf; n=strlen(p); /* get length of strings */ n1=strlen("Private-key-format: v"); if (n1 > n || strncmp(buf, "Private-key-format: v", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ sscanf((char *)p, "%d.%d", &file_major, &file_minor); /* should do some error checking with these someday */ while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Algorithm: "); if (n1 > n || strncmp(p, "Algorithm: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ if (sscanf((char *)p, "%d", &alg)!=1) { fprintf(stderr, "Invalid key file format\n"); exit(1); } while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Key: "); if (n1 > n || strncmp(p, "Key: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ pp=p; while (*pp++!='\n'); /* skip to end of line, * terminate it */ *--pp='\0'; key.data=malloc(1024*sizeof(char)); key.len=b64_pton(p, key.data, 1024); strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); #else /* use the dst* routines to parse the key files * * This requires that both the .key and the .private * files exist in your cwd, so the keyfile parmeter * here is assumed to be a path in which the * K*.{key,private} files exist. */ DST_KEY *dst_key; char cwd[PATH_MAX+1]; if (getcwd(cwd, PATH_MAX)==NULL) { perror("unable to get current directory"); exit(1); } if (chdir(keyfile)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", keyfile, strerror(errno)); exit(1); } dst_init(); dst_key = dst_read_key(keyname, 0 /* not used for priv keys */, KEY_HMAC_MD5, DST_PRIVATE); if (!dst_key) { fprintf(stderr, "dst_read_key: error reading key\n"); exit(1); } key.data=malloc(1024*sizeof(char)); dst_key_to_buffer(dst_key, key.data, 1024); key.len=dst_key->dk_key_size; strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); if (chdir(cwd)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", cwd, strerror(errno)); exit(1); } #endif } if (res.pfcode & 0x80000) printf("; pfcode: %08lx, options: %08lx\n", res.pfcode, res.options); /* * Current env. (after this parse) is to become the * new "working" environmnet. Used in conj. with sticky. */ if (envset) { res_x = res; envset = 0; } /* * Current env. (after this parse) is to become the * new default saved environmnet. Save in user specified * file if exists else is SAVEENV (== "DiG.env"). */ if (envsave) { afile = (char *) getenv("LOCALDEF"); if ((afile && ((fp = open(afile, O_WRONLY|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE)) > 0)) || ((fp = open(SAVEENV, O_WRONLY|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE)) > 0)) { write(fp, (char *)&res, (sizeof res)); close(fp); } envsave = 0; } if (res.pfcode & RES_PRF_CMD) printf("%s\n", cmd); addrflag = anyflag = 0; /* * Find address of server to query. If not dot-notation, then * try to resolve domain-name (if so, save and turn off print * options, this domain-query is not the one we want. Restore * user options when done. * Things get a bit wierd since we need to use resolver to be * able to "put the resolver to work". */ srvbuf[0] = 0; srvmsg = defsrv; if (srv != NULL) { struct in_addr addr; if (inet_aton(srv, &addr)) { res.nscount = 1; res.nsaddr.sin_addr = addr; srvmsg = strcat(srvbuf, srv); } else { res_t = res; res_ninit(&res); res.pfcode = 0; res.options = RES_DEFAULT; hp = gethostbyname(srv); res = res_t; if (hp == NULL || hp->h_addr_list == NULL || *hp->h_addr_list == NULL) { fflush(stdout); fprintf(stderr, "; Bad server: %s -- using default server and timer opts\n", srv); fflush(stderr); srvmsg = defsrv; srv = NULL; } else { u_int32_t **addr; res.nscount = 0; for (addr = (u_int32_t**)hp->h_addr_list; *addr && (res.nscount < MAXNS); addr++) { res.nsaddr_list[ res.nscount++ ].sin_addr.s_addr = **addr; } srvmsg = strcat(srvbuf,srv); strcat(srvbuf, " "); strcat(srvmsg, inet_ntoa(res.nsaddr.sin_addr)); } } printf("; (%d server%s found)\n", res.nscount, (res.nscount==1)?"":"s"); res.id += res.retry; } { int i; for (i = 0; i < res.nscount; i++) { res.nsaddr_list[i].sin_family = AF_INET; res.nsaddr_list[i].sin_port = port; } res.id += res.retry; } if (ns_t_xfr_p(xfr)) { int i; for (i = 0; i < res.nscount; i++) { int x; if (keyfile) x = printZone(xfr, domain, &res.nsaddr_list[i], &key); else x = printZone(xfr, domain, &res.nsaddr_list[i], NULL); if (res.pfcode & RES_PRF_STATS) { exectime = time(NULL); printf(";; FROM: %s to SERVER: %s\n", myhostname, inet_ntoa(res.nsaddr_list[i] .sin_addr)); printf(";; WHEN: %s", ctime(&exectime)); } if (!x) break; /* success */ } fflush(stdout); continue; } if (*domain && !qtypeSet) { queryType = T_A; qtypeSet++; } bytes_out = n = res_nmkquery(&res, QUERY, domain, queryClass, queryType, NULL, 0, NULL, packet, sizeof packet); if (n < 0) { fflush(stderr); printf(";; res_nmkquery: buffer too small\n\n"); continue; } if (queryType == T_IXFR) { HEADER *hp = (HEADER *) packet; u_char *cpp = packet + bytes_out; hp->nscount = htons(1+ntohs(hp->nscount)); n = dn_comp(domain, cpp, (sizeof packet) - (cpp - packet), NULL, NULL); cpp += n; PUTSHORT(T_SOA, cpp); /* type */ PUTSHORT(C_IN, cpp); /* class */ PUTLONG(0, cpp); /* ttl */ PUTSHORT(22, cpp); /* dlen */ *cpp++ = 0; /* mname */ *cpp++ = 0; /* rname */ PUTLONG(ixfr_serial, cpp); PUTLONG(0xDEAD, cpp); /* Refresh */ PUTLONG(0xBEEF, cpp); /* Retry */ PUTLONG(0xABCD, cpp); /* Expire */ PUTLONG(0x1776, cpp); /* Min TTL */ bytes_out = n = cpp - packet; }; eecode = 0; if (res.pfcode & RES_PRF_HEAD1) fp_resstat(&res, stdout); (void) gettimeofday(&start_time, NULL); if (keyfile) n = res_nsendsigned(&res, packet, n, &key, answer, sizeof answer); else n = res_nsend(&res, packet, n, answer, sizeof answer); if ((bytes_in = n) < 0) { fflush(stdout); n = 0 - n; msg[0]=0; if (keyfile) strcat(msg,";; res_nsendsigned to server "); else strcat(msg,";; res_nsend to server "); strcat(msg,srvmsg); perror(msg); fflush(stderr); if (!dofile) { if (eecode) exit(eecode); else exit(9); } } (void) gettimeofday(&end_time, NULL); if (res.pfcode & RES_PRF_STATS) { time_t t; query_time = difftv(start_time, end_time); printf(";; Total query time: "); prnttime(query_time); putchar('\n'); exectime = time(NULL); printf(";; FROM: %s to SERVER: %s\n", myhostname, srvmsg); printf(";; WHEN: %s", ctime(&exectime)); printf(";; MSG SIZE sent: %d rcvd: %d\n", bytes_out, bytes_in); } fflush(stdout); /* * Argh ... not particularly elegant. Should put in *real* ping code. * Would necessitate root priviledges for icmp port though! */ if (*pingstr) { sprintf(doping,"%s %s 56 3 | tail -3",pingstr, (srv==NULL)?(defsrv+10):srv); system(doping); } putchar('\n'); /* * Fairly crude method and low overhead method of keeping two * batches started at different sites somewhat synchronized. */ gettimeofday(&tv2, NULL); delay = (int)(tv2.tv_sec - tv1.tv_sec); if (delay < wait) { sleep(wait - delay); } } return (eecode); } /* Private. */ static void Usage() { fputs("\ usage: dig [@server] [domain] [q-type] [q-class] {q-opt} {d-opt} [%comment]\n\ where: server,\n\ domain are names in the Domain Name System\n\ q-class is one of (in,any,...) [default: in]\n\ q-type is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default: a]\n\ ", stderr); fputs("\ q-opt is one of:\n\ -x dot-notation-address (shortcut to in-addr.arpa lookups)\n\ -f file (batch mode input file name)\n\ -T time (batch mode time delay, per query)\n\ -p port (nameserver is on this port) [53]\n\ -b addr[:port] (bind to this tcp address) [*]\n\ -P[ping-string] (see man page)\n\ -t query-type (synonym for q-type)\n\ -c query-class (synonym for q-class)\n\ -k keydir:keyname (sign the query with this TSIG key)\n\ -envsav,-envset (see man page)\n\ -[no]stick (see man page)\n\ ", stderr); fputs("\ d-opt is of the form ``+keyword=value'' where keyword is one of:\n\ [no]debug [no]d2 [no]recurse retry=# time=# [no]ko [no]vc\n\ [no]defname [no]search domain=NAME [no]ignore [no]primary\n\ [no]aaonly [no]cmd [no]stats [no]Header [no]header\n\ [no]ttlid [no]cl [no]qr [no]reply [no]ques [no]answer\n\ [no]author [no]addit pfdef pfmin pfset=# pfand=# pfor=#\n\ ", stderr); fputs("\ notes: defname and search don't work; use fully-qualified names.\n\ this is DiG version " VSTRING "\n\ - $Id: dig.c,v 8.36 1999/11/05 05:05:14 vixie Exp $\n\ + $Id: dig.c,v 8.41 2000/04/20 07:36:04 vixie Exp $\n\ ", stderr); } static int SetOption(const char *string) { char option[NAME_LEN], type[NAME_LEN], *ptr; int i; i = pickString(string, option, sizeof option); if (i == 0) { fprintf(stderr, ";*** Invalid option: %s\n", string); /* this is ugly, but fixing the caller to behave properly with an error return value would require a major cleanup. */ exit(9); } if (strncmp(option, "aa", 2) == 0) { /* aaonly */ res.options |= RES_AAONLY; } else if (strncmp(option, "noaa", 4) == 0) { res.options &= ~RES_AAONLY; } else if (strncmp(option, "deb", 3) == 0) { /* debug */ res.options |= RES_DEBUG; } else if (strncmp(option, "nodeb", 5) == 0) { res.options &= ~(RES_DEBUG | RES_DEBUG2); } else if (strncmp(option, "ko", 2) == 0) { /* keepopen */ res.options |= (RES_STAYOPEN | RES_USEVC); } else if (strncmp(option, "noko", 4) == 0) { res.options &= ~RES_STAYOPEN; } else if (strncmp(option, "d2", 2) == 0) { /* d2 (more debug) */ res.options |= (RES_DEBUG | RES_DEBUG2); } else if (strncmp(option, "nod2", 4) == 0) { res.options &= ~RES_DEBUG2; } else if (strncmp(option, "def", 3) == 0) { /* defname */ res.options |= RES_DEFNAMES; } else if (strncmp(option, "nodef", 5) == 0) { res.options &= ~RES_DEFNAMES; } else if (strncmp(option, "sea", 3) == 0) { /* search list */ res.options |= RES_DNSRCH; } else if (strncmp(option, "nosea", 5) == 0) { res.options &= ~RES_DNSRCH; } else if (strncmp(option, "do", 2) == 0) { /* domain */ ptr = strchr(option, '='); if (ptr != NULL) { i = pickString(++ptr, res.defdname, sizeof res.defdname); if (i == 0) { /* value's too long or non-existant. This actually shouldn't happen due to pickString() above */ fprintf(stderr, "*** Invalid domain: %s\n", ptr) ; exit(9); /* see comment at previous call to exit()*/ } } } else if (strncmp(option, "ti", 2) == 0) { /* timeout */ ptr = strchr(option, '='); if (ptr != NULL) sscanf(++ptr, "%d", &res.retrans); } else if (strncmp(option, "ret", 3) == 0) { /* retry */ ptr = strchr(option, '='); if (ptr != NULL) sscanf(++ptr, "%d", &res.retry); } else if (strncmp(option, "i", 1) == 0) { /* ignore */ res.options |= RES_IGNTC; } else if (strncmp(option, "noi", 3) == 0) { res.options &= ~RES_IGNTC; } else if (strncmp(option, "pr", 2) == 0) { /* primary */ res.options |= RES_PRIMARY; } else if (strncmp(option, "nop", 3) == 0) { res.options &= ~RES_PRIMARY; } else if (strncmp(option, "rec", 3) == 0) { /* recurse */ res.options |= RES_RECURSE; } else if (strncmp(option, "norec", 5) == 0) { res.options &= ~RES_RECURSE; } else if (strncmp(option, "v", 1) == 0) { /* vc */ res.options |= RES_USEVC; } else if (strncmp(option, "nov", 3) == 0) { res.options &= ~RES_USEVC; } else if (strncmp(option, "pfset", 5) == 0) { ptr = strchr(option, '='); if (ptr != NULL) res.pfcode = xstrtonum(++ptr); } else if (strncmp(option, "pfand", 5) == 0) { ptr = strchr(option, '='); if (ptr != NULL) res.pfcode = res.pfcode & xstrtonum(++ptr); } else if (strncmp(option, "pfor", 4) == 0) { ptr = strchr(option, '='); if (ptr != NULL) res.pfcode |= xstrtonum(++ptr); } else if (strncmp(option, "pfmin", 5) == 0) { res.pfcode = PRF_MIN; } else if (strncmp(option, "pfdef", 5) == 0) { res.pfcode = PRF_DEF; } else if (strncmp(option, "an", 2) == 0) { /* answer section */ res.pfcode |= RES_PRF_ANS; } else if (strncmp(option, "noan", 4) == 0) { res.pfcode &= ~RES_PRF_ANS; } else if (strncmp(option, "qu", 2) == 0) { /* question section */ res.pfcode |= RES_PRF_QUES; } else if (strncmp(option, "noqu", 4) == 0) { res.pfcode &= ~RES_PRF_QUES; } else if (strncmp(option, "au", 2) == 0) { /* authority section */ res.pfcode |= RES_PRF_AUTH; } else if (strncmp(option, "noau", 4) == 0) { res.pfcode &= ~RES_PRF_AUTH; } else if (strncmp(option, "ad", 2) == 0) { /* addition section */ res.pfcode |= RES_PRF_ADD; } else if (strncmp(option, "noad", 4) == 0) { res.pfcode &= ~RES_PRF_ADD; } else if (strncmp(option, "tt", 2) == 0) { /* TTL & ID */ res.pfcode |= RES_PRF_TTLID; } else if (strncmp(option, "nott", 4) == 0) { res.pfcode &= ~RES_PRF_TTLID; } else if (strncmp(option, "he", 2) == 0) { /* head flags stats */ res.pfcode |= RES_PRF_HEAD2; } else if (strncmp(option, "nohe", 4) == 0) { res.pfcode &= ~RES_PRF_HEAD2; } else if (strncmp(option, "H", 1) == 0) { /* header all */ res.pfcode |= RES_PRF_HEADX; } else if (strncmp(option, "noH", 3) == 0) { res.pfcode &= ~(RES_PRF_HEADX); } else if (strncmp(option, "qr", 2) == 0) { /* query */ res.pfcode |= RES_PRF_QUERY; } else if (strncmp(option, "noqr", 4) == 0) { res.pfcode &= ~RES_PRF_QUERY; } else if (strncmp(option, "rep", 3) == 0) { /* reply */ res.pfcode |= RES_PRF_REPLY; } else if (strncmp(option, "norep", 5) == 0) { res.pfcode &= ~RES_PRF_REPLY; } else if (strncmp(option, "cm", 2) == 0) { /* command line */ res.pfcode |= RES_PRF_CMD; } else if (strncmp(option, "nocm", 4) == 0) { res.pfcode &= ~RES_PRF_CMD; } else if (strncmp(option, "cl", 2) == 0) { /* class mnemonic */ res.pfcode |= RES_PRF_CLASS; } else if (strncmp(option, "nocl", 4) == 0) { res.pfcode &= ~RES_PRF_CLASS; } else if (strncmp(option, "st", 2) == 0) { /* stats*/ res.pfcode |= RES_PRF_STATS; } else if (strncmp(option, "nost", 4) == 0) { res.pfcode &= ~RES_PRF_STATS; } else { fprintf(stderr, "; *** Invalid option: %s\n", option); return (ERROR); } res_re_init(); return (SUCCESS); } /* * Force a reinitialization when the domain is changed. */ static void res_re_init() { static char localdomain[] = "LOCALDOMAIN"; u_long pfcode = res.pfcode, options = res.options; unsigned ndots = res.ndots; + int retrans = res.retrans, retry = res.retry; char *buf; /* * This is ugly but putenv() is more portable than setenv(). */ buf = malloc((sizeof localdomain) + strlen(res.defdname) +10/*fuzz*/); sprintf(buf, "%s=%s", localdomain, res.defdname); putenv(buf); /* keeps the argument, so we won't free it */ res_ninit(&res); res.pfcode = pfcode; res.options = options; res.ndots = ndots; + res.retrans = retrans; + res.retry = retry; } /* * convert char string (decimal, octal, or hex) to integer */ static int xstrtonum(char *p) { int v = 0; int i; int b = 10; int flag = 0; while (*p != 0) { if (!flag++) if (*p == '0') { b = 8; p++; continue; } if (isupper(*p)) *p = tolower(*p); if (*p == 'x') { b = 16; p++; continue; } if (isdigit(*p)) { i = *p - '0'; } else if (isxdigit(*p)) { i = *p - 'a' + 10; } else { fprintf(stderr, "; *** Bad char in numeric string..ignored\n"); i = -1; } if (i >= b) { fprintf(stderr, "; *** Bad char in numeric string..ignored\n"); i = -1; } if (i >= 0) v = v * b + i; p++; } return (v); } typedef union { HEADER qb1; u_char qb2[PACKETSZ]; } querybuf; static int printZone(ns_type xfr, const char *zone, const struct sockaddr_in *sin, ns_tsig_key *key) { static u_char *answer = NULL; static int answerLen = 0; querybuf buf; HEADER *headerPtr; int msglen, amtToRead, numRead, result = 0, sockFD, len; int count, type, class, rlen, done, n; int numAnswers = 0, numRecords = 0, soacnt = 0; u_char *cp, tmp[NS_INT16SZ]; char dname[2][NS_MAXDNAME], file[NAME_LEN]; enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING } error = NO_ERRORS; pid_t zpid; u_char *newmsg; int newmsglen; ns_tcp_tsig_state tsig_state; - int tsig_ret; + int tsig_ret, tsig_required, tsig_present; switch (xfr) { case ns_t_axfr: case ns_t_zxfr: break; default: fprintf(stderr, ";; %s - transfer type not supported\n", p_type(xfr)); return (ERROR); } /* * Create a query packet for the requested zone name. */ msglen = res_nmkquery(&res, ns_o_query, zone, queryClass, ns_t_axfr, NULL, 0, 0, buf.qb2, sizeof buf); if (msglen < 0) { if (res.options & RES_DEBUG) fprintf(stderr, ";; res_nmkquery failed\n"); return (ERROR); } /* * Sign the message if a key was sent */ if (key == NULL) { newmsg = (u_char *)&buf; newmsglen = msglen; } else { DST_KEY *dstkey; int bufsize, siglen; u_char sig[64]; int ret; /* ns_sign() also calls dst_init(), but there is no harm * doing it twice */ dst_init(); bufsize = msglen + 1024; newmsg = (u_char *) malloc(bufsize); if (newmsg == NULL) { errno = ENOMEM; return (-1); } memcpy(newmsg, (u_char *)&buf, msglen); newmsglen = msglen; if (strcmp(key->alg, NS_TSIG_ALG_HMAC_MD5) != 0) dstkey = NULL; else dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5, NS_KEY_TYPE_AUTH_ONLY, NS_KEY_PROT_ANY, key->data, key->len); if (dstkey == NULL) { errno = EINVAL; if (key) free(newmsg); return (-1); } siglen = sizeof(sig); /* newmsglen++; */ ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0, sig, &siglen, 0); if (ret < 0) { if (key) free (newmsg); if (ret == NS_TSIG_ERROR_NO_SPACE) errno = EMSGSIZE; else if (ret == -1) errno = EINVAL; return (ret); } ns_verify_tcp_init(dstkey, sig, siglen, &tsig_state); } /* * Set up a virtual circuit to the server. */ if ((sockFD = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { int e = errno; perror(";; socket"); return (e); } if (bind(sockFD, (struct sockaddr *)&myaddress, sizeof myaddress) < 0){ int e = errno; fprintf(stderr, ";; bind(%s:%u): %s\n", inet_ntoa(myaddress.sin_addr), ntohs(myaddress.sin_port), strerror(e)); (void) close(sockFD); sockFD = -1; return (e); } if (connect(sockFD, (struct sockaddr *)sin, sizeof *sin) < 0) { int e = errno; perror(";; connect"); (void) close(sockFD); sockFD = -1; return (e); } /* * Send length & message for zone transfer */ ns_put16(newmsglen, tmp); if (write(sockFD, (char *)tmp, NS_INT16SZ) != NS_INT16SZ || write(sockFD, (char *)newmsg, newmsglen) != newmsglen) { int e = errno; if (key) free (newmsg); perror(";; write"); (void) close(sockFD); sockFD = -1; return (e); } /* * If we're compressing, push a gzip into the pipeline. */ if (xfr == ns_t_zxfr) { enum { rd = 0, wr = 1 }; int z[2]; if (pipe(z) < 0) { int e = errno; if (key) free (newmsg); perror(";; pipe"); (void) close(sockFD); sockFD = -1; return (e); } zpid = vfork(); if (zpid < 0) { int e = errno; if (key) free (newmsg); perror(";; fork"); (void) close(sockFD); sockFD = -1; return (e); } else if (zpid == 0) { /* Child. */ (void) close(z[rd]); (void) dup2(sockFD, STDIN_FILENO); (void) close(sockFD); (void) dup2(z[wr], STDOUT_FILENO); (void) close(z[wr]); execlp("gzip", "gzip", "-d", "-v", NULL); perror(";; child: execlp(gunzip)"); _exit(1); } /* Parent. */ (void) close(z[wr]); (void) dup2(z[rd], sockFD); (void) close(z[rd]); } dname[0][0] = '\0'; for (done = 0; !done; (void)NULL) { /* * Read the length of the response. */ cp = tmp; amtToRead = INT16SZ; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_LEN; break; } len = ns_get16(tmp); if (len == 0) break; /* nothing left to read */ /* * The server sent too much data to fit the existing buffer -- * allocate a new one. */ if (len > answerLen) { if (answerLen != 0) free(answer); answerLen = len; answer = (u_char *)Malloc(answerLen); } /* * Read the response. */ amtToRead = len; cp = answer; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_MSG; break; } - /* - * Verify the TSIG - */ - - if (key) { - tsig_ret = ns_verify_tcp(answer, &len, &tsig_state, 1); - if (tsig_ret == 0) - printf("; TSIG ok\n"); - else - printf("; TSIG invalid\n"); - } - result = print_axfr(stdout, answer, len); if (result != 0) { error = ERR_PRINTING; break; } numRecords += htons(((HEADER *)answer)->ancount); numAnswers++; /* Header. */ cp = answer + HFIXEDSZ; /* Question. */ for (count = ntohs(((HEADER *)answer)->qdcount); count > 0; count--) { n = dn_skipname(cp, answer + len); if (n < 0) { error = ERR_PRINTING; done++; break; } cp += n + QFIXEDSZ; if (cp > answer + len) { error = ERR_PRINTING; done++; break; } } /* Answer. */ for (count = ntohs(((HEADER *)answer)->ancount); count > 0 && !done; count--) { n = dn_expand(answer, answer + len, cp, dname[soacnt], sizeof dname[0]); if (n < 0) { error = ERR_PRINTING; done++; break; } cp += n; if (cp + 3 * INT16SZ + INT32SZ > answer + len) { error = ERR_PRINTING; done++; break; } GETSHORT(type, cp); GETSHORT(class, cp); cp += INT32SZ; /* ttl */ GETSHORT(rlen, cp); cp += rlen; if (cp > answer + len) { error = ERR_PRINTING; done++; break; } if (type == T_SOA && soacnt++ && ns_samename(dname[0], dname[1]) == 1) { done++; break; } } + + /* + * Verify the TSIG + */ + + if (key) { + if (ns_find_tsig(answer, answer + len) != NULL) + tsig_present = 1; + else + tsig_present = 0; + if (numAnswers == 1 || soacnt > 1) + tsig_required = 1; + else + tsig_required = 0; + tsig_ret = ns_verify_tcp(answer, &len, &tsig_state, + tsig_required); + if (tsig_ret == 0) { + if (tsig_present) + printf("; TSIG ok\n"); + } + else + printf("; TSIG invalid\n"); + } + } printf(";; Received %d answer%s (%d record%s).\n", numAnswers, (numAnswers != 1) ? "s" : "", numRecords, (numRecords != 1) ? "s" : ""); (void) close(sockFD); sockFD = -1; /* * If we were uncompressing, reap the uncompressor. */ if (xfr == ns_t_zxfr) { pid_t pid; int status; pid = wait(&status); if (pid < 0) { int e = errno; perror(";; wait"); return (e); } if (pid != zpid) { fprintf(stderr, ";; wrong pid (%lu != %lu)\n", (u_long)pid, (u_long)zpid); return (ERROR); } printf(";; pid %lu: exit %d, signal %d, core %c\n", pid, WEXITSTATUS(status), WIFSIGNALED(status) ? WTERMSIG(status) : 0, WCOREDUMP(status) ? 't' : 'f'); } /* XXX This should probably happen sooner than here */ if (key) free (newmsg); switch (error) { case NO_ERRORS: return (0); case ERR_READING_LEN: return (EMSGSIZE); case ERR_PRINTING: return (result); case ERR_READING_MSG: return (EMSGSIZE); default: return (EFAULT); } } static int print_axfr(FILE *file, const u_char *msg, size_t msglen) { ns_msg handle; if (ns_initparse(msg, msglen, &handle) < 0) { fprintf(file, ";; ns_initparse: %s\n", strerror(errno)); return (ns_r_formerr); } if (ns_msg_getflag(handle, ns_f_rcode) != ns_r_noerror) return (ns_msg_getflag(handle, ns_f_rcode)); /* * We are looking for info from answer resource records. * If there aren't any, return with an error. We assume * there aren't any question records. */ if (ns_msg_count(handle, ns_s_an) == 0) return (NO_INFO); #ifdef PROTOCOLDEBUG printf(";;; (message of %d octets has %d answers)\n", msglen, ns_msg_count(handle, ns_s_an)); #endif for (;;) { static char origin[NS_MAXDNAME], name_ctx[NS_MAXDNAME]; const char *name; char buf[2048]; /* XXX need to malloc/realloc. */ ns_rr rr; if (ns_parserr(&handle, ns_s_an, -1, &rr)) { if (errno != ENODEV) { fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); return (FORMERR); } break; } name = ns_rr_name(rr); if (origin[0] == '\0' && name[0] != '\0') { - fprintf(file, "$ORIGIN %s.\n", name); - strcpy(origin, name); + if (strcmp(name, ".") != 0) + strcpy(origin, name); + fprintf(file, "$ORIGIN %s.\n", origin); + if (strcmp(name, ".") == 0) + strcpy(origin, name); + strcpy(name_ctx, "@"); } if (ns_sprintrr(&handle, &rr, name_ctx, origin, buf, sizeof buf) < 0) { fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); return (FORMERR); } strcpy(name_ctx, name); fputs(buf, file); fputc('\n', file); } return (SUCCESS); } static struct timeval difftv(struct timeval a, struct timeval b) { static struct timeval diff; diff.tv_sec = b.tv_sec - a.tv_sec; if ((diff.tv_usec = b.tv_usec - a.tv_usec) < 0) { diff.tv_sec--; diff.tv_usec += 1000000; } return (diff); } static void prnttime(struct timeval t) { printf("%lu msec", (u_long)(t.tv_sec * 1000 + (t.tv_usec / 1000))); } /* * Take arguments appearing in simple string (from file or command line) * place in char**. */ static void stackarg(char *l, char **y) { int done = 0; while (!done) { switch (*l) { case '\t': case ' ': l++; break; case '\0': case '\n': done++; *y = NULL; break; default: *y++ = l; while (!isspace(*l)) l++; if (*l == '\n') done++; *l++ = '\0'; *y = NULL; } } } Index: head/contrib/bind/bin/host/host.c =================================================================== --- head/contrib/bind/bin/host/host.c (revision 60940) +++ head/contrib/bind/bin/host/host.c (revision 60941) @@ -1,1954 +1,1954 @@ #ifndef lint -static const char rcsid[] = "$Id: host.c,v 8.34 1999/11/11 19:39:10 cyarnell Exp $"; +static const char rcsid[] = "$Id: host.c,v 8.36 2000/01/25 00:20:21 cyarnell Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1986 Regents of the University of California.\n\ Portions Copyright (c) 1993 Digital Equipment Corporation.\n\ Portions Copyright (c) 1996-1999 Internet Software Consortium.\n\ All rights reserved.\n"; #endif /* not lint */ /* * Actually, this program is from Rutgers University, however it is * based on nslookup and other pieces of named tools, so it needs * the above copyright notices. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Global. */ #define SIG_RDATA_BY_NAME 18 #define NS_HEADERDATA_SIZE 10 #define NUMNS 8 #define NUMNSADDR 16 #define NUMMX 50 #define NUMRR 127 /* max rr's per node to verify signatures for */ #define SUCCESS 0 #define TIME_OUT -1 #define NO_INFO -2 #define ERROR -3 #define NONAUTH -4 #define MY_PACKETSZ 64*1024 /* need this to hold tcp answers */ typedef union { HEADER qb1; u_char qb2[MY_PACKETSZ]; } querybuf; #define SD_RR 1 #define SD_SIG 2 #define SD_BADSIG 4 typedef struct { u_char data[MY_PACKETSZ]; size_t len; } rrstruct; static char chase_domain[NS_MAXDNAME]; static int chase_class; static int chase_type; static char chase_sigorigttl[NS_INT32SZ]; static rrstruct chase_rr[NUMRR]; static int chase_rr_num; static char chase_lastgoodkey[NS_MAXDNAME]; static char chase_signer[NS_MAXDNAME]; static u_char chase_sigrdata[MY_PACKETSZ]; static size_t chase_sigrdata_len; static u_char chase_signature[MY_PACKETSZ]; static size_t chase_signature_len; static int chase_step; static int sigchase; static char cnamebuf[NS_MAXDNAME]; static u_char hostbuf[NS_MAXDNAME]; static int sockFD; static FILE *filePtr; static struct __res_state res, orig; static char *cname = NULL; static const char *progname = "amnesia"; static int getclass = ns_c_in, verbose = 0, list = 0; static int server_specified = 0; -static int gettype; +static int gettype = 0; static char getdomain[NS_MAXDNAME]; /* Forward. */ static int parsetype(const char *s); static int parseclass(const char *s); static void printanswer(const struct hostent *hp); static void hperror(int errnum); static int addrinfo(struct in_addr addr); static int gethostinfo(char *name); static int getdomaininfo(const char *name, const char *domain); static int getinfo(const char *name, const char *domain, int type); static int printinfo(const querybuf *answer, const u_char *eom, int filter, int isls); static const u_char * pr_rr(const u_char *cp, const u_char *msg, FILE *file, int filter); static const char * pr_type(int type); static const char * pr_class(int class); static const u_char * pr_cdname(const u_char *cp, const u_char *msg, char *name, int namelen); static int ListHosts(char *namePtr, int queryType); static const char * DecodeError(int result); static void usage(const char *msg) { fprintf(stderr, "%s: usage error (%s)\n", progname, msg); fprintf(stderr, "\ Usage: %s [-adlrwv] [-t querytype] [-c class] host [server]\n\ \t-a is equivalent to '-v -t *'\n\ \t-c class to look for non-Internet data\n\ \t-d to turn on debugging output\n\ \t-l to turn on 'list mode'\n\ \t-r to disable recursive processing\n\ \t-s recursively chase signature found in answers\n\ \t-t querytype to look for a specific type of information\n\ \t-v for verbose output\n\ \t-w to wait forever until reply\n\ ", progname); exit(1); } /* Public. */ int main(int argc, char **argv) { struct in_addr addr; struct hostent *hp; char *s; int inverse = 0, waitmode = 0; int ncnames, ch; int nkeychains, i; dst_init(); if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else progname++; res_ninit(&res); res.retrans = 5; while ((ch = getopt(argc, argv, "ac:dlrst:vw")) != -1) { switch (ch) { case 'a': verbose = 1; gettype = ns_t_any; break; case 'c': getclass = parseclass(optarg); break; case 'd': res.options |= RES_DEBUG; break; case 'l': list = 1; break; case 'r': res.options &= ~RES_RECURSE; break; case 's': sigchase = 1; break; case 't': gettype = parsetype(optarg); break; case 'v': verbose = 1; break; case 'w': res.retry = 1; res.retrans = 15; waitmode = 1; break; default: usage("unrecogized switch"); /*NOTREACHED*/ } } - if (gettype == 0) { + if ((gettype == 0) && (sigchase)) { if (verbose) printf ("Forcing `-t a' for signature trace.\n"); gettype = ns_t_a; } argc -= optind; argv += optind; if (argc < 1) usage("missing host argument"); strncpy(getdomain, *argv++, NS_MAXDNAME); getdomain[NS_MAXDNAME-1] = 0; argc--; if (argc > 1) usage("extra undefined arguments"); if (argc == 1) { s = *argv++; argc--; server_specified++; if (!inet_aton(s, &addr)) { hp = gethostbyname(s); if (hp == NULL) { fprintf(stderr, "Error in looking up server name:\n"); hperror(res.res_h_errno); exit(1); } memcpy(&res.nsaddr.sin_addr, hp->h_addr, NS_INADDRSZ); printf("Using domain server:\n"); printanswer(hp); } else { res.nsaddr.sin_family = AF_INET; res.nsaddr.sin_addr = addr; res.nsaddr.sin_port = htons(NAMESERVER_PORT); printf("Using domain server %s:\n", inet_ntoa(res.nsaddr.sin_addr)); } res.nscount = 1; res.retry = 2; } if (strcmp(getdomain, ".") == 0 || !inet_aton(getdomain, &addr)) addr.s_addr = INADDR_NONE; hp = NULL; res.res_h_errno = TRY_AGAIN; /* * We handle default domains ourselves, thank you. */ res.options &= ~RES_DEFNAMES; if (list) exit(ListHosts(getdomain, gettype ? gettype : ns_t_a)); ncnames = 5; nkeychains = 18; while (hp == NULL && res.res_h_errno == TRY_AGAIN) { if (addr.s_addr == INADDR_NONE) { cname = NULL; hp = (struct hostent *)gethostinfo(getdomain); getdomain[0] = 0; /* clear this query */ if (sigchase && (chase_step & SD_RR)) { if (nkeychains-- == 0) { printf("Too many sig/key chains. Loop?\n"); exit(1); } if (chase_step & SD_SIG) { /* start new query, for KEY */ strcpy (getdomain, chase_signer); strcat (getdomain, "."); gettype = ns_t_key; } else if (!(chase_step & SD_BADSIG)) { /* start new query, for SIG */ strcpy (getdomain, chase_domain); strcat (getdomain, "."); gettype = ns_t_sig; } else if (hp && !(chase_step & SD_SIG) && (chase_step & SD_BADSIG)) { printf ("%s for %s not found, last verified key %s\n", chase_step & SD_SIG ? "Key" : "Signature", chase_step & SD_SIG ? chase_signer : chase_domain, chase_domain, chase_lastgoodkey ? chase_lastgoodkey : "None"); } } if (!getdomain[0] && cname) { if (ncnames-- == 0) { printf("Too many cnames. Loop?\n"); exit(1); } strcpy(getdomain, cname); strcat(getdomain, "."); } if (getdomain[0]) { if (chase_step & SD_SIG) { printf ("Locating key for %s\n", getdomain); } else if (chase_step & SD_SIG) { printf ("Locating signature for %s record(s) on %s\n", sym_ntos(__p_type_syms, chase_type, NULL), getdomain); } hp = NULL; res.res_h_errno = TRY_AGAIN; continue; } } else { if (addrinfo(addr) == 0) hp = NULL; else hp = (struct hostent *)1; /* XXX */ } if (!waitmode) break; } if (hp == NULL) { hperror(res.res_h_errno); exit(1); } exit(0); } /* Private. */ static int parsetype(const char *s) { int type, success; type = sym_ston(__p_type_syms, s, &success); if (success) return (type); if (strcmp(s, "*") == 0) return (ns_t_any); if (atoi(s)) return (atoi(s)); fprintf(stderr, "Invalid query type: %s\n", s); exit(2); /*NOTREACHED*/ } static int parseclass(const char *s) { int class, success; class = sym_ston(__p_class_syms, s, &success); if (success) return (class); if (atoi(s)) return (atoi(s)); fprintf(stderr, "Invalid query class: %s\n", s); exit(2); /*NOTREACHED*/ } static void printanswer(const struct hostent *hp) { struct in_addr **hptr; char **cp; printf("Name: %s\n", hp->h_name); printf("Address:"); for (hptr = (struct in_addr **)hp->h_addr_list; *hptr; hptr++) printf(" %s", inet_ntoa(**hptr)); printf("\nAliases:"); for (cp = hp->h_aliases; cp && *cp && **cp; cp++) printf(" %s", *cp); printf("\n\n"); } static void hperror(int errnum) { switch(errnum) { case HOST_NOT_FOUND: fprintf(stderr, "Host not found.\n"); break; case TRY_AGAIN: fprintf(stderr, "Host not found, try again.\n"); break; case NO_RECOVERY: fprintf(stderr, "No recovery, Host not found.\n"); break; case NO_ADDRESS: fprintf(stderr, "There is an entry for this host, but it doesn't have " ); switch (gettype) { case ns_t_a: fprintf(stderr, "an Internet address.\n"); break; case ns_t_ns: fprintf(stderr, "a Name Server.\n"); break; case ns_t_md: fprintf(stderr, "a Mail Destination.\n"); break; case ns_t_mf: fprintf(stderr, "a Mail Forwarder.\n"); break; case ns_t_cname: fprintf(stderr, "a Canonical Name.\n"); break; case ns_t_soa: fprintf(stderr, "a Start of Authority record.\n"); break; case ns_t_mb: fprintf(stderr, "a Mailbox Domain Name.\n"); break; case ns_t_mg: fprintf(stderr, "a Mail Group Member.\n"); break; case ns_t_mr: fprintf(stderr, "a Mail Rename Name.\n"); break; case ns_t_null: fprintf(stderr, "a Null Resource record.\n"); break; case ns_t_wks: fprintf(stderr, "any Well Known Service information.\n"); break; case ns_t_ptr: fprintf(stderr, "a Pointer record.\n"); break; case ns_t_hinfo: fprintf(stderr, "any Host Information.\n"); break; case ns_t_minfo: fprintf(stderr, "any Mailbox Information.\n"); break; case ns_t_mx: fprintf(stderr, "a Mail Exchanger record.\n"); break; case ns_t_txt: fprintf(stderr, "a Text record.\n"); break; case ns_t_rp: fprintf(stderr, "a Responsible Person.\n"); break; case ns_t_srv: fprintf(stderr, "a Server Selector.\n"); break; case ns_t_naptr: fprintf(stderr, "a URN Naming Authority.\n"); break; default: fprintf(stderr, "the information you requested.\n"); break; } break; } } static int addrinfo(struct in_addr addr) { u_int32_t ha = ntohl(addr.s_addr); char name[NS_MAXDNAME]; sprintf(name, "%u.%u.%u.%u.IN-ADDR.ARPA.", (ha) & 0xff, (ha >> 8) & 0xff, (ha >> 16) & 0xff, (ha >> 24) & 0xff); return (getinfo(name, NULL, ns_t_ptr)); } static int gethostinfo(char *name) { char *cp, **domain; char tmp[NS_MAXDNAME]; const char *tp; int hp, nDomain; int asis = 0; u_int n; if (strcmp(name, ".") == 0) return (getdomaininfo(name, NULL)); for (cp = name, n = 0; *cp; cp++) if (*cp == '.') n++; if (n && cp[-1] == '.') { if (cp[-1] == '.') cp[-1] = 0; hp = getdomaininfo(name, (char *)NULL); if (cp[-1] == 0) cp[-1] = '.'; return (hp); } if (n == 0 && (tp = res_hostalias(&res, name, tmp, sizeof tmp))) { if (verbose) printf("Aliased to \"%s\"\n", tp); res.options |= RES_DEFNAMES; return (getdomaininfo(tp, (char *)NULL)); } if (n >= res.ndots) { asis = 1; if (verbose) printf("Trying null domain\n"); hp = getdomaininfo(name, (char*)NULL); if (hp) return (hp); } for (domain = res.dnsrch; *domain; domain++) { if (verbose) printf("Trying domain \"%s\"\n", *domain); hp = getdomaininfo(name, *domain); if (hp) return (hp); } if (res.res_h_errno != HOST_NOT_FOUND || (res.options & RES_DNSRCH) == 0) return (0); if (!asis) return (0); if (verbose) printf("Trying null domain\n"); return (getdomaininfo(name, (char *)NULL)); } static int getdomaininfo(const char *name, const char *domain) { int val1, val2; if (gettype) return (getinfo(name, domain, gettype)); else { val1 = getinfo(name, domain, gettype=ns_t_a); if (cname || verbose) return (val1); val2 = getinfo(name, domain, gettype=ns_t_mx); return (val1 || val2); } } static int getinfo(const char *name, const char *domain, int type) { HEADER *hp; u_char *eom, *bp, *cp; querybuf buf, answer; int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen; u_short pref, class; char host[NS_MAXDNAME]; if (domain == NULL) sprintf(host, "%.*s", NS_MAXDNAME, name); else sprintf(host, "%.*s.%.*s", NS_MAXDNAME, name, NS_MAXDNAME, domain); n = res_nmkquery(&res, QUERY, host, getclass, type, NULL, 0, NULL, buf.qb2, sizeof buf); if (n < 0) { if (res.options & RES_DEBUG) printf("res_nmkquery failed\n"); res.res_h_errno = NO_RECOVERY; return (0); } n = res_nsend(&res, buf.qb2, n, answer.qb2, sizeof answer); if (n < 0) { if (res.options & RES_DEBUG) printf("res_nsend failed\n"); res.res_h_errno = TRY_AGAIN; return (0); } eom = answer.qb2 + n; return (printinfo(&answer, eom, ns_t_any, 0)); } static int printinfo(const querybuf *answer, const u_char *eom, int filter, int isls) { int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen, savesigchase; u_short pref, class; const u_char *bp, *cp; const HEADER *hp; /* * Find first satisfactory answer. */ hp = (HEADER *) answer; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); nscount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); if (res.options & RES_DEBUG || (verbose && isls == 0)) printf("rcode = %d (%s), ancount=%d\n", hp->rcode, DecodeError(hp->rcode), ancount); if (hp->rcode != NOERROR || (ancount+nscount+arcount) == 0) { switch (hp->rcode) { case NXDOMAIN: res.res_h_errno = HOST_NOT_FOUND; return (0); case SERVFAIL: res.res_h_errno = TRY_AGAIN; return (0); case NOERROR: res.res_h_errno = NO_DATA; return (0); case FORMERR: case NOTIMP: case REFUSED: res.res_h_errno = NO_RECOVERY; return (0); } return (0); } bp = hostbuf; nmx = 0; buflen = sizeof(hostbuf); cp = answer->qb2 + HFIXEDSZ; if (qdcount > 0) { while (qdcount-- > 0) { n = dn_skipname(cp, eom); if (n < 0) { printf("Form error.\n"); return (0); } cp += n + QFIXEDSZ; if (cp > eom) { printf("Form error.\n"); return (0); } } } if (ancount) { if (!hp->aa) if (verbose && isls == 0) printf( "The following answer is not authoritative:\n" ); if (!hp->ad) if (verbose && isls == 0) printf("The following answer is not verified as authentic by the server:\n"); while (--ancount >= 0 && cp && cp < eom) cp = pr_rr(cp, answer->qb2, stdout, filter); } if (!verbose) return (1); /* don't chase signatures for non-answer stuff */ savesigchase = sigchase; sigchase = 0; if (nscount) { printf("For authoritative answers, see:\n"); while (--nscount >= 0 && cp && cp < eom) cp = (u_char *)pr_rr(cp, answer->qb2, stdout, filter); } if (arcount) { printf("Additional information:\n"); while (--arcount >= 0 && cp && cp < eom) cp = (u_char *)pr_rr(cp, answer->qb2, stdout, filter); } /* restore sigchase value */ sigchase = savesigchase; return (1); } void print_hex_field (u_int8_t field[], int length, int width, char *pref) { /* Prints an arbitrary bit field, from one address for some number of bytes. Output is formatted via the width, and includes the raw hex value and (if printable) the printed value underneath. "pref" is a string used to start each line, e.g., " " to indent. This is very useful in gdb to see what's in a memory field. */ int i, start, stop; start=0; do { stop=(start+width)= 0) cname = cnamebuf; case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ns: case ns_t_ptr: { const u_char *startrdata = cp; u_char cdname[NS_MAXCDNAME]; cp = (u_char *)pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, "%c%s", punc, name); /* Extract DNSSEC canonical RR. */ n = ns_name_unpack(msg, msg+MY_PACKETSZ, startrdata, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Overwrite length field. */ ns_put16(n, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); /* Copy unpacked name. */ memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n); canonrr_len = NS_HEADERDATA_SIZE + n; } break; } case ns_t_hinfo: case ns_t_isdn: { const u_char *cp2 = cp + dlen; n = *cp++; if (n != 0) { if (doprint) fprintf(file,"%c%.*s", punc, n, cp); cp += n; } if ((cp < cp2) && (n = *cp++)) { if (doprint) fprintf(file,"%c%.*s", punc, n, cp); cp += n; } else if (type == ns_t_hinfo) if (doprint) fprintf(file, "\n; *** Warning *** OS-type missing" ); } break; case ns_t_soa: { const u_char *startname = cp; u_char cdname[NS_MAXCDNAME]; cp = (u_char *)pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, "\t%s", name); n = ns_name_unpack(msg, msg + 512, startname, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Copy expanded name. */ memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n); canonrr_len = NS_HEADERDATA_SIZE + n; } startname = cp; cp = (u_char *)pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, " %s", name); n = ns_name_unpack(msg, msg + 512, startname, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy expanded name. */ memcpy(canonrr + canonrr_len, cdname, n); canonrr_len += n; /* Copy rest of SOA. */ memcpy(canonrr + canonrr_len, cp, 5 * INT32SZ); canonrr_len += 5 * INT32SZ; /* Overwrite length field. */ ns_put16(canonrr_len - NS_HEADERDATA_SIZE, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); } if (doprint) fprintf(file, "(\n\t\t\t%ld\t;serial (version)", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%ld\t;refresh period", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%ld\t;retry refresh this often", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%ld\t;expiration period", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%ld\t;minimum TTL\n\t\t\t)", ns_get32(cp)); cp += INT32SZ; break; } case ns_t_mx: case ns_t_afsdb: case ns_t_rt: { const u_char *startrdata = cp; u_char cdname[NS_MAXCDNAME]; if (doprint) { if (type == ns_t_mx && !verbose) fprintf(file," (pri=%d) by ", ns_get16(cp)); else if (verbose) fprintf(file,"\t%d ", ns_get16(cp)); else fprintf(file," "); } cp += sizeof(u_short); cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, "%s", name); n = ns_name_unpack(msg, msg+512, startrdata + sizeof(u_short), cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Overwrite length field. */ ns_put16(sizeof(u_short) + n, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); /* Copy u_short. */ memcpy(canonrr + NS_HEADERDATA_SIZE, startrdata, sizeof(u_short)); /* Copy expanded name. */ memcpy(canonrr + NS_HEADERDATA_SIZE + sizeof(u_short), cdname, n); canonrr_len = NS_HEADERDATA_SIZE + sizeof(u_short) + n; } break; } case ns_t_srv: if (doprint) fprintf(file," %d", ns_get16(cp)); cp += sizeof(u_short); if (doprint) fprintf(file," %d", ns_get16(cp)); cp += sizeof(u_short); if (doprint) fprintf(file," %d", ns_get16(cp)); cp += sizeof(u_short); cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file,"%s",name); break; case ns_t_naptr: /* order */ if (doprint) fprintf(file, " %d", ns_get16(cp)); cp += sizeof(u_short); /* preference */ if (doprint) fprintf(file, " %d", ns_get16(cp)); cp += NS_INT16SZ; /* Flags */ n = *cp++; if (doprint) { if (n) fprintf(file, "%c%.*s", punc, n, cp); else fprintf(file, "%c\"\"",punc); } cp += n; /* Service */ n = *cp++; if (doprint) { if (n) fprintf(file, "%c%.*s", punc, n, cp); else fprintf(file,"%c\"\"",punc); } cp += n; /* Regexp */ n = *cp++; if (doprint) { if (n) fprintf(file, "%c%.*s", punc, n, cp); else fprintf(file, "%c\"\"",punc); } cp += n; /* replacement */ cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, "%s", name); break; case ns_t_minfo: case ns_t_rp: cp = (u_char *)pr_cdname(cp, msg, name, sizeof name); if (doprint) { if (type == ns_t_rp) { char *p; p = strchr(name, '.'); if (p != NULL) *p = '@'; } fprintf(file, "%c%s", punc, name); } cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, " %s", name); break; case ns_t_x25: n = *cp++; if (n != 0) { if (doprint) fprintf(file, "%c%.*s", punc, n, cp); cp += n; } break; case ns_t_txt: { int n, j; const u_char *end = cp + dlen; while (cp < end) { if (doprint) (void) fputs(" \"", file); n = *cp++; if (n != 0) for (j = n; j > 0 && cp < end ; j --) { if (doprint) { if (*cp == '\n' || *cp == '"' || *cp == '\\') putc('\\', file); putc(*cp, file); } cp++; } if (doprint) putc('"', file); } } break; case ns_t_wks: if (dlen < INT32SZ + 1) break; memcpy(&inaddr, cp, INADDRSZ); cp += INT32SZ; proto = *cp++; protop = getprotobynumber(proto); if (doprint) { if (protop) fprintf(file, "%c%s %s", punc, inet_ntoa(inaddr), protop->p_name); else fprintf(file, "%c%s %d", punc, inet_ntoa(inaddr), proto); } n = 0; while (cp < cp1 + dlen) { c = *cp++; do { if (c & 0200) { servp = NULL; if (protop) servp = getservbyport(htons(n), protop-> p_name); if (doprint) { if (servp) fprintf(file, " %s", servp->s_name); else fprintf(file, " %d", n); } } c <<= 1; } while (++n & 07); } break; case ns_t_nxt: { const u_char *startrdata = cp; u_char cdname[NS_MAXCDNAME]; size_t bitmaplen; cp = (u_char *) pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, "%c%s", punc, name); bitmaplen = dlen - (cp - startrdata); /* extract dnssec canonical rr */ n = ns_name_unpack(msg, msg+MY_PACKETSZ, startrdata, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Overwrite length field. */ ns_put16(n + bitmaplen, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); /* Copy expanded name. */ memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n); /* Copy type bit map. */ memcpy(canonrr + NS_HEADERDATA_SIZE + n, cp, bitmaplen); canonrr_len = NS_HEADERDATA_SIZE + n + bitmaplen; } cp += bitmaplen; break; } case ns_t_sig: { int tc; const u_char *origttl; /* type covered */ tc = ns_get16(cp); if (doprint && verbose) fprintf(file, "%c%s", punc, sym_ntos(__p_type_syms, tc, NULL)); cp += sizeof(u_short); /* algorithm */ if (doprint && verbose) fprintf(file, " %d", *cp); cp++; /* labels */ if (doprint && verbose) fprintf(file, " %d", *cp); cp++; /* original ttl */ origttl = cp; if (doprint && verbose) fprintf(file, " %d", ns_get32(cp)); cp += INT32SZ; /* signature expiration */ if (doprint && verbose) fprintf(file, " %d", ns_get32(cp)); cp += INT32SZ; /* time signed */ if (doprint && verbose) fprintf(file, " %d", ns_get32(cp)); cp += INT32SZ; /* key footprint */ if (doprint && verbose) fprintf(file, " %d", ns_get16(cp)); cp += sizeof(u_short); /* signer's name */ cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name)); if (doprint && verbose) fprintf(file, " %s", name); else if (doprint && !verbose) fprintf (file, " %s for type %s", name, sym_ntos(__p_type_syms, tc, NULL)); /* signature */ { char str[MY_PACKETSZ]; size_t len = cp1-cp+dlen; b64_ntop (cp, len, str, MY_PACKETSZ-1); if (sigchase && !(chase_step & SD_SIG) && strcmp (chase_domain, thisdomain) == 0 && chase_class == class & chase_type == tc) { u_char cdname[NS_MAXCDNAME]; if (doprint && !verbose) fprintf(file, " (chasing key)"); strcpy(chase_signer, name); memcpy(&chase_sigorigttl[0], origttl, NS_INT32SZ); n = ns_name_ntol(cp1 + SIG_RDATA_BY_NAME, cdname, sizeof cdname); if (n >= 0) { memcpy(chase_sigrdata, cp1, SIG_RDATA_BY_NAME); memcpy(chase_sigrdata + SIG_RDATA_BY_NAME, cdname, n); chase_sigrdata_len += SIG_RDATA_BY_NAME + n; memcpy(chase_signature, cp, len); chase_signature_len = len; chase_step |= SD_SIG; } } else if (sigchase) { chase_step |= SD_BADSIG; } cp += len; if (doprint && verbose) fprintf (file, " %s", str); } break; } case ns_t_key: /* flags */ if (doprint && verbose) fprintf(file, "%c%d", punc, ns_get16(cp)); cp += sizeof(u_short); /* protocol */ if (doprint && verbose) fprintf(file, " %d", *cp); cp++; /* algorithm */ n = *cp; if (doprint && verbose) fprintf(file, " %d", *cp); cp++; switch (n) { case 1: /* MD5/RSA */ { char str[MY_PACKETSZ]; size_t len = cp1-cp+dlen; b64_ntop (cp, len, str, MY_PACKETSZ-1); cp += len; if (doprint && verbose) fprintf (file, " %s", str); break; } default: fprintf (stderr, "Unknown algorithm %d\n", n); cp = cp1 + dlen; break; } if (sigchase && (chase_step & (SD_SIG|SD_RR)) && strcmp (getdomain, name) == 0 && getclass == class & gettype == type) { DST_KEY *dstkey; int rc, len, i, j; /* convert dnskey to dstkey */ dstkey = dst_dnskey_to_key (name, cp1, dlen); /* fix ttl in rr */ for (i = 0; i < NUMRR && chase_rr[i].len; i++) { len = dn_skipname(chase_rr[i].data, chase_rr[i].data + chase_rr[i].len); if (len>=0) memcpy(chase_rr[i].data + len + NS_INT16SZ + NS_INT16SZ, &chase_sigorigttl, INT32SZ); } /* sort rr's (qsort() is too slow) */ for (i = 0; i < NUMRR && chase_rr[i].len; i++) for (j = i + 1; i < NUMRR && chase_rr[j].len; j++) if (memcmp(chase_rr[i].data, chase_rr[j].data, MY_PACKETSZ) > 0) memswap(&chase_rr[i], &chase_rr[j], sizeof(rrstruct)); /* append rr's to sigrdata */ for (i = 0; i < NUMRR && chase_rr[i].len; i++) { memcpy (chase_sigrdata + chase_sigrdata_len, chase_rr[i].data, chase_rr[i].len); chase_sigrdata_len += chase_rr[i].len; } /* print rr-data and signature */ if (verbose) { print_hex_field(chase_sigrdata, chase_sigrdata_len, 21,"DATA: "); print_hex_field(chase_signature, chase_signature_len, 21,"SIG: "); } /* do the works */ if (dstkey) rc = dst_verify_data(SIG_MODE_ALL, dstkey, NULL, chase_sigrdata, chase_sigrdata_len, chase_signature, chase_signature_len); else rc = 1; dst_free_key(dstkey); if (verbose) { fprintf(file, "\nVerification %s", rc == 0 ? "was SUCCESSFULL" : "FAILED"); } else { fprintf (file, " that %s verify our %s " "record(s) on %s", rc == 0 ? "successfully" : "DOES NOT", sym_ntos(__p_type_syms, chase_type, NULL), chase_domain); } if (rc == 0) { strcpy (chase_lastgoodkey, name); } else { /* don't trace further after a failure */ sigchase = 0; } chase_step = 0; chase_signature_len = 0; chase_sigrdata_len = 0; memset(chase_sigorigttl, 0, NS_INT32SZ); memset(chase_rr, 0, sizeof(chase_rr)); chase_rr_num = 0; } break; default: if (doprint) fprintf(file, "%c???", punc); cp += dlen; break; } if (cp != cp1 + dlen) fprintf(file, "packet size error (%p != %p)\n", cp, cp1 + dlen); if (sigchase && !(chase_step & SD_SIG) && strcmp (getdomain, thisdomain) == 0 && getclass == class && gettype == type && type != ns_t_sig) { u_char cdname[NS_MAXCDNAME]; if (doprint && !verbose) fprintf (file, " (chasing signature)", sigchase-1); /* unpack rr */ n = ns_name_unpack(msg, msg + MY_PACKETSZ, savecp, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { memcpy(chase_rr[chase_rr_num].data, cdname, n); memcpy(chase_rr[chase_rr_num].data + n, canonrr_len ? canonrr : cp1 - NS_HEADERDATA_SIZE, canonrr_len ? canonrr_len : dlen + NS_HEADERDATA_SIZE); chase_rr[chase_rr_num].len = n + (canonrr_len != 0 ? canonrr_len : dlen + NS_HEADERDATA_SIZE); strcpy(chase_domain, getdomain); chase_class = class; chase_type = type; chase_step |= SD_RR; chase_rr_num++; } } if (doprint) fprintf(file, "\n"); return (cp); } /* * Return a string for the type. A few get special treatment when * not in verbose mode, to make the program more chatty and easier to * understand. */ static const char * pr_type(int type) { if (!verbose) switch (type) { case ns_t_a: return ("has address"); case ns_t_cname: return ("is a nickname for"); case ns_t_mx: return ("mail is handled"); case ns_t_txt: return ("descriptive text"); case ns_t_sig: return ("has a signature signed by"); case ns_t_key: return ("has a key"); case ns_t_nxt: return ("next valid name"); case ns_t_afsdb: return ("DCE or AFS service from"); } if (verbose) return (sym_ntos(__p_type_syms, type, NULL)); else return (sym_ntop(__p_type_syms, type, NULL)); } /* * Return a mnemonic for class */ static const char * pr_class(int class) { static char spacestr[20]; if (!verbose) switch (class) { case ns_c_in: /* internet class */ return (""); case ns_c_hs: /* hesiod class */ return (""); } spacestr[0] = ' '; strcpy(&spacestr[1], p_class(class)); return (spacestr); } static const u_char * pr_cdname(const u_char *cp, const u_char *msg, char *name, int namelen) { int n = dn_expand(msg, msg + MY_PACKETSZ, cp, name, namelen - 2); if (n < 0) return (NULL); if (name[0] == '\0') { name[0] = '.'; name[1] = '\0'; } return (cp + n); } static int ListHosts(char *namePtr, int queryType) { querybuf buf, answer; struct sockaddr_in sin; const HEADER *headerPtr; const struct hostent *hp; enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING } error = NO_ERRORS; int msglen, amtToRead, numRead, i, len, dlen, type, nscount, n; int numAnswers = 0, soacnt = 0, result = 0; u_char tmp[NS_INT16SZ]; char name[NS_MAXDNAME], dname[2][NS_MAXDNAME], domain[NS_MAXDNAME]; u_char *cp, *nmp, *eom; /* Names and addresses of name servers to try. */ char nsname[NUMNS][NS_MAXDNAME]; int nshaveaddr[NUMNS]; struct in_addr nsipaddr[NUMNSADDR]; int numns, numnsaddr, thisns; /* * Normalize to not have trailing dot. We do string compares below * of info from name server, and it won't have trailing dots. */ i = strlen(namePtr); if (namePtr[i-1] == '.') namePtr[i-1] = 0; if (server_specified) { memcpy(&nsipaddr[0], &res.nsaddr.sin_addr, NS_INADDRSZ); numnsaddr = 1; } else { /* * First we have to find out where to look. This needs a NS * query, possibly followed by looking up addresses for some * of the names. */ msglen = res_nmkquery(&res, ns_o_query, namePtr, ns_c_in, ns_t_ns, NULL, 0, NULL, buf.qb2, sizeof buf); if (msglen < 0) { printf("res_nmkquery failed\n"); return (ERROR); } msglen = res_nsend(&res, buf.qb2, msglen, answer.qb2, sizeof answer); if (msglen < 0) { printf("Cannot find nameserver -- try again later\n"); return (ERROR); } if (res.options & RES_DEBUG || verbose) printf("rcode = %d (%s), ancount=%d\n", answer.qb1.rcode, DecodeError(answer.qb1.rcode), ntohs(answer.qb1.ancount)); /* * Analyze response to our NS lookup. */ nscount = ntohs(answer.qb1.ancount) + ntohs(answer.qb1.nscount) + ntohs(answer.qb1.arcount); if (answer.qb1.rcode != NOERROR || nscount == 0) { switch (answer.qb1.rcode) { case NXDOMAIN: /* Check if it's an authoritive answer */ if (answer.qb1.aa) printf("No such domain\n"); else printf("Unable to get information about domain -- try again later.\n"); break; case SERVFAIL: printf("Unable to get information about that domain -- try again later.\n"); break; case NOERROR: printf("That domain exists, but seems to be a leaf node.\n"); break; case FORMERR: case NOTIMP: case REFUSED: printf("Unrecoverable error looking up domain name.\n"); break; } return (0); } cp = answer.qb2 + HFIXEDSZ; eom = answer.qb2 + msglen; if (ntohs(answer.qb1.qdcount) > 0) { n = dn_skipname(cp, eom); if (n < 0) { printf("Form error.\n"); return (ERROR); } cp += n + QFIXEDSZ; if (cp > eom) { printf("Form error.\n"); return (ERROR); } } numns = 0; numnsaddr = 0; /* * Look at response from NS lookup for NS and A records. */ for ((void)NULL; nscount; nscount--) { cp += dn_expand(answer.qb2, answer.qb2 + msglen, cp, domain, sizeof(domain)); if (cp + 3 * INT16SZ + INT32SZ > eom) { printf("Form error.\n"); return (ERROR); } type = ns_get16(cp); cp += INT16SZ + INT16SZ + INT32SZ; dlen = ns_get16(cp); cp += INT16SZ; if (cp + dlen > eom) { printf("Form error.\n"); return (ERROR); } if (type == ns_t_ns) { if (dn_expand(answer.qb2, eom, cp, name, sizeof(name)) >= 0) { if (numns < NUMNS && ns_samename((char *)domain, namePtr) == 1) { for (i = 0; i < numns; i++) if (ns_samename( nsname[i], (char *)name ) == 1) /* duplicate */ break; if (i >= numns) { strncpy(nsname[numns], (char *)name, sizeof(name)); nshaveaddr[numns] = 0; numns++; } } } } else if (type == ns_t_a) { if (numnsaddr < NUMNSADDR) for (i = 0; i < numns; i++) { if (ns_samename(nsname[i], (char *)domain) == 1) { nshaveaddr[i]++; memcpy( &nsipaddr[numnsaddr], cp, NS_INADDRSZ); numnsaddr++; break; } } } cp += dlen; } /* * Usually we'll get addresses for all the servers in the * additional info section. But in case we don't, look up * their addresses. */ for (i = 0; i < numns; i++) { if (nshaveaddr[i] == 0) { struct in_addr **hptr; int numaddrs = 0; hp = gethostbyname(nsname[i]); if (hp) { for (hptr = (struct in_addr **) hp->h_addr_list; *hptr != NULL; hptr++) if (numnsaddr < NUMNSADDR) { memcpy( &nsipaddr[numnsaddr], *hptr, NS_INADDRSZ); numnsaddr++; numaddrs++; } } if (res.options & RES_DEBUG || verbose) printf( "Found %d addresses for %s by extra query\n", numaddrs, nsname[i]); } else if (res.options & RES_DEBUG || verbose) printf("Found %d addresses for %s\n", nshaveaddr[i], nsname[i]); } } /* * Now nsipaddr has numnsaddr addresses for name servers that * serve the requested domain. Now try to find one that will * accept a zone transfer. */ thisns = 0; again: numAnswers = 0; soacnt = 0; /* * Create a query packet for the requested domain name. */ msglen = res_nmkquery(&res, QUERY, namePtr, getclass, ns_t_axfr, NULL, 0, NULL, buf.qb2, sizeof buf); if (msglen < 0) { if (res.options & RES_DEBUG) fprintf(stderr, "ListHosts: Res_mkquery failed\n"); return (ERROR); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(NAMESERVER_PORT); /* * Set up a virtual circuit to the server. */ for ((void)NULL; thisns < numnsaddr; thisns++) { if ((sockFD = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("ListHosts"); return (ERROR); } memcpy(&sin.sin_addr, &nsipaddr[thisns], NS_INADDRSZ); if (res.options & RES_DEBUG || verbose) printf("Trying %s\n", inet_ntoa(sin.sin_addr)); if (connect(sockFD, (struct sockaddr *)&sin, sizeof(sin)) >= 0) break; if (verbose) perror("Connection failed, trying next server"); close(sockFD); sockFD = -1; } if (thisns >= numnsaddr) { printf("No server for that domain responded\n"); if (!verbose) perror("Error from the last server was"); return (ERROR); } /* * Send length & message for zone transfer */ ns_put16(msglen, tmp); if (write(sockFD, (char *)tmp, INT16SZ) != INT16SZ || write(sockFD, (char *)buf.qb2, msglen) != msglen) { perror("ListHosts"); (void) close(sockFD); sockFD = -1; return (ERROR); } filePtr = stdout; for (;;) { /* * Read the length of the response. */ cp = buf.qb2; amtToRead = INT16SZ; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_LEN; break; } if ((len = ns_get16(buf.qb2)) == 0) break; /* Protocol violation. */ /* * Read the response. */ amtToRead = len; cp = buf.qb2; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_MSG; break; } i = buf.qb1.rcode; if (i != NOERROR || ntohs(buf.qb1.ancount) == 0) { if (thisns + 1 < numnsaddr && (i == SERVFAIL || i == NOTIMP || i == REFUSED)) { if (res.options & RES_DEBUG || verbose) printf( "Server failed, trying next server: %s\n", i != NOERROR ? DecodeError(i) : "Premature end of data"); (void) close(sockFD); sockFD = -1; thisns++; goto again; } printf("Server failed: %s\n", i != NOERROR ? DecodeError(i) : "Premature end of data"); break; } result = printinfo(&buf, cp, queryType, 1); if (! result) { error = ERR_PRINTING; break; } numAnswers++; cp = buf.qb2 + HFIXEDSZ; if (ntohs(buf.qb1.qdcount) > 0) { n = dn_skipname(cp, buf.qb2 + len); if (n < 0) { error = ERR_PRINTING; break; } cp += n + QFIXEDSZ; } nmp = cp; n = dn_skipname(cp, buf.qb2 + len); if (n < 0) { error = ERR_PRINTING; break; } cp += n; if (cp + INT16SZ > buf.qb2 + len) { error = ERR_PRINTING; break; } if ((ns_get16(cp) == ns_t_soa)) { (void) dn_expand(buf.qb2, buf.qb2 + len, nmp, dname[soacnt], sizeof dname[0]); if (soacnt) { if (ns_samename(dname[0], dname[1]) == 1) break; } else soacnt++; } } (void) close(sockFD); sockFD = -1; switch (error) { case NO_ERRORS: return (SUCCESS); case ERR_READING_LEN: return (ERROR); case ERR_PRINTING: fprintf(stderr,"*** Error during listing of %s: %s\n", namePtr, DecodeError(result)); return (result); case ERR_READING_MSG: headerPtr = (HEADER *) &buf; fprintf(stderr,"ListHosts: error receiving zone transfer:\n"); fprintf(stderr, " result: %s, answers = %d, authority = %d, additional = %d\n", p_rcode(headerPtr->rcode), ntohs(headerPtr->ancount), ntohs(headerPtr->nscount), ntohs(headerPtr->arcount)); return (ERROR); default: return (ERROR); } } static const char * DecodeError(int result) { switch(result) { case NOERROR: return ("Success"); case FORMERR: return ("Format error"); case SERVFAIL: return ("Server failed"); case NXDOMAIN: return ("Non-existent domain"); case NOTIMP: return ("Not implemented"); case REFUSED: return ("Query refused"); case NO_INFO: return ("No information"); case ERROR: return ("Unspecified error"); case TIME_OUT: return ("Timed out"); case NONAUTH: return ("Non-authoritative answer"); default: return ("BAD ERROR VALUE"); } /* NOTREACHED */ } Index: head/contrib/bind/bin/irpd/Makefile =================================================================== --- head/contrib/bind/bin/irpd/Makefile (revision 60940) +++ head/contrib/bind/bin/irpd/Makefile (revision 60941) @@ -1,98 +1,98 @@ ## Copyright (c) 1996, 1997 by Internet Software Consortium ## ## Permission to use, copy, modify, and distribute this software for any ## purpose with or without fee is hereby granted, provided that the above ## copyright notice and this permission notice appear in all copies. ## ## THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS ## ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES ## OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE ## CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL ## DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR ## PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ## ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ## SOFTWARE. -# $Id: Makefile,v 1.3 1999/02/22 02:47:55 vixie Exp $ +# $Id: Makefile,v 1.4 2000/05/09 07:02:22 vixie Exp $ DESTDIR= CC= cc SHELL= /bin/sh CDEBUG= -g #(net2 and its descendents) SYSTYPE = bsdos TOP = ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.a A=a O=o EXE= LEX = lex -I YACC = yacc -d SYSLIBS = -ll -lutil DESTBIN = /usr/local/bin DESTSBIN = /usr/local/sbin DESTEXEC = /usr/local/libexec DESTMAN = /usr/share/man DESTHELP= /usr/share/misc DESTETC= /etc DESTRUN= /var/run AR= ar cru INSTALL= install STRIP=-s PS=ps LDFLAGS= CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} -I${TOP}/lib/irs ${DEFS} VER= LOCAL-`date +%y%m%d.%H%M%S` HOSTNAMECMD= hostname || uname -n PROG= irpd HDRS= SRCS= irpd.c OBJS= irpd.${O} all: ${PROG}${EXE} ${PROG}${EXE}: irpd.${O} tmp_version.${O} ${LIBBIND} ${CC} ${CDEBUG} ${LDFLAGS} -o ${PROG}${EXE} ${OBJS} tmp_version.${O} \ ${LIBBIND} ${SYSLIBS} tmp_version.${O}: tmp_version.c tmp_version.c: version.c Makefile ../Makefile ${SRCS} ${HDRS} (u=$${USER-root} d=`pwd` h=`${HOSTNAMECMD}` t=`date`; \ sed -e "s|%WHEN%|$${t}|" -e "s|%VERSION%|"${VER}"|" \ -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \ - < version.c > tmp_version.c) + < version.c > tmp_version.c); sleep 1 distclean: clean clean: FRC rm -f ${PROG}${EXE} ${OBJS} core .depend rm -f *.BAK *.CKP *~ *.orig rm -f tmp_version.c tmp_version.${O} depend: ${SRCS} mkdep ${CPPFLAGS} -I${INCL} -I${PORTINCL} -I${TOP}/lib/irs ${DEFS} ${SRCS} ${DESTDIR}${DESTSBIN}: mkdir -p ${DESTDIR}${DESTSBIN} install: ${DESTDIR}${DESTSBIN} ${PROG}${EXE} ${INSTALL} ${STRIP} -c -m 755 ${PROG}${EXE} ${DESTDIR}${DESTSBIN}/${PROG}${EXE} links: FRC @ln -s SRC/*.[chy] SRC/test .; rm -f ns_parser.[ch] tags: FRC ctags ${SRCS} *.h FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/bin/irpd/irpd.c =================================================================== --- head/contrib/bind/bin/irpd/irpd.c (revision 60940) +++ head/contrib/bind/bin/irpd/irpd.c (revision 60941) @@ -1,2252 +1,2259 @@ /* * Copyright(c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Notes. */ #if 0 I have to use an AF_INET. Ctl_server should probably take a AF arugment. The server has no way to issue any other greeting than HELLO. E.g., would like to be able to drop connection on greeting if client is not comming from 127.0.0.1. Need to fix client to handle response with body. should add iovec with body to the struct ctl_sess? should we close connections on some errors (like marshalling errors)? getnetbyname falls back to /etc/networks when named not running. Does not seem to be so for getnetbyaddr #endif #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: irpd.c,v 1.7 1999/10/13 16:26:23 vixie Exp $"; +static const char rcsid[] = "$Id: irpd.c,v 1.8 2000/02/04 08:28:27 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EMPTY /* Digital UNIX utmp.h defines this. */ #undef EMPTY #endif #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Macros. */ #define ALLDIGITS(s) (strspn((s), "0123456789") == strlen((s))) #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif #define MAXNETNAMELEN 256 #if !defined(SUN_LEN) #define SUN_LEN(su) \ (sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path)) #endif /* * This macro is used to initialize a specified field of a net_data struct. * If the initialization fails then an error response code is sent with a * description of which field failed to be initialized. * * This is only meant for use at the start of the various verb functions. */ #define ND_INIT(nd, field, sess, respcode) \ do{ if ((nd)->field == 0) { \ (nd)->field = (*(nd)->irs->field ## _map)(nd->irs); \ if ((nd)->field == 0) { \ char *msg = "net_data " #field " initialization failed"; \ ctl_response(sess, respcode, msg, CTL_EXIT, NULL, \ NULL, NULL, NULL, 0); \ return; \ } \ } \ } while (0) /* Data structures. */ struct arg_s { struct iovec * iov; int iovlen; }; struct response_buff { char * buff; size_t bufflen; }; struct client_ctx { struct net_data * net_data; }; /* Forwards. */ static struct response_buff *newbuffer(u_int length); static void release_buffer(struct response_buff *b); static struct arg_s *split_string(const char *string); static void free_args(struct arg_s *args); static int is_all_digits(char *p); static struct client_ctx *make_cli_ctx(void); static struct net_data *get_net_data(struct ctl_sess *sess); static void irpd_gethostbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_gethostbyname2(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_gethostbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_gethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_sethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getpwnam(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getpwuid(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_setpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getnetbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getnetbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_setnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getgrnam(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getgrgid(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_setgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getservbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getservbyport(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getservent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_setservent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getprotobyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getprotobynumber(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_setprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_getnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_innetgr(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_setnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_endnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_quit(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_help(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_accept(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_abort(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx); static void irpd_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *param); static void response_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *uap); static void logger(enum ctl_severity, const char *fmt, ...); /* Constants. */ static const u_int hello_code = IRPD_WELCOME_CODE; static const char hello_msg[] = "Welcome to IRPD (v 1)"; static const u_int unkncode = 500; static const u_int timeoutcode = 501; static const u_int irpd_quit_ok = 201; static const u_int timeout = IRPD_TIMEOUT; /* Globals. */ static int main_needs_exit = 0; static evContext ev; struct ctl_verb verbs [] = { { "gethostbyname", irpd_gethostbyname }, { "gethostbyname2", irpd_gethostbyname2 }, { "gethostbyaddr", irpd_gethostbyaddr }, { "gethostent", irpd_gethostent }, { "sethostent", irpd_sethostent }, #ifdef WANT_IRS_PW { "getpwnam", irpd_getpwnam }, { "getpwuid", irpd_getpwuid }, { "getpwent", irpd_getpwent }, { "setpwent", irpd_setpwent }, #endif { "getnetbyname", irpd_getnetbyname }, { "getnetbyaddr", irpd_getnetbyaddr }, { "getnetent", irpd_getnetent }, { "setnetent", irpd_setnetent }, #ifdef WANT_IRS_GR { "getgrnam", irpd_getgrnam }, { "getgrgid", irpd_getgrgid }, { "getgrent", irpd_getgrent }, { "setgrent", irpd_setgrent }, #endif { "getservbyname", irpd_getservbyname }, { "getservbyport", irpd_getservbyport }, { "getservent", irpd_getservent }, { "setservent", irpd_setservent }, { "getprotobyname", irpd_getprotobyname }, { "getprotobynumber", irpd_getprotobynumber }, { "getprotoent", irpd_getprotoent }, { "setprotoent", irpd_setprotoent }, { "getnetgrent", irpd_getnetgrent }, { "innetgr", irpd_innetgr }, { "setnetgrent", irpd_setnetgrent }, { "endnetgrent", irpd_endnetgrent }, { "quit", irpd_quit }, { "help", irpd_help }, { "", irpd_accept }, /* For connection setups. */ /* abort is a verb expected by the ctl library. Is called when the * client drops the connection unexpectedly. */ { "abort", irpd_abort }, { NULL, NULL } }; /* * An empty string causes the library to use the compiled in * defaults and to ignore any external files. */ char *conffile = ""; /* Public. */ int main(int argc, char **argv) { struct ctl_sctx *ctx; struct sockaddr *addr; +#ifndef NO_SOCKADDR_UN struct sockaddr_un uaddr; +#endif struct sockaddr_in iaddr; log_channel chan; short port = IRPD_PORT; char *prog = argv[0]; char *sockname = IRPD_PATH; char *p; int ch; size_t socksize; addr = (struct sockaddr *)&iaddr; socksize = sizeof iaddr; openlog("iprd", LOG_CONS|LOG_PID, LOG_DAEMON); while ((ch = getopt(argc, argv, "u:p:c:")) != -1) { switch(ch) { case 'c': conffile = optarg; break; case 'p': port = strtol(optarg, &p, 10); if (*p != '\0') { /* junk in argument */ syslog(LOG_ERR, "port option not a number"); exit(1); } break; +#ifndef NO_SOCKADDR_UN case 'u': sockname = optarg; addr = (struct sockaddr *)&uaddr; socksize = sizeof uaddr; break; +#endif case 'h': case '?': default: fprintf(stderr, "%s [ -c config-file ]\n", prog); exit(1); } } argc -= optind; argv += optind; - memset(&uaddr, 0, sizeof uaddr); memset(&iaddr, 0, sizeof iaddr); #ifdef HAVE_SA_LEN iaddr.sin_len = sizeof iaddr; #endif iaddr.sin_family = AF_INET; iaddr.sin_port = htons(IRPD_PORT); iaddr.sin_addr.s_addr = htonl(INADDR_ANY); - uaddr.sun_family = AF_UNIX; - strncpy(uaddr.sun_path, sockname, sizeof uaddr.sun_path); +#ifndef NO_SOCKADDR_UN + memset(&uaddr, 0, sizeof uaddr); + if (addr == (struct sockaddr *)&uaddr) { + uaddr.sun_family = AF_UNIX; + strncpy(uaddr.sun_path, sockname, sizeof uaddr.sun_path); #ifdef HAVE_SA_LEN - uaddr.sun_len = SUN_LEN(&uaddr); + uaddr.sun_len = SUN_LEN(&uaddr); #endif - if (addr == (struct sockaddr *)&uaddr) socksize = SUN_LEN(&uaddr); - /* XXX what if this file is not currently a socket? */ - unlink(sockname); + /* XXX what if this file is not currently a socket? */ + unlink(sockname); + } +#endif evCreate(&ev); ctx = ctl_server(ev, addr, socksize, verbs, unkncode, timeoutcode, /* IRPD_TIMEOUT */ 30, 5, IRPD_MAXSESS, logger, NULL); INSIST(ctx != NULL); while (!main_needs_exit) { evEvent event; INSIST_ERR(evGetNext(ev, &event, EV_WAIT) != -1); INSIST_ERR(evDispatch(ev, event) != -1); } return (0); } /* * static void * simple_response(struct ctl_sess *sess, u_int code, char *msg); * Send back a simple, one-line response to the client. */ static void simple_response(struct ctl_sess *sess, u_int code, char *msg) { struct response_buff *b = newbuffer(strlen(msg) + 1); if (b == 0) return; strcpy(b->buff, msg); ctl_response(sess, code, b->buff, 0, 0, response_done, b, NULL, 0); } /* * static void * send_hostent(struct ctl_sess *sess, struct hostent *ho); * Send a hostent struct over the wire. If HO is NULL, then * a "No such host" is sent instead. */ static void send_hostent(struct ctl_sess *sess, struct hostent *ho) { if (ho == NULL) simple_response(sess, IRPD_GETHOST_NONE, "No such host"); else { size_t need; struct response_buff *b = newbuffer(0); if (irp_marshall_ho(ho, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETHOST_ERROR, "Internal error"); logger(ctl_warning, "Cannot marshall host data for %s\n", ho->h_name); release_buffer(b); } else { strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETHOST_OK, "Host found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } } /* * static void * do_gethostbyname2(struct ctl_sess *sess, struct net_data *nd, * const char *hostname, int af); * Look up the given HOSTNAME by Address-Family * and then send the results to the client connected to * SESS. */ static void do_gethostbyname2(struct ctl_sess *sess, struct net_data *nd, const char *hostname, int af) { struct hostent *ho; ho = gethostbyname2_p(hostname, af, nd); send_hostent(sess, ho); } /* * static void * irpd_gethostbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETHOSTBYNAME verb. */ static void irpd_gethostbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { char hname[MAXHOSTNAMELEN]; struct arg_s *args; int i; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETHOST_ERROR, "Incorrect usage: GETHOSTBYNAME hostname"); } else { if (args->iov[0].iov_len >= sizeof hname) { simple_response(sess, IRPD_GETHOST_ERROR, "GETHOSTBYNAME: name too long"); } else { strncpy(hname, args->iov[0].iov_base, args->iov[0].iov_len); hname[args->iov[0].iov_len] = '\0'; do_gethostbyname2(sess, netdata, hname, AF_INET); } } free_args(args); } /* * static void * irpd_gethostbyname2(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETHOSTBYNAME2 verb. */ static void irpd_gethostbyname2(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { char hname[MAXHOSTNAMELEN]; struct arg_s *args; int i; int af; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); args = split_string(rest); if (args->iovlen != 3) { /* len includes NULL at end */ simple_response(sess, IRPD_GETHOST_ERROR, "Incorrect usage: GETHOSTBYNAME2 hostname AF"); } else if (args->iov[0].iov_len >= sizeof hname) { simple_response(sess, IRPD_GETHOST_ERROR, "GETHOSTBYNAME2: name too long"); } else { if (strncasecmp(args->iov[1].iov_base, "af_inet6", 8) == 0) af = AF_INET6; else if (strncasecmp(args->iov[1].iov_base, "af_inet", 7) == 0) af = AF_INET; else { simple_response(sess, IRPD_GETHOST_ERROR, "Unknown address family"); goto untimely; } strncpy(hname, args->iov[0].iov_base, args->iov[0].iov_len); hname[args->iov[0].iov_len] = '\0'; do_gethostbyname2(sess, netdata, hname, af); } untimely: free_args(args); } /* * static void * irpd_gethostbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETHOSTBYADDR verb. */ static void irpd_gethostbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct hostent *ho; char haddr[MAXHOSTNAMELEN]; char tmpaddr[NS_IN6ADDRSZ]; struct arg_s *args; int i; int af; int addrlen; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); args = split_string(rest); if (args->iovlen != 3) { simple_response(sess, IRPD_GETHOST_ERROR, "GETHOSTBYADDR addr afamily"); } else { if (args->iov[0].iov_len >= sizeof haddr) { simple_response(sess, IRPD_GETHOST_ERROR, "Address too long"); } else { strncpy(haddr, args->iov[1].iov_base, args->iov[1].iov_len); haddr[args->iov[1].iov_len] = '\0'; if (strcasecmp(haddr, "af_inet") == 0) { af = AF_INET; addrlen = NS_INADDRSZ; } else if (strcasecmp(haddr, "af_inet6") == 0) { af = AF_INET6; addrlen = NS_IN6ADDRSZ; } else { simple_response(sess, IRPD_GETHOST_ERROR, "Unknown address family"); goto untimely; } strncpy(haddr, args->iov[0].iov_base, args->iov[0].iov_len); haddr[args->iov[0].iov_len] = '\0'; if (inet_pton(af, haddr, tmpaddr) != 1) { simple_response(sess, IRPD_GETHOST_ERROR, "Invalid address"); goto untimely; } ho = gethostbyaddr_p(tmpaddr, addrlen, af, netdata); send_hostent(sess, ho); } } untimely: free_args(args); } /* * static void * irpd_gethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETHOSTENT verb */ static void irpd_gethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct hostent *ho; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); ho = gethostent_p(netdata); send_hostent(sess, ho); } /* * static void * irpd_sethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the SETHOSTENT verb */ static void irpd_sethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct hostent *ho; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); sethostent_p(1, netdata); /* always stayopen */ simple_response(sess, IRPD_GETHOST_SETOK, "ok"); } #ifdef WANT_IRS_PW /* * static void * send_pwent(struct ctl_sess *sess, struct passwd *pw); * Send PW over the wire, or, if PW is NULL, a "No such * user" response. */ static void send_pwent(struct ctl_sess *sess, struct passwd *pw) { if (pw == NULL) { simple_response(sess, IRPD_GETUSER_NONE, "No such user"); } else { struct response_buff *b = newbuffer(0); if (irp_marshall_pw(pw, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETUSER_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall pw\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETUSER_OK, "User found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } /* * static void * irpd_getpwnam(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETPWNAM verb */ static void irpd_getpwnam(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct passwd *pw; char username[64]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETUSER_ERROR, "GETPWNAM username"); } else { if (args->iov[0].iov_len >= sizeof username) { simple_response(sess, IRPD_GETUSER_ERROR, "Name too long"); } else { strncpy(username, args->iov[0].iov_base, args->iov[0].iov_len); username[args->iov[0].iov_len] = '\0'; pw = getpwnam_p(username, netdata); send_pwent(sess, pw); } } free_args(args); } /* * static void * irpd_getpwuid(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETPWUID verb. */ static void irpd_getpwuid(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct passwd *pw; char userid[64]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETUSER_ERROR, "GETPWUID uid"); } else { if (args->iov[0].iov_len >= sizeof userid) { simple_response(sess, IRPD_GETUSER_ERROR, "Name too long"); } else { strncpy(userid, args->iov[0].iov_base, args->iov[0].iov_len); userid[args->iov[0].iov_len] = '\0'; if (!ALLDIGITS(userid)) { simple_response(sess, IRPD_GETUSER_ERROR, "Not a uid"); } else { uid_t uid; long lval; lval = strtol(userid, 0, 10); uid = (uid_t)lval; if ((long)uid != lval) { /* value was too big */ simple_response(sess, IRPD_GETUSER_ERROR, "Not a valid uid"); goto untimely; } pw = getpwuid_p(uid, netdata); send_pwent(sess, pw); } } } untimely: free_args(args); } /* * static void * irpd_getpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implemtnation of the GETPWENT verb. */ static void irpd_getpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct passwd *pw; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); pw = getpwent_p(netdata); send_pwent(sess, pw); } /* * static void * irpd_setpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implemtnation of the SETPWENT verb. */ static void irpd_setpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct passwd *pw; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); setpwent_p(netdata); simple_response(sess, IRPD_GETUSER_SETOK, "ok"); } #endif /* WANT_IRS_PW */ /* * static void * send_nwent(struct ctl_sess *sess, struct nwent *ne); * Sends a nwent structure over the wire, or "No such * network" if NE is NULL. */ static void send_nwent(struct ctl_sess *sess, struct nwent *nw) { if (nw == NULL) { simple_response(sess, IRPD_GETNET_NONE, "No such net"); } else { struct response_buff *b = newbuffer(0); if (irp_marshall_nw(nw, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETNET_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall nw\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETNET_OK, "Network found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } /* * static void * send_netent(struct ctl_sess *sess, struct netent *ne); * Sends a NETENT structure over the wire, or "No such * Network" error if NE is NULL. */ static void send_netent(struct ctl_sess *sess, struct netent *ne) { if (ne == NULL) { simple_response(sess, IRPD_GETNET_NONE, "No such net"); } else { struct response_buff *b = newbuffer(0); if (irp_marshall_ne(ne, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETNET_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall ne\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETNET_OK, "Network found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } /* * static void * irpd_getnetbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of GETNETBYNAME verb. */ static void irpd_getnetbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct netent *ne; struct nwent *nw; char netname[MAXNETNAMELEN]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, nw, sess, IRPD_GETNET_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETNET_ERROR, "GETNETBYNAME name"); } else { if (args->iov[0].iov_len >= sizeof netname) { simple_response(sess, IRPD_GETNET_ERROR, "Name too long"); } else { strncpy(netname, args->iov[0].iov_base, args->iov[0].iov_len); netname[args->iov[0].iov_len] = '\0'; ne = getnetbyname_p(netname, netdata); /* The public interface only gives us a struct netent, and we need a struct nwent that irs uses internally, so we go dig it out ourselves. Yuk */ nw = NULL; if (ne != NULL) { /* Puke. */ INSIST(netdata->nw_last == ne); nw = netdata->nww_last; } send_nwent(sess, nw); } } free_args(args); } /* * static void * irpd_getnetbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); */ static void irpd_getnetbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct netent *ne; struct nwent *nw; char haddr[MAXHOSTNAMELEN]; long tmpaddr; struct arg_s *args; int i; int af; int addrlen; int bits; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, nw, sess, IRPD_GETUSER_ERROR); args = split_string(rest); if (args->iovlen != 3) { simple_response(sess, IRPD_GETNET_ERROR, "GETNETBYADDR addr afamily"); } else { if (args->iov[0].iov_len >= sizeof haddr) { simple_response(sess, IRPD_GETNET_ERROR, "Address too long"); } else { strncpy(haddr, args->iov[1].iov_base, args->iov[1].iov_len); haddr[args->iov[1].iov_len] = '\0'; if (strcasecmp(haddr, "af_inet") == 0) { af = AF_INET; addrlen = NS_INADDRSZ; } else if (strcasecmp(haddr, "af_inet6") == 0) { af = AF_INET6; addrlen = NS_IN6ADDRSZ; /* XXX the interface we use(getnetbyaddr) * can't handle AF_INET6, so for now we * bail. */ simple_response(sess, IRPD_GETNET_ERROR, "AF_INET6 unsupported"); goto untimely; } else { simple_response(sess, IRPD_GETNET_ERROR, "Unknown address family"); goto untimely; } strncpy(haddr, args->iov[0].iov_base, args->iov[0].iov_len); haddr[args->iov[0].iov_len] = '\0'; bits = inet_net_pton(af, haddr, &tmpaddr, sizeof tmpaddr); if (bits < 0) { simple_response(sess, IRPD_GETNET_ERROR, "Invalid address"); goto untimely; } ne = getnetbyaddr_p(tmpaddr, af, netdata); /* The public interface only gives us a struct netent, and we need a struct nwent that irs uses internally, so we go dig it out ourselves. Yuk */ nw = NULL; if (ne != NULL) { /* Puke puke */ INSIST(netdata->nw_last == ne); nw = netdata->nww_last; } send_nwent(sess, nw); } } untimely: free_args(args); } /* * static void * irpd_getnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETNETENT verb. */ static void irpd_getnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct netent *ne; struct nwent *nw; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, nw, sess, IRPD_GETNET_ERROR); ne = getnetent_p(netdata); nw = NULL; if (ne != NULL) { /* triple puke */ INSIST(netdata->nw_last == ne); nw = netdata->nww_last; } send_nwent(sess, nw); } /* * static void * irpd_setnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the SETNETENT verb. */ static void irpd_setnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct netent *ne; struct nwent *nw; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, nw, sess, IRPD_GETNET_ERROR); setnetent_p(1, netdata); /* always stayopen */ simple_response(sess, IRPD_GETNET_SETOK, "ok"); } #ifdef WANT_IRS_GR /* * static void * send_grent(struct ctl_sess *sess, struct group *gr); * Marshall GR and send as body of response. If GR is NULL * then a "No such group" response is sent instead. */ static void send_grent(struct ctl_sess *sess, struct group *gr) { if (gr == NULL) { simple_response(sess, IRPD_GETGROUP_NONE, "No such user"); } else { struct response_buff *b = newbuffer(0); if (irp_marshall_gr(gr, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETGROUP_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall gr\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETGROUP_OK, "Group found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } /* * static void * irpd_getgrnam(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETGRNAM verb. */ static void irpd_getgrnam(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct group *gr; char groupname[64]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETGROUP_ERROR, "GETGRNAM groupname"); } else { if (args->iov[0].iov_len >= sizeof groupname) { simple_response(sess, IRPD_GETGROUP_ERROR, "Name too long"); } else { strncpy(groupname, args->iov[0].iov_base, args->iov[0].iov_len); groupname[args->iov[0].iov_len] = '\0'; gr = getgrnam_p(groupname, netdata); send_grent(sess, gr); } } free_args(args); } /* * static void * irpd_getgrgid(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implentation of the GETGRGID verb. */ static void irpd_getgrgid(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct group *gr; char groupid[64]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETGROUP_ERROR, "GETGRUID gid"); } else { if (args->iov[0].iov_len >= sizeof groupid) { simple_response(sess, IRPD_GETGROUP_ERROR, "Name too long"); } else { strncpy(groupid, args->iov[0].iov_base, args->iov[0].iov_len); groupid[args->iov[0].iov_len] = '\0'; if (!ALLDIGITS(groupid)) { simple_response(sess, IRPD_GETGROUP_ERROR, "Not a gid"); } else { gid_t gid; long lval; lval = strtol(groupid, 0, 10); gid = (gid_t)lval; if ((long)gid != lval) { /* value was too big */ simple_response(sess, IRPD_GETGROUP_ERROR, "Not a valid gid"); goto untimely; } gr = getgrgid_p(gid, netdata); send_grent(sess, gr); } } } untimely: free_args(args); } /* * static void * irpd_getgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the GETGRENT verb. */ static void irpd_getgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct group *gr; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); gr = getgrent_p(netdata); send_grent(sess, gr); } /* * static void * irpd_setgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Implementation of the SETGRENT verb. */ static void irpd_setgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct group *gr; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); setgrent_p(netdata); simple_response(sess, IRPD_GETGROUP_SETOK, "ok"); } #endif /* WANT_IRS_GR */ static void send_servent(struct ctl_sess *sess, struct servent *serv) { if (serv == NULL) { simple_response(sess, IRPD_GETSERVICE_NONE, "No such service"); } else { struct response_buff *b = newbuffer(0); if (irp_marshall_sv(serv, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETSERVICE_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall servent\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETSERVICE_OK, "Service found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } static void irpd_getservbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct servent *serv; char servicename[64]; char protoname[10]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); args = split_string(rest); if (args->iovlen != 3) { /* len includes NULL at end */ simple_response(sess, IRPD_GETSERVICE_ERROR, "GETSERVNAM servicename protocol"); } else { if (args->iov[0].iov_len >= sizeof servicename) { simple_response(sess, IRPD_GETSERVICE_ERROR, "Invalid service name"); } else if (args->iov[1].iov_len >= sizeof protoname) { simple_response(sess, IRPD_GETSERVICE_ERROR, "Invalid protocol name"); } else { strncpy(servicename, args->iov[0].iov_base, args->iov[0].iov_len); servicename[args->iov[0].iov_len] = '\0'; strncpy(protoname, args->iov[1].iov_base, args->iov[1].iov_len); protoname[args->iov[1].iov_len] = '\0'; serv = getservbyname_p(servicename, protoname, netdata); send_servent(sess, serv); } } free_args(args); } /* * static void * irpd_getservbyport(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the GETSERVBYPORT verb. */ static void irpd_getservbyport(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct servent *sv; char portnum[64]; char protoname[10]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); args = split_string(rest); if (args->iovlen != 3) { /* len includes NULL at end */ simple_response(sess, IRPD_GETSERVICE_ERROR, "GETSERVBYPORT port protocol"); } else { if (args->iov[0].iov_len >= sizeof portnum) { simple_response(sess, IRPD_GETSERVICE_ERROR, "Invalid port"); } else if (args->iov[1].iov_len > sizeof protoname - 1) { simple_response(sess, IRPD_GETSERVICE_ERROR, "Invalid protocol"); } else { strncpy(portnum, args->iov[0].iov_base, args->iov[0].iov_len); portnum[args->iov[0].iov_len] = '\0'; strncpy(protoname, args->iov[1].iov_base, args->iov[1].iov_len); protoname[args->iov[1].iov_len] = '\0'; if (!ALLDIGITS(portnum)) { simple_response(sess, IRPD_GETSERVICE_ERROR, "Not a port number"); } else { short port; long lval; lval = strtol(portnum, 0, 10); port = (short)lval; if ((long)port != lval) { /* value was too big */ simple_response(sess, IRPD_GETSERVICE_ERROR, "Not a valid port"); goto untimely; } port = htons(port); sv = getservbyport_p(port, protoname, netdata); send_servent(sess, sv); } } } untimely: free_args(args); } /* * static void * irpd_getservent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the GETSERVENT verb. */ static void irpd_getservent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct servent *sv; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); sv = getservent_p(netdata); send_servent(sess, sv); } /* * static void * irpd_setservent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the SETSERVENT verb. */ static void irpd_setservent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct servent *sv; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); setservent_p(1, netdata); /* always stay open */ simple_response(sess, IRPD_GETSERVICE_SETOK, "ok"); } /* * static void * send_prent(struct ctl_sess *sess, struct protoent *pr); * Send the PR structure over the wire. If PR is NULL, then * the response "No such protocol" is sent instead. */ static void send_prent(struct ctl_sess *sess, struct protoent *pr) { if (pr == NULL) { simple_response(sess, IRPD_GETPROTO_NONE, "No such protocol"); } else { struct response_buff *b = newbuffer(0); if (irp_marshall_pr(pr, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETPROTO_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall pr\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETPROTO_OK, "Protocol found", 0, 0, response_done, b, b->buff, strlen(b->buff)); } } /* * static void * irpd_getprotobyname(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the GETPROTOBYNAME verb. */ static void irpd_getprotobyname(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct protoent *pr; char protoname[64]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETPROTO_ERROR, "GETPROTOBYNAME protocol"); } else { if (args->iov[0].iov_len >= sizeof protoname) { simple_response(sess, IRPD_GETPROTO_ERROR, "Name too long"); } else { strncpy(protoname, args->iov[0].iov_base, args->iov[0].iov_len); protoname[args->iov[0].iov_len] = '\0'; pr = getprotobyname_p(protoname, netdata); send_prent(sess, pr); } } free_args(args); } /* * static void * irpd_getprotobynumber(struct ctl_sctx *ctx, * struct ctl_sess *sess, const struct ctl_verb *verb, * const char *rest, u_int respflags, void *respctx, * void *uctx); * Handle the GETPROTOBYNUMBER verb. */ static void irpd_getprotobynumber(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct protoent *pr; char protonum[64]; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETPROTO_ERROR, "GETPROTOBYNUMBER protocol"); } else { if (args->iov[0].iov_len >= sizeof protonum) { simple_response(sess, IRPD_GETGROUP_ERROR, "Name too long"); } else { strncpy(protonum, args->iov[0].iov_base, args->iov[0].iov_len); protonum[args->iov[0].iov_len] = '\0'; if (!ALLDIGITS(protonum)) { simple_response(sess, IRPD_GETPROTO_ERROR, "Not a protocol number"); } else { int proto; long lval; lval = strtol(protonum, 0, 10); proto = (int)lval; if ((long)proto != lval) { /* value was too big */ simple_response(sess, IRPD_GETPROTO_ERROR, "Not a valid proto"); goto untimely; } pr = getprotobynumber_p(proto, netdata); send_prent(sess, pr); } } } untimely: free_args(args); } /* * static void * irpd_getprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the GETPROTOENT verb. */ static void irpd_getprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct protoent *pr; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); pr = getprotoent_p(netdata); send_prent(sess, pr); } /* * static void * irpd_setprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the SETPROTOENT verb. */ static void irpd_setprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct protoent *pr; size_t need; size_t need_total = 0; struct response_buff *b; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); setprotoent_p(1, netdata); /* always stay open */ simple_response(sess, IRPD_GETPROTO_SETOK, "ok"); } /* * static void * send_pwent(struct ctl_sess *sess, struct passwd *pw); * Send PW over the wire, or, if PW is NULL, a "No such * user" response. */ static void send_ngent(struct ctl_sess *sess, char *host, char *user, char *domain) { struct response_buff *b = newbuffer(0); if (irp_marshall_ng(host, user, domain, &b->buff, &b->bufflen) != 0) { simple_response(sess, IRPD_GETNETGR_ERROR, "Internal error"); logger(ctl_warning, "Cant marshall ng\n"); return; } strcat(b->buff, "\r\n"); ctl_response(sess, IRPD_GETNETGR_OK, "Netgroup entry", 0, 0, response_done, b, b->buff, strlen(b->buff)); } /* * static void * irpd_getnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the GETNETGRENT verb. */ static void irpd_getnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { char netgroupname[64]; struct response_buff *b = NULL; size_t need; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); if (rest != NULL && strlen(rest) > 0) { simple_response(sess, IRPD_GETNETGR_ERROR, "GETNETGRENT"); } else { char *host, *user, *domain; if (getnetgrent_p(&host, &user, &domain, netdata) == 1) { send_ngent(sess, host, user, domain); } else { simple_response(sess, IRPD_GETNETGR_NOMORE, "No more"); } } } /* * static void * irpd_innetgr(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the INNETGR verb. */ static void irpd_innetgr(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct response_buff *b; size_t need; struct net_data *netdata = get_net_data(sess); char *host; char *user; char *domain; INSIST(netdata != NULL); ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); args = split_string(rest); if (args->iovlen != 3) { /* len includes NULL at end */ simple_response(sess, IRPD_GETNETGR_ERROR, "INNETGR netgroup ngentry"); } else { char *grptmp = memget(args->iov[0].iov_len + 1); char *ngtmp = memget(args->iov[1].iov_len + 1); strncpy(grptmp, args->iov[0].iov_base, args->iov[0].iov_len); strncpy(ngtmp, args->iov[1].iov_base, args->iov[1].iov_len); grptmp[args->iov[0].iov_len] = '\0'; ngtmp[args->iov[1].iov_len] = '\0'; if (irp_unmarshall_ng(&host, &user, &domain, ngtmp) != 0) { simple_response(sess, IRPD_GETNETGR_ERROR, "ngentry must be (host,user,domain)"); } else { if (innetgr_p(grptmp, host, user, domain, netdata) == 1) { simple_response(sess, IRPD_GETNETGR_MATCHES, "INNETGR matches"); } else { simple_response(sess, IRPD_GETNETGR_NOMATCH, "INNETGR does not match"); } } memput(grptmp, args->iov[0].iov_len + 1); memput(ngtmp, args->iov[1].iov_len + 1); } untimely: free_args(args); } /* * static void * irpd_setnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the SETNETGRENT verb. */ static void irpd_setnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); args = split_string(rest); if (args->iovlen != 2) { /* len includes NULL at end */ simple_response(sess, IRPD_GETNETGR_ERROR, "setnetgrent netgroup"); } else { setnetgrent_p(rest, netdata); simple_response(sess, IRPD_GETNETGR_SETOK, "setnetgrent ok"); } untimely: free_args(args); } /* * static void * irpd_endnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the ENDNETGRENT verb. */ static void irpd_endnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct arg_s *args; struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); if (rest != NULL && strlen (rest) > 0) { simple_response(sess, IRPD_GETNETGR_ERROR, "endnetgrent netgroup"); } else { endnetgrent_p(netdata); simple_response(sess, IRPD_GETNETGR_SETOK, "endnetgrent ok"); } } /* * static void * irpd_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *param) * Callback for when QUIT respnse is sent out. */ static void irpd_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *param) { struct net_data *netdata = get_net_data(sess); INSIST(netdata != NULL); net_data_destroy(netdata); } /* * static void * irpd_quit(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the QUIT verb. */ static void irpd_quit(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { ctl_response(sess, irpd_quit_ok, "See ya!", CTL_EXIT, NULL, 0 , NULL, NULL, 0); } /* * static void * irpd_help(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle the HELP verb. */ static void irpd_help(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { /* XXX should make this do something better (like include required * arguments. */ ctl_sendhelp(sess, 231); } /* * static void * irpd_accept(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle a new connection. */ static void irpd_accept(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct sockaddr *sa = respctx; char raddr[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; int reject = 1; int response; char *respmsg = NULL; if (sa->sa_family == AF_UNIX) { syslog (LOG_INFO, "New AF_UNIX connection"); reject = 0; } else if (sa->sa_family == AF_INET) { struct sockaddr_in *sin = respctx; static long localhost; static long zero; if (localhost == 0) { /* yes, this could be done with simple arithmetic... */ inet_pton(AF_INET, "127.0.0.1", &localhost); } inet_ntop(AF_INET, &sin->sin_addr, raddr, sizeof raddr); /* we reject INET connections that are not from the local * machine. */ if (sin->sin_addr.s_addr == zero || sin->sin_addr.s_addr == localhost) { reject = 0; syslog(LOG_INFO, "New connection from %s", raddr); } else { syslog(LOG_INFO, "New connection from %s (reject)", raddr); respmsg = "Connections from off host not permitted"; } } else if (sa->sa_family == AF_INET6) { /* XXX should do something intelligent here. */ respmsg = "IPv6 connections not implemented yet."; syslog(LOG_ERR, "Cannot handle AF_INET6 connections yet"); } else { syslog (LOG_ERR, "Unknown peer type: %d", sa->sa_family); respmsg = "What are you???"; } if (reject) { response = IRPD_NOT_WELCOME_CODE; if (respmsg == NULL) { respmsg = "Go away!"; } /* XXX can we be sure that stacked up commands will not be * processed before the control connection is closed??? */ } else { void *ctx = make_cli_ctx(); if (ctx == NULL) { response = IRPD_NOT_WELCOME_CODE; respmsg = "Internal error (client context)"; } else { response = IRPD_WELCOME_CODE; if (respmsg == NULL) { respmsg = "Welcome to IRPD (v 1)"; } ctl_setcsctx(sess, ctx); } } ctl_response(sess, response, respmsg, (reject ? CTL_EXIT : 0), NULL, 0, NULL, NULL, 0); } /* * static void * irpd_abort(struct ctl_sctx *ctx, struct ctl_sess *sess, * const struct ctl_verb *verb, const char *rest, * u_int respflags, void *respctx, void *uctx); * Handle a dropped connection. */ static void irpd_abort(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct net_data *netdata = get_net_data(sess); if (netdata != NULL) net_data_destroy(netdata); } /* * void * response_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *uap) * UAP is the response_buffer passed through to * ctl_response. */ static void response_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *uap) { release_buffer(uap); } /* * static void * logger(enum ctl_severity sev, const char *fmt, ...); * Logging routine called by the ctl_* functions. For now we * just spit everything to stderr. */ static void logger(enum ctl_severity sev, const char *fmt, ...) { char buffer[1024]; va_list ap; int level; if (sev == ctl_debug) return; if (sev == ctl_warning) level = LOG_WARNING; else if (sev == ctl_error) level = LOG_ERR; else { syslog(LOG_CRIT, "Invalid severity: %d", (int)sev); exit(1); } va_start(ap, fmt); #if 0 fprintf(stderr, "irpd: "); vfprintf(stderr, fmt, ap); #else if (vsprintf(buffer, fmt, ap) > (sizeof (buffer) - 1)) { syslog(LOG_CRIT, "Buffer overrun in logger"); abort(); } syslog(level, "%s", buffer); #endif va_end(ap); } /* * static struct response_buff * * newbuffer(u_int length); * Create a structure to hold an allocated buffer. We do * this so we can get the size to deallocate later. * Returns: * Pointer to the structure */ static struct response_buff * newbuffer(u_int length) { struct response_buff *h; h = memget(sizeof *h); if (h == NULL) { errno = ENOMEM; return (NULL); } h->buff = NULL; h->bufflen = length; if (length > 0) { h->buff = memget(h->bufflen); if (h->buff == NULL) { memput(h, sizeof *h); errno = ENOMEM; return (NULL); } memset(h->buff, 0, h->bufflen); } return (h); } /* * static void * release_buffer(struct response_buff *b); * Free up a buffer allocated with newbuffer. */ static void release_buffer(struct response_buff *b) { memset(b->buff, 0, b->bufflen); memput(b->buff, b->bufflen); memset(b, 0, sizeof *b); memput(b, sizeof *b); } /* * static struct arg_s * * split_string(const char *string); * Create an array of iovecs(last one having NULL fields) * pointing into STRING at the non-whitespace sections. The * iovecs are stashed inside a structure so we can get the * size back later at deallocation time. Iovecs are used to avoid * modifying the argument with added nulls. * Returns: * Pointer to the wrapper structure. Must be given to free_args() * when done */ static struct arg_s * split_string(const char *string) { struct iovec *iovs; const char *p; int i, c, iswh; struct arg_s *a; /* count + 1 of the number of runs of non-whitespace. */ for (iswh = 1, i = 1, p = string ; p != NULL && *p ; p++) { if (iswh && !isspace(*p)) { iswh = 0; i++; } else if (!iswh && isspace(*p)) { iswh = 1; } } iovs = memget(sizeof (struct iovec) * i); if (iovs == NULL) { errno = ENOMEM; return (NULL); } a = memget(sizeof *a); if (a == NULL) { errno = ENOMEM; memput(iovs, sizeof (struct iovec) * i); return (NULL); } a->iov = iovs; a->iovlen = i; for (c = 0, p = string ; p != NULL && *p ; c++) { while (isspace(*p)) { p++; } if (*p == '\0') break; iovs[c].iov_base = (void *)p; while (*p && !isspace(*p)) { p++; } iovs[c].iov_len = p - (char *)iovs[c].iov_base; } INSIST(c == i - 1); iovs[c].iov_base = NULL; iovs[c].iov_len = 0; return (a); } /* * static void * free_args(struct arg_s *args); * Free up the argument structure created with * split_string(). */ static void free_args(struct arg_s *args) { memput(args->iov, sizeof (struct iovec) * args->iovlen); memput(args, sizeof *args); } static struct client_ctx * make_cli_ctx(void) { struct client_ctx *p = memget (sizeof *p); if (p == NULL) return (NULL); p->net_data = net_data_create(conffile); return (p); } static void release_cli_ctx(struct client_ctx *ctx) { INSIST(ctx != NULL); INSIST(ctx->net_data != NULL); net_data_destroy(ctx->net_data); memput(ctx, sizeof *ctx); } static struct net_data * get_net_data(struct ctl_sess *sess) { struct client_ctx *ctx = ctl_getcsctx(sess); INSIST(ctx != NULL); INSIST(ctx->net_data != NULL); return (ctx->net_data); } Index: head/contrib/bind/bin/named/db_defs.h =================================================================== --- head/contrib/bind/bin/named/db_defs.h (revision 60940) +++ head/contrib/bind/bin/named/db_defs.h (revision 60941) @@ -1,309 +1,313 @@ /* * from db.h 4.16 (Berkeley) 6/1/90 - * $Id: db_defs.h,v 8.36 1999/08/26 18:42:32 vixie Exp $ + * $Id: db_defs.h,v 8.38 2000/04/21 06:54:01 vixie Exp $ */ /* * Copyright (c) 1985, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Global definitions for data base routines. */ /* max length of data in RR data field */ #define MAXDATA (2*MAXDNAME + 5*INT32SZ) /* max length of data in a TXT RR segment */ #define MAXCHARSTRING 255 #define DB_ROOT_TIMBUF 3600 #define TIMBUF 300 #define DICT_INDEXBITS 24 #define DICT_MAXLENGTH 127 #define DICT_INSERT_P 0x0001 /* Average hash chain depths. */ #define AVGCH_MARSHAL 5 #define AVGCH_NLOOKUP 3 /* Nonstandard maximum class to force better packing. */ #define ZONE_BITS 24 #define CLASS_BITS 8 #define ZONE_MAX ((1<> ((sizeof(v) * 8) - HASHSHIFT))) #define HASHLOWER(c) ((isascii(c) && isupper(c)) ? tolower(c) : (c)) #define HASHIMILATE(v,c) ((v) = (HASHROTATE(v)) + (HASHLOWER(c) & HASHMASK)) #define TSIG_BUF_SIZE 640 #define TSIG_SIG_SIZE 20 struct tsig_record { u_int8_t sig[TSIG_SIG_SIZE]; struct dst_key *key; int siglen; }; struct sig_record { u_int16_t sig_type_n; u_int8_t sig_alg_n, sig_labels_n; u_int32_t sig_ottl_n, sig_exp_n, sig_time_n; u_int16_t sig_keyid_n; }; /* This is the wire format size of "struct sig_record", i.e., no padding. */ #define SIG_HDR_SIZE 18 struct dnode { struct databuf *dp; struct dnode *dn_next; int line; char *file; }; typedef struct dnode * dlist; struct db_rrset { dlist rr_list; dlist rr_sigs; char *rr_name; int16_t rr_class; int16_t rr_type; struct db_rrset *rr_next; }; #define DBHASHSIZE(s) (sizeof(struct hashbuf) + \ (s-1) * sizeof(struct db_rrset *)) #define SIG_COVERS(dp) (ns_get16(dp->d_data)) /* * Flags to updatedb */ #define DB_NODATA 0x01 /* data should not exist */ #define DB_MEXIST 0x02 /* data must exist */ #define DB_DELETE 0x04 /* delete data if it exists */ #define DB_NOTAUTH 0x08 /* must not update authoritative data */ #define DB_NOHINTS 0x10 /* don't reflect update in fcachetab */ #define DB_PRIMING 0x20 /* is this update the result of priming? */ #define DB_MERGE 0x40 /* make no control on rr in db_update (for ixfr) */ #define DB_REPLACE 0x80 /* replace data if it exists */ #define DB_Z_CACHE 0 /* cache-zone-only db_dump() */ #define DB_Z_ALL 65535 /* normal db_dump() */ #define DB_Z_SPECIAL(z) ((z) == DB_Z_CACHE || (z) == DB_Z_ALL) /* * Error return codes */ #define OK 0 #define NONAME (-1) #define NOCLASS (-2) #define NOTYPE (-3) #define NODATA (-4) #define DATAEXISTS (-5) #define NODBFILE (-6) #define TOOMANYZONES (-7) #define GOODDB (-8) #define NEWDB (-9) #define AUTH (-10) #ifdef BIND_UPDATE #define SERIAL (-11) #endif #define CNAMEANDOTHER (-12) #define DNSSECFAIL (-13) /* db_set_update */ /* * getnum() options */ #define GETNUM_NONE 0x00 /* placeholder */ #define GETNUM_SERIAL 0x01 /* treat as serial number */ #define GETNUM_SCALED 0x02 /* permit "k", "m" suffixes, scale result */ /* * db_load() options */ #define ISNOTIXFR 0 #define ISIXFR 1 #define ISAXFRIXFR 2 /* * Database access abstractions. */ #define foreach_rr(dp, np, ty, cl, zn) \ for ((dp) = (np)->n_data; (dp) != NULL; (dp) = (dp)->d_next) \ if (!match(dp, (cl), (ty))) \ continue; \ else if (((zn) == DB_Z_CACHE) \ ? stale(dp) \ : (zn) != (dp)->d_zone) \ continue; \ else if ((dp)->d_rcode) \ continue; \ else \ /* Caller code follows in sequence. */ #define DRCNTINC(x) \ do { \ if (++((x)->d_rcnt) == 0) \ ns_panic(ns_log_db, 1, "++d_rcnt == 0"); \ } while (0) #define DRCNTDEC(x) \ do { \ if (((x)->d_rcnt)-- == 0) \ ns_panic(ns_log_db, 1, "d_rcnt-- == 0"); \ } while (0) + +#define ISVALIDGLUE(xdp) ((xdp)->d_type == T_NS || (xdp)->d_type == T_A \ + || (xdp)->d_type == T_AAAA || (xdp)->d_type == ns_t_a6) + Index: head/contrib/bind/bin/named/db_dump.c =================================================================== --- head/contrib/bind/bin/named/db_dump.c (revision 60940) +++ head/contrib/bind/bin/named/db_dump.c (revision 60941) @@ -1,663 +1,677 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_dump.c 4.33 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: db_dump.c,v 8.40 1999/10/13 16:39:01 vixie Exp $"; +static const char rcsid[] = "$Id: db_dump.c,v 8.43 2000/04/21 06:54:01 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static const char *MkCredStr(int); /* * Dump current data base in a format similar to RFC 883. */ void doadump() { FILE *fp; ns_notice(ns_log_db, "dumping nameserver data"); if ((fp = write_open(server_options->dump_filename)) == NULL) return; gettime(&tt); fprintf(fp, "; Dumped at %s", ctimel(tt.tv_sec)); if (zones != NULL && nzones != 0) zt_dump(fp); + if (fwddata != NULL && fwddata_count != 0) + fwd_dump(fp); fputs( "; Note: Cr=(auth,answer,addtnl,cache) tag only shown for non-auth RR's\n", fp); fputs( "; Note: NT=milliseconds for any A RR which we've used as a nameserver\n", fp); fprintf(fp, "; --- Cache & Data ---\n"); if (hashtab != NULL) (void) db_dump(hashtab, fp, DB_Z_ALL, ""); fprintf(fp, "; --- Hints ---\n"); if (fcachetab != NULL) (void) db_dump(fcachetab, fp, DB_Z_ALL, ""); (void) my_fclose(fp); ns_notice(ns_log_db, "finished dumping nameserver data"); } int zt_dump(FILE *fp) { struct zoneinfo *zp; fprintf(fp, ";; ++zone table++\n"); for (zp = &zones[0]; zp < &zones[nzones]; zp++) { char *pre, buf[64]; u_int cnt; if (!zp->z_origin) continue; fprintf(fp, "; %s (type %d, class %d, source %s)\n", zp->z_origin ? (*zp->z_origin ? zp->z_origin : ".") : "Nil", zp->z_type, zp->z_class, zp->z_source ? zp->z_source : "Nil"); fprintf(fp, ";\ttime=%lu, lastupdate=%lu, serial=%u,\n", (u_long)zp->z_time, (u_long)zp->z_lastupdate, zp->z_serial); fprintf(fp, ";\trefresh=%u, retry=%u, expire=%u, minimum=%u\n", zp->z_refresh, zp->z_retry, zp->z_expire, zp->z_minimum); fprintf(fp, ";\tftime=%lu, xaddrcnt=%d, state=%04x, pid=%d\n", (u_long)zp->z_ftime, zp->z_xaddrcnt, zp->z_flags, (int)zp->z_xferpid); sprintf(buf, ";\tz_addr[%d]: ", zp->z_addrcnt); pre = buf; for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { fprintf(fp, "%s[%s]", pre, inet_ntoa(zp->z_addr[cnt])); pre = ", "; } if (zp->z_addrcnt) fputc('\n', fp); if (zp->z_axfr_src.s_addr != 0) fprintf(fp, ";\tupdate source [%s]\n", inet_ntoa(zp->z_axfr_src)); } fprintf(fp, ";; --zone table--\n"); return (0); } +int +fwd_dump(FILE *fp) { + int i; + fprintf(fp, ";; ++forwarders table++\n"); + for (i=0;ifwdaddr.sin_addr), + fwddata[i]->nsdata->d_nstime); + } + fprintf(fp, ";; --forwarders table--\n"); + return (0); +} int db_dump(struct hashbuf *htp, FILE *fp, int zone, char *origin) { struct databuf *dp = NULL; struct namebuf *np; struct namebuf **npp, **nppend; char dname[MAXDNAME]; u_int32_t n; int j, i, found_data, tab, printed_origin; u_char *cp, *end; const char *proto, *sep; int16_t type; u_int16_t keyflags; u_char *sigdata, *certdata; u_char *savecp; char temp_base64[NS_MD5RSA_MAX_BASE64]; found_data = 0; printed_origin = 0; npp = htp->h_tab; nppend = npp + htp->h_size; while (npp < nppend) { for (np = *npp++; np != NULL; np = np->n_next) { if (np->n_data == NULL) continue; /* Blecch - can't tell if there is data here for the * right zone, so can't print name yet */ found_data = 0; /* we want a snapshot in time... */ for (dp = np->n_data; dp != NULL; dp = dp->d_next) { /* Is the data for this zone? */ if (zone != DB_Z_ALL && dp->d_zone != zone) continue; /* XXX why are we not calling stale() here? */ if (dp->d_zone == DB_Z_CACHE && dp->d_ttl <= (u_int32_t)tt.tv_sec && (dp->d_flags & DB_F_HINT) == 0) continue; if (!printed_origin) { fprintf(fp, "$ORIGIN %s.\n", origin); printed_origin++; } tab = 0; if (dp->d_rcode == NXDOMAIN || dp->d_rcode == NOERROR_NODATA) { fputc(';', fp); } else if (found_data == 0 || found_data == 1) { found_data = 2; } if (found_data == 0 || found_data == 2) { if (NAME(*np)[0] == '\0') { if (origin[0] == '\0') fprintf(fp, ".\t"); else fprintf(fp, ".%s.\t", origin); /* ??? */ } else fprintf(fp, "%s\t", NAME(*np)); if (NAMELEN(*np) < (unsigned)8) tab = 1; found_data++; } else { (void) putc('\t', fp); tab = 1; } if (dp->d_zone == DB_Z_CACHE) { if (dp->d_flags & DB_F_HINT && (int32_t)(dp->d_ttl - tt.tv_sec) < DB_ROOT_TIMBUF) fprintf(fp, "%d\t", DB_ROOT_TIMBUF); else fprintf(fp, "%d\t", (int)(dp->d_ttl - tt.tv_sec)); } else if (dp->d_ttl != USE_MINIMUM) - fprintf(fp, "%d\t", (int)dp->d_ttl); + fprintf(fp, "%u\t", dp->d_ttl); else - fprintf(fp, "%d\t", + fprintf(fp, "%u\t", zones[dp->d_zone].z_minimum); fprintf(fp, "%s\t%s\t", p_class(dp->d_class), p_type(dp->d_type)); cp = (u_char *)dp->d_data; sep = "\t;"; type = dp->d_type; if (dp->d_rcode == NXDOMAIN || dp->d_rcode == NOERROR_NODATA) { #ifdef RETURNSOA if (dp->d_size == 0) { #endif fprintf(fp, "%s%s-$", (dp->d_rcode == NXDOMAIN) ?"NXDOMAIN" :"NODATA", sep); goto eoln; #ifdef RETURNSOA } else { type = T_SOA; } #endif } /* * Print type specific data */ /* XXX why are we not using ns_sprintrr() here? */ switch (type) { case T_A: switch (dp->d_class) { case C_IN: case C_HS: fputs(inet_ntoa(ina_get(cp)), fp); break; } if (dp->d_nstime) { fprintf(fp, "%sNT=%d", sep, dp->d_nstime); sep = " "; } break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: fprintf(fp, "%s.", cp); break; case T_NS: cp = (u_char *)dp->d_data; if (cp[0] == '\0') fprintf(fp, ".\t"); else fprintf(fp, "%s.", cp); break; case T_HINFO: case T_ISDN: { char buf[256]; if ((n = *cp++) != '\0') { memcpy(buf, cp, n); buf[n] = '\0'; fprintf(fp, "\"%.*s\"", (int)n, buf); cp += n; } else fprintf(fp, "\"\""); if ((n = *cp++) != '\0') { memcpy(buf, cp, n); buf[n] = '\0'; fprintf(fp, " \"%.*s\"", (int)n, buf); } else fprintf(fp, " \"\""); break; } case T_SOA: fprintf(fp, "%s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s. (\n", cp); #if defined(RETURNSOA) if (dp->d_rcode) fputs(";", fp); #endif cp += strlen((char *)cp) + 1; NS_GET32(n, cp); fprintf(fp, "\t\t%u", n); NS_GET32(n, cp); fprintf(fp, " %u", n); NS_GET32(n, cp); fprintf(fp, " %u", n); NS_GET32(n, cp); fprintf(fp, " %u", n); NS_GET32(n, cp); fprintf(fp, " %u )", n); #if defined(RETURNSOA) if (dp->d_rcode) { fprintf(fp,";%s.;%s%s-$",cp, (dp->d_rcode == NXDOMAIN) ? "NXDOMAIN" : "NODATA", sep); } #endif break; case T_MX: case T_AFSDB: case T_RT: NS_GET16(n, cp); fprintf(fp, "%u", n); fprintf(fp, " %s.", cp); break; case T_PX: NS_GET16(n, cp); fprintf(fp, "%u", n); fprintf(fp, " %s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s.", cp); break; case T_X25: if ((n = *cp++) != '\0') fprintf(fp, " \"%.*s\"", (int)n, cp); else fprintf(fp, " \"\""); break; case T_TXT: end = (u_char *)dp->d_data + dp->d_size; while (cp < end) { (void) putc('"', fp); if ((n = *cp++) != '\0') { for (j = n ; j > 0 && cp < end ; j--) { if (*cp == '\n' || *cp == '"' || *cp == '\\') (void) putc('\\', fp); (void) putc(*cp++, fp); } } (void) putc('"', fp); if (cp < end) (void) putc(' ', fp); } break; case T_NSAP: (void) fputs(inet_nsap_ntoa(dp->d_size, dp->d_data, NULL), fp); break; case T_AAAA: { char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255" ]; (void) fputs(inet_ntop(AF_INET6, dp->d_data, t, sizeof t), fp); break; } case T_LOC: { char t[256]; (void) fputs(loc_ntoa(dp->d_data, t), fp); break; } case T_NAPTR: { u_int32_t order, preference; NS_GET16(order, cp); fprintf(fp, "%u", order); NS_GET16(preference, cp); fprintf(fp, "%u", preference); if ((n = *cp++) != 0) { fprintf(fp, "\"%.*s\"", (int)n, cp); cp += n; } if ((n = *cp++) != 0) { fprintf(fp, "\"%.*s\"", (int)n, cp); cp += n; } if ((n = *cp++) != 0) { fprintf(fp, " \"%.*s\"", (int)n, cp); cp += n; } fprintf(fp, " %s.", cp); break; } case T_SRV: { u_int priority, weight, port; NS_GET16(priority, cp); NS_GET16(weight, cp); NS_GET16(port, cp); fprintf(fp, "\t%u %u %u %s.", priority, weight, port, cp); break; } case T_WKS: fputs(inet_ntoa(ina_get(cp)), fp); cp += INADDRSZ; proto = protocolname(*cp); cp += sizeof(char); fprintf(fp, " %s ", proto); i = 0; while(cp < (u_char *)dp->d_data + dp->d_size) { j = *cp++; do { if (j & 0200) fprintf(fp, " %s", servicename(i, proto)); j <<= 1; } while (++i & 07); } break; case T_MINFO: case T_RP: fprintf(fp, "%s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s.", cp); break; case T_KEY: savecp = cp; /* save the beginning */ /*>>> Flags (unsigned_16) */ NS_GET16(keyflags,cp); fprintf(fp, "0x%04x ", keyflags); /*>>> Protocol (8-bit decimal) */ fprintf(fp, "%3u ", *cp++); /*>>> Algorithm id (8-bit decimal) */ fprintf(fp, "%3u ", *cp++); /*>>> Public-Key Data (multidigit BASE64) */ /* containing ExponentLen, Exponent, and Modulus */ i = b64_ntop(cp, dp->d_size - (cp - savecp), temp_base64, sizeof temp_base64); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case T_SIG: sigdata = cp; /* RRtype (char *) */ NS_GET16(n,cp); fprintf(fp, "%s ", p_type(n)); /* Algorithm id (8-bit decimal) */ fprintf(fp, "%d ", *cp++); /* Labels (8-bit decimal) */ fprintf(fp, "%d ", *cp++); /* OTTL (u_long) */ NS_GET32(n, cp); fprintf(fp, "%u ", n); /* Texp (u_long) */ NS_GET32(n, cp); fprintf(fp, "%s ", p_secstodate (n)); /* Tsig (u_long) */ NS_GET32(n, cp); fprintf(fp, "%s ", p_secstodate (n)); /* Kfootprint (unsigned_16) */ NS_GET16(n, cp); fprintf(fp, "%u ", n); /* Signer's Name (char *) */ fprintf(fp, "%s ", cp); cp += strlen((char *)cp) + 1; /* Signature (base64 of any length) */ i = b64_ntop(cp, dp->d_size - (cp - sigdata), temp_base64, sizeof temp_base64); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case T_NXT: fprintf(fp, "%s.", cp); n = strlen ((char *)cp) + 1; cp += n; i = 8 * (dp->d_size - n); /* How many bits? */ for (n = 0; n < (u_int32_t)i; n++) { if (NS_NXT_BIT_ISSET(n, cp)) fprintf(fp," %s", p_type(n)); } break; case ns_t_cert: certdata = cp; NS_GET16(n,cp); fprintf(fp, "%d ", n); /* cert type */ NS_GET16(n,cp); fprintf(fp, "%d %d ", n, *cp++); /* tag & alg */ /* Certificate (base64 of any length) */ i = b64_ntop(cp, dp->d_size - (cp - certdata), temp_base64, sizeof(temp_base64)); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; default: fprintf(fp, "%s?d_type=%d?", sep, dp->d_type); sep = " "; } if (dp->d_cred < DB_C_ZONE) { fprintf(fp, "%sCr=%s", sep, MkCredStr(dp->d_cred)); sep = " "; } else { fprintf(fp, "%sCl=%d", sep, dp->d_clev); sep = " "; } if ((dp->d_flags & DB_F_LAME) != 0) { time_t when; getname(np, dname, sizeof(dname)); when = db_lame_find(dname, dp); if (when != 0 && when > tt.tv_sec) { fprintf(fp, "%sLAME=%d", sep, when - tt.tv_sec); sep = " "; } } eoln: if (dp->d_ns != NULL){ fprintf(fp, "%s[%s]", sep, inet_ntoa(dp->d_ns->addr)); sep = " "; } putc('\n', fp); } } } if (ferror(fp)) return (NODBFILE); npp = htp->h_tab; nppend = npp + htp->h_size; while (npp < nppend) { for (np = *npp++; np != NULL; np = np->n_next) { if (np->n_hash == NULL) continue; getname(np, dname, sizeof(dname)); if (db_dump(np->n_hash, fp, zone, dname) == NODBFILE) return (NODBFILE); } } return (OK); } static const char * MkCredStr(int cred) { static char badness[20]; switch (cred) { case DB_C_ZONE: return "zone"; case DB_C_AUTH: return "auth"; case DB_C_ANSWER: return "answer"; case DB_C_ADDITIONAL: return "addtnl"; case DB_C_CACHE: return "cache"; default: break; } sprintf(badness, "?%d?", cred); return (badness); } Index: head/contrib/bind/bin/named/db_func.h =================================================================== --- head/contrib/bind/bin/named/db_func.h (revision 60940) +++ head/contrib/bind/bin/named/db_func.h (revision 60941) @@ -1,213 +1,213 @@ /* * Copyright (c) 1985, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* db_proc.h - prototypes for functions in db_*.c * - * $Id: db_func.h,v 8.40 1999/10/07 08:24:06 vixie Exp $ + * $Id: db_func.h,v 8.42 2000/04/21 06:54:02 vixie Exp $ */ /* ++from db_update.c++ */ extern int db_update(const char *name, struct databuf *odp, struct databuf *newdp, struct databuf **savedpp, int flags, struct hashbuf *htp, struct sockaddr_in from), - db_cmp(const struct databuf *, const struct databuf *), + db_cmp(const struct databuf *, const struct databuf *), findMyZone(struct namebuf *np, int class); void fixttl(struct databuf *dp); /* --from db_update.c-- */ /* ++from db_save.c++ */ extern struct namebuf *savename(const char *, int); extern struct databuf *savedata(int, int, u_int32_t, u_char *, int); extern struct hashbuf *savehash(struct hashbuf *); /* --from db_save.c-- */ /* ++from db_dump.c++ */ extern int db_dump(struct hashbuf *, FILE *, int, char *), zt_dump(FILE *); extern void doadump(void); /* --from db_dump.c-- */ /* ++from db_load.c++ */ extern int makename_ok(char *name, const char *origin, int class, struct zoneinfo *zp, enum transport transport, enum context context, const char *owner, const char *filename, int lineno, int size); extern void endline(FILE *); extern int getword(char *, size_t, FILE *, int), getttl(FILE *, const char *, int, u_int32_t *, int *), getnum(FILE *, const char *, int), db_load(const char *, const char *, struct zoneinfo *, const char *, int); extern int getnonblank(FILE *, const char *), getservices(int, char *, FILE *, const char *); extern char getprotocol(FILE *, const char *); extern int makename(char *, const char *, int); extern void db_err(int, char *, int, const char *, int); extern int parse_sec_rdata(char *inp, int inp_len, int inp_full, u_char *data, int data_len, FILE *fp, struct zoneinfo *zp, char *domain, u_int32_t ttl, int type, enum context context, enum transport transport, char **errmsg); /* --from db_load.c-- */ /* ++from db_glue.c++ */ extern void buildservicelist(void), destroyservicelist(void), buildprotolist(void), destroyprotolist(void), getname(struct namebuf *, char *, int); extern int servicenumber(const char *), protocolnumber(const char *), get_class(const char *); extern u_int nhash(const char *); extern const char *protocolname(int), *servicename(u_int16_t, const char *); #ifndef BSD extern int getdtablesize(void); #endif extern struct databuf *rm_datum(struct databuf *, struct namebuf *, struct databuf *, struct databuf **); extern struct namebuf *rm_name(struct namebuf *, struct namebuf **, struct namebuf *); extern void rm_hash(struct hashbuf *); extern void db_freedata(struct databuf *); extern void db_lame_add(char *zone, char *server, time_t when); extern time_t db_lame_find(char *zone, struct databuf *dp); extern void db_lame_clean(void); extern void db_lame_destroy(void); /* --from db_glue.c-- */ /* ++from db_lookup.c++ */ extern struct namebuf *nlookup(const char *, struct hashbuf **, const char **, int); extern struct namebuf *np_parent __P((struct namebuf *)); extern int match(struct databuf *, int, int), nxtmatch(const char *, struct databuf *, struct databuf *), rrmatch(const char *, struct databuf *, struct databuf *); /* --from db_lookup.c-- */ /* ++from db_ixfr.c++ */ -struct ns_updrec * ixfr_get_change_list(struct zoneinfo *, u_int32_t, +extern ns_deltalist * ixfr_get_change_list(struct zoneinfo *, u_int32_t, u_int32_t); int ixfr_have_log(struct zoneinfo *, u_int32_t, u_int32_t); /* --from db_ixfr.c++ */ /* ++from db_sec.c++ */ int add_trusted_key(const char *name, const int flags, const int proto, const int alg, const char *str); int db_set_update(char *name, struct databuf *dp, void **state, int flags, struct hashbuf **htp, struct sockaddr_in from, int *rrcount, int line, const char *file); /* --from db_sec.c-- */ /* ++from db_tsig.c++ */ char * tsig_alg_name(int value); int tsig_alg_value(char *name); struct dst_key * tsig_key_from_addr(struct in_addr addr); struct tsig_record * new_tsig(struct dst_key *key, u_char *sig, int siglen); void free_tsig(struct tsig_record *tsig); /* --from db_tsig.c-- */ Index: head/contrib/bind/bin/named/db_glob.h =================================================================== --- head/contrib/bind/bin/named/db_glob.h (revision 60940) +++ head/contrib/bind/bin/named/db_glob.h (revision 60941) @@ -1,103 +1,104 @@ /* * from db.h 4.16 (Berkeley) 6/1/90 - * $Id: db_glob.h,v 8.12 1999/08/08 21:10:01 vixie Exp $ + * $Id: db_glob.h,v 8.14 2000/04/21 06:54:02 vixie Exp $ */ /* * Copyright (c) 1985, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Global variables for data base routines. */ /* ONE_WEEK maximum ttl */ DECL u_int max_cache_ttl INIT(7*24*60*60); /* no minimum ttl */ DECL u_int min_cache_ttl INIT(0); /* current line number */ DECL int lineno INIT(0); /* root hash table */ DECL struct hashbuf *hashtab INIT(NULL); /* hash table of cache read from file */ DECL struct hashbuf *fcachetab INIT(NULL); /* state of ns_reload() and ns_reconfig(). */ DECL int reloading INIT(0); DECL int reconfiging INIT(0); +DECL int noexpired INIT(0); DECL const int hashsizes[] #ifdef MAIN_PROGRAM = { 2, 11, 113, 337, 977, 2053, 4073, 8011, 16001, 99887, 0 } #endif ; Index: head/contrib/bind/bin/named/db_glue.c =================================================================== --- head/contrib/bind/bin/named/db_glue.c (revision 60940) +++ head/contrib/bind/bin/named/db_glue.c (revision 60941) @@ -1,656 +1,656 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_glue.c 4.4 (Berkeley) 6/1/90"; -static const char rcsid[] = "$Id: db_glue.c,v 8.39 1999/10/15 19:48:57 vixie Exp $"; +static const char rcsid[] = "$Id: db_glue.c,v 8.40 2000/04/21 06:54:02 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct valuelist { struct valuelist * next; struct valuelist * prev; char * name; char * proto; int port; }; static struct valuelist *servicelist, *protolist; void buildservicelist() { struct servent *sp; struct valuelist *slp; #ifdef MAYBE_HESIOD setservent(0); #else setservent(1); #endif while ((sp = getservent()) != NULL) { slp = (struct valuelist *)memget(sizeof(struct valuelist)); if (!slp) panic("memget(servent)", NULL); slp->name = savestr(sp->s_name, 1); slp->proto = savestr(sp->s_proto, 1); slp->port = ntohs((u_int16_t)sp->s_port); /* host byt order */ slp->next = servicelist; slp->prev = NULL; if (servicelist) servicelist->prev = slp; servicelist = slp; } endservent(); } void destroyservicelist() { struct valuelist *slp, *slp_next; for (slp = servicelist; slp != NULL; slp = slp_next) { slp_next = slp->next; freestr(slp->name); freestr(slp->proto); memput(slp, sizeof *slp); } servicelist = NULL; } void buildprotolist() { struct protoent *pp; struct valuelist *slp; #ifdef MAYBE_HESIOD setprotoent(0); #else setprotoent(1); #endif while ((pp = getprotoent()) != NULL) { slp = (struct valuelist *)memget(sizeof(struct valuelist)); if (!slp) panic("memget(protoent)", NULL); slp->name = savestr(pp->p_name, 1); slp->port = pp->p_proto; /* host byte order */ slp->next = protolist; slp->prev = NULL; if (protolist) protolist->prev = slp; protolist = slp; } endprotoent(); } void destroyprotolist() { struct valuelist *plp, *plp_next; for (plp = protolist; plp != NULL; plp = plp_next) { plp_next = plp->next; freestr(plp->name); memput(plp, sizeof *plp); } protolist = NULL; } static int findservice(const char *s, struct valuelist **list) { struct valuelist *lp = *list; int n; for (; lp != NULL; lp = lp->next) if (strcasecmp(lp->name, s) == 0) { if (lp != *list) { lp->prev->next = lp->next; if (lp->next) lp->next->prev = lp->prev; (*list)->prev = lp; lp->next = *list; *list = lp; } return (lp->port); /* host byte order */ } if (sscanf(s, "%d", &n) != 1 || n <= 0) n = -1; return (n); } /* * Convert service name or (ascii) number to int. */ int servicenumber(const char *p) { return (findservice(p, &servicelist)); } /* * Convert protocol name or (ascii) number to int. */ int protocolnumber(const char *p) { return (findservice(p, &protolist)); } static struct servent * cgetservbyport(u_int16_t port, const char *proto) { /* Host byte order. */ struct valuelist **list = &servicelist; struct valuelist *lp = *list; static struct servent serv; port = ntohs(port); for (; lp != NULL; lp = lp->next) { if (port != (u_int16_t)lp->port) /* Host byte order. */ continue; if (strcasecmp(lp->proto, proto) == 0) { if (lp != *list) { lp->prev->next = lp->next; if (lp->next) lp->next->prev = lp->prev; (*list)->prev = lp; lp->next = *list; *list = lp; } serv.s_name = lp->name; serv.s_port = htons((u_int16_t)lp->port); serv.s_proto = lp->proto; return (&serv); } } return (0); } static struct protoent * cgetprotobynumber(int proto) { /* Host byte order. */ struct valuelist **list = &protolist; struct valuelist *lp = *list; static struct protoent prot; for (; lp != NULL; lp = lp->next) if (lp->port == proto) { /* Host byte order. */ if (lp != *list) { lp->prev->next = lp->next; if (lp->next) lp->next->prev = lp->prev; (*list)->prev = lp; lp->next = *list; *list = lp; } prot.p_name = lp->name; prot.p_proto = lp->port; /* Host byte order. */ return (&prot); } return (0); } const char * protocolname(int num) { static char number[8]; struct protoent *pp; pp = cgetprotobynumber(num); if (pp == 0) { (void) sprintf(number, "%d", num); return (number); } return (pp->p_name); } const char * servicename(u_int16_t port, const char *proto) { /* Host byte order. */ static char number[8]; struct servent *ss; ss = cgetservbyport(htons(port), proto); if (ss == 0) { (void) sprintf(number, "%d", port); return (number); } return (ss->s_name); } static struct map map_class[] = { { "in", C_IN }, { "chaos", C_CHAOS }, { "hs", C_HS }, { NULL, 0 } }; int get_class(const char *class) { const struct map *mp; if (isdigit(*class)) return (atoi(class)); for (mp = map_class; mp->token != NULL; mp++) if (strcasecmp(class, mp->token) == 0) return (mp->val); return (C_IN); } /* rm_datum(dp, np, pdp, savedpp) * remove datum 'dp' from name 'np'. pdp is previous data pointer. * if savedpp is not NULL, and compiled with BIND_UPDATE, save * datum dp there rather than freeing the memory (caller will take * care of freeing it) * return value: * "next" field from removed datum, suitable for relinking */ struct databuf * rm_datum(struct databuf *dp, struct namebuf *np, struct databuf *pdp, struct databuf **savedpp) { struct databuf *ndp = dp->d_next; ns_debug(ns_log_db, 3, "rm_datum(%lx, %lx, %lx, %lx) -> %lx", (u_long)dp, (u_long)np->n_data, (u_long)pdp, (u_long)savedpp, (u_long)ndp); if ((dp->d_flags & DB_F_ACTIVE) == 0) panic("rm_datum: DB_F_ACTIVE not set", NULL); if (pdp == NULL) np->n_data = ndp; else pdp->d_next = ndp; #ifdef BIND_UPDATE if (savedpp != NULL) { /* mark deleted or pending deletion */ dp->d_mark |= D_MARK_DELETED; dp->d_next = *savedpp; *savedpp = dp; } else dp->d_next = NULL; #else dp->d_next = NULL; #endif dp->d_flags &= ~DB_F_ACTIVE; DRCNTDEC(dp); if (dp->d_rcnt) { #ifdef DEBUG int32_t ii; #endif switch(dp->d_type) { case T_NS: ns_debug(ns_log_db, 3, "rm_datum: %s rcnt = %d", dp->d_data, dp->d_rcnt); break; #ifdef DEBUG case T_A: memcpy(&ii, dp->d_data, sizeof ii); ns_debug(ns_log_db, 3, "rm_datum: %08.8X rcnt = %d", ii, dp->d_rcnt); break; #endif default: ns_debug(ns_log_db, 3, "rm_datum: rcnt = %d", dp->d_rcnt); } } else #ifdef BIND_UPDATE if (savedpp == NULL) #endif db_freedata(dp); return (ndp); } /* rm_name(np, he, pnp) * remove name 'np' from parent 'pp'. pnp is previous name pointer. * return value: * "next" field from removed name, suitable for relinking. */ struct namebuf * rm_name(struct namebuf *np, struct namebuf **pp, struct namebuf *pnp) { struct namebuf *nnp = np->n_next; const char *msg; /* verify */ if ( (np->n_data && (msg = "data")) || (np->n_hash && (msg = "hash")) ) { ns_panic(ns_log_db, 1, "rm_name(%#x(%s)): non-nil %s pointer", np, NAME(*np), msg); } /* unlink */ if (pnp) pnp->n_next = nnp; else *pp = nnp; /* deallocate */ memput(np, NAMESIZE(NAMELEN(*np))); /* done */ return (nnp); } void rm_hash(struct hashbuf *htp) { REQUIRE(htp != NULL); REQUIRE(htp->h_cnt == 0); memput(htp, HASHSIZE(htp->h_size)); } /* * Get the domain name of 'np' and put in 'buf'. Bounds checking is done. */ void getname(struct namebuf *np, char *buf, int buflen) { char *cp; int i; cp = buf; while (np != NULL) { i = (int) NAMELEN(*np); if (i + 1 >= buflen) { *cp = '\0'; ns_info(ns_log_db, "domain name too long: %s...", buf); strcpy(buf, "Name_Too_Long"); return; } if (cp != buf) *cp++ = '.'; memcpy(cp, NAME(*np), i); cp += i; buflen -= i + 1; np = np->n_parent; } *cp = '\0'; } /* u_int * nhash(name) * compute hash for this name and return it; ignore case differences * note: * this logic is intended to produce the same result as nlookup()'s. */ u_int nhash(const char *name) { u_char ch; u_int hval; hval = 0; while ((ch = (u_char)*name++) != (u_char)'\0') HASHIMILATE(hval, ch); return (hval); } void db_freedata(struct databuf *dp) { int bytes = DATASIZE(dp->d_size); if (dp->d_rcnt != 0) panic("db_freedata: d_rcnt != 0", NULL); if ((dp->d_flags & (DB_F_ACTIVE|DB_F_FREE)) != 0) panic("db_freedata: %s set", (dp->d_flags & DB_F_FREE) != 0 ? "DB_F_FREE" : "DB_F_ACTIVE"); if (dp->d_next != NULL) panic("db_free: d_next != NULL", NULL); dp->d_flags |= DB_F_FREE; memput(dp, bytes); } struct lame_hash { struct lame_hash *next; char *zone; char *server; time_t when; unsigned int hval; } **lame_hash = NULL; static int lame_hash_size = 0; static int lame_hash_cnt = 0; void db_lame_add(char *zone, char *server, time_t when) { unsigned int hval = nhash(zone); struct lame_hash *last, *this; struct lame_hash **new; int n; int newsize; db_lame_clean(); /* grow / initalise hash table */ if (lame_hash_cnt >= lame_hash_size) { if (lame_hash_size == 0) newsize = hashsizes[0]; else { for (n = 0; (newsize = hashsizes[n++]) != 0; (void)NULL) if (lame_hash_size == newsize) { newsize = hashsizes[n]; break; } if (newsize == 0) newsize = lame_hash_size * 2 + 1; } new = memget(newsize * sizeof this); if (new == NULL) return; memset(new, 0, newsize * sizeof this); for (n = 0 ; n < lame_hash_size; n++) { this = lame_hash[n]; while (this) { last = this; this = this->next; last->next = new[hval%newsize]; new[hval%newsize] = last; } } if (lame_hash != NULL) memput(lame_hash, lame_hash_size * sizeof this); lame_hash = new; lame_hash_size = newsize; } last = NULL; this = lame_hash[hval%lame_hash_size]; while (this) { if ((ns_samename(this->server, server) == 1) && (ns_samename(this->zone, zone) == 1)) { this->when = when; return; } last = this; this = this->next; } this = memget(sizeof *this); if (this == NULL) return; this->server = savestr(server, 0); this->zone = savestr(zone, 0); if (this->server == NULL || this->zone == NULL) { if (this->server != NULL) freestr(this->server); if (this->zone != NULL) freestr(this->zone); memput(this, sizeof *this); return; } this->when = when; this->hval = hval; this->next = NULL; if (last != NULL) last->next = this; else lame_hash[hval%lame_hash_size] = this; lame_hash_cnt++; } time_t db_lame_find(char *zone, struct databuf *dp) { unsigned int hval = nhash(zone); struct lame_hash *this; if (lame_hash_size == 0) { /* db_lame_destroy() must have been called. */ dp->d_flags &= ~DB_F_LAME; return (0); } db_lame_clean(); /* Remove expired record so that we can * clear DB_F_LAME when there are no * additions. */ this = lame_hash[hval % lame_hash_size]; while (this) { if ((ns_samename(this->server, (char*)dp->d_data) == 1) && (ns_samename(this->zone, zone) == 1)) return (this->when); this = this->next; } dp->d_flags &= ~DB_F_LAME; return (0); } void db_lame_clean(void) { int i; struct lame_hash *last, *this; for (i = 0 ; i < lame_hash_size; i++) { last = NULL; this = lame_hash[i]; while (this != NULL) { if (this->when < tt.tv_sec) { freestr(this->zone); freestr(this->server); if (last != NULL) { last->next = this->next; memput(this, sizeof *this); this = last->next; } else { lame_hash[i] = this->next; memput(this, sizeof *this); this = lame_hash[i]; } lame_hash_cnt--; } else { last = this; this = this->next; } } } } void db_lame_destroy(void) { int i; struct lame_hash *last, *this; if (lame_hash_size == 0) return; for (i = 0 ; i < lame_hash_size; i++) { this = lame_hash[i]; while (this != NULL) { last = this; this = this->next; freestr(last->zone); freestr(last->server); memput(last, sizeof *this); } } memput(lame_hash, lame_hash_size * sizeof this); lame_hash_cnt = 0; lame_hash_size = 0; lame_hash = NULL; } Index: head/contrib/bind/bin/named/db_ixfr.c =================================================================== --- head/contrib/bind/bin/named/db_ixfr.c (revision 60940) +++ head/contrib/bind/bin/named/db_ixfr.c (revision 60941) @@ -1,861 +1,925 @@ #if !defined(lint) && !defined(SABER) -static char rcsid[] = "$Id: db_ixfr.c,v 8.18 1999/10/15 19:48:57 vixie Exp $"; -#endif /* not lint */ +static char rcsid[] = "$Id: db_ixfr.c,v 8.20 2000/02/29 05:15:03 vixie Exp $"; +#endif /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Manage ixfr transaction log */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "port_after.h" #include "named.h" -#define DBIXFR_ERROR -1 -#define DBIXFR_FOUND_RR 2 -#define DBIXFR_END 3 +#define DBIXFR_ERROR (-1) +#define DBIXFR_FOUND_RR 2 +#define DBIXFR_END 3 -static int ixfr_getrr(struct zoneinfo *, FILE *, const char *, char *, - ns_updrec **, u_int32_t *, u_int32_t *); +static int ixfr_getdelta(struct zoneinfo *, FILE *, const char *, char *, + ns_updque *, u_int32_t *, u_int32_t *); -ns_updrec * +ns_deltalist * ixfr_get_change_list(struct zoneinfo *zp, u_int32_t from_serial, u_int32_t to_serial) { - FILE * fp; + FILE * fp = NULL; u_int32_t old_serial, new_serial; char origin[MAXDNAME]; - struct namebuf *np, *listnp, *finlistnp; - LIST(ns_updrec) listuprec; - int ret, mode; + ns_deltalist *dlhead = NULL; + int ret; ns_updrec *uprec; + ns_delta *dl; if (SEQ_GT(from_serial, to_serial)) return (NULL); - listnp = finlistnp = NULL; - INIT_LIST(listuprec); + + dlhead = memget(sizeof(*dlhead)); + if (dlhead == NULL) + return (NULL); + INIT_LIST(*dlhead); + if ((fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); - return (NULL); + goto cleanup; } strcpy(origin, zp->z_origin); lineno = 1; - np = NULL; - mode = 0; old_serial = new_serial = 0; + for (;;) { - ret = ixfr_getrr(zp, fp, zp->z_ixfr_base, origin, &uprec, - &old_serial, &new_serial); + dl = memget(sizeof *dl); + if (dl == NULL) { + ns_warning(ns_log_db, + "ixfr_get_change_list: out of memory"); + goto cleanup; + } + INIT_LINK(dl, d_link); + INIT_LIST(dl->d_changes); + ret = ixfr_getdelta(zp, fp, zp->z_ixfr_base, origin, &dl->d_changes, + &old_serial, &new_serial); switch (ret) { case DBIXFR_ERROR: - (void) my_fclose(fp); - ns_warning(ns_log_db, "Logical error in %s line %d", - zp->z_ixfr_base, lineno); - return (NULL); + ns_warning(ns_log_db, "Logical error in %s: unlinking", + zp->z_ixfr_base); + unlink(zp->z_ixfr_base); + goto cleanup; + case DBIXFR_FOUND_RR: - if (EMPTY(listuprec)) { + ns_debug(ns_log_default, 4, "ixfr_getdelta DBIXFR_FOUND_RR (%s)", + zp->z_origin); + if (EMPTY(*dlhead)) { /* skip updates prior to the one we want */ - if (uprec->r_zone != from_serial) { - while (uprec != NULL) { - ns_updrec *prev; + uprec = HEAD(dl->d_changes); + INSIST(uprec != NULL); + if ((uprec->r_zone < from_serial) || + (uprec->r_zone > to_serial)) + { + while ((uprec = HEAD(dl->d_changes)) != NULL) { + UNLINK(dl->d_changes, uprec, r_link); if (uprec->r_dp != NULL) - db_freedata(uprec->r_dp); + db_freedata(uprec->r_dp); uprec->r_dp = NULL; - prev = PREV(uprec, r_link); res_freeupdrec(uprec); - uprec = prev; } + memput(dl, sizeof *dl); break; } + else if (uprec->r_zone > from_serial) { + /* missed the boat */ + ns_debug(ns_log_default, 3, + "ixfr_getdelta first SOA is %d, asked for %d (%s)", + uprec->r_zone, + from_serial, + zp->z_origin); + goto cleanup; + } } - APPEND(listuprec, uprec, r_link); - /* continue; */ + ns_debug(ns_log_default, 4, + "adding to change list (%s)", + zp->z_origin); + APPEND(*dlhead, dl, d_link); break; + case DBIXFR_END: + ns_debug(ns_log_default, 4, + "ixfr_getdelta DBIXFR_END (%s)", + zp->z_origin); (void) my_fclose(fp); - return (HEAD(listuprec)); + memput(dl, sizeof *dl); + return (dlhead); + default: (void) my_fclose(fp); + if (dl != NULL) + memput(dl, sizeof *dl); return (NULL); } } + + cleanup: + if (fp != NULL) + (void) my_fclose(fp); + + while ((dl = HEAD(*dlhead)) != NULL) { + UNLINK(*dlhead, dl, d_link); + while ((uprec = HEAD(dl->d_changes)) != NULL) { + UNLINK(dl->d_changes, uprec, r_link); + + if (uprec->r_dp != NULL) + db_freedata(uprec->r_dp); + uprec->r_dp = NULL; + res_freeupdrec(uprec); + } + memput(dl, sizeof *dl); + } + memput(dlhead, sizeof *dlhead); + return (NULL); } /* * int ixfr_have_log(struct zoneinfo *zp,u_int32_t from_serial, * u_int32_t to_serial) * * verify that ixfr transaction log contains changes * from from_serial to to_serial * * returns: * 0 = serial number is up to date - * 1 = transision is possible + * 1 = transmission is possible * -1 = error while opening the ixfr transaction log * -2 = error in parameters * -3 = logical error in the history file */ int ixfr_have_log(struct zoneinfo *zp, u_int32_t from_serial, u_int32_t to_serial) { FILE *fp; u_int32_t old_serial = 0, new_serial = 0; + u_int32_t last_serial = 0; + u_int32_t first_serial = 0; char buf[BUFSIZ]; char *cp; struct stat st; int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; int err; + int first = 0; + int rval = 0; int id, rcode = NOERROR; - if (SEQ_GT(from_serial, to_serial)) return (-2); if (from_serial == to_serial) return (0); /* If there is no log file, just return. */ if (zp->z_ixfr_base == NULL || zp->z_updatelog == NULL) return (-1); + if (zp->z_serial_ixfr_start > 0) { + if (from_serial >= zp->z_serial_ixfr_start) + return (1); + } if (stat(zp->z_ixfr_base, &st) < 0) { if (errno != ENOENT) ns_error(ns_log_db, "unexpected stat(%s) failure: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } if ((fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } if (fgets(buf, sizeof(buf), fp) == NULL) { ns_error(ns_log_update, "fgets() from %s failed: %s", - zp->z_updatelog, strerror(errno)); + zp->z_ixfr_base, strerror(errno)); fclose(fp); return (-1); } if (strcmp(buf, LogSignature) != 0) { ns_error(ns_log_update, "invalid log file %s", - zp->z_updatelog); + zp->z_ixfr_base); fclose(fp); return (-3); } lineno = 1; + first = 1; for (;;) { if (getword(buf, sizeof buf, fp, 0)) { nonempty_lineno = lineno; } else { if (lineno == (nonempty_lineno + 1)) continue; inside_next = 0; prev_pktdone = 1; cont = 1; } if (!strcasecmp(buf, "[DYNAMIC_UPDATE]") || !strcasecmp(buf, "[IXFR_UPDATE]")) { err = 0; rcode = NOERROR; cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *) cp, "id %d", &id)) id = -1; inside_next = 1; prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "serial")) { cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (sscanf((char *) cp, "%u", &old_serial)) { + if (first == 1) { + first = 0; + first_serial = old_serial; + } + last_serial = old_serial; if (from_serial >= old_serial) { - fclose(fp); - return (1); - } else { - fclose(fp); - return (-1); + rval = 1; } } prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { /* XXXRTH not enough error checking here */ cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || sscanf((char *) cp, "from %u to %u", &old_serial, &new_serial) != 2) { - fclose(fp); - return (-3); + rval = -3; + break; } else if (from_serial >= old_serial) { - fclose(fp); - return (1); + if (first == 1) { + first = 0; + first_serial = old_serial; + } + last_serial = old_serial; + rval = 1; } - fclose(fp); - return (-1); } if (prev_pktdone) { prev_pktdone = 0; if (feof(fp)) break; } } fclose(fp); - return (0); + if (last_serial +1 < zp->z_serial) { + ns_warning(ns_log_db, + "%s: File Deleted. Found gap between serial:" + " %d and current serial: %d", + zp->z_ixfr_base, last_serial, zp->z_serial); + (void) unlink(zp->z_ixfr_base); + rval = -3; + } + if (from_serial < first_serial || from_serial > last_serial) + rval = -3; + if (rval == 1) + zp->z_serial_ixfr_start = first_serial; + return (rval); } /* from db_load.c */ static struct map m_section[] = { {"zone", S_ZONE}, {"prereq", S_PREREQ}, {"update", S_UPDATE}, {"reserved", S_ADDT}, }; #define M_SECTION_CNT (sizeof(m_section) / sizeof(struct map)) /* from ns_req.c */ static struct map m_opcode[] = { {"nxdomain", NXDOMAIN}, {"yxdomain", YXDOMAIN}, {"nxrrset", NXRRSET}, {"yxrrset", YXRRSET}, {"delete", DELETE}, {"add", ADD}, }; #define M_OPCODE_CNT (sizeof(m_opcode) / sizeof(struct map)) /* XXXRTH workaround map difficulties */ #define M_CLASS_CNT m_class_cnt #define M_TYPE_CNT m_type_cnt /* - * int - * ixfr_getrr(struct zoneinfo *zp, FILE *fp, - * const char *filename, char *origin, struct namebuf **np, - * u_int32_t *old_serial, u_int32_t *new_serial) + * read a line from the history of a zone. * - * read a line from the historic of a zone. - * * returns: * * DBIXFR_ERROR = an error occured * DBIXFR_FOUND_RR = a rr encountered * DBIXFR_END = end of file */ static int -ixfr_getrr(struct zoneinfo *zp, FILE *fp, const char *filename, char *origin, - ns_updrec **uprec, u_int32_t *old_serial, +ixfr_getdelta(struct zoneinfo *zp, FILE *fp, const char *filename, char *origin, + ns_updque *listuprec, u_int32_t *old_serial, u_int32_t *new_serial) { static int read_soa, read_ns, rrcount; char data[MAXDATA], dnbuf[MAXDNAME], sclass[3]; const char *errtype = "Database"; char *dname, *cp, *cp1; char buf[MAXDATA]; u_int32_t serial, ttl; int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; - int id, rcode = NOERROR; + int id; int i, c, section, opcode, matches, zonenum, err, multiline; int type, class; u_int32_t n; enum transport transport; struct map *mp; int zonelist[MAXDNAME]; struct databuf *dp; struct in_addr ina; struct sockaddr_in empty_from; int datasize; - ns_updque listuprec; ns_updrec * rrecp; u_long l; #define ERRTO(msg) if (1) { errtype = msg; goto err; } else (void)NULL err = 0; transport = primary_trans; lineno = 1; - INIT_LIST(listuprec); for (;;) { if (!getword(buf, sizeof buf, fp, 0)) { if (lineno == (nonempty_lineno + 1) && !(feof(fp))) { /* * End of a nonempty line inside an update * packet or not inside an update packet. */ continue; } /* * Empty line or EOF. - * - * Marks completion of current update packet. */ + if (feof(fp)) + break; inside_next = 0; - prev_pktdone = 1; cont = 1; } else { nonempty_lineno = lineno; } if (!strcasecmp(buf, "[DYNAMIC_UPDATE]") || !strcasecmp(buf, "[IXFR_UPDATE]")) { - err = 0; - rcode = NOERROR; cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *) cp, "id %d", &id)) id = -1; inside_next = 1; - prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { /* XXXRTH not enough error checking here */ cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || sscanf((char *) cp, "from %u to %u", old_serial, new_serial) != 2) { ns_error(ns_log_update, "incr_serial problem with %s", zp->z_updatelog); } else { serial = get_serial(zp); } cont = 1; } else if (!strcasecmp(buf, "[END_DELTA]")) { prev_pktdone = 1; cont = 1; lineno++; } if (prev_pktdone) { - if (!EMPTY(listuprec)) { + if (!EMPTY(*listuprec)) { n++; - *uprec = TAIL(listuprec); return (DBIXFR_FOUND_RR); } prev_pktdone = 0; - if (feof(fp)) - break; } if (cont) { cont = 0; continue; } if (!inside_next) continue; /* * inside the same update packet, continue accumulating * records. */ section = -1; n = strlen(buf); if (buf[n - 1] == ':') buf[--n] = '\0'; for (mp = m_section; mp < m_section + M_SECTION_CNT; mp++) if (!strcasecmp(buf, mp->token)) { section = mp->val; break; } ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; switch (section) { case S_ZONE: cp = fgets(buf, sizeof buf, fp); if (!cp) *buf = '\0'; n = sscanf(cp, "origin %s class %s serial %ul", origin, sclass, &serial); if (n != 3 || ns_samename(origin, zp->z_origin) != 1) err++; if (cp) lineno++; if (!err && inside_next) { int success; dname = origin; type = T_SOA; class = sym_ston(__p_class_syms, sclass, &success); if (!success) { err++; break; } matches = findzone(dname, class, 0, zonelist, MAXDNAME); if (matches) zonenum = zonelist[0]; else err++; } break; case S_PREREQ: case S_UPDATE: /* Operation code. */ if (!getword(buf, sizeof buf, fp, 0)) { err++; break; } opcode = -1; if (buf[0] == '{') { n = strlen(buf); for (i = 0; (u_int32_t) i < n; i++) buf[i] = buf[i + 1]; if (buf[n - 2] == '}') buf[n - 2] = '\0'; } for (mp = m_opcode; mp < m_opcode + M_OPCODE_CNT; mp++) if (!strcasecmp(buf, mp->token)) { opcode = mp->val; break; } if (opcode == -1) { err++; break; } /* Owner's domain name. */ if (!getword((char *) dnbuf, sizeof dnbuf, fp, 0)) { err++; break; } n = strlen((char *) dnbuf) - 1; if (dnbuf[n] == '.') dnbuf[n] = '\0'; dname = dnbuf; ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; (void) getword(buf, sizeof buf, fp, 1); if (isdigit(buf[0])) { /* ttl */ if (ns_parse_ttl(buf, &l) < 0) { err++; break; } ttl = l; (void) getword(buf, sizeof buf, fp, 1); } /* possibly class */ if (buf[0] != '\0') { int success; int maybe_class; maybe_class = sym_ston(__p_class_syms, buf, &success); if (success) { class = maybe_class; (void) getword(buf, sizeof buf, fp, 1); } } /* possibly type */ if (buf[0] != '\0') { int success; int maybe_type; maybe_type = sym_ston(__p_type_syms, buf, &success); if (success) { type = maybe_type; (void) getword(buf, sizeof buf, fp, 1); } } if (buf[0] != '\0') /* possibly rdata */ /* * Convert the ascii data 'buf' to the proper * format based on the type and pack into * 'data'. * * XXX - same as in db_load(), consolidation * needed */ switch (type) { case T_A: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); n = INT32SZ; break; case T_HINFO: case T_ISDN: n = strlen(buf); data[0] = n; memcpy(data + 1, buf, n); n++; if (!getword(buf, sizeof buf, fp, 0)) { i = 0; } else { endline(fp); i = strlen(buf); } data[n] = i; n++; memcpy(data + n + 1, buf, i); n += i; break; case T_SOA: case T_MINFO: case T_RP: (void) strcpy(data, buf); cp = data + strlen(data) + 1; if (!getword((char *) cp, sizeof data - (cp - data), fp, 1)) { err++; break; } cp += strlen((char *) cp) + 1; if (type != T_SOA) { n = cp - data; break; } if (class != zp->z_class || ns_samename(dname, zp->z_origin) != 1) { err++; break; } c = getnonblank(fp, zp->z_updatelog); if (c == '(') { multiline = 1; } else { multiline = 0; ungetc(c, fp); } n = getnum(fp, zp->z_updatelog, GETNUM_SERIAL); if (getnum_error) { err++; break; } if (opcode == ADD && i == 0) *new_serial = n; PUTLONG(n, cp); for (i = 0; i < 4; i++) { if (!getword(buf, sizeof buf, fp, 1)) { err++; break; } if (ns_parse_ttl(buf, &l) < 0) { err++; break; } n = l; PUTLONG(n, cp); } if (multiline && getnonblank(fp, zp->z_updatelog) != ')') { err++; break; } endline(fp); n = cp - data; break; case T_WKS: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); *cp = (char) getprotocol(fp, zp->z_updatelog); n = INT32SZ + sizeof(char); n = getservices((int) n, data, fp, zp->z_updatelog); break; case T_NS: case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: (void) strcpy(data, buf); if (makename(data, origin, sizeof(data)) == -1) { err++; break; } n = strlen(data) + 1; break; case T_MX: case T_AFSDB: case T_RT: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ cp = data; PUTSHORT((u_int16_t) n, cp); if (!getword(buf, sizeof(buf), fp, 1)) { err++; break; } (void) strcpy((char *) cp, buf); if (makename((char *) cp, origin, sizeof(data) - (cp - data)) == -1) { err++; break; } /* advance pointer to end of data */ cp += strlen((char *) cp) + 1; /* now save length */ n = (cp - data); break; case T_PX: n = 0; data[0] = '\0'; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); cp = data; PUTSHORT((u_int16_t) n, cp); for (i = 0; i < 2; i++) { if (!getword(buf, sizeof(buf), fp, 0)) { err++; break; } (void) strcpy((char *) cp, buf); cp += strlen((char *) cp) + 1; } n = cp - data; break; case T_TXT: case T_X25: i = strlen(buf); cp = data; datasize = sizeof data; cp1 = buf; while (i > MAXCHARSTRING) { if (datasize <= MAXCHARSTRING) { ns_error(ns_log_update, "record too big"); return (-1); } datasize -= MAXCHARSTRING; *cp++ = (char)MAXCHARSTRING; memcpy(cp, cp1, MAXCHARSTRING); cp += MAXCHARSTRING; cp1 += MAXCHARSTRING; i -= MAXCHARSTRING; } if (datasize < i + 1) { ns_error(ns_log_update, "record too big"); return (-1); } *cp++ = i; memcpy(cp, cp1, i); cp += i; n = cp - data; endline(fp); /* XXXVIX: segmented texts 4.9.5 */ break; case T_NSAP: n = inet_nsap_addr(buf, (u_char *) data, sizeof data); endline(fp); break; case T_LOC: cp = buf + (n = strlen(buf)); *cp = ' '; cp++; while ((i = getc(fp), *cp = i, i != EOF) && *cp != '\n' && (n < MAXDATA)) { cp++; n++; } if (*cp == '\n') ungetc(*cp, fp); *cp = '\0'; n = loc_aton(buf, (u_char *) data); if (n == 0) { err++; break; } endline(fp); break; case ns_t_sig: case ns_t_nxt: case ns_t_key: case ns_t_cert:{ char *errmsg = NULL; n = parse_sec_rdata(buf, sizeof(buf), 1, (u_char *) data, sizeof(data), fp, zp, dname, ttl, type, domain_ctx, transport, &errmsg); if (errmsg) { err++; endline(fp); n = 0; } break; } default: err++; } if (section == S_PREREQ) { ttl = 0; if (opcode == NXDOMAIN) { class = C_NONE; type = T_ANY; n = 0; } else if (opcode == YXDOMAIN) { class = C_ANY; type = T_ANY; n = 0; } else if (opcode == NXRRSET) { class = C_NONE; n = 0; } else if (opcode == YXRRSET) { if (n == 0) class = C_ANY; } } else {/* section == S_UPDATE */ if (opcode == DELETE) { if (n == 0) { class = C_ANY; if (type == -1) type = T_ANY; } else { class = zp->z_class; } } } break; case S_ADDT: default: ns_debug(ns_log_update, 1, "cannot interpret section: %d", section); inside_next = 0; err++; } if (err) { inside_next = 0; ns_debug(ns_log_update, 1, "merge of update id %d failed due to error at line %d", id, lineno); - memset(&empty_from, 0, sizeof empty_from); - free_rrecp(&listuprec, rcode, empty_from); - continue; + return (DBIXFR_ERROR); } rrecp = res_mkupdrec(section, dname, class, type, ttl); if (section != S_ZONE) { dp = savedata(class, type, ttl, (u_char *) data, n); dp->d_zone = zonenum; dp->d_cred = DB_C_ZONE; dp->d_clev = nlabels(zp->z_origin); rrecp->r_dp = dp; rrecp->r_opcode = opcode; } else { rrecp->r_zone = zonenum; rrecp->r_opcode = opcode; } /* remove add/delete pairs */ if (section == S_UPDATE) { ns_updrec *arp; int foundmatch; - arp = TAIL(listuprec); + arp = TAIL(*listuprec); foundmatch = 0; while (arp) { if (arp->r_section == S_UPDATE && ((arp->r_opcode == DELETE && opcode == ADD) || (opcode == DELETE && arp->r_opcode == ADD)) && arp->r_dp->d_type == dp->d_type && arp->r_dp->d_class == dp->d_class && arp->r_dp->d_ttl == dp->d_ttl && ns_samename(arp->r_dname, dname) == 1 && db_cmp(arp->r_dp, dp) == 0) { db_freedata(dp); db_freedata(arp->r_dp); - UNLINK(listuprec, arp, r_link); + UNLINK(*listuprec, arp, r_link); res_freeupdrec(arp); res_freeupdrec(rrecp); foundmatch = 1; break; } arp = PREV(arp, r_link); } if (foundmatch) continue; } - APPEND(listuprec, rrecp, r_link); + APPEND(*listuprec, rrecp, r_link); /* Override zone number with current zone serial number */ rrecp->r_zone = serial; } if (err) return (DBIXFR_ERROR); return (DBIXFR_END); } + Index: head/contrib/bind/bin/named/db_load.c =================================================================== --- head/contrib/bind/bin/named/db_load.c (revision 60940) +++ head/contrib/bind/bin/named/db_load.c (revision 60941) @@ -1,2600 +1,2614 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_load.c 4.38 (Berkeley) 3/2/91"; -static const char rcsid[] = "$Id: db_load.c,v 8.97 1999/10/30 03:21:35 vixie Exp $"; +static const char rcsid[] = "$Id: db_load.c,v 8.103 2000/04/21 06:54:02 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Load zone from ASCII file on local host. Format similar to RFC 883. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* Forward. */ static int gettoken(FILE *, const char *); static int getcharstring(char *, char *, int, int, int, FILE *, const char *); static int genname(char *, int, const char *, char *, int); static int getmlword(char *, size_t, FILE *, int); static int getallwords(char *, size_t, FILE *, int); static u_int32_t wordtouint32(char *); static void fixup_soa(const char *fn, struct zoneinfo *zp); static int get_nxt_types(u_char *, FILE *, const char *); static int parse_sig_rr(char *, int, u_char *, int, FILE *, struct zoneinfo *, char *, u_int32_t , enum context , enum transport , char **); static int parse_key_rr(char *, int, u_char *, int, FILE *, struct zoneinfo *, char *, enum context, enum transport, char **); static int parse_cert_rr(char *, int, u_char *, int, FILE *, char **); static int parse_nxt_rr(char *, int, u_char *, int, FILE *, struct zoneinfo *, char *, enum context, enum transport, char **); static int wordtouint32_error = 0; static int empty_token = 0; static int getmlword_nesting = 0; /* Global. */ static int clev; /* a zone deeper in a hierarchy has more credibility */ /* * Parser token values */ #define CURRENT 1 #define DOT 2 #define AT 3 #define DNAME 4 #define INCLUDE 5 #define ORIGIN 6 #define GENERATE 7 #define DEFAULTTTL 8 #define ERRTOK 9 #define MAKENAME_OK(N) \ do { \ if (!makename_ok(N, origin, class, zp, \ transport, context, \ domain, filename, lineno, \ data_size - ((u_char*)N - data))) { \ errs++; \ sprintf(buf, "bad name \"%s\"", N); \ goto err; \ } \ } while (0) #define MAKENAME_OKZP(N, SI) \ do { \ if (!makename_ok(N, zp->z_origin, zp->z_class, zp, \ transport, context, \ domain, zp->z_source, lineno, \ SI - ((u_char*)N - data))) { \ errs++; \ sprintf(buf, "bad name \"%s\"", N); \ goto err; \ } \ } while (0) #define RANGE(x, min, max) \ (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) /* Public. */ /* int * db_load(filename, in_origin, zp, def_domain, isixfr) * load a database from `filename' into zone `zp'. append `in_origin' * to all nonterminal domain names in the file. `def_domain' is the * default domain for include files or NULL for zone base files. * returns: * -1 = can't open file * 0 = success * >0 = number of errors encountered */ int db_load(const char *filename, const char *in_origin, struct zoneinfo *zp, const char *def_domain, int isixfr) { static int read_soa, read_ns, rrcount; static u_int32_t default_ttl, default_warn; static struct filenames { struct filenames *next; char *name; } *filenames, *fn; const char *errtype = "Database"; char *cp; char domain[MAXDNAME], origin[MAXDNAME], tmporigin[MAXDNAME]; char buf[MAXDATA]; char genlhs[MAXDNAME], genrhs[MAXDNAME]; u_char data[MAXDATA]; int data_size = sizeof(data); int c, someclass, class, type, dbflags, dataflags, multiline = 0; int slineno, i, errs, didinclude, ininclude, escape, success; u_int32_t ttl, n, serial; u_long tmplong; struct databuf *dp; FILE *fp; struct stat sb; struct in_addr ina; enum transport transport; enum context context; struct sockaddr_in empty_from; int genstart, genend, genstep; char *thisfile; void *state = NULL; empty_from.sin_family = AF_INET; empty_from.sin_addr.s_addr = htonl(INADDR_ANY); empty_from.sin_port = htons(0); /* * We use an 'if' inside of the 'do' below because otherwise the Solaris * compiler detects that the 'while' is never executed because of the 'goto' * and complains. */ #define ERRTO(msg) do { if (1) { errtype = msg; goto err; } } while (0) #define ERRTOZ(msg) do { if (1) { errtype = msg; buf[0] = '\0'; goto err; } } while (0) switch (zp->z_type) { case Z_PRIMARY: + /* Any updates should be saved before we attempt to reload. */ + INSIST((zp->z_flags & (Z_NEED_DUMP|Z_NEED_SOAUPDATE)) == 0); case Z_HINT: transport = primary_trans; break; case Z_SECONDARY: case Z_STUB: transport = secondary_trans; break; case Z_CACHE: transport = response_trans; break; default: transport = response_trans; /*guessing*/ break; } errs = 0; didinclude = 0; ininclude = (def_domain != NULL); if (!ininclude) { rrcount = 0; read_soa = 0; read_ns = 0; default_ttl = USE_MINIMUM; default_warn = 1; clev = nlabels(in_origin); filenames = NULL; + zp->z_minimum = USE_MINIMUM; } ttl = default_ttl; ns_debug(ns_log_load, 1, "db_load(%s, %s, %d, %s, %s)", filename, in_origin, zp - zones, def_domain ? def_domain : "Nil", isixfr ? "IXFR" : "Normal"); fn = (struct filenames *)memget(sizeof *filenames); if (fn == NULL) ns_panic(ns_log_db, 0, "db_load: memget failed"); thisfile = fn->name = savestr(filename, 1); fn->next = filenames; filenames = fn; strcpy(origin, in_origin); if ((fp = fopen(filename, "r")) == NULL) { ns_warning(ns_log_load, "db_load could not open: %s: %s", filename, strerror(errno)); zp->z_ftime = 0; return (-1); } if (zp->z_type == Z_HINT) { dbflags = DB_NODATA | DB_NOHINTS; dataflags = DB_F_HINT; #ifdef STUBS } else if (zp->z_type == Z_STUB && clev == 0) { dbflags = DB_NODATA | DB_NOHINTS; dataflags = DB_F_HINT; #endif } else { dbflags = DB_NODATA; dataflags = 0; } gettime(&tt); if (fstat(fileno(fp), &sb) < 0) { ns_warning(ns_log_load, "fstat failed: %s: %s", filename, strerror(errno)); sb.st_mtime = (int)tt.tv_sec; } slineno = lineno; lineno = 1; if (def_domain) strcpy(domain, def_domain); else domain[0] = '\0'; class = zp->z_class; zp->z_flags &= ~(Z_INCLUDE|Z_DB_BAD); while ((c = gettoken(fp, filename)) != EOF) { switch (c) { case INCLUDE: if (isixfr) { c = ERRTOK; break; } if (!getword(buf, sizeof buf, fp, 0)) /* file name*/ break; if (!getword(tmporigin, sizeof(tmporigin), fp, 1)) strcpy(tmporigin, origin); else { if (makename(tmporigin, origin, sizeof(tmporigin)) == -1) ERRTO("$INCLUDE makename failed"); endline(fp); } didinclude = 1; - errs += db_load(buf, tmporigin, zp, domain, ISNOTIXFR); + i = db_load(buf, tmporigin, zp, domain, ISNOTIXFR); + errs += (i == -1) ? 1 : i; continue; case ORIGIN: (void) strcpy(buf, origin); if (!getword(origin, sizeof(origin), fp, 1)) break; ns_debug(ns_log_load, 3, "db_load: origin %s, buf %s", origin, buf); if (makename(origin, buf, sizeof(origin)) == -1) ERRTO("$ORIGIN makename failed"); ns_debug(ns_log_load, 3, "db_load: origin now %s", origin); continue; case GENERATE: if (!getword(buf, sizeof(buf), fp, 0)) ERRTOZ("$GENERATE missing RANGE"); n = sscanf(buf, "%d-%d/%d", &genstart, &genend, &genstep); if (n != 2 && n != 3) ERRTO("$GENERATE invalid range"); if (n == 2) genstep = 1; if ((genend < genstart) || (genstart < 0) || (genstep < 0)) ERRTO("$GENERATE invalid range"); if (!getword(genlhs, sizeof(genlhs), fp, 2)) ERRTOZ("$GENERATE missing LHS"); if (!getword(buf, sizeof(buf), fp, 0)) ERRTOZ("GENERATE missing TYPE"); type = sym_ston(__p_type_syms, buf, &success); if (success == 0 || type == ns_t_any) { ns_info(ns_log_load, "%s: Line %d: $GENERATE unknown type: %s.", filename, lineno, buf); errs++; endline(fp); continue; } switch (type) { case ns_t_ns: case ns_t_ptr: case ns_t_cname: case ns_t_a: case ns_t_aaaa: break; default: ERRTO("$GENERATE unsupported type"); } if (!getword(genrhs, sizeof(genrhs), fp, 2)) ERRTOZ("$GENERATE missing RHS"); for (i = genstart; i <= genend; i += genstep) { if (genname(genlhs, i, origin, domain, sizeof domain) == -1) ERRTOZ("$GENERATE genname LHS failed"); context = ns_ownercontext(type, transport); if (!ns_nameok(NULL, domain, class, zp, transport, context, domain, inaddr_any)) { strcpy(buf, domain); ERRTO("$GENERATE owner name error"); } switch (type) { case ns_t_ns: case ns_t_ptr: case ns_t_cname: if (genname(genrhs, i, origin, (char *)data, sizeof data) == -1) ERRTOZ("$GENERATE genname RHS failed"); switch (type) { case ns_t_ns: context = hostname_ctx; break; case ns_t_ptr: context = ns_ptrcontext(domain); break; case ns_t_cname: context = domain_ctx; break; } if (!ns_nameok(NULL, (char *)data, class, zp, transport, context, domain, inaddr_any)) { strncpy(buf, domain, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; ERRTO("$GENERATE name error"); } n = strlen((char *)data) + 1; break; case ns_t_a: case ns_t_aaaa: if (genname(genrhs, i, NULL, (char *)data, sizeof data) == -1) ERRTOZ("$GENERATE genname RHS failed"); strncpy(buf, (char*)data, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; switch (type) { case ns_t_a: if (!inet_aton(buf, &ina)) ERRTO("IP Address"); (void) ina_put(ina, data); n = NS_INT32SZ; break; case ns_t_aaaa: if (inet_pton(AF_INET6, buf, data) <= 0) ERRTO("IPv6 Address"); n = NS_IN6ADDRSZ; break; } break; default: ERRTOZ("$GENERATE unsupported context"); } dp = savedata(class, type, (u_int32_t)ttl, (u_char *)data, (int)n); dp->d_zone = zp - zones; dp->d_flags = dataflags; dp->d_cred = DB_C_ZONE; dp->d_clev = clev; c = db_set_update(domain, dp, &state, dbflags, (dataflags & DB_F_HINT) != 0 ? &fcachetab : &hashtab, empty_from, &rrcount, lineno, filename); if (c != OK) { if (c == CNAMEANDOTHER) errs++; } } endline(fp); continue; case DNAME: if (!getword(domain, sizeof(domain), fp, 1)) break; if (makename(domain, origin, sizeof(domain)) == -1) ERRTO("ownername makename failed"); goto gotdomain; case DEFAULTTTL: if (getttl(fp, filename, lineno, &n, &multiline) <= 0 || n > MAXIMUM_TTL) { ERRTO("$TTL bad TTL value"); } ttl = default_ttl = n; continue; case AT: (void) strcpy(domain, origin); goto gotdomain; case DOT: domain[0] = '\0'; /* FALLTHROUGH */ case CURRENT: gotdomain: if (!getword(buf, sizeof buf, fp, 0)) { if (c == CURRENT) continue; break; } if (ns_parse_ttl(buf, &tmplong) < 0) { if (zp->z_type == z_master && default_warn && (default_ttl == USE_MINIMUM)) { ns_warning(ns_log_load, "Zone \"%s\" (file %s): %s", zp->z_origin, filename, "No default TTL set using SOA minimum instead"); default_warn = 0; } ttl = (u_int32_t)default_ttl; } else { ttl = tmplong; if (ttl > MAXIMUM_TTL) { ns_info(ns_log_load, "%s: Line %d: TTL > %u; converted to 0", filename, lineno, MAXIMUM_TTL); ttl = 0; } if (zp->z_type == Z_CACHE) { /* * This allows the cache entry to age * while sitting on disk (powered off). */ if (ttl > max_cache_ttl) ttl = max_cache_ttl; ttl += sb.st_mtime; } if (!getword(buf, sizeof buf, fp, 0)) break; } /* Parse class (IN, etc) */ someclass = sym_ston(__p_class_syms, buf, &success); if (success && someclass != zp->z_class) { ns_info(ns_log_load, "%s: Line %d: wrong class: %s.", filename, lineno, p_class(someclass)); errs++; break; } if (success && someclass != C_ANY) { class = someclass; (void) getword(buf, sizeof buf, fp, 0); } /* Parse RR type (A, MX, etc) */ type = sym_ston(__p_type_syms, buf, &success); if (success == 0 || type == ns_t_any) { ns_info(ns_log_load, "%s: Line %d: Unknown type: %s.", filename, lineno, buf); errs++; break; } if (ttl == USE_MINIMUM) ttl = zp->z_minimum; context = ns_ownercontext(type, transport); if (!ns_nameok(NULL, domain, class, zp, transport, context, domain, inaddr_any)) { errs++; ns_notice(ns_log_load, "%s:%d: owner name error", filename, lineno); break; } context = domain_ctx; switch (type) { case ns_t_key: case ns_t_sig: case ns_t_nxt: case ns_t_cert: /* * Don't do anything here for these types -- * they read their own input separately later. */ goto dont_get_word; case ns_t_soa: case ns_t_minfo: case ns_t_rp: case ns_t_ns: case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ptr: escape = 1; break; default: escape = 0; } if (!getword(buf, sizeof buf, fp, escape)) break; ns_debug(ns_log_load, 3, "d='%s', c=%d, t=%d, ttl=%u, data='%s'", domain, class, type, ttl, buf); /* * Convert the ascii data 'buf' to the proper format * based on the type and pack into 'data'. */ dont_get_word: switch (type) { case ns_t_a: if (!inet_aton(buf, &ina)) ERRTO("IP Address"); (void) ina_put(ina, data); n = NS_INT32SZ; break; case ns_t_soa: context = hostname_ctx; goto soa_rp_minfo; case ns_t_rp: case ns_t_minfo: context = mailname_ctx; /* FALLTHROUGH */ soa_rp_minfo: (void) strcpy((char *)data, buf); MAKENAME_OK((char *)data); cp = (char *)(data + strlen((char *)data) + 1); if (!getword(cp, (sizeof data) - (cp - (char*)data), fp, 1)) ERRTO("Domain Name"); if (type == ns_t_rp) context = domain_ctx; else context = mailname_ctx; MAKENAME_OK(cp); cp += strlen((char *)cp) + 1; if (type != ns_t_soa) { n = cp - (char *)data; break; } if (ns_samename(zp->z_origin, domain) != 1) { errs++; ns_error(ns_log_load, "%s:%d: SOA for \"%s\" not at zone top \"%s\"", filename, lineno, domain, zp->z_origin); } c = getnonblank(fp, filename); if (c == '(') { multiline = 1; } else { multiline = 0; ungetc(c, fp); } serial = zp->z_serial; zp->z_serial = getnum(fp, filename, GETNUM_SERIAL); if (getnum_error) errs++; n = (u_int32_t) zp->z_serial; PUTLONG(n, cp); if (serial != 0 && SEQ_GT(serial, zp->z_serial)) { ns_notice(ns_log_load, "%s:%d: WARNING: new serial number < old (%lu < %lu)", filename , lineno, zp->z_serial, serial); } if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = INIT_REFRESH; } PUTLONG(n, cp); zp->z_refresh = RANGE(n, MIN_REFRESH, MAX_REFRESH); if (zp->z_type == Z_SECONDARY #if defined(STUBS) || zp->z_type == Z_STUB #endif ) { ns_refreshtime(zp, MIN(sb.st_mtime, tt.tv_sec)); sched_zone_maint(zp); } #ifdef BIND_UPDATE if ((zp->z_type == Z_PRIMARY) && (zp->z_flags & Z_DYNAMIC)) if ((u_int32_t)zp->z_soaincrintvl > zp->z_refresh/3) { ns_info(ns_log_load, "zone soa update time truncated to 1/3rd of refresh time"); zp->z_soaincrintvl = zp->z_refresh / 3; } #endif if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = INIT_REFRESH; } PUTLONG(n, cp); zp->z_retry = RANGE(n, MIN_RETRY, MAX_RETRY); if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = INIT_REFRESH; } PUTLONG(n, cp); zp->z_expire = RANGE(n, zp->z_refresh, MAX_EXPIRE); if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = 120; } PUTLONG(n, cp); if (n > MAXIMUM_TTL) { ns_info(ns_log_load, "%s: Line %d: SOA minimum TTL > %u; converted to 0", filename, lineno, MAXIMUM_TTL); zp->z_minimum = 0; } else zp->z_minimum = n; - if (default_ttl == USE_MINIMUM) + if (ttl == USE_MINIMUM) ttl = n; n = cp - (char *)data; if (multiline) { buf[0] = getnonblank(fp, filename); buf[1] = '\0'; if (buf[0] != ')') ERRTO("SOA \")\""); + multiline = 0; endline(fp); } read_soa++; if (zp->z_type == Z_PRIMARY) fixup_soa(filename, zp); break; case ns_t_wks: /* Address */ if (!inet_aton(buf, &ina)) ERRTO("WKS IP Address"); (void) ina_put(ina, data); /* Protocol */ data[INADDRSZ] = getprotocol(fp, filename); /* Services */ n = getservices(NS_INT32SZ + sizeof(char), (char *)data, fp, filename); break; case ns_t_ns: if (ns_samename(zp->z_origin, domain) == 1) read_ns++; context = hostname_ctx; goto cname_etc; case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: context = domain_ctx; goto cname_etc; case ns_t_ptr: context = ns_ptrcontext(domain); cname_etc: (void) strcpy((char *)data, buf); MAKENAME_OK((char *)data); n = strlen((char *)data) + 1; break; case ns_t_naptr: /* Order Preference Flags Service Replacement Regexp */ n = 0; cp = buf; /* Order */ while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ if (cp == buf || n > 65535) ERRTO("NAPTR Order"); cp = (char *)data; PUTSHORT((u_int16_t)n, cp); /* Preference */ n = getnum(fp, filename, GETNUM_NONE); if (getnum_error || n > 65536) ERRTO("NAPTR Preference"); PUTSHORT((u_int16_t)n, cp); /* Flags */ if (!getword(buf, sizeof buf, fp, 0)) ERRTO("NAPTR Flags"); n = strlen(buf); if (n > 255) ERRTO("NAPTR Flags too big"); *cp++ = n; memcpy(cp, buf, (int)n); cp += n; /* Service Classes */ if (!getword(buf, sizeof buf, fp, 0)) ERRTO("NAPTR Service Classes"); n = strlen(buf); if (n > 255) ERRTO("NAPTR Service Classes too big"); *cp++ = n; memcpy(cp, buf, (int)n); cp += n; /* Pattern */ if (!getword(buf, sizeof buf, fp, 0)) ERRTO("NAPTR Pattern"); n = strlen(buf); if (n > 255) ERRTO("NAPTR Pattern too big"); *cp++ = n; memcpy(cp, buf, (int)n); cp += n; /* Replacement */ if (!getword(buf, sizeof buf, fp, 1)) ERRTO("NAPTR Replacement"); n = strlen(buf); if (n > data_size - ((u_char *)cp - data)) ERRTO("NAPTR Replacement too big"); (void) strcpy((char *)cp, buf); context = domain_ctx; MAKENAME_OK(cp); /* advance pointer to end of data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - (char *)data); break; case ns_t_mx: case ns_t_afsdb: case ns_t_rt: case ns_t_srv: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ if ((cp == buf) || (n > 65535)) ERRTO("Priority"); cp = (char *)data; PUTSHORT((u_int16_t)n, cp); if (type == ns_t_srv) { n = getnum(fp, filename, GETNUM_NONE); if (getnum_error || n > 65536) ERRTO("SRV RR"); PUTSHORT((u_int16_t)n, cp); n = getnum(fp, filename, GETNUM_NONE); if (getnum_error || n > 65536) ERRTO("SRV RR"); PUTSHORT((u_int16_t)n, cp); } if (!getword(buf, sizeof buf, fp, 1)) ERRTO("Domain Name"); (void) strcpy((char *)cp, buf); context = hostname_ctx; MAKENAME_OK(cp); /* advance pointer to end of data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - (char *)data); break; case ns_t_px: context = domain_ctx; n = 0; data[0] = '\0'; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ if ((cp == buf) || (n > 65535)) ERRTO("PX Priority"); cp = (char *)data; PUTSHORT((u_int16_t)n, cp); if (!getword(buf, sizeof buf, fp, 0)) ERRTO("PX Domain1"); (void) strcpy((char *)cp, buf); MAKENAME_OK(cp); /* advance pointer to next field */ cp += strlen((char *)cp) + 1; if (!getword(buf, sizeof buf, fp, 0)) ERRTO("PX Domain2"); (void) strcpy((char *)cp, buf); MAKENAME_OK(cp); /* advance pointer to end of data */ cp += strlen((char *)cp) + 1; /* now save length */ n = (cp - (char *)data); break; case ns_t_hinfo: n = getcharstring(buf, (char *)data, type, 2, 2, fp, filename); if (n == 0) ERRTO("HINFO RR"); break; case ns_t_isdn: n = getcharstring(buf, (char *)data, type, 1, 2, fp, filename); if (n == 0) ERRTO("ISDN RR"); break; case ns_t_txt: n = getcharstring(buf, (char *)data, type, 1, 0, fp, filename); if (n == 0) ERRTO("TXT RR"); break; case ns_t_x25: n = getcharstring(buf, (char *)data, type, 1, 1, fp, filename); if (n == 0) ERRTO("X25 RR"); break; case ns_t_nsap: n = inet_nsap_addr(buf, (u_char *)data, sizeof data); if (n == 0) ERRTO("NSAP RR"); endline(fp); break; case ns_t_aaaa: if (inet_pton(AF_INET6, buf, data) <= 0) ERRTO("IPv4 Address"); n = NS_IN6ADDRSZ; endline(fp); break; case ns_t_nxt: case ns_t_key: case ns_t_cert: case ns_t_sig: { char *errmsg = NULL; - int ret = parse_sec_rdata(buf, sizeof(buf), 0, + int ret; + if (ttl == USE_MINIMUM) /* no ttl set */ + ttl = 0; + ret = parse_sec_rdata(buf, sizeof(buf), 0, data, sizeof(data), fp, zp, domain, ttl, type, domain_ctx, transport, &errmsg); if (ret < 0) { errtype = errmsg; goto err; } else n = ret; break; } case ns_t_loc: cp = buf + (n = strlen(buf)); *cp = ' '; cp++; n++; while ((i = getc(fp), *cp = i, i != EOF) && *cp != '\n' && (n < MAXDATA)) { cp++; n++; } if (*cp == '\n') /* leave \n for getword */ ungetc(*cp, fp); *cp = '\0'; /* now process the whole line */ n = loc_aton(buf, (u_char *)data); if (n == 0) goto err; endline(fp); break; default: goto err; } /* * Ignore data outside the zone. */ if (zp->z_type != Z_CACHE && !ns_samedomain(domain, zp->z_origin)) { ns_info(ns_log_load, "%s:%d: data \"%s\" outside zone \"%s\" (ignored)", filename, lineno, domain, zp->z_origin); continue; } + if (ttl == USE_MINIMUM) /* no ttl set */ + ttl = 0; dp = savedata(class, type, (u_int32_t)ttl, (u_char *)data, (int)n); dp->d_zone = zp - zones; dp->d_flags = dataflags; dp->d_cred = DB_C_ZONE; dp->d_clev = clev; c = db_set_update(domain, dp, &state, dbflags, (dataflags & DB_F_HINT) != 0 ? &fcachetab : &hashtab, empty_from, &rrcount, lineno, filename); if (c == CNAMEANDOTHER) errs++; continue; case ERRTOK: break; } err: errs++; ns_notice(ns_log_load, "%s:%d: %s error near (%s)", filename, empty_token ? (lineno - 1) : lineno, errtype, buf); if (!empty_token) endline(fp); } c = db_set_update(NULL, NULL, &state, dbflags, (dataflags & DB_F_HINT) ? &fcachetab : &hashtab, empty_from, &rrcount, lineno, filename); if (c != OK) { if (c == CNAMEANDOTHER) errs++; } (void) my_fclose(fp); lineno = slineno; if (!ininclude) { if (didinclude) { zp->z_flags |= Z_INCLUDE; zp->z_ftime = 0; } else zp->z_ftime = sb.st_mtime; zp->z_lastupdate = sb.st_mtime; if (zp->z_type != Z_CACHE && zp->z_type != Z_HINT) { const char *msg = NULL; if (read_soa == 0) msg = "no SOA RR found"; else if (read_soa != 1) msg = "multiple SOA RRs found"; else if (read_ns == 0) msg = "no NS RRs found at zone top"; else if (!rrcount) msg = "no relevant RRs found"; if (msg != NULL) { errs++; ns_warning(ns_log_load, "Zone \"%s\" (file %s): %s", zp->z_origin, filename, msg); } } + errs += purge_nonglue(zp->z_origin, + (dataflags & DB_F_HINT) ? fcachetab : + hashtab, zp->z_class); while (filenames) { fn = filenames; filenames = filenames->next; freestr(fn->name); memput(fn, sizeof *fn); } if (errs != 0) ns_warning(ns_log_load, "%s zone \"%s\" (%s) rejected due to errors (serial %u)", zoneTypeString(zp->z_type), zp->z_origin, p_class(zp->z_class), zp->z_serial); else ns_info(ns_log_load, "%s zone \"%s\" (%s) loaded (serial %u)", zoneTypeString(zp->z_type), zp->z_origin, p_class(zp->z_class), zp->z_serial); } if (errs != 0) { zp->z_flags |= Z_DB_BAD; zp->z_ftime = 0; } #ifdef BIND_NOTIFY if (errs == 0 && (!ininclude) && (zp->z_type == z_master || zp->z_type == z_slave)) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif return (errs); } void db_err(int err, char *domain, int type, const char *filename, int lineno) { if (filename != NULL && err == CNAMEANDOTHER) - ns_notice(ns_log_load, "%s:%d:%s: CNAME and OTHER data error", - filename, lineno, domain); + ns_warning(ns_log_load, "%s:%d:%s: CNAME and OTHER data error", + filename, lineno, domain); if (err != DATAEXISTS) ns_debug(ns_log_load, 1, "update failed %s %d", domain, type); } static int gettoken(FILE *fp, const char *src) { int c; char op[32]; for (;;) { c = getc(fp); top: switch (c) { case EOF: return (EOF); case '$': if (getword(op, sizeof op, fp, 0)) { if (!strcasecmp("include", op)) return (INCLUDE); if (!strcasecmp("origin", op)) return (ORIGIN); if (!strcasecmp("generate", op)) return (GENERATE); if (!strcasecmp("ttl", op)) return (DEFAULTTTL); } ns_notice(ns_log_db, "%s:%d: Unknown $ option: $%s", src, lineno, op); return (ERRTOK); case ';': while ((c = getc(fp)) != EOF && c != '\n') ; goto top; case ' ': case '\t': return (CURRENT); case '.': return (DOT); case '@': return (AT); case '\n': lineno++; continue; case '\r': if (NS_OPTION_P(OPTION_TREAT_CR_AS_SPACE) != 0) return (CURRENT); default: (void) ungetc(c, fp); return (DNAME); } } } /* int * getword(buf, size, fp, preserve) * get next word, skipping blanks & comments. * '\' '\n' outside of "quotes" is considered a blank. * parameters: * buf - destination * size - of destination * fp - file to read from * preserve - should we preserve \ before \\ and \.? * if preserve == 2, then keep all \ * return value: * 0 = no word; perhaps EOL or EOF; lineno was incremented. * 1 = word was read */ int getword(char *buf, size_t size, FILE *fp, int preserve) { char *cp = buf; int c, spaceok, once; empty_token = 0; /* XXX global side effect. */ once = 0; while ((c = getc(fp)) != EOF) { once++; if (c == ';') { /* Comment. Skip to end of line. */ while ((c = getc(fp)) != EOF && c != '\n') (void)NULL; c = '\n'; } if (c == '\n') { /* * Unescaped newline. It's a terminator unless we're * already midway into a token. */ if (cp != buf) ungetc(c, fp); else lineno++; break; } if (c == '"') { /* "Quoted string." Gather the whole string here. */ while ((c = getc(fp)) != EOF && c!='"' && c!='\n') { if (c == '\\') { if ((c = getc(fp)) == EOF) c = '\\'; if (preserve) switch (c) { default: if (preserve == 1) break; case '\\': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (cp >= buf+size-1) break; *cp++ = '\\'; } if (c == '\n') lineno++; } if (cp >= buf+size-1) break; *cp++ = c; } /* * Newline string terminators are * not token terminators. */ if (c == '\n') { lineno++; break; } /* Sample following character, check for terminator. */ if ((c = getc(fp)) != EOF) ungetc(c, fp); if (c == EOF || isspace(c)) { *cp = '\0'; return (1); } continue; } spaceok = 0; if (c == '\\') { /* Do escape processing. */ if ((c = getc(fp)) == EOF) c = '\\'; if (preserve) switch (c) { default: if (preserve == 1) break; case '\\': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (cp >= buf+size-1) break; *cp++ = '\\'; } if (c == ' ' || c == '\t') spaceok++; } if (isspace(c) && !spaceok) { /* Blank of some kind. Skip run. */ while (isspace(c = getc(fp)) && c != '\n') (void)NULL; ungetc(c, fp); /* Blank means terminator if the token is nonempty. */ if (cp != buf) /* Trailing whitespace */ break; continue; /* Leading whitespace */ } if (cp >= buf + size - 1) break; *cp++ = (char)c; } *cp = '\0'; if (cp == buf) empty_token = 1; if (!once) lineno++; return (cp != buf); } /* * int * getttl(fp, fn, ln, ttl, multiline) * read a word from the file and parse it as a TTL. * return: * 1 ttl found * 0 word not read (EOF or EOL?) * -1 word read but it wasn't a ttl * side effects: * *ttl is written if the return value is to be 1. */ int getttl(FILE *fp, const char *fn, int lineno, u_int32_t *ttl, int *multiline) { char buf[MAXDATA]; u_long tmp; int ch; int len; while (!feof(fp) && !getword(buf, sizeof buf, fp, 0) && *multiline) (void)NULL; len = strlen(buf); if (*multiline && len && buf[len-1] == ')') { buf[len-1] = '\0'; *multiline = 0; } if (ns_parse_ttl(buf, &tmp) < 0) { ns_notice(ns_log_db, "%s:%d: expected a TTL, got \"%s\"", fn, lineno, buf); return (-1); } if (*multiline) { ch = getnonblank(fp, fn); if (ch == EOF) return (-1); if (ch == ';') endline(fp); else ungetc(ch, fp); } *ttl = (u_int32_t)tmp; return (1); } /* Get multiline words. Same parameters as getword. Handles any number of leading ('s or )'s in the words it sees. FIXME: We kludge recognition of ( and ) for multiline input. Each paren must appear at the start of a (blank-separated) word, which is particularly counter-intuitive for ). Good enough for now, until Paul rewrites the parser. (gnu@toad.com, oct96) */ static int getmlword(char *buf, size_t size, FILE *fp, int preserve) { char *p; do { while (!getword (buf, size, fp, preserve)) { /* No more words on this line. See if doing the multiline thing. */ if (!getmlword_nesting) { /* Nope... */ ungetc('\n', fp); /* Push back newline */ lineno--; /* Unbump the lineno */ empty_token = 0; /* Undo this botch */ return 0; } if (feof(fp) || ferror(fp)) return 0; /* Error, no terminating ')' */ /* Continue reading til we get a word... */ } while ('(' == *buf) { /* Word starts with paren. Multiline mode. Move the rest of the word down over the paren. */ getmlword_nesting++; p = buf; while (0 != (p[0]=p[1])) p++; } while (')' == *buf) { getmlword_nesting--; p = buf; while (0 != (p[0]=p[1])) p++; } } while (buf[0] == 0); /* loop til we get a non-( non-) word */ return 1; /* Got a word... */ } /* Get all the remaining words on a line, concatenated into one big long (not too long!) string, with the whitespace squeezed out. This routine, like getword(), does not swallow the newline if words seen. This routine, unlike getword(), never swallows the newline if no words. Parameters are the same as getword(). Result is: 0 got no words at all 1 got one or more words -1 got too many words, they don't all fit; or missing close paren */ static int getallwords(char *buf, size_t size, FILE *fp, int preserve) { char *runningbuf = buf; int runningsize = size; int len; while (runningsize > 0) { if (!getmlword (runningbuf, runningsize, fp, preserve)) { return runningbuf!=buf; /* 1 or 0 */ } len = strlen(runningbuf); runningbuf += len; runningsize -= len; } return -1; /* Error, String too long */ } int getnum(FILE *fp, const char *src, int opt) { int c, n; int seendigit = 0; int seendecimal = 0; int m = 0; int allow_dots = 0; getnum_error = 0; #ifdef DOTTED_SERIAL if (opt & GETNUM_SERIAL) allow_dots++; #endif for (n = 0; (c = getc(fp)) != EOF; ) { if (isspace(c)) { if (c == '\n') lineno++; if (seendigit) break; continue; } if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') lineno++; if (seendigit) break; continue; } if (getnum_error) continue; if (!isdigit(c)) { if (c == ')' && seendigit) { (void) ungetc(c, fp); break; } if (seendigit && (opt & GETNUM_SCALED) && strchr("KkMmGg", c) != NULL) { switch (c) { case 'K': case 'k': n *= 1024; break; case 'M': case 'm': n *= (1024 * 1024); break; case 'G': case 'g': n *= (1024 * 1024 * 1024); break; } break; } if (seendecimal || c != '.' || !allow_dots) { ns_notice(ns_log_db, "%s:%d: expected a number", src, lineno); getnum_error = 1; } else { if (!seendigit) n = 1; #ifdef SENSIBLE_DOTS n *= 10000; #else n *= 1000; #endif seendigit = 1; seendecimal = 1; } continue; } #ifdef SENSIBLE_DOTS if (seendecimal) m = m * 10 + (c - '0'); else n = n * 10 + (c - '0'); #else n = n * 10 + (c - '0'); #endif seendigit = 1; } if (getnum_error) return (0); if (m > 9999) { ns_info(ns_log_db, "%s:%d: number after the decimal point exceeds 9999", src, lineno); getnum_error = 1; return (0); } if (seendecimal) { ns_info(ns_log_db, "%s:%d: decimal serial number interpreted as %d", src, lineno, n+m); } return (n + m); } #ifndef BIND_UPDATE static #endif int getnonblank(FILE *fp, const char *src) { int c; while ((c = getc(fp)) != EOF) { if (isspace(c)) { if (c == '\n') lineno++; continue; } if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') lineno++; continue; } return (c); } ns_info(ns_log_db, "%s:%d: unexpected EOF", src, lineno); return (EOF); } /* * Replace all single "$"'s in "name" with "it". * ${delta} will add delta to "it" before printing. * ${delta,width} will change print width as well, zero fill is implied * ${delta,width,radix} will change radix as well, can be d, o, x, X. * i.e. ${0,2,X} will produce a two digit hex (upper case) with zero fill. * Append "origin" to name if required and validate result with makename. * To get a "$" or "{" in the output use \ before it. * Return 0 on no error or -1 on error. * Resulting name stored in "buf". */ static int genname(char *name, int it, const char *origin, char *buf, int size) { char *bp = buf; char *eom = buf + size; char *cp; char numbuf[32]; char fmt[32]; int delta = 0; int width; while (*name) { if (*name == '$') { if (*(++name) == '$') { /* should be deprecated. how? */ if (bp >= eom) return (-1); *bp++ = *name++; } else { strcpy(fmt, "%d"); if (*name == '{') { switch (sscanf(name, "{%d,%d,%1[doxX]}", &delta, &width, numbuf)) { case 1: break; case 2: sprintf(fmt, "%%0%dd", width); break; case 3: sprintf(fmt, "%%0%d%c", width, numbuf[0]); break; default: return (-1); } while (*name && *name++ != '}') { continue; } } sprintf(numbuf, fmt, it + delta); cp = numbuf; while (*cp) { if (bp >= eom) return (-1); *bp++ = *cp++; } } } else if (*name == '\\') { if (*(++name) == '\0') { if (bp >= eom) return (-1); *bp++ = '\\'; } else { switch (*name) { case '\\': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (bp >= eom) return (-1); *bp++ = '\\'; default: if (bp >= eom) return (-1); *bp++ = *name++; } } } else { if (bp >= eom) return (-1); *bp++ = *name++; } } if (bp >= eom) return (-1); *bp = '\0'; return (origin == NULL ? 0 : makename(buf, origin, size)); } /* * Take name and fix it according to following rules: * "." means root. * "@" means current origin. * "name." means no changes. * "name" means append origin. */ int makename(char *name, const char *origin, int size) { int n; u_char domain[MAXCDNAME]; switch (ns_name_pton(name, domain, sizeof(domain))) { case -1: return (-1); case 1: /* FULLY QUALIFIED */ break; case 0: /* UNQUALIFIED */ if (strcmp(name, "@") == 0) /* must test raw name */ domain[0] = 0; if ((n = dn_skipname(domain, domain+sizeof(domain))) == -1) return (-1); /* step back over root, append origin */ switch (ns_name_pton(origin, domain+n-1, sizeof(domain)-n+1)) { case -1: return (-1); case 0: case 1: break; } break; } if (ns_name_ntop(domain, name, size) == -1) return (-1); if (name[0] == '.') /* root */ name[0] = '\0'; return (0); } int makename_ok(char *name, const char *origin, int class, struct zoneinfo *zp, enum transport transport, enum context context, const char *owner, const char *filename, int lineno, int size) { int ret = 1; if (makename(name, origin, size) == -1) { ns_info(ns_log_db, "%s:%d: makename failed", filename, lineno); return (0); } if (!ns_nameok(NULL, name, class, zp, transport, context, owner, inaddr_any)) { ns_info(ns_log_db, "%s:%d: database naming error", filename, lineno); ret = 0; } return (ret); } void endline(FILE *fp) { int c; while ((c = getc(fp)) != '\0') { if (c == '\n') { (void) ungetc(c,fp); break; } else if (c == EOF) { break; } } } #define MAXPORT 1024 #define MAXLEN 24 #ifndef BIND_UPDATE static #endif char getprotocol(FILE *fp, const char *src) { int k; char b[MAXLEN]; (void) getword(b, sizeof(b), fp, 0); k = protocolnumber(b); if (k == -1) ns_info(ns_log_db, "%s:%d: unknown protocol: %s.", src, lineno, b); return ((char) k); } #ifndef BIND_UPDATE static #endif int getservices(int offset, char *data, FILE *fp, const char *src) { int j, ch, k, maxl, bracket; char bm[MAXPORT/8]; char b[MAXLEN]; for (j = 0; j < MAXPORT/8; j++) bm[j] = 0; maxl = 0; bracket = 0; while (getword(b, sizeof(b), fp, 0) || bracket) { if (feof(fp) || ferror(fp)) break; if (strlen(b) == 0) continue; if (b[0] == '(') { bracket++; continue; } if (b[0] == ')') { bracket = 0; while ((ch = getc(fp)) != EOF && ch != '\n') (void)NULL; if (ch == '\n') lineno++; break; } k = servicenumber(b); if (k == -1) { ns_info(ns_log_db, "%s:%d: Unknown service '%s'", src, lineno, b); continue; } if ((k < MAXPORT) && (k)) { bm[k/8] |= (0x80>>(k%8)); if (k > maxl) maxl = k; } else { ns_info(ns_log_db, "%s:%d: port no. (%d) too big", src, lineno, k); } } if (bracket) ns_info(ns_log_db, "%s:%d: missing close paren", src, lineno); maxl = maxl/8+1; memcpy(data+offset, bm, maxl); return (maxl+offset); } /* * Converts a word to a u_int32_t. Error if any non-numeric * characters in the word, except leading or trailing white space. */ static u_int32_t wordtouint32(buf) char *buf; { u_long result; u_int32_t res2; char *bufend; wordtouint32_error = 0; result = strtoul(buf, &bufend, 0); if (bufend == buf) wordtouint32_error = 1; else while ('\0' != *bufend) { if (isspace(*bufend)) bufend++; else { wordtouint32_error = 1; break; } } /* Check for truncation between u_long and u_int32_t */ res2 = result; if (res2 != result) wordtouint32_error = 1; return (res2); } static int getcharstring(char *buf, char *data, int type, int minfields, int maxfields, FILE *fp, const char *src) { int nfield = 0, done = 0, n = 0, i; char *b = buf; do { nfield++; i = strlen(buf); #ifdef ALLOW_LONG_TXT_RDATA b = buf; if (type == ns_t_txt || type == ns_t_x25) { while (i > MAXCHARSTRING && n + MAXCHARSTRING + 1 < MAXDATA) { data[n] = (char)MAXCHARSTRING; memmove(data + n + 1, b, MAXCHARSTRING); n += MAXCHARSTRING + 1; b += MAXCHARSTRING; i -= MAXCHARSTRING; } } #endif /* ALLOW_LONG_TXT_RDATA */ if (i > MAXCHARSTRING) { ns_info(ns_log_db, "%s:%d: RDATA field %d too long", src, lineno -1, nfield); return (0); } if (n + i + 1 > MAXDATA) { ns_info(ns_log_db, "%s:%d: total RDATA too long", src, lineno -1); return (0); } data[n] = i; memmove(data + n + 1, b, (int)i); n += i + 1; done = (maxfields && nfield >= maxfields); } while (!done && getword(buf, MAXDATA, fp, 0)); if (nfield < minfields) { ns_info(ns_log_db, "%s:%d: expected %d RDATA fields, only saw %d", src, lineno -1, minfields, nfield); return (0); } if (done) endline(fp); return (n); } /* * get_nxt_types(): Read the list of types in the NXT record. * * Data is the array where the bit flags are stored; it must * contain at least ns_t_any/NS_NXT_BITS bytes. * FP is the input FILE *. * Filename is the sourcefile * * The result is how many bytes are significant in the result. * ogud@tis.com 1995 */ static int get_nxt_types(u_char *data, FILE *fp, const char *filename) { char b[MAXLABEL]; /* Not quite the right size, but good enough */ int maxtype=0; int success; int type; int errs = 0; memset(data, 0, NS_NXT_MAX/NS_NXT_BITS+1); while (getmlword(b, sizeof(b), fp, 0)) { if (feof(fp) || ferror(fp)) break; if (strlen(b) == 0 || b[0] == '\n') continue; /* Parse RR type (A, MX, etc) */ type = sym_ston(__p_type_syms, (char *)b, &success); if ((!success) || type == ns_t_any) { errs++; ns_info(ns_log_db, "%s: Line %d: Unknown type: %s in NXT record.", filename, lineno, b); continue; } NS_NXT_BIT_SET(type, data); if (type > maxtype) maxtype = type; } if (errs) return (0); else return (maxtype/NS_NXT_BITS+1); } /* sanity checks PRIMARY ONLY */ static void fixup_soa(const char *fn, struct zoneinfo *zp) { /* Sanity: give enough time for the zone to transfer (retry). */ if (zp->z_expire < (zp->z_refresh + zp->z_retry)) ns_notice(ns_log_db, "%s: WARNING SOA expire value is less than SOA refresh+retry (%u < %u+%u)", fn, zp->z_expire, zp->z_refresh, zp->z_retry); /* Sanity. */ if (zp->z_expire < (zp->z_refresh + 10 * zp->z_retry)) ns_warning(ns_log_db, "%s: WARNING SOA expire value is less than refresh + 10 * retry \ (%u < (%u + 10 * %u))", fn, zp->z_expire, zp->z_refresh, zp->z_retry); /* * Sanity: most hardware/telco faults are detected and fixed within * a week, secondaries should continue to operate for this time. * (minimum of 4 days for long weekends) */ if (zp->z_expire < (7 * 24 * 3600)) ns_warning(ns_log_db, "%s: WARNING SOA expire value is less than 7 days (%u)", fn, zp->z_expire); /* * Sanity: maximum down time if we havn't talked for six months * war must have broken out. */ if (zp->z_expire > ( 183 * 24 * 3600)) ns_warning(ns_log_db, "%s: WARNING SOA expire value is greater than 6 months (%u)", fn, zp->z_expire); /* Sanity. */ if (zp->z_refresh < (zp->z_retry * 2)) ns_warning(ns_log_db, "%s: WARNING SOA refresh value is less than 2 * retry (%u < %u * 2)", fn, zp->z_refresh, zp->z_retry); } /* this function reads in the sig record rdata from the input file and * returns the following codes * > 0 length of the recrod * ERR_EOF end of file * */ static int parse_sig_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, u_int32_t ttl, enum context domain_ctx, enum transport transport, char **errmsg) { /* The SIG record looks like this in the db file: Name Cl SIG RRtype Algid [OTTL] Texp Tsig Kfoot Signer Sig where: Name and Cl are as usual SIG is a keyword RRtype is a char string ALGid is 8 bit u_int Labels is 8 bit u_int OTTL is 32 bit u_int (optionally present) Texp is YYYYMMDDHHMMSS Tsig is YYYYMMDDHHMMSS Kfoot is 16-bit unsigned decimal integer Signer is a char string Sig is 64 to 319 base-64 digits A missing OTTL is detected by the magnitude of the Texp value that follows it, which is larger than any u_int. The Labels field in the binary RR does not appear in the text RR. It's too crazy to run these pages of SIG code at the right margin. I'm exdenting them for readability. */ u_int32_t sig_type; int dateerror; int siglen, success; u_char *cp; u_int32_t al, la, n; u_int32_t signtime, exptime, timetilexp; u_int32_t origTTL; enum context context; time_t now; char *errtype = "SIG error"; int i, my_buf_size = MAXDATA, errs = 0; /* The TTL gets checked against the Original TTL, and bounded by the signature expiration time, which are both under the signature. We can't let TTL drift based on the SOA record. If defaulted, fix it now. (It's not clear to me why USE_MINIMUM isn't eliminated before putting ALL RR's into the database. -gnu@toad.com) */ if (ttl == USE_MINIMUM) ttl = zp->z_minimum; i = 0; data[i] = '\0'; getmlword_nesting = 0; /* KLUDGE err recovery */ /* RRtype (char *) * if old style inp will contain the next token *copy that into buffer, otherwise read from file */ if (buf && buf_len == 0) if (!getmlword((char*)buf, my_buf_size, fp, 0)) ERRTO("SIG record doesn't specify type"); sig_type = sym_ston(__p_type_syms, buf, &success); if (!success || sig_type == ns_t_any) { /* * We'll also accept a numeric RR type, * for signing RR types that this version * of named doesn't yet understand. * In the ns_t_any case, we rely on wordtouint32 * to fail when scanning the string "ANY". */ sig_type = wordtouint32 (buf); if (wordtouint32_error || sig_type > 0xFFFF) ERRTO("Unknown RR type in SIG record"); } cp = &data[i]; PUTSHORT((u_int16_t)sig_type, cp); i += 2; /* Algorithm id (8-bit decimal) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing algorithm ID"); al = wordtouint32(buf); if (0 == al || wordtouint32_error || 255 <= al) ERRTO("Bad algorithm number"); data[i] = (u_char) al; i++; /* * Labels (8-bit decimal) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing label count"); la = wordtouint32(buf); - if (0 == la || wordtouint32_error || 255 <= la) + if (wordtouint32_error || 255 <= la || + (0 == la && *domain != '\0')) ERRTO("Bad label count number"); data[i] = (u_char) la; i++; /* * OTTL (optional u_int32_t) and * Texp (u_int32_t date) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("OTTL and expiration time missing"); /* * See if OTTL is missing and this is a date. * This relies on good, silent error checking * in ns_datetosecs. */ exptime = ns_datetosecs(buf, &dateerror); if (!dateerror) { /* Output TTL as OTTL */ origTTL = ttl; cp = &data[i]; PUTLONG (origTTL, cp); i += 4; } else { /* Parse and output OTTL; scan TEXP */ origTTL = wordtouint32(buf); if (0 >= origTTL || wordtouint32_error || (origTTL > 0x7fffffff)) ERRTO("Original TTL value bad"); cp = &data[i]; PUTLONG(origTTL, cp); i += 4; if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Expiration time missing"); exptime = ns_datetosecs(buf, &dateerror); } if (dateerror || exptime > 0x7fffffff || exptime <= 0) ERRTO("Invalid expiration time"); cp = &data[i]; PUTLONG(exptime, cp); i += 4; /* Tsig (u_int32_t) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing signature time"); signtime = ns_datetosecs(buf, &dateerror); if (0 == signtime || dateerror) ERRTO("Invalid signature time"); cp = &data[i]; PUTLONG(signtime, cp); i += 4; /* Kfootprint (unsigned_16) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing key footprint"); n = wordtouint32(buf); if (wordtouint32_error || n >= 0x0ffff) ERRTO("Invalid key footprint"); cp = &data[i]; PUTSHORT((u_int16_t)n, cp); i += 2; /* Signer's Name */ if (!getmlword((char*)buf, my_buf_size, fp, 0)) ERRTO("Missing signer's name"); cp = &data[i]; strcpy((char *)cp, buf); context = domain_ctx; MAKENAME_OKZP((char *)cp, data_size); i += strlen((char *)cp) + 1; /* * Signature (base64 of any length) * We don't care what algorithm it uses or what * the internal structure of the BASE64 data is. */ if (!getallwords(buf, my_buf_size, fp, 0)) { siglen = 0; } else { cp = &data[i]; siglen = b64_pton(buf, (u_char*)cp, data_size - i); if (siglen < 0) ERRTO("Signature block bad"); } /* set total length and we're done! */ n = i + siglen; /* * Check signature time, expiration, and adjust TTL. Note * that all time values are in GMT (UTC), *not* local time. */ now = time (0); /* need to find a better place for this XXX ogud */ /* Don't let bogus name servers increase the signed TTL */ if (ttl > origTTL) ERRTO("TTL is greater than signed original TTL"); /* Don't let bogus signers "sign" in the future. */ if (signtime > (u_int32_t)now) ERRTO("signature time is in the future"); /* Ignore received SIG RR's that are already expired. */ if (exptime <= (u_int32_t)now) ERRTO("expiration time is in the past"); /* Lop off the TTL at the expiration time. */ timetilexp = exptime - now; if (timetilexp < ttl) { ns_debug(ns_log_load, 1, "shrinking expiring %s SIG TTL from %d to %d", p_secstodate(exptime), ttl, timetilexp); ttl = timetilexp; } /* * Check algorithm-ID and key structure, for * the algorithm-ID's that we know about. */ switch (al) { case NS_ALG_MD5RSA: if (siglen == 0) ERRTO("No key for RSA algorithm"); if (siglen < 1) ERRTO("Signature too short"); if (siglen > (NS_MD5RSA_MAX_BITS + 7) / 8) ERRTO("Signature too long"); break; case NS_ALG_DH: if (siglen < 1) ERRTO("DH Signature too short"); break; /* need more tests here */ case NS_ALG_DSA: if (siglen < NS_DSA_SIG_SIZE) ERRTO("DSS Signature too short"); else if (siglen > NS_DSA_SIG_SIZE) ERRTO("DSS Signature too long "); break; /* need more tests here */ case NS_ALG_EXPIRE_ONLY: if (siglen != 0) ERRTO( "Signature supplied to expire-only algorithm"); break; case NS_ALG_PRIVATE_OID: if (siglen == 0) ERRTO("No ObjectID in key"); break; default: ERRTO("UNKOWN SIG algorithm"); } /* Should we complain about algorithm-ID's that we don't understand? It may help debug some obscure cases, but in general we should accept any RR whether we could cryptographically process it or not; it may be being published for some newer DNS clients to validate themselves. */ endline(fp); /* flush the rest of the line */ return (n); err: *errmsg = errtype; return (-1); } static int parse_nxt_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, enum context context, enum transport transport, char **errmsg) { /* The NXT record looks like: Name Cl NXT nextname RRT1 RRT2 MX A SOA ... where: Name and Cl are as usual NXT is a keyword nextname is the next valid name in the zone after "Name". All names between the two are known to be nonexistent. RRT's... are a series of RR type names, which indicate that RR's of these types are published for "Name", and that no RR's of any other types are published for "Name". When a NXT record is cryptographically signed, it proves the nonexistence of an RR (actually a whole set of RR's). */ int n, errs = 0, i; u_char *cp; /* char *origin = zp->z_origin; int class = zp->z_class; */ *errmsg = "NXT name error"; (void) strcpy((char *)data, buf); MAKENAME_OKZP((char *)data, data_size); n = strlen((char *)data) + 1; cp = n + data; i = get_nxt_types(cp, fp, zp->z_source); if( i > 0) return (n + i); *errmsg = "NXT type error"; err: return (-1); } static int parse_cert_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, char **errmsg) { /* Cert record looks like: * Type Key_tag Alg Cert * Type: certification type number (16) * Key_tag: tag of corresponding KEY RR (16) * Alg: algorithm of the KEY RR (8) * Cert: base64 enocded block */ u_char *cp; u_int32_t cert_type, key_tag, alg; char *errtype = "CERT parse error"; int certlen, i, n, success; i = 0; cp = &data[i]; cert_type = sym_ston(__p_cert_syms, buf, &success); if (!success) { cert_type = wordtouint32(buf); if (wordtouint32_error || cert_type > 0xFFFF) ERRTO("CERT type out of range"); } PUTSHORT((u_int16_t)cert_type, cp); i += INT16SZ; if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("CERT doesn't specify type"); key_tag = wordtouint32(buf); if (wordtouint32_error || key_tag > 0xFFFF) ERRTO("CERT KEY tag out of range"); PUTSHORT((u_int16_t)key_tag, cp); i += INT16SZ; if (!getmlword(buf, buf_len, fp, 0)) ERRTO("CERT missing algorithm ID"); alg = sym_ston(__p_key_syms, buf, &success); if (!success) { alg = wordtouint32(buf); if (wordtouint32_error || alg > 0xFF) ERRTO("CERT KEY alg out of range"); } data[i++] = (u_char)alg; if (!getallwords(buf, buf_len, fp, 0)) { certlen = 0; } else { cp = &data[i]; certlen = b64_pton(buf, (u_char*)cp, sizeof(data) - i); if (certlen < 0) ERRTO("CERT blob has encoding error"); } /* set total length */ n = i + certlen; return (n); err: *errmsg = errtype; return (-1); } static int parse_key_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, enum context context, enum transport transport, char **errmsg) { /* The KEY record looks like this in the db file: * Name Cl KEY Flags Proto Algid PublicKeyData * where: * Name,Cl per usual * KEY RR type * Flags 4 digit hex value (unsigned_16) * Proto 8 bit u_int * Algid 8 bit u_int * PublicKeyData * a string of base64 digits, * skipping any embedded whitespace. */ u_int32_t al, pr; int nk, klen,i, n; u_int32_t keyflags; char *errtype = "KEY error"; u_char *cp, *expstart; u_int expbytes, modbytes; i = n = 0; data[i] = '\0'; cp = data; getmlword_nesting = 0; /* KLUDGE err recov. */ /*>>> Flags (unsigned_16) */ keyflags = wordtouint32(buf); if (wordtouint32_error || 0xFFFF < keyflags) ERRTO("KEY flags error"); if (keyflags & NS_KEY_RESERVED_BITMASK) ERRTO("KEY Reserved Flag Bit"); PUTSHORT(keyflags, cp); /*>>> Protocol (8-bit decimal) */ if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("KEY Protocol Field"); pr = wordtouint32(buf); if (wordtouint32_error || 255 < pr) ERRTO("KEY Protocol Field"); *cp++ = (u_char) pr; /*>>> Algorithm id (8-bit decimal) */ if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("KEY Algorithm ID"); al = wordtouint32(buf); if (wordtouint32_error || 0 == al || 255 == al || 255 < al) ERRTO("KEY Algorithm ID"); *cp++ = (u_char) al; /*>>> Extended KEY flag field in bytes 5 and 6 */ if (NS_KEY_EXTENDED_FLAGS & keyflags) { u_int32_t keyflags2; if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("KEY Flags Field"); keyflags2 = wordtouint32(buf); if (wordtouint32_error || 0xFFFF < keyflags2) ERRTO("Extended key flags error"); if (keyflags2 & NS_KEY_RESERVED_BITMASK2) ERRTO("KEY Reserved Flag2 Bit"); PUTSHORT(keyflags2, cp); } /*>>> Public Key data is in BASE64. * We don't care what algorithm it uses or what * the internal structure of the BASE64 data is. */ if (!getallwords(buf, MAXDATA, fp, 0)) klen = 0; else { /* Convert from BASE64 to binary. */ klen = b64_pton(buf, (u_char*)cp, data_size - (cp - data)); if (klen < 0) ERRTO("KEY Public Key"); } /* set total length */ n = klen + (cp - data); /* * Now check for valid key flags & algs & etc, from the RFC. */ if (NS_KEY_TYPE_NO_KEY == (keyflags & NS_KEY_TYPEMASK)) nk = 1; /* No-key */ else nk = 0; /* have a key */ if ((keyflags & (NS_KEY_NAME_TYPE | NS_KEY_TYPEMASK)) == (NS_KEY_NAME_ZONE | NS_KEY_TYPE_CONF_ONLY)) /* Zone key must have Auth bit set. */ ERRTO("KEY Zone Key Auth. bit"); if (al == 0 && nk == 0) ERRTO("KEY Algorithm"); if (al != 0 && pr == 0) ERRTO("KEY Protocols"); if (nk == 1 && klen != 0) ERRTO("KEY No-Key Flags Set"); if (nk == 0 && klen == 0) ERRTO("KEY Type Spec'd"); /* * Check algorithm-ID and key structure, for the algorithm-ID's * that we know about. */ switch (al) { case NS_ALG_MD5RSA: if (klen == 0) break; expstart = cp; expbytes = *expstart++; if (expbytes == 0) GETSHORT(expbytes, expstart); if (expbytes < 1) ERRTO("Exponent too short"); if (expbytes > (NS_MD5RSA_MAX_BITS + 7) / 8) ERRTO("Exponent too long"); if (*expstart == 0) ERRTO("Exponent w/ 0"); modbytes = klen - (expbytes + (expstart - cp)); if (modbytes < (NS_MD5RSA_MIN_BITS + 7) / 8) ERRTO("Modulus too short"); if (modbytes > (NS_MD5RSA_MAX_BITS + 7) / 8) ERRTO("Modulus too long"); if (*(expstart+expbytes) == 0) ERRTO("Modulus starts w/ 0"); break; case NS_ALG_DH: { u_char *dh_cp; u_int16_t dh_len, plen, glen, ulen; dh_cp = (u_char *)cp; GETSHORT(plen, dh_cp); if(plen < 16) ERRTO("DH short plen"); dh_len = 2 + plen; if(dh_len > klen) ERRTO("DH plen > klen"); GETSHORT(glen, dh_cp); if(glen <= 0 || glen > plen) ERRTO("DH glen bad"); dh_len = 2 + glen; if(dh_len > klen) ERRTO("DH glen > klen"); GETSHORT(ulen, dh_cp); if(ulen <= 0 || ulen > plen) ERRTO("DH ulen bad"); dh_len = 2 + ulen; if(dh_len > klen) ERRTO("DH ulen > klen"); else if (dh_len < klen) ERRTO("DH *len < klen"); break; } case NS_ALG_DSA: { u_int8_t t; if ( klen == 0) break; t = *cp; if (t > 8) ERRTO("DSA T value"); if (klen != (1 + 20 + 3 *(64+8*t))) ERRTO("DSA length"); break; } case NS_ALG_PRIVATE_OID: if (klen == 0) ERRTO("No ObjectID in key"); break; default: ERRTO("Unknown Key algorithm"); } endline(fp); /* flush the rest of the line */ return (n); err: *errmsg = errtype; return (-1); } /*T_KEY*/ /* * function to invoke DNSSEC specific parsing routines. * this is simpler than copying these complicated blocks into the * multiple souce files that read files (ixfr, nsupdate etc..). * this code should be in a library rather than in this file but * what the heck for now (ogud@tislabs.com) */ int parse_sec_rdata(char *buf, int buf_len, int buf_full, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, u_int32_t ttl, int type, enum context context, enum transport transport, char **errmsg) { int ret = -1; getmlword_nesting = 0; /* KLUDGE err recov. */ if (!buf_full && buf && buf_len != 0) /* check if any data in buf */ if (!getmlword(buf, buf_len, fp, 1)) { *errmsg = "unexpected end of input"; goto err; } switch (type) { case ns_t_sig: ret = parse_sig_rr(buf, buf_len, data, data_size, fp, zp, domain, ttl, context, transport, errmsg); break; case ns_t_key: ret = parse_key_rr(buf, buf_len, data, data_size, fp, zp, domain, context, transport, errmsg); break; case ns_t_nxt: ret = parse_nxt_rr(buf, buf_len, data, data_size, fp, zp, domain, context, transport, errmsg); break; case ns_t_cert: ret = parse_cert_rr(buf, buf_len, data, data_size, fp, errmsg); break; default: ret = -1; *errmsg = "parse_sec_rdata():Unsupported SEC type type"; goto err; } return (ret); err: endline(fp); return (ret); } Index: head/contrib/bind/bin/named/db_lookup.c =================================================================== --- head/contrib/bind/bin/named/db_lookup.c (revision 60940) +++ head/contrib/bind/bin/named/db_lookup.c (revision 60941) @@ -1,331 +1,341 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_lookup.c 4.18 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: db_lookup.c,v 8.24 1999/10/15 19:48:58 vixie Exp $"; +static const char rcsid[] = "$Id: db_lookup.c,v 8.26 2000/04/21 06:54:03 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Table lookup routines. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* * Lookup 'name' and return a pointer to the namebuf; * NULL otherwise. If 'insert', insert name into tables. * Wildcard lookups are handled. */ struct namebuf * nlookup(const char *name, struct hashbuf **htpp, const char **fname, int insert) { struct namebuf *np; const char *cp; int c; u_int hval; struct hashbuf *htp; struct namebuf *parent = NULL; int escaped = 0; htp = *htpp; hval = 0; *fname = "???"; for (cp = name; (c = *cp++) != 0; (void)NULL) { if (!escaped && (c == '.')) { parent = np = nlookup(cp, htpp, fname, insert); if (np == NULL) return (NULL); if (*fname != cp) return (np); if ((htp = np->n_hash) == NULL) { if (!insert) { if (ns_wildcard(NAME(*np))) *fname = name; return (np); } htp = savehash((struct hashbuf *)NULL); np->n_hash = htp; } *htpp = htp; break; } HASHIMILATE(hval, c); if (escaped) escaped = 0; else if (c == '\\') escaped = 1; } cp--; /* * Lookup this label in current hash table. */ for (np = htp->h_tab[hval % htp->h_size]; np != NULL; np = np->n_next) { if (np->n_hashval == hval && ((size_t)NAMELEN(*np) == (size_t)(cp - name)) && (strncasecmp(name, NAME(*np), cp - name) == 0)) { *fname = name; return (np); } } if (!insert) { /* * Look for wildcard in this hash table. * Don't use a cached "*" name as a wildcard, * only authoritative. */ hval = ('*' & HASHMASK) % htp->h_size; for (np = htp->h_tab[hval]; np != NULL; np = np->n_next) { if (ns_wildcard(NAME(*np)) && np->n_data && np->n_data->d_zone != 0) { *fname = name; return (np); } } return (parent); } np = savename(name, cp - name); np->n_parent = parent; np->n_hashval = hval; hval %= htp->h_size; np->n_next = htp->h_tab[hval]; htp->h_tab[hval] = np; /* Increase hash table size. */ if (++htp->h_cnt > (htp->h_size * AVGCH_NLOOKUP)) { *htpp = savehash(htp); if (parent == NULL) { if (htp == hashtab) { hashtab = *htpp; } else { fcachetab = *htpp; } } else parent->n_hash = *htpp; htp = *htpp; } *fname = name; return (np); } /* struct namebuf * * np_parent(struct namebuf *np) * Find the "parent" namebuf of np. * This is tricky since the parent of "com" is "" and both are stored * in the same hashbuf. * See also: * the AXFR wart description in ns_axfr.c */ struct namebuf * np_parent(struct namebuf *np) { struct hashbuf *htp; struct namebuf *np2; if (np->n_parent != NULL || NAME(*np)[0] == '\0') return (np->n_parent); /* Try to figure out if np is pointing into the cache or hints. */ /* Try the cache first. */ htp = hashtab; try_again: /* Search the hash chain that np should be part of. */ for (np2 = htp->h_tab[np->n_hashval % htp->h_size]; np2 != NULL; np2 = np2->n_next) { if (np == np2) { /* found it! */ /* "" hashes into the first bucket */ for (np = htp->h_tab[0]; np != NULL; np = np->n_next) { if (NAME(*np)[0] == '\0') /* found the root namebuf */ return (np); } /* there are no RR's with a owner name of "." yet */ return (NULL); } } /* Try the hints. */ if (htp == hashtab) { htp = fcachetab; goto try_again; } ns_debug(ns_log_db, 1, "np_parent(0x%lx) couldn't find namebuf", (u_long)np); return (NULL); /* XXX shouldn't happen */ } /* int * match(dp, class, type) * Does data record `dp' match the class and type? * return value: * boolean */ int match(struct databuf *dp, int class, int type) { if (dp->d_class != class && class != C_ANY) return (0); if (dp->d_type != type && dp->d_type != T_SIG && type != T_ANY) return (0); if (type != T_SIG && dp->d_type == T_SIG && SIG_COVERS(dp) != type) return (0); return (1); } /* static int * nxtlower(name, dp) * Is the NXT/SIG NXT record 'lower'? * return value: * boolean */ static int nxtlower(const char *name, struct databuf *dp) { /* An NXT is a lower NXT iff the SOA bit is set in the bitmap */ if (dp->d_type == T_NXT) { u_char *nxtbitmap = dp->d_data + strlen((char *)dp->d_data) + 1; return (NS_NXT_BIT_ISSET(T_SOA, nxtbitmap) ? 1 : 0); } /* If it's not an NXT, it's a SIG NXT. An NXT record must be signed * by the zone, so the signer name must be the same as the owner. */ return (ns_samename(name, (char *)dp->d_data + SIG_HDR_SIZE) != 1 ? 0 : 1); } /* int * nxtmatch(name, dp1, dp2) * Do NXT/SIG NXT records `dp1' and `dp2' belong to the same NXT set? * return value: * boolean */ int nxtmatch(const char *name, struct databuf *dp1, struct databuf *dp2) { int dp1_lower, dp2_lower; - - if (dp1->d_type != ns_t_nxt || dp2->d_type != ns_t_nxt) + int type1, type2; + + if (dp1->d_type == ns_t_sig) + type1 = SIG_COVERS(dp1); + else + type1 = dp1->d_type; + if (dp2->d_type == ns_t_sig) + type2 = SIG_COVERS(dp2); + else + type2 = dp2->d_type; + + if (type1 != ns_t_nxt || type2 != ns_t_nxt) return (0); dp1_lower = nxtlower(name, dp1); dp2_lower = nxtlower(name, dp2); return (dp1_lower == dp2_lower); } /* int * rrmatch(name, dp1, dp2) * Do data records `dp1' and `dp2' match in class and type? * If both are NXTs, do they belong in the same NXT set? * If both are SIGs, do the covered types match? * If both are SIG NXTs, do the covered NXTs belong in the same set? * Why is DNSSEC so confusing? * return value: * boolean */ int rrmatch(const char *name, struct databuf *dp1, struct databuf *dp2) { if (dp1->d_class != dp2->d_class && dp1->d_class != C_ANY && dp2->d_class != C_ANY) return(0); if (dp1->d_type != dp2->d_type && dp1->d_type != T_ANY && dp2->d_type != T_ANY) return(0); if (dp1->d_type == T_NXT) return(nxtmatch(name, dp1, dp2)); if (dp1->d_type != T_SIG) return(1); if (SIG_COVERS(dp1) == SIG_COVERS(dp2)) { if (SIG_COVERS(dp1) == ns_t_nxt) return(nxtmatch(name, dp1, dp2)); else return(1); } return(0); } Index: head/contrib/bind/bin/named/db_save.c =================================================================== --- head/contrib/bind/bin/named/db_save.c (revision 60940) +++ head/contrib/bind/bin/named/db_save.c (revision 60941) @@ -1,211 +1,211 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_save.c 4.16 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: db_save.c,v 8.26 1999/10/13 16:39:02 vixie Exp $"; +static const char rcsid[] = "$Id: db_save.c,v 8.27 2000/04/21 06:54:03 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Buffer allocation and deallocation routines. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* * Allocate a name buffer & save name. */ struct namebuf * savename(const char *name, int len) { struct namebuf *np; /* * Note that MAXLABEL * 4 < 256, so a single length byte is enough. * Also, we use MAXLABEL * 4 because each label character can * expand into up to four characters when rendered in canonical * form. */ INSIST(len >= 0 && len <= (MAXLABEL * 4)); np = (struct namebuf *) memget(NAMESIZE(len)); if (np == NULL) panic("savename: memget", NULL); memset(np, 0, NAMESIZE(len)); NAMELEN(*np) = (unsigned)len; memcpy(NAME(*np), name, len); NAME(*np)[len] = '\0'; return (np); } /* * Allocate a data buffer & save data. */ struct databuf * savedata(class, type, ttl, data, size) int class, type; u_int32_t ttl; u_char *data; int size; { struct databuf *dp; int bytes = DATASIZE(size); dp = (struct databuf *)memget(bytes); if (dp == NULL) panic("savedata: memget", NULL); if (class > CLASS_MAX) panic("savedata: bad class", NULL); memset(dp, 0, bytes); dp->d_next = NULL; dp->d_type = type; dp->d_class = class; dp->d_ttl = ttl; dp->d_size = size; dp->d_mark = 0; dp->d_flags = 0; dp->d_cred = 0; dp->d_clev = 0; dp->d_secure = DB_S_INSECURE; dp->d_rcode = NOERROR; dp->d_ns = NULL; dp->d_nstime = 0; memcpy(dp->d_data, data, dp->d_size); return (dp); } /* * Allocate a data buffer & save data. */ struct hashbuf * savehash(oldhtp) struct hashbuf *oldhtp; { struct hashbuf *htp; struct namebuf *np, *nnp, **hp; int n, newsize; if (oldhtp == NULL) newsize = hashsizes[0]; else { for (n = 0; (newsize = hashsizes[n++]) != 0; (void)NULL) if (oldhtp->h_size == newsize) { newsize = hashsizes[n]; break; } if (newsize == 0) newsize = oldhtp->h_size * 2 + 1; } ns_debug(ns_log_db, 4, "savehash GROWING to %d", newsize); htp = (struct hashbuf *) memget(HASHSIZE(newsize)); if (htp == NULL) ns_panic(ns_log_db, 0, "savehash: %s", strerror(errno)); htp->h_size = newsize; memset(htp->h_tab, 0, newsize * sizeof(struct namebuf *)); if (oldhtp == NULL) { htp->h_cnt = 0; return (htp); } ns_debug(ns_log_db, 4, "savehash(%#lx) cnt=%d, sz=%d, newsz=%d", (u_long)oldhtp, oldhtp->h_cnt, oldhtp->h_size, newsize); htp->h_cnt = oldhtp->h_cnt; for (n = 0; n < oldhtp->h_size; n++) { for (np = oldhtp->h_tab[n]; np != NULL; np = nnp) { nnp = np->n_next; hp = &htp->h_tab[np->n_hashval % htp->h_size]; np->n_next = *hp; *hp = np; } } oldhtp->h_cnt = 0; /* Keep rm_hash() happy. */ rm_hash(oldhtp); return (htp); } Index: head/contrib/bind/bin/named/db_sec.c =================================================================== --- head/contrib/bind/bin/named/db_sec.c (revision 60940) +++ head/contrib/bind/bin/named/db_sec.c (revision 60941) @@ -1,1097 +1,1097 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: db_sec.c,v 8.30 1999/10/15 21:06:49 vixie Exp $"; +static const char rcsid[] = "$Id: db_sec.c,v 8.31 2000/04/21 06:54:04 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct zpubkey { struct dst_key *zpk_key; /* Should be DST_KEY */ char *zpk_name; struct zpubkey *zpk_next; }; typedef struct zpubkey *zpubkey_list; static int nxt_match_rrset(struct databuf *dp, struct db_rrset *rrset); /* * A converted databuf is a stripped down databuf after converting the * data to wire format. */ struct converted_databuf { struct converted_databuf *cd_next; u_char *cd_data; int cd_size, cd_alloc; }; /* All of the trusted keys and zone keys */ static tree *trusted_keys = NULL; static int compare_pubkey (struct zpubkey *zpk1, struct zpubkey *zpk2) { char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; if (ns_makecanon(zpk1->zpk_name, ta, sizeof ta) < 0 || ns_makecanon(zpk2->zpk_name, tb, sizeof tb) < 0) return (-1); return (strcasecmp(ta, tb)); } static struct zpubkey * tree_srch_pubkey (const char *name) { struct zpubkey tkey, *key; tkey.zpk_name = (char *) name; if (trusted_keys == NULL) { tree_init(&trusted_keys); return (NULL); } key = (struct zpubkey *)tree_srch(&trusted_keys, compare_pubkey, &tkey); return (key); } static DST_KEY * find_public_key (const char *name, u_int16_t key_id) { struct namebuf *knp; struct hashbuf *htp; struct databuf *dp; const char *fname; DST_KEY *key; ns_debug(ns_log_default, 5, "find_public_key(%s, %d)", name, key_id); htp = hashtab; knp = nlookup (name, &htp, &fname, 0); if (fname != name) /* The name doesn't exist, so there's no key */ return (NULL); for (dp = knp->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_type != ns_t_key || dp->d_secure < DB_S_SECURE) continue; key = dst_dnskey_to_key(name, dp->d_data, dp->d_size); /* XXX what about multiple keys with same footprint? */ if (key) { if (key->dk_id == ntohs(key_id)) return (key); else dst_free_key(key); } } return (NULL); } static DST_KEY * find_trusted_key (const char *name, u_int16_t key_id) { struct zpubkey *zpk; zpubkey_list keylist = tree_srch_pubkey (name); ns_debug(ns_log_default, 5, "find_trusted_key(%s, %d)", name, key_id); for (zpk = keylist; zpk; zpk = zpk->zpk_next) if (zpk->zpk_key->dk_id == ntohs(key_id)) return (zpk->zpk_key); return (NULL); } int add_trusted_key (const char *name, const int flags, const int proto, const int alg, const char *str) { zpubkey_list keylist; struct zpubkey *zpk; u_char buf[1024]; int n; keylist = tree_srch_pubkey (name); zpk = (struct zpubkey *) memget (sizeof (struct zpubkey)); if (zpk == NULL) ns_panic(ns_log_default, 1, "add_trusted_key: memget failed(%s)", name); n = b64_pton(str, buf, sizeof(buf)); if (n < 0) goto failure; zpk->zpk_key = dst_buffer_to_key(name, alg, flags, proto, buf, n); if (zpk->zpk_key == NULL) { ns_warning(ns_log_default, "add_trusted_key: dst_buffer_to_key(%s) failed", name); goto failure; } zpk->zpk_name = zpk->zpk_key->dk_key_name; zpk->zpk_next = NULL; if (keylist == NULL) { if (tree_add (&trusted_keys, compare_pubkey, zpk, NULL) == NULL) goto failure; } else { struct zpubkey *tkey = keylist; while (tkey->zpk_next) tkey = tkey->zpk_next; tkey->zpk_next = zpk; } return (1); failure: memput(zpk, sizeof (struct zpubkey)); return (0); } /* Can the signer sign records for this name? This is a heuristic. */ static int can_sign(const char *name, const char *signer) { return (ns_samedomain(name, signer) && dn_count_labels(name) - dn_count_labels(signer) <= 2); } static int rrset_set_security(struct db_rrset *rrset, int slev) { struct dnode *dnp; for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) dnp->dp->d_secure = slev; for (dnp = rrset->rr_sigs; dnp != NULL; dnp = dnp->dn_next) dnp->dp->d_secure = slev; return (slev); } static int convert_databuf(struct databuf *dp, struct converted_databuf *cdp) { u_char *bp = cdp->cd_data; u_char *cp = dp->d_data; u_char *eob = cdp->cd_data + cdp->cd_alloc; int len; u_char buf[MAXDNAME]; switch (dp->d_type) { case ns_t_soa: case ns_t_minfo: case ns_t_rp: if (eob - bp < strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; if (eob - bp < strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; if (dp->d_type == ns_t_soa) { if (eob - bp < 5 * INT32SZ) return (-1); memcpy(bp, cp, 5 * INT32SZ); bp += (5 * INT32SZ); cp += (5 * INT32SZ); } break; case ns_t_ns: case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ptr: case ns_t_nxt: if (eob - bp < strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += (len = strlen((char *)cp) + 1); if (dp->d_type == ns_t_nxt) { if (eob - bp < dp->d_size - len) return (-1); memcpy(bp, cp, dp->d_size - len); bp += (dp->d_size - len); cp += (dp->d_size - len); } break; case ns_t_srv: if (eob - bp < 2 * INT16SZ) return (-1); memcpy(bp, cp, 2 * INT16SZ); bp += (2 * INT16SZ); cp += (2 * INT16SZ); /* no break */ case ns_t_rt: case ns_t_mx: case ns_t_afsdb: case ns_t_px: if (eob - bp < INT16SZ) return (-1); memcpy (bp, cp, INT16SZ); bp += INT16SZ; cp += INT16SZ; if (eob - bp < strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; if (dp->d_type == ns_t_px) { if (eob - bp < strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; } break; default: if (eob - bp < dp->d_size) return (-1); memcpy(bp, cp, dp->d_size); bp += dp->d_size; } cdp->cd_size = bp - cdp->cd_data; return (cdp->cd_size); } static int digest_rr(char *envelope, int elen, struct converted_databuf *cdp, char *buffer, int blen) { char *bp = buffer, *eob = buffer + blen; if (eob - bp < elen) return (-1); memcpy (bp, envelope, elen); bp += elen; if (eob - bp < INT16SZ) return (-1); PUTSHORT(cdp->cd_size, bp); if (eob - bp < cdp->cd_size) return (-1); memcpy (bp, cdp->cd_data, cdp->cd_size); bp += cdp->cd_size; return (bp - buffer); } /* Sorts the converted databuf in the list */ static void insert_converted_databuf(struct converted_databuf *cdp, struct converted_databuf **clist) { struct converted_databuf *tcdp, *next; int t; #define compare_cdatabuf(c1, c2, t) \ (t = memcmp(c1->cd_data, c2->cd_data, MIN(c1->cd_size, c2->cd_size)), \ t == 0 ? c1->cd_size - c2->cd_size : t) if (*clist == NULL) { *clist = cdp; return; } tcdp = *clist; if (compare_cdatabuf(cdp, tcdp, t) < 0) { cdp->cd_next = tcdp; *clist = cdp; return; } next = tcdp->cd_next; while (next) { if (compare_cdatabuf(cdp, next, t) < 0) { cdp->cd_next = next; tcdp->cd_next = cdp; return; } tcdp = next; next = next->cd_next; } tcdp->cd_next = cdp; #undef compare_cdatabuf } static void free_clist(struct converted_databuf *clist) { struct converted_databuf *cdp; while (clist != NULL) { cdp = clist; clist = clist->cd_next; memput(cdp->cd_data, cdp->cd_alloc); memput(cdp, sizeof(struct converted_databuf)); } } /* Removes all empty nodes from an rrset's SIG list. */ static void rrset_trim_sigs(struct db_rrset *rrset) { struct dnode *dnp, *odnp, *ndnp; odnp = NULL; dnp = rrset->rr_sigs; while (dnp != NULL) { if (dnp->dp != NULL) { odnp = dnp; dnp = dnp->dn_next; } else { if (odnp != NULL) odnp->dn_next = dnp->dn_next; else rrset->rr_sigs = dnp->dn_next; ndnp = dnp->dn_next; memput(dnp, sizeof(struct dnode)); dnp = ndnp; } } } int verify_set(struct db_rrset *rrset) { DST_KEY *key = NULL; struct sig_record *sigdata; struct dnode *sigdn; struct databuf *sigdp; time_t now; char *signer; u_char name_n[MAXDNAME]; u_char *sig, *eom; int trustedkey = 0, siglen, labels, len = 0, ret; u_char *buffer = NULL, *bp; u_char envelope[MAXDNAME+32], *ep; struct dnode *dnp; int bufsize = 2048; /* Large enough for MAXDNAME + SIG_HDR_SIZE */ struct converted_databuf *clist = NULL, *cdp; int dnssec_failed = 0, dnssec_succeeded = 0; int return_value; int i; if (rrset == NULL || rrset->rr_name == NULL) { ns_warning (ns_log_default, "verify_set: missing rrset/name"); return (rrset_set_security(rrset, DB_S_FAILED)); } if (rrset->rr_sigs == NULL) return (rrset_set_security(rrset, DB_S_INSECURE)); ns_debug(ns_log_default, 5, "verify_set(%s, %s, %s)", rrset->rr_name, p_type(rrset->rr_type), p_class(rrset->rr_class)); now = time(NULL); for (sigdn = rrset->rr_sigs; sigdn != NULL; sigdn = sigdn->dn_next) { u_int32_t namefield; struct sig_record sigrec; sigdp = sigdn->dp; eom = sigdp->d_data + sigdp->d_size; if (sigdp->d_size < SIG_HDR_SIZE) { return_value = DB_S_FAILED; goto end; } memcpy(&sigrec, sigdp->d_data, SIG_HDR_SIZE); sigdata = &sigrec; signer = (char *)sigdp->d_data + SIG_HDR_SIZE; sig = (u_char *)signer + strlen(signer) + 1; siglen = eom - sig; /* * Don't verify a set if the SIG inception time is in * the future. This should be fixed before 2038 (BEW) */ if (ntohl(sigdata->sig_time_n) > now) continue; /* An expired set is dropped, but the data is not. */ if (ntohl(sigdata->sig_exp_n) < now) { db_freedata(sigdp); sigdn->dp = NULL; continue; } /* Cleanup from the last iteration if we continue'd */ if (trustedkey == 0 && key != NULL) dst_free_key(key); key = find_trusted_key(signer, sigdata->sig_keyid_n); if (key == NULL) { trustedkey = 0; key = find_public_key(signer, sigdata->sig_keyid_n); } else trustedkey = 1; /* if we don't have the key, either * - the data should be considered insecure * - the sig is not a dnssec signature */ if (key == NULL) continue; /* Can a key with this name sign the data? */ if (!can_sign(rrset->rr_name, signer)) continue; /* Check the protocol and flags of the key */ if (key->dk_proto != NS_KEY_PROT_DNSSEC && key->dk_proto != NS_KEY_PROT_ANY) continue; if (key->dk_flags & NS_KEY_NO_AUTH) continue; namefield = key->dk_flags & NS_KEY_NAME_TYPE; if (namefield == NS_KEY_NAME_USER || namefield == NS_KEY_NAME_RESERVED) continue; if (namefield == NS_KEY_NAME_ENTITY && (key->dk_flags & NS_KEY_SIGNATORYMASK == 0)) continue; /* * If we're still here, we have a non-null key that's either * a zone key or an entity key with signing authority. */ if (buffer == NULL) { bp = buffer = memget(bufsize); if (bp == NULL) { return_value = DB_S_FAILED; goto end; } } else bp = buffer; /* Digest the fixed portion of the SIG record */ memcpy(bp, (char *) sigdata, SIG_HDR_SIZE); bp += SIG_HDR_SIZE; /* Digest the signer's name, canonicalized */ if (ns_name_pton(signer, name_n, sizeof name_n) < 0) { return_value = DB_S_FAILED; goto end; } i = ns_name_ntol(name_n, (u_char *)bp, bufsize - SIG_HDR_SIZE); if (i < 0) { return_value = DB_S_FAILED; goto end; } bp += i; /* create the dns record envelope: * */ if (ns_name_pton(rrset->rr_name, name_n, sizeof name_n) < 0 || ns_name_ntol(name_n, (u_char *)envelope, sizeof envelope) < 0) { return_value = DB_S_FAILED; goto end; } labels = dn_count_labels(rrset->rr_name); if (labels > sigdata->sig_labels_n) { ep = envelope; for (i=0; i < (labels - 1 - sigdata->sig_labels_n); i++) ep += (*ep+1); i = dn_skipname(ep, envelope + sizeof envelope); if (i < 0) { return_value = DB_S_FAILED; goto end; } envelope[0] = '\001'; envelope[1] = '*'; memmove(envelope + 2, ep, i); } i = dn_skipname(envelope, envelope + sizeof envelope); if (i < 0) { return_value = DB_S_FAILED; goto end; } ep = envelope + i; PUTSHORT (rrset->rr_type, ep); PUTSHORT (rrset->rr_class, ep); if (envelope + sizeof(envelope) - ep < INT32SZ) { return_value = DB_S_FAILED; goto end; } memcpy (ep, &sigdata->sig_ottl_n, INT32SZ); ep += INT32SZ; if (clist == NULL) { for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) { struct databuf *dp = dnp->dp; cdp = memget(sizeof(struct converted_databuf)); if (cdp == NULL) { return_value = DB_S_FAILED; goto end; } memset(cdp, 0, sizeof(*cdp)); /* Should be large enough... */ cdp->cd_alloc = dp->d_size + 8; cdp->cd_data = memget(cdp->cd_alloc); if (cdp->cd_data == NULL) { memput(cdp, sizeof(*cdp)); return_value = DB_S_FAILED; goto end; } while (convert_databuf(dp, cdp) < 0) { memput(cdp->cd_data, cdp->cd_alloc); cdp->cd_alloc *= 2; cdp->cd_data = memget(cdp->cd_alloc); if (cdp->cd_data == NULL) { memput(cdp, sizeof(*cdp)); return_value = DB_S_FAILED; goto end; } } insert_converted_databuf(cdp, &clist); } } for (cdp = clist; cdp != NULL; cdp = cdp->cd_next) { len = digest_rr((char *)envelope, ep-envelope, cdp, (char *)bp, bufsize - (bp - buffer)); while (len < 0) { u_char *newbuf; /* Double the buffer size */ newbuf = memget(bufsize*2); if (newbuf == NULL) { return_value = DB_S_FAILED; goto end; } memcpy(newbuf, buffer, bp - buffer); bp = (bp - buffer) + newbuf; memput(buffer, bufsize); buffer = newbuf; bufsize *= 2; len = digest_rr((char *)envelope, ep-envelope, cdp, (char *)bp, bufsize - (bp - buffer)); } bp += len; } if (len < 0) { return_value = DB_S_FAILED; goto end; } ret = dst_verify_data(SIG_MODE_ALL, key, NULL, buffer, bp - buffer, sig, siglen); if (ret < 0) { dnssec_failed++; db_freedata(sigdp); sigdn->dp = NULL; } else dnssec_succeeded++; } end: if (dnssec_failed > 0) rrset_trim_sigs(rrset); if (trustedkey == 0 && key != NULL) dst_free_key(key); if (dnssec_failed > 0 && dnssec_succeeded == 0) { ns_warning (ns_log_default, "verify_set(%s, %s, %s) failed", rrset->rr_name, p_type(rrset->rr_type), p_class(rrset->rr_class)); return_value = DB_S_FAILED; } else if (dnssec_succeeded > 0) return_value = DB_S_SECURE; else return_value = DB_S_INSECURE; free_clist(clist); if (buffer != NULL) memput(buffer, bufsize); return (rrset_set_security(rrset, return_value)); } static void rrset_free_partial(struct db_rrset *rrset, int free_data, struct dnode *start) { struct dnode *dnp; int found_start = 0; ns_debug(ns_log_default, 5, "rrset_free(%s)", rrset->rr_name); if (start == NULL) found_start = 1; while (rrset->rr_list) { dnp = rrset->rr_list; if (dnp == start) found_start = 1; rrset->rr_list = rrset->rr_list->dn_next; if (dnp->dp != NULL && free_data == 1 && found_start == 1) db_freedata(dnp->dp); memput(dnp, sizeof(struct dnode)); } while (rrset->rr_sigs) { dnp = rrset->rr_sigs; if (dnp == start) found_start = 1; rrset->rr_sigs = rrset->rr_sigs->dn_next; if (dnp->dp != NULL && free_data == 1 && found_start == 1) db_freedata(dnp->dp); memput(dnp, sizeof(struct dnode)); } } static void rrset_free(struct db_rrset *rrset, int free_data) { rrset_free_partial(rrset, free_data, NULL); } /* * This is called when we have an rrset with SIGs and no other data. * Returns 1 if we either found the necessary data or if the SIG can be added * with no other data. 0 indicates that the SIG cannot be added. */ static int attach_data(struct db_rrset *rrset) { int type, class; struct databuf *dp, *newdp, *sigdp; struct dnode *dnp; struct namebuf *np; struct hashbuf *htp; char *signer; const char *fname; char *name = rrset->rr_name; sigdp = rrset->rr_sigs->dp; type = SIG_COVERS(sigdp); class = sigdp->d_class; signer = (char *)(sigdp + SIG_HDR_SIZE); /* First, see if the signer can sign data for the name. If not, * it's not a DNSSEC signature, so we can insert it with no * corresponding data. */ if (!can_sign(name, signer)) return (1); htp = hashtab; np = nlookup (name, &htp, &fname, 0); if (fname != name) return (0); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_type == type && dp->d_class == class) { newdp = savedata(class, type, dp->d_ttl, dp->d_data, dp->d_size); dnp = (struct dnode *) memget (sizeof (struct dnode)); if (dnp == NULL) ns_panic(ns_log_default, 1, "attach_data: memget failed"); dnp->dp = newdp; dnp->dn_next = rrset->rr_list; rrset->rr_list = dnp; } } if (rrset->rr_list != NULL) return (1); else return (0); } static int rrset_db_update(struct db_rrset *rrset, int flags, struct hashbuf **htpp, struct sockaddr_in from, int *rrcount) { struct dnode *dnp; struct databuf *dp; int ret; /* If we have any unattached SIG records that are DNSSEC signatures, * don't cache them unless we already have the corresponding data. * If we do cache unattached SIGs, we run into problems later if we * have a SIG X and get a query for type X. */ if (rrset->rr_list == NULL) { if (attach_data(rrset) == 0) { rrset_free(rrset, 1); return (OK); } if (rrset->rr_list != NULL && verify_set(rrset) == DB_S_FAILED) { rrset_free(rrset, 1); return (OK); } } for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) { dp = dnp->dp; ret = db_update(rrset->rr_name, dp, dp, NULL, flags, (*htpp), from); if (ret != OK) { /* XXX Probably should do rollback. */ db_err(ret, rrset->rr_name, dp->d_type, dnp->file, dnp->line); if (ret != DATAEXISTS) { rrset_free_partial(rrset, 1, dnp); return (ret); } db_freedata(dp); } if (rrcount != NULL) (*rrcount)++; dnp->dp = NULL; } for (dnp = rrset->rr_sigs; dnp != NULL; dnp = dnp->dn_next) { dp = dnp->dp; if (dp == NULL) /* verifyset() can remove sigs */ continue; ret = db_update(rrset->rr_name, dp, dp, NULL, flags, (*htpp), from); if (ret != OK) { /* XXX Probably should do rollback. */ db_err(ret, rrset->rr_name, dp->d_type, dnp->file, dnp->line); if (ret != DATAEXISTS) { rrset_free_partial(rrset, 1, dnp); return (ret); } db_freedata(dp); } if (rrcount != NULL) (*rrcount)++; dnp->dp = NULL; } rrset_free(rrset, 0); return (OK); } static int rr_in_set(struct databuf *rr, struct dnode *set) { struct dnode *dnp; if (set == NULL) return (0); for(dnp = set; dnp != NULL; dnp = dnp->dn_next) { if (dnp->dp->d_size == rr->d_size && memcmp(dnp->dp->d_data, rr->d_data, dnp->dp->d_size) == 0) return (1); } return (0); } static int add_to_rrset_list(struct db_rrset **rrsets, char *name, struct databuf *dp, int line, const char *file) { struct db_rrset *rrset = *rrsets; struct dnode *dnp; while (rrset != NULL) { if (rrset->rr_type != ns_t_nxt || dp->d_type != ns_t_nxt) { if (dp->d_type == ns_t_sig) { if (SIG_COVERS(dp) == rrset->rr_type) break; } else { if (dp->d_type == rrset->rr_type) break; } } else if (nxt_match_rrset(dp, rrset)) break; rrset = rrset->rr_next; } if (rrset != NULL) { if ((dp->d_type == ns_t_sig && rr_in_set(dp, rrset->rr_sigs)) || (dp->d_type != ns_t_sig && rr_in_set(dp, rrset->rr_list))) { db_freedata(dp); return (DATAEXISTS); } } else { rrset = (struct db_rrset *) memget(sizeof(struct db_rrset)); if (rrset == NULL) ns_panic(ns_log_default, 1, "add_to_rrset_list: memget failed(%s)", name); memset(rrset, 0, sizeof(struct db_rrset)); rrset->rr_name = savestr(name, 1); rrset->rr_class = dp->d_class; if (dp->d_type == ns_t_sig) rrset->rr_type = SIG_COVERS(dp); else rrset->rr_type = dp->d_type; rrset->rr_next = *rrsets; *rrsets = rrset; } dnp = (struct dnode *) memget(sizeof(struct dnode)); if (dnp == NULL) ns_panic(ns_log_default, 1, "add_to_rrset_list: memget failed(%s)", name); memset(dnp, 0, sizeof(struct dnode)); dnp->dp = dp; if (dp->d_type == ns_t_sig) { if (rrset->rr_sigs != NULL) { struct dnode *fdnp; /* Preserve the order of the RRs */ /* Add this one to the end of the list */ for (fdnp = rrset->rr_sigs; fdnp->dn_next != NULL; fdnp = fdnp->dn_next) /* NULL */ ; fdnp->dn_next = dnp; } else rrset->rr_sigs = dnp; } else { if (rrset->rr_list != NULL) { struct dnode *fdnp; /* Preserve the order of the RRs */ /* Add this one to the end of the list */ for (fdnp = rrset->rr_list; fdnp->dn_next != NULL; fdnp = fdnp->dn_next) /* NULL */ ; fdnp->dn_next = dnp; } else rrset->rr_list = dnp; } dnp->file = (char *) file; dnp->line = line; return (0); } static int update_rrset_list(struct db_rrset **rrsets, int flags, struct hashbuf **htpp, struct sockaddr_in from, int *rrcount) { struct db_rrset *rrset = *rrsets, *next = NULL, *last = NULL; int result = 0, tresult, cnameandother = 0; while (rrset != NULL) { if (rrset->rr_type == ns_t_key) break; last = rrset; rrset = rrset->rr_next; } if (rrset != NULL && last != NULL) { last->rr_next = rrset->rr_next; rrset->rr_next = *rrsets; *rrsets = rrset; } rrset = *rrsets; while (rrset != NULL) { if (verify_set(rrset) > DB_S_FAILED) { ns_debug(ns_log_default, 10, "update_rrset_list(%s, %s): set verified", rrset->rr_name, p_type(rrset->rr_type)); tresult = rrset_db_update(rrset, flags, htpp, from, rrcount); if (tresult == CNAMEANDOTHER) cnameandother++; if (tresult != OK) result = tresult; } else { rrset_free(rrset, 1); result = DNSSECFAIL; } freestr(rrset->rr_name); next = rrset->rr_next; memput(rrset, sizeof(struct db_rrset)); rrset = next; } *rrsets = NULL; if (cnameandother != 0) return (CNAMEANDOTHER); return (result); } int db_set_update(char *name, struct databuf *dp, void **state, int flags, struct hashbuf **htpp, struct sockaddr_in from, int *rrcount, int line, const char *file) { struct db_rrset **rrsets; struct db_rrset *rrset; int result = 0; ns_debug(ns_log_default, 5, "db_set_update(%s)", (name == NULL) ? "" : (*name == 0) ? "." : name); if (state == NULL) ns_panic(ns_log_default, 1, "Called db_set_update with state == NULL"); rrsets = (struct db_rrset **) state; if (*rrsets != NULL) { rrset = *rrsets; if (rrset->rr_name != NULL && dp != NULL && name != NULL && ns_samename(name, rrset->rr_name) == 1 && dp->d_class == rrset->rr_class) return (add_to_rrset_list(rrsets, name, dp, line, file)); } if (*rrsets != NULL) result = update_rrset_list(rrsets, flags, htpp, from, rrcount); if (dp != NULL) { ns_debug(ns_log_default, 10, "db_set_update(%s), creating new list", name); (void) add_to_rrset_list(rrsets, name, dp, line, file); } return (result); } static int nxt_match_rrset(struct databuf *dp, struct db_rrset *rrset) { if (rrset->rr_list != NULL) return (nxtmatch(rrset->rr_name, dp, rrset->rr_list->dp)); else return (nxtmatch(rrset->rr_name, dp, rrset->rr_sigs->dp)); } Index: head/contrib/bind/bin/named/db_tsig.c =================================================================== --- head/contrib/bind/bin/named/db_tsig.c (revision 60940) +++ head/contrib/bind/bin/named/db_tsig.c (revision 60941) @@ -1,158 +1,158 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: db_tsig.c,v 8.5 1999/10/15 19:48:59 vixie Exp $"; +static const char rcsid[] = "$Id: db_tsig.c,v 8.6 2000/04/21 06:54:04 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" typedef struct { DST_KEY *key; void *ctx; } tsig_axfr_state; #define TSIG_ALG_MD5 "HMAC-MD5.SIG-ALG.REG.INT" #define TSIG_ALG_MD5_SHORT "hmac-md5" char * tsig_alg_name(int value) { if (value == KEY_HMAC_MD5) return(TSIG_ALG_MD5); else return(NULL); } int tsig_alg_value(char *name) { if (ns_samename(name, TSIG_ALG_MD5) == 1 || strcasecmp(name, TSIG_ALG_MD5_SHORT) == 0) return (KEY_HMAC_MD5); else return (-1); } DST_KEY * tsig_key_from_addr(struct in_addr addr) { server_info si = si = find_server(addr); if (si == NULL || si->key_list == NULL || si->key_list->first == NULL) return(NULL); return(si->key_list->first->key); } struct tsig_record * new_tsig(DST_KEY *key, u_char *sig, int siglen) { struct tsig_record *tsig; if (siglen > TSIG_SIG_SIZE) return(NULL); tsig = memget(sizeof(struct tsig_record)); if (tsig == NULL) return(NULL); tsig->key = key; tsig->siglen = siglen; memcpy(tsig->sig, sig, siglen); return(tsig); } void free_tsig(struct tsig_record *tsig) { if (tsig == NULL) return; memput(tsig, sizeof(struct tsig_record)); } Index: head/contrib/bind/bin/named/db_update.c =================================================================== --- head/contrib/bind/bin/named/db_update.c (revision 60940) +++ head/contrib/bind/bin/named/db_update.c (revision 60941) @@ -1,993 +1,989 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_update.c 4.28 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: db_update.c,v 8.39 1999/10/15 19:48:59 vixie Exp $"; +static const char rcsid[] = "$Id: db_update.c,v 8.42 2000/04/21 06:54:04 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* int * isRefByNS(name, htp) * recurse through all of `htp' looking for NS RR's that refer to `name'. * returns: * nonzero if at least one such NS RR exists * cautions: * this is very expensive; probably you only want to use on fcachetab. */ static int isRefByNS(const char *name, struct hashbuf *htp) { struct namebuf *np; struct databuf *dp; for (np = htp->h_tab[0]; np != NULL; np = np->n_next) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if ((dp->d_class == C_ANY || dp->d_class == C_IN || dp->d_class == C_HS) && dp->d_type == T_NS && !dp->d_rcode && ns_samename(name, (char *)dp->d_data) == 1) { return (1); } } if (np->n_hash && isRefByNS(name, np->n_hash)) return (1); } return (0); } /* int * findMyZone(struct namebuf *np, int class) * surf the zone cuts and find this zone the hard way * return value: * zone number or DB_Z_CACHE if it's outside a zone * interesting cases: * DEC.COM SOA (primary) * CRL.DEC.COM NS (in primary) * if you start at CRL.. here, you find the DEC.COM zone * if you start at NS.CRL.. here, you're in the cache * DEC.COM SOA (primary) * CRL.DEC.COM NS (in primary) * CRL.DEC.COM SOA (secondary) * CRL.DEC.COM NS (in secondary) * if you start at CRL.. here, you find the CRL.DEC.COM zone * if you start at NS.CRL.. here, you're in the CRL.. zone */ int findMyZone(struct namebuf *np, int class) { for ((void)NULL; np; np = np_parent(np)) { struct databuf *dp; /* if we encounter an SOA, we're in its zone (which can be * the cache or an authoritative zone, depending). */ for (dp = np->n_data; dp; dp = dp->d_next) if (match(dp, class, T_SOA) && dp->d_type == T_SOA) return (dp->d_zone); /* if we find an NS at some node without having seen an SOA * (above), then we're out in the cache somewhere. */ for (dp = np->n_data; dp; dp = dp->d_next) if (match(dp, class, T_NS) && dp->d_type == T_NS) return (DB_Z_CACHE); } /* The cache has not yet been primed. */ return (DB_Z_CACHE); } - -#define ISVALIDGLUE(xdp) ((xdp)->d_type == T_NS || (xdp)->d_type == T_A \ - || (xdp)->d_type == T_AAAA) - /* int * db_update(name, odp, newdp, savedpp, flags, htp, from) * update data base node at `name'. `flags' controls the action. * side effects: * inverse query tables modified, if we're using them. * return value: * OK - success * NONAME - name doesn't exist * AUTH - you can't do that * DATAEXISTS - there's something there and DB_NODATA was specified * NODATA - there's no data, and (DB_DELETE or DB_MEXIST) was spec'd * * Policy: How to add data if one more RR is -ve data * * NEND NOERROR_NODATA * NXD NXDOMAIN * * match * old * Data NEND NXD * Data Merge Data Data * new NEND NEND NEND NEND * NXD NXD NXD NXD * * no match * old * Data NEND NXD * Data Merge Merge Data * new NEND Merge Merge NEND * NXD NXD NXD NXD * */ /* XXX: this code calls nlookup, which can create namebuf's. if this code * has to exit with a fatal error, it should scan from the new np upward * and for each node which has no children and no data it should remove * the namebuf. design notes: (1) there's no harm in doing this even if * success occurred; (2) stopping on the first nonremovable np is optimal; * the code for removal should be taken out of clean_cache() and made * general enough for this use, and for clean_cache()'s continued use. * vix, 21jul94 */ int db_update(const char *name, struct databuf *odp, struct databuf *newdp, struct databuf **savedpp, int flags, struct hashbuf *htp, struct sockaddr_in from) { struct databuf *dp, *pdp; struct namebuf *np; int zn, isHintNS; int check_ttl = 0; int deleted_something = 0; const char *fname; #ifdef BIND_UPDATE int found_other_ns = 0; struct databuf *tmpdp; #endif ns_debug(ns_log_db, 3, "db_update(%s, %#x, %#x, %#x, 0%o, %#x)%s", name, odp, newdp, savedpp, flags, htp, (odp && (odp->d_flags&DB_F_HINT)) ? " hint" : ""); np = nlookup(name, &htp, &fname, newdp != NULL); if (np == NULL || fname != name) return (NONAME); if (newdp && zones[newdp->d_zone].z_type == Z_PRIMARY) check_ttl = 1; /* don't let nonauthoritative updates write in authority zones */ if (newdp && ((zn = findMyZone(np, newdp->d_class)) != DB_Z_CACHE) && #ifdef STUBS (zones[zn].z_type != Z_STUB) && #endif (flags & DB_NOTAUTH)) { int foundRR = 0; /* * Don't generate the warning if the update * would have been harmless (identical data). */ for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!db_cmp(dp, newdp)) { foundRR++; break; } } if (!foundRR) ns_debug(ns_log_db, 5, "[%s].%d update? to auth zone \"%s\" (%s)", inet_ntoa(from.sin_addr), ntohs(from.sin_port), zones[zn].z_origin, name); return (AUTH); } if (newdp && zn && !(flags & DB_NOTAUTH)) { if (nlabels(zones[zn].z_origin) > newdp->d_clev) { ns_debug(ns_log_db, 5, "attempted update child zone %s, %s", zones[zn].z_origin, name); return (AUTH); } } /* some special checks for root NS' A RR's */ isHintNS = isRefByNS(name, fcachetab); #ifdef DEPRECATED if (newdp && isHintNS && newdp->d_type == T_A) { /* upgrade credibility of additional data for rootsrv addrs */ if (newdp->d_cred == DB_C_ADDITIONAL) { ns_debug(ns_log_db, 3, "upgrading credibility for A RR (%s)", name); /* XXX: should copy NS RR's, but we really just want * to prevent deprecation later so this will do. */ newdp->d_cred = DB_C_ANSWER; newdp->d_clev = 0; } } #endif /* Reflect certain updates in hint cache also... */ /* Don't stick data we are authoritative for in hints. */ if (!(flags & DB_NOHINTS) && (flags & DB_PRIMING) && (odp != NULL) && (htp != fcachetab) && (DB_Z_SPECIAL(odp->d_zone)) && !(odp->d_flags & DB_F_HINT) && (!newdp || !newdp->d_rcode) && ((name[0] == '\0' && odp->d_type == T_NS) || (odp->d_type == T_A && isHintNS) ) ) { ns_debug(ns_log_db, 3, "db_update: hint '%s' %u", name, odp->d_ttl); dp = savedata(odp->d_class, odp->d_type, odp->d_ttl, odp->d_data, odp->d_size); dp->d_zone = DB_Z_CACHE; dp->d_flags = DB_F_HINT; dp->d_cred = DB_C_CACHE; dp->d_secure = odp->d_secure; /* BEW - this should be ok */ dp->d_clev = 0; if (db_update(name, dp, dp, NULL, (flags|DB_NOHINTS), fcachetab, from) != OK) { ns_debug(ns_log_db, 3, "db_update: hint %#x freed", dp); db_freedata(dp); } } if (odp != NULL) { int foundRR = 0; pdp = NULL; for (dp = np->n_data; dp != NULL; ) { if (!rrmatch(name, dp, odp)) { /* {class,type} doesn't match. these are * the aggregation cases. */ /* Check that CNAMEs are only accompanied by * Secure DNS RR's (KEY, SIG, and NXT). */ if (((dp->d_type == T_CNAME && odp->d_type != T_KEY && odp->d_type != T_SIG && odp->d_type != T_NXT) || (odp->d_type == T_CNAME && dp->d_type != T_KEY && dp->d_type != T_SIG && dp->d_type != T_NXT)) && odp->d_class == dp->d_class && /* XXXRTH d_mark removed in 4.9.5, but still here for dynamic update */ odp->d_mark == dp->d_mark && !dp->d_rcode && !odp->d_rcode && #ifdef BIND_UPDATE /* updating a CNAME with another CNAME is permitted */ (dp->d_type != T_CNAME || odp->d_type != T_CNAME) && #endif zones[odp->d_zone].z_type != Z_CACHE) { ns_info(ns_log_db, "%s has CNAME and other data (invalid)", name); if (zones[odp->d_zone].z_type == Z_PRIMARY) return (CNAMEANDOTHER); goto skip; } if (!newdp || newdp->d_class != dp->d_class) goto skip; /* if the new data is authorative * remove any data for this domain with * the same class that isn't as credable */ if (newdp->d_cred == DB_C_ZONE && newdp->d_cred > dp->d_cred) /* better credibility and the old datum * was not from a zone file. remove * the old datum. */ goto delete; #if 0 /* caught by findMyZone() now. */ /* if we have authoritative data for a * node, don't add in other data. */ if (dp->d_cred == DB_C_ZONE && newdp->d_cred < dp->d_cred) return (AUTH); #endif /* if the new data is authoritative * but isn't as credible, reject it. */ if (newdp->d_cred == DB_C_ZONE && dp->d_cred == DB_C_ZONE) { /* Both records are from a zone file. * If their credibility levels differ, * we're dealing with a zone cut. The * record with lower clev is from the * upper zone's file and is therefore * glue. */ /* BEW/OG: we see no reason to override * these rules with new security based * rules. */ if (newdp->d_clev < dp->d_clev) { if (!ISVALIDGLUE(newdp)) { ns_info(ns_log_db, "domain %s %s record in zone %s should be in zone %s, ignored", name, p_type(newdp->d_type), zones[newdp->d_zone].z_origin, zones[dp->d_zone].z_origin); } return (AUTH); } if (newdp->d_clev > dp->d_clev) { if (!ISVALIDGLUE(dp)) { ns_info(ns_log_db, "domain %s %s record in zone %s should be in zone %s, deleted", name, p_type(dp->d_type), zones[dp->d_zone].z_origin, zones[newdp->d_zone].z_origin); } goto delete; } } /* process NXDOMAIN */ /* policy */ if (newdp->d_rcode == NXDOMAIN) { if (dp->d_cred < DB_C_AUTH && newdp->d_secure >= dp->d_secure) goto delete; else return (DATAEXISTS); } if (dp->d_rcode == NXDOMAIN) goto delete; /* process NOERROR_NODATA */ /* NO PROCESSING REQUIRED */ goto skip; } /*if {class,type} did not match*/ /* * {type,class} did match. This is the replace case. */ ns_debug(ns_log_db, 5, "db_update: flags = %#x, sizes = %d, %d (cmp %d)", flags, odp->d_size, dp->d_size, db_cmp(dp, odp)); if (newdp) { ns_debug(ns_log_db, 4, "credibility for %s is %d(%d)(sec %d) from [%s].%d, is %d(%d)(sec %d) in cache", *name ? name : ".", newdp->d_cred, newdp->d_clev, newdp->d_secure, inet_ntoa(from.sin_addr), ntohs(from.sin_port), dp->d_cred, dp->d_secure, dp->d_clev); if ((newdp->d_secure > dp->d_secure) || (newdp->d_secure == dp->d_secure && (newdp->d_cred > dp->d_cred))) { /* better credibility / security. * remove the old datum. */ goto delete; } if ((newdp->d_secure < dp->d_secure) || (newdp->d_secure == dp->d_secure && (newdp->d_cred < dp->d_cred))) { /* credibility / security is worse. * ignore it. */ return (AUTH); } /* BEW/OG: from above, we know the security * levels are the same. */ if (newdp->d_cred == DB_C_ZONE && dp->d_cred == DB_C_ZONE ) { /* Both records are from a zone file. * If their credibility levels differ, * we're dealing with a zone cut. The * record with lower clev is from the * upper zone's file and is therefore * glue. */ /* XXX - Tricky situation here is you * have 2 zones a.b.c and sub.a.b.c * being served by the same server. * named will send NS records for * sub.a.b.c during zone transfer of * a.b.c zone. If we're secondary for * both zones, and we reload zone * a.b.c, we'll get the NS records * (and possibly A records to go with * them?) for sub.a.b.c as part of the * a.b.c zone transfer. But we've * already got a more credible record * from the sub.a.b.c zone. So we want * to ignore the new record, but we * shouldn't syslog because there's * nothing the user can do to prevent * the situation. Perhaps we should * only complain when we are primary? */ if (newdp->d_clev < dp->d_clev) { if (!ISVALIDGLUE(newdp)) { ns_info(ns_log_db, "domain %s %s record in zone %s should be in zone %s, ignored", name, p_type(newdp->d_type), zones[newdp->d_zone].z_origin, zones[dp->d_zone].z_origin); } return (AUTH); } if (newdp->d_clev > dp->d_clev) { if (!ISVALIDGLUE(dp)) { ns_info(ns_log_db, "domain %s %s record in zone %s should be in zone %s, deleted", name, p_type(dp->d_type), zones[dp->d_zone].z_origin, zones[newdp->d_zone].z_origin); } goto delete; } } /* credibility is the same. * let it aggregate in the normal way. */ /* * if the new or old RR is -ve, delete old. */ if (dp->d_rcode || newdp->d_rcode) { /* XXX: how can a zone rr be neg? */ if (dp->d_cred != DB_C_ZONE) goto delete; else return (DATAEXISTS); } /* * Some RR types should not be aggregated. */ if (dp->d_type == T_SOA) { #ifdef BIND_UPDATE u_int32_t dp_ser, ndp_ser; u_char *dp_cp, *ndp_cp; dp_cp = findsoaserial(dp->d_data); ndp_cp = findsoaserial(newdp->d_data); GETLONG(dp_ser, dp_cp); GETLONG(ndp_ser, ndp_cp); if (SEQ_GT(ndp_ser, dp_ser)) goto delete; else return (SERIAL); #else goto delete; #endif /*BIND_UPDATE*/ } if (dp->d_type == T_WKS && !memcmp(dp->d_data, newdp->d_data, INT32SZ + sizeof(u_char))) goto delete; if (dp->d_type == T_CNAME && !NS_OPTION_P(OPTION_MULTIPLE_CNAMES) && db_cmp(dp, odp) != 0) if ((flags & DB_REPLACE) == 0 && zones[dp->d_zone].z_type == Z_PRIMARY) { - ns_info(ns_log_db, + ns_warning(ns_log_db, "%s has multiple CNAMES", - name); + name); return (CNAMEANDOTHER); } else goto delete; #if 0 /* BEW - this _seriously_ breaks DNSSEC. Is it necessary for dynamic update? */ #ifdef BIND_UPDATE if (dp->d_type == T_SIG) /* * Type covered has already been * checked. */ goto delete; #endif #endif if (dp->d_type == T_NXT) { goto delete; } if (dp->d_type == T_SIG && SIG_COVERS(dp) == T_NXT) { struct sig_record *sr1, *sr2; sr1 = (struct sig_record *) dp->d_data; sr2 = (struct sig_record *) newdp->d_data; if (sr1->sig_alg_n == sr2->sig_alg_n) goto delete; } if (check_ttl) { if (newdp->d_ttl != dp->d_ttl) ns_warning(ns_log_db, "%s %s %s differing ttls: corrected", name[0]?name:".", p_class(dp->d_class), p_type(dp->d_type)); if (newdp->d_ttl > dp->d_ttl) { newdp->d_ttl = dp->d_ttl; } else { dp->d_ttl = newdp->d_ttl; } } } if ((flags & DB_NODATA) && !db_cmp(dp, odp)) { /* Refresh ttl if cache entry. */ if (dp->d_zone == DB_Z_CACHE) { if (odp->d_zone != DB_Z_CACHE) { /* Changing cache->auth. */ dp->d_zone = odp->d_zone; dp->d_ttl = odp->d_ttl; ns_debug(ns_log_db, 4, "db_update: cache entry now in auth zone"); return (DATAEXISTS); } fixttl(odp); if (odp->d_ttl > dp->d_ttl) dp->d_ttl = odp->d_ttl; ns_debug(ns_log_db, 3, "db_update: new ttl %u +%lu", dp->d_ttl, (u_long)(dp->d_ttl - tt.tv_sec) ); } return (DATAEXISTS); } /* * If the old databuf has some data, check that the * data matches that in the new databuf. */ if (odp->d_size > 0) if (db_cmp(dp, odp)) goto skip; if (odp->d_clev < dp->d_clev) goto skip; if ((odp->d_secure < dp->d_secure) || ((odp->d_secure == dp->d_secure) && (odp->d_cred < dp->d_cred))) goto skip; #ifdef BIND_UPDATE if (ns_samename(name, zones[dp->d_zone].z_origin) == 1 && newdp == NULL) { /* do not delete SOA or NS records as a set */ /* XXXRTH isn't testing d_size unnecessary? */ if ((odp->d_size == 0) && (odp->d_class == C_ANY) && (odp->d_type == T_ANY || odp->d_type == T_SOA || odp->d_type == T_NS) && (dp->d_type == T_SOA || dp->d_type == T_NS)) goto skip; /* XXXRTH I added this to prevent SOA deletion I'm using the same style of comparison as the other code in this section. Do we really need to look at dp->d_type here? We're in the "match" section... */ if ((odp->d_type == T_SOA) && (dp->d_type == T_SOA)) goto skip; /* do not delete the last NS record for the zone */ if ((odp->d_type == T_NS) && (dp->d_type == T_NS)) { found_other_ns = 0; for (tmpdp = np->n_data; tmpdp && !found_other_ns; tmpdp = tmpdp->d_next) if ((tmpdp->d_type == T_NS) && (tmpdp != dp)) found_other_ns = 1; if (!found_other_ns) { ns_debug(ns_log_db, 3, "cannot delete last remaining NS record for zone %s", name); goto skip; } } } #endif foundRR = 1; if (flags & DB_DELETE) { delete: #ifdef BIND_UPDATE /* * XXX assume here that savedpp!=NULL iff. db_update * has been called by the dyanmic update code. * Maybe a new flag is more appropriate? */ if (savedpp != NULL) foundRR = 1; #endif deleted_something = 1; dp = rm_datum(dp, np, pdp, savedpp); } else { skip: pdp = dp; dp = dp->d_next; } } if (!foundRR) { if (flags & DB_DELETE) return (NODATA); if (flags & DB_MEXIST) return (NODATA); } } if (newdp == NULL) { if (deleted_something) { while (np->n_data == NULL && np->n_hash == NULL) { np = purge_node(htp, np); if (np == NULL) break; } } return (OK); } /* XXX: empty nodes bypass credibility checks above; should check * response source address here if flags&NOTAUTH. */ fixttl(newdp); ns_debug(ns_log_db, 3, "db_update: adding%s %#x", (newdp->d_flags&DB_F_HINT) ? " hint":"", newdp); if (NS_OPTION_P(OPTION_HOSTSTATS) && newdp->d_zone == DB_Z_CACHE && (newdp->d_flags & DB_F_HINT) == 0) newdp->d_ns = nameserFind(from.sin_addr, NS_F_INSERT); /* Add to end of list, generally preserving order */ newdp->d_next = NULL; if ((dp = np->n_data) == NULL) { DRCNTINC(newdp); if (newdp->d_flags & DB_F_ACTIVE) panic("db_update: DB_F_ACTIVE set", NULL); newdp->d_flags |= DB_F_ACTIVE; np->n_data = newdp; return (OK); } while (dp->d_next != NULL) { if ((flags & DB_NODATA) && !db_cmp(dp, newdp)) return (DATAEXISTS); dp = dp->d_next; } if ((flags & DB_NODATA) && !db_cmp(dp, newdp)) return (DATAEXISTS); DRCNTINC(newdp); if (newdp->d_flags & DB_F_ACTIVE) panic("db_update: DB_F_ACTIVE set", NULL); newdp->d_flags |= DB_F_ACTIVE; dp->d_next = newdp; return (OK); } void fixttl(struct databuf *dp) { if (dp->d_zone == DB_Z_CACHE && (dp->d_flags & DB_F_HINT) == 0) { if (dp->d_ttl <= (u_int32_t)tt.tv_sec) return; else if (dp->d_ttl < (u_int32_t)tt.tv_sec+min_cache_ttl) dp->d_ttl = (u_int32_t)tt.tv_sec+min_cache_ttl; else if (dp->d_ttl > (u_int32_t)tt.tv_sec+max_cache_ttl) dp->d_ttl = (u_int32_t)tt.tv_sec+max_cache_ttl; } } /* * Compare type, class and data from databufs for equivalence. * All domain names in RR's must be compared case-insensitively. * Return 0 if equivalent, nonzero otherwise. */ int db_cmp(const struct databuf *dp1, const struct databuf *dp2) { const u_char *cp1, *cp2; int len, len2; /* XXXDYNUP- should be changed to if (!match(dp1, dp2->d_type, dp2->d_class) */ if (dp1->d_type != dp2->d_type || dp1->d_class != dp2->d_class) return (1); /* XXXDYNUP - should be changed to (dp1->d_size != dp2->d_size && dp1->d_size != 0 && dp2->d_size != 0) */ if (dp1->d_size != dp2->d_size) return (1); /* d_mark is only used for dynamic updates currently */ #ifndef BIND_UPDATE if (dp1->d_mark != dp2->d_mark) return (1); /* old and new RR's are distinct */ #endif if (dp1->d_rcode && dp2->d_rcode) return ((dp1->d_rcode == dp1->d_rcode)?0:1); if (dp1->d_rcode || dp2->d_rcode) return (1); switch (dp1->d_type) { case T_A: case T_WKS: case T_NULL: case T_NSAP: case T_AAAA: case T_LOC: case T_KEY: /* Only binary data */ return (memcmp(dp1->d_data, dp2->d_data, dp1->d_size)); case T_NS: case T_CNAME: case T_PTR: case T_MB: case T_MG: case T_MR: /* Only a domain name */ if (ns_samename((char *)dp1->d_data, (char *)dp2->d_data) == 1) return (0); else return (1); case T_SIG: /* Binary data, a domain name, more binary data */ if (dp1->d_size < NS_SIG_SIGNER) return (1); if (memcmp(dp1->d_data, dp2->d_data, NS_SIG_SIGNER)) return (1); len = NS_SIG_SIGNER + strlen((char *)dp1->d_data + NS_SIG_SIGNER); if (ns_samename((char *)dp1->d_data + NS_SIG_SIGNER, (char *)dp2->d_data + NS_SIG_SIGNER) != 1) return (1); return (memcmp(dp1->d_data + len, dp2->d_data + len, dp1->d_size - len)); case T_NXT: /* First a domain name, then binary data */ if (ns_samename((char *)dp1->d_data, (char *)dp2->d_data) != 1) return (1); len = strlen((char *)dp1->d_data)+1; return (memcmp(dp1->d_data + len, dp2->d_data + len, dp1->d_size - len)); case T_HINFO: case T_ISDN: cp1 = dp1->d_data; cp2 = dp2->d_data; len = *cp1; len2 = *cp2; if (len != len2) return (1); if (strncasecmp((char *)++cp1, (char *)++cp2, len)) return (1); cp1 += len; cp2 += len; len = *cp1; len2 = *cp2; if (len != len2) return (1); return (strncasecmp((char *)++cp1, (char *)++cp2, len)); case T_SOA: case T_MINFO: case T_RP: if (ns_samename((char *)dp1->d_data, (char *)dp2->d_data) != 1) return (1); cp1 = dp1->d_data + strlen((char *)dp1->d_data) + 1; cp2 = dp2->d_data + strlen((char *)dp2->d_data) + 1; if (ns_samename((char *)cp1, (char *)cp2) != 1) return (1); if (dp1->d_type != T_SOA) return (0); cp1 += strlen((char *)cp1) + 1; cp2 += strlen((char *)cp2) + 1; return (memcmp(cp1, cp2, INT32SZ * 5)); case T_NAPTR: { int t1,t2; if (dp1->d_size != dp2->d_size) return (1); cp1 = dp1->d_data; cp2 = dp2->d_data; /* Order */ if (*cp1++ != *cp2++ || *cp1++ != *cp2++) return (1); /* Preference */ if (*cp1++ != *cp2++ || *cp1++ != *cp2++) return (1); /* Flags */ t1 = *cp1++; t2 = *cp2++; if (t1 != t2 || memcmp(cp1, cp2, t1)) return (1); cp1 += t1; cp2 += t2; /* Services */ t1 = *cp1++; t2 = *cp2++; if (t1 != t2 || memcmp(cp1, cp2, t1)) return (1); cp1 += t1; cp2 += t2; /* Regexp */ t1 = *cp1++; t2 = *cp2++; if (t1 != t2 || memcmp(cp1, cp2, t1)) return (1); cp1 += t1; cp2 += t2; /* Replacement */ t1 = strlen((char *)cp1); t2 = strlen((char *)cp2); if (t1 != t2 || memcmp(cp1, cp2, t1)) return (1); cp1 += t1 + 1; cp2 += t2 + 1; /* they all checked out! */ return (0); } case T_MX: case T_AFSDB: case T_RT: case T_SRV: cp1 = dp1->d_data; cp2 = dp2->d_data; if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* cmp prio */ return (1); if (dp1->d_type == T_SRV) { if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* weight */ return (1); if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* port */ return (1); } if (ns_samename((char *)cp1, (char *)cp2) != 1) return (1); return (0); case T_PX: cp1 = dp1->d_data; cp2 = dp2->d_data; if (*cp1++ != *cp2++ || *cp1++ != *cp2++) /* cmp prio */ return (1); if (ns_samename((char *)cp1, (char *)cp2) != 1) return (1); cp1 += strlen((char *)cp1) + 1; cp2 += strlen((char *)cp2) + 1; if (ns_samename((char *)cp1, (char *)cp2) != 1) return (1); return (0); case T_TXT: case T_X25: if (dp1->d_size != dp2->d_size) return (1); return (memcmp(dp1->d_data, dp2->d_data, dp1->d_size)); default: return (1); } } Index: head/contrib/bind/bin/named/named.h =================================================================== --- head/contrib/bind/bin/named/named.h (revision 60940) +++ head/contrib/bind/bin/named/named.h (revision 60941) @@ -1,64 +1,63 @@ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * $Id: named.h,v 8.25 1999/10/13 18:00:19 vixie Exp $ + * $Id: named.h,v 8.27 2000/04/21 06:54:04 vixie Exp $ */ /* Options. Change them at your peril. */ #define DEBUG #define ADDAUTH #define STUBS #define RETURNSOA #define BOGUSNS #define TRACEROOT #define XFRNETS #define QRYLOG #define YPKLUDGE #define RENICE -#define SLAVE_FORWARD #define BIND_IXFR #define BIND_NOTIFY #define BIND_UPDATE #define WANT_PIDFILE #define FWD_LOOP #define DOTTED_SERIAL #define SENSIBLE_DOTS #define ROUND_ROBIN #define DNS_SECURITY #undef RSAREF #undef BSAFE #define ALLOW_LONG_TXT_RDATA #define STRICT_RFC2308 #undef BIND_ZXFR #include #include #include #include #include "pathnames.h" #include "ns_defs.h" #include "db_defs.h" #include "ns_glob.h" #include "db_glob.h" #include "ns_func.h" #include "db_func.h" Index: head/contrib/bind/bin/named/ns_config.c =================================================================== --- head/contrib/bind/bin/named/ns_config.c (revision 60940) +++ head/contrib/bind/bin/named/ns_config.c (revision 60941) @@ -1,3060 +1,3126 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_config.c,v 8.104 1999/11/08 23:09:42 vixie Exp $"; +static const char rcsid[] = "$Id: ns_config.c,v 8.114 2000/04/23 02:18:58 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef HAVE_GETRUSAGE /* XXX */ #include #endif #include "named.h" #include "ns_parseutil.h" /* Private. */ static int tmpnum = 0; static int config_initialized = 0; static int need_logging_free = 0; static int default_logging_installed; static int options_installed = 0; static int logging_installed = 0; static int default_options_installed; static int initial_configuration = 1; static char **logging_categories; static char *current_pid_filename = NULL; #define ZONE_SYM_TABLE_SIZE 4973 static symbol_table zone_symbol_table; /* Zones */ void free_zone_timerinfo(struct zoneinfo *zp) { if (zp->z_timerinfo != NULL) { freestr(zp->z_timerinfo->name); memput(zp->z_timerinfo, sizeof *zp->z_timerinfo); zp->z_timerinfo = NULL; } else ns_error(ns_log_config, "timer for zone '%s' had no timerinfo", zp->z_origin); } void free_zone_contents(struct zoneinfo *zp, int undefine_sym) { INSIST(zp != NULL); if (undefine_sym) undefine_symbol(zone_symbol_table, zp->z_origin, zp->z_class); if (zp->z_flags & Z_TIMER_SET) { free_zone_timerinfo(zp); if (evClearTimer(ev, zp->z_timer) < 0) ns_error(ns_log_config, "evClearTimer for zone '%s' failed in ns_init: %s", zp->z_origin, strerror(errno)); } if (zp->z_origin != NULL) freestr(zp->z_origin); zp->z_origin = NULL; if (zp->z_source != NULL) freestr(zp->z_source); zp->z_source = NULL; if (zp->z_ixfr_base != NULL) freestr(zp->z_ixfr_base); zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = NULL; if (zp->z_update_acl != NULL) free_ip_match_list(zp->z_update_acl); zp->z_update_acl = NULL; if (zp->z_query_acl != NULL) free_ip_match_list(zp->z_query_acl); zp->z_query_acl = NULL; if (zp->z_transfer_acl != NULL) free_ip_match_list(zp->z_transfer_acl); zp->z_transfer_acl = NULL; #ifdef BIND_UPDATE if (zp->z_updatelog != NULL) freestr(zp->z_updatelog); zp->z_updatelog = NULL; #endif /* BIND_UPDATE */ #ifdef BIND_NOTIFY if (zp->z_also_notify != NULL) memput(zp->z_also_notify, zp->z_notify_count * sizeof *zp->z_also_notify); zp->z_also_notify = NULL; #endif block_signals(); if (LINKED(zp, z_reloadlink)) UNLINK(reloadingzones, zp, z_reloadlink); unblock_signals(); } static void release_zone(struct zoneinfo *zp) { INSIST(zp != NULL); free_zone_contents(zp, 0); memput(zp, sizeof *zp); } struct zoneinfo * find_zone(const char *name, int class) { struct zoneinfo *zp; symbol_value value; ns_debug(ns_log_config, 3, "find_zone(%s, %d)", *name ? name : ".", class); if (lookup_symbol(zone_symbol_table, name, class, &value)) { INSIST(value.integer >= 0 && value.integer < nzones); ns_debug(ns_log_config, 3, "find_zone: existing zone %d", value.integer); zp = &zones[value.integer]; return (zp); } ns_debug(ns_log_config, 3, "find_zone: unknown zone"); return (NULL); } static struct zoneinfo * new_zone(int class, int type) { struct zoneinfo *zp; if (EMPTY(freezones)) make_new_zones(); zp = HEAD(freezones); UNLINK(freezones, zp, z_freelink); return (zp); } /* * Check out a zoneinfo structure and return non-zero if it's OK. */ static int validate_zone(struct zoneinfo *zp) { char filename[MAXPATHLEN+1]; /* Check name */ if (!res_dnok(zp->z_origin)) { ns_error(ns_log_config, "invalid zone name '%s'", zp->z_origin); return (0); } /* Check class */ if (zp->z_class == C_ANY || zp->z_class == C_NONE) { ns_error(ns_log_config, "invalid class %d for zone '%s'", zp->z_class, zp->z_origin); return (0); } /* Check type. */ if (zp->z_type == 0) { ns_error(ns_log_config, "no type specified for zone '%s'", zp->z_origin); return (0); } if (zp->z_type == z_cache && ns_samename(zp->z_origin, "") != 1) { ns_error(ns_log_config, "only the root zone may be a cache zone (zone '%s')", zp->z_origin); return (0); } if (zp->z_type == z_hint && ns_samename(zp->z_origin, "") != 1) { ns_error(ns_log_config, "only the root zone may be a hint zone (zone '%s')", zp->z_origin); return (0); } /* Check filename. */ if (zp->z_type == z_master && zp->z_source == NULL) { ns_error(ns_log_config, "'file' statement missing for master zone %s", zp->z_origin); return (0); } /* * XXX We should run filename through an OS-specific * validator here. */ if (zp->z_source != NULL && strlen(zp->z_source) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for zone '%s'", zp->z_origin); return (0); } if (zp->z_ixfr_base != NULL && strlen(zp->z_ixfr_base) > MAXPATHLEN) { ns_error(ns_log_config, "ixfr filename too long for zone '%s'", zp->z_origin); return (0); } if (zp->z_ixfr_tmp != NULL && strlen(zp->z_ixfr_tmp) > MAXPATHLEN) { ns_error(ns_log_config, "tmp ixfr filename too long for zone '%s'", zp->z_origin); return (0); } /* Check masters */ if (zp->z_addrcnt != 0) { if (zp->z_type == z_master || zp->z_type == z_hint || zp->z_type == z_cache) { ns_error(ns_log_config, "'masters' statement present for %s zone '%s'", (zp->z_type == z_master) ? "master" : (zp->z_type == z_hint) ? "hint" : "cache", zp->z_origin); return (0); } } else { if (zp->z_type == z_slave || zp->z_type == z_stub) { ns_error(ns_log_config, "no 'masters' statement for non-master zone '%s'", zp->z_origin); return (0); } } /* Check allow-update and allow-transfer. */ if (zp->z_update_acl || zp->z_transfer_acl) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'allow-{update,transfer}' option for non-{master,slave} zone '%s'", zp->z_origin); return (0); } } /* Check allow-query. */ if (zp->z_query_acl) { if (zp->z_type != z_master && zp->z_type != z_slave && zp->z_type != z_stub) { ns_error(ns_log_config, "'allow-query' option for non-{master,slave,stub} zone '%s'", zp->z_origin); return (0); } } #ifdef BIND_NOTIFY /* Check notify */ if (zp->z_notify != znotify_use_default) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'notify' given for non-master, non-slave zone '%s'", zp->z_origin); return (0); } } /* Check also-notify */ if (zp->z_notify_count != 0) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'also-notify' given for non-master, non-slave zone '%s'", zp->z_origin); return (0); } } #endif #ifdef BIND_UPDATE /* XXX need more checking here */ if (!zp->z_updatelog && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".log" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.log", zp->z_source); zp->z_updatelog = savestr(filename, 1); } /* Check forward */ if (zp->z_optset & OPTION_FORWARD_ONLY) { if (zp->z_type == z_hint) { ns_error(ns_log_config, "'forward' given for hint zone '%s'", zp->z_origin); return (0); } } /* Check forwarders */ if (zp->z_fwdtab) { if (zp->z_type == z_hint) { ns_error(ns_log_config, "'forwarders' given for hint zone '%s'", zp->z_origin); return (0); } } if (zp->z_type == z_master) { if (!zp->z_soaincrintvl) zp->z_soaincrintvl = SOAINCRINTVL; if (!zp->z_dumpintvl) zp->z_dumpintvl = DUMPINTVL; if (!zp->z_deferupdcnt) zp->z_deferupdcnt = DEFERUPDCNT; } #endif /* BIND_UPDATE */ if (!zp->z_ixfr_base && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".ixfr" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.ixfr", zp->z_source); zp->z_ixfr_base = savestr(filename, 1); } if (!zp->z_ixfr_tmp && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".ixfr.tmp" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.ixfr.tmp", zp->z_source); zp->z_ixfr_tmp = savestr(filename, 1); } return (1); } /* * Start building a new zoneinfo structure. Returns an opaque * zone_config suitable for use by the parser. */ zone_config begin_zone(char *name, int class) { zone_config zh; struct zoneinfo *zp; /* * require: name is canonical, class is a valid class */ ns_debug(ns_log_config, 3, "begin_zone('%s', %d)", (*name == '\0') ? "." : name, class); zp = (struct zoneinfo *)memget(sizeof (struct zoneinfo)); if (zp == NULL) panic("memget failed in begin_zone", NULL); memset(zp, 0, sizeof (struct zoneinfo)); zp->z_origin = name; zp->z_class = class; zp->z_checknames = not_set; - zp->z_log_size_ixfr = 0; if (server_options->flags & OPTION_MAINTAIN_IXFR_BASE) zp->z_maintain_ixfr_base = 1; else zp->z_maintain_ixfr_base = 0; zp->z_max_log_size_ixfr = server_options->max_log_size_ixfr; zh.opaque = zp; return (zh); } /* * Merge new configuration information into an existing zone. The * new zoneinfo must be valid. */ static void update_zone_info(struct zoneinfo *zp, struct zoneinfo *new_zp) { char buf[MAXPATHLEN+1]; int i; INSIST(zp != NULL); INSIST(new_zp != NULL); ns_debug(ns_log_config, 1, "update_zone_info('%s', %d)", (*new_zp->z_origin == '\0') ? "." : new_zp->z_origin, new_zp->z_type); #ifdef BIND_UPDATE /* * A dynamic master zone that's becoming non-dynamic may need to be * dumped before we start the update. */ if ((zp->z_flags & Z_DYNAMIC) && !(new_zp->z_flags & Z_DYNAMIC) && ((zp->z_flags & Z_NEED_SOAUPDATE) || (zp->z_flags & Z_NEED_DUMP))) (void) zonedump(zp, ISNOTIXFR); #endif /* * First do the simple stuff, making sure to free * any data that was dynamically allocated. */ if (zp->z_origin != NULL) freestr(zp->z_origin); zp->z_origin = new_zp->z_origin; new_zp->z_origin = NULL; zp->z_maintain_ixfr_base = new_zp->z_maintain_ixfr_base; zp->z_max_log_size_ixfr = new_zp->z_max_log_size_ixfr; - zp->z_log_size_ixfr = new_zp->z_log_size_ixfr; zp->z_class = new_zp->z_class; zp->z_type = new_zp->z_type; zp->z_checknames = new_zp->z_checknames; for (i = 0; i < new_zp->z_addrcnt; i++) zp->z_addr[i] = new_zp->z_addr[i]; zp->z_addrcnt = new_zp->z_addrcnt; if (zp->z_update_acl) free_ip_match_list(zp->z_update_acl); zp->z_update_acl = new_zp->z_update_acl; new_zp->z_update_acl = NULL; if (zp->z_query_acl) free_ip_match_list(zp->z_query_acl); zp->z_query_acl = new_zp->z_query_acl; new_zp->z_query_acl = NULL; zp->z_axfr_src = new_zp->z_axfr_src; if (zp->z_transfer_acl) free_ip_match_list(zp->z_transfer_acl); zp->z_transfer_acl = new_zp->z_transfer_acl; new_zp->z_transfer_acl = NULL; zp->z_max_transfer_time_in = new_zp->z_max_transfer_time_in; #ifdef BIND_NOTIFY zp->z_notify = new_zp->z_notify; if (zp->z_also_notify) memput(zp->z_also_notify, zp->z_notify_count * sizeof *zp->z_also_notify); zp->z_also_notify = new_zp->z_also_notify; zp->z_notify_count = new_zp->z_notify_count; new_zp->z_also_notify = NULL; new_zp->z_notify_count = 0; #endif if ((new_zp->z_flags & Z_FORWARD_SET) != 0) zp->z_flags |= Z_FORWARD_SET; else zp->z_flags &= ~Z_FORWARD_SET; if (zp->z_fwdtab != NULL) free_forwarders(zp->z_fwdtab); zp->z_fwdtab = new_zp->z_fwdtab; new_zp->z_fwdtab = NULL; zp->z_dialup = new_zp->z_dialup; zp->z_options = new_zp->z_options; zp->z_optset = new_zp->z_optset; #ifdef BIND_UPDATE if (new_zp->z_flags & Z_DYNAMIC) zp->z_flags |= Z_DYNAMIC; else zp->z_flags &= ~Z_DYNAMIC; zp->z_soaincrintvl = new_zp->z_soaincrintvl; zp->z_dumpintvl = new_zp->z_dumpintvl; zp->z_deferupdcnt = new_zp->z_deferupdcnt; if (zp->z_updatelog) freestr(zp->z_updatelog); zp->z_updatelog = new_zp->z_updatelog; new_zp->z_updatelog = NULL; #endif /* BIND_UPDATE */ zp->z_port = new_zp->z_port; /* * Now deal with files. */ switch (zp->z_type) { case z_cache: ns_panic(ns_log_config, 1, "impossible condition"); break; case z_hint: ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); zp->z_refresh = 0; /* No dumping. */ if (zp->z_source != NULL && strcmp(new_zp->z_source, zp->z_source) == 0 && (reconfiging || !zonefile_changed_p(zp))) { ns_debug(ns_log_config, 1, "cache is up to date"); break; } /* File has changed, or hasn't been loaded yet. */ if (zp->z_source) { freestr(zp->z_source); + ns_stopxfrs(zp); purge_zone(zp->z_origin, fcachetab, zp->z_class); } zp->z_source = new_zp->z_source; new_zp->z_source = NULL; if (zp->z_ixfr_base) freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp) freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; ns_debug(ns_log_config, 1, "reloading hint zone"); (void) db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR); break; case z_master: ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); /* * If we've loaded this file, and the file hasn't changed * then there's no need to reload. */ if (zp->z_source != NULL && strcmp(new_zp->z_source, zp->z_source) == 0 && (reconfiging || !zonefile_changed_p(zp))) { ns_debug(ns_log_config, 1, "zone is up to date"); break; } #ifdef BIND_UPDATE if (zp->z_source && (zp->z_flags & Z_DYNAMIC)) ns_warning(ns_log_config, "source file of dynamic zone '%s' has changed", zp->z_origin); primary_reload: #endif /* BIND_UPDATE */ if (zp->z_source != NULL) freestr(zp->z_source); zp->z_source = new_zp->z_source; new_zp->z_source = NULL; if (zp->z_ixfr_base != NULL) freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; if (reload_master(zp) == 1) { /* * Note that going to primary_reload * unconditionally reloads the zone. */ new_zp->z_source = savestr(zp->z_source, 1); new_zp->z_ixfr_base = savestr(zp->z_ixfr_base, 1); new_zp->z_ixfr_tmp = savestr(zp->z_ixfr_tmp, 1); goto primary_reload; } break; case z_slave: #ifdef STUBS case z_stub: #endif ns_debug(ns_log_config, 1, "addrcnt = %d", zp->z_addrcnt); if (!new_zp->z_source) { /* * We will always transfer this zone again * after a reload. */ sprintf(buf, "NsTmp%ld.%d", (long)getpid(), tmpnum++); new_zp->z_source = savestr(buf, 1); zp->z_flags |= Z_TMP_FILE; } else zp->z_flags &= ~Z_TMP_FILE; /* * If we had a backup file name, and it was changed, * free old zone and start over. If we don't have * current zone contents, try again now in case * we have a new server on the list. */ if (zp->z_source != NULL && (strcmp(new_zp->z_source, zp->z_source) != 0 || ((!reconfiging) && zonefile_changed_p(zp)))) { ns_debug(ns_log_config, 1, "backup file changed or missing"); freestr(zp->z_source); zp->z_source = NULL; zp->z_serial = 0; /* force xfer */ ns_stopxfrs(zp); /* * We only need to reload if we have ever * successfully transferred the zone. */ if ((zp->z_flags & Z_AUTH) != 0) { zp->z_flags &= ~Z_AUTH; /* * Purge old data and mark the parent for * reloading so that NS records are present * during the zone transfer. */ do_reload(zp->z_origin, zp->z_type, zp->z_class, 1); } } if (zp->z_source == NULL) { zp->z_source = new_zp->z_source; new_zp->z_source = NULL; } if (zp->z_ixfr_base != NULL) freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; - if ((zp->z_flags & Z_AUTH) == 0) + if ((!noexpired || ((zp->z_flags & Z_EXPIRED) == 0)) && + ((zp->z_flags & Z_AUTH) == 0)) zoneinit(zp); else { /* ** Force secondary to try transfer soon ** after SIGHUP. */ if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0 && reloading && !reconfiging) { qserial_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); } } break; case z_forward: /* * We don't know if the forwarder's list has changed * so just purge the cache. In the future we may want * see if the forwarders list has changed and only * do this then. */ clean_cache_from(zp->z_origin, hashtab); break; } if ((zp->z_flags & Z_FOUND) != 0 && /* already found? */ (zp - zones) != DB_Z_CACHE) /* cache never sets Z_FOUND */ ns_error(ns_log_config, "Zone \"%s\" declared more than once", zp->z_origin); zp->z_flags |= Z_FOUND; ns_debug(ns_log_config, 1, "zone[%d] type %d: '%s' z_time %lu, z_refresh %u", zp-zones, zp->z_type, *(zp->z_origin) == '\0' ? "." : zp->z_origin, (u_long)zp->z_time, zp->z_refresh); } /* * Finish constructing a new zone. If valid, the constructed zone is * merged into the zone database. The zone_config used is invalid after * end_zone() completes. */ void end_zone(zone_config zh, int should_install) { struct zoneinfo *zp, *new_zp; char *zname; symbol_value value; new_zp = zh.opaque; INSIST(new_zp != NULL); zname = (new_zp->z_origin[0] == '\0') ? "." : new_zp->z_origin; ns_debug(ns_log_config, 3, "end_zone('%s', %d)", zname, should_install); if (!should_install) { release_zone(new_zp); return; } if (!validate_zone(new_zp)) { ns_error(ns_log_config, "zone '%s' did not validate, skipping", zname); release_zone(new_zp); return; } zp = find_zone(new_zp->z_origin, new_zp->z_class); if (zp != NULL && zp->z_type != new_zp->z_type) { remove_zone(zp, "redefined"); zp = NULL; } if (zp == NULL) { zp = new_zone(new_zp->z_class, new_zp->z_type); INSIST(zp != NULL); value.integer = (zp - zones); define_symbol(zone_symbol_table, savestr(new_zp->z_origin, 1), new_zp->z_class, value, SYMBOL_FREE_KEY); } ns_debug(ns_log_config, 5, "zone '%s', type = %d, class = %d", zname, new_zp->z_type, new_zp->z_class); if (new_zp->z_source != NULL) ns_debug(ns_log_config, 5, " file = %s", new_zp->z_source); ns_debug(ns_log_config, 5, " checknames = %d", new_zp->z_checknames); if (new_zp->z_addrcnt != 0) { int i; ns_debug(ns_log_config, 5, " masters:"); for (i = 0; i < new_zp->z_addrcnt; i++) ns_debug(ns_log_config, 5, " %s", inet_ntoa(new_zp->z_addr[i])); } update_zone_info(zp, new_zp); release_zone(new_zp); zh.opaque = NULL; } int set_zone_type(zone_config zh, int type) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if type already set for this zone */ if (zp->z_type != 0) return (0); zp->z_type = type; return (1); } int set_zone_filename(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_source != NULL) return (0); zp->z_source = filename; return (1); } int set_zone_checknames(zone_config zh, enum severity s) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if checknames already set for this zone */ if (zp->z_checknames != not_set) return (0); zp->z_checknames = s; return (1); } int set_zone_ixfr_file(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_ixfr_base != NULL) return (0); zp->z_ixfr_base = filename; if (zp->z_ixfr_tmp == NULL) { - int len = strlen(zp->z_ixfr_base) + (sizeof ".tmp" - 1); + int len = strlen(zp->z_ixfr_base) + (sizeof ".tmp"); char *str = (char *) memget(len); sprintf(str, "%s.tmp", zp->z_ixfr_base); zp->z_ixfr_tmp = savestr(str, 1); memput(str, len); } return (1); } int set_zone_ixfr_tmp(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_ixfr_tmp != NULL) return (0); zp->z_ixfr_tmp = filename; return (1); } int set_zone_dialup(zone_config zh, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); if (value) { zp->z_dialup = zdialup_yes; #ifdef BIND_NOTIFY zp->z_notify = znotify_yes; #endif } else zp->z_dialup = zdialup_no; return (1); } int set_zone_notify(zone_config zh, int value) { #ifdef BIND_NOTIFY struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); if (value) zp->z_notify = znotify_yes; else zp->z_notify = znotify_no; #endif return (1); } int set_zone_maintain_ixfr_base(zone_config zh, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_maintain_ixfr_base = value; return (1); } int set_zone_update_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if update_acl already set for this zone */ if (zp->z_update_acl != NULL) return (0); zp->z_update_acl = iml; #ifdef BIND_UPDATE if (!ip_match_is_none(iml)) zp->z_flags |= Z_DYNAMIC; else ns_debug(ns_log_config, 3, "update acl is none for '%s'", zp->z_origin); #endif return (1); } int set_zone_query_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if checknames already set for this zone */ if (zp->z_query_acl != NULL) return (0); zp->z_query_acl = iml; return (1); } int set_zone_master_port(zone_config zh, u_short port) { struct zoneinfo *zp = zh.opaque; zp->z_port = port; return (1); } int set_zone_transfer_source(zone_config zh, struct in_addr ina) { struct zoneinfo *zp = zh.opaque; zp->z_axfr_src = ina; return (1); } int set_zone_transfer_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if checknames already set for this zone */ if (zp->z_transfer_acl != NULL) return (0); zp->z_transfer_acl = iml; return (1); } int set_zone_transfer_time_in(zone_config zh, long max_time) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if checknames already set for this zone */ if (zp->z_max_transfer_time_in) return (0); zp->z_max_transfer_time_in = max_time; return (1); } int set_zone_max_log_size_ixfr(zone_config zh, int size) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_max_log_size_ixfr = size; return (0); } int set_zone_pubkey(zone_config zh, const int flags, const int proto, const int alg, const char *str) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); INSIST(zp != NULL && zp->z_origin != NULL); return (add_trusted_key(zp->z_origin, flags, proto, alg, str)); } int set_trusted_key(const char *name, const int flags, const int proto, const int alg, const char *str) { INSIST(name != NULL); return (add_trusted_key(name, flags, proto, alg, str)); } int add_zone_master(zone_config zh, struct in_addr address) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_addr[zp->z_addrcnt] = address; zp->z_addrcnt++; if (zp->z_addrcnt >= NSMAX) { ns_warning(ns_log_config, "NSMAX reached for zone '%s'", zp->z_origin); zp->z_addrcnt = NSMAX - 1; } return (1); } int add_zone_notify(zone_config zh, struct in_addr address) { #ifdef BIND_NOTIFY struct zoneinfo *zp; int i; zp = zh.opaque; INSIST(zp != NULL); /* Check for duplicates. */ for (i = 0; i < zp->z_notify_count; i++) { if (memcmp(zp->z_also_notify + i, &address, sizeof address) == 0) { ns_warning(ns_log_config, "duplicate also-notify address ignored [%s] for zone '%s'", inet_ntoa(address), zp->z_origin); return (1); } } i = 0; if (zp->z_also_notify == NULL) { zp->z_also_notify = memget(sizeof *zp->z_also_notify); if (zp->z_also_notify == NULL) i = 1; } else { register size_t size; register struct in_addr *an_tmp; size = zp->z_notify_count * sizeof *zp->z_also_notify; an_tmp = memget(size + sizeof *zp->z_also_notify); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, zp->z_also_notify, size); memput(zp->z_also_notify, size); zp->z_also_notify = an_tmp; } } if (i == 0) { zp->z_also_notify[zp->z_notify_count] = address; zp->z_notify_count++; } else { ns_warning(ns_log_config, "also-notify add failed (memget) [%s] for zone '%s'", inet_ntoa(address), zp->z_origin); } #endif return (1); } /* Options */ options new_options() { options op; op = (options)memget(sizeof (struct options)); if (op == NULL) panic("memget failed in new_options()", NULL); op->version = savestr(ShortVersion, 1); op->directory = savestr(".", 1); op->pid_filename = savestr(_PATH_PIDFILE, 1); op->named_xfer = savestr(_PATH_XFER, 1); op->dump_filename = savestr(_PATH_DUMPFILE, 1); op->stats_filename = savestr(_PATH_STATS, 1); op->memstats_filename = savestr(_PATH_MEMSTATS, 1); op->flags = DEFAULT_OPTION_FLAGS; op->transfers_in = DEFAULT_XFERS_RUNNING; op->transfers_per_ns = DEFAULT_XFERS_PER_NS; op->transfers_out = 0; op->serial_queries = MAXQSERIAL; op->transfer_format = axfr_one_answer; op->max_transfer_time_in = MAX_XFER_TIME; memset(&op->query_source, 0, sizeof op->query_source); op->query_source.sin_family = AF_INET; op->query_source.sin_addr.s_addr = htonl(INADDR_ANY); op->query_source.sin_port = htons(0); /* INPORT_ANY */ op->axfr_src.s_addr = 0; #ifdef BIND_NOTIFY op->notify_count = 0; op->also_notify = NULL; #endif op->blackhole_acl = NULL; op->query_acl = NULL; op->transfer_acl = NULL; op->recursion_acl = NULL; op->sortlist = NULL; op->topology = NULL; op->data_size = 0UL; /* use system default */ op->stack_size = 0UL; /* use system default */ op->core_size = 0UL; /* use system default */ op->files = ULONG_MAX; /* unlimited */ op->check_names[primary_trans] = fail; op->check_names[secondary_trans] = warn; op->check_names[response_trans] = ignore; op->listen_list = NULL; op->fwdtab = NULL; /* XXX init forwarding */ op->clean_interval = 3600; op->interface_interval = 3600; op->stats_interval = 3600; op->ordering = NULL; op->max_ncache_ttl = DEFAULT_MAX_NCACHE_TTL; op->lame_ttl = NTTL; op->heartbeat_interval = 3600; op->max_log_size_ixfr = 20; op->minroots = MINROOTS; return (op); } void free_options(options op) { INSIST(op != NULL); if (op->version) freestr(op->version); if (op->directory) freestr(op->directory); if (op->pid_filename) freestr(op->pid_filename); if (op->named_xfer) freestr(op->named_xfer); if (op->dump_filename) freestr(op->dump_filename); if (op->stats_filename) freestr(op->stats_filename); if (op->memstats_filename) freestr(op->memstats_filename); #ifdef BIND_NOTIFY if (op->also_notify) free_also_notify(op); #endif if (op->blackhole_acl) free_ip_match_list(op->blackhole_acl); if (op->query_acl) free_ip_match_list(op->query_acl); if (op->recursion_acl) free_ip_match_list(op->recursion_acl); if (op->transfer_acl) free_ip_match_list(op->transfer_acl); if (op->sortlist) free_ip_match_list(op->sortlist); if (op->ordering) free_rrset_order_list(op->ordering); if (op->topology) free_ip_match_list(op->topology); if (op->listen_list) free_listen_info_list(op->listen_list); if (op->fwdtab) free_forwarders(op->fwdtab); memput(op, sizeof *op); } static void set_boolean_option(u_int *op_flags, int bool_opt, int value) { INSIST(op_flags != NULL); switch (bool_opt) { case OPTION_NORECURSE: case OPTION_NOFETCHGLUE: case OPTION_FORWARD_ONLY: case OPTION_FAKE_IQUERY: case OPTION_NONOTIFY: case OPTION_NONAUTH_NXDOMAIN: case OPTION_MULTIPLE_CNAMES: case OPTION_USE_IXFR: case OPTION_MAINTAIN_IXFR_BASE: case OPTION_HOSTSTATS: case OPTION_DEALLOC_ON_EXIT: case OPTION_USE_ID_POOL: case OPTION_NORFC2308_TYPE1: case OPTION_NODIALUP: case OPTION_TREAT_CR_AS_SPACE: if (value) *op_flags |= bool_opt; else *op_flags &= ~bool_opt; break; default: panic("unexpected option in set_boolean_option", NULL); } } void set_global_boolean_option(options op, int bool_opt, int value) { INSIST(op != NULL); set_boolean_option(&op->flags, bool_opt, value); } void set_zone_boolean_option(zone_config zh, int bool_opt, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); set_boolean_option(&zp->z_options, bool_opt, value); /* Flag that zone option overrides corresponding global option */ zp->z_optset |= bool_opt; } #ifdef HAVE_GETRUSAGE enum limit { Datasize, Stacksize, Coresize, Files }; static struct rlimit initial_data_size; static struct rlimit initial_stack_size; static struct rlimit initial_core_size; static struct rlimit initial_num_files; static void get_initial_limits() { int fdlimit = evHighestFD(ev) + 1; # ifdef RLIMIT_DATA if (getrlimit(RLIMIT_DATA, &initial_data_size) < 0) ns_warning(ns_log_config, "getrlimit(DATA): %s", strerror(errno)); # endif # ifdef RLIMIT_STACK if (getrlimit(RLIMIT_STACK, &initial_stack_size) < 0) ns_warning(ns_log_config, "getrlimit(STACK): %s", strerror(errno)); # endif # ifdef RLIMIT_CORE if (getrlimit(RLIMIT_CORE, &initial_core_size) < 0) ns_warning(ns_log_config, "getrlimit(CORE): %s", strerror(errno)); # endif # ifdef RLIMIT_NOFILE if (getrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) ns_warning(ns_log_config, "getrlimit(NOFILE): %s", strerror(errno)); else if (initial_num_files.rlim_cur > fdlimit) { initial_num_files.rlim_cur = fdlimit; if (initial_num_files.rlim_cur > initial_num_files.rlim_max) initial_num_files.rlim_max = fdlimit; if (setrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) { ns_warning(ns_log_config, "setrlimit(files): %s", strerror(errno)); } else { ns_warning(ns_log_config, "limit files set to fdlimit (%d)", fdlimit); } } # endif } static void ns_rlimit(enum limit limit, u_long limit_value) { struct rlimit limits, old_limits; int rlimit = -1; int fdlimit = evHighestFD(ev) + 1; char *name; rlimit_type value; if (limit_value == ULONG_MAX) { #ifndef RLIMIT_FILE_INFINITY if (limit == Files) value = MIN((rlimit_type)evHighestFD(ev) + 1, initial_num_files.rlim_max); else #endif value = (rlimit_type)RLIM_INFINITY; } else value = (rlimit_type)limit_value; limits.rlim_cur = limits.rlim_max = value; switch (limit) { case Datasize: #ifdef RLIMIT_DATA rlimit = RLIMIT_DATA; #endif name = "max data size"; if (value == 0) limits = initial_data_size; break; case Stacksize: #ifdef RLIMIT_STACK rlimit = RLIMIT_STACK; #endif name = "max stack size"; if (value == 0) limits = initial_stack_size; break; case Coresize: #ifdef RLIMIT_CORE rlimit = RLIMIT_CORE; #endif name = "max core size"; if (value == 0) limits = initial_core_size; break; case Files: #ifdef RLIMIT_NOFILE rlimit = RLIMIT_NOFILE; #endif name = "max number of open files"; if (value == 0) limits = initial_num_files; if (value > fdlimit) limits.rlim_cur = limits.rlim_max = value = fdlimit; break; default: name = NULL; /* Make gcc happy. */ panic("impossible condition in ns_rlimit()", NULL); } if (rlimit == -1) { ns_warning(ns_log_config, "limit \"%s\" not supported on this system - ignored", name); return; } if (getrlimit(rlimit, &old_limits) < 0) { ns_warning(ns_log_config, "getrlimit(%s): %s", name, strerror(errno)); } if (user_id != 0 && limits.rlim_max == RLIM_INFINITY) limits.rlim_cur = limits.rlim_max = old_limits.rlim_max; if (setrlimit(rlimit, &limits) < 0) { ns_warning(ns_log_config, "setrlimit(%s): %s", name, strerror(errno)); return; } else { if (value == 0) ns_debug(ns_log_config, 3, "%s is default", name); else if (value == RLIM_INFINITY) ns_debug(ns_log_config, 3, "%s is unlimited", name); else #ifdef RLIMIT_LONGLONG ns_debug(ns_log_config, 3, "%s is %llu", name, (unsigned long long)value); #else ns_debug(ns_log_config, 3, "%s is %lu", name, value); #endif } } #endif /* HAVE_GETRUSAGE */ listen_info_list new_listen_info_list() { listen_info_list ll; ll = (listen_info_list)memget(sizeof (struct listen_info_list)); if (ll == NULL) panic("memget failed in new_listen_info_list()", NULL); ll->first = NULL; ll->last = NULL; return (ll); } void free_listen_info_list(listen_info_list ll) { listen_info li, next_li; INSIST(ll != NULL); for (li = ll->first; li != NULL; li = next_li) { next_li = li->next; free_ip_match_list(li->list); memput(li, sizeof *li); } memput(ll, sizeof *ll); } void add_listen_on(options op, u_short port, ip_match_list iml) { listen_info_list ll; listen_info ni; INSIST(op != NULL); if (op->listen_list == NULL) op->listen_list = new_listen_info_list(); ll = op->listen_list; ni = (listen_info)memget(sizeof (struct listen_info)); if (ni == NULL) panic("memget failed in add_listen_on", NULL); ni->port = port; ni->list = iml; ni->next = NULL; if (ll->last != NULL) ll->last->next = ni; ll->last = ni; if (ll->first == NULL) ll->first = ni; } FILE * write_open(char *filename) { FILE *stream; int fd; struct stat sb; int regular; if (stat(filename, &sb) < 0) { if (errno != ENOENT) { ns_error(ns_log_os, "write_open: stat of %s failed: %s", filename, strerror(errno)); return (NULL); } regular = 1; } else regular = (sb.st_mode & S_IFREG); if (!regular) { ns_error(ns_log_os, "write_open: %s isn't a regular file", filename); return (NULL); } (void)unlink(filename); fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd < 0) return (NULL); + (void) fchown(fd, user_id, group_id); stream = fdopen(fd, "w"); if (stream == NULL) (void)close(fd); return (stream); } void update_pid_file() { FILE *fp; REQUIRE(server_options != NULL); REQUIRE(server_options->pid_filename != NULL); /* XXX */ ns_debug(ns_log_default, 1, "update_pid_file()"); if (current_pid_filename != NULL) { (void)unlink(current_pid_filename); freestr(current_pid_filename); current_pid_filename = NULL; } current_pid_filename = savestr(server_options->pid_filename, 0); if (current_pid_filename == NULL) { ns_error(ns_log_config, "savestr() failed in update_pid_file()"); return; } fp = write_open(current_pid_filename); if (fp != NULL) { (void) fprintf(fp, "%ld\n", (long)getpid()); (void) fclose(fp); } else ns_error(ns_log_config, "couldn't create pid file '%s'", server_options->pid_filename); } /* * XXX This function will eventually be public and will be relocated to * the UNIX OS support library. */ static int os_change_directory(const char *name) { struct stat sb; if (name == NULL || *name == '\0') { errno = EINVAL; return (0); } if (chdir(name) < 0) return (0); if (stat(name, &sb) < 0) { ns_error(ns_log_os, "stat(%s) failed: %s", name, strerror(errno)); return (1); } if (sb.st_mode & S_IWOTH) ns_warning(ns_log_os, "directory %s is world-writable", name); return (1); } static void periodic_getnetconf(evContext ctx, void *uap, struct timespec due, struct timespec inter) { getnetconf(1); } static void set_interval_timer(int which_timer, int interval) { evTimerID *tid = NULL; evTimerFunc func = NULL; switch (which_timer) { case CLEAN_TIMER: tid = &clean_timer; func = ns_cleancache; break; case INTERFACE_TIMER: tid = &interface_timer; func = periodic_getnetconf; break; case STATS_TIMER: tid = &stats_timer; func = ns_logstats; break; case HEARTBEAT_TIMER: tid = &heartbeat_timer; func = ns_heartbeat; break; default: ns_panic(ns_log_config, 1, "set_interval_timer: unknown timer %d", which_timer); } if ((active_timers & which_timer) != 0) { if (interval > 0) { if (evResetTimer(ev, *tid, func, NULL, evAddTime(evNowTime(), evConsTime(interval, 0)), evConsTime(interval, 0)) < 0) ns_error(ns_log_config, "evResetTimer %d interval %d failed: %s", which_timer, interval, strerror(errno)); } else { if (evClearTimer(ev, *tid) < 0) ns_error(ns_log_config, "evClearTimer %d failed: %s", which_timer, strerror(errno)); else active_timers &= ~which_timer; } } else if (interval > 0) { if (evSetTimer(ev, func, NULL, evAddTime(evNowTime(), evConsTime(interval, 0)), evConsTime(interval, 0), tid) < 0) ns_error(ns_log_config, "evSetTimer %d interval %d failed: %s", which_timer, interval, strerror(errno)); else active_timers |= which_timer; } } /* * Set all named global options based on the global options structure * generated by the parser. */ void set_options(options op, int is_default) { INSIST(op != NULL); if (op->listen_list == NULL) { ip_match_list iml; ip_match_element ime; struct in_addr address; op->listen_list = new_listen_info_list(); address.s_addr = htonl(INADDR_ANY); iml = new_ip_match_list(); ime = new_ip_match_pattern(address, 0); add_to_ip_match_list(iml, ime); add_listen_on(op, htons(NS_DEFAULTPORT), iml); } if (op->topology == NULL) { ip_match_list iml; ip_match_element ime; /* default topology is { localhost; localnets; } */ iml = new_ip_match_list(); ime = new_ip_match_localhost(); add_to_ip_match_list(iml, ime); ime = new_ip_match_localnets(); add_to_ip_match_list(iml, ime); op->topology = iml; } if (server_options != NULL) free_options(server_options); server_options = op; /* XXX should validate pid filename */ INSIST(op->pid_filename != NULL); if (op->directory && !os_change_directory(op->directory)) ns_panic(ns_log_config, 0, "can't change directory to %s: %s", op->directory, strerror(errno)); /* XXX currently a value of 0 means "use default"; it would be better if the options block had a "attributes updated" vector (like the way X deals with GC updates) */ if (!op->transfers_in) op->transfers_in = DEFAULT_XFERS_RUNNING; else if (op->transfers_in > MAX_XFERS_RUNNING) { ns_warning(ns_log_config, "the maximum number of concurrent inbound transfers is %d", MAX_XFERS_RUNNING); op->transfers_in = MAX_XFERS_RUNNING; } if (!op->transfers_per_ns) op->transfers_per_ns = DEFAULT_XFERS_PER_NS; if (!op->max_transfer_time_in) op->max_transfer_time_in = MAX_XFER_TIME; /* XXX currently transfers_out is not used */ if (!op->max_ncache_ttl) op->max_ncache_ttl = DEFAULT_MAX_NCACHE_TTL; else if (op->max_ncache_ttl > max_cache_ttl) op->max_ncache_ttl = max_cache_ttl; if (op->lame_ttl > (3 * NTTL)) op->lame_ttl = 3 * NTTL; /* * Limits */ #ifdef HAVE_GETRUSAGE ns_rlimit(Datasize, op->data_size); ns_rlimit(Stacksize, op->stack_size); ns_rlimit(Coresize, op->core_size); ns_rlimit(Files, op->files); #else ns_info(ns_log_config, "cannot set resource limits on this system"); #endif /* * Timers */ set_interval_timer(CLEAN_TIMER, server_options->clean_interval); set_interval_timer(INTERFACE_TIMER, server_options->interface_interval); set_interval_timer(STATS_TIMER, server_options->stats_interval); set_interval_timer(HEARTBEAT_TIMER, server_options->heartbeat_interval); options_installed = 1; default_options_installed = is_default; } void use_default_options() { set_options(new_options(), 1); } /* * rrset order types */ static struct res_sym order_table [] = { { unknown_order, " unknown " }, /* can't match */ { fixed_order, "fixed" }, { cyclic_order, "cyclic" }, { random_order, "random" }, { unknown_order, NULL } }; /* * Return the print name of the ordering value. */ const char * p_order(int order) { return (__sym_ntos(order_table, order, (int *)0)); } /* * Lookup the ordering by name and return the matching enum value. */ enum ordering lookup_ordering(const char *name) { int i; for (i = 0; order_table[i].name != NULL; i++) if (strcasecmp(name,order_table[i].name) == 0) return ((enum ordering)order_table[i].number); return (unknown_order); } /* * rrset-order Lists */ rrset_order_list new_rrset_order_list() { rrset_order_list rol ; rol = (rrset_order_list)memget(sizeof (struct rrset_order_list)); if (rol == NULL) panic("memget failed in new_rrset_order_list", NULL); rol->first = NULL; rol->last = NULL; return (rol); } void free_rrset_order_list(rrset_order_list rol) { rrset_order_element roe, next_element; for (roe = rol->first; roe != NULL; roe = next_element) { next_element = roe->next; freestr(roe->name); memput(roe, sizeof (*roe)); } memput(rol, sizeof (*rol)); } void add_to_rrset_order_list(rrset_order_list rol, rrset_order_element roe) { INSIST(rol != NULL); INSIST(roe != NULL); if (rol->last != NULL) rol->last->next = roe; roe->next = NULL; rol->last = roe; if (rol->first == NULL) rol->first = roe; } /* XXX this isn't being used yet, but it probably should be. Where? */ void dprint_rrset_order_list(int category, rrset_order_list rol, int indent, char *allow, char *deny) { rrset_order_element roe ; char spaces[40+1]; INSIST(rol != NULL); if (indent > 40) indent = 40; if (indent) memset(spaces, ' ', indent); spaces[indent] = '\0'; for (roe = rol->first; roe != NULL; roe = roe->next) { ns_debug(category, 7, "%sclass %s type %s name %s order %s", spaces, p_class(roe->class), p_type(roe->type), roe->name, p_order(roe->order)); } } rrset_order_element new_rrset_order_element(int class, int type, char *name, enum ordering order) { rrset_order_element roe; int i ; roe = (rrset_order_element)memget(sizeof (struct rrset_order_element)); if (roe == NULL) panic("memget failed in new_rrset_order_element", NULL); roe->class = class ; roe->type = type ; roe->name = name; roe->order = order; i = strlen(roe->name) - 1; INSIST (i >= 0); if (roe->name[i - 1] == '.') { /* We compare from right to left so we don't need a dot on the end. */ roe->name[i - 1] = '\0' ; } return roe ; } /* * IP Matching Lists */ ip_match_list new_ip_match_list() { ip_match_list iml; iml = (ip_match_list)memget(sizeof (struct ip_match_list)); if (iml == NULL) panic("memget failed in new_ip_match_list", NULL); iml->first = NULL; iml->last = NULL; return (iml); } void free_ip_match_list(ip_match_list iml) { ip_match_element ime, next_element; for (ime = iml->first; ime != NULL; ime = next_element) { next_element = ime->next; memput(ime, sizeof *ime); } memput(iml, sizeof *iml); } ip_match_element new_ip_match_pattern(struct in_addr address, u_int mask_bits) { ip_match_element ime; u_int32_t mask; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_pattern", NULL); ime->type = ip_match_pattern; ime->flags = 0; ime->u.direct.address = address; if (mask_bits == 0) /* can't shift >= the size of a type in bits, so we deal with an empty mask here */ mask = 0; else { /* set the 'mask_bits' most significant bits */ mask = 0xffffffffU; mask >>= (32 - mask_bits); mask <<= (32 - mask_bits); } mask = ntohl(mask); ime->u.direct.mask.s_addr = mask; ime->next = NULL; if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, ime->u.direct.mask)) { memput(ime, sizeof *ime); ime = NULL; } return (ime); } ip_match_element new_ip_match_mask(struct in_addr address, struct in_addr mask) { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_pattern", NULL); ime->type = ip_match_pattern; ime->flags = 0; ime->u.direct.address = address; ime->u.direct.mask = mask; ime->next = NULL; if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, ime->u.direct.mask)) { memput(ime, sizeof *ime); ime = NULL; } return (ime); } ip_match_element new_ip_match_indirect(ip_match_list iml) { ip_match_element ime; INSIST(iml != NULL); ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_indirect", NULL); ime->type = ip_match_indirect; ime->flags = 0; ime->u.indirect.list = iml; ime->next = NULL; return (ime); } ip_match_element new_ip_match_key(DST_KEY *dst_key) { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_key", NULL); ime->type = ip_match_key; ime->flags = 0; ime->u.key.key = dst_key; return (ime); } ip_match_element new_ip_match_localhost() { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_localhost", NULL); ime->type = ip_match_localhost; ime->flags = 0; ime->u.indirect.list = NULL; ime->next = NULL; return (ime); } ip_match_element new_ip_match_localnets() { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_localnets", NULL); ime->type = ip_match_localnets; ime->flags = 0; ime->u.indirect.list = NULL; ime->next = NULL; return (ime); } void ip_match_negate(ip_match_element ime) { if (ime->flags & IP_MATCH_NEGATE) ime->flags &= ~IP_MATCH_NEGATE; else ime->flags |= IP_MATCH_NEGATE; } void add_to_ip_match_list(ip_match_list iml, ip_match_element ime) { INSIST(iml != NULL); INSIST(ime != NULL); if (iml->last != NULL) iml->last->next = ime; ime->next = NULL; iml->last = ime; if (iml->first == NULL) iml->first = ime; } void dprint_ip_match_list(int category, ip_match_list iml, int indent, char *allow, char *deny) { ip_match_element ime; char spaces[40+1]; char addr_text[sizeof "255.255.255.255"]; char mask_text[sizeof "255.255.255.255"]; INSIST(iml != NULL); if (indent > 40) indent = 40; if (indent) memset(spaces, ' ', indent); spaces[indent] = '\0'; for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: memset(addr_text, 0, sizeof addr_text); strncpy(addr_text, inet_ntoa(ime->u.direct.address), ((sizeof addr_text) - 1)); memset(mask_text, 0, sizeof mask_text); strncpy(mask_text, inet_ntoa(ime->u.direct.mask), ((sizeof mask_text) - 1)); ns_debug(category, 7, "%s%saddr: %s, mask: %s", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, addr_text, mask_text); break; case ip_match_localhost: ns_debug(category, 7, "%s%slocalhost", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow); break; case ip_match_localnets: ns_debug(category, 7, "%s%slocalnets", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow); break; case ip_match_indirect: ns_debug(category, 7, "%s%sindirect list %p", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, ime->u.indirect.list); if (ime->u.indirect.list != NULL) dprint_ip_match_list(category, ime->u.indirect.list, indent+2, allow, deny); break; case ip_match_key: ns_debug(category, 7, "%s%skey %s", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, ime->u.key.key->dk_key_name); break; default: panic("unexpected ime type in dprint_ip_match_list()", NULL); } } } int ip_match_addr_or_key(ip_match_list iml, struct in_addr address, DST_KEY *key) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: if (key == NULL) { indirect = 0; break; } else { if (ns_samename(ime->u.key.key->dk_key_name, key->dk_key_name) == 1) return (1); else continue; } default: panic("unexpected ime type in ip_match_addr_or_key()", NULL); } if (indirect) { ret = ip_match_addr_or_key(ime->u.indirect.list, address, key); - if (ret >= 0) { + if (ret > 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } int ip_match_address(ip_match_list iml, struct in_addr address) { return ip_match_addr_or_key(iml, address, NULL); } int ip_addr_or_key_allowed(ip_match_list iml, struct in_addr address, DST_KEY *key) { int ret; if (iml == NULL) return (0); ret = ip_match_addr_or_key(iml, address, key); if (ret < 0) ret = 0; return (ret); } int ip_address_allowed(ip_match_list iml, struct in_addr address) { return(ip_addr_or_key_allowed(iml, address, NULL)); } int ip_match_network(ip_match_list iml, struct in_addr address, struct in_addr mask) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: indirect = 0; break; default: indirect = 0; /* Make gcc happy. */ panic("unexpected ime type in ip_match_network()", NULL); } if (indirect) { ret = ip_match_network(ime->u.indirect.list, address, mask); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (address.s_addr == ime->u.direct.address.s_addr && mask.s_addr == ime->u.direct.mask.s_addr) { if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } int distance_of_address(ip_match_list iml, struct in_addr address) { ip_match_element ime; int ret; int indirect; int distance; INSIST(iml != NULL); for (distance = 1, ime = iml->first; ime != NULL; ime = ime->next, distance++) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: indirect = 0; return (-1); default: indirect = 0; /* Make gcc happy. */ panic("unexpected ime type in distance_of_address()", NULL); } if (indirect) { ret = ip_match_address(ime->u.indirect.list, address); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; if (distance > MAX_TOPOLOGY_DISTANCE) distance = MAX_TOPOLOGY_DISTANCE; if (ret) return (distance); else return (MAX_TOPOLOGY_DISTANCE); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { if (distance > MAX_TOPOLOGY_DISTANCE) distance = MAX_TOPOLOGY_DISTANCE; if (ime->flags & IP_MATCH_NEGATE) return (MAX_TOPOLOGY_DISTANCE); else return (distance); } } } return (UNKNOWN_TOPOLOGY_DISTANCE); } int ip_match_is_none(ip_match_list iml) { ip_match_element ime; if ((iml == NULL) || (iml->first == NULL)) return (1); ime = iml->first; if (ime->type == ip_match_indirect) { if (ime->flags & IP_MATCH_NEGATE) return (0); iml = ime->u.indirect.list; if ((iml == NULL) || (iml->first == NULL)) return (0); ime = iml->first; } if (ime->type == ip_match_pattern) { if ((ime->flags & IP_MATCH_NEGATE) && ime->u.direct.address.s_addr == 0 && ime->u.direct.mask.s_addr == 0) return (1); } return (0); } +/* + * find_forwarder finds the fwddata structure for an address, + * allocating one if we can't find one already existing. + */ +static struct fwddata * +find_forwarder(struct in_addr address) +{ + struct fwddata *fdp; + struct databuf *ns, *nsdata; + register int i; + + for (i=0;ifwdaddr.sin_addr,&address,sizeof(address))==0) { + fdp->ref_count++; + return fdp; + } + } + + fdp = (struct fwddata *)memget(sizeof(struct fwddata)); + if (!fdp) + panic("memget failed in find_forwarder", NULL); + fdp->fwdaddr.sin_family = AF_INET; + fdp->fwdaddr.sin_addr = address; + fdp->fwdaddr.sin_port = ns_port; + ns = fdp->ns = (struct databuf *)memget(sizeof(*ns)); + if (!ns) + panic("memget failed in find_forwarder", NULL); + memset(ns,0,sizeof(*ns)); + nsdata = fdp->nsdata = (struct databuf *)memget(sizeof(*nsdata)); + if (!nsdata) + panic("memget failed in find_forwarder", NULL); + memset(nsdata,0,sizeof(*nsdata)); + ns->d_type = T_NS; + ns->d_class = C_IN; + ns->d_rcnt=1; + nsdata->d_type = T_A; + nsdata->d_class = C_IN; + nsdata->d_nstime = 1 + (int)(25.0*rand()/(RAND_MAX + 1.0)); + nsdata->d_rcnt=1; + fdp->ref_count=1; + + i=0; + if (fwddata == NULL) { + fwddata = memget(sizeof *fwddata); + if (fwddata == NULL) + i = 1; + } else { + register size_t size; + register struct fwddata **an_tmp; + size = fwddata_count * sizeof *fwddata; + an_tmp = memget(size + sizeof *fwddata); + if (an_tmp == NULL) { + i = 1; + } else { + memcpy(an_tmp, fwddata, size); + memput(fwddata, size); + fwddata = an_tmp; + } + } + + if (i == 0) { + fwddata[fwddata_count] = fdp; + fwddata_count++; + } else { + ns_warning(ns_log_config, + "forwarder add failed (memget) [%s]", + inet_ntoa(address)); + } + + return fdp; +} /* * Forwarder glue * * XXX This will go away when the rest of bind understands * forward zones. */ static void add_forwarder(struct fwdinfo **fipp, struct in_addr address) { struct fwdinfo *fip = *fipp, *ftp = NULL; + struct fwddata *fdp; +#ifdef FWD_LOOP + if (aIsUs(address)) { + ns_error(ns_log_config, "forwarder '%s' ignored, my address", + inet_ntoa(address)); + return; + } +#endif /* FWD_LOOP */ + /* On multiple forwarder lines, move to end of the list. */ while (fip != NULL && fip->next != NULL) fip = fip->next; + fdp = find_forwarder(address); ftp = (struct fwdinfo *)memget(sizeof(struct fwdinfo)); if (!ftp) panic("memget failed in add_forwarder", NULL); - ftp->fwdaddr.sin_family = AF_INET; - ftp->fwdaddr.sin_addr = address; - ftp->fwdaddr.sin_port = ns_port; -#ifdef FWD_LOOP - if (aIsUs(ftp->fwdaddr.sin_addr)) { - ns_error(ns_log_config, "forwarder '%s' ignored, my address", - inet_ntoa(address)); - memput(ftp, sizeof *ftp); - return; - } -#endif /* FWD_LOOP */ + ftp->fwddata = fdp; ftp->next = NULL; if (fip == NULL) *fipp = ftp; /* First time only */ else fip->next = ftp; } void free_also_notify(options op) { #ifdef BIND_NOTIFY memput(op->also_notify, op->notify_count * sizeof *op->also_notify); op->also_notify = NULL; op->notify_count = 0; #endif } int add_global_also_notify(options op, struct in_addr address) { #ifdef BIND_NOTIFY int i; INSIST(op != NULL); ns_debug(ns_log_config, 2, "adding global notify %s", inet_ntoa(address)); /* Check for duplicates. */ for (i = 0; i < op->notify_count; i++) { if (memcmp(op->also_notify + i, &address, sizeof address) == 0) { ns_warning(ns_log_config, "duplicate global also-notify address ignored [%s]", inet_ntoa(address)); return (1); } } i = 0; if (op->also_notify == NULL) { op->also_notify = memget(sizeof *op->also_notify); if (op->also_notify == NULL) i = 1; } else { register size_t size; register struct in_addr *an_tmp; size = op->notify_count * sizeof *op->also_notify; an_tmp = memget(size + sizeof *op->also_notify); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, op->also_notify, size); memput(op->also_notify, size); op->also_notify = an_tmp; } } if (i == 0) { op->also_notify[op->notify_count] = address; op->notify_count++; } else { ns_warning(ns_log_config, "global also-notify add failed (memget) [%s]", inet_ntoa(address)); } #endif return (1); } void add_global_forwarder(options op, struct in_addr address) { -#ifdef SLAVE_FORWARD - struct fwdinfo *fip; - int forward_count; -#endif INSIST(op != NULL); ns_debug(ns_log_config, 2, "adding default forwarder %s", inet_ntoa(address)); add_forwarder(&op->fwdtab, address); - -#ifdef SLAVE_FORWARD - /* - ** Set the slave retry time to 60 seconds total divided - ** between each forwarder - */ - for (forward_count = 0, fip = op->fwdtab; fip != NULL; fip = fip->next) - forward_count++; - if (forward_count != 0) { - slave_retry = (int) (60 / forward_count); - if(slave_retry <= 0) - slave_retry = 1; - } -#endif } void set_zone_forward(zone_config zh) { struct zoneinfo *zp; zp = zh.opaque; zp->z_flags |= Z_FORWARD_SET; set_zone_boolean_option(zh, OPTION_FORWARD_ONLY, 0); } void add_zone_forwarder(zone_config zh, struct in_addr address) { struct zoneinfo *zp; char *zname; zp = zh.opaque; INSIST(zp != NULL); zname = (zp->z_origin[0] == '\0') ? "." : zp->z_origin; ns_debug(ns_log_config, 2, "adding forwarder %s for zone zone '%s'", inet_ntoa(address), zname); zp->z_flags |= Z_FORWARD_SET; add_forwarder(&zp->z_fwdtab, address); } void free_forwarders(struct fwdinfo *fwdtab) { struct fwdinfo *ftp, *fnext; for (ftp = fwdtab; ftp != NULL; ftp = fnext) { fnext = ftp->next; + if (!--ftp->fwddata->ref_count) { + memput(ftp->fwddata->ns, sizeof *ftp->fwddata->ns); + memput(ftp->fwddata->nsdata, + sizeof *ftp->fwddata->nsdata); + memput(ftp->fwddata,sizeof *ftp->fwddata); + } memput(ftp, sizeof *ftp); } fwdtab = NULL; } /* * Servers */ static server_info new_server(struct in_addr address) { server_info si; si = (server_info)memget(sizeof (struct server_info)); if (si == NULL) panic("memget failed in new_server()", NULL); si->address = address; si->flags = 0U; si->transfers = 0; si->transfer_format = axfr_use_default; si->key_list = NULL; si->next = NULL; if (server_options->flags & OPTION_MAINTAIN_IXFR_BASE) si->flags |= SERVER_INFO_SUPPORT_IXFR; else si->flags &= ~SERVER_INFO_SUPPORT_IXFR; return (si); } static void free_server(server_info si) { /* Don't free key; it'll be done when the auth table is freed. */ memput(si, sizeof *si); } server_info find_server(struct in_addr address) { server_info si; for (si = nameserver_info; si != NULL; si = si->next) if (si->address.s_addr == address.s_addr) break; return (si); } static void add_server(server_info si) { ip_match_element ime; si->next = nameserver_info; nameserver_info = si; /* * To ease transition, we'll add bogus nameservers to an * ip matching list. This will probably be redone when the * merging of nameserver data structures occurs. */ if (si->flags & SERVER_INFO_BOGUS) { ime = new_ip_match_pattern(si->address, 32); INSIST(ime != NULL); add_to_ip_match_list(bogus_nameservers, ime); } ns_debug(ns_log_config, 3, "server %s: flags %08x transfers %d", inet_ntoa(si->address), si->flags, si->transfers); if (si->key_list != NULL) dprint_key_info_list(si->key_list); } static void free_nameserver_info() { server_info si_next, si; for (si = nameserver_info; si != NULL; si = si_next) { si_next = si->next; free_server(si); } nameserver_info = NULL; if (bogus_nameservers != NULL) { free_ip_match_list(bogus_nameservers); bogus_nameservers = NULL; } } static void free_secretkey_info() { if (secretkey_info != NULL) { free_key_info_list(secretkey_info); secretkey_info = NULL; } } server_config begin_server(struct in_addr address) { server_config sc; sc.opaque = new_server(address); return (sc); } void end_server(server_config sc, int should_install) { server_info si; si = sc.opaque; INSIST(si != NULL); if (should_install) add_server(si); else free_server(si); sc.opaque = NULL; } void set_server_option(server_config sc, int bool_opt, int value) { server_info si; si = sc.opaque; INSIST(si != NULL); switch (bool_opt) { case SERVER_INFO_BOGUS: case SERVER_INFO_SUPPORT_IXFR: if (value) si->flags |= bool_opt; else si->flags &= ~bool_opt; break; default: panic("unexpected option in set_server_option", NULL); } } void set_server_transfers(server_config sc, int transfers) { server_info si; si = sc.opaque; INSIST(si != NULL); if (transfers < 0) transfers = 0; si->transfers = transfers; } void set_server_transfer_format(server_config sc, enum axfr_format transfer_format) { server_info si; si = sc.opaque; INSIST(si != NULL); si->transfer_format = transfer_format; } void add_server_key_info(server_config sc, DST_KEY *dst_key) { server_info si; si = sc.opaque; INSIST(si != NULL); if (si->key_list == NULL) si->key_list = new_key_info_list(); add_to_key_info_list(si->key_list, dst_key); } /* * Keys */ DST_KEY * new_key_info(char *name, char *algorithm, char *secret) { DST_KEY *dst_key; int alg, blen; u_char buffer[1024]; INSIST(name != NULL); INSIST(algorithm != NULL); INSIST(secret != NULL); alg = tsig_alg_value(algorithm); if (alg == -1) { ns_warning(ns_log_config, "Unsupported TSIG algorithm %s", algorithm); return (NULL); } blen = b64_pton(secret, buffer, sizeof(buffer)); if (blen < 0) { ns_warning(ns_log_config, "Invalid TSIG secret \"%s\"", secret); return (NULL); } dst_key = dst_buffer_to_key(name, alg, NS_KEY_TYPE_AUTH_ONLY|NS_KEY_NAME_ENTITY, NS_KEY_PROT_ANY, buffer, blen); if (dst_key == NULL) ns_warning(ns_log_config, "dst_buffer_to_key failed in new_key_info"); return (dst_key); } void free_key_info(DST_KEY *dst_key) { INSIST(dst_key != NULL); dst_free_key(dst_key); } DST_KEY * find_key(char *name, char *algorithm) { key_list_element ke; if (secretkey_info == NULL) return (NULL); for (ke = secretkey_info->first; ke != NULL; ke = ke->next) { DST_KEY *dst_key = ke->key; if (ns_samename(name, dst_key->dk_key_name) != 1) continue; if (algorithm == NULL || dst_key->dk_alg == tsig_alg_value(algorithm)) break; } if (ke == NULL) return (NULL); return (ke->key); } void dprint_key_info(DST_KEY *dst_key) { INSIST(dst_key != NULL); ns_debug(ns_log_config, 7, "key %s", dst_key->dk_key_name); ns_debug(ns_log_config, 7, " algorithm %d", dst_key->dk_alg); } key_info_list new_key_info_list() { key_info_list kil; kil = (key_info_list)memget(sizeof (struct key_info_list)); if (kil == NULL) panic("memget failed in new_key_info_list()", NULL); kil->first = NULL; kil->last = NULL; return (kil); } void free_key_info_list(key_info_list kil) { key_list_element kle, kle_next; INSIST(kil != NULL); for (kle = kil->first; kle != NULL; kle = kle_next) { kle_next = kle->next; /* note we do NOT free kle->info */ memput(kle, sizeof *kle); } memput(kil, sizeof *kil); } void add_to_key_info_list(key_info_list kil, DST_KEY *dst_key) { key_list_element kle; INSIST(kil != NULL); INSIST(dst_key != NULL); kle = (key_list_element)memget(sizeof (struct key_list_element)); if (kle == NULL) panic("memget failed in add_to_key_info_list()", NULL); kle->key = dst_key; if (kil->last != NULL) kil->last->next = kle; kle->next = NULL; kil->last = kle; if (kil->first == NULL) kil->first = kle; } void dprint_key_info_list(key_info_list kil) { key_list_element kle; INSIST(kil != NULL); for (kle = kil->first; kle != NULL; kle = kle->next) dprint_key_info(kle->key); } /* * Logging. */ log_config begin_logging() { log_config log_cfg; log_context lc; log_cfg = (log_config)memget(sizeof (struct log_config)); if (log_cfg == NULL) ns_panic(ns_log_config, 0, "memget failed creating log_config"); if (log_new_context(ns_log_max_category, logging_categories, &lc) < 0) ns_panic(ns_log_config, 0, "log_new_context() failed: %s", strerror(errno)); log_cfg->log_ctx = lc; log_cfg->eventlib_channel = NULL; log_cfg->packet_channel = NULL; log_cfg->default_debug_active = 0; return (log_cfg); } void add_log_channel(log_config log_cfg, int category, log_channel chan) { log_channel_type type; INSIST(log_cfg != NULL); type = log_get_channel_type(chan); if (category == ns_log_eventlib) { if (type != log_file && type != log_null) { ns_error(ns_log_config, "must specify a file or null channel for the eventlib category"); return; } if (log_cfg->eventlib_channel != NULL) { ns_error(ns_log_config, "only one channel allowed for the eventlib category"); return; } log_cfg->eventlib_channel = chan; } if (category == ns_log_packet) { if (type != log_file && type != log_null) { ns_error(ns_log_config, "must specify a file or null channel for the packet category"); return; } if (log_cfg->packet_channel != NULL) { ns_error(ns_log_config, "only one channel allowed for the packet category"); return; } log_cfg->packet_channel = chan; } if (log_add_channel(log_cfg->log_ctx, category, chan) < 0) { ns_error(ns_log_config, "log_add_channel() failed"); return; } if (chan == debug_channel) log_cfg->default_debug_active = 1; } void open_special_channels() { int using_null = 0; if (log_open_stream(eventlib_channel) == NULL) { eventlib_channel = null_channel; using_null = 1; } if (log_open_stream(packet_channel) == NULL) { packet_channel = null_channel; using_null = 1; } if (using_null && log_open_stream(null_channel) == NULL) ns_panic(ns_log_config, 1, "couldn't open null channel"); } void set_logging(log_config log_cfg, int is_default) { log_context lc; INSIST(log_cfg != NULL); lc = log_cfg->log_ctx; /* * Add the default category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_default)) { add_log_channel(log_cfg, ns_log_default, debug_channel); add_log_channel(log_cfg, ns_log_default, syslog_channel); } /* * Add the panic category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_panic)) { add_log_channel(log_cfg, ns_log_panic, stderr_channel); add_log_channel(log_cfg, ns_log_panic, syslog_channel); } /* * Add the eventlib category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_eventlib)) add_log_channel(log_cfg, ns_log_eventlib, debug_channel); /* * Add the packet category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_packet)) add_log_channel(log_cfg, ns_log_packet, debug_channel); #ifdef DEBUG /* * Preserve debugging state. */ log_option(lc, LOG_OPTION_DEBUG, debug); log_option(lc, LOG_OPTION_LEVEL, debug); #endif /* * Special case for query-log, so we can co-exist with the command * line option and SIGWINCH. */ if (log_category_is_active(lc, ns_log_queries)) qrylog = 1; /* * Cleanup the old context. */ if (need_logging_free) log_free_context(log_ctx); /* * The default file channels will never have their reference counts * drop to zero, and so they will not be closed by the logging system * when log_free_context() is called. We don't want to keep files * open unnecessarily, and we want them to behave like user-created * channels, so we close them here. */ if (log_get_stream(debug_channel) != stderr) (void)log_close_stream(debug_channel); (void)log_close_stream(null_channel); /* * Install the new context. */ log_ctx = lc; eventlib_channel = log_cfg->eventlib_channel; packet_channel = log_cfg->packet_channel; #ifdef DEBUG if (debug) { open_special_channels(); evSetDebug(ev, debug, log_get_stream(eventlib_channel)); } #endif log_ctx_valid = 1; need_logging_free = 1; logging_installed = 1; default_logging_installed = is_default; } void end_logging(log_config log_cfg, int should_install) { if (should_install) set_logging(log_cfg, 0); else log_free_context(log_cfg->log_ctx); memput(log_cfg, sizeof (struct log_config)); } void use_default_logging() { log_config log_cfg; log_cfg = begin_logging(); set_logging(log_cfg, 1); memput(log_cfg, sizeof (struct log_config)); } static void init_default_log_channels() { u_int flags; char *name; FILE *stream; syslog_channel = log_new_syslog_channel(0, log_info, LOG_DAEMON); if (syslog_channel == NULL || log_inc_references(syslog_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create syslog_channel"); flags = LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; if (foreground) { name = NULL; stream = stderr; } else { name = _PATH_DEBUG; stream = NULL; } debug_channel = log_new_file_channel(flags, log_info, name, stream, 0, ULONG_MAX); if (debug_channel == NULL || log_inc_references(debug_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create debug_channel"); + log_set_file_owner(debug_channel, user_id, group_id); stderr_channel = log_new_file_channel(0, log_info, NULL, stderr, 0, ULONG_MAX); if (stderr_channel == NULL || log_inc_references(stderr_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create stderr_channel"); + log_set_file_owner(stderr_channel, user_id, group_id); null_channel = log_new_file_channel(LOG_CHANNEL_OFF, log_info, _PATH_DEVNULL, NULL, 0, ULONG_MAX); if (null_channel == NULL || log_inc_references(null_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create null_channel"); + log_set_file_owner(null_channel, user_id, group_id); } static void shutdown_default_log_channels() { log_free_channel(syslog_channel); log_free_channel(debug_channel); log_free_channel(stderr_channel); log_free_channel(null_channel); } void init_logging() { int size; const struct ns_sym *s; char category_name[256]; size = ns_log_max_category * (sizeof (char *)); logging_categories = (char **)memget(size); if (logging_categories == NULL) ns_panic(ns_log_config, 0, "memget failed in init_logging"); memset(logging_categories, 0, size); for (s = category_constants; s != NULL && s->name != NULL; s++) { sprintf(category_name, "%s: ", s->name); logging_categories[s->number] = savestr(category_name, 1); } init_default_log_channels(); use_default_logging(); } void shutdown_logging() { int size; const struct ns_sym *s; evSetDebug(ev, 0, NULL); shutdown_default_log_channels(); log_free_context(log_ctx); for (s = category_constants; s != NULL && s->name != NULL; s++) freestr(logging_categories[s->number]); size = ns_log_max_category * (sizeof (char *)); memput(logging_categories, size); logging_categories = NULL; } /* * Main Loader */ void init_configuration() { /* * Remember initial limits for use if "default" is specified in * a config file. */ #ifdef HAVE_GETRUSAGE get_initial_limits(); #endif zone_symbol_table = new_symbol_table(ZONE_SYM_TABLE_SIZE, NULL); use_default_options(); parser_initialize(); ns_ctl_initialize(); config_initialized = 1; } void shutdown_configuration() { REQUIRE(config_initialized); ns_ctl_shutdown(); if (server_options != NULL) { free_options(server_options); server_options = NULL; } if (current_pid_filename != NULL) freestr(current_pid_filename); free_nameserver_info(); free_secretkey_info(); free_symbol_table(zone_symbol_table); parser_shutdown(); config_initialized = 0; } void load_configuration(const char *filename) { REQUIRE(config_initialized); ns_debug(ns_log_config, 3, "load configuration %s", filename); loading = 1; /* * Clean up any previous configuration and initialize * global data structures we'll be updating. */ free_nameserver_info(); free_secretkey_info(); bogus_nameservers = new_ip_match_list(); options_installed = 0; logging_installed = 0; parse_configuration(filename); /* * If the user didn't specify logging or options, but they previously * had specified one or both of them, then we need to * re-establish the default environment. We have to be careful * about when we install default options because the parser * must respect limits (e.g. data-size, number of open files) * specified in the options file. In the ordinary case where the * options section isn't changing on a zone reload, it would be bad * to lower these limits temporarily, because we might not survive * to the point where they get raised back again. The logging case * has similar motivation -- we don't want to override the existing * logging scheme (perhaps causing log messages to go somewhere * unexpected) when the user hasn't expressed a desire for a new * scheme. */ if (!logging_installed) use_default_logging(); if (!options_installed && !default_options_installed) { use_default_options(); ns_warning(ns_log_config, "re-establishing default options"); } update_pid_file(); /* Init or reinit the interface/port list and associated sockets. */ getnetconf(0); opensocket_f(); initial_configuration = 0; loading = 0; + /* release queued notifies */ + notify_afterload(); } Index: head/contrib/bind/bin/named/ns_ctl.c =================================================================== --- head/contrib/bind/bin/named/ns_ctl.c (revision 60940) +++ head/contrib/bind/bin/named/ns_ctl.c (revision 60941) @@ -1,866 +1,937 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_ctl.c,v 8.28 1999/10/13 16:39:04 vixie Exp $"; +static const char rcsid[] = "$Id: ns_ctl.c,v 8.34 2000/04/21 06:54:05 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1997-1999 by Internet Software Consortium. + * Copyright (c) 1997-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Extern. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* Defs. */ #define CONTROL_FOUND 0x0001 /* for mark and sweep. */ #define MAX_STR_LEN 500 struct control { LINK(struct control) link; enum { t_dead, t_inet, t_unix } type; struct ctl_sctx *sctx; u_int flags; union { struct { struct sockaddr_in in; ip_match_list allow; } v_inet; +#ifndef NO_SOCKADDR_UN struct { struct sockaddr_un un; mode_t mode; uid_t owner; gid_t group; } v_unix; +#endif } var; }; /* Forward. */ static struct ctl_sctx *mksrvr(control, const struct sockaddr *, size_t); static control new_control(void); static void free_control(controls *, control); static void free_controls(controls *); static int match_control(control, control); static control find_control(controls, control); static void propagate_changes(const control, control); static void install(control); static void install_inet(control); static void install_unix(control); static void logger(enum ctl_severity, const char *fmt, ...); static void verb_connect(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_getpid(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void getpid_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_status(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void status_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_stop(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_exec(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_reload(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_reconfig(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_dumpdb(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_stats(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_trace(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void trace_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_notrace(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_querylog(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_help(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void verb_quit(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); /* Private data. */ static controls server_controls; static struct ctl_verb verbs[] = { { "", verb_connect, ""}, { "getpid", verb_getpid, "getpid"}, { "status", verb_status, "status"}, { "stop", verb_stop, "stop"}, { "exec", verb_exec, "exec"}, { "reload", verb_reload, "reload [zone] ..."}, - { "reconfig", verb_reconfig, "reconfig (just sees new/gone zones)"}, + { "reconfig", verb_reconfig, "reconfig [-noexpired] (just sees new/gone zones)"}, { "dumpdb", verb_dumpdb, "dumpdb"}, { "stats", verb_stats, "stats"}, { "trace", verb_trace, "trace [level]"}, { "notrace", verb_notrace, "notrace"}, { "querylog", verb_querylog, "querylog"}, { "qrylog", verb_querylog, "qrylog"}, { "help", verb_help, "help"}, { "quit", verb_quit, "quit"}, { NULL, NULL, NULL} }; /* Public functions. */ void ns_ctl_initialize(void) { INIT_LIST(server_controls); } void ns_ctl_shutdown(void) { if (!EMPTY(server_controls)) free_controls(&server_controls); } void ns_ctl_defaults(controls *list) { +#ifdef NO_SOCKADDR_UN + struct in_addr saddr; + ip_match_list iml; + ip_match_element ime; + + /* + * If the operating system does not support local domain sockets, + * connect with ndc on 127.0.0.1, port 101, and only allow + * connections from 127.0.0.1. + */ + saddr.s_addr = htonl (INADDR_LOOPBACK); + iml = new_ip_match_list(); + ime = new_ip_match_pattern(saddr, 32); + add_to_ip_match_list(iml, ime); + + ns_ctl_add(list, ns_ctl_new_inet(saddr, htons (101), iml)); +#else +#ifdef NEED_SECURE_DIRECTORY + ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0700, 0, 0)); +#else ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0600, 0, 0)); +#endif +#endif /*NO_SOCKADDR_UN*/ } void ns_ctl_add(controls *list, control new) { if (!find_control(*list, new)) APPEND(*list, new, link); } control ns_ctl_new_inet(struct in_addr saddr, u_int sport, ip_match_list allow) { control new = new_control(); INIT_LINK(new, link); new->type = t_inet; memset(&new->var.v_inet.in, 0, sizeof new->var.v_inet.in); new->var.v_inet.in.sin_family = AF_INET; new->var.v_inet.in.sin_addr = saddr; new->var.v_inet.in.sin_port = sport; new->var.v_inet.allow = allow; return (new); } +#ifndef NO_SOCKADDR_UN control ns_ctl_new_unix(char *path, mode_t mode, uid_t owner, gid_t group) { control new = new_control(); INIT_LINK(new, link); new->type = t_unix; memset(&new->var.v_unix.un, 0, sizeof new->var.v_unix.un); new->var.v_unix.un.sun_family = AF_UNIX; strncpy(new->var.v_unix.un.sun_path, path, sizeof new->var.v_unix.un.sun_path - 1); new->var.v_unix.mode = mode; new->var.v_unix.owner = owner; new->var.v_unix.group = group; return (new); } +#endif void ns_ctl_install(controls *new) { control ctl, old, next; /* Find all the controls which aren't new or deleted. */ for (ctl = HEAD(server_controls); ctl != NULL; ctl = NEXT(ctl, link)) ctl->flags &= ~CONTROL_FOUND; for (ctl = HEAD(*new); ctl != NULL; ctl = next) { next = NEXT(ctl, link); old = find_control(server_controls, ctl); if (old != NULL) { old->flags |= CONTROL_FOUND; propagate_changes(ctl, old); if (old->sctx == NULL) free_control(&server_controls, old); free_control(new, ctl); } } /* Destroy any old controls which weren't found. */ for (ctl = HEAD(server_controls); ctl != NULL; ctl = next) { next = NEXT(ctl, link); if ((ctl->flags & CONTROL_FOUND) == 0) free_control(&server_controls, ctl); } /* Add any new controls which were found. */ for (ctl = HEAD(*new); ctl != NULL; ctl = next) { next = NEXT(ctl, link); APPEND(server_controls, ctl, link); install(ctl); if (ctl->sctx == NULL) free_control(&server_controls, ctl); } } /* Private functions. */ static struct ctl_sctx * mksrvr(control ctl, const struct sockaddr *sa, size_t salen) { return (ctl_server(ev, sa, salen, verbs, 500, 222, 600, 5, 10, logger, ctl)); } static control new_control(void) { control new = memget(sizeof *new); if (new == NULL) panic("memget failed in new_control()", NULL); new->type = t_dead; new->sctx = NULL; return (new); } static void free_control(controls *list, control this) { int was_live = 0; struct stat sb; if (this->sctx != NULL) { ctl_endserver(this->sctx); this->sctx = NULL; was_live = 1; } switch (this->type) { case t_inet: if (this->var.v_inet.allow != NULL) { free_ip_match_list(this->var.v_inet.allow); this->var.v_inet.allow = NULL; } break; +#ifndef NO_SOCKADDR_UN case t_unix: /* XXX Race condition. */ if (was_live && stat(this->var.v_unix.un.sun_path, &sb) == 0 && (S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) { /* XXX Race condition. */ unlink(this->var.v_unix.un.sun_path); } break; +#endif default: panic("impossible type in free_control", NULL); /* NOTREACHED */ } UNLINK(*list, this, link); memput(this, sizeof *this); } static void free_controls(controls *list) { control ctl, next; for (ctl = HEAD(*list); ctl != NULL; ctl = next) { next = NEXT(ctl, link); free_control(list, ctl); } INIT_LIST(*list); } static int match_control(control l, control r) { int match = 1; if (l->type != r->type) match = 0; else switch (l->type) { case t_inet: if (l->var.v_inet.in.sin_family != r->var.v_inet.in.sin_family || l->var.v_inet.in.sin_port != r->var.v_inet.in.sin_port || l->var.v_inet.in.sin_addr.s_addr != r->var.v_inet.in.sin_addr.s_addr) match = 0; break; +#ifndef NO_SOCKADDR_UN case t_unix: if (l->var.v_unix.un.sun_family != r->var.v_unix.un.sun_family || strcmp(l->var.v_unix.un.sun_path, r->var.v_unix.un.sun_path) != 0) match = 0; break; +#endif default: panic("impossible type in match_control", NULL); /* NOTREACHED */ } ns_debug(ns_log_config, 20, "match_control(): %d", match); return (match); } static control find_control(controls list, control new) { control ctl; for (ctl = HEAD(list); ctl != NULL; ctl = NEXT(ctl, link)) if (match_control(ctl, new)) return (ctl); return (NULL); } static void propagate_changes(const control diff, control base) { int need_install = 0; switch (base->type) { case t_inet: if (base->var.v_inet.allow != NULL) free_ip_match_list(base->var.v_inet.allow); base->var.v_inet.allow = diff->var.v_inet.allow; diff->var.v_inet.allow = NULL; need_install++; break; +#ifndef NO_SOCKADDR_UN case t_unix: if (base->var.v_unix.mode != diff->var.v_unix.mode) { base->var.v_unix.mode = diff->var.v_unix.mode; need_install++; } if (base->var.v_unix.owner != diff->var.v_unix.owner) { base->var.v_unix.owner = diff->var.v_unix.owner; need_install++; } if (base->var.v_unix.group != diff->var.v_unix.group) { base->var.v_unix.group = diff->var.v_unix.group; need_install++; } break; +#endif default: panic("impossible type in ns_ctl::propagate_changes", NULL); /* NOTREACHED */ } if (need_install) install(base); } static void install(control ctl) { switch (ctl->type) { case t_inet: install_inet(ctl); break; +#ifndef NO_SOCKADDR_UN case t_unix: install_unix(ctl); break; +#endif default: panic("impossible type in ns_ctl::install", NULL); /* NOTREACHED */ } } static void install_inet(control ctl) { if (ctl->sctx == NULL) { ctl->sctx = mksrvr(ctl, (struct sockaddr *)&ctl->var.v_inet.in, sizeof ctl->var.v_inet.in); } } +#ifndef NO_SOCKADDR_UN /* * Unattach an old unix domain socket if it exists. */ static void unattach(control ctl) { int s; struct stat sb; s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { ns_warning(ns_log_config, "unix control \"%s\" socket failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); return; } if (stat(ctl->var.v_unix.un.sun_path, &sb) < 0) { switch (errno) { case ENOENT: /* We exited cleanly last time */ break; default: ns_warning(ns_log_config, "unix control \"%s\" stat failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); break; } goto cleanup; } if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) { ns_warning(ns_log_config, "unix control \"%s\" not socket", ctl->var.v_unix.un.sun_path); goto cleanup; } if (connect(s, (struct sockaddr *)&ctl->var.v_unix.un, sizeof ctl->var.v_unix.un) < 0) { switch (errno) { case ECONNREFUSED: case ECONNRESET: if (unlink(ctl->var.v_unix.un.sun_path) < 0) ns_warning(ns_log_config, "unix control \"%s\" unlink failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); break; default: ns_warning(ns_log_config, "unix control \"%s\" connect failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); break; } } cleanup: close(s); } static void install_unix(control ctl) { + char *path; +#ifdef NEED_SECURE_DIRECTORY + char *slash; + + path = savestr(ctl->var.v_unix.un.sun_path, 1); + + slash = strrchr(path, '/'); + if (slash != NULL) { + if (slash != path) + *slash = '\0'; + else { + freestr(path); + path = savestr("/", 1); + } + } else { + freestr(path); + path = savestr(".", 1); + } + if (mkdir(path, ctl->var.v_unix.mode) < 0) { + if (errno != EEXIST) { + ns_warning(ns_log_config, + "unix control \"%s\" mkdir failed: %s", + path, strerror(errno)); + } + } +#else + path = ctl->var.v_unix.un.sun_path; +#endif + if (ctl->sctx == NULL) { unattach(ctl); ctl->sctx = mksrvr(ctl, (struct sockaddr *)&ctl->var.v_unix.un, sizeof ctl->var.v_unix.un); } if (ctl->sctx != NULL) { /* XXX Race condition. */ - if (chmod(ctl->var.v_unix.un.sun_path, - ctl->var.v_unix.mode) < 0) { + if (chmod(path, ctl->var.v_unix.mode) < 0) { ns_warning(ns_log_config, "chmod(\"%s\", 0%03o): %s", ctl->var.v_unix.un.sun_path, ctl->var.v_unix.mode, strerror(errno)); } - if (chown(ctl->var.v_unix.un.sun_path, - ctl->var.v_unix.owner, + if (chown(path, ctl->var.v_unix.owner, ctl->var.v_unix.group) < 0) { ns_warning(ns_log_config, "chown(\"%s\", %d, %d): %s", ctl->var.v_unix.un.sun_path, ctl->var.v_unix.owner, ctl->var.v_unix.group, strerror(errno)); } } +#ifdef NEED_SECURE_DIRECTORY + freestr(path); +#endif } +#endif static void logger(enum ctl_severity ctlsev, const char *format, ...) { va_list args; int logsev; switch (ctlsev) { case ctl_debug: logsev = log_debug(5); break; case ctl_warning: logsev = log_warning; break; case ctl_error: logsev = log_error; break; default: panic("invalid ctlsev in logger", NULL); } if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, ns_log_control, logsev, format, args); va_end(args); } static void verb_connect(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { const struct sockaddr *sa = (struct sockaddr *)respctx; control nsctl = (control)uctx; if (sa->sa_family == AF_INET) { const struct sockaddr_in *in = (struct sockaddr_in *)sa; const ip_match_list acl = nsctl->var.v_inet.allow; if (!ip_address_allowed(acl, in->sin_addr)) { ctl_response(sess, 502, "Permission denied.", CTL_EXIT, NULL, NULL, NULL, NULL, 0); return; } } ctl_response(sess, 220, server_options->version, 0, NULL, NULL, NULL, NULL, 0); } static void verb_getpid(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { char *msg = memget(MAX_STR_LEN); if (msg == NULL) { ctl_response(sess, 503, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } sprintf(msg, "my pid is <%ld>", (long)getpid()); ctl_response(sess, 250, msg, 0, NULL, getpid_closure, msg, NULL, 0); } static void getpid_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { char *msg = uap; memput(msg, MAX_STR_LEN); } enum state { e_version = 0, e_nzones, e_debug, e_xfersrun, e_xfersdfr, e_qserials, e_qrylog, e_priming, e_loading, e_finito }; struct pvt_status { enum state state; char text[MAX_STR_LEN]; }; static void verb_status(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct pvt_status *pvt = ctl_getcsctx(sess); if (pvt == NULL) { pvt = memget(sizeof *pvt); if (pvt == NULL) { ctl_response(sess, 505, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } pvt->state = e_version; (void)ctl_setcsctx(sess, pvt); } switch (pvt->state++) { case e_version: strncpy(pvt->text, Version, sizeof pvt->text); pvt->text[sizeof pvt->text - 1] = '\0'; break; case e_nzones: sprintf(pvt->text, "number of zones allocated: %d", nzones); break; case e_debug: sprintf(pvt->text, "debug level: %d", debug); break; case e_xfersrun: sprintf(pvt->text, "xfers running: %d", xfers_running); break; case e_xfersdfr: sprintf(pvt->text, "xfers deferred: %d", xfers_deferred); break; case e_qserials: sprintf(pvt->text, "soa queries in progress: %d", qserials_running); break; case e_qrylog: sprintf(pvt->text, "query logging is %s", qrylog ? "ON" : "OFF"); break; case e_priming: sprintf(pvt->text, "server is %s priming", priming ? "STILL" : "DONE"); break; case e_loading: sprintf(pvt->text, "server %s loading its configuration", loading ? "IS" : "IS NOT"); break; case e_finito: return; } ctl_response(sess, 250, pvt->text, (pvt->state == e_finito) ? 0 : CTL_MORE, NULL, status_closure, NULL, NULL, 0); } static void status_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { struct pvt_status *pvt = ctl_getcsctx(sess); memput(pvt, sizeof *pvt); ctl_setcsctx(sess, NULL); } static void verb_stop(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { ns_need(main_need_exit); ctl_response(sess, 250, "Shutdown initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_exec(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { struct stat sb; if (rest != NULL && *rest != '\0') { if (stat(rest, &sb) < 0) { ctl_response(sess, 503, strerror(errno), 0, NULL, NULL, NULL, NULL, 0); return; } saved_argv[0] = savestr(rest, 1); /* Never strfreed. */ } if (stat(saved_argv[0], &sb) < 0) { const char *save = strerror(errno); ns_warning(ns_log_default, "can't exec, %s: %s", saved_argv[0], save); ctl_response(sess, 502, save, 0, NULL, NULL, NULL, NULL, 0); } else { ns_need(main_need_restart); ctl_response(sess, 250, "Restart initiated.", 0, NULL, NULL, NULL, NULL, 0); } } static void verb_reload(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { static const char spaces[] = " \t"; struct zoneinfo *zp; char *tmp = NULL, *x; const char *msg; int class, code, success; /* If there are no args, this is a classic reload of the config. */ if (rest == NULL || *rest == '\0') { ns_need(main_need_reload); code = 250; msg = "Reload initiated."; goto respond; } /* Look for optional zclass argument. Default is "in". */ tmp = savestr(rest, 1); x = tmp + strcspn(tmp, spaces); if (*x != '\0') { *x++ = '\0'; x += strspn(x, spaces); } if (x == NULL || *x == '\0') x = "in"; class = sym_ston(__p_class_syms, x, &success); if (!success) { code = 507; msg = "unrecognized class"; goto respond; } /* Look for the zone, and do the right thing to it. */ zp = find_zone(tmp, class); if (zp == NULL) { code = 506; msg = "Zone not found."; goto respond; } switch (zp->z_type) { case z_master: ns_stopxfrs(zp); /*FALLTHROUGH*/ case z_hint: block_signals(); code = 251; msg = deferred_reload_unsafe(zp); unblock_signals(); break; case z_slave: case z_stub: ns_stopxfrs(zp); + if (zonefile_changed_p(zp)) + zp->z_serial = 0; /* force xfer */ addxfer(zp); code = 251; msg = "Slave transfer queued."; goto respond; case z_forward: case z_cache: default: msg = "Non reloadable zone."; code = 507; break; } respond: ctl_response(sess, code, msg, 0, NULL, NULL, NULL, NULL, 0); if (tmp != NULL) freestr(tmp); } static void verb_reconfig(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { - ns_need(main_need_reconfig); + if (strcmp(rest, "-noexpired") != 0) + ns_need(main_need_reconfig); + else + ns_need(main_need_noexpired); ctl_response(sess, 250, "Reconfig initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_dumpdb(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { ns_need(main_need_dump); ctl_response(sess, 250, "Database dump initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_stats(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { ns_need(main_need_statsdump); ctl_response(sess, 250, "Statistics dump initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_trace(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { int i = atoi(rest); char *msg = memget(MAX_STR_LEN); if (msg == NULL) { ctl_response(sess, 503, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } if (i > 0) desired_debug = i; else desired_debug++; ns_need(main_need_debug); sprintf(msg, "Debug level: %d", desired_debug); ctl_response(sess, 250, msg, 0, NULL, trace_closure, msg, NULL, 0); } static void trace_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { char *msg = uap; memput(msg, MAX_STR_LEN); } static void verb_notrace(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { desired_debug = 0; ns_need(main_need_debug); ctl_response(sess, 250, "Debugging turned off.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_querylog(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { static const char on[] = "Query logging is now on.", off[] = "Query logging is now off."; toggle_qrylog(); ctl_response(sess, 250, qrylog ? on : off, 0, NULL, NULL, NULL, NULL, 0); } static void verb_help(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { ctl_sendhelp(sess, 214); } static void verb_quit(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, void *respctx, void *uctx) { ctl_response(sess, 221, "End of control session.", CTL_EXIT, NULL, NULL, NULL, NULL, 0); } Index: head/contrib/bind/bin/named/ns_defs.h =================================================================== --- head/contrib/bind/bin/named/ns_defs.h (revision 60940) +++ head/contrib/bind/bin/named/ns_defs.h (revision 60941) @@ -1,878 +1,901 @@ /* * from ns.h 4.33 (Berkeley) 8/23/90 - * $Id: ns_defs.h,v 8.89 1999/10/07 08:24:08 vixie Exp $ + * $Id: ns_defs.h,v 8.96 2000/04/21 06:54:06 vixie Exp $ */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Global definitions for the name server. */ /* * Effort has been expended here to make all structure members 32 bits or * larger land on 32-bit boundaries; smaller structure members have been * deliberately shuffled and smaller integer sizes chosen where possible * to make sure this happens. This is all meant to avoid structure member * padding which can cost a _lot_ of memory when you have hundreds of * thousands of entries in your cache. */ /* * Timeout time should be around 1 minute or so. Using the * the current simplistic backoff strategy, the sequence * retrys after 4, 8, and 16 seconds. With 3 servers, this * dies out in a little more than a minute. * (sequence RETRYBASE, 2*RETRYBASE, 4*RETRYBASE... for MAXRETRY) */ #define NEWZONES 64 /* must be a power of two. */ #define MINROOTS 2 /* min number of root hints */ #define NSMAX 16 /* max number of NS addrs to try ([0..255]) */ #define RETRYBASE 4 /* base time between retries */ #define MAXCLASS 255 /* XXX - may belong elsewhere */ #define MAXRETRY 3 /* max number of retries per addr */ #define MAXCNAMES 8 /* max # of CNAMES tried per addr */ #define MAXQUERIES 20 /* max # of queries to be made */ #define MAXQSERIAL 4 /* max # of outstanding QSERIAL's */ /* (prevent "recursive" loops) */ #define INIT_REFRESH 600 /* retry time for initial secondary */ /* contact (10 minutes) */ #define MIN_REFRESH 2 /* never refresh more frequently than once */ /* every MIN_REFRESH seconds */ #define MIN_RETRY 1 /* never retry more frequently than once */ /* every MIN_RETRY seconds */ #define MAX_REFRESH 2419200 /* perform a refresh query at least */ /* every 4 weeks*/ #define MAX_RETRY 1209600 /* perform a retry after no more than 2 weeks */ #define MAX_EXPIRE 31536000 /* expire a zone if we have not talked to */ /* the primary in 1 year */ #define NADDRECS 20 /* max addt'l rr's per resp */ #define XFER_TIMER 120 /* named-xfer's connect timeout */ #define MAX_XFER_TIME 60*60*2 /* default max seconds for an xfer */ #define XFER_TIME_FUDGE 10 /* MAX_XFER_TIME fudge */ #define MAX_XFERS_RUNNING 20 /* max value of transfers_in */ #define DEFAULT_XFERS_RUNNING 10 /* default value of transfers_in */ #define DEFAULT_XFERS_PER_NS 2 /* default # of xfers per peer nameserver */ #define XFER_BUFSIZE (16*1024) /* arbitrary but bigger than most MTU's */ /* maximum time to cache negative answers */ #define DEFAULT_MAX_NCACHE_TTL (3*60*60) #define ALPHA 0.7 /* How much to preserve of old response time */ #define BETA 1.2 /* How much to penalize response time on failure */ #define GAMMA 0.98 /* How much to decay unused response times */ /* What maintainance operations need to be performed sometime soon? */ typedef enum need { main_need_zreload = 0, /* ns_zreload() needed. */ main_need_reload, /* ns_reload() needed. */ main_need_reconfig, /* ns_reconfig() needed. */ main_need_endxfer, /* endxfer() needed. */ main_need_zoneload, /* loadxfer() needed. */ main_need_dump, /* doadump() needed. */ main_need_statsdump, /* ns_stats() needed. */ main_need_exit, /* exit() needed. */ main_need_qrylog, /* toggle_qrylog() needed. */ main_need_debug, /* use_desired_debug() needed. */ main_need_restart, /* exec() needed. */ main_need_reap, /* need to reap dead children */ - main_need_num /* number of needs, used for array bound. */ + main_need_noexpired, /* ns_reconfig() needed w/ noexpired set */ + main_need_num, /* number of needs, used for array bound. */ + main_need_tick /* tick every second to poll for cleanup (NT)*/ } main_need; /* What global options are set? */ #define OPTION_NORECURSE 0x0001 /* Don't recurse even if asked. */ #define OPTION_NOFETCHGLUE 0x0002 /* Don't fetch missing glue. */ #define OPTION_FORWARD_ONLY 0x0004 /* Don't use NS RR's, just forward. */ #define OPTION_FAKE_IQUERY 0x0008 /* Fake up bogus response to IQUERY. */ #ifdef BIND_NOTIFY #define OPTION_NONOTIFY 0x0010 /* Turn off notify */ #endif #define OPTION_NONAUTH_NXDOMAIN 0x0020 /* Generate non-auth NXDOMAINs? */ #define OPTION_MULTIPLE_CNAMES 0x0040 /* Allow a name to have multiple * CNAME RRs */ #define OPTION_HOSTSTATS 0x0080 /* Maintain per-host statistics? */ #define OPTION_DEALLOC_ON_EXIT 0x0100 /* Deallocate everything on exit? */ -#define OPTION_USE_IXFR 0x0110 /* Use by delault ixfr in zone transfer */ -#define OPTION_MAINTAIN_IXFR_BASE 0x0120 #define OPTION_NODIALUP 0x0200 /* Turn off dialup support */ #define OPTION_NORFC2308_TYPE1 0x0400 /* Prevent type1 respones (RFC 2308) * to cached negative respones */ #define OPTION_USE_ID_POOL 0x0800 /* Use the memory hogging query ID */ #define OPTION_TREAT_CR_AS_SPACE 0x1000 /* Treat CR in zone files as space */ +#define OPTION_USE_IXFR 0x2000 /* Use by delault ixfr in zone transfer */ +#define OPTION_MAINTAIN_IXFR_BASE 0x4000 /* Part of IXFR file name logic. */ #define DEFAULT_OPTION_FLAGS (OPTION_NODIALUP|OPTION_NONAUTH_NXDOMAIN|\ OPTION_USE_ID_POOL|OPTION_NORFC2308_TYPE1) #ifdef BIND_UPDATE #define SOAINCRINTVL 300 /* default value for the time after which * the zone serial number must be incremented * after a successful update has occurred */ #define DUMPINTVL 3600 /* default interval at which to dump changed zones * randomized, not exact */ #define DEFERUPDCNT 100 /* default number of updates that can happen * before the zone serial number will be * incremented */ #define UPDATE_TIMER XFER_TIMER #endif /* BIND_UPDATE */ #define USE_MINIMUM 0xffffffff #define MAXIMUM_TTL 0x7fffffff #define CLEAN_TIMER 0x01 #define INTERFACE_TIMER 0x02 #define STATS_TIMER 0x04 #define HEARTBEAT_TIMER 0x08 /* IP address accessor, network byte order. */ #define ina_ulong(ina) (ina.s_addr) /* IP address accessor, host byte order, read only. */ #define ina_hlong(ina) ntohl(ina.s_addr) /* IP address equality. */ /* XXX: assumes that network byte order won't affect equality. */ #define ina_equal(a, b) (ina_ulong(a) == ina_ulong(b)) /* IP address equality with a mask. */ #define ina_onnet(h, n, m) ((ina_ulong(h) & ina_ulong(m)) == ina_ulong(n)) /* Sequence space arithmetic. */ #define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) #define NS_OPTION_P(option) ((server_options == NULL) ? \ (panic(panic_msg_no_options, NULL), 0) : \ ((server_options->flags & option) != 0)) #define NS_ZOPTION_P(zp, option) \ (((zp) != NULL && (((zp)->z_optset & option) != 0)) ? \ (((zp)->z_options & option) != 0) : NS_OPTION_P(option)) #define NS_ZFWDTAB(zp) (((zp) == NULL) ? \ server_options->fwdtab : (zp)->z_fwdtab) #define NS_INCRSTAT(addr, which) \ do { \ if ((int)which >= (int)nssLast) \ ns_panic(ns_log_insist, 1, panic_msg_bad_which, \ __FILE__, __LINE__, #which); \ else { \ if (NS_OPTION_P(OPTION_HOSTSTATS)) { \ struct nameser *ns = \ nameserFind(addr, NS_F_INSERT); \ if (ns != NULL) \ ns->stats[(int)which]++; \ } \ globalStats[(int)which]++; \ } \ } while (0) enum severity { ignore, warn, fail, not_set }; #ifdef BIND_NOTIFY enum znotify { znotify_use_default=0, znotify_yes, znotify_no }; #endif enum zdialup { zdialup_use_default=0, zdialup_yes, zdialup_no }; enum axfr_format { axfr_use_default=0, axfr_one_answer, axfr_many_answers }; struct ip_match_direct { struct in_addr address; struct in_addr mask; }; struct ip_match_indirect { struct ip_match_list *list; }; struct ip_match_key { struct dst_key *key; }; typedef enum { ip_match_pattern, ip_match_indirect, ip_match_localhost, ip_match_localnets, ip_match_key } ip_match_type; typedef struct ip_match_element { ip_match_type type; u_int flags; union { struct ip_match_direct direct; struct ip_match_indirect indirect; struct ip_match_key key; } u; struct ip_match_element *next; } *ip_match_element; /* Flags for ip_match_element */ #define IP_MATCH_NEGATE 0x01 /* match means deny access */ typedef struct ip_match_list { ip_match_element first; ip_match_element last; } *ip_match_list; typedef struct ztimer_info { char *name; int class; int type; } *ztimer_info; -/* these fields are ordered to maintain word-alignment; +/* + * These fields are ordered to maintain word-alignment; * be careful about changing them. */ struct zoneinfo { char *z_origin; /* root domain name of zone */ time_t z_time; /* time for next refresh */ time_t z_lastupdate; /* time of last soa serial increment */ u_int32_t z_refresh; /* refresh interval */ u_int32_t z_retry; /* refresh retry interval */ u_int32_t z_expire; /* expiration time for cached info */ u_int32_t z_minimum; /* minimum TTL value */ u_int32_t z_serial; /* changes if zone modified */ char *z_source; /* source location of data */ time_t z_ftime; /* modification time of source file */ struct in_addr z_axfr_src; /* bind() the axfr socket to this */ struct in_addr z_addr[NSMAX]; /* list of master servers for zone */ u_char z_addrcnt; /* number of entries in z_addr[] */ struct in_addr z_xaddr[NSMAX]; /* list of master servers for xfer */ u_char z_xaddrcnt; /* number of entries in z_xaddr[] */ u_char z_type; /* type of zone; see below */ u_int32_t z_flags; /* state bits; see below */ pid_t z_xferpid; /* xfer child pid */ u_int z_options; /* options set specific to this zone */ u_int z_optset; /* which opts override global opts */ int z_class; /* class of zone */ int z_numxfrs; /* Ref count of concurrent xfrs. */ enum severity z_checknames; /* How to handle non-RFC-compliant names */ #ifdef BIND_UPDATE time_t z_dumptime; /* randomized time for next zone dump * if Z_NEED_DUMP is set */ u_int32_t z_dumpintvl; /* time interval between zone dumps */ time_t z_soaincrintvl; /* interval for updating soa serial */ time_t z_soaincrtime; /* time for soa increment */ u_int32_t z_deferupdcnt; /* max number of updates before SOA * serial number incremented */ u_int32_t z_updatecnt; /* number of update requests processed * since the last SOA serial update */ char *z_updatelog; /* log file for updates */ #endif ip_match_list z_update_acl; /* list of who can issue dynamic updates */ ip_match_list z_query_acl; /* sites we'll answer questions for */ ip_match_list z_transfer_acl; /* sites that may get a zone transfer from us */ long z_max_transfer_time_in; /* max num seconds for AXFR */ #ifdef BIND_NOTIFY enum znotify z_notify; /* Notify mode */ struct in_addr *z_also_notify; /* More nameservers to notify */ int z_notify_count; #endif enum zdialup z_dialup; /* secondaries over a dialup link */ char *z_ixfr_base; /* where to find the history of the zone */ char *z_ixfr_tmp; /* tmp file for the ixfr */ - int z_maintain_ixfr_base; - int z_log_size_ixfr; - int z_max_log_size_ixfr; + int z_maintain_ixfr_base; + long z_max_log_size_ixfr; + u_int32_t z_serial_ixfr_start; evTimerID z_timer; /* maintenance timer */ ztimer_info z_timerinfo; /* UAP associated with timer */ time_t z_nextmaint; /* time of next maintenance */ u_int16_t z_port; /* perform AXFR to this port */ struct fwdinfo *z_fwdtab; /* zone-specific forwarders */ LINK(struct zoneinfo) z_freelink; /* if it's on the free list. */ LINK(struct zoneinfo) z_reloadlink; /* if it's on the reload list. */ }; /* zone types (z_type) */ enum zonetype { z_nil, z_master, z_slave, z_hint, z_stub, z_forward, z_cache, z_any }; #define Z_NIL z_nil /* XXX */ #define Z_MASTER z_master /* XXX */ #define Z_PRIMARY z_master /* XXX */ #define Z_SLAVE z_slave /* XXX */ #define Z_SECONDARY z_slave /* XXX */ #define Z_HINT z_hint /* XXX */ #define Z_CACHE z_cache /* XXX */ #define Z_STUB z_stub /* XXX */ #define Z_FORWARD z_forward /* XXX */ #define Z_ANY z_any /* XXX*2 */ /* zone state bits (32 bits) */ #define Z_AUTH 0x00000001 /* zone is authoritative */ #define Z_NEED_XFER 0x00000002 /* waiting to do xfer */ #define Z_XFER_RUNNING 0x00000004 /* asynch. xfer is running */ #define Z_NEED_RELOAD 0x00000008 /* waiting to do reload */ #define Z_SYSLOGGED 0x00000010 /* have logged timeout */ #define Z_QSERIAL 0x00000020 /* sysquery()'ing for serial number */ #define Z_FOUND 0x00000040 /* found in boot file when reloading */ #define Z_INCLUDE 0x00000080 /* set if include used in file */ #define Z_DB_BAD 0x00000100 /* errors when loading file */ #define Z_TMP_FILE 0x00000200 /* backup file for xfer is temporary */ #ifdef BIND_UPDATE #define Z_DYNAMIC 0x00000400 /* allow dynamic updates */ #define Z_NEED_DUMP 0x00000800 /* zone has changed, needs a dump */ #define Z_NEED_SOAUPDATE 0x00001000 /* soa serial number needs increment */ #endif /* BIND_UPDATE */ #define Z_XFER_ABORTED 0x00002000 /* zone transfer has been aborted */ #define Z_XFER_GONE 0x00004000 /* zone transfer process is gone */ #define Z_TIMER_SET 0x00008000 /* z_timer contains a valid id */ #ifdef BIND_NOTIFY #define Z_NOTIFY 0x00010000 /* has an outbound notify executing */ #endif #define Z_NEED_QSERIAL 0x00020000 /* we need to re-call qserial() */ #define Z_PARENT_RELOAD 0x00040000 /* we need to reload this as parent */ #define Z_FORWARD_SET 0x00080000 /* has forwarders been set */ +#define Z_EXPIRED 0x00100000 /* expire timer has gone off */ /* named_xfer exit codes */ #define XFER_UPTODATE 0 /* zone is up-to-date */ #define XFER_SUCCESS 1 /* performed transfer successfully */ #define XFER_TIMEOUT 2 /* no server reachable/xfer timeout */ #define XFER_FAIL 3 /* other failure, has been logged */ #define XFER_SUCCESSAXFR 4 /* named-xfr recived a xfr */ #define XFER_SUCCESSIXFR 5 /* named-xfr recived a ixfr */ #define XFER_SUCCESSAXFRIXFRFILE 6 /* named-xfr received AXFR for IXFR */ #define XFER_ISAXFR -1 /* the last XFR is AXFR */ #define XFER_ISIXFR -2 /* the last XFR is IXFR */ #define XFER_ISAXFRIXFR -3 /* the last XFR is AXFR but we must create IXFR base */ struct qserv { struct sockaddr_in ns_addr; /* address of NS */ struct databuf *ns; /* databuf for NS record */ struct databuf *nsdata; /* databuf for server address */ struct timeval stime; /* time first query started */ unsigned int forwarder:1; /* this entry is for a forwarder */ unsigned int nretry:31; /* # of times addr retried */ u_int32_t serial; /* valid if Q_ZSERIAL */ }; /* * Structure for recording info on forwarded or generated queries. */ struct qinfo { u_int16_t q_id; /* id of query */ u_int16_t q_nsid; /* id of forwarded query */ struct sockaddr_in q_from; /* requestor's address */ u_char *q_msg, /* the message */ *q_cmsg; /* the cname message */ int16_t q_msglen, /* len of message */ q_msgsize, /* allocated size of message */ q_cmsglen, /* len of cname message */ q_cmsgsize; /* allocated size of cname message */ int16_t q_dfd; /* UDP file descriptor */ time_t q_time; /* time to retry */ time_t q_expire; /* time to expire */ struct qinfo *q_next; /* rexmit list (sorted by time) */ struct qinfo *q_link; /* storage list (random order) */ struct databuf *q_usedns[NSMAX]; /* databuf for NS that we've tried */ struct qserv q_addr[NSMAX]; /* addresses of NS's */ #ifdef notyet struct nameser *q_ns[NSMAX]; /* name servers */ #endif u_char q_naddr; /* number of addr's in q_addr */ u_char q_curaddr; /* last addr sent to */ u_char q_nusedns; /* number of elements in q_usedns[] */ u_int8_t q_flags; /* see below */ int16_t q_cname; /* # of cnames found */ int16_t q_nqueries; /* # of queries required */ struct qstream *q_stream; /* TCP stream, null if UDP */ struct zoneinfo *q_zquery; /* Zone query is about (Q_ZSERIAL) */ struct zoneinfo *q_fzone; /* Forwarding zone, if any */ char *q_domain; /* domain of most enclosing zone cut */ char *q_name; /* domain of query */ u_int16_t q_class; /* class of query */ u_int16_t q_type; /* type of query */ #ifdef BIND_NOTIFY int q_notifyzone; /* zone which needs another znotify() * when the reply to this comes in. */ #endif struct tsig_record *q_tsig; /* forwarded query's TSIG record */ struct tsig_record *q_nstsig; /* forwarded query's TSIG record */ }; /* q_flags bits (8 bits) */ #define Q_SYSTEM 0x01 /* is a system query */ #define Q_PRIMING 0x02 /* generated during priming phase */ #define Q_ZSERIAL 0x04 /* getting zone serial for xfer test */ #define Q_USEVC 0x08 /* forward using tcp not udp */ #define Q_NEXTADDR(qp,n) (&(qp)->q_addr[n].ns_addr) #define RETRY_TIMEOUT 45 /* * Return codes from ns_forw: */ #define FW_OK 0 #define FW_DUP 1 #define FW_NOSERVER 2 #define FW_SERVFAIL 3 typedef void (*sq_closure)(struct qstream *qs); #ifdef BIND_UPDATE struct fdlist { int fd; struct fdlist *next; }; #endif + +typedef struct ns_delta { + LINK(struct ns_delta) d_link; + ns_updque d_changes; +} ns_delta; + +typedef LIST(ns_delta) ns_deltalist; + typedef struct _interface { int dfd, /* Datagram file descriptor */ sfd; /* Stream file descriptor. */ time_t gen; /* Generation number. */ struct in_addr addr; /* Interface address. */ u_int16_t port; /* Interface port. */ u_int16_t flags; /* Valid bits for evXXXXID. */ evFileID evID_d; /* Datagram read-event. */ evConnID evID_s; /* Stream listen-event. */ LINK(struct _interface) link; } interface; #define INTERFACE_FILE_VALID 0x01 #define INTERFACE_CONN_VALID 0x02 #define INTERFACE_FORWARDING 0x04 struct qstream { int s_rfd; /* stream file descriptor */ int s_size; /* expected amount of data to rcv */ int s_bufsize; /* amount of data received in s_buf */ u_char *s_buf; /* buffer of received data */ u_char *s_wbuf; /* send buffer */ u_char *s_wbuf_send; /* next sendable byte of send buffer */ u_char *s_wbuf_free; /* next free byte of send buffer */ u_char *s_wbuf_end; /* byte after end of send buffer */ sq_closure s_wbuf_closure; /* callback for writable descriptor */ struct qstream *s_next; /* next stream */ struct sockaddr_in s_from; /* address query came from */ interface *s_ifp; /* interface query came from */ time_t s_time; /* time stamp of last transaction */ int s_refcnt; /* number of outstanding queries */ u_char s_temp[HFIXEDSZ]; #ifdef BIND_UPDATE int s_opcode; /* type of request */ int s_linkcnt; /* number of client connections using * this connection to forward updates * to the primary */ struct fdlist *s_fds; /* linked list of connections to the * primaries that have been used by * the server to forward this client's * update requests */ #endif evStreamID evID_r; /* read event. */ evFileID evID_w; /* writable event handle. */ evConnID evID_c; /* connect event handle */ u_int flags; /* see below */ struct qstream_xfr { enum { s_x_base, s_x_firstsoa, s_x_zone, s_x_lastsoa, s_x_done, s_x_adding, s_x_deleting, s_x_addsoa, s_x_deletesoa } state; /* state of transfer. */ u_char *msg, /* current assembly message. */ *cp, /* where are we in msg? */ *eom, /* end of msg. */ *ptrs[128]; /* ptrs for dn_comp(). */ int class, /* class of an XFR. */ type, /* type of XFR. */ id, /* id of an XFR. */ opcode; /* opcode of an XFR. */ u_int zone; /* zone being XFR'd. */ union { struct namebuf *axfr; /* top np of an AXFR. */ - struct ns_updrec *ixfr; /* top udp of an IXFR. */ + ns_deltalist *ixfr; /* top udp of an IXFR. */ } top; int ixfr_zone; u_int32_t serial; /* serial number requested in IXFR */ ns_tcp_tsig_state *tsig_state; /* used by ns_sign_tcp */ int tsig_skip; /* skip calling ns_sign_tcp * during the next flush */ struct qs_x_lev { /* decompose the recursion. */ enum {sxl_ns, sxl_all, sxl_sub} state; /* what's this level doing? */ int flags; /* see below (SXL_*). */ char dname[MAXDNAME]; struct namebuf *np, /* this node. */ *nnp, /* next node to process. */ **npp, /* subs. */ **npe; /* end of subs. */ struct databuf *dp; /* current rr. */ struct qs_x_lev *next; /* link. */ } *lev; /* LIFO. */ enum axfr_format transfer_format; } xfr; }; #define SXL_GLUING 0x01 #define SXL_ZONECUT 0x02 /* flags */ #define STREAM_MALLOC 0x01 #define STREAM_WRITE_EV 0x02 #define STREAM_READ_EV 0x04 #define STREAM_CONNECT_EV 0x08 #define STREAM_DONE_CLOSE 0x10 #define STREAM_AXFR 0x20 -#define STREAM_AXFRIXFR 0x22 +#define STREAM_AXFRIXFR 0x40 #define ALLOW_NETS 0x0001 #define ALLOW_HOSTS 0x0002 #define ALLOW_ALL (ALLOW_NETS | ALLOW_HOSTS) +struct fwddata { + struct sockaddr_in + fwdaddr; /* address of NS */ + struct databuf *ns; /* databuf for NS record */ + struct databuf *nsdata; /* databuf for server address */ + int ref_count; /* how many users of this */ +}; + struct fwdinfo { struct fwdinfo *next; - struct sockaddr_in - fwdaddr; + struct fwddata *fwddata; }; enum nameserStats { nssRcvdR, /* sent us an answer */ nssRcvdNXD, /* sent us a negative response */ nssRcvdFwdR, /* sent us a response we had to fwd */ nssRcvdDupR, /* sent us an extra answer */ nssRcvdFail, /* sent us a SERVFAIL */ nssRcvdFErr, /* sent us a FORMERR */ nssRcvdErr, /* sent us some other error */ nssRcvdAXFR, /* sent us an AXFR */ nssRcvdLDel, /* sent us a lame delegation */ nssRcvdOpts, /* sent us some IP options */ nssSentSysQ, /* sent them a sysquery */ nssSentAns, /* sent them an answer */ nssSentFwdQ, /* fwdd a query to them */ nssSentDupQ, /* sent them a retry */ nssSendtoErr, /* error in sendto */ nssRcvdQ, /* sent us a query */ nssRcvdIQ, /* sent us an inverse query */ nssRcvdFwdQ, /* sent us a query we had to fwd */ nssRcvdDupQ, /* sent us a retry */ nssRcvdTCP, /* sent us a query using TCP */ nssSentFwdR, /* fwdd a response to them */ nssSentFail, /* sent them a SERVFAIL */ nssSentFErr, /* sent them a FORMERR */ nssSentNaAns, /* sent them a non autoritative answer */ nssSentNXD, /* sent them a negative response */ + nssRcvdUQ, /* sent us an unapproved query */ + nssRcvdURQ, /* sent us an unapproved recursive query */ + nssRcvdUXFR, /* sent us an unapproved AXFR or IXFR */ + nssRcvdUUpd, /* sent us an unapproved update */ nssLast }; struct nameser { struct in_addr addr; /* key */ u_long stats[nssLast]; /* statistics */ #ifdef notyet u_int32_t rtt; /* round trip time */ /* XXX - need to add more stuff from "struct qserv", and use our rtt */ u_int16_t flags; /* see below */ #endif u_int8_t xfers; /* #/xfers running right now */ }; enum transport { primary_trans, secondary_trans, response_trans, update_trans, num_trans }; /* types used by the parser or config routines */ typedef struct zone_config { void *opaque; } zone_config; typedef struct listen_info { u_short port; ip_match_list list; struct listen_info *next; } *listen_info; typedef struct listen_info_list { listen_info first; listen_info last; } *listen_info_list; #ifndef RLIMIT_TYPE #define RLIMIT_TYPE u_long #endif typedef RLIMIT_TYPE rlimit_type; struct control; typedef struct control *control; typedef LIST(struct control) controls; enum ordering { unknown_order, fixed_order, cyclic_order, random_order }; #define DEFAULT_ORDERING cyclic_order typedef struct rrset_order_element { int class; int type; char *name; enum ordering order; struct rrset_order_element *next; } *rrset_order_element ; typedef struct rrset_order_list { rrset_order_element first; rrset_order_element last; } *rrset_order_list; typedef struct options { u_int flags; char *version; char *directory; char *dump_filename; char *pid_filename; char *stats_filename; char *memstats_filename; char *named_xfer; int transfers_in; int transfers_per_ns; int transfers_out; int serial_queries; int max_log_size_ixfr; enum axfr_format transfer_format; long max_transfer_time_in; struct sockaddr_in query_source; struct in_addr axfr_src; #ifdef BIND_NOTIFY int notify_count; struct in_addr *also_notify; #endif ip_match_list query_acl; ip_match_list recursion_acl; ip_match_list transfer_acl; ip_match_list blackhole_acl; ip_match_list topology; ip_match_list sortlist; enum severity check_names[num_trans]; u_long data_size; u_long stack_size; u_long core_size; u_long files; listen_info_list listen_list; struct fwdinfo *fwdtab; /* XXX need to add forward option */ int clean_interval; int interface_interval; int stats_interval; rrset_order_list ordering; int heartbeat_interval; u_int max_ncache_ttl; u_int lame_ttl; int minroots; } *options; typedef struct key_list_element { struct dst_key *key; struct key_list_element *next; } *key_list_element; typedef struct key_info_list { key_list_element first; key_list_element last; } *key_info_list; typedef struct topology_config { void *opaque; } topology_config; #define UNKNOWN_TOPOLOGY_DISTANCE 9998 #define MAX_TOPOLOGY_DISTANCE 9999 typedef struct topology_distance { ip_match_list patterns; struct topology_distance *next; } *topology_distance; typedef struct topology_context { topology_distance first; topology_distance last; } *topology_context; typedef struct acl_table_entry { char *name; ip_match_list list; struct acl_table_entry *next; } *acl_table_entry; typedef struct server_config { void *opaque; } server_config; #define SERVER_INFO_BOGUS 0x01 #define SERVER_INFO_SUPPORT_IXFR 0x02 typedef struct server_info { struct in_addr address; u_int flags; int transfers; enum axfr_format transfer_format; key_info_list key_list; /* could move statistics to here, too */ struct server_info *next; } *server_info; /* * enum <--> name translation */ struct ns_sym { int number; /* Identifying number, like ns_log_default */ char * name; /* Its symbolic name, like "default" */ }; /* * Logging options */ typedef enum ns_logging_categories { ns_log_default = 0, ns_log_config, ns_log_parser, ns_log_queries, ns_log_lame_servers, ns_log_statistics, ns_log_panic, ns_log_update, ns_log_ncache, ns_log_xfer_in, ns_log_xfer_out, ns_log_db, ns_log_eventlib, ns_log_packet, #ifdef BIND_NOTIFY ns_log_notify, #endif ns_log_cname, ns_log_security, ns_log_os, ns_log_insist, ns_log_maint, ns_log_load, ns_log_resp_checks, ns_log_control, ns_log_max_category } ns_logging_categories; typedef struct log_config { log_context log_ctx; log_channel eventlib_channel; log_channel packet_channel; int default_debug_active; } *log_config; struct map { char * token; int val; }; #define NOERROR_NODATA 15 /* only used internally by the server, used for * -ve $ing non-existence of records. 15 is not * a code used as yet anyway. */ #define NTTL 600 /* ttl for negative data: 10 minutes? */ #define VQEXPIRY 900 /* a VQ entry expires in 15*60 = 900 seconds */ #ifdef BIND_UPDATE enum req_action { Finish, Refuse, Return }; #endif #ifdef INIT error "INIT already defined, check system include files" #endif #ifdef DECL error "DECL already defined, check system include files" #endif #ifdef MAIN_PROGRAM #define INIT(x) = x #define DECL #else #define INIT(x) #define DECL extern #endif Index: head/contrib/bind/bin/named/ns_forw.c =================================================================== --- head/contrib/bind/bin/named/ns_forw.c (revision 60940) +++ head/contrib/bind/bin/named/ns_forw.c (revision 60941) @@ -1,1269 +1,1272 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_forw.c 4.32 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: ns_forw.c,v 8.68 1999/10/13 16:39:07 vixie Exp $"; +static const char rcsid[] = "$Id: ns_forw.c,v 8.75 2000/05/09 07:12:58 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct complaint { u_long tag1, tag2; time_t expire; struct complaint *next; }; static struct complaint *complaints = NULL; static int retry_timer_set = 0; /* * Forward the query to get the answer since its not in the database. * Returns FW_OK if a request struct is allocated and the query sent. * Returns FW_DUP if this is a duplicate of a pending request. * Returns FW_NOSERVER if there were no addresses for the nameservers. * Returns FW_SERVFAIL on memory allocation error or if asked to do something * dangerous, such as fwd to ourselves or fwd to the host that asked us. * * (no action is taken on errors and qpp is not filled in.) */ int ns_forw(struct databuf *nsp[], u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp, int dfd, struct qinfo **qpp, const char *dname, int class, int type, struct namebuf *np, int use_tcp, struct tsig_record *in_tsig) { struct qinfo *qp; char tmpdomain[MAXDNAME]; struct sockaddr_in *nsa; HEADER *hp; u_int16_t id; int sendto_errno = 0; int n, has_tsig, oldqlen; u_char *oldqbuf; u_char *smsg; int smsglen, smsgsize, siglen; u_char sig[TSIG_SIG_SIZE]; DST_KEY *key; ns_debug(ns_log_default, 3, "ns_forw()"); hp = (HEADER *) msg; id = hp->id; /* Look at them all */ for (qp = nsqhead; qp != NULL; qp = qp->q_link) { if (qp->q_id == id && memcmp(&qp->q_from, &from, sizeof qp->q_from) == 0 && ((qp->q_cmsglen == 0 && qp->q_msglen == msglen && memcmp(qp->q_msg + 2, msg + 2, msglen - 2) == 0) || (qp->q_cmsglen == msglen && memcmp(qp->q_cmsg + 2, msg + 2, msglen - 2) == 0) )) { ns_debug(ns_log_default, 3, "forw: dropped DUP id=%d", ntohs(id)); nameserIncr(from.sin_addr, nssRcvdDupQ); return (FW_DUP); } } - qp = qnew(dname, class, type); + qp = qnew(dname, class, type, 1); getname(np, tmpdomain, sizeof tmpdomain); qp->q_domain = savestr(tmpdomain, 1); qp->q_from = from; /* nslookup wants to know this */ if (NS_ZFWDTAB(qp->q_fzone)) nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) n = 0; else n = nslookup(nsp, qp, dname, "ns_forw"); if (n < 0) { if (n == -1) ns_debug(ns_log_default, 2, "forw: nslookup reports danger"); ns_freeqry(qp); return (FW_SERVFAIL); } if (n == 0 && !NS_ZFWDTAB(qp->q_fzone)) { ns_debug(ns_log_default, 2, "forw: no nameservers found"); ns_freeqry(qp); return (FW_NOSERVER); } qp->q_stream = qsp; qp->q_curaddr = 0; qp->q_dfd = dfd; qp->q_id = id; qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; if (in_tsig != NULL) qp->q_tsig = new_tsig(in_tsig->key, in_tsig->sig, in_tsig->siglen); if (use_tcp) qp->q_flags |= Q_USEVC; hp->id = qp->q_nsid = htons(nsid_next()); hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); if ((qp->q_msg = (u_char *)memget((unsigned)msglen)) == NULL) { ns_notice(ns_log_default, "forw: memget: %s", strerror(errno)); ns_freeqry(qp); return (FW_SERVFAIL); } qp->q_msgsize = msglen; memcpy(qp->q_msg, msg, qp->q_msglen = msglen); hp = (HEADER *) qp->q_msg; hp->rd = (qp->q_addr[0].forwarder ? 1 : 0); qp->q_addr[0].stime = tt; -#ifdef SLAVE_FORWARD - if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) - schedretry(qp, (time_t)slave_retry); - else -#endif /* SLAVE_FORWARD */ schedretry(qp, retrytime(qp)); nsa = Q_NEXTADDR(qp, 0); ns_debug(ns_log_default, 1, "forw: forw -> [%s].%d ds=%d nsid=%d id=%d %dms retry %dsec", inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port), ds, ntohs(qp->q_nsid), ntohs(qp->q_id), (qp->q_addr[0].nsdata != NULL) ? qp->q_addr[0].nsdata->d_nstime : -1, (int)(qp->q_time - tt.tv_sec)); #ifdef DEBUG if (debug >= 10) res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); #endif key = tsig_key_from_addr(nsa->sin_addr); if (key != NULL) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE; smsg = memget(smsgsize); if (smsg == NULL) ns_panic(ns_log_default, 1, "ns_forw: memget failed"); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; hp = (HEADER *) qp->q_msg; has_tsig = 1; qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (qp->q_flags & Q_USEVC) { if (tcp_send(qp) != NOERROR) { if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)tcpsendStr)) ns_info(ns_log_default, "ns_forw: tcp_send(%s) failed: %s", sin_ntoa(*nsa), strerror(errno)); } } else if (sendto(ds, (char *)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "ns_forw: sendto(%s): %s", sin_ntoa(*nsa), strerror(errno)); nameserIncr(nsa->sin_addr, nssSendtoErr); } if (has_tsig == 1) { memput(qp->q_msg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; hp = (HEADER *) qp->q_msg; } - if (NS_OPTION_P(OPTION_HOSTSTATS)) - nameserIncr(from.sin_addr, nssRcvdFwdQ); + nameserIncr(from.sin_addr, nssRcvdFwdQ); nameserIncr(nsa->sin_addr, nssSentFwdQ); if (qpp) *qpp = qp; hp->rd = 1; switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: unsched(qp); schedretry(qp, (time_t) 0); } return (0); } /* haveComplained(tag1, tag2) * check to see if we have complained about (tag1,tag2) recently * returns: * boolean: have we complained recently? * side-effects: * outdated complaint records removed from our static list * author: * Paul Vixie (DECWRL) April 1991 */ int haveComplained(u_long tag1, u_long tag2) { struct complaint *cur, *next, *prev; int r = 0; for (cur = complaints, prev = NULL; cur != NULL; prev = cur, cur = next) { next = cur->next; if (tt.tv_sec > cur->expire) { if (prev) prev->next = next; else complaints = next; memput(cur, sizeof *cur); cur = prev; } else if (tag1 == cur->tag1 && tag2 == cur->tag2) r++; } if (!r) { cur = (struct complaint *)memget(sizeof(struct complaint)); if (cur) { cur->tag1 = tag1; cur->tag2 = tag2; cur->expire = tt.tv_sec + INIT_REFRESH; /* "10:00" */ cur->next = NULL; if (prev) prev->next = cur; else complaints = cur; } } return (r); } void freeComplaints(void) { struct complaint *cur, *next; for (cur = complaints; cur != NULL; cur = next) { next = cur->next; memput(cur, sizeof *cur); } complaints = NULL; } /* void * nslookupComplain(sysloginfo, queryname, complaint, dname, a_rr) * Issue a complaint about a dangerous situation found by nslookup(). * params: * sysloginfo is a string identifying the complainant. * queryname is the domain name associated with the problem. * complaint is a string describing what is wrong. * dname and a_rr are the problematic other name server. */ static void nslookupComplain(const char *sysloginfo, const char *queryname, const char *complaint, const char *dname, const struct databuf *a_rr, const struct databuf *nsdp) { char *a, *ns; const char *a_type; int print_a; ns_debug(ns_log_default, 2, "NS '%s' %s", dname, complaint); if (sysloginfo && queryname && !haveComplained((u_long)queryname, (u_long)complaint)) { a = ns = (char *)NULL; print_a = (a_rr->d_type == T_A); a_type = p_type(a_rr->d_type); if (a_rr->d_rcode) { print_a = 0; switch(a_rr->d_rcode) { case NXDOMAIN: a_type = "NXDOMAIN"; break; case NOERROR_NODATA: a_type = "NODATA"; break; } } if (NS_OPTION_P(OPTION_HOSTSTATS)) { char nsbuf[20], abuf[20]; if (nsdp != NULL) { if (nsdp->d_ns != NULL) { strcpy(nsbuf, inet_ntoa(nsdp->d_ns->addr)); ns = nsbuf; } else { ns = zones[nsdp->d_zone].z_origin; } } if (a_rr->d_ns != NULL) { strcpy(abuf, inet_ntoa(a_rr->d_ns->addr)); a = abuf; } else { a = zones[a_rr->d_zone].z_origin; } } if (a != NULL || ns != NULL) ns_info(ns_log_default, "%s: query(%s) %s (%s:%s) learnt (%s=%s:NS=%s)", sysloginfo, queryname, complaint, dname, print_a ? inet_ntoa(ina_get(a_rr->d_data)) : "", a_type, a ? a : "", ns ? ns : "" ); else ns_info(ns_log_default, "%s: query(%s) %s (%s:%s)", sysloginfo, queryname, complaint, dname, print_a ? inet_ntoa(ina_get(a_rr->d_data)) : ""); } } /* * nslookup(nsp, qp, syslogdname, sysloginfo) * Lookup the address for each nameserver in `nsp' and add it to * the list saved in the qinfo structure pointed to by `qp'. * Omits information about nameservers that we shouldn't ask. * Detects the following dangerous operations: * One of the A records for one of the nameservers in nsp * refers to the address of one of our own interfaces; * One of the A records refers to the nameserver port on * the host that asked us this question. * returns: the number of addresses added, or -1 if a dangerous operation * is detected. * side effects: * logs if a dangerous situation is detected and * (syslogdname && sysloginfo) */ int nslookup(struct databuf *nsp[], struct qinfo *qp, const char *syslogdname, const char *sysloginfo) { struct namebuf *np; struct databuf *dp, *nsdp; struct qserv *qs; int n; u_int i; struct hashbuf *tmphtp; char *dname; const char *fname; int oldn, naddr, class, found_arr, potential_ns, lame_ns; time_t curtime; ns_debug(ns_log_default, 3, "nslookup(nsp=%#x, qp=%#x, \"%s\")", nsp, qp, syslogdname); lame_ns = potential_ns = 0; naddr = n = qp->q_naddr; curtime = (u_long) tt.tv_sec; - while ((nsdp = *nsp++) != NULL) { + while ((nsdp = *nsp++) != NULL && n < NSMAX) { class = nsdp->d_class; dname = (char *)nsdp->d_data; ns_debug(ns_log_default, 3, "nslookup: NS \"%s\" c=%d t=%d (flags 0x%lu)", dname, class, nsdp->d_type, (u_long)nsdp->d_flags); /* don't put in servers we have tried */ for (i = 0; i < qp->q_nusedns; i++) { if (qp->q_usedns[i] == nsdp) { ns_debug(ns_log_default, 2, "skipping used NS w/name %s", nsdp->d_data); goto skipserver; } } /* skip lame servers */ if ((nsdp->d_flags & DB_F_LAME) != 0) { time_t when; when = db_lame_find(qp->q_domain, nsdp); if (when != 0 && when > tt.tv_sec) { ns_debug(ns_log_default, 3, "skipping lame NS"); lame_ns++; goto skipserver; } } tmphtp = ((nsdp->d_flags & DB_F_HINT) ?fcachetab :hashtab); np = nlookup(dname, &tmphtp, &fname, 0); if (np == NULL) { ns_debug(ns_log_default, 3, "%s: not found %s %#x", dname, fname, np); found_arr = 0; goto need_sysquery; } if (fname != dname) { found_arr = 0; goto need_sysquery; } found_arr = 0; oldn = n; /* look for name server addresses */ (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { struct in_addr nsa; if (dp->d_type == T_CNAME && dp->d_class == class) { static const char *complaint = "NS points to CNAME"; if (dp->d_rcode) continue; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); goto skipserver; } if (dp->d_type != T_A || dp->d_class != class) continue; if (dp->d_rcode) { /* Negative caching element. */ goto skipserver; } if (ina_hlong(ina_get(dp->d_data)) == INADDR_ANY) { static const char *complaint = "Bogus (0.0.0.0) A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #ifdef INADDR_LOOPBACK if (ina_hlong(ina_get(dp->d_data))==INADDR_LOOPBACK) { static const char *complaint = "Bogus LOOPBACK A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #endif #ifdef INADDR_BROADCAST if (ina_hlong(ina_get(dp->d_data))==INADDR_BROADCAST){ static const char *complaint = "Bogus BROADCAST A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #endif #ifdef IN_MULTICAST if (IN_MULTICAST(ina_hlong(ina_get(dp->d_data)))) { static const char *complaint = "Bogus MULTICAST A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #endif /* * Don't use records that may become invalid to * reference later when we do the rtt computation. * Never delete our safety-belt information! */ if ((dp->d_zone == DB_Z_CACHE) && (dp->d_ttl < (u_int32_t)curtime) && !(dp->d_flags & DB_F_HINT) ) { ns_debug(ns_log_default, 1, "nslookup: stale '%s'", NAME(*np)); n = oldn; found_arr = 0; goto need_sysquery; } found_arr++; nsa = ina_get(dp->d_data); /* don't put in duplicates */ qs = qp->q_addr; for (i = 0; i < (u_int)n; i++, qs++) if (ina_equal(qs->ns_addr.sin_addr, nsa)) goto skipaddr; qs->ns_addr.sin_family = AF_INET; qs->ns_addr.sin_port = ns_port; qs->ns_addr.sin_addr = nsa; qs->ns = nsdp; qs->nsdata = dp; qs->forwarder = 0; qs->nretry = 0; /* * If this A RR has no RTT, initialize its RTT to a * small random value. */ if (dp->d_nstime == 0) dp->d_nstime = 1 + (int)(25.0*rand()/(RAND_MAX + 1.0)); /* * if we are being asked to fwd a query whose * nameserver list includes our own name/address(es), * then we have detected a lame delegation and rather * than melt down the network and hose down the other * servers (who will hose us in return), we'll return * -1 here which will cause SERVFAIL to be sent to * the client's resolver which will hopefully then * shut up. * * (originally done in nsContainsUs by vix@dec mar92; * moved into nslookup by apb@und jan1993) * * try to limp along instead of denying service * gdonl mar96 */ if (aIsUs(nsa)) { static char *complaint = "contains our address"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } /* * If we want to forward to a host that asked us * this question then either we or they are sick * (unless they asked from some port other than * their nameserver port). (apb@und jan1993) * * try to limp along instead of denying service * gdonl mar96 */ if (memcmp(&qp->q_from, &qs->ns_addr, sizeof(qp->q_from)) == 0) { static char *complaint = "forwarding loop"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #ifdef BOGUSNS /* * Don't forward queries to bogus servers. Note * that this is unlike the previous tests, which * are fatal to the query. Here we just skip the * server, which is only fatal if it's the last * server. Note also that we antialias here -- all * A RR's of a server are considered the same server, * and if any of them is bogus we skip the whole * server. Those of you using multiple A RR's to * load-balance your servers will (rightfully) lose * here. But (unfortunately) only if they are bogus. */ if (ip_match_address(bogus_nameservers, nsa) > 0) goto skipserver; #endif if (server_options->blackhole_acl != NULL && ip_match_address(server_options->blackhole_acl, nsa) == 1) continue; n++; if (n >= NSMAX) - goto out; + break; skipaddr: (void)NULL; } ns_debug(ns_log_default, 8, "nslookup: %d ns addrs", n); need_sysquery: if (found_arr == 0) { potential_ns++; if (!(qp->q_flags & Q_SYSTEM)) (void) sysquery(dname, class, T_A, NULL, 0, ns_port, QUERY); } skipserver: (void)NULL; } out: ns_debug(ns_log_default, 3, "nslookup: %d ns addrs total", n); qp->q_naddr = n; if (n == 0 && potential_ns == 0 && !NS_ZFWDTAB(qp->q_fzone)) { static char *complaint = "No possible A RRs"; if (lame_ns != 0) complaint = "All possible A RR's lame"; if (sysloginfo && syslogdname && !haveComplained((u_long)syslogdname, (u_long)complaint)) { ns_info(ns_log_default, "%s: query(%s) %s", sysloginfo, syslogdname, complaint); } return ((lame_ns == 0) ? -1 : -2); } /* Update the refcounts before the sort. */ for (i = naddr; i < (u_int)n; i++) { DRCNTINC(qp->q_addr[i].nsdata); DRCNTINC(qp->q_addr[i].ns); } - if (n > 1) { - qsort((char *)qp->q_addr, n, sizeof(struct qserv), + /* Just sort the NS RR's we added, since the forwarders may + * be ahead of us (naddr > 0) + */ + if (n > naddr) { + qsort((char *)(qp->q_addr+naddr), n-naddr, sizeof(struct qserv), (int (*)(const void *, const void *))qcomp); } return (n - naddr); } /* * qcomp - compare two NS addresses, and return a negative, zero, or * positive value depending on whether the first NS address is * "better than", "equally good as", or "inferior to" the second * NS address. * * How "goodness" is defined (for the purposes of this routine): * - If the estimated round trip times differ by an amount deemed significant * then the one with the smaller estimate is preferred; else * - If we can determine which one is topologically closer then the * closer one is preferred; else * - The one with the smaller estimated round trip time is preferred * (zero is returned if the two estimates are identical). * * How "topological closeness" is defined (for the purposes of this routine): * Ideally, named could consult some magic map of the Internet and * determine the length of the path to an arbitrary destination. Sadly, * no such magic map exists. However, named does have a little bit of * topological information in the form of the sortlist (which includes * the directly connected subnet(s), the directly connected net(s), and * any additional nets that the administrator has added using the "sortlist" * directive in the bootfile. Thus, if only one of the addresses matches * something in the sortlist then it is considered to be topologically * closer. If both match, but match different entries in the sortlist, * then the one that matches the entry closer to the beginning of the * sorlist is considered to be topologically closer. In all other cases, * topological closeness is ignored because it's either indeterminate or * equal. * * How times are compared: * Both times are rounded to the closest multiple of the NOISE constant * defined below and then compared. If the rounded values are equal * then the difference in the times is deemed insignificant. Rounding * is used instead of merely taking the absolute value of the difference * because doing the latter would make the ordering defined by this * routine be incomplete in the mathematical sense (e.g. A > B and * B > C would not imply A > C). The mathematics are important in * practice to avoid core dumps in qsort(). * * XXX: this doesn't solve the European root nameserver problem very well. * XXX: we should detect and mark as inferior nameservers that give bogus * answers * * (this was originally vixie's stuff but almquist fixed fatal bugs in it * and wrote the above documentation) */ /* * RTT delta deemed to be significant, in milliseconds. With the current * definition of RTTROUND it must be a power of 2. */ -#define NOISE 128 /* milliseconds; 0.128 seconds */ +#define NOISE 64 -#define sign(x) (((x) < 0) ? -1 : ((x) > 0) ? 1 : 0) #define RTTROUND(rtt) (((rtt) + (NOISE >> 1)) & ~(NOISE - 1)) int qcomp(struct qserv *qs1, struct qserv *qs2) { - int pos1, pos2, pdiff; - u_long rtt1, rtt2; - long tdiff; + u_int rtt1, rtt2, rttr1, rttr2; - if ((!qs1->nsdata) || (!qs2->nsdata)) - return 0; - rtt1 = qs1->nsdata->d_nstime; - rtt2 = qs2->nsdata->d_nstime; + if (qs1->nsdata == NULL || qs2->nsdata == NULL) { + rtt1 = 0; + rttr1 = 0; + rtt2 = 0; + rttr2 = 0; + } else { + rtt1 = qs1->nsdata->d_nstime; + rttr1 = RTTROUND(rtt1); + rtt2 = qs2->nsdata->d_nstime; + rttr2 = RTTROUND(rtt2); + } #ifdef DEBUG if (debug >= 10) { - char a1[sizeof "255.255.255.255"], - a2[sizeof "255.255.255.255"]; + char t[sizeof "255.255.255.255"]; - strcpy(a1, inet_ntoa(qs1->ns_addr.sin_addr)); - strcpy(a2, inet_ntoa(qs2->ns_addr.sin_addr)); + strcpy(t, inet_ntoa(qs1->ns_addr.sin_addr)); ns_debug(ns_log_default, 10, "qcomp(%s, %s) %lu (%lu) - %lu (%lu) = %lu", - a1, a2, - rtt1, RTTROUND(rtt1), - rtt2, RTTROUND(rtt2), - rtt1 - rtt2); + t, inet_ntoa(qs2->ns_addr.sin_addr), + rtt1, rttr1, rtt2, rttr2, rtt1 - rtt2); } #endif - if (RTTROUND(rtt1) == RTTROUND(rtt2)) { + if (rttr1 == rttr2) { + int pos1, pos2, pdiff; + pos1 = distance_of_address(server_options->topology, qs1->ns_addr.sin_addr); pos2 = distance_of_address(server_options->topology, qs2->ns_addr.sin_addr); pdiff = pos1 - pos2; ns_debug(ns_log_default, 10, "\tpos1=%d, pos2=%d", pos1, pos2); - if (pdiff) + if (pdiff != 0) return (pdiff); } - tdiff = rtt1 - rtt2; - return (sign(tdiff)); + return (rtt1 - rtt2); } -#undef sign #undef RTTROUND /* * Arrange that forwarded query (qp) is retried after t seconds. * Query list will be sorted after z_time is updated. */ void schedretry(struct qinfo *qp, time_t t) { struct qinfo *qp1, *qp2; ns_debug(ns_log_default, 4, "schedretry(%#x, %ld sec)", qp, (long)t); if (qp->q_time) ns_debug(ns_log_default, 4, "WARNING: schedretry(%#lx, %ld) q_time already %ld", (u_long)qp, (long)t, (long)qp->q_time); gettime(&tt); t += (u_long) tt.tv_sec; qp->q_time = t; if ((qp1 = retryqp) == NULL) { retryqp = qp; qp->q_next = NULL; goto done; } if (t < qp1->q_time) { qp->q_next = qp1; retryqp = qp; goto done; } while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t) qp1 = qp2; qp1->q_next = qp; qp->q_next = qp2; done: reset_retrytimer(); } /* * Unsched is called to remove a forwarded query entry. */ void unsched(struct qinfo *qp) { struct qinfo *np; ns_debug(ns_log_default, 3, "unsched(%#lx, %d)", (u_long)qp, ntohs(qp->q_id)); if (retryqp == qp) { retryqp = qp->q_next; } else { for (np = retryqp; np->q_next != NULL; np = np->q_next) { if (np->q_next != qp) continue; np->q_next = qp->q_next; /* dequeue */ break; } } qp->q_next = NULL; /* sanity check */ qp->q_time = 0; reset_retrytimer(); } void reset_retrytimer() { static evTimerID id; if (retry_timer_set) { (void) evClearTimer(ev, id); retry_timer_set = 0; } if (retryqp) { evSetTimer(ev, retrytimer, NULL, evConsTime(retryqp->q_time, 0), evConsTime(0, 0), &id); retry_timer_set = 1; } else memset(&id, 0, sizeof id); } void retrytimer(evContext ctx, void *uap, struct timespec due, struct timespec ival) { retry_timer_set = 0; retry(retryqp); } /* * Retry is called to retransmit query 'qp'. */ void retry(struct qinfo *qp) { int n, has_tsig, oldqlen; HEADER *hp; struct sockaddr_in *nsa; int sendto_errno = 0; u_char *oldqbuf; u_char *smsg; int smsglen, smsgsize, siglen; u_char sig[TSIG_SIG_SIZE]; DST_KEY *key; ns_debug(ns_log_default, 3, "retry(%#lx) id=%d", (u_long)qp, ntohs(qp->q_id)); if (qp->q_msg == NULL) { qremove(qp); return; } if (qp->q_expire < tt.tv_sec) { ns_debug(ns_log_default, 1, "retry(%#lx): expired @ %lu (%d secs before now (%lu))", (u_long)qp, (u_long)qp->q_expire, (int)(tt.tv_sec - qp->q_expire), (u_long)tt.tv_sec); goto fail; } /* Try next address. */ n = qp->q_curaddr; if (qp->q_naddr > 0) { ++qp->q_addr[n].nretry; do { if (++n >= (int)qp->q_naddr) n = 0; if ((qp->q_flags & Q_ZSERIAL) != 0 && qp->q_addr[n].serial != 0) continue; if (qp->q_addr[n].nretry < MAXRETRY) goto found; } while (n != qp->q_curaddr); if ((qp->q_flags & Q_ZSERIAL) != 0) { qremove(qp); return; } } fail: /* * Give up. Can't reach destination. */ hp = (HEADER *)(qp->q_cmsg ? qp->q_cmsg : qp->q_msg); if ((qp->q_flags & Q_PRIMING) != 0) { /* Can't give up priming */ if (qp->q_expire < tt.tv_sec) { /* * The query has expired. Reset it and retry from * the beginning. */ hp->rcode = NOERROR; hp->qr = hp->aa = 0; for (n = 0; n < (int)qp->q_naddr; n++) qp->q_addr[n].nretry = 0; n = 0; qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; goto found; } /* * The query hasn't expired yet; it probably ran out * of servers or forwarders. Wait up to 60 seconds * past the expire time. */ unsched(qp); schedretry(qp, (time_t)(qp->q_expire - tt.tv_sec + 60)); return; } ns_debug(ns_log_default, 5, "give up"); if ((qp->q_flags & Q_SYSTEM) == 0) { n = ((HEADER *)qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen); hp->id = qp->q_id; hp->qr = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); hp->rd = 1; hp->rcode = SERVFAIL; #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, n, log_get_stream(packet_channel)); #endif if (send_msg((u_char *)hp, n, qp)) { ns_debug(ns_log_default, 1, "gave up retry(%#lx) nsid=%d id=%d", (u_long)qp, ntohs(qp->q_nsid), ntohs(qp->q_id)); } if (NS_OPTION_P(OPTION_HOSTSTATS)) nameserIncr(qp->q_from.sin_addr, nssSentFail); } qremove(qp); return; found: if (qp->q_addr[n].nretry == 0) qp->q_addr[n].stime = tt; qp->q_curaddr = n; hp = (HEADER *)qp->q_msg; hp->rd = (qp->q_addr[n].forwarder ? 1 : 0); nsa = Q_NEXTADDR(qp, n); ns_debug(ns_log_default, 1, "%s(addr=%d n=%d) -> [%s].%d ds=%d nsid=%d id=%d %dms", (qp->q_addr[n].forwarder ? "reforw" : "resend"), n, qp->q_addr[n].nretry, inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port), ds, ntohs(qp->q_nsid), ntohs(qp->q_id), (qp->q_addr[n].nsdata != 0) ? qp->q_addr[n].nsdata->d_nstime : (-1)); #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, qp->q_msglen, log_get_stream(packet_channel)); #endif key = tsig_key_from_addr(nsa->sin_addr); if (key != NULL) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; has_tsig = 1; qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (qp->q_flags & Q_USEVC) { if (tcp_send(qp) != NOERROR) ns_debug(ns_log_default, 3, "error resending tcp msg: %s", strerror(errno)); } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; ns_debug(ns_log_default, 3, "error resending msg: %s", strerror(errno)); } if (has_tsig == 1) { memput(qp->q_msg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } hp->rd = 1; /* leave set to 1 for dup detection */ nameserIncr(nsa->sin_addr, nssSentDupQ); unsched(qp); switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: schedretry(qp, (time_t) 0); return; } -#ifdef SLAVE_FORWARD - if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) - schedretry(qp, (time_t)slave_retry); - else -#endif /* SLAVE_FORWARD */ schedretry(qp, retrytime(qp)); } /* * Compute retry time for the next server for a query. * Use a minimum time of RETRYBASE (4 sec.) or twice the estimated * service time; * back off exponentially on retries, but place a 45-sec. * ceiling on retry times for now. (This is because we don't hold a reference * on servers or their addresses, and we have to finish before they time out.) */ time_t retrytime(struct qinfo *qp) { time_t t, u, v; struct qserv *ns = &qp->q_addr[qp->q_curaddr]; if (ns->nsdata != NULL) t = (time_t) MAX(RETRYBASE, 2 * ns->nsdata->d_nstime / 1000); else t = (time_t) RETRYBASE; u = t << ns->nretry; v = MIN(u, RETRY_TIMEOUT); /* max. retry timeout for now */ ns_debug(ns_log_default, 3, "retrytime: nstime%ldms t%ld nretry%ld u%ld : v%ld", ns->nsdata ? (long)(ns->nsdata->d_nstime / 1000) : (long)-1, (long)t, (long)ns->nretry, (long)u, (long)v); return (v); } void qflush() { while (nsqhead) qremove(nsqhead); nsqhead = NULL; priming = 0; } void qremove(struct qinfo *qp) { ns_debug(ns_log_default, 3, "qremove(%#lx)", (u_long)qp); if ((qp->q_flags & Q_ZSERIAL) != 0) qserial_answer(qp); unsched(qp); ns_freeqry(qp); } struct qinfo * qfindid(u_int16_t id) { struct qinfo *qp; for (qp = nsqhead; qp != NULL; qp = qp->q_link) if (qp->q_nsid == id) break; ns_debug(ns_log_default, 3, "qfindid(%d) -> %#lx", ntohs(id), (u_long)qp); return (qp); } struct qinfo * -qnew(const char *name, int class, int type) { +qnew(const char *name, int class, int type, int forward) { struct qinfo *qp; const char *s; int escape = 0; qp = (struct qinfo *)memget(sizeof *qp); if (qp == NULL) ns_panic(ns_log_default, 1, "qnew: memget failed"); memset(qp, 0, sizeof *qp); ns_debug(ns_log_default, 5, "qnew(%#lx)", (u_long)qp); #ifdef BIND_NOTIFY qp->q_notifyzone = DB_Z_CACHE; #endif qp->q_link = nsqhead; nsqhead = qp; qp->q_name = savestr(name, 1); qp->q_class = (u_int16_t)class; qp->q_type = (u_int16_t)type; qp->q_flags = 0; s = name; - for (;;) { /* find forwarding zone, if any */ + qp->q_fzone = NULL; + for (;forward;) { /* find forwarding zone, if any */ if ((qp->q_fzone = find_zone(s, class)) != NULL && (qp->q_fzone->z_flags & Z_FORWARD_SET) != 0) break; qp->q_fzone = NULL; if (*s == '\0') break; while (*s != '\0' && (escape || *s != '.')) { escape = escape ? 0 : (*s == '\\'); s++; } if (*s != '\0') s++; } return (qp); } void ns_freeqns(struct qinfo *qp, char *where) { static const char freed[] = "freed", busy[] = "busy"; const char *result; struct databuf *dp; int i; for (i = 0 ; i < (int)qp->q_naddr ; i++) { dp = qp->q_addr[i].ns; if (dp) { DRCNTDEC(dp); result = (dp->d_rcnt) ? busy : freed; ns_debug(ns_log_default, 3, "%s: ns %s rcnt %d (%s)", where, dp->d_data, dp->d_rcnt, result); if (result == freed) db_freedata(dp); } dp = qp->q_addr[i].nsdata; if (dp) { DRCNTDEC(dp); result = (dp->d_rcnt) ? busy : freed; ns_debug(ns_log_default, 3, "%s: nsdata %s rcnt %d (%s)", where, inet_ntoa(ina_get(dp->d_data)), dp->d_rcnt, result); if (result == freed) db_freedata(dp); } } } void ns_freeqry(struct qinfo *qp) { struct qinfo *np; ns_debug(ns_log_default, 3, "ns_freeqry(%#lx)", (u_long)qp); if (qp->q_next) ns_debug(ns_log_default, 1, "WARNING: ns_freeqry of linked ptr %#lx", (u_long)qp); if (qp->q_msg != NULL) memput(qp->q_msg, qp->q_msgsize); if (qp->q_cmsg != NULL) memput(qp->q_cmsg, qp->q_cmsgsize); if (qp->q_domain != NULL) freestr(qp->q_domain); if (qp->q_name != NULL) freestr(qp->q_name); if (qp->q_tsig != NULL) memput(qp->q_tsig, sizeof(struct tsig_record)); if (qp->q_nstsig != NULL) memput(qp->q_nstsig, sizeof(struct tsig_record)); ns_freeqns(qp, "ns_freeqry"); if (nsqhead == qp) nsqhead = qp->q_link; else { for(np = nsqhead; np->q_link != NULL; np = np->q_link) { if (np->q_link != qp) continue; np->q_link = qp->q_link; /* dequeue */ break; } } memput(qp, sizeof *qp); } void nsfwdadd(struct qinfo *qp, struct fwdinfo *fwd) { int i, n; struct qserv *qs; n = qp->q_naddr; - while (fwd != NULL && n < MAXNS) { + while (fwd != NULL && n < NSMAX) { qs = qp->q_addr; for (i = 0; i < (u_int)n; i++, qs++) if (ina_equal(qs->ns_addr.sin_addr, - fwd->fwdaddr.sin_addr)) + fwd->fwddata->fwdaddr.sin_addr)) goto nextfwd; - qs->ns_addr = fwd->fwdaddr; - qs->ns = NULL; - qs->nsdata = NULL; + qs->ns_addr = fwd->fwddata->fwdaddr; + qs->ns = fwd->fwddata->ns; + qs->nsdata = fwd->fwddata->nsdata; qs->forwarder = 1; qs->nretry = 0; n++; nextfwd: fwd = fwd->next; } qp->q_naddr = n; + + /* Update the refcounts before the sort. */ + for (i = 0; i < (u_int)n; i++) { + DRCNTINC(qp->q_addr[i].nsdata); + DRCNTINC(qp->q_addr[i].ns); + } + if (n > 1) { + qsort((char *)qp->q_addr, n, sizeof(struct qserv), + (int (*)(const void *, const void *))qcomp); + } } Index: head/contrib/bind/bin/named/ns_func.h =================================================================== --- head/contrib/bind/bin/named/ns_func.h (revision 60940) +++ head/contrib/bind/bin/named/ns_func.h (revision 60941) @@ -1,498 +1,503 @@ /* * Copyright (c) 1985, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ns_func.h - declarations for ns_*.c's externally visible functions * - * $Id: ns_func.h,v 8.90 1999/10/11 18:22:20 vixie Exp $ + * $Id: ns_func.h,v 8.96 2000/04/21 06:54:06 vixie Exp $ */ /* ++from ns_glue.c++ */ extern struct in_addr ina_get(const u_char *data); extern const char *sin_ntoa(struct sockaddr_in); extern int ns_wouldlog(int category, int level); extern void ns_debug(int, int, const char *, ...), ns_info(int, const char *, ...), ns_notice(int, const char *, ...), ns_warning(int, const char *, ...), ns_error(int, const char *, ...), ns_panic(int, int, const char *, ...), ns_assertion_failed(char *file, int line, assertion_type type, char *cond, int print_errno); extern void panic(const char *, const void *), gettime(struct timeval *); extern int nlabels(const char *), my_close(int), my_fclose(FILE *); extern void __freestr(char *); extern char *__newstr(size_t, int), *__savestr(const char *, int), *checked_ctime(const time_t *t), *ctimel(long); extern void __freestr_record(char *, char *, int); extern char *__newstr_record(size_t, int, char *, int); extern char *__savestr_record(const char *, int, char *, int); extern u_char *ina_put(struct in_addr ina, u_char *data), *savebuf(const u_char *, size_t, int); extern void dprintf(int level, const char *format, ...); #ifdef DEBUG_STRINGS extern char *debug_newstr(size_t, int, const char *, int), *debug_savestr(const char *, int, const char *, int); extern void debug_freestr(char *, const char *, int); #define newstr(l, n) debug_newstr((l), (n), __FILE__, __LINE__) #define savestr(s, n) debug_savestr((s), (n), __FILE__, __LINE__) #define freestr(s) debug_freestr((s), __FILE__, __LINE__) #else #ifdef RECORD_STRINGS #define newstr(l, n) __newstr_record((l), (n), __FILE__, __LINE__) #define savestr(s, n) __savestr_record((s), (n), __FILE__, __LINE__) #define freestr(s) __freestr_record((s), __FILE__, __LINE__) #else #define newstr(l, n) __newstr((l), (n)) #define savestr(s, n) __savestr((s), (n)) #define freestr(s) __freestr((s)) #endif #endif /* DEBUG_STRINGS */ int movefile(const char *, const char *); /* --from ns_glue.c-- */ /* ++from ns_notify.c++ */ #ifdef BIND_NOTIFY void ns_notify(const char *, ns_class, ns_type); +void notify_afterload(void); void ns_unnotify(void); +void ns_stopnotify(const char *, ns_class); #endif /* --from ns_notify.c-- */ /* ++from ns_resp.c++ */ extern void ns_resp(u_char *, int, struct sockaddr_in, struct qstream *), prime_cache(void), delete_all(struct namebuf *, int, int); extern int delete_stale(struct namebuf *); extern struct qinfo *sysquery(const char *, int, int, struct in_addr *, int, u_int16_t, int); extern int doupdate(u_char *, u_char *, struct databuf **, int, int, int, u_int, struct sockaddr_in), send_msg(u_char *, int, struct qinfo *), findns(struct namebuf **, int, struct databuf **, int *, int), finddata(struct namebuf *, int, int, HEADER *, char **, int *, int *), add_data(struct namebuf *, struct databuf **, u_char *, int, int *), trunc_adjust(u_char *, int, int); /* --from ns_resp.c-- */ /* ++from ns_req.c++ */ extern void ns_req(u_char *, int, int, struct qstream *, struct sockaddr_in, int), free_addinfo(void), free_nsp(struct databuf **); extern int stale(struct databuf *), make_rr(const char *, struct databuf *, u_char *, int, int, u_char **, u_char **, int), doaddinfo(HEADER *, u_char *, int), doaddauth(HEADER *, u_char *, int, struct namebuf *, struct databuf *); #ifdef BIND_NOTIFY extern int findZonePri(const struct zoneinfo *, const struct sockaddr_in); #endif /* --from ns_req.c-- */ /* ++from ns_xfr.c++ */ void ns_xfr(struct qstream *qsp, struct namebuf *znp, int zone, int class, int type, int id, int opcode, u_int32_t serial_ixfr, struct tsig_record *in_tsig), ns_stopxfrs(struct zoneinfo *), ns_freexfr(struct qstream *), sx_newmsg(struct qstream *qsp), sx_sendlev(struct qstream *qsp), sx_sendsoa(struct qstream *qsp); /* --from ns_xfr.c-- */ /* ++from ns_ctl.c++ */ void ns_ctl_initialize(void); void ns_ctl_shutdown(void); void ns_ctl_defaults(controls *); void ns_ctl_add(controls *, control); control ns_ctl_new_inet(struct in_addr, u_int, ip_match_list); #ifndef WINNT control ns_ctl_new_unix(char *, mode_t, uid_t, gid_t); #endif void ns_ctl_install(controls *); /* --from ns_ctl.c-- */ /* ++from ns_ixfr.c++ */ void sx_send_ixfr(struct qstream *qsp); /* --from ns_ixfr.c-- */ /* ++from ns_forw.c++ */ extern time_t retrytime(struct qinfo *); extern int ns_forw(struct databuf *nsp[], u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp, int dfd, struct qinfo **qpp, const char *dname, int class, int type, struct namebuf *np, int use_tcp, struct tsig_record *in_tsig), haveComplained(u_long, u_long), nslookup(struct databuf *nsp[], struct qinfo *qp, const char *syslogdname, const char *sysloginfo), qcomp(struct qserv *, struct qserv *); extern void schedretry(struct qinfo *, time_t), unsched(struct qinfo *), reset_retrytimer(void), retrytimer(evContext ctx, void *uap, struct timespec due, struct timespec ival), retry(struct qinfo *), qflush(void), qremove(struct qinfo *), ns_freeqns(struct qinfo *, char *), ns_freeqry(struct qinfo *), freeComplaints(void), nsfwdadd(struct qinfo *, struct fwdinfo *); extern struct qinfo *qfindid(u_int16_t), - *qnew(const char *, int, int); + *qnew(const char *, int, int, int); /* --from ns_forw.c-- */ /* ++from ns_main.c++ */ extern struct in_addr net_mask(struct in_addr); extern void sq_remove(struct qstream *), sq_flushw(struct qstream *), sq_flush(struct qstream *allbut), dq_remove_gen(time_t gen), dq_remove_all(), sq_done(struct qstream *), ns_setproctitle(char *, int), getnetconf(int), nsid_init(void), ns_setoption(int option), writestream(struct qstream *, const u_char *, int), ns_need_unsafe(enum need), ns_need(enum need), opensocket_f(void), nsid_hash(u_char *, size_t); extern u_int16_t nsid_next(void); extern int sq_openw(struct qstream *, int), sq_writeh(struct qstream *, sq_closure), sq_write(struct qstream *, const u_char *, int), tcp_send(struct qinfo *), aIsUs(struct in_addr); /* --from ns_main.c-- */ /* ++from ns_maint.c++ */ extern void zone_maint(struct zoneinfo *), sched_zone_maint(struct zoneinfo *), ns_cleancache(evContext ctx, void *uap, struct timespec due, struct timespec inter), clean_cache_from(char *dname, struct hashbuf *htp), remove_zone(struct zoneinfo *, const char *), purge_zone(const char *, struct hashbuf *, int), loadxfer(void), qserial_retrytime(struct zoneinfo *, time_t), qserial_query(struct zoneinfo *), qserial_answer(struct qinfo *), #ifdef DEBUG printzoneinfo(int, int, int), #endif endxfer(void), addxfer(struct zoneinfo *), ns_zreload(void), ns_reload(void), - ns_reconfig(void); + ns_reconfig(void), + ns_noexpired(void); #if 0 extern int reload_all_unsafe(void); #endif extern int zonefile_changed_p(struct zoneinfo *); int reload_master(struct zoneinfo *); extern const char * deferred_reload_unsafe(struct zoneinfo *); extern struct namebuf * purge_node(struct hashbuf *htp, struct namebuf *np); extern int clean_cache(struct hashbuf *, int); extern void reapchild(void); extern const char * zoneTypeString(unsigned int); extern void ns_heartbeat(evContext ctx, void *uap, struct timespec, struct timespec); extern void make_new_zones(void); extern void free_zone(struct zoneinfo *); extern struct zoneinfo *find_auth_zone(const char *, ns_class); +extern int purge_nonglue(const char *dname, struct hashbuf *htp, + int class); /* --from ns_maint.c-- */ /* ++from ns_sort.c++ */ extern void sort_response(u_char *, u_char *, int, struct sockaddr_in *); /* --from ns_sort.c-- */ /* ++from ns_init.c++ */ extern void ns_refreshtime(struct zoneinfo *, time_t); extern void ns_retrytime(struct zoneinfo *, time_t); extern void ns_init(const char *); extern void purgeandload(struct zoneinfo *zp); extern enum context ns_ptrcontext(const char *owner); extern enum context ns_ownercontext(int type, enum transport); extern int ns_nameok(const struct qinfo *qry, const char *name, int class, struct zoneinfo *zp, enum transport, enum context, const char *owner, struct in_addr source); extern int ns_wildcard(const char *name); extern void zoneinit(struct zoneinfo *); extern void do_reload(const char *, int, int, int); extern void ns_shutdown(void); /* --from ns_init.c-- */ /* ++from ns_ncache.c++ */ extern void cache_n_resp(u_char *, int, struct sockaddr_in, const char *, int, int); /* --from ns_ncache.c-- */ /* ++from ns_udp.c++ */ extern void ns_udp(void); /* --from ns_udp.c-- */ /* ++from ns_stats.c++ */ extern void ns_stats(void), ns_freestats(void); extern void ns_logstats(evContext ctx, void *uap, struct timespec, struct timespec); extern void qtypeIncr(int qtype); extern struct nameser *nameserFind(struct in_addr addr, int flags); #define NS_F_INSERT 0x0001 #define nameserIncr(a,w) NS_INCRSTAT(a,w) /* XXX should change name. */ /* --from ns_stats.c-- */ /* ++from ns_update.c++ */ void free_rrecp(ns_updque *, int rcode, struct sockaddr_in); int findzone(const char *, int, int, int *, int); u_char * findsoaserial(u_char *data); u_int32_t get_serial_unchecked(struct zoneinfo *zp); u_int32_t get_serial(struct zoneinfo *zp); void set_serial(struct zoneinfo *zp, u_int32_t serial); int schedule_soa_update(struct zoneinfo *, int); int schedule_dump(struct zoneinfo *); int incr_serial(struct zoneinfo *zp); int merge_logs(struct zoneinfo *zp, char *logname); int zonedump(struct zoneinfo *zp, int isixfr); void dynamic_about_to_exit(void); enum req_action req_update(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, struct qstream *qsp, int dfd, struct sockaddr_in from, struct tsig_record *in_tsig); void rdata_dump(struct databuf *dp, FILE *fp); /* --from ns_update.c-- */ /* ++from ns_config.c++ */ void free_zone_timerinfo(struct zoneinfo *); void free_zone_contents(struct zoneinfo *, int); struct zoneinfo * find_zone(const char *, int); zone_config begin_zone(char *, int); void end_zone(zone_config, int); int set_zone_type(zone_config, int); int set_zone_filename(zone_config, char *); int set_zone_checknames(zone_config, enum severity); #ifdef BIND_NOTIFY int set_zone_notify(zone_config, int value); #endif int set_zone_maintain_ixfr_base(zone_config, int value); int set_zone_update_acl(zone_config, ip_match_list); int set_zone_query_acl(zone_config, ip_match_list); int set_zone_transfer_acl(zone_config, ip_match_list); int set_zone_transfer_source(zone_config, struct in_addr); int set_zone_pubkey(zone_config, const int, const int, const int, const char *); int set_zone_transfer_time_in(zone_config, long); int add_zone_master(zone_config, struct in_addr); #ifdef BIND_NOTIFY int add_zone_notify(zone_config, struct in_addr); #endif void set_zone_forward(zone_config); void add_zone_forwarder(zone_config, struct in_addr); void set_zone_boolean_option(zone_config, int, int); options new_options(void); void free_options(options); void free_rrset_order_list(rrset_order_list); void set_global_boolean_option(options, int, int); listen_info_list new_listen_info_list(void); void free_listen_info_list(listen_info_list); -void add_listen_on(options, u_int16_t, ip_match_list); +void add_listen_on(options, u_short, ip_match_list); FILE * write_open(char *filename); void update_pid_file(void); void set_options(options, int); void use_default_options(void); enum ordering lookup_ordering(const char *); rrset_order_list new_rrset_order_list(void); rrset_order_element new_rrset_order_element(int, int, char *, enum ordering); ip_match_list new_ip_match_list(void); void free_ip_match_list(ip_match_list); ip_match_element new_ip_match_pattern(struct in_addr, u_int); ip_match_element new_ip_match_mask(struct in_addr, struct in_addr); ip_match_element new_ip_match_indirect(ip_match_list); ip_match_element new_ip_match_key(struct dst_key *dst_key); ip_match_element new_ip_match_localhost(void); ip_match_element new_ip_match_localnets(void); void ip_match_negate(ip_match_element); void add_to_ip_match_list(ip_match_list, ip_match_element); void dprint_ip_match_list(int, ip_match_list, int, char *, char *); int ip_match_address(ip_match_list, struct in_addr); int ip_match_addr_or_key(ip_match_list, struct in_addr, struct dst_key *key); int ip_address_allowed(ip_match_list, struct in_addr); int ip_addr_or_key_allowed(ip_match_list iml, struct in_addr, struct dst_key *key); int ip_match_network(ip_match_list, struct in_addr, struct in_addr); int ip_match_key_name(ip_match_list iml, char *name); int distance_of_address(ip_match_list, struct in_addr); int ip_match_is_none(ip_match_list); #ifdef BIND_NOTIFY void free_also_notify(options); int add_global_also_notify(options, struct in_addr); #endif void add_global_forwarder(options, struct in_addr); void free_forwarders(struct fwdinfo *); server_info find_server(struct in_addr); server_config begin_server(struct in_addr); void end_server(server_config, int); void set_server_option(server_config, int, int); void set_server_transfers(server_config, int); void set_server_transfer_format(server_config, enum axfr_format); void add_server_key_info(server_config, struct dst_key *); struct dst_key *new_key_info(char *, char *, char *); void free_key_info(struct dst_key *); struct dst_key *find_key(char *name, char *algorithm); void dprint_key_info(struct dst_key *); key_info_list new_key_info_list(void); void free_key_info_list(key_info_list); void add_to_key_info_list(key_info_list, struct dst_key *); void dprint_key_info_list(key_info_list); log_config begin_logging(void); void add_log_channel(log_config, int, log_channel); void open_special_channels(void); void set_logging(log_config, int); void end_logging(log_config, int); void use_default_logging(void); void init_logging(void); void shutdown_logging(void); void init_configuration(void); void shutdown_configuration(void); void load_configuration(const char *); /* --from ns_config.c-- */ /* ++from parser.y++ */ ip_match_list lookup_acl(char *); void define_acl(char *, ip_match_list); struct dst_key *lookup_key(char *); void define_key(char *, struct dst_key *); void parse_configuration(const char *); void parser_initialize(void); void parser_shutdown(void); /* --from parser.y-- */ /* ++from ns_signal.c++ */ void init_signals(void); void block_signals(void); void unblock_signals(void); /* --from ns_signal.c-- */ Index: head/contrib/bind/bin/named/ns_glob.h =================================================================== --- head/contrib/bind/bin/named/ns_glob.h (revision 60940) +++ head/contrib/bind/bin/named/ns_glob.h (revision 60941) @@ -1,342 +1,342 @@ /* * from ns.h 4.33 (Berkeley) 8/23/90 - * $Id: ns_glob.h,v 8.51 1999/10/15 21:53:32 vixie Exp $ + * $Id: ns_glob.h,v 8.54 2000/04/21 06:54:07 vixie Exp $ */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Global variables for the name server. */ /* original argv[] from main() */ DECL char **saved_argv; #ifdef DEBUG DECL int debug INIT(0); DECL int desired_debug INIT(0); #endif /* global event context */ DECL evContext ev; /* global resolver context. */ DECL struct __res_state res; /* list of open streams */ DECL struct qstream *streamq; /* often set to the current time */ DECL struct timeval tt; /* head of allocated queries */ DECL struct qinfo *nsqhead; /* datagram socket for sysquery() and ns_forw(). */ DECL int ds INIT(-1); /* event ID for reads of "ds". */ DECL evFileID ds_evID; #ifdef QRYLOG /* is query logging turned on? */ DECL int qrylog; #endif /*QRYLOG*/ /* port to which we send queries */ DECL u_int16_t ns_port; /* Source addr of our internal resolver. */ DECL struct sockaddr_in source_addr; /* INITs to . */ /* Used by ns_stats */ DECL time_t boottime; DECL time_t resettime; /* next query to retry */ DECL struct qinfo *retryqp; /* default configuration file */ DECL char *conffile; /* default debug output file */ DECL char *debugfile; /* zone information */ DECL struct zoneinfo *zones; /* number of zones allocated */ DECL int nzones; /* free list of unused zones[] elements. */ DECL LIST(struct zoneinfo) freezones; /* list of zones that have a reload pending. */ DECL LIST(struct zoneinfo) reloadingzones; /* set if we need a priming */ DECL int needs_prime_cache; /* is cache being primed */ DECL int priming; /* ptrs to dnames in msg for dn_comp */ DECL u_char *dnptrs[40]; /* end pointer for dnptrs */ DECL u_char **dnptrs_end INIT(dnptrs + sizeof dnptrs / sizeof(u_char*)); + /* data about all forwarders */ +DECL struct fwddata **fwddata; + /* how many forwarders are there in fwddata? */ +DECL int fwddata_count; + /* number of names in addinfo */ DECL int addcount; /* name of cache file */ DECL const char *cache_file; - -#ifdef SLAVE_FORWARD - /* retry time when a slave */ -DECL int slave_retry INIT(4); -#endif #ifdef BIND_UPDATE DECL const char * LogSignature INIT(";BIND LOG V8\n"); DECL const char * DumpSignature INIT(";BIND DUMP V8\n"); DECL const char * DumpSuffix INIT(".dumptmp"); #endif DECL const char sendtoStr[] INIT("sendto"); DECL const char tcpsendStr[] INIT("tcp_send"); /* defined in version.c, can't use DECL/INIT */ extern char Version[]; extern char ShortVersion[]; /* If getnum() has an error, here will be the result. */ DECL int getnum_error INIT(0); enum context { domain_ctx, owner_ctx, mailname_ctx, hostname_ctx }; DECL const char *context_strings[] #ifdef MAIN_PROGRAM = { "domain", "owner", "mail", "host", NULL } #endif ; DECL const char *transport_strings[] #ifdef MAIN_PROGRAM = { "primary", "secondary", "response", NULL } #endif ; DECL const char *severity_strings[] #ifdef MAIN_PROGRAM = { "ignore", "warn", "fail", "not_set", NULL } #endif ; DECL struct in_addr inaddr_any; /* Inits to 0.0.0.0 */ DECL options server_options INIT(NULL); DECL server_info nameserver_info INIT(NULL); DECL key_info_list secretkey_info INIT(NULL); DECL int main_needs_exit INIT(0); DECL ip_match_list bogus_nameservers INIT(NULL); DECL log_context log_ctx; DECL int log_ctx_valid INIT(0); DECL log_channel syslog_channel INIT(NULL); DECL log_channel debug_channel INIT(NULL); DECL log_channel stderr_channel INIT(NULL); DECL log_channel eventlib_channel INIT(NULL); DECL log_channel packet_channel INIT(NULL); DECL log_channel null_channel INIT(NULL); DECL ip_match_list local_addresses INIT(NULL); DECL ip_match_list local_networks INIT(NULL); /* are we running in no-fork mode? */ DECL int foreground INIT(0); DECL const struct ns_sym logging_constants[] #ifdef MAIN_PROGRAM = { { log_info, "info" }, { log_notice, "notice" }, { log_warning, "warning" }, { log_error, "error" }, { log_critical, "critical" }, { 0, NULL } } #endif ; DECL const struct ns_sym syslog_constants[] #ifdef MAIN_PROGRAM = { { LOG_KERN, "kern" }, { LOG_USER, "user" }, { LOG_MAIL, "mail" }, { LOG_DAEMON, "daemon" }, { LOG_AUTH, "auth" }, { LOG_SYSLOG, "syslog" }, { LOG_LPR, "lpr" }, #ifdef LOG_NEWS { LOG_NEWS, "news" }, #endif #ifdef LOG_UUCP { LOG_UUCP, "uucp" }, #endif #ifdef LOG_CRON { LOG_CRON, "cron" }, #endif #ifdef LOG_AUTHPRIV { LOG_AUTHPRIV, "authpriv" }, #endif #ifdef LOG_FTP { LOG_FTP, "ftp" }, #endif { LOG_LOCAL0, "local0"}, { LOG_LOCAL1, "local1"}, { LOG_LOCAL2, "local2"}, { LOG_LOCAL3, "local3"}, { LOG_LOCAL4, "local4"}, { LOG_LOCAL5, "local5"}, { LOG_LOCAL6, "local6"}, { LOG_LOCAL7, "local7"}, { 0, NULL } } #endif ; DECL const struct ns_sym category_constants[] #ifdef MAIN_PROGRAM = { { ns_log_default, "default" }, { ns_log_config, "config" }, { ns_log_parser, "parser" }, { ns_log_queries, "queries" }, { ns_log_lame_servers, "lame-servers" }, { ns_log_statistics, "statistics" }, { ns_log_panic, "panic" }, { ns_log_update, "update" }, { ns_log_ncache, "ncache" }, { ns_log_xfer_in, "xfer-in" }, { ns_log_xfer_out, "xfer-out" }, { ns_log_db, "db" }, { ns_log_eventlib, "eventlib" }, { ns_log_packet, "packet" }, #ifdef BIND_NOTIFY { ns_log_notify, "notify" }, #endif { ns_log_cname, "cname" }, { ns_log_security, "security" }, { ns_log_os, "os" }, { ns_log_insist, "insist" }, { ns_log_maint, "maintenance" }, { ns_log_load, "load" }, { ns_log_resp_checks, "response-checks" }, { ns_log_control, "control" }, { 0, NULL } } #endif ; DECL const char panic_msg_no_options[] INIT("no server_options in NS_OPTION_P"); DECL const char panic_msg_insist_failed[] INIT("%s:%d: insist '%s' failed: %s"); DECL const char panic_msg_bad_which[] INIT("%s:%d: INCRSTATS(%s): bad \"which\""); DECL u_long globalStats[nssLast]; DECL evTimerID clean_timer; DECL evTimerID interface_timer; DECL evTimerID stats_timer; DECL evTimerID heartbeat_timer; DECL int active_timers INIT(0); DECL uid_t user_id; DECL char * user_name INIT(NULL); DECL gid_t group_id; DECL char * group_name INIT(NULL); DECL char * chroot_dir INIT(NULL); DECL int loading INIT(0); DECL int xfers_running INIT(0); DECL int xfers_deferred INIT(0); DECL int qserials_running INIT(0); Index: head/contrib/bind/bin/named/ns_glue.c =================================================================== --- head/contrib/bind/bin/named/ns_glue.c (revision 60940) +++ head/contrib/bind/bin/named/ns_glue.c (revision 60941) @@ -1,450 +1,463 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_glue.c,v 8.14 1999/10/19 02:06:26 gson Exp $"; +static const char rcsid[] = "$Id: ns_glue.c,v 8.16 2000/04/21 06:50:18 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* * IP address from unaligned octets. */ struct in_addr ina_get(const u_char *data) { struct in_addr ret; u_int32_t i; GETLONG(i, data); ina_ulong(ret) = htonl(i); return (ret); } /* * IP address to unaligned octets. */ u_char * ina_put(struct in_addr ina, u_char *data) { PUTLONG(ntohl(ina_ulong(ina)), data); return (data); } /* - * XXX: sin_ntoa() should probably be in libc. + * IP address to presentation format. */ const char * sin_ntoa(struct sockaddr_in sin) { static char ret[sizeof "[111.222.333.444].55555"]; - sprintf(ret, "[%s].%u", - inet_ntoa(sin.sin_addr), - ntohs(sin.sin_port)); + sprintf(ret, "[%s].%u", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); return (ret); } /* * Logging Support */ int ns_wouldlog(int category, int level) { if (log_ctx_valid) return (log_check(log_ctx, category, level)); return (0); } void ns_debug(int category, int level, const char *format, ...) { va_list args; if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, category, log_debug(level), format, args); va_end(args); } void ns_info(int category, const char *format, ...) { va_list args; if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, category, log_info, format, args); va_end(args); } void ns_notice(int category, const char *format, ...) { va_list args; if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, category, log_notice, format, args); va_end(args); } void ns_warning(int category, const char *format, ...) { va_list args; if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, category, log_warning, format, args); va_end(args); } void ns_error(int category, const char *format, ...) { va_list args; if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, category, log_error, format, args); va_end(args); } void ns_panic(int category, int dump_core, const char *format, ...) { va_list args; if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, category, log_critical, format, args); va_end(args); va_start(args, format); log_vwrite(log_ctx, ns_log_panic, log_critical, format, args); va_end(args); if (dump_core) abort(); else exit(1); } void ns_assertion_failed(char *file, int line, assertion_type type, char *cond, int print_errno) { ns_panic(ns_log_insist, 1, "%s:%d: %s(%s)%s%s failed.", file, line, assertion_type_to_text(type), cond, (print_errno) ? ": " : "", (print_errno) ? strerror(errno) : ""); } /* - * XXX This is for compatibility and will eventually be removed. + * XXX This is for compatibility and should eventually be removed. */ void panic(const char *msg, const void *arg) { ns_panic(ns_log_default, 1, msg, arg); } /* * How many labels in this name? * Note: the root label is not included in the count. */ int -nlabels (const char *dname) { +nlabels(const char *dname) { int count, i, found, escaped; const char *tmpdname, *end_tmpdname; int tmpdnamelen, c; INSIST(dname != NULL); count = 0; tmpdname = dname; tmpdnamelen = strlen(tmpdname); /* * Ignore a trailing label separator (i.e. an unescaped dot) * in 'tmpdname'. */ if (tmpdnamelen && tmpdname[tmpdnamelen-1] == '.') { escaped = 0; /* note this loop doesn't get executed if tmpdnamelen==1 */ for (i = tmpdnamelen - 2; i >= 0; i--) if (tmpdname[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; - } else { + } else break; - } if (!escaped) tmpdnamelen--; } end_tmpdname = tmpdname + tmpdnamelen; while(tmpdname != end_tmpdname) { count++; /* * Strip off the first label if we're not already at * the root label. */ for (escaped = found = 0; (tmpdname != end_tmpdname) && !found; tmpdname++) { c = *tmpdname; if (!escaped && (c == '.')) found = 1; if (escaped) escaped = 0; else if (c == '\\') escaped = 1; } } - ns_debug(ns_log_default, 12, "nlabels of \"%s\" -> %d", dname, - count); + ns_debug(ns_log_default, 12, "nlabels of \"%s\" -> %d", dname, count); return (count); } /* * Get current system time and put it in a global. */ void gettime(struct timeval *ttp) { if (gettimeofday(ttp, NULL) < 0) ns_error(ns_log_default, "gettimeofday: %s", strerror(errno)); } /* * This is useful for tracking down lost file descriptors. */ int my_close(int fd) { int s; do { errno = 0; s = close(fd); } while (s < 0 && errno == EINTR); if (s < 0 && errno != EBADF) ns_info(ns_log_default, "close(%d) failed: %s", fd, strerror(errno)); else ns_debug(ns_log_default, 3, "close(%d) succeeded", fd); return (s); } /* * This is useful for tracking down lost file descriptors. */ int my_fclose(FILE *fp) { int fd = fileno(fp), s = fclose(fp); if (s < 0) ns_info(ns_log_default, "fclose(%d) failed: %s", fd, strerror(errno)); else ns_debug(ns_log_default, 3, "fclose(%d) succeeded", fd); return (s); } /* * Save a counted buffer and return a pointer to it. */ u_char * savebuf(const u_char *buf, size_t len, int needpanic) { u_char *bp = (u_char *)memget(len); if (bp == NULL) { if (needpanic) panic("savebuf: memget failed (%s)", strerror(errno)); else return (NULL); } memcpy(bp, buf, len); return (bp); } char * __newstr(size_t len, int needpanic) { return (__newstr_record(len, needpanic, __FILE__, __LINE__)); } char * __savestr(const char *str, int needpanic) { return (__savestr_record(str, needpanic, __FILE__, __LINE__)); } void __freestr(char *str) { __freestr_record(str, __FILE__, __LINE__); } #ifdef DEBUG_STRINGS char * debug_newstr(size_t len, int needpanic, const char *file, int line) { size_t size; size = len + 3; /* 2 length bytes + NUL. */ printf("%s:%d: newstr %d\n", file, line, size); return (__newstr_record(len, needpanic, file, line)); } char * debug_savestr(const char *str, int needpanic, const char *file, int line) { size_t len; len = strlen(str); len += 3; /* 2 length bytes + NUL. */ printf("%s:%d: savestr %d %s\n", file, line, len, str); return (__savestr_record(str, needpanic, file, line)); } void debug_freestr(char *str, const char *file, int line) { u_char *buf, *bp; size_t len; buf = (u_char *)str - 2/*Len*/; bp = buf; NS_GET16(len, bp); len += 3; /* 2 length bytes + NUL. */ printf("%s:%d: freestr %d %s\n", file, line, len, str); __freestr_record(str, file, line); return; } #endif /* DEBUG_STRINGS */ /* * Return a counted string buffer big enough for a string of length 'len'. */ char * __newstr_record(size_t len, int needpanic, char *file, int line) { u_char *buf, *bp; REQUIRE(len <= 65536); buf = (u_char *)__memget_record(2/*Len*/ + len + 1/*Nul*/, file, line); if (buf == NULL) { if (needpanic) panic("savestr: memget failed (%s)", strerror(errno)); else return (NULL); } bp = buf; NS_PUT16(len, bp); return ((char *)bp); } /* * Save a NUL terminated string and return a pointer to it. */ char * __savestr_record(const char *str, int needpanic, char *file, int line) { char *buf; size_t len; len = strlen(str); if (len > 65536) { if (needpanic) ns_panic(ns_log_default, 1, "savestr: string too long"); else return (NULL); } buf = __newstr_record(len, needpanic, file, line); memcpy(buf, str, len + 1); return (buf); } void __freestr_record(char *str, char *file, int line) { u_char *buf, *bp; size_t len; buf = (u_char *)str - 2/*Len*/; bp = buf; NS_GET16(len, bp); __memput_record(buf, 2/*Len*/ + len + 1/*Nul*/, file, line); } char * checked_ctime(const time_t *t) { char *ctime_result; ctime_result = ctime(t); if (ctime_result == NULL) { ns_error(ns_log_default, "ctime() returned NULL!"); ctime_result = "\n"; } return (ctime_result); } /* * Since the fields in a "struct timeval" are longs, and the argument to ctime * is a pointer to a time_t (which might not be a long), here's a bridge. */ char * ctimel(long l) { time_t t = (time_t)l; return (checked_ctime(&t)); } /* * rename() is lame (can't overwrite an existing file) on some systems. * use movefile() instead, and let lame OS ports do what they need to. */ #ifndef HAVE_MOVEFILE int movefile(const char *oldname, const char *newname) { return (rename(oldname, newname)); +} +#endif + +#ifdef ultrix +/* + * Some library routines in libc need to be able to see the res_send + * and res_close symbols with out __ prefix otherwise we get multiply + * defined symbol errors when linking named. + */ + +#undef res_send +int res_send(const u_char *buf, int buflen, u_char *ans, int anssiz) { + return __res_send(buf, buflen, ans, anssiz); +} +#undef _res_close +void _res_close(void) { + __res_close(); } #endif Index: head/contrib/bind/bin/named/ns_init.c =================================================================== --- head/contrib/bind/bin/named/ns_init.c (revision 60940) +++ head/contrib/bind/bin/named/ns_init.c (revision 60941) @@ -1,564 +1,579 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_init.c 4.38 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: ns_init.c,v 8.63 1999/10/15 19:49:04 vixie Exp $"; +static const char rcsid[] = "$Id: ns_init.c,v 8.68 2000/04/21 06:54:07 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #ifdef DEBUG static void content_zone(int, int); #endif /* * Set new refresh time for zone. Use a random number in the last half of * the refresh limit; we want it to be substantially correct while still * preventing slave synchronization. */ void ns_refreshtime(struct zoneinfo *zp, time_t timebase) { u_long refresh = (zp->z_refresh > 0) ? zp->z_refresh : INIT_REFRESH; time_t half = (refresh + 1) / 2; zp->z_time = timebase + half + (rand() % half); } /* * Set new retry time for zone. */ void ns_retrytime(struct zoneinfo *zp, time_t timebase) { zp->z_time = timebase + zp->z_retry; } /* * Read configuration file and save it as internal state. */ void ns_init(const char *conffile) { struct zoneinfo *zp; static int loads = 0; /* number of times loaded */ ns_debug(ns_log_config, 1, "ns_init(%s)", conffile); gettime(&tt); if (loads == 0) { /* Init zone data. */ zones = NULL; INIT_LIST(freezones); INIT_LIST(reloadingzones); nzones = 0; make_new_zones(); /* Init cache. */ zones[0].z_type = z_cache; zones[0].z_origin = savestr("", 1); /* Allocate cache hash table, formerly the root hash table. */ hashtab = savehash((struct hashbuf *)NULL); /* Allocate root-hints/file-cache hash table. */ fcachetab = savehash((struct hashbuf *)NULL); /* Init other misc stuff. */ dst_init(); init_configuration(); } else { /* Mark previous zones as not yet found in boot file. */ block_signals(); for (zp = &zones[1]; zp < &zones[nzones]; zp++) if (zp->z_type != z_nil) { zp->z_flags &= ~Z_FOUND; if (LINKED(zp, z_reloadlink)) UNLINK(reloadingzones, zp, z_reloadlink); } unblock_signals(); } #ifdef DEBUG if (debug >= 3) { ns_debug(ns_log_config, 3, "content of zones before loading"); content_zone(nzones - 1, 3); } #endif load_configuration(conffile); /* Erase all old zones that were not found. */ for (zp = &zones[0]; zp < &zones[nzones]; zp++) { if (zp->z_type == z_cache) continue; if (zp->z_type != z_nil && (zp->z_flags & Z_FOUND) == 0) remove_zone(zp, "removed"); } /* Reload parent zones of zones removed */ for (zp = &zones[0]; zp < &zones[nzones]; zp++) { if (zp->z_type == z_cache) continue; if (zp->z_type != z_nil && (zp->z_flags & Z_PARENT_RELOAD) != 0) { zp->z_flags &= ~Z_PARENT_RELOAD; purgeandload(zp); } } #ifdef DEBUG if (debug >= 2) { ns_debug(ns_log_config, 2, "content of zones after loading"); content_zone(nzones-1, 2); } #endif ns_debug(ns_log_config, 1, "exit ns_init()"); loads++; } void zoneinit(struct zoneinfo *zp) { struct stat sb; int result; /* * Try to load zone from backup file, * if one was specified and it exists. * If not, or if the data are out of date, * we will refresh the zone from a primary * immediately. */ if (zp->z_source == NULL) return; result = stat(zp->z_source, &sb); if (result != -1) { ns_stopxfrs(zp); purge_zone(zp->z_origin, hashtab, zp->z_class); } if (result == -1 || db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR)) { /* * Set zone to be refreshed immediately. */ zp->z_refresh = INIT_REFRESH; zp->z_retry = INIT_REFRESH; if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) { zp->z_time = tt.tv_sec; sched_zone_maint(zp); } } else { zp->z_flags |= Z_AUTH; - zp->z_flags &= ~Z_NEED_RELOAD; + zp->z_flags &= ~(Z_NEED_RELOAD|Z_EXPIRED); ns_refreshtime(zp, tt.tv_sec); sched_zone_maint(zp); } } /* * Purge the zone and reload all parent zones. This needs to be done when * we unload a zone, since the child zone will have stomped the parent's * delegation to that child when it was first loaded. */ void do_reload(const char *domain, int type, int class, int mark) { struct zoneinfo *zp; ns_debug(ns_log_config, 1, "do_reload: %s %d %d %d", *domain ? domain : ".", type, class, mark); /* * Check if the zone has changed type. If so, we might not need to * do any purging or parent reloading. * * If the new zone is a master zone, then it will have purged the * old data and loaded, so we don't need to do anything. * * If the new zone is a slave or stub zone and has successfully loaded, * then we don't need to do anything either. * * NOTE: we take care not to match ourselves. */ zp = find_zone(domain, class); if (zp != NULL && (type != z_master && zp->z_type == z_master) || (type != z_slave && zp->z_type == z_slave && zp->z_serial != 0) || (type != z_stub && zp->z_type == z_stub && zp->z_serial != 0)) return; /* * Clean up any leftover data. */ + ns_stopxfrs(zp); purge_zone(domain, hashtab, class); /* * Reload */ while (*domain) { const char *s; int escaped; /* * XXX this is presentation level hair and belongs elsewhere. */ escaped = 0; for (s = domain; *s != '\0'; s++) { if (!escaped) { if (*s == '.') break; else if (*s == '\\') escaped = 1; } else escaped = 0; } if (*s != '\0') domain = s + 1; /* skip label and its separator */ else domain = ""; /* root zone */ zp = find_zone(domain, class); if (zp != NULL) { ns_debug(ns_log_config, 1, "do_reload: matched %s", *domain ? domain : "."); if (mark) zp->z_flags |= Z_PARENT_RELOAD; else purgeandload(zp); break; } } } void purgeandload(struct zoneinfo *zp) { + +#ifdef BIND_UPDATE + /* + * A dynamic zone might have changed, so we + * need to dump it before removing it. + */ + if (zp->z_type == Z_PRIMARY && + (zp->z_flags & Z_DYNAMIC) != 0 && + ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || + (zp->z_flags & Z_NEED_DUMP) != 0)) + (void) zonedump(zp, ISNOTIXFR); +#endif + ns_stopxfrs(zp); + if (zp->z_type == Z_HINT) purge_zone(zp->z_origin, fcachetab, zp->z_class); else purge_zone(zp->z_origin, hashtab, zp->z_class); zp->z_flags &= ~Z_AUTH; switch (zp->z_type) { case Z_SECONDARY: case Z_STUB: zoneinit(zp); break; case Z_PRIMARY: if (db_load(zp->z_source, zp->z_origin, zp, 0, ISNOTIXFR) == 0) zp->z_flags |= Z_AUTH; break; case Z_HINT: case Z_CACHE: (void)db_load(zp->z_source, zp->z_origin, zp, 0, ISNOTIXFR); break; } } #ifdef DEBUG /* prints out the content of zones */ static void content_zone(int end, int level) { int i; for (i = 0; i <= end; i++) { printzoneinfo(i, ns_log_config, level); } } #endif enum context ns_ptrcontext(owner) const char *owner; { if (ns_samedomain(owner, "in-addr.arpa") || ns_samedomain(owner, "ip6.int")) return (hostname_ctx); return (domain_ctx); } enum context ns_ownercontext(type, transport) int type; enum transport transport; { enum context context = domain_ctx; switch (type) { case T_A: case T_WKS: case T_MX: switch (transport) { case update_trans: case primary_trans: case secondary_trans: context = owner_ctx; break; case response_trans: context = hostname_ctx; break; default: panic("impossible condition in ns_ownercontext()", NULL); } break; case T_MB: case T_MG: context = mailname_ctx; break; default: /* Nothing to do. */ break; } return (context); } int ns_nameok(const struct qinfo *qry, const char *name, int class, struct zoneinfo *zp, enum transport transport, enum context context, const char *owner, struct in_addr source) { enum severity severity = not_set; int ok = 1; if (zp != NULL) severity = zp->z_checknames; if (severity == not_set) severity = server_options->check_names[transport]; if (severity == ignore) return (1); switch (context) { case domain_ctx: ok = (class != C_IN) || res_dnok(name); break; case owner_ctx: ok = (class != C_IN) || res_ownok(name); break; case mailname_ctx: ok = res_mailok(name); break; case hostname_ctx: ok = res_hnok(name); break; default: ns_panic(ns_log_default, 1, "unexpected context %d in ns_nameok", (int)context); } if (!ok) { char *q, *s, *o; if (source.s_addr == INADDR_ANY) s = savestr(transport_strings[transport], 0); else { s = newstr(strlen(transport_strings[transport]) + sizeof " from [000.000.000.000] for [000.000.000.000]", 0); if (s) if ( (transport == response_trans) && (qry != NULL) ) { if ( qry->q_flags & Q_PRIMING ) { sprintf(s, "%s from [%s] for priming", transport_strings[transport], inet_ntoa(source)); } else if ( qry->q_flags & Q_ZSERIAL ) { sprintf(s, "%s from [%s] for soacheck", transport_strings[transport], inet_ntoa(source)); } else if ( qry->q_flags & Q_SYSTEM ) { sprintf(s, "%s from [%s] for sysquery", transport_strings[transport], inet_ntoa(source)); } else { q=strdup(inet_ntoa(qry->q_from.sin_addr)); sprintf(s, "%s from [%s] for [%s]", transport_strings[transport], inet_ntoa(source), q != NULL ? q : "memget failed"); free(q); } } else { sprintf(s, "%s from [%s]", transport_strings[transport], inet_ntoa(source)); } } if (ns_samename(owner, name) == 1) o = savestr("", 0); else { const char *t = (*owner == '\0') ? "." : owner; o = newstr(strlen(t) + sizeof " (owner \"\")", 0); if (o) sprintf(o, " (owner \"%s\")", t); } /* * We use log_write directly here to avoid duplicating * the message formatting and arguments. */ log_write(log_ctx, ns_log_default, (transport != response_trans) || (o == NULL) || (s == NULL) || ( (qry != NULL) && (qry->q_flags & (Q_PRIMING|Q_ZSERIAL)) ) ? log_warning : log_info, "%s name \"%s\"%s %s (%s) is invalid - %s", context_strings[context], name, o != NULL ? o : "[memget failed]", p_class(class), s != NULL ? s : "[memget failed]", (severity == fail) ? "rejecting" : "proceeding anyway"); if (severity == warn) ok = 1; if (s != NULL) freestr(s); if (o != NULL) freestr(o); } return (ok); } int ns_wildcard(const char *name) { if (*name != '*') return (0); return (*++name == '\0'); } void ns_shutdown() { struct zoneinfo *zp; #ifdef BIND_NOTIFY ns_unnotify(); #endif /* Erase zones. */ for (zp = &zones[0]; zp < &zones[nzones]; zp++) { if (zp->z_type) { if (zp->z_type != z_hint && zp->z_type != z_cache) { ns_stopxfrs(zp); purge_zone(zp->z_origin, hashtab, zp->z_class); } else if (zp->z_type == z_hint) purge_zone(zp->z_origin, fcachetab, zp->z_class); free_zone_contents(zp, 1); } } /* Erase the cache. */ clean_cache(hashtab, 1); hashtab->h_cnt = 0; /* ??? */ rm_hash(hashtab); hashtab = NULL; clean_cache(fcachetab, 1); fcachetab->h_cnt = 0; /* ??? */ rm_hash(fcachetab); fcachetab = NULL; if (zones != NULL) memput(zones, nzones * sizeof *zones); zones = NULL; freeComplaints(); shutdown_configuration(); } Index: head/contrib/bind/bin/named/ns_ixfr.c =================================================================== --- head/contrib/bind/bin/named/ns_ixfr.c (revision 60940) +++ head/contrib/bind/bin/named/ns_ixfr.c (revision 60941) @@ -1,563 +1,612 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_ixfr.c,v 8.17 1999/11/05 04:48:28 vixie Exp $"; +static const char rcsid[] = "$Id: ns_ixfr.c,v 8.19 2000/04/18 20:47:27 vixie Exp $"; #endif /* not lint */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static void sx_new_ixfrmsg(struct qstream * qsp); void sx_send_ixfr(struct qstream * qsp); static int sx_flush(struct qstream * qsp), sx_addrr(struct qstream * qsp, const char *dname, struct databuf * dp); -extern void sx_sendsoa(struct qstream * qsp); /* * u_char * sx_new_ixfrmsg(msg) init the header of a message, reset the * compression pointers, and reset the write pointer to the first byte * following the header. */ static void sx_new_ixfrmsg(struct qstream *qsp) { HEADER * hp = (HEADER *) qsp->xfr.msg; - ns_updrec * up; memset(hp, 0, HFIXEDSZ); hp->id = htons(qsp->xfr.id); hp->opcode = qsp->xfr.opcode; hp->qr = 1; hp->aa = 1; hp->rcode = NOERROR; qsp->xfr.ptrs[0] = qsp->xfr.msg; qsp->xfr.ptrs[1] = NULL; qsp->xfr.cp = qsp->xfr.msg + HFIXEDSZ; if (qsp->xfr.ixfr_zone == 0) { int count, n; int buflen; struct namebuf *np; struct hashbuf *htp; struct zoneinfo *zp; struct databuf *dp; const char * fname; u_char ** edp = qsp->xfr.ptrs + sizeof qsp->xfr.ptrs / sizeof(u_char *); qsp->xfr.ixfr_zone = qsp->xfr.zone; zp = &zones[qsp->xfr.zone]; - up = qsp->xfr.top.ixfr; n = dn_comp(zp->z_origin, qsp->xfr.cp, XFER_BUFSIZE - (qsp->xfr.cp - qsp->xfr.msg), NULL, NULL); qsp->xfr.cp += n; PUTSHORT((u_int16_t) T_IXFR, qsp->xfr.cp); PUTSHORT((u_int16_t) zp->z_class, qsp->xfr.cp); hp->qdcount = htons(ntohs(hp->qdcount) + 1); count = qsp->xfr.cp - qsp->xfr.msg; htp = hashtab; np = nlookup(zp->z_origin, &htp, &fname, 0); buflen = XFER_BUFSIZE; foreach_rr(dp, np, T_SOA, qsp->xfr.class, qsp->xfr.zone) { n = make_rr(zp->z_origin, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); qsp->xfr.cp += n; hp->ancount = htons(ntohs(hp->ancount) + 1); } } } /* - * int sx_flush(qsp) flush the intermediate buffer out to the stream IO - * system. return: passed through from sq_write(). + * int + * sx_flush(qsp) + * flush the intermediate buffer out to the stream IO system. + * return: + * passed through from sq_write(). */ static int sx_flush(struct qstream *qsp) { int ret; #ifdef DEBUG if (debug >= 10) fp_nquery(qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg, log_get_stream(packet_channel)); #endif - ret = sq_write(qsp, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg); - if (ret >= 0) + if (qsp->xfr.tsig_state != NULL && qsp->xfr.tsig_skip == 0) { + int msglen = qsp->xfr.cp - qsp->xfr.msg; + + ns_sign_tcp(qsp->xfr.msg, &msglen, qsp->xfr.eom - qsp->xfr.msg, + NOERROR, qsp->xfr.tsig_state, + qsp->xfr.state == s_x_done); + + if (qsp->xfr.state == s_x_done) { + memput(qsp->xfr.tsig_state, sizeof(ns_tcp_tsig_state)); + qsp->xfr.tsig_state = NULL; + } + qsp->xfr.cp = qsp->xfr.msg + msglen; + + } + if (qsp->xfr.cp - qsp->xfr.msg > 0) + ret = sq_write(qsp, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg); + else { + ns_debug(ns_log_default, 3, " Flush negative number *********"); + ret = -1; + } + if (ret >= 0) { qsp->xfr.cp = NULL; + qsp->xfr.tsig_skip = 0; + } + else + qsp->xfr.tsig_skip = 1; return (ret); } - /* * int sx_addrr(qsp, name, dp) add name/dp's RR to the current assembly * message. if it won't fit, write current message out, renew the message, * and then RR should fit. return: -1 = the sq_write() failed so we could not * queue the full message. 0 = one way or another, everything is fine. side * effects: on success, the ANCOUNT is incremented and the pointers are * advanced. */ static int sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { HEADER *hp = (HEADER *) qsp->xfr.msg; u_char **edp = qsp->xfr.ptrs + sizeof qsp->xfr.ptrs / sizeof(u_char *); int n; if (qsp->xfr.cp != NULL) { if (qsp->xfr.transfer_format == axfr_one_answer && sx_flush(qsp) < 0) return (-1); } if (qsp->xfr.cp == NULL) sx_new_ixfrmsg(qsp); n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); if (n < 0) { if (sx_flush(qsp) < 0) return (-1); if (qsp->xfr.cp == NULL) sx_new_ixfrmsg(qsp); n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); INSIST(n >= 0); } hp->ancount = htons(ntohs(hp->ancount) + 1); qsp->xfr.cp += n; return (0); } void sx_send_ixfr(struct qstream *qsp) { char * cp; u_int32_t serial = 0; struct zoneinfo *zp = NULL; struct databuf *soa_dp; struct databuf *old_soadp; - ns_updrec * rp; - ns_updrec * trp; + ns_delta *dp; + ns_updrec *rp; + ns_updrec *trp; int foundsoa; zp = &zones[qsp->xfr.zone]; soa_dp = (struct databuf *) findzonesoa(zp); if (soa_dp == NULL) { /* XXX should be more graceful */ ns_panic(ns_log_update, 1, "sx_send_ixfr: unable to locate soa"); } old_soadp = memget(DATASIZE(soa_dp->d_size)); memcpy(old_soadp, soa_dp, DATASIZE(soa_dp->d_size)); again: switch (qsp->xfr.state) { case s_x_firstsoa: + ns_debug(ns_log_default, 3, + "IXFR: s_x_firstsoa (%s)", zp->z_origin); /* * The current SOA has been emited already. * It would be cleaner if the first one was emited here... * * if (sx_addrr(qsp, zp->z_origin, soa_dp) < 0) * goto cleanup; */ qsp->xfr.state = s_x_deletesoa; /* FALLTHROUGH */ case s_x_deletesoa: - if (qsp->xfr.top.ixfr) { + ns_debug(ns_log_default, 3, + "IXFR: s_x_deletesoa (%s)", zp->z_origin); + dp = NULL; + if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) + dp = HEAD(*qsp->xfr.top.ixfr); + if (dp != NULL) { foundsoa = 0; - rp = qsp->xfr.top.ixfr; - while (PREV(rp, r_link) != NULL) - rp = PREV(rp, r_link); + + rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == DELETE && rp->r_dp != NULL && rp->r_dp->d_type == T_SOA) { if (sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_freedata(rp->r_dp); rp->r_dp = NULL; foundsoa = 1; break; } - trp = rp; rp = NEXT(rp, r_link); } if (!foundsoa) { cp = (char *)findsoaserial(old_soadp->d_data); - PUTLONG(qsp->xfr.top.ixfr->r_zone, cp); + PUTLONG(HEAD(dp->d_changes)->r_zone, cp); if (sx_addrr(qsp, zp->z_origin, old_soadp) < 0) goto cleanup; } } qsp->xfr.state = s_x_deleting; /* FALLTHROUGH */ case s_x_deleting: - if (qsp->xfr.top.ixfr) { - /* - * The order s important here. - * Go to start of this update via PREV(r_link) - * then extract all deletions. - */ - rp = qsp->xfr.top.ixfr; - while (PREV(rp, r_link) != NULL) - rp = PREV(rp, r_link); + ns_debug(ns_log_default, 3, + "IXFR: s_x_deleting (%s)", zp->z_origin); + dp = NULL; + if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) + dp = HEAD(*qsp->xfr.top.ixfr); + if (dp != NULL) { + rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == DELETE && rp->r_dp != NULL) { /* * Drop any SOA deletes */ if (rp->r_dp->d_type != T_SOA && sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_freedata(rp->r_dp); rp->r_dp = NULL; } - trp = rp; rp = NEXT(rp, r_link); } } qsp->xfr.state = s_x_addsoa; /* FALLTHROUGH */ case s_x_addsoa: - if (qsp->xfr.top.ixfr) { + ns_debug(ns_log_default, 3, + "IXFR: s_x_addsoa (%s)", zp->z_origin); + dp = NULL; + if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) + dp = HEAD(*qsp->xfr.top.ixfr); + if (dp != NULL) { foundsoa = 0; - rp = qsp->xfr.top.ixfr; - while (PREV(rp, r_link) != NULL) - rp = PREV(rp, r_link); + rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == ADD && rp->r_dp != NULL && rp->r_dp->d_type == T_SOA) { if (sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_freedata(rp->r_dp); rp->r_dp = NULL; foundsoa = 1; break; } - trp = rp; rp = NEXT(rp, r_link); } if (!foundsoa) { cp = (char *)findsoaserial(old_soadp->d_data); - if (NEXT(qsp->xfr.top.ixfr, r_link) != NULL) { - trp = qsp->xfr.top.ixfr; - PUTLONG(NEXT(trp, r_link)->r_zone, cp); + if (NEXT(dp, d_link) != NULL) { + PUTLONG(HEAD(dp->d_changes)->r_zone, cp); if (sx_addrr(qsp, zp->z_origin, old_soadp) < 0) goto cleanup; } else { if (sx_addrr(qsp, zp->z_origin, soa_dp) < 0) goto cleanup; } } } qsp->xfr.state = s_x_adding; /* FALLTHROUGH */ case s_x_adding: - if (qsp->xfr.top.ixfr) { - /* see s_x_deleting */ - rp = qsp->xfr.top.ixfr; - while (PREV(rp, r_link) != NULL) - rp = PREV(rp, r_link); - while (rp != NULL) { - if (rp->r_opcode == ADD && - rp->r_dp != NULL && - rp->r_dp->d_type != T_SOA) { - if (sx_addrr(qsp, rp->r_dname, - rp->r_dp) < 0) - goto cleanup; - db_freedata(rp->r_dp); - rp->r_dp = NULL; + ns_debug(ns_log_default, 3, + "IXFR: s_x_adding (%s)", zp->z_origin); + dp = NULL; + if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) { + dp = HEAD(*qsp->xfr.top.ixfr); + if (dp != NULL) { + /* see s_x_deleting */ + rp = HEAD(dp->d_changes); + while (rp != NULL) { + if (rp->r_opcode == ADD && + rp->r_dp != NULL && + rp->r_dp->d_type != T_SOA) { + if (sx_addrr(qsp, rp->r_dname, + rp->r_dp) < 0) + goto cleanup; + db_freedata(rp->r_dp); + rp->r_dp = NULL; + } + rp = NEXT(rp, r_link); } - trp = rp; - rp = NEXT(rp, r_link); - } - /* move to next update */ - rp = qsp->xfr.top.ixfr; - qsp->xfr.top.ixfr = NEXT(rp, r_link); - PREV(rp, r_link) = NULL; - /* clean up old update */ - while (rp != NULL) { - trp = PREV(rp, r_link); - if (rp->r_dp != NULL) { - db_freedata(rp->r_dp); - rp->r_dp = NULL; + /* move to next update */ + UNLINK(*qsp->xfr.top.ixfr, dp, d_link); + + /* clean up old update */ + while ((rp = HEAD(dp->d_changes)) != NULL) { + UNLINK(dp->d_changes, rp, r_link); + if (rp->r_dp != NULL) { + db_freedata(rp->r_dp); + rp->r_dp = NULL; + } + res_freeupdrec(rp); } - res_freeupdrec(rp); - rp = trp; + memput(dp, sizeof (*dp)); + if (HEAD(*qsp->xfr.top.ixfr) != NULL) { + qsp->xfr.state = s_x_deletesoa; + goto again; + } } } qsp->xfr.state = s_x_lastsoa; /* FALLTHROUGH */ case s_x_lastsoa: - if (qsp->xfr.ixfr_zone != 0) { + ns_debug(ns_log_default, 3, + "IXFR: s_x_lastsoa (%s)", zp->z_origin); + if (qsp->xfr.ixfr_zone != 0) sx_addrr(qsp, zp->z_origin, soa_dp); - } break; } + ns_debug(ns_log_default, 3, "IXFR: flushing %s", zp->z_origin); qsp->xfr.state = s_x_done; sx_flush(qsp); sq_writeh(qsp, sq_flushw); cleanup: + if (qsp->xfr.top.ixfr != NULL) { + if(!EMPTY(*qsp->xfr.top.ixfr)) { + while ((dp = HEAD(*qsp->xfr.top.ixfr)) != NULL) { + UNLINK(*qsp->xfr.top.ixfr, dp, d_link); + while ((rp = HEAD(dp->d_changes)) != NULL) { + UNLINK(dp->d_changes, rp, r_link); + if (rp->r_dp != NULL) + db_freedata(rp->r_dp); + rp->r_dp = NULL; + res_freeupdrec(rp); + } + memput(dp, sizeof *dp); + } + } + memput(qsp->xfr.top.ixfr, sizeof *qsp->xfr.top.ixfr); + qsp->xfr.top.ixfr = NULL; + } memput(old_soadp, DATASIZE(old_soadp->d_size)); } #ifndef MAXBSIZE #define MAXBSIZE 8192 #endif -int ixfr_log_maint(struct zoneinfo *zp) { - int fd, rcount, wcount, rval; - int found = 0, seek = 0; +/* + * int ixfr_log_maint(struct zoneinfo *zp, int fast_trim) + * + * zp - pointer to the zone information + * fast_trim - is used to denote that this is not called on the regular + * maintaince cycle. + * + */ +int ixfr_log_maint(struct zoneinfo *zp, int fast_trim) { + int fd, rcount, wcount; + int found = 0; + int error = 0; + long seek = 0; FILE *to_fp, *from_fp, *db_fp; static char *tmpname; struct stat db_sb; struct stat sb; static char buf[MAXBSIZE]; ns_debug(ns_log_default, 3, "ixfr_log_maint(%s)", zp->z_origin); - tmpname = memget(strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1); - if (!tmpname) { - ns_warning(ns_log_default, "memget failed"); - return (-1); - } -#ifdef SHORT_FNAMES - filenamecpy(tmpname, zp->z_ixfr_base); -#else - (void) strcpy(tmpname, zp->z_ixfr_base); -#endif /* SHORT_FNAMES */ - - (void) strcat(tmpname, ".XXXXXX"); - if ((fd = mkstemp(tmpname)) == -1) { - ns_warning(ns_log_db, "can't make tmpfile (%s): %s", - strerror(errno)); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - return (-1); - } - if ((to_fp = fdopen(fd, "r+")) == NULL) { - ns_warning(ns_log_db, "%s: %s", - tmpname, strerror(errno)); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - (void) close(fd); - return (-1); - } /* find out how big the zone db file is */ if ((db_fp = fopen(zp->z_source, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_source, strerror(errno)); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - (void) my_fclose(to_fp); - (void) close(fd); return (-1); } if (fstat(fileno(db_fp), &db_sb) < 0) { ns_warning(ns_log_db, "%s: %s", zp->z_source, strerror(errno)); - (void) my_fclose(to_fp); (void) my_fclose(db_fp); - (void) close(fd); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); return (-1); } (void) my_fclose(db_fp); ns_debug(ns_log_default, 3, "%s, size %d blk %d", zp->z_source, db_sb.st_size, db_sb.st_size); /* open up the zone ixfr log */ - if ((from_fp = fopen(zp->z_ixfr_base, "r")) == NULL) { + if ((from_fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); - (void) my_fclose(to_fp); - (void) close(fd); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); return (-1); } if (fstat(fileno(from_fp), &sb) < 0) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); - (void) my_fclose(to_fp); - (void) close(fd); - (void) unlink(tmpname); (void) my_fclose(from_fp); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); return (-1); } - ns_debug(ns_log_default, 3, "%s, size %d log_s %d max %d\n", + ns_debug(ns_log_default, 3, "%s, size %d max %d\n", zp->z_ixfr_base, sb.st_size, - zp->z_log_size_ixfr, zp->z_max_log_size_ixfr); if (zp->z_max_log_size_ixfr) { if (sb.st_size > zp->z_max_log_size_ixfr) - seek = sb.st_size - (zp->z_max_log_size_ixfr + (zp->z_max_log_size_ixfr *.10)); + seek = (signed)sb.st_size - + (signed)(zp->z_max_log_size_ixfr + + (zp->z_max_log_size_ixfr * .10) ); else seek = 0; } else { if (sb.st_size > (db_sb.st_size * .50)) - seek = sb.st_size - ((db_sb.st_size * .50) + seek = (signed)sb.st_size - (signed)((db_sb.st_size * .50) + ((db_sb.st_size * zp->z_max_log_size_ixfr) *.10)); else seek = 0; } ns_debug(ns_log_default, 3, "seek: %d", seek); if (seek < 1) { ns_debug(ns_log_default, 3, "%s does not need to be reduced", zp->z_ixfr_base); - (void) my_fclose(to_fp); - (void) close(fd); - (void) unlink(tmpname); (void) my_fclose(from_fp); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); return (-1); } + if ((fast_trim) && seek < (zp->z_max_log_size_ixfr + 100000)) { + (void) my_fclose(from_fp); + return (0); + } + tmpname = memget(strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1); + if (!tmpname) { + ns_warning(ns_log_default, "memget failed"); + return (-1); + } +#ifdef SHORT_FNAMES + filenamecpy(tmpname, zp->z_ixfr_base); +#else + (void) strcpy(tmpname, zp->z_ixfr_base); +#endif /* SHORT_FNAMES */ + + (void) strcat(tmpname, ".XXXXXX"); + if ((fd = mkstemp(tmpname)) == -1) { + ns_warning(ns_log_db, "can't make tmpfile (%s): %s", + strerror(errno)); + memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); + return (-1); + } + if ((to_fp = fdopen(fd, "r+")) == NULL) { + ns_warning(ns_log_db, "%s: %s", + tmpname, strerror(errno)); + (void) unlink(tmpname); + memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); + (void) close(fd); + return (-1); + } + if (fgets(buf, sizeof(buf), from_fp) == NULL) { ns_error(ns_log_update, "fgets() from %s failed: %s", zp->z_ixfr_base, strerror(errno)); - (void) my_fclose(from_fp); - (void) my_fclose(to_fp); - (void) close(fd); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - return (-1); + error++; + goto clean_up; } if (strcmp(buf, LogSignature) != 0) { ns_error(ns_log_update, "invalid log file %s", zp->z_ixfr_base); - (void) my_fclose(from_fp); - (void) my_fclose(to_fp); - (void) close(fd); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - return (-3); + error++; + goto clean_up; } - if (fseek( from_fp, seek, 0) < 0) { - (void) my_fclose(from_fp); - (void) my_fclose(to_fp); - (void) close(fd); - (void) unlink(tmpname); - memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - return (-1); + if (fseek( from_fp, seek, 0) < 0) { + error++; + goto clean_up; } found = 0; for (;;) { if (getword(buf, sizeof buf, from_fp, 0)) { if (strcasecmp(buf, "[END_DELTA]") == 0) { if (!(fgets(buf, 2, from_fp) == NULL)) /* eat */ found = 1; break; } } if (feof(from_fp)) break; } if (found) { ns_debug(ns_log_default, 1, "ixfr_log_maint(): found [END_DELTA]"); + fprintf(to_fp, "%s", LogSignature); + while ((rcount = fread(buf, sizeof(char), MAXBSIZE, from_fp)) > 0) { wcount = fwrite(buf, sizeof(char), rcount, to_fp); if (rcount != wcount || wcount == -1) { ns_warning(ns_log_default, "ixfr_log_maint: error in writting copy"); - rval = 1; break; } - } - if (rcount < 0) { - ns_warning(ns_log_default, "ixfr_log_maint: error in reading copy"); - rval = 1; } + if (rcount < 0) + ns_warning(ns_log_default, + "ixfr_log_maint: error in reading copy"); } + clean_up: (void) my_fclose(to_fp); (void) close(fd); (void) my_fclose(from_fp); - if (rename(tmpname, zp->z_ixfr_base) == -1) { - ns_warning(ns_log_default, "can not rename %s to %s :%s", - tmpname, zp->z_ixfr_base, strerror(errno)); + if (error == 0) { + if (rename(tmpname, zp->z_ixfr_base) == -1) { + ns_warning(ns_log_default, "can not rename %s to %s :%s", + tmpname, zp->z_ixfr_base, strerror(errno)); + } + if ((from_fp = fopen(zp->z_ixfr_base, "r")) == NULL) { + ns_warning(ns_log_db, "%s: %s", + zp->z_ixfr_base, strerror(errno)); + return (-1); + } + if (fstat(fileno(from_fp), &sb) < 0) { + ns_warning(ns_log_db, "%s: %s", + zp->z_ixfr_base, strerror(errno)); + (void) my_fclose(from_fp); + return (-1); + } + if (sb.st_size <= 0) + (void) unlink(zp->z_ixfr_base); + else if (chmod(zp->z_ixfr_base, 0644) < 0) + ns_error(ns_log_update, + "chmod(%s,%o) failed, pressing on: %s", + zp->z_source, sb.st_mode, + strerror(errno)); } (void) unlink(tmpname); memput(tmpname, (strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1)); - if ((from_fp = fopen(zp->z_ixfr_base, "r")) == NULL) { - ns_warning(ns_log_db, "%s: %s", - zp->z_ixfr_base, strerror(errno)); - return (-1); - } - if (fstat(fileno(from_fp), &sb) < 0) { - ns_warning(ns_log_db, "%s: %s", - zp->z_ixfr_base, strerror(errno)); - (void) my_fclose(from_fp); - return (-1); - } - if (sb.st_size <= 0) - (void) unlink(zp->z_ixfr_base); (void) my_fclose(from_fp); - ns_debug(ns_log_default, 3, "%s, size %d log_s %d max %d\n", + zp->z_serial_ixfr_start = 0; /* signal to read for lowest serial number */ + + ns_debug(ns_log_default, 3, "%s, size %d max %d\n", zp->z_ixfr_base, sb.st_size, - zp->z_log_size_ixfr, zp->z_max_log_size_ixfr); - return (0); + + if (error) + return(-1); + else + return (0); } + Index: head/contrib/bind/bin/named/ns_lexer.c =================================================================== --- head/contrib/bind/bin/named/ns_lexer.c (revision 60940) +++ head/contrib/bind/bin/named/ns_lexer.c (revision 60941) @@ -1,809 +1,809 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_lexer.c,v 8.19 1999/10/13 16:39:08 vixie Exp $"; +static const char rcsid[] = "$Id: ns_lexer.c,v 8.20 2000/04/21 06:54:07 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #include "ns_parser.h" #include "ns_parseutil.h" #include "ns_lexer.h" typedef enum lexer_state { scan, number, identifier, ipv4, quoted_string } LexerState; #define LEX_EOF 0x01 #define LEXER_MAX_PUSHBACK 2 typedef struct lexer_file_context { const char * name; FILE * stream; int line_number; LexerState state; u_int flags; int warnings; int errors; u_int pushback_count; char pushback[LEXER_MAX_PUSHBACK]; struct lexer_file_context * next; } *LexerFileContext; LexerFileContext current_file = NULL; #define LEX_LAST_WAS_DOT 0x01 #define LEX_CONSECUTIVE_DOTS 0x02 typedef struct lexer_identifier { char buffer[LEX_MAX_IDENT_SIZE+1]; int index; int num_dots; unsigned int flags; } *LexerIdentifier; static LexerIdentifier id; static char special_chars[256]; #define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') #define domain_char(c) (isalnum((c)) || (c) == '.' || (c) == '-') #define special_char(c) (special_chars[(c)] == 1) #define identifier_char(c) (!whitespace(c) && !special_char(c)) static int last_token; static YYSTYPE last_yylval; static int lexer_initialized = 0; /* * Problem Reporting */ static char * token_to_text(int token, YYSTYPE lval) { static char buffer[LEX_MAX_IDENT_SIZE+50]; if (token < 128) { if (token == 0) strcpy(buffer, ""); else sprintf(buffer, "'%c'", token); } else { switch (token) { case L_EOS: strcpy(buffer, ";"); break; case L_STRING: sprintf(buffer, "'%s'", lval.cp); break; case L_QSTRING: sprintf(buffer, "\"%s\"", lval.cp); break; case L_IPADDR: sprintf(buffer, "%s", inet_ntoa(lval.ip_addr)); break; case L_NUMBER: sprintf(buffer, "%ld", lval.num); break; case L_END_INCLUDE: sprintf(buffer, ""); break; default: sprintf(buffer, "%s", lval.cp); } } return (buffer); } static char where[MAXPATHLEN + 100]; static char message[20480]; static void parser_complain(int is_warning, int print_last_token, const char *format, va_list args) { LexerFileContext lf; int severity; if (is_warning) { severity = log_warning; } else { severity = log_error; } INSIST(current_file != NULL); if (current_file->next != NULL) { for (lf = current_file; lf != NULL; lf = lf->next) { log_write(log_ctx, ns_log_parser, severity, "%s '%s' line %d", (lf == current_file) ? "In" : "included from", lf->name, lf->line_number); } } sprintf(where, "%s:%d: ", current_file->name, current_file->line_number); vsprintf(message, format, args); if (print_last_token) log_write(log_ctx, ns_log_parser, severity, "%s%s near %s", where, message, token_to_text(last_token, last_yylval)); else log_write(log_ctx, ns_log_parser, severity, "%s%s", where, message); } int parser_warning(int print_last_token, const char *format, ...) { va_list args; va_start(args, format); parser_complain(1, print_last_token, format, args); va_end(args); current_file->warnings++; return (1); } int parser_error(int print_last_token, const char *format, ...) { va_list args; va_start(args, format); parser_complain(0, print_last_token, format, args); va_end(args); current_file->errors++; return (1); } void yyerror(const char *message) { parser_error(1, message); } /* * Keywords */ struct keyword { char *name; int token; }; /* * "keywords" is an array of the keywords which are the fixed syntactic * elements of the configuration file. Each keyword has a string version * of the keyword and a token id, which should be an identifier which * matches that in a %token statement inside the parser.y file. */ static struct keyword keywords[] = { {"acl", T_ACL}, {"address", T_ADDRESS}, {"algorithm", T_ALGID}, {"allow", T_ALLOW}, {"allow-query", T_ALLOW_QUERY}, {"allow-recursion", T_ALLOW_RECURSION}, {"allow-transfer", T_ALLOW_TRANSFER}, {"allow-update", T_ALLOW_UPDATE}, #ifdef BIND_NOTIFY {"also-notify", T_ALSO_NOTIFY}, #endif {"auth-nxdomain", T_AUTH_NXDOMAIN}, {"blackhole", T_BLACKHOLE}, {"bogus", T_BOGUS}, {"category", T_CATEGORY}, {"class", T_CLASS}, {"channel", T_CHANNEL}, {"check-names", T_CHECK_NAMES}, {"cleaning-interval", T_CLEAN_INTERVAL}, {"controls", T_CONTROLS}, {"coresize", T_CORESIZE}, {"datasize", T_DATASIZE}, {"deallocate-on-exit", T_DEALLOC_ON_EXIT}, {"debug", T_DEBUG}, {"default", T_DEFAULT}, {"dialup", T_DIALUP}, {"directory", T_DIRECTORY}, {"dump-file", T_DUMP_FILE}, {"dynamic", T_DYNAMIC}, {"fail", T_FAIL}, {"fake-iquery", T_FAKE_IQUERY}, {"false", T_FALSE}, {"fetch-glue", T_FETCH_GLUE}, {"file", T_FILE}, {"files", T_FILES}, {"first", T_FIRST}, {"forward", T_FORWARD}, {"forwarders", T_FORWARDERS}, {"group", T_GROUP}, {"has-old-clients", T_HAS_OLD_CLIENTS}, {"heartbeat-interval", T_HEARTBEAT}, {"hint", T_HINT}, {"host-statistics", T_HOSTSTATS}, {"if-no-answer", T_IF_NO_ANSWER}, {"if-no-domain", T_IF_NO_DOMAIN}, {"ignore", T_IGNORE}, {"include", T_INCLUDE}, {"inet", T_INET}, {"interface-interval", T_INTERFACE_INTERVAL}, {"ixfr-base", T_FILE_IXFR}, {"ixfr-tmp-file", T_IXFR_TMP}, {"key", T_SEC_KEY}, {"keys", T_KEYS}, {"lame-ttl", T_LAME_TTL}, {"listen-on", T_LISTEN_ON}, {"logging", T_LOGGING}, {"maintain-ixfr-base", T_MAINTAIN_IXFR_BASE}, {"many-answers", T_MANY_ANSWERS}, {"master", T_MASTER}, {"masters", T_MASTERS}, {"max-ixfr-log-size", T_MAX_LOG_SIZE_IXFR}, {"max-ncache-ttl", T_MAX_NCACHE_TTL}, {"max-transfer-time-in", T_MAX_TRANSFER_TIME_IN}, {"memstatistics-file", T_MEMSTATS_FILE}, {"min-roots", T_MIN_ROOTS}, {"multiple-cnames", T_MULTIPLE_CNAMES}, {"name", T_NAME}, {"named-xfer", T_NAMED_XFER}, {"no", T_NO}, #ifdef BIND_NOTIFY {"notify", T_NOTIFY}, #endif {"null", T_NULL_OUTPUT}, {"one-answer", T_ONE_ANSWER}, {"only", T_ONLY}, {"order", T_ORDER}, {"options", T_OPTIONS}, {"owner", T_OWNER}, {"perm", T_PERM}, {"pid-file", T_PIDFILE}, {"port", T_PORT}, {"print-category", T_PRINT_CATEGORY}, {"print-severity", T_PRINT_SEVERITY}, {"print-time", T_PRINT_TIME}, {"pubkey", T_PUBKEY}, {"query-source", T_QUERY_SOURCE}, {"rfc2308-type1", T_RFC2308_TYPE1}, {"rrset-order", T_RRSET_ORDER}, {"recursion", T_RECURSION}, {"response", T_RESPONSE}, {"secret", T_SECRET}, {"serial-queries", T_SERIAL_QUERIES}, {"server", T_SERVER}, {"severity", T_SEVERITY}, {"size", T_SIZE}, {"slave", T_SLAVE}, {"sortlist", T_SORTLIST}, {"stacksize", T_STACKSIZE}, {"statistics-file", T_STATS_FILE}, {"statistics-interval", T_STATS_INTERVAL}, {"stub", T_STUB}, {"support-ixfr", T_SUPPORT_IXFR}, {"syslog", T_SYSLOG}, {"topology", T_TOPOLOGY}, {"transfer-format", T_TRANSFER_FORMAT}, {"transfer-source", T_TRANSFER_SOURCE}, {"transfers", T_TRANSFERS}, {"transfers-in", T_TRANSFERS_IN}, {"transfers-out", T_TRANSFERS_OUT}, {"transfers-per-ns", T_TRANSFERS_PER_NS}, {"treat-cr-as-space", T_TREAT_CR_AS_SPACE}, {"true", T_TRUE}, {"trusted-keys", T_TRUSTED_KEYS}, {"type", T_TYPE}, {"unix", T_UNIX}, {"unlimited", T_UNLIMITED}, {"use-id-pool", T_USE_ID_POOL}, {"use-ixfr", T_USE_IXFR}, {"version", T_VERSION}, {"versions", T_VERSIONS}, {"warn", T_WARN}, {"yes", T_YES}, {"zone", T_ZONE}, {(char *) NULL, 0}, }; /* * The table size should be a prime chosen to minimize collisions. */ #define KEYWORD_TABLE_SIZE 461 static symbol_table keyword_table = NULL; static void init_keywords() { struct keyword *k; symbol_value value; if (keyword_table != NULL) free_symbol_table(keyword_table); keyword_table = new_symbol_table(KEYWORD_TABLE_SIZE, NULL); for (k = keywords; k->name != NULL; k++) { value.integer = k->token; define_symbol(keyword_table, k->name, 0, value, 0); } dprint_symbol_table(99, keyword_table); } /* * File Contexts */ void lexer_begin_file(const char *filename, FILE *stream) { LexerFileContext lf; if (stream == NULL) { stream = fopen(filename, "r"); if (stream == NULL) { parser_error(0, "couldn't open include file '%s'", filename); return; } } lf = (LexerFileContext)memget(sizeof (struct lexer_file_context)); if (lf == NULL) panic("memget failed in lexer_begin_file", NULL); INSIST(stream != NULL); lf->stream = stream; lf->name = filename; /* note copy by reference */ lf->line_number = 1; lf->state = scan; lf->flags = 0; lf->warnings = 0; lf->errors = 0; lf->pushback_count = 0; lf->next = current_file; current_file = lf; } void lexer_end_file(void) { LexerFileContext lf; INSIST(current_file != NULL); lf = current_file; current_file = lf->next; fclose(lf->stream); memput(lf, sizeof *lf); } /* * Character Input */ #define LEXER_GETC(c, cf) \ do { \ if ((cf)->pushback_count > 0) { \ (cf)->pushback_count--; \ (c) = (cf)->pushback[(cf)->pushback_count]; \ } else \ (c) = getc((cf)->stream); \ } while (0); #define LEXER_UNGETC(c, cf) \ do { \ INSIST((cf)->pushback_count < LEXER_MAX_PUSHBACK); \ (cf)->pushback[(cf)->pushback_count++] = (c); \ } while (0); static void scan_to_comment_end(int c_plus_plus_style) { int c; int done = 0; int prev_was_star = 0; while (!done) { LEXER_GETC(c, current_file); switch (c) { case EOF: if (!c_plus_plus_style) parser_error(0, "EOF in comment"); current_file->flags |= LEX_EOF; done = 1; break; case '*': prev_was_star = 1; break; case '/': if (prev_was_star && !c_plus_plus_style) done = 1; prev_was_star = 0; break; case '\n': if (c_plus_plus_style) { /* don't consume the newline because we want it to be a delimiter for anything before the comment started */ LEXER_UNGETC(c, current_file); done = 1; } else { current_file->line_number++; } prev_was_star = 0; break; default: prev_was_star = 0; } } } int get_next_char(int comment_ok) { int c, nc; if (current_file->flags & LEX_EOF) return (EOF); LEXER_GETC(c, current_file); if (comment_ok) { while (c == '/' || c == '#') { if (c == '#') { scan_to_comment_end(1); if (current_file->flags & LEX_EOF) return (EOF); LEXER_GETC(c, current_file); } else { LEXER_GETC(nc, current_file); switch (nc) { case EOF: current_file->flags |= LEX_EOF; return ('/'); case '*': case '/': scan_to_comment_end((nc == '/')); if (current_file->flags & LEX_EOF) return (EOF); LEXER_GETC(c, current_file); break; default: LEXER_UNGETC(nc, current_file); return ('/'); } } } } if (c == EOF) current_file->flags |= LEX_EOF; else if (c == '\n') current_file->line_number++; return (c); } void put_back_char(int c) { if (c == EOF) current_file->flags |= LEX_EOF; else { LEXER_UNGETC(c, current_file); if (c == '\n') current_file->line_number--; } } /* * Identifiers */ static void clear_identifier(LexerIdentifier id) { INSIST(id != NULL); id->index = 0; id->num_dots = 0; id->flags = 0; } static char * dup_identifier(LexerIdentifier id) { char *duplicate; INSIST(id != NULL); duplicate = savestr(id->buffer, 1); return (duplicate); } static void finish_identifier(LexerIdentifier id) { INSIST(id != NULL && id->index < LEX_MAX_IDENT_SIZE); id->buffer[id->index] = '\0'; } static void add_to_identifier(LexerIdentifier id, int c) { INSIST(id != NULL); id->buffer[id->index] = c; id->index++; if (id->index >= LEX_MAX_IDENT_SIZE) { parser_error(0, "identifier too long"); current_file->state = scan; /* discard chars until we hit a non-identifier char */ while (c != EOF && identifier_char(c)) { c = get_next_char(1); } put_back_char(c); clear_identifier(id); } else { if (c == '.') { if (id->flags & LEX_LAST_WAS_DOT) id->flags |= LEX_CONSECUTIVE_DOTS; id->flags |= LEX_LAST_WAS_DOT; id->num_dots++; } else { id->flags &= ~LEX_LAST_WAS_DOT; } } } /* * yylex() -- return the next token from the current input stream */ int yylex() { int c; int comment_ok = 1; int token = -1; symbol_value value; while (token < 0) { c = get_next_char(comment_ok); switch(current_file->state) { case scan: if (c == EOF) { if (current_file->next == NULL) /* * We don't want to call * lexer_end_file() here because we * want to keep the toplevel file * context to log errors against. */ token = 0; else { lexer_end_file(); token = L_END_INCLUDE; } break; } if (whitespace(c)) break; if (identifier_char(c)) { if (isdigit(c)) current_file->state = number; else current_file->state = identifier; clear_identifier(id); add_to_identifier(id, c); } else if (special_char(c)) { if (c == ';') { token = L_EOS; break; } if (c == '"') { clear_identifier(id); current_file->state = quoted_string; comment_ok = 0; break; } token = c; } else { parser_error(0, "invalid character '%c'", c); } break; case number: if (c != EOF && identifier_char(c)) { if (!isdigit(c)) current_file->state = (c == '.') ? ipv4 : identifier; add_to_identifier(id, c); } else { put_back_char(c); current_file->state = scan; finish_identifier(id); yylval.num = strtol(id->buffer, (char**)0, 0); token = L_NUMBER; } break; case identifier: if (c != EOF && identifier_char(c)) { add_to_identifier(id, c); } else { put_back_char(c); current_file->state = scan; finish_identifier(id); /* is it a keyword? */ if (lookup_symbol(keyword_table, id->buffer, 0, &value)) { yylval.cp = id->buffer; token = value.integer; } else { yylval.cp = dup_identifier(id); token = L_STRING; } } break; case ipv4: if (c != EOF && identifier_char(c)) { if (!isdigit(c)) { if (c != '.' || (id->flags & LEX_CONSECUTIVE_DOTS)) current_file->state = identifier; } add_to_identifier(id, c); } else { put_back_char(c); if (id->num_dots > 3 || (id->flags & LEX_LAST_WAS_DOT)) current_file->state = identifier; else { if (id->num_dots == 1) { add_to_identifier(id, '.'); add_to_identifier(id, '0'); add_to_identifier(id, '.'); add_to_identifier(id, '0'); } else if (id->num_dots == 2) { add_to_identifier(id, '.'); add_to_identifier(id, '0'); } current_file->state = scan; finish_identifier(id); token = L_IPADDR; if (inet_aton(id->buffer, &(yylval.ip_addr))==0) { yylval.cp = dup_identifier(id); token = L_STRING; } } } break; case quoted_string: if (c == EOF) { parser_error(0, "EOF in quoted string"); return 0; } else { if (c == '"') { comment_ok = 1; current_file->state = scan; finish_identifier(id); yylval.cp = dup_identifier(id); token = L_QSTRING; } else { /* XXX add backslash escapes here */ add_to_identifier(id, c); } } break; default: panic("unhandled state in yylex", NULL); } } last_token = token; last_yylval = yylval; return (token); } /* * Initialization */ symbol_table constants; static void import_constants(const struct ns_sym *s, int type) { symbol_value value; for ((void)NULL; s != NULL && s->name != NULL; s++) { value.integer = s->number; define_symbol(constants, s->name, type, value, 0); } } static void import_res_constants(const struct res_sym *r, int type) { symbol_value value; for ((void)NULL; r != NULL && r->name != NULL; r++) { value.integer = r->number; define_symbol(constants, r->name, type, value, 0); } } #define CONSTANTS_TABLE_SIZE 397 /* should be prime */ static void import_all_constants() { constants = new_symbol_table(CONSTANTS_TABLE_SIZE, NULL); import_res_constants(__p_class_syms, SYM_CLASS); import_constants(category_constants, SYM_CATEGORY); import_constants(logging_constants, SYM_LOGGING); import_constants(syslog_constants, SYM_SYSLOG); } void lexer_initialize() { memset(special_chars, 0, sizeof special_chars); special_chars[';'] = 1; special_chars['{'] = 1; special_chars['}'] = 1; special_chars['!'] = 1; special_chars['/'] = 1; special_chars['"'] = 1; special_chars['*'] = 1; id = (LexerIdentifier)memget(sizeof (struct lexer_identifier)); if (id == NULL) panic("memget failed in lexer_initialize", NULL); init_keywords(); import_all_constants(); lexer_initialized = 1; } void lexer_setup(void) { REQUIRE(lexer_initialized); current_file = NULL; /* XXX should we INSIST(current_file==NULL)? */ INSIST(id != NULL); } void lexer_shutdown(void) { REQUIRE(lexer_initialized); free_symbol_table(keyword_table); free_symbol_table(constants); memput(id, sizeof (struct lexer_identifier)); id = NULL; lexer_initialized = 0; } Index: head/contrib/bind/bin/named/ns_lexer.h =================================================================== --- head/contrib/bind/bin/named/ns_lexer.h (revision 60940) +++ head/contrib/bind/bin/named/ns_lexer.h (revision 60941) @@ -1,45 +1,45 @@ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef _NS_LEXER_H #define _NS_LEXER_H /* * Note: and "ns_parseutil.h" must be included * before this file is included. */ #define LEX_MAX_IDENT_SIZE 1024 #define SYM_CLASS 0x01 #define SYM_CATEGORY 0x02 #define SYM_LOGGING 0x04 #define SYM_SYSLOG 0x08 int parser_warning(int, const char *, ...); int parser_error(int, const char *, ...); void yyerror(const char *); void lexer_begin_file(const char *, FILE *); void lexer_end_file(void); int yylex(void); void lexer_initialize(void); void lexer_setup(void); void lexer_shutdown(void); extern symbol_table constants; #endif /* !_NS_LEXER_H */ Index: head/contrib/bind/bin/named/ns_main.c =================================================================== --- head/contrib/bind/bin/named/ns_main.c (revision 60940) +++ head/contrib/bind/bin/named/ns_main.c (revision 60941) @@ -1,2737 +1,2759 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_main.c 4.55 (Berkeley) 7/1/91"; -static const char rcsid[] = "$Id: ns_main.c,v 8.117 1999/11/08 23:01:38 vixie Exp $"; +static const char rcsid[] = "$Id: ns_main.c,v 8.125 2000/04/21 06:54:08 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1989, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(lint) && !defined(SABER) char copyright[] = "@(#) Copyright (c) 1986, 1989, 1990 The Regents of the University of California.\n" "portions Copyright (c) 1993 Digital Equipment Corporation\n" "portions Copyright (c) 1995-1999 Internet Software Consortium\n" "portions Copyright (c) 1999 Check Point Software Technologies\n" "All rights reserved.\n"; #endif /* not lint */ /* * Internet Name server (see RCF1035 & others). */ #include "port_before.h" #include #include #include #include #include #include #include #include #ifdef SVR4 /* XXX */ # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef HAVE_GETRUSAGE /* XXX */ #include #endif #define MAIN_PROGRAM #include "named.h" #undef MAIN_PROGRAM /* list of interfaces */ static LIST(struct _interface) iflist; static int iflist_initialized = 0; static int iflist_dont_rescan = 0; static const int drbufsize = 32 * 1024, /* UDP rcv buf size */ dsbufsize = 48 * 1024, /* UDP snd buf size */ sbufsize = 16 * 1024, /* TCP snd buf size */ +#ifdef BROKEN_RECVFROM + nudptrans = 1, +#else nudptrans = 20, /* #/udps per select */ +#endif listenmax = 50; static u_int16_t nsid_state; static u_int16_t *nsid_pool; /* optional query id pool */ static u_int16_t *nsid_vtable; /* optional shuffle table */ static u_int32_t nsid_hash_state; static u_int16_t nsid_a1, nsid_a2, nsid_a3; static u_int16_t nsid_c1, nsid_c2, nsid_c3; static u_int16_t nsid_state2; static int nsid_algorithm; typedef void (*handler)(void); static int needs = 0; static handler handlers[main_need_num]; static struct qstream *sq_add(void); static int opensocket_d(interface *), opensocket_s(interface *); static void sq_query(struct qstream *), dq_remove(interface *); static int sq_dowrite(struct qstream *); static void use_desired_debug(void); static void stream_write(evContext, void *, int, int); static interface * if_find(struct in_addr, u_int16_t port); static int sq_here(struct qstream *); static void deallocate_everything(void), stream_accept(evContext, void *, int, const void *, int, const void *, int), stream_getlen(evContext, void *, int, int), stream_getmsg(evContext, void *, int, int), datagram_read(evContext, void *, int, int), dispatch_message(u_char *, int, int, struct qstream *, struct sockaddr_in, int, interface *); static void stream_send(evContext, void *, int, const void *, int, const void *, int); static int only_digits(const char *); static void init_needs(void), handle_need(void); #ifndef HAVE_CUSTOM static void custom_init(void), custom_shutdown(void); #endif static void usage() { fprintf(stderr, "Usage: named [-d #] [-q] [-r] [-v] [-f] [-p port] [[-b|-c] configfile]\n"); #ifdef CAN_CHANGE_ID fprintf(stderr, " [-u (username|uid)] [-g (groupname|gid)]\n"); #endif #ifdef HAVE_CHROOT fprintf(stderr, " [-t directory]\n"); #endif exit(1); } static char bad_p_option[] = "-p remote/local obsolete; use 'listen-on' in config file to specify local"; static char bad_directory[] = "chdir failed for directory '%s': %s"; /*ARGSUSED*/ int main(int argc, char *argv[], char *envp[]) { int n; char *p; int ch; struct passwd *pw; struct group *gr; #ifdef _AUX_SOURCE set42sig(); #endif debugfile = savestr(_PATH_DEBUG, 1); user_id = getuid(); group_id = getgid(); ns_port = htons(NAMESERVER_PORT); desired_debug = debug; /* BSD has a better random number generator but it's not clear * that we need it here. */ gettime(&tt); srand(((unsigned)getpid()) + (unsigned)tt.tv_usec); (void) umask(022); /* Save argv[] before getopt() destroys it -- needed for execvp(). */ saved_argv = malloc(sizeof(char *) * (argc + 1)); INSIST(saved_argv != NULL); for (n = 0; n < argc; n++) { saved_argv[n] = strdup(argv[n]); INSIST(saved_argv[n] != NULL); } saved_argv[argc] = NULL; /* XXX we need to free() this for clean shutdowns. */ while ((ch = getopt(argc, argv, "b:c:d:g:p:t:u:vw:qrf")) != -1) { switch (ch) { case 'b': case 'c': if (conffile != NULL) freestr(conffile); conffile = savestr(optarg, 1); break; case 'd': desired_debug = atoi(optarg); if (desired_debug <= 0) desired_debug = 1; break; case 'p': /* use nonstandard port number. * usage: -p remote/local * remote is the port number to which * we send queries. local is the port * on which we listen for queries. * local defaults to same as remote. */ ns_port = htons((u_int16_t) atoi(optarg)); p = strchr(optarg, '/'); if (p) { syslog(LOG_WARNING, bad_p_option); fprintf(stderr, bad_p_option); fputc('\n', stderr); } break; case 'w': if (chdir(optarg) < 0) { syslog(LOG_CRIT, bad_directory, optarg, strerror(errno)); fprintf(stderr, bad_directory, optarg, strerror(errno)); fputc('\n', stderr); exit(1); } break; #ifdef QRYLOG case 'q': qrylog = 1; break; #endif case 'r': ns_setoption(OPTION_NORECURSE); break; case 'f': foreground = 1; break; case 't': chroot_dir = savestr(optarg, 1); break; case 'v': - fprintf(stderr, "%s\n", Version); - exit(1); + fprintf(stdout, "%s\n", Version); + exit(0); #ifdef CAN_CHANGE_ID case 'u': user_name = savestr(optarg, 1); if (only_digits(user_name)) user_id = atoi(user_name); else { pw = getpwnam(user_name); if (pw == NULL) { fprintf(stderr, "user \"%s\" unknown\n", user_name); exit(1); } user_id = pw->pw_uid; if (group_name == NULL) { char name[256]; sprintf(name, "%lu", (u_long)pw->pw_gid); group_name = savestr(name, 1); group_id = pw->pw_gid; } } break; case 'g': if (group_name != NULL) freestr(group_name); group_name = savestr(optarg, 1); if (only_digits(group_name)) group_id = atoi(group_name); else { gr = getgrnam(group_name); if (gr == NULL) { fprintf(stderr, "group \"%s\" unknown\n", group_name); exit(1); } group_id = gr->gr_gid; } break; #endif /* CAN_CHANGE_ID */ case '?': default: usage(); } } argc -= optind; argv += optind; if (argc) { if (conffile != NULL) freestr(conffile); conffile = savestr(*argv, 1); argc--, argv++; } if (argc) usage(); if (conffile == NULL) conffile = savestr(_PATH_CONF, 1); /* * Make sure we don't inherit any open descriptors * other than those that daemon() can deal with. */ for (n = sysconf(_SC_OPEN_MAX) - 1; n >= 0; n--) if (n != STDIN_FILENO && n != STDOUT_FILENO && n != STDERR_FILENO) (void) close(n); /* * Chroot if desired. */ if (chroot_dir != NULL) { #ifdef HAVE_CHROOT if (chroot(chroot_dir) < 0) { fprintf(stderr, "chroot %s failed: %s\n", chroot_dir, strerror(errno)); exit(1); } if (chdir("/") < 0) { fprintf(stderr, "chdir(\"/\") failed: %s\n", strerror(errno)); exit(1); } #else fprintf(stderr, "warning: chroot() not available\n"); freestr(chroot_dir); chroot_dir = NULL; #endif } /* Establish global event context. */ evCreate(&ev); /* Establish global resolver context. */ res_ninit(&res); res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); /* * Set up logging. */ n = LOG_PID; #ifdef LOG_NOWAIT n |= LOG_NOWAIT; #endif #ifdef LOG_NDELAY n |= LOG_NDELAY; #endif #if defined(LOG_CONS) && defined(USE_LOG_CONS) n |= LOG_CONS; #endif #ifdef SYSLOG_42BSD openlog("named", n); #else openlog("named", n, LOG_DAEMON); #endif init_logging(); set_assertion_failure_callback(ns_assertion_failed); #ifdef DEBUG use_desired_debug(); #endif /* Perform system-dependent initialization */ custom_init(); init_needs(); init_signals(); ns_notice(ns_log_default, "starting. %s", Version); /* * Initialize and load database. */ gettime(&tt); buildservicelist(); buildprotolist(); ns_init(conffile); time(&boottime); resettime = boottime; nsid_init(); /* * Fork and go into background now that * we've done any slow initialization * and are ready to answer queries. */ if (foreground == 0) { if (daemon(1, 0)) ns_panic(ns_log_default, 1, "daemon: %s", strerror(errno)); update_pid_file(); } /* Check that udp checksums are on. */ ns_udp(); /* * We waited until now to log this because we wanted logging to * be set up the way the user prefers. */ if (chroot_dir != NULL) ns_info(ns_log_security, "chrooted to %s", chroot_dir); #ifdef CAN_CHANGE_ID /* * Set user and group if desired. */ if (group_name != NULL) { if (setgid(group_id) < 0) ns_panic(ns_log_security, 1, "setgid(%s): %s", group_name, strerror(errno)); ns_info(ns_log_security, "group = %s", group_name); } if (user_name != NULL) { if (getuid() == 0 && initgroups(user_name, group_id) < 0) ns_panic(ns_log_security, 1, "initgroups(%s, %d): %s", user_name, (int)group_id, strerror(errno)); endgrent(); endpwent(); if (setuid(user_id) < 0) ns_panic(ns_log_security, 1, "setuid(%s): %s", user_name, strerror(errno)); ns_info(ns_log_security, "user = %s", user_name); if (user_id != 0) iflist_dont_rescan++; } #endif /* CAN_CHANGE_ID */ ns_notice(ns_log_default, "Ready to answer queries."); gettime(&tt); prime_cache(); while (!main_needs_exit) { evEvent event; ns_debug(ns_log_default, 15, "main loop"); if (needs != 0) { /* Drain outstanding events; handlers ~block~. */ while (evGetNext(ev, &event, EV_POLL) != -1) INSIST_ERR(evDispatch(ev, event) != -1); INSIST_ERR(errno == EINTR || errno == EWOULDBLOCK); handle_need(); } else if (evGetNext(ev, &event, EV_WAIT) != -1) { INSIST_ERR(evDispatch(ev, event) != -1); } else { INSIST_ERR(errno == EINTR); } } ns_info(ns_log_default, "named shutting down"); #ifdef BIND_UPDATE dynamic_about_to_exit(); #endif if (server_options && server_options->pid_filename) (void)unlink(server_options->pid_filename); ns_logstats(ev, NULL, evNowTime(), evConsTime(0, 0)); if (NS_OPTION_P(OPTION_DEALLOC_ON_EXIT)) deallocate_everything(); else shutdown_configuration(); /* Cleanup for system-dependent stuff */ custom_shutdown(); return (0); } #ifndef IP_OPT_BUF_SIZE /* arbitrary size */ #define IP_OPT_BUF_SIZE 50 #endif static void stream_accept(evContext lev, void *uap, int rfd, const void *lav, int lalen, const void *rav, int ralen) { interface *ifp = uap; struct qstream *sp; struct iovec iov; int len, n; const int on = 1; #ifdef IP_OPTIONS /* XXX */ u_char ip_opts[IP_OPT_BUF_SIZE]; #endif const struct sockaddr_in *la, *ra; la = (const struct sockaddr_in *)lav; ra = (const struct sockaddr_in *)rav; INSIST(ifp != NULL); if (rfd < 0) { switch (errno) { case EINTR: case EAGAIN: #if (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case ECONNABORTED: #ifdef EPROTO case EPROTO: #endif case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: case ENETDOWN: case ECONNREFUSED: #ifdef ENONET case ENONET: #endif /* * These errors are expected and harmless, so * we ignore them. */ return; case EBADF: case ENOTSOCK: case EFAULT: /* * If one these happens, we're broken. */ ns_panic(ns_log_default, 1, "accept: %s", strerror(errno)); case EMFILE: /* * If we're out of file descriptors, find the least * busy fd and close it. Then we'll return to the * eventlib which will call us right back. */ if (streamq) { struct qstream *nextsp; struct qstream *candidate = NULL; time_t lasttime, maxctime = 0; for (sp = streamq; sp; sp = nextsp) { nextsp = sp->s_next; if (sp->s_refcnt) continue; gettime(&tt); lasttime = tt.tv_sec - sp->s_time; if (lasttime >= VQEXPIRY) sq_remove(sp); else if (lasttime > maxctime) { candidate = sp; maxctime = lasttime; } } if (candidate) sq_remove(candidate); return; } /* fall through */ default: /* * Either we got an error we didn't expect, or we * got EMFILE and didn't have anything left to close. * Log it and press on. */ ns_info(ns_log_default, "accept: %s", strerror(errno)); return; } } /* Condition the socket. */ #ifndef CANNOT_SET_SNDBUF if (setsockopt(rfd, SOL_SOCKET, SO_SNDBUF, (char*)&sbufsize, sizeof sbufsize) < 0) { ns_info(ns_log_default, "setsockopt(rfd, SO_SNDBUF, %d): %s", sbufsize, strerror(errno)); (void) close(rfd); return; } #endif if (setsockopt(rfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on) < 0) { ns_info(ns_log_default, "setsockopt(rfd, KEEPALIVE): %s", strerror(errno)); (void) close(rfd); return; } if ((n = fcntl(rfd, F_GETFL, 0)) == -1) { ns_info(ns_log_default, "fcntl(rfd, F_GETFL): %s", strerror(errno)); (void) close(rfd); return; } if (fcntl(rfd, F_SETFL, n|PORT_NONBLOCK) == -1) { ns_info(ns_log_default, "fcntl(rfd, NONBLOCK): %s", strerror(errno)); (void) close(rfd); return; } /* * We don't like IP options. Turn them off if the connection came in * with any. log this event since it usually indicates a security * problem. */ #if defined(IP_OPTIONS) /* XXX */ len = sizeof ip_opts; if (getsockopt(rfd, IPPROTO_IP, IP_OPTIONS, (char *)ip_opts, &len) < 0) { ns_info(ns_log_default, "getsockopt(rfd, IP_OPTIONS): %s", strerror(errno)); (void) close(rfd); return; } if (len != 0) { nameserIncr(ra->sin_addr, nssRcvdOpts); if (!haveComplained(ina_ulong(ra->sin_addr), (u_long)"rcvd ip options")) { ns_info(ns_log_default, "rcvd IP_OPTIONS from %s (ignored)", sin_ntoa(*ra)); } if (setsockopt(rfd, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) { ns_info(ns_log_default, "setsockopt(!IP_OPTIONS): %s", strerror(errno)); (void) close(rfd); } } #endif /* Create and populate a qsp for this socket. */ if ((sp = sq_add()) == NULL) { (void) close(rfd); return; } sp->s_rfd = rfd; /* stream file descriptor */ gettime(&tt); sp->s_time = tt.tv_sec; /* last transaction time */ sp->s_from = *ra; /* address to respond to */ sp->s_ifp = ifp; INSIST(sizeof sp->s_temp >= INT16SZ); iov = evConsIovec(sp->s_temp, INT16SZ); INSIST_ERR(evRead(lev, rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) != -1); sp->flags |= STREAM_READ_EV; #ifdef DEBUG if (debug) ns_info(ns_log_default, "IP/TCP connection from %s (fd %d)", sin_ntoa(sp->s_from), rfd); #endif } int tcp_send(struct qinfo *qp) { struct qstream *sp; int on = 1; ns_debug(ns_log_default, 1, "tcp_send"); if ((sp = sq_add()) == NULL) { return (SERVFAIL); } if ((sp->s_rfd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == -1) { sq_remove(sp); return (SERVFAIL); } if (sp->s_rfd > evHighestFD(ev)) { sq_remove(sp); return (SERVFAIL); } + if (fcntl(sp->s_rfd, F_SETFD, 1) < 0) { + sq_remove(sp); + return (SERVFAIL); + } if (sq_openw(sp, qp->q_msglen + INT16SZ) == -1) { sq_remove(sp); return (SERVFAIL); } if (sq_write(sp, qp->q_msg, qp->q_msglen) == -1) { sq_remove(sp); return (SERVFAIL); } if (setsockopt(sp->s_rfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)) < 0) ns_info(ns_log_default, "tcp_send: setsockopt(rfd, SO_KEEPALIVE): %s", strerror(errno)); gettime(&tt); sp->s_size = -1; sp->s_time = tt.tv_sec; /* last transaction time */ sp->s_refcnt = 1; sp->flags |= STREAM_DONE_CLOSE; sp->s_from = qp->q_addr[qp->q_curaddr].ns_addr; if (evConnect(ev, sp->s_rfd, &sp->s_from, sizeof(sp->s_from), stream_send, sp, &sp->evID_c) == -1) { sq_remove(sp); return (SERVFAIL); } sp->flags |= STREAM_CONNECT_EV; return (NOERROR); } static void stream_send(evContext lev, void *uap, int fd, const void *la, int lalen, - const void *ra, int ralen) { + const void *ra, int ralen) { struct qstream *sp = uap; ns_debug(ns_log_default, 1, "stream_send"); sp->flags &= ~STREAM_CONNECT_EV; if (fd == -1) { /* connect failed */ sq_remove(sp); return; } if (evSelectFD(ev, sp->s_rfd, EV_WRITE, stream_write, sp, &sp->evID_w) < 0) { sq_remove(sp); return; } sp->flags |= STREAM_WRITE_EV; } static void stream_write(evContext ctx, void *uap, int fd, int evmask) { struct qstream *sp = uap; struct iovec iov; ns_debug(ns_log_default, 1, "stream_write"); INSIST(evmask & EV_WRITE); INSIST(fd == sp->s_rfd); if (sq_dowrite(sp) < 0) { sq_remove(sp); return; } if (sp->s_wbuf_free != sp->s_wbuf_send) return; if (sp->s_wbuf) { memput(sp->s_wbuf, sp->s_wbuf_end - sp->s_wbuf); sp->s_wbuf_send = sp->s_wbuf_free = NULL; sp->s_wbuf_end = sp->s_wbuf = NULL; } (void) evDeselectFD(ev, sp->evID_w); sp->flags &= ~STREAM_WRITE_EV; sp->s_refcnt = 0; iov = evConsIovec(sp->s_temp, INT16SZ); INSIST_ERR(evRead(ctx, fd, &iov, 1, stream_getlen, sp, &sp->evID_r) != -1); sp->flags |= STREAM_READ_EV; } static void stream_getlen(evContext lev, void *uap, int fd, int bytes) { struct qstream *sp = uap; struct iovec iov; sp->flags &= ~STREAM_READ_EV; if (bytes != INT16SZ) { /* * bytes == 0 is normal EOF; see if something unusual * happened. */ if (bytes < 0) { /* * ECONNRESET happens frequently and is not worth * logging. */ if (errno != ECONNRESET) ns_info(ns_log_default, "stream_getlen(%s): %s", sin_ntoa(sp->s_from), strerror(errno)); } else if (bytes != 0) ns_error(ns_log_default, "stream_getlen(%s): unexpected byte count %d", sin_ntoa(sp->s_from), bytes); sq_remove(sp); return; } /* * Unpack the size, allocate memory for the query. This is * tricky since in a low memory situation with possibly very * large (64KB) queries, we want to make sure we can read at * least the header since we need it to send back a SERVFAIL * (owing to the out-of-memory condition). */ sp->s_size = ns_get16(sp->s_temp); ns_debug(ns_log_default, 5, "stream message: %d bytes", sp->s_size); if (sp->s_size < HFIXEDSZ) { ns_error(ns_log_default, "stream_getlen(%s): request too small", sin_ntoa(sp->s_from)); sq_remove(sp); return; } if (!(sp->flags & STREAM_MALLOC)) { sp->s_bufsize = 64*1024-1; /* maximum tcp message size */ sp->s_buf = (u_char *)memget(sp->s_bufsize); if (sp->s_buf != NULL) sp->flags |= STREAM_MALLOC; else { sp->s_buf = sp->s_temp; sp->s_bufsize = HFIXEDSZ; } } - iov = evConsIovec(sp->s_buf, sp->s_size); + iov = evConsIovec(sp->s_buf, (sp->s_size <= sp->s_bufsize) ? + sp->s_size : sp->s_bufsize); if (evRead(lev, sp->s_rfd, &iov, 1, stream_getmsg, sp, &sp->evID_r) == -1) ns_panic(ns_log_default, 1, "evRead(fd %d): %s", (void *)sp->s_rfd, strerror(errno)); sp->flags |= STREAM_READ_EV; } static void stream_getmsg(evContext lev, void *uap, int fd, int bytes) { struct qstream *sp = uap; sp->flags &= ~STREAM_READ_EV; if (bytes == -1) { ns_info(ns_log_default, "stream_getmsg(%s): %s", sin_ntoa(sp->s_from), strerror(errno)); sq_remove(sp); return; } gettime(&tt); sp->s_time = tt.tv_sec; if (ns_wouldlog(ns_log_default,5)) { ns_debug(ns_log_default, 5, "sp %#x rfd %d size %d time %d next %#x", sp, sp->s_rfd, sp->s_size, sp->s_time, sp->s_next); ns_debug(ns_log_default, 5, "\tbufsize %d bytes %d", sp->s_bufsize, bytes); } /* * Do we have enough memory for the query? If not, and if we have a * query id, then we will send a SERVFAIL error back to the client. */ if (bytes != sp->s_size) { HEADER *hp = (HEADER *)sp->s_buf; hp->qr = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); hp->ancount = htons(0); hp->qdcount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = SERVFAIL; writestream(sp, sp->s_buf, HFIXEDSZ); sp->flags |= STREAM_DONE_CLOSE; return; } nameserIncr(sp->s_from.sin_addr, nssRcvdTCP); sq_query(sp); dispatch_message(sp->s_buf, bytes, sp->s_bufsize, sp, sp->s_from, -1, sp->s_ifp); } static void datagram_read(evContext lev, void *uap, int fd, int evmask) { interface *ifp = uap; struct sockaddr_in from; int from_len = sizeof from; int n, nudp; union { HEADER h; /* Force alignment of 'buf'. */ u_char buf[PACKETSZ+1]; } u; tt = evTimeVal(evNowTime()); nudp = 0; more: n = recvfrom(fd, (char *)u.buf, sizeof u.buf, 0, (struct sockaddr *)&from, &from_len); if (n < 0) { switch (errno) { case EINTR: case EAGAIN: #if (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: case ENETDOWN: case ECONNREFUSED: #ifdef ENONET case ENONET: #endif /* * These errors are expected and harmless, so we * ignore them. */ return; default: /* * An error we don't expect. Log it and press * on. */ ns_info(ns_log_default, "recvfrom: %s", strerror(errno)); return; } } /* Handle bogosity on systems that need it. */ if (n == 0) return; if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "datagram from %s, fd %d, len %d", sin_ntoa(from), fd, n); } if (n > PACKETSZ) { /* * The message is too big. It's probably a response to * one of our questions, so we truncate it and press on. */ n = trunc_adjust(u.buf, PACKETSZ, PACKETSZ); ns_debug(ns_log_default, 1, "truncated oversize UDP packet"); } dispatch_message(u.buf, n, PACKETSZ, NULL, from, fd, ifp); if (++nudp < nudptrans) goto more; } static void dispatch_message(u_char *msg, int msglen, int buflen, struct qstream *qsp, struct sockaddr_in from, int dfd, interface *ifp) { HEADER *hp = (HEADER *)msg; if (msglen < HFIXEDSZ) { ns_debug(ns_log_default, 1, "dropping undersize message"); if (qsp) { qsp->flags |= STREAM_DONE_CLOSE; sq_done(qsp); } return; } if (server_options->blackhole_acl != NULL && ip_match_address(server_options->blackhole_acl, from.sin_addr) == 1) { ns_debug(ns_log_default, 1, "dropping blackholed %s from %s", hp->qr ? "response" : "query", sin_ntoa(from)); if (qsp) { qsp->flags |= STREAM_DONE_CLOSE; sq_done(qsp); } return; } /* Drop UDP packets from port zero. They are invariable forged. */ if (qsp == NULL && ntohs(from.sin_port) == 0) { ns_notice(ns_log_security, "dropping source port zero packet from %s", sin_ntoa(from)); return; } if (hp->qr) { ns_resp(msg, msglen, from, qsp); if (qsp) sq_done(qsp); /* Now is a safe time for housekeeping. */ if (needs_prime_cache) prime_cache(); } else if (ifp != NULL) ns_req(msg, msglen, buflen, qsp, from, dfd); else { ns_notice(ns_log_security, "refused query on non-query socket from %s", sin_ntoa(from)); if (qsp) { qsp->flags |= STREAM_DONE_CLOSE; sq_done(qsp); } /* XXX Send refusal here. */ } } void getnetconf(int periodic_scan) { struct ifconf ifc; struct ifreq ifreq; struct in_addr ina; interface *ifp; char *buf, *cp, *cplim; static int bufsiz = 4095; time_t my_generation = time(NULL); int s, cpsize, n; int found; listen_info li; ip_match_element ime; u_char *mask_ptr; struct in_addr mask; if (iflist_initialized) { if (iflist_dont_rescan) return; } else { INIT_LIST(iflist); iflist_initialized = 1; } ns_debug(ns_log_default, 1, "getnetconf(generation %lu)", (u_long)my_generation); /* Get interface list from system. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { if (!periodic_scan) ns_panic(ns_log_default, 1, "socket(SOCK_RAW): %s", strerror(errno)); ns_error(ns_log_default, "socket(SOCK_RAW): %s", strerror(errno)); return; } if (local_addresses != NULL) free_ip_match_list(local_addresses); local_addresses = new_ip_match_list(); if (local_networks != NULL) free_ip_match_list(local_networks); local_networks = new_ip_match_list(); for (;;) { buf = memget(bufsiz); if (!buf) ns_panic(ns_log_default, 1, "memget(interface)", NULL); ifc.ifc_len = bufsiz; ifc.ifc_buf = buf; #ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF - /* - * This is a fix for IRIX OS in which the call to ioctl with - * the flag SIOCGIFCONF may not return an entry for all the - * interfaces like most flavors of Unix. - */ - if (emul_ioctl(&ifc) >= 0) - break; + /* + * This is a fix for IRIX OS in which the call to ioctl with + * the flag SIOCGIFCONF may not return an entry for all the + * interfaces like most flavors of Unix. + */ + if (emul_ioctl(&ifc) >= 0) + break; #else if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) { /* * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If * ifc.ifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ if (ifc.ifc_len + 2 * sizeof(ifreq) < bufsiz) break; } #endif if ((n == -1) && errno != EINVAL) ns_panic(ns_log_default, 1, "get interface configuration: %s", strerror(errno)); if (bufsiz > 1000000) ns_panic(ns_log_default, 1, "get interface configuration: maximum buffer size exceeded"); memput(buf, bufsiz); bufsiz += 4096; } ns_debug(ns_log_default, 2, "getnetconf: SIOCGIFCONF: ifc_len = %d", ifc.ifc_len); /* Parse system's interface list and open some sockets. */ cplim = buf + ifc.ifc_len; /* skip over if's with big ifr_addr's */ for (cp = buf; cp < cplim; cp += cpsize) { memcpy(&ifreq, cp, sizeof ifreq); #ifdef HAVE_SA_LEN #ifdef FIX_ZERO_SA_LEN if (ifreq.ifr_addr.sa_len == 0) ifreq.ifr_addr.sa_len = 16; #endif #ifdef HAVE_MINIMUM_IFREQ ns_debug(ns_log_default, 2, "%s sa_len = %d", ifreq.ifr_name, (int)ifreq.ifr_addr.sa_len); cpsize = sizeof ifreq; if (ifreq.ifr_addr.sa_len > sizeof (struct sockaddr)) cpsize += (int)ifreq.ifr_addr.sa_len - (int)(sizeof (struct sockaddr)); #else cpsize = sizeof ifreq.ifr_name + ifreq.ifr_addr.sa_len; #endif /* HAVE_MINIMUM_IFREQ */ #elif defined SIOCGIFCONF_ADDR cpsize = sizeof ifreq; #else cpsize = sizeof ifreq.ifr_name; if (ioctl(s, SIOCGIFADDR, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get interface addr (%s): %s", ifreq.ifr_name, strerror(errno)); continue; } #endif if (ifreq.ifr_addr.sa_family != AF_INET) { ns_debug(ns_log_default, 2, "getnetconf: %s AF %d != INET", ifreq.ifr_name, ifreq.ifr_addr.sa_family); continue; } ina = ina_get((u_char *)&((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr); ns_debug(ns_log_default, 1, "getnetconf: considering %s [%s]", ifreq.ifr_name, inet_ntoa(ina)); /* * Don't test IFF_UP, packets may still be received at this * address if any other interface is up. */ if (ina_hlong(ina) == INADDR_ANY) { ns_debug(ns_log_default, 2, "getnetconf: INADDR_ANY, ignoring."); continue; } INSIST(server_options != NULL); INSIST(server_options->listen_list != NULL); found=0; for (li = server_options->listen_list->first; li != NULL; li = li->next) { if (ip_match_address(li->list, ina) > 0) { found++; /* * Look for an already existing source * interface address/port pair. * This happens mostly when reinitializing. * Also, if the machine has multiple point to * point interfaces, then the local address * may appear more than once. */ ifp = if_find(ina, li->port); if (ifp != NULL) { ns_debug(ns_log_default, 1, "dup interface addr [%s].%u (%s)", inet_ntoa(ina), ntohs(li->port), ifreq.ifr_name); ifp->gen = my_generation; continue; } ifp = (interface *)memget(sizeof *ifp); if (!ifp) ns_panic(ns_log_default, 1, "memget(interface)", NULL); memset(ifp, 0, sizeof *ifp); APPEND(iflist, ifp, link); ifp->addr = ina; ifp->port = li->port; ifp->gen = my_generation; ifp->flags = 0; ifp->dfd = -1; ifp->sfd = -1; if (opensocket_d(ifp) < 0 || opensocket_s(ifp) < 0) { dq_remove(ifp); found = 0; break; } ns_info(ns_log_default, "listening on [%s].%u (%s)", inet_ntoa(ina), ntohs(li->port), ifreq.ifr_name); } } if (!found) ns_debug(ns_log_default, 1, "not listening on addr [%s] (%s)", inet_ntoa(ina), ifreq.ifr_name); /* * Add this interface's address to the list of local * addresses if we haven't added it already. */ if (ip_match_address(local_addresses, ina) < 0) { ime = new_ip_match_pattern(ina, 32); add_to_ip_match_list(local_addresses, ime); } /* * Get interface flags. */ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get interface flags: %s", strerror(errno)); continue; } if ((ifreq.ifr_flags & IFF_POINTOPOINT)) { /* * The local network for a PPP link is just the * two ends of the link, so for each endpoint we * add a pattern that will only match the endpoint. */ if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get dst addr: %s", strerror(errno)); continue; } mask.s_addr = htonl(INADDR_BROADCAST); /* * Our end. * * Only add it if we haven't seen it before. */ if (ip_match_network(local_networks, ina, mask) < 0) { ime = new_ip_match_pattern(ina, 32); add_to_ip_match_list(local_networks, ime); } /* * The other end. */ ina = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr; /* * Only add it if we haven't seen it before. */ if (ip_match_network(local_networks, ina, mask) < 0) { ime = new_ip_match_pattern(ina, 32); add_to_ip_match_list(local_networks, ime); } } else { /* * Add this interface's network and netmask to the * list of local networks. */ #ifdef SIOCGIFNETMASK /* XXX */ if (ioctl(s, SIOCGIFNETMASK, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get netmask: %s", strerror(errno)); continue; } /* * Use ina_get because the ifreq structure might not * be aligned. */ mask_ptr = (u_char *) &((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; mask = ina_get(mask_ptr); #else mask = net_mask(ina); #endif ina.s_addr &= mask.s_addr; /* make network address */ /* * Only add it if we haven't seen it before. */ if (ip_match_network(local_networks, ina, mask) < 0) { ime = new_ip_match_mask(ina, mask); add_to_ip_match_list(local_networks, ime); } } } close(s); memput(buf, bufsiz); ns_debug(ns_log_default, 7, "local addresses:"); dprint_ip_match_list(ns_log_default, local_addresses, 2, "", ""); ns_debug(ns_log_default, 7, "local networks:"); dprint_ip_match_list(ns_log_default, local_networks, 2, "", ""); /* * now go through the iflist and delete anything that * does not have the current generation number. this is * how we catch interfaces that go away or change their * addresses. note that 0.0.0.0 is the wildcard element * and should never be deleted by this code. */ dq_remove_gen(my_generation); if (EMPTY(iflist)) ns_warning(ns_log_default, "not listening on any interfaces"); } /* opensocket_d(ifp) * Open datagram socket bound to interface address. * Returns: * 0 on success. * -1 on failure. */ static int opensocket_d(interface *ifp) { struct sockaddr_in nsa; const int on = 1; int m, n; int fd; memset(&nsa, 0, sizeof nsa); nsa.sin_family = AF_INET; nsa.sin_addr = ifp->addr; nsa.sin_port = ifp->port; if ((ifp->dfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { ns_error(ns_log_default, "socket(SOCK_DGRAM): %s", strerror(errno)); return (-1); } if (ifp->dfd > evHighestFD(ev)) { ns_error(ns_log_default, "socket too high: %d", ifp->dfd); close(ifp->dfd); return (-1); } if ((n = fcntl(ifp->dfd, F_GETFL, 0)) == -1) { ns_info(ns_log_default, "fcntl(ifp->dfd, F_GETFL): %s", strerror(errno)); (void) close(ifp->dfd); return (-1); } if (fcntl(ifp->dfd, F_SETFL, n|PORT_NONBLOCK) == -1) { ns_info(ns_log_default, "fcntl(ifp->dfd, NONBLOCK): %s", strerror(errno)); (void) close(ifp->dfd); return (-1); } #ifdef F_DUPFD /* XXX */ /* * Leave a space for stdio to work in. */ if ((fd = fcntl(ifp->dfd, F_DUPFD, 20)) != -1) { close(ifp->dfd); ifp->dfd = fd; } else ns_notice(ns_log_default, "fcntl(dfd, F_DUPFD, 20): %s", strerror(errno)); #endif + if (fcntl(ifp->dfd, F_SETFD, 1) < 0) { + ns_error(ns_log_default, "F_SETFD: %s", strerror(errno)); + close(ifp->dfd); + return (-1); + } ns_debug(ns_log_default, 1, "ifp->addr %s d_dfd %d", sin_ntoa(nsa), ifp->dfd); if (setsockopt(ifp->dfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", strerror(errno)); /* XXX press on regardless, this is not too serious. */ } #ifdef SO_RCVBUF /* XXX */ m = sizeof n; if ((getsockopt(ifp->dfd, SOL_SOCKET, SO_RCVBUF, (char*)&n, &m) >= 0) && (m == sizeof n) && (n < drbufsize)) { (void) setsockopt(ifp->dfd, SOL_SOCKET, SO_RCVBUF, (char *)&drbufsize, sizeof drbufsize); } #endif /* SO_RCVBUF */ #ifndef CANNOT_SET_SNDBUF if (setsockopt(ifp->dfd, SOL_SOCKET, SO_SNDBUF, (char*)&dsbufsize, sizeof dsbufsize) < 0) { ns_info(ns_log_default, "setsockopt(dfd=%d, SO_SNDBUF, %d): %s", ifp->dfd, dsbufsize, strerror(errno)); /* XXX press on regardless, this is not too serious. */ } #endif if (bind(ifp->dfd, (struct sockaddr *)&nsa, sizeof nsa)) { ns_error(ns_log_default, "bind(dfd=%d, %s): %s", ifp->dfd, sin_ntoa(nsa), strerror(errno)); return (-1); } if (evSelectFD(ev, ifp->dfd, EV_READ, datagram_read, ifp, &ifp->evID_d) == -1) { ns_error(ns_log_default, "evSelectFD(dfd=%d): %s", ifp->dfd, strerror(errno)); return (-1); } ifp->flags |= INTERFACE_FILE_VALID; return (0); } /* opensocket_s(ifp) * Open stream (listener) socket bound to interface address. * Returns: * 0 on success. * -1 on failure. */ static int opensocket_s(interface *ifp) { struct sockaddr_in nsa; const int on = 1; int n; int fd; memset(&nsa, 0, sizeof nsa); nsa.sin_family = AF_INET; nsa.sin_addr = ifp->addr; nsa.sin_port = ifp->port; /* * Open stream (listener) port. */ n = 0; again: if ((ifp->sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ns_error(ns_log_default, "socket(SOCK_STREAM): %s", strerror(errno)); return (-1); } if (ifp->sfd > evHighestFD(ev)) { ns_error(ns_log_default, "socket too high: %d", ifp->sfd); close(ifp->sfd); return (-1); } #ifdef F_DUPFD /* XXX */ /* * Leave a space for stdio to work in. */ if ((fd = fcntl(ifp->sfd, F_DUPFD, 20)) != -1) { close(ifp->sfd); ifp->sfd = fd; } else ns_notice(ns_log_default, "fcntl(sfd, F_DUPFD, 20): %s", strerror(errno)); #endif + if (fcntl(ifp->sfd, F_SETFD, 1) < 0) { + ns_error(ns_log_default, "F_SETFD: %s", strerror(errno)); + close(ifp->sfd); + return (-1); + } if (setsockopt(ifp->sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on) != 0) { ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", strerror(errno)); /* Consider that your first warning of trouble to come. */ } if (bind(ifp->sfd, (struct sockaddr *)&nsa, sizeof nsa) < 0) { if (errno != EADDRINUSE || ++n > 4) { if (errno == EADDRINUSE) ns_error(ns_log_default, "There may be a name server already running on %s", sin_ntoa(nsa)); else ns_error(ns_log_default, "bind(sfd=%d, %s): %s", ifp->sfd, sin_ntoa(nsa), strerror(errno)); return (-1); } /* Retry opening the socket a few times */ close(ifp->sfd); ifp->sfd = -1; sleep(30); goto again; } if (evListen(ev, ifp->sfd, listenmax, stream_accept, ifp, &ifp->evID_s) == -1) { ns_error(ns_log_default, "evListen(sfd=%d): %s", ifp->sfd, strerror(errno)); return (-1); } ifp->flags |= INTERFACE_CONN_VALID; return (0); } /* opensocket_f() * Open datagram socket bound to no particular interface; use for ns_forw * and sysquery. */ void opensocket_f() { static struct sockaddr_in prev_qsrc; static int been_here; static interface *prev_ifp; struct sockaddr_in nsa; const int on = 1; int n, need_close; interface *ifp; need_close = 0; if (been_here) { if (prev_ifp != NULL) prev_ifp->flags &= ~INTERFACE_FORWARDING; else if (server_options->query_source.sin_port == htons(0) || prev_qsrc.sin_addr.s_addr != server_options->query_source.sin_addr.s_addr || prev_qsrc.sin_port != server_options->query_source.sin_port) need_close = 1; } else ds = -1; been_here = 1; INSIST(server_options != NULL); if (need_close) { evDeselectFD(ev, ds_evID); close(ds); ds = -1; } /* * If we're already listening on the query_source address and port, * we don't need to open another socket. We mark the interface, so * we'll notice we're in trouble if it goes away. */ ifp = if_find(server_options->query_source.sin_addr, server_options->query_source.sin_port); if (ifp != NULL) { ifp->flags |= INTERFACE_FORWARDING; prev_ifp = ifp; ds = ifp->dfd; ns_info(ns_log_default, "forwarding source address is %s", sin_ntoa(server_options->query_source)); return; } /* * If we're already using the correct query source, we're done. */ if (ds >= 0) return; prev_qsrc = server_options->query_source; prev_ifp = NULL; if ((ds = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ns_panic(ns_log_default, 1, "socket(SOCK_DGRAM): %s", strerror(errno)); if (ds > evHighestFD(ev)) ns_panic(ns_log_default, 1, "socket too high: %d", ds); + if (fcntl(ds, F_SETFD, 1) < 0) + ns_panic(ns_log_default, 1, "F_SETFD: %s", strerror(errno)); if (setsockopt(ds, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on) != 0) { ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", strerror(errno)); /* XXX press on regardless, this is not too serious. */ } if (bind(ds, (struct sockaddr *)&server_options->query_source, sizeof server_options->query_source) < 0) ns_panic(ns_log_default, 0, "opensocket_f: bind(%s): %s", sin_ntoa(server_options->query_source), strerror(errno)); n = sizeof nsa; if (getsockname(ds, (struct sockaddr *)&nsa, &n) < 0) ns_panic(ns_log_default, 1, "opensocket_f: getsockaddr: %s", strerror(errno)); ns_debug(ns_log_default, 1, "fwd ds %d addr %s", ds, sin_ntoa(nsa)); ns_info(ns_log_default, "Forwarding source address is %s", sin_ntoa(nsa)); if (evSelectFD(ev, ds, EV_READ, datagram_read, NULL, &ds_evID) == -1) ns_panic(ns_log_default, 1, "evSelectFD(fd %d): %s", (void *)ds, strerror(errno)); /* XXX: should probably use a different FileFunc that only accepts * responses, since requests on this socket make no sense. */ } static void setdebug(int new_debug) { #ifdef DEBUG int old_debug; if (!new_debug) ns_debug(ns_log_default, 1, "Debug off"); old_debug = debug; debug = new_debug; log_option(log_ctx, LOG_OPTION_DEBUG, debug); log_option(log_ctx, LOG_OPTION_LEVEL, debug); evSetDebug(ev, debug, log_get_stream(eventlib_channel)); if (debug) { if (!old_debug) open_special_channels(); ns_debug(ns_log_default, 1, "Debug level %d", debug); if (!old_debug) { ns_debug(ns_log_default, 1, "Version = %s", Version); ns_debug(ns_log_default, 1, "conffile = %s", conffile); } } #endif } /* ** Routines for managing stream queue */ static struct qstream * sq_add() { struct qstream *sqp; if (!(sqp = (struct qstream *)memget(sizeof *sqp))) { ns_error(ns_log_default, "sq_add: memget: %s", strerror(errno)); return (NULL); } memset(sqp, 0, sizeof *sqp); ns_debug(ns_log_default, 3, "sq_add(%#lx)", (u_long)sqp); sqp->flags = 0; /* XXX should init other fields too? */ sqp->s_next = streamq; streamq = sqp; return (sqp); } /* sq_remove(qp) * remove stream queue structure `qp'. * no current queries may refer to this stream when it is removed. * side effects: * memory is deallocated. sockets are closed. lists are relinked. */ void sq_remove(struct qstream *qp) { struct qstream *qsp; ns_debug(ns_log_default, 2, "sq_remove(%#lx, %d) rfcnt=%d", (u_long)qp, qp->s_rfd, qp->s_refcnt); if (qp->s_wbuf != NULL) { memput(qp->s_wbuf, qp->s_wbuf_end - qp->s_wbuf); qp->s_wbuf_send = qp->s_wbuf_free = NULL; qp->s_wbuf_end = qp->s_wbuf = NULL; } if (qp->flags & STREAM_MALLOC) memput(qp->s_buf, qp->s_bufsize); if (qp->flags & STREAM_READ_EV) INSIST_ERR(evCancelRW(ev, qp->evID_r) != -1); if (qp->flags & STREAM_WRITE_EV) INSIST_ERR(evDeselectFD(ev, qp->evID_w) != -1); if (qp->flags & STREAM_CONNECT_EV) INSIST_ERR(evCancelConn(ev, qp->evID_c) != -1); - if (qp->flags & STREAM_AXFR) + if (qp->flags & STREAM_AXFR || qp->flags & STREAM_AXFRIXFR) ns_freexfr(qp); (void) close(qp->s_rfd); if (qp == streamq) streamq = qp->s_next; else { for (qsp = streamq; qsp && (qsp->s_next != qp); qsp = qsp->s_next) (void)NULL; if (qsp) qsp->s_next = qp->s_next; } memput(qp, sizeof *qp); } /* void * sq_flush(allbut) * call sq_remove() on all open streams except `allbut' * side effects: * global list `streamq' modified * idiocy: * is N^2 due to the scan inside of sq_remove() */ void sq_flush(struct qstream *allbut) { struct qstream *sp, *spnext; for (sp = streamq; sp != NULL; sp = spnext) { spnext = sp->s_next; if (sp != allbut) sq_remove(sp); } } /* int * sq_openw(qs, buflen) * add a write buffer to a stream * return: * 0 = success * -1 = failure (check errno) */ int sq_openw(struct qstream *qs, int buflen) { #ifdef DO_SO_LINGER /* XXX */ static const struct linger ll = { 1, 120 }; #endif INSIST(qs->s_wbuf == NULL); qs->s_wbuf = (u_char *)memget(buflen); if (qs->s_wbuf == NULL) return (-1); qs->s_wbuf_send = qs->s_wbuf; qs->s_wbuf_free = qs->s_wbuf; qs->s_wbuf_end = qs->s_wbuf + buflen; #ifdef DO_SO_LINGER /* XXX */ /* kernels that map pages for IO end up failing if the pipe is full * at exit and we take away the final buffer. this is really a kernel * bug but it's harmless on systems that are not broken, so... */ setsockopt(qs->s_rfd, SOL_SOCKET, SO_LINGER, (char *)&ll, sizeof ll); #endif return (0); } /* static void * sq_dowrite(qs) * try to submit data to the system, remove it from our queue. */ static int sq_dowrite(struct qstream *qs) { if (qs->s_wbuf_free > qs->s_wbuf_send) { int n = write(qs->s_rfd, qs->s_wbuf_send, qs->s_wbuf_free - qs->s_wbuf_send); INSIST(qs->s_wbuf != NULL); if (n < 0) { if (errno != EINTR && errno != EAGAIN #if (EWOULDBLOCK != EAGAIN) && errno != EWOULDBLOCK #endif ) return (-1); return (0); } qs->s_wbuf_send += n; if (qs->s_wbuf_free > qs->s_wbuf_send) { /* XXX: need some kind of delay here during which the * socket will be deselected so we don't spin. */ n = qs->s_wbuf_free - qs->s_wbuf_send; memmove(qs->s_wbuf, qs->s_wbuf_send, n); qs->s_wbuf_send = qs->s_wbuf; qs->s_wbuf_free = qs->s_wbuf + n; } } if (qs->s_wbuf_free == qs->s_wbuf_send) qs->s_wbuf_free = qs->s_wbuf_send = qs->s_wbuf; return (0); } /* void * sq_flushw(qs) * called when the socket becomes writable and we want to flush our * buffers and the system's socket buffers. use as a closure with * sq_writeh(). */ void sq_flushw(struct qstream *qs) { if (qs->s_wbuf_free == qs->s_wbuf_send) { sq_writeh(qs, NULL); sq_done(qs); } } /* static void * sq_writable(ctx, uap, fd, evmask) * glue between eventlib closures and qstream closures */ static void sq_writable(evContext ctx, void *uap, int fd, int evmask) { struct qstream *qs = uap; INSIST(evmask & EV_WRITE); INSIST(fd == qs->s_rfd); if (sq_dowrite(qs) < 0) { sq_remove(qs); return; } if (qs->s_wbuf_closure && qs->s_wbuf_end - qs->s_wbuf_free >= HFIXEDSZ+2) /* XXX guess */ (*qs->s_wbuf_closure)(qs); if (sq_dowrite(qs) < 0) { sq_remove(qs); return; } } /* int * sq_writeh(qs, closure) * register a closure to be called when a stream becomes writable * return: * 0 = success * -1 = failure (check errno) */ int sq_writeh(struct qstream *qs, sq_closure c) { if (c) { if (!qs->s_wbuf_closure) { if (evSelectFD(ev, qs->s_rfd, EV_WRITE, sq_writable, qs, &qs->evID_w) < 0) { return (-1); } qs->flags |= STREAM_WRITE_EV; } } else { (void) evDeselectFD(ev, qs->evID_w); qs->flags &= ~STREAM_WRITE_EV; } qs->s_wbuf_closure = c; return (0); } /* int * sq_write(qs, buf, len) * queue a message onto the stream, prepended by a two byte length field * return: * 0 = success * -1 = failure (check errno; E2BIG means we can't handle this right now) */ int sq_write(struct qstream *qs, const u_char *buf, int len) { INSIST(qs->s_wbuf != NULL); if (NS_INT16SZ + len > qs->s_wbuf_end - qs->s_wbuf_free) { if (sq_dowrite(qs) < 0) return (-1); if (NS_INT16SZ + len > qs->s_wbuf_end - qs->s_wbuf_free) { errno = E2BIG; return (-1); } } __putshort(len, qs->s_wbuf_free); qs->s_wbuf_free += NS_INT16SZ; memcpy(qs->s_wbuf_free, buf, len); qs->s_wbuf_free += len; return (0); } /* int * sq_here(sp) * determine whether stream 'sp' is still on the streamq * return: * boolean: is it here? */ static int sq_here(struct qstream *sp) { struct qstream *t; for (t = streamq; t != NULL; t = t->s_next) if (t == sp) return (1); return (0); } /* * Initiate query on stream; * mark as referenced and stop selecting for input. */ static void sq_query(struct qstream *sp) { sp->s_refcnt++; } /* * Note that the current request on a stream has completed, * and that we should continue looking for requests on the stream. */ void sq_done(struct qstream *sp) { struct iovec iov; if (sp->s_wbuf != NULL) { INSIST(sp->s_wbuf_send == sp->s_wbuf_free); memput(sp->s_wbuf, sp->s_wbuf_end - sp->s_wbuf); sp->s_wbuf_send = sp->s_wbuf_free = NULL; sp->s_wbuf_end = sp->s_wbuf = NULL; } - if (sp->flags & STREAM_AXFR) + if (sp->flags & STREAM_AXFR || sp->flags & STREAM_AXFRIXFR) ns_freexfr(sp); sp->s_refcnt = 0; sp->s_time = tt.tv_sec; if (sp->flags & STREAM_DONE_CLOSE) { /* XXX */ sq_remove(sp); return; } iov = evConsIovec(sp->s_temp, INT16SZ); if (evRead(ev, sp->s_rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) == -1) ns_panic(ns_log_default, 1, "evRead(fd %d): %s", (void *)sp->s_rfd, strerror(errno)); sp->flags |= STREAM_READ_EV; } /* void * dq_remove_gen(gen) * close/deallocate all the udp sockets (except 0.0.0.0) which are * not from the current generation. * side effects: * global list `iflist' is modified. */ void dq_remove_gen(time_t gen) { interface *this, *next; for (this = HEAD(iflist); this != NULL; this = next) { next = NEXT(this, link); if (this->gen != gen && ina_hlong(this->addr) != INADDR_ANY) dq_remove(this); } } /* void * dq_remove_all() * close/deallocate all interfaces. * side effects: * global list `iflist' is modified. */ void dq_remove_all() { interface *this, *next; for (this = HEAD(iflist); this != NULL; this = next) { next = NEXT(this, link); /* * Clear the forwarding flag so we don't panic the server. */ this->flags &= ~INTERFACE_FORWARDING; dq_remove(this); } } /* void * dq_remove(interface *this) * close/deallocate an interface's sockets. called on errors * or if the interface disappears. * side effects: * global list `iflist' is modified. */ static void dq_remove(interface *this) { ns_notice(ns_log_default, "deleting interface [%s].%u", inet_ntoa(this->addr), ntohs(this->port)); if ((this->flags & INTERFACE_FORWARDING) != 0) ns_panic(ns_log_default, 0, "forwarding interface [%s].%u gone", inet_ntoa(this->addr), ntohs(this->port)); /* Deallocate fields. */ if ((this->flags & INTERFACE_FILE_VALID) != 0) (void) evDeselectFD(ev, this->evID_d); if (this->dfd >= 0) (void) close(this->dfd); if ((this->flags & INTERFACE_CONN_VALID) != 0) (void) evCancelConn(ev, this->evID_s); if (this->sfd >= 0) (void) close(this->sfd); UNLINK(iflist, this, link); memput(this, sizeof *this); } /* struct in_addr * net_mask(ina) * makes a classful assumption in a classless world, and returns it. */ struct in_addr net_mask(struct in_addr ina) { u_long hl = ina_hlong(ina); struct in_addr ret; if (IN_CLASSA(hl)) hl = IN_CLASSA_NET; else if (IN_CLASSB(hl)) hl = IN_CLASSB_NET; else if (IN_CLASSC(hl)) hl = IN_CLASSC_NET; else hl = INADDR_BROADCAST; ina_ulong(ret) = htonl(hl); return (ret); } /* aIsUs(addr) * scan our list of interface addresses for "addr". * returns: * 0: address isn't one of our interfaces * >0: address is one of our interfaces, or INADDR_ANY */ int aIsUs(struct in_addr addr) { if (ina_hlong(addr) == INADDR_ANY || if_find(addr, 0) != NULL) return (1); return (0); } /* interface * * if_find(addr, port) * scan our list of interface addresses for "addr" and port. * port == 0 means match any port * returns: * pointer to interface with this address/port, or NULL if there isn't * one. */ static interface * if_find(struct in_addr addr, u_int16_t port) { interface *ifp; for (ifp = HEAD(iflist); ifp != NULL; ifp = NEXT(ifp, link)) if (ina_equal(addr, ifp->addr)) if (port == 0 || ifp->port == port) break; return (ifp); } /* * These are here in case we ever want to get more clever, like perhaps * using a bitmap to keep track of outstanding queries and a random * allocation scheme to make it a little harder to predict them. Note * that the resolver will need the same protection so the cleverness * should be put there rather than here; this is just an interface layer. * * This is true but ... most clients only send out a few queries, they * use varying port numbers, and the queries aren't sent to the outside * world which we know is full of spoofers. Doing a good job of randomizing * ids may also be to expensive for each client. Queries forwarded by the * server always come from the same port (unless you let 8.x pick a port * and restart it periodically - maybe it should open several and use * them randomly). The server sends out lots more queries, and if it's * cache is corrupted, it has the potential to affect more clients. * NOTE: - randomizing the ID or source port doesn't help a bit if the * queries can be sniffed. * -- DL */ /* * Allow the user to pick one of two ID randomization algorithms. * * The first algorithm is an adaptation of the sequence shuffling * algorithm discovered by Carter Bays and S. D. Durham [ACM Trans. Math. * Software 2 (1976), 59-64], as documented as Algorithm B in Chapter * 3.2.2 in Volume 2 of Knuth's "The Art of Computer Programming". We use * a randomly selected linear congruential random number generator with a * modulus of 2^16, whose increment is a randomly picked odd number, and * whose multiplier is picked from a set which meets the following * criteria: * Is of the form 8*n+5, which ensures "high potency" according to * principle iii in the summary chapter 3.6. This form also has a * gcd(a-1,m) of 4 which is good according to principle iv. * * Is between 0.01 and 0.99 times the modulus as specified by * principle iv. * * Passes the spectral test "with flying colors" (ut >= 1) in * dimensions 2 through 6 as calculated by Algorithm S in Chapter * 3.3.4 and the ratings calculated by formula 35 in section E. * * Of the multipliers that pass this test, pick the set that is * best according to the theoretical bounds of the serial * correlation test. This was calculated using a simplified * version of Knuth's Theorem K in Chapter 3.3.3. * * These criteria may not be important for this use, but we might as well * pick from the best generators since there are so many possible ones and * we don't have that many random bits to do the picking. * * We use a modulus of 2^16 instead of something bigger so that we will * tend to cycle through all the possible IDs before repeating any, * however the shuffling will perturb this somewhat. Theoretically there * is no minimimum interval between two uses of the same ID, but in * practice it seems to be >64000. * * Our adaptatation of Algorithm B mixes the hash state which has * captured various random events into the shuffler to perturb the * sequence. * * One disadvantage of this algorithm is that if the generator parameters * were to be guessed, it would be possible to mount a limited brute force * attack on the ID space since the IDs are only shuffled within a limited * range. * * The second algorithm uses the same random number generator to populate * a pool of 65536 IDs. The hash state is used to pick an ID from a window * of 4096 IDs in this pool, then the chosen ID is swapped with the ID * at the beginning of the window and the window position is advanced. * This means that the interval between uses of the ID will be no less * than 65536-4096. The ID sequence in the pool will become more random * over time. * * For both algorithms, two more linear congruential random number generators * are selected. The ID from the first part of algorithm is used to seed * the first of these generators, and its output is used to seed the second. * The strategy is use these generators as 1 to 1 hashes to obfuscate the * properties of the generator used in the first part of either algorithm. * * The first algorithm may be suitable for use in a client resolver since * its memory requirements are fairly low and it's pretty random out of * the box. It is somewhat succeptible to a limited brute force attack, * so the second algorithm is probably preferable for a longer running * program that issues a large number of queries and has time to randomize * the pool. */ #define NSID_SHUFFLE_TABLE_SIZE 100 /* Suggested by Knuth */ /* * Pick one of the next 4096 IDs in the pool. * There is a tradeoff here between randomness and how often and ID is reused. */ #define NSID_LOOKAHEAD 4096 /* Must be a power of 2 */ #define NSID_SHUFFLE_ONLY 1 /* algorithm 1 */ #define NSID_USE_POOL 2 /* algorithm 2 */ /* * Keep a running hash of various bits of data that we'll use to * stir the ID pool or perturb the ID generator */ void nsid_hash(u_char *data, size_t len) { /* * Hash function similar to the one we use for hashing names. * We don't fold case or toss the upper bit here, though. * This hash doesn't do much interesting when fed binary zeros, * so there may be a better hash function. * This function doesn't need to be very strong since we're * only using it to stir the pool, but it should be reasonably * fast. */ while (len-- > 0) { HASHROTATE(nsid_hash_state); nsid_hash_state += *data++; } } /* * Table of good linear congruential multipliers for modulus 2^16 * in order of increasing serial correlation bounds (so trim from * the end). */ static const u_int16_t nsid_multiplier_table[] = { 17565, 25013, 11733, 19877, 23989, 23997, 24997, 25421, 26781, 27413, 35901, 35917, 35973, 36229, 38317, 38437, 39941, 40493, 41853, 46317, 50581, 51429, 53453, 53805, 11317, 11789, 12045, 12413, 14277, 14821, 14917, 18989, 19821, 23005, 23533, 23573, 23693, 27549, 27709, 28461, 29365, 35605, 37693, 37757, 38309, 41285, 45261, 47061, 47269, 48133, 48597, 50277, 50717, 50757, 50805, 51341, 51413, 51581, 51597, 53445, 11493, 14229, 20365, 20653, 23485, 25541, 27429, 29421, 30173, 35445, 35653, 36789, 36797, 37109, 37157, 37669, 38661, 39773, 40397, 41837, 41877, 45293, 47277, 47845, 49853, 51085, 51349, 54085, 56933, 8877, 8973, 9885, 11365, 11813, 13581, 13589, 13613, 14109, 14317, 15765, 15789, 16925, 17069, 17205, 17621, 17941, 19077, 19381, 20245, 22845, 23733, 24869, 25453, 27213, 28381, 28965, 29245, 29997, 30733, 30901, 34877, 35485, 35613, 36133, 36661, 36917, 38597, 40285, 40693, 41413, 41541, 41637, 42053, 42349, 45245, 45469, 46493, 48205, 48613, 50861, 51861, 52877, 53933, 54397, 55669, 56453, 56965, 58021, 7757, 7781, 8333, 9661, 12229, 14373, 14453, 17549, 18141, 19085, 20773, 23701, 24205, 24333, 25261, 25317, 27181, 30117, 30477, 34757, 34885, 35565, 35885, 36541, 37957, 39733, 39813, 41157, 41893, 42317, 46621, 48117, 48181, 49525, 55261, 55389, 56845, 7045, 7749, 7965, 8469, 9133, 9549, 9789, 10173, 11181, 11285, 12253, 13453, 13533, 13757, 14477, 15053, 16901, 17213, 17269, 17525, 17629, 18605, 19013, 19829, 19933, 20069, 20093, 23261, 23333, 24949, 25309, 27613, 28453, 28709, 29301, 29541, 34165, 34413, 37301, 37773, 38045, 38405, 41077, 41781, 41925, 42717, 44437, 44525, 44613, 45933, 45941, 47077, 50077, 50893, 52117, 5293, 55069, 55989, 58125, 59205, 6869, 14685, 15453, 16821, 17045, 17613, 18437, 21029, 22773, 22909, 25445, 25757, 26541, 30709, 30909, 31093, 31149, 37069, 37725, 37925, 38949, 39637, 39701, 40765, 40861, 42965, 44813, 45077, 45733, 47045, 50093, 52861, 52957, 54181, 56325, 56365, 56381, 56877, 57013, 5741, 58101, 58669, 8613, 10045, 10261, 10653, 10733, 11461, 12261, 14069, 15877, 17757, 21165, 23885, 24701, 26429, 26645, 27925, 28765, 29197, 30189, 31293, 39781, 39909, 40365, 41229, 41453, 41653, 42165, 42365, 47421, 48029, 48085, 52773, 5573, 57037, 57637, 58341, 58357, 58901, 6357, 7789, 9093, 10125, 10709, 10765, 11957, 12469, 13437, 13509, 14773, 15437, 15773, 17813, 18829, 19565, 20237, 23461, 23685, 23725, 23941, 24877, 25461, 26405, 29509, 30285, 35181, 37229, 37893, 38565, 40293, 44189, 44581, 45701, 47381, 47589, 48557, 4941, 51069, 5165, 52797, 53149, 5341, 56301, 56765, 58581, 59493, 59677, 6085, 6349, 8293, 8501, 8517, 11597, 11709, 12589, 12693, 13517, 14909, 17397, 18085, 21101, 21269, 22717, 25237, 25661, 29189, 30101, 31397, 33933, 34213, 34661, 35533, 36493, 37309, 40037, 4189, 42909, 44309, 44357, 44389, 4541, 45461, 46445, 48237, 54149, 55301, 55853, 56621, 56717, 56901, 5813, 58437, 12493, 15365, 15989, 17829, 18229, 19341, 21013, 21357, 22925, 24885, 26053, 27581, 28221, 28485, 30605, 30613, 30789, 35437, 36285, 37189, 3941, 41797, 4269, 42901, 43293, 44645, 45221, 46893, 4893, 50301, 50325, 5189, 52109, 53517, 54053, 54485, 5525, 55949, 56973, 59069, 59421, 60733, 61253, 6421, 6701, 6709, 7101, 8669, 15797, 19221, 19837, 20133, 20957, 21293, 21461, 22461, 29085, 29861, 30869, 34973, 36469, 37565, 38125, 38829, 39469, 40061, 40117, 44093, 47429, 48341, 50597, 51757, 5541, 57629, 58405, 59621, 59693, 59701, 61837, 7061, 10421, 11949, 15405, 20861, 25397, 25509, 25893, 26037, 28629, 28869, 29605, 30213, 34205, 35637, 36365, 37285, 3773, 39117, 4021, 41061, 42653, 44509, 4461, 44829, 4725, 5125, 52269, 56469, 59085, 5917, 60973, 8349, 17725, 18637, 19773, 20293, 21453, 22533, 24285, 26333, 26997, 31501, 34541, 34805, 37509, 38477, 41333, 44125, 46285, 46997, 47637, 48173, 4925, 50253, 50381, 50917, 51205, 51325, 52165, 52229, 5253, 5269, 53509, 56253, 56341, 5821, 58373, 60301, 61653, 61973, 62373, 8397, 11981, 14341, 14509, 15077, 22261, 22429, 24261, 28165, 28685, 30661, 34021, 34445, 39149, 3917, 43013, 43317, 44053, 44101, 4533, 49541, 49981, 5277, 54477, 56357, 57261, 57765, 58573, 59061, 60197, 61197, 62189, 7725, 8477, 9565, 10229, 11437, 14613, 14709, 16813, 20029, 20677, 31445, 3165, 31957, 3229, 33541, 36645, 3805, 38973, 3965, 4029, 44293, 44557, 46245, 48917, 4909, 51749, 53709, 55733, 56445, 5925, 6093, 61053, 62637, 8661, 9109, 10821, 11389, 13813, 14325, 15501, 16149, 18845, 22669, 26437, 29869, 31837, 33709, 33973, 34173, 3677, 3877, 3981, 39885, 42117, 4421, 44221, 44245, 44693, 46157, 47309, 5005, 51461, 52037, 55333, 55693, 56277, 58949, 6205, 62141, 62469, 6293, 10101, 12509, 14029, 17997, 20469, 21149, 25221, 27109, 2773, 2877, 29405, 31493, 31645, 4077, 42005, 42077, 42469, 42501, 44013, 48653, 49349, 4997, 50101, 55405, 56957, 58037, 59429, 60749, 61797, 62381, 62837, 6605, 10541, 23981, 24533, 2701, 27333, 27341, 31197, 33805, 3621, 37381, 3749, 3829, 38533, 42613, 44381, 45901, 48517, 51269, 57725, 59461, 60045, 62029, 13805, 14013, 15461, 16069, 16157, 18573, 2309, 23501, 28645, 3077, 31541, 36357, 36877, 3789, 39429, 39805, 47685, 47949, 49413, 5485, 56757, 57549, 57805, 58317, 59549, 62213, 62613, 62853, 62933, 8909, 12941, 16677, 20333, 21541, 24429, 26077, 26421, 2885, 31269, 33381, 3661, 40925, 42925, 45173, 4525, 4709, 53133, 55941, 57413, 57797, 62125, 62237, 62733, 6773, 12317, 13197, 16533, 16933, 18245, 2213, 2477, 29757, 33293, 35517, 40133, 40749, 4661, 49941, 62757, 7853, 8149, 8573, 11029, 13421, 21549, 22709, 22725, 24629, 2469, 26125, 2669, 34253, 36709, 41013, 45597, 46637, 52285, 52333, 54685, 59013, 60997, 61189, 61981, 62605, 62821, 7077, 7525, 8781, 10861, 15277, 2205, 22077, 28517, 28949, 32109, 33493, 3685, 39197, 39869, 42621, 44997, 48565, 5221, 57381, 61749, 62317, 63245, 63381, 23149, 2549, 28661, 31653, 33885, 36341, 37053, 39517, 42805, 45853, 48997, 59349, 60053, 62509, 63069, 6525, 1893, 20181, 2365, 24893, 27397, 31357, 32277, 33357, 34437, 36677, 37661, 43469, 43917, 50997, 53869, 5653, 13221, 16741, 17893, 2157, 28653, 31789, 35301, 35821, 61613, 62245, 12405, 14517, 17453, 18421, 3149, 3205, 40341, 4109, 43941, 46869, 48837, 50621, 57405, 60509, 62877, 8157, 12933, 12957, 16501, 19533, 3461, 36829, 52357, 58189, 58293, 63053, 17109, 1933, 32157, 37701, 59005, 61621, 13029, 15085, 16493, 32317, 35093, 5061, 51557, 62221, 20765, 24613, 2629, 30861, 33197, 33749, 35365, 37933, 40317, 48045, 56229, 61157, 63797, 7917, 17965, 1917, 1973, 20301, 2253, 33157, 58629, 59861, 61085, 63909, 8141, 9221, 14757, 1581, 21637, 26557, 33869, 34285, 35733, 40933, 42517, 43501, 53653, 61885, 63805, 7141, 21653, 54973, 31189, 60061, 60341, 63357, 16045, 2053, 26069, 33997, 43901, 54565, 63837, 8949, 17909, 18693, 32349, 33125, 37293, 48821, 49053, 51309, 64037, 7117, 1445, 20405, 23085, 26269, 26293, 27349, 32381, 33141, 34525, 36461, 37581, 43525, 4357, 43877, 5069, 55197, 63965, 9845, 12093, 2197, 2229, 32165, 33469, 40981, 42397, 8749, 10853, 1453, 18069, 21693, 30573, 36261, 37421, 42533 }; #define NSID_MULT_TABLE_SIZE \ - ((sizeof nsid_multiplier_table)/(sizeof nsid_multiplier_table[0])) + ((sizeof nsid_multiplier_table)/(sizeof nsid_multiplier_table[0])) void nsid_init(void) { struct timeval now; pid_t mypid; u_int16_t a1ndx, a2ndx, a3ndx, c1ndx, c2ndx, c3ndx; int i; if (nsid_algorithm != 0) return; gettimeofday(&now, NULL); mypid = getpid(); /* Initialize the state */ nsid_hash_state = 0; nsid_hash((u_char *)&now, sizeof now); nsid_hash((u_char *)&mypid, sizeof mypid); /* * Select our random number generators and initial seed. * We could really use more random bits at this point, * but we'll try to make a silk purse out of a sows ear ... */ /* generator 1 */ a1ndx = ((u_long) NSID_MULT_TABLE_SIZE * (nsid_hash_state & 0xFFFF)) >> 16; nsid_a1 = nsid_multiplier_table[a1ndx]; c1ndx = (nsid_hash_state >> 9) & 0x7FFF; nsid_c1 = 2*c1ndx + 1; /* generator 2, distinct from 1 */ a2ndx = ((u_long) (NSID_MULT_TABLE_SIZE - 1) * ((nsid_hash_state >> 10) & 0xFFFF)) >> 16; if (a2ndx >= a1ndx) a2ndx++; nsid_a2 = nsid_multiplier_table[a2ndx]; c2ndx = nsid_hash_state % 32767; if (c2ndx >= c1ndx) c2ndx++; nsid_c2 = 2*c2ndx + 1; /* generator 3, distinct from 1 and 2 */ a3ndx = ((u_long) (NSID_MULT_TABLE_SIZE - 2) * ((nsid_hash_state >> 20) & 0xFFFF)) >> 16; if (a3ndx >= a1ndx || a3ndx >= a2ndx) a3ndx++; if (a3ndx >= a1ndx && a3ndx >= a2ndx) a3ndx++; nsid_a3 = nsid_multiplier_table[a3ndx]; c3ndx = nsid_hash_state % 32766; if (c3ndx >= c1ndx || c3ndx >= c2ndx) c3ndx++; if (c3ndx >= c1ndx && c3ndx >= c2ndx) c3ndx++; nsid_c3 = 2*c3ndx + 1; nsid_state = ((nsid_hash_state >> 16) ^ (nsid_hash_state)) & 0xFFFF; /* Do the algorithm specific initialization */ INSIST(server_options != NULL); if (NS_OPTION_P(OPTION_USE_ID_POOL) == 0) { /* Algorithm 1 */ nsid_algorithm = NSID_SHUFFLE_ONLY; nsid_vtable = memget(NSID_SHUFFLE_TABLE_SIZE * (sizeof(u_int16_t)) ); if (!nsid_vtable) ns_panic(ns_log_default, 1, "memget(nsid_vtable)", NULL); for (i = 0; i < NSID_SHUFFLE_TABLE_SIZE; i++) { nsid_vtable[i] = nsid_state; nsid_state = (((u_long) nsid_a1 * nsid_state) + nsid_c1) & 0xFFFF; } nsid_state2 = nsid_state; } else { /* Algorithm 2 */ nsid_algorithm = NSID_USE_POOL; nsid_pool = memget(0x10000 * (sizeof(u_int16_t))); if (!nsid_pool) ns_panic(ns_log_default, 1, "memget(nsid_pool)", NULL); for (i = 0; ; i++) { nsid_pool[i] = nsid_state; nsid_state = (((u_long) nsid_a1 * nsid_state) + nsid_c1) & 0xFFFF; if (i == 0xFFFF) break; } } } #define NSID_RANGE_MASK (NSID_LOOKAHEAD - 1) #define NSID_POOL_MASK 0xFFFF /* used to wrap the pool index */ u_int16_t nsid_next() { u_int16_t id, compressed_hash; compressed_hash = ((nsid_hash_state >> 16) ^ (nsid_hash_state)) & 0xFFFF; if (nsid_algorithm == NSID_SHUFFLE_ONLY) { u_int16_t j; /* * This is the original Algorithm B * j = ((u_long) NSID_SHUFFLE_TABLE_SIZE * nsid_state2) * >> 16; * * We'll perturb it with some random stuff ... */ j = ((u_long) NSID_SHUFFLE_TABLE_SIZE * (nsid_state2 ^ compressed_hash)) >> 16; nsid_state2 = id = nsid_vtable[j]; nsid_state = (((u_long) nsid_a1 * nsid_state) + nsid_c1) & 0xFFFF; nsid_vtable[j] = nsid_state; } else if (nsid_algorithm == NSID_USE_POOL) { u_int16_t pick; pick = compressed_hash & NSID_RANGE_MASK; id = nsid_pool[(nsid_state + pick) & NSID_POOL_MASK]; if (pick != 0) { /* Swap two IDs to stir the pool */ nsid_pool[(nsid_state + pick) & NSID_POOL_MASK] = nsid_pool[nsid_state]; nsid_pool[nsid_state] = id; } /* increment the base pointer into the pool */ if (nsid_state == 65535) nsid_state = 0; else nsid_state++; } else ns_panic(ns_log_default, 1, "Unknown ID algorithm", NULL); /* Now lets obfuscate ... */ id = (((u_long) nsid_a2 * id) + nsid_c2) & 0xFFFF; id = (((u_long) nsid_a3 * id) + nsid_c3) & 0xFFFF; return (id); } /* Note: this function CAN'T deallocate the saved_argv[]. */ static void deallocate_everything(void) { FILE *f; f = write_open(server_options->memstats_filename); ns_freestats(); qflush(); sq_flush(NULL); free_addinfo(); ns_shutdown(); dq_remove_all(); db_lame_destroy(); if (local_addresses != NULL) free_ip_match_list(local_addresses); if (local_networks != NULL) free_ip_match_list(local_networks); destroyservicelist(); destroyprotolist(); shutdown_logging(); evDestroy(ev); if (conffile != NULL) freestr(conffile); conffile = NULL; if (debugfile != NULL) freestr(debugfile); debugfile = NULL; if (user_name != NULL) freestr(user_name); user_name = NULL; if (group_name != NULL) freestr(group_name); group_name = NULL; if (chroot_dir != NULL) freestr(chroot_dir); chroot_dir = NULL; if (nsid_pool != NULL) memput(nsid_pool, 0x10000 * (sizeof(u_int16_t))); nsid_pool = NULL; irs_destroy(); if (f != NULL) { memstats(f); (void)fclose(f); } } static void ns_exit(void) { main_needs_exit++; } static void ns_restart(void) { ns_info(ns_log_default, "named restarting"); #ifdef BIND_UPDATE dynamic_about_to_exit(); #endif if (server_options && server_options->pid_filename) (void)unlink(server_options->pid_filename); ns_logstats(ev, NULL, evNowTime(), evConsTime(0, 0)); if (NS_OPTION_P(OPTION_DEALLOC_ON_EXIT)) deallocate_everything(); else shutdown_configuration(); execvp(saved_argv[0], saved_argv); abort(); } static void use_desired_debug(void) { #ifdef DEBUG sigset_t set; /* Protect against race conditions by blocking debugging signals. */ if (sigemptyset(&set) < 0) { ns_error(ns_log_os, "sigemptyset failed in use_desired_debug: %s", strerror(errno)); return; } if (sigaddset(&set, SIGUSR1) < 0) { ns_error(ns_log_os, "sigaddset SIGUSR1 failed in use_desired_debug: %s", strerror(errno)); return; } if (sigaddset(&set, SIGUSR2) < 0) { ns_error(ns_log_os, "sigaddset SIGUSR2 failed in use_desired_debug: %s", strerror(errno)); return; } if (sigprocmask(SIG_BLOCK, &set, NULL) < 0) { ns_error(ns_log_os, "sigprocmask to block USR1 and USR2 failed: %s", strerror(errno)); return; } setdebug(desired_debug); if (sigprocmask(SIG_UNBLOCK, &set, NULL) < 0) ns_error(ns_log_os, "sigprocmask to unblock USR1 and USR2 failed: %s", strerror(errno)); #endif } void toggle_qrylog(void) { qrylog = !qrylog; ns_notice(ns_log_default, "query log %s\n", qrylog ?"on" :"off"); } static void wild(void) { ns_panic(ns_log_default, 1, "wild need", NULL); } /* * This is a functional interface to the global needs and options. */ static void init_needs(void) { int need; for (need = 0; need < main_need_num; need++) handlers[need] = wild; handlers[main_need_zreload] = ns_zreload; handlers[main_need_reload] = ns_reload; handlers[main_need_reconfig] = ns_reconfig; handlers[main_need_endxfer] = endxfer; handlers[main_need_zoneload] = loadxfer; handlers[main_need_dump] = doadump; handlers[main_need_statsdump] = ns_stats; handlers[main_need_exit] = ns_exit; handlers[main_need_qrylog] = toggle_qrylog; handlers[main_need_debug] = use_desired_debug; handlers[main_need_restart] = ns_restart; handlers[main_need_reap] = reapchild; + handlers[main_need_noexpired] = ns_noexpired; } static void handle_need(void) { int need; ns_debug(ns_log_default, 15, "handle_need()"); for (need = 0; need < main_need_num; need++) if ((needs & (1 << need)) != 0) { /* Turn off flag first, handlers ~turn~ it back on. */ block_signals(); needs &= ~(1 << need); unblock_signals(); (handlers[need])(); return; } ns_panic(ns_log_default, 1, "handle_need() found no needs", NULL); } void ns_need(enum need need) { block_signals(); ns_need_unsafe(need); unblock_signals(); } /* Note: this function should only be called with signals blocked. */ void ns_need_unsafe(enum need need) { needs |= (1 << need); } void ns_setoption(int option) { ns_warning(ns_log_default, "used obsolete ns_setoption(%d)", option); } void writestream(struct qstream *sp, const u_char *msg, int msglen) { if (sq_openw(sp, msglen + INT16SZ) == -1) { sq_remove(sp); return; } if (sq_write(sp, msg, msglen) == -1) { sq_remove(sp); return; } sq_writeh(sp, sq_flushw); } static int only_digits(const char *s) { if (*s == '\0') return (0); while (*s != '\0') { if (!isdigit(*s)) return (0); s++; } return (1); } #if defined(__GNUC__) && defined(__BOUNDS_CHECKING_ON) /* Use bounds checking malloc, etc. */ void * memget(size_t len) { return (malloc(len)); } void memput(void *addr, size_t len) { free(addr); } int meminit(size_t init_max_size, size_t target_size) { return (0); } void * memget_debug(size_t size, const char *file, int line) { - void *ptr; - ptr = __memget(size); - fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line, - (u_long)size, ptr); - return (ptr); + void *ptr; + ptr = __memget(size); + fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line, + (u_long)size, ptr); + return (ptr); } void memput_debug(void *ptr, size_t size, const char *file, int line) { - fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, ptr, - (u_long)size); - __memput(ptr, size); + fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, ptr, + (u_long)size); + __memput(ptr, size); } void memstats(FILE *out) { fputs("No memstats\n", out); } #endif #ifndef HAVE_CUSTOM /* Standard implementation has nothing here */ static void custom_init(void) { /* Noop. */ } static void custom_shutdown(void) { /* Noop. */ } #endif Index: head/contrib/bind/bin/named/ns_maint.c =================================================================== --- head/contrib/bind/bin/named/ns_maint.c (revision 60940) +++ head/contrib/bind/bin/named/ns_maint.c (revision 60941) @@ -1,1744 +1,1935 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91"; -static const char rcsid[] = "$Id: ns_maint.c,v 8.95 1999/10/13 16:39:09 vixie Exp $"; +static const char rcsid[] = "$Id: ns_maint.c,v 8.103 2000/04/23 02:18:58 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static int nxfers(struct zoneinfo *, int), bottom_of_zone(struct databuf *, int); static void startxfer(struct zoneinfo *), abortxfer(struct zoneinfo *), tryxfer(void), purge_z_2(struct hashbuf *, int); +static int purge_nonglue_2(const char *, struct hashbuf *, + int, int); #ifndef HAVE_SPAWNXFER static pid_t spawnxfer(char **, struct zoneinfo *); #endif static time_t stats_time; /* Redundant ??? XXX ogud */ /* State of all running zone transfers */ static struct { pid_t xfer_pid; int xfer_state; /* see below */ WAIT_T xfer_status; } xferstatus[MAX_XFERS_RUNNING]; #define XFER_IDLE 0 #define XFER_RUNNING 1 #define XFER_DONE 2 /* * Perform routine zone maintenance. */ void zone_maint(struct zoneinfo *zp) { gettime(&tt); ns_debug(ns_log_maint, 1, "zone_maint('%s'); now %lu", zp->z_origin[0] == '\0' ? "." : zp->z_origin, (u_long)tt.tv_sec); #ifdef DEBUG if (debug >= 2) printzoneinfo((zp - zones), ns_log_maint, 2); #endif switch (zp->z_type) { case Z_SECONDARY: /*FALLTHROUGH*/ #ifdef STUBS case Z_STUB: #endif if (zp->z_serial != 0 && ((zp->z_lastupdate+zp->z_expire) < (u_int32_t)tt.tv_sec)) { + if ((zp->z_flags & Z_NOTIFY) != 0) + ns_stopnotify(zp->z_origin, zp->z_class); /* calls purge_zone */ do_reload(zp->z_origin, zp->z_type, zp->z_class, 0); /* reset zone state */ + if (!haveComplained((u_long)zp, (u_long)stale)) { + ns_notice(ns_log_default, + "%s zone \"%s\" expired", + zoneTypeString(zp->z_type), + zp->z_origin); + } zp->z_flags &= ~Z_AUTH; + zp->z_flags |= Z_EXPIRED; zp->z_refresh = INIT_REFRESH; zp->z_retry = INIT_REFRESH; zp->z_serial = 0; } if ((zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) != 0) { ns_retrytime(zp, tt.tv_sec); break; } if (zp->z_flags & Z_XFER_RUNNING) { abortxfer(zp); /* * Check again in 30 seconds in case the first * abort doesn't work. */ if (zp->z_time != 0 && zp->z_time <= tt.tv_sec) zp->z_time = tt.tv_sec + 30; break; } /* * If we don't have the zone loaded or dialup is off * or we attempted a qserial_query before and the queue was * full attempt to verify / load the zone. */ if ((zp->z_serial == 0) || (zp->z_flags & Z_NEED_QSERIAL) || (zp->z_dialup == zdialup_no) || (zp->z_dialup == zdialup_use_default && NS_OPTION_P(OPTION_NODIALUP))) qserial_query(zp); else { ns_info(ns_log_default, "Suppressed qserial_query(%s)", *(zp->z_origin) ? zp->z_origin : "."); ns_refreshtime(zp, tt.tv_sec); } break; #ifdef BIND_UPDATE case Z_PRIMARY: if ((zp->z_flags & Z_DYNAMIC) == 0) break; if (tt.tv_sec >= zp->z_soaincrtime && zp->z_soaincrintvl > 0 && zp->z_flags & Z_NEED_SOAUPDATE) { if (incr_serial(zp) < 0) { /* Try again later. */ ns_error(ns_log_maint, "error updating serial number for %s from %d", zp->z_origin, zp->z_serial); zp->z_soaincrtime = 0; (void)schedule_soa_update(zp, 0); } } if (tt.tv_sec >= zp->z_dumptime && zp->z_dumpintvl > 0 && zp->z_flags & Z_NEED_DUMP) { if (zonedump(zp, ISNOTIXFR) < 0) { /* Try again later. */ ns_error(ns_log_maint, "zone dump for '%s' failed, rescheduling", zp->z_origin); zp->z_dumptime = 0; (void)schedule_dump(zp); } - if (zp->z_maintain_ixfr_base) - ixfr_log_maint(zp); } + if (zp->z_maintain_ixfr_base) + ixfr_log_maint(zp); break; #endif /* BIND_UPDATE */ default: break; } /* * It is essential that we never try to set a timer in the past * or for now because doing so could cause an infinite loop. */ INSIST(zp->z_time == 0 || zp->z_time > tt.tv_sec); sched_zone_maint(zp); } static void do_zone_maint(evContext ctx, void *uap, struct timespec due, struct timespec inter) { ztimer_info zti = uap; struct zoneinfo *zp; INSIST(zti != NULL); ns_debug(ns_log_maint, 1, "do_zone_maint for zone %s (class %s)", zti->name, p_class(zti->class)); zp = find_zone(zti->name, zti->class); if (zp == NULL) { ns_error(ns_log_maint, "do_zone_maint: %s zone '%s' (class %s) is not authoritative", zoneTypeString(zti->type), zti->name, p_class(zti->class)); return; } if (zp->z_type != zti->type) { ns_error(ns_log_maint, "do_zone_maint: %s zone '%s' (class %s) has changed its type", zoneTypeString(zti->type), zti->name, p_class(zti->class)); return; } free_zone_timerinfo(zp); zp->z_flags &= ~Z_TIMER_SET; zone_maint(zp); } /* * Figure out the next maintenance time for the zone and set a timer. */ void sched_zone_maint(struct zoneinfo *zp) { time_t next_maint = (time_t)0; ztimer_info zti; if (zp->z_time != 0) next_maint = zp->z_time; #ifdef BIND_UPDATE if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC) != 0) { if (zp->z_soaincrintvl > 0 && (next_maint == 0 || next_maint > zp->z_soaincrtime)) next_maint = zp->z_soaincrtime; if (zp->z_dumpintvl > 0 && (next_maint == 0 || next_maint > zp->z_dumptime)) next_maint = zp->z_dumptime; } #endif if (next_maint != 0) { if (next_maint < tt.tv_sec) next_maint = tt.tv_sec; if (zp->z_flags & Z_TIMER_SET) { if (next_maint == zp->z_nextmaint) { ns_debug(ns_log_maint, 1, "no schedule change for zone '%s'", zp->z_origin[0] == '\0' ? "." : zp->z_origin); return; } if (evResetTimer(ev, zp->z_timer, do_zone_maint, zp->z_timerinfo, evConsTime(next_maint, 0), evConsTime(0, 0)) < 0) { ns_error(ns_log_maint, "evChangeTimer failed in sched_zone_maint for zone '%s': %s", zp->z_origin[0] == '\0' ? "." : zp->z_origin, strerror(errno)); return; } } else { zti = (ztimer_info)memget(sizeof *zti); if (zti == NULL) ns_panic(ns_log_maint, 1, "memget failed in sched_zone_maint"); zti->name = savestr(zp->z_origin, 1); zti->class = zp->z_class; zti->type = zp->z_type; if (evSetTimer(ev, do_zone_maint, zti, evConsTime(next_maint, 0), evConsTime(0, 0), &zp->z_timer) < 0) { ns_error(ns_log_maint, "evSetTimer failed in sched_zone_maint for zone '%s': %s", zp->z_origin[0] == '\0' ? "." : zp->z_origin, strerror(errno)); return; } zp->z_flags |= Z_TIMER_SET; zp->z_timerinfo = zti; } ns_debug(ns_log_maint, 1, "next maintenance for zone '%s' in %lu sec", zp->z_origin[0] == '\0' ? "." : zp->z_origin, (u_long)(next_maint - tt.tv_sec)); } else { if (zp->z_flags & Z_TIMER_SET) { free_zone_timerinfo(zp); if (evClearTimer(ev, zp->z_timer) < 0) ns_error(ns_log_maint, "evClearTimer failed in sched_zone_maint for zone '%s': %s", zp->z_origin[0] == '\0' ? "." : zp->z_origin, strerror(errno)); zp->z_flags &= ~Z_TIMER_SET; } ns_debug(ns_log_maint, 1, "no scheduled maintenance for zone '%s'", zp->z_origin[0] == '\0' ? "." : zp->z_origin); } zp->z_nextmaint = next_maint; } void ns_cleancache(evContext ctx, void *uap, struct timespec due, struct timespec inter) { int deleted; gettime(&tt); INSIST(uap == NULL); deleted = clean_cache(hashtab, 0); ns_info(ns_log_maint, "Cleaned cache of %d RRset%s", deleted, (deleted==1) ? "" : "s"); } void ns_heartbeat(evContext ctx, void *uap, struct timespec due, - struct timespec inter) + struct timespec inter) { struct zoneinfo *zp; gettime(&tt); INSIST(uap == NULL); for (zp = zones; zp < &zones[nzones]; zp++) { enum zonetype zt = zp->z_type; if ((zt == z_nil) || (zp->z_dialup == zdialup_no) || (zp->z_dialup == zdialup_use_default && NS_OPTION_P(OPTION_NODIALUP))) continue; -#ifdef BIND_NOTIFY - if ((zp->z_notify == znotify_no) || - ((zp->z_notify == znotify_use_default) && - NS_OPTION_P(OPTION_NONOTIFY))) - continue; -#endif + /* + * Perform the refresh query that was suppressed. + */ if ((zt == z_slave || zt == z_stub) && (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING) ) == 0) { ns_info(ns_log_default, "Heartbeat: qserial \"%s\"", *(zp->z_origin) ? zp->z_origin : "."); qserial_query(zp); } +#ifdef BIND_NOTIFY + /* + * Trigger a refresh query while the link is up by + * sending a notify. + */ + if (((zp->z_notify == znotify_yes) || + ((zp->z_notify == znotify_use_default) && + !NS_OPTION_P(OPTION_NONOTIFY))) && + (zt == z_master || zt == z_slave) && !loading && + ((zp->z_flags & Z_AUTH) != 0)) + ns_notify(zp->z_origin, zp->z_class, ns_t_soa); +#endif } } /* * Mark a zone "up to date" after named-xfer tells us this or we * discover it through the qserial_*() logic. * The caller is responsible for calling sched_zone_maint(zp). */ static void markUpToDate(struct zoneinfo *zp) { struct stat f_time; zp->z_flags &= ~Z_SYSLOGGED; zp->z_lastupdate = tt.tv_sec; ns_refreshtime(zp, tt.tv_sec); /* * Restore Z_AUTH in case expired, * but only if there were no errors * in the zone file. */ - if ((zp->z_flags & Z_DB_BAD) == 0) + if ((zp->z_flags & Z_DB_BAD) == 0) { zp->z_flags |= Z_AUTH; + zp->z_flags &= ~Z_EXPIRED; + } if (zp->z_source) { struct timeval t[2]; t[0] = tt; t[1] = tt; (void) utimes(zp->z_source, t); } /* we use "stat" to set zp->z_ftime instead of just setting it to tt.tv_sec in order to avoid any possible rounding problems in utimes(). */ if (stat(zp->z_source, &f_time) != -1) zp->z_ftime = f_time.st_mtime; /* XXX log if stat fails? */ } void qserial_retrytime(struct zoneinfo *zp, time_t timebase) { zp->z_time = timebase + 5 + (rand() % 25); } /* * Query for the serial number of a zone, so that we can check to see if * we need to transfer it. If there are too many outstanding serial * number queries, we'll try again later. * The caller is responsible for calling sched_zone_maint(zp). */ void qserial_query(struct zoneinfo *zp) { struct qinfo *qp; ns_debug(ns_log_default, 1, "qserial_query(%s)", zp->z_origin); if (qserials_running >= server_options->serial_queries) { qserial_retrytime(zp, tt.tv_sec); zp->z_flags |= Z_NEED_QSERIAL; return; } qp = sysquery(zp->z_origin, zp->z_class, T_SOA, zp->z_addr, zp->z_addrcnt, ntohs(zp->z_port) ? zp->z_port : ns_port, QUERY); if (qp == NULL) { ns_debug(ns_log_default, 1, "qserial_query(%s): sysquery FAILED", zp->z_origin); /* XXX - this is bad, we should do something */ qserial_retrytime(zp, tt.tv_sec); zp->z_flags |= Z_NEED_QSERIAL; return; } qp->q_flags |= Q_ZSERIAL; qp->q_zquery = zp; zp->z_flags |= Z_QSERIAL; zp->z_flags &= ~Z_NEED_QSERIAL; zp->z_xaddrcnt = 0; ns_refreshtime(zp, tt.tv_sec); qserials_running++; ns_debug(ns_log_default, 1, "qserial_query(%s) QUEUED", zp->z_origin); } static int qserv_compare(const void *a, const void *b) { const struct qserv *qs1 = a, *qs2 = b; u_int32_t s1 = qs1->serial, s2 = qs2->serial; /* Note that we sort the "best" serial numbers to the front. */ if (s1 == s2) return (0); if (s1 == 0) return (-1); if (s2 == 0) return (1); if (!SEQ_GT(s1, s2)) return (1); assert(SEQ_GT(s1, s2)); return (-1); } void qserial_answer(struct qinfo *qp) { struct zoneinfo *zp = qp->q_zquery; struct qserv *qs = NULL; u_int32_t serial = 0; int n, cnt = 0; /* Take this query out of the global quotas. */ zp->z_flags &= ~Z_QSERIAL; qp->q_flags &= ~Q_ZSERIAL; /* keeps us from being called twice */ qserials_running--; /* Find best serial among those returned. */ for (n = 0; n < qp->q_naddr; n++) { qs = &qp->q_addr[n]; ns_debug(ns_log_default, 1, "qserial_answer(%s): [%s] -> %lu", zp->z_origin, inet_ntoa(qs->ns_addr.sin_addr), qs->serial); /* Don't consider serials which weren't set by a response. */ if (qs->serial == 0) continue; /* Count valid answers. */ cnt++; /* Remove from consideration serials which aren't "better." */ if (zp->z_serial != 0 && !SEQ_GT(qs->serial, zp->z_serial)) { if (serial == 0 && qs->serial == zp->z_serial) serial = qs->serial; if (qs->serial != zp->z_serial) ns_notice(ns_log_xfer_in, "Zone \"%s\" (%s) SOA serial# (%lu) rcvd from [%s] is < ours (%lu)%s", zp->z_origin, p_class(zp->z_class), qs->serial, inet_ntoa(qs->ns_addr.sin_addr), zp->z_serial, qp->q_naddr != 1 ? ": skipping" : ""); qs->serial = 0; continue; } if (serial == 0 || SEQ_GT(qs->serial, serial)) serial = qs->serial; } /* If we have an existing serial number, then sort by "better." */ if (zp->z_serial != 0) { qsort(qp->q_addr, qp->q_naddr, sizeof(struct qserv), qserv_compare); for (n = 0; n < qp->q_naddr; n++) { qs = &qp->q_addr[n]; ns_debug(ns_log_default, 1, "qserial_answer after sort: [%s] -> %lu", inet_ntoa(qs->ns_addr.sin_addr), qs->serial); } } /* Now see about kicking off an inbound transfer. */ if (serial == 0) { /* An error occurred, or the all queries timed out. */ if (qp->q_naddr != cnt) ns_info(ns_log_xfer_in, "Err/TO getting serial# for \"%s\"", zp->z_origin); addxfer(zp); } else if (zp->z_serial == 0 || SEQ_GT(serial, zp->z_serial)) { ns_debug(ns_log_xfer_in, 1, "qserial_answer: zone is out of date"); /* Use all servers whose serials are better than ours. */ zp->z_xaddrcnt = 0; for (n = 0; n < qp->q_naddr; n++) { qs = &qp->q_addr[n]; if (qs->serial != 0) zp->z_xaddr[zp->z_xaddrcnt++] = qs->ns_addr.sin_addr; } addxfer(zp); } else if (zp->z_serial == serial) { ns_debug(ns_log_xfer_in, 1, "qserial_answer: zone serial is still OK"); markUpToDate(zp); sched_zone_maint(zp); } } /* * Writes TSIG key info for an address to a file, optionally opening it first. */ static int write_tsig_info(struct in_addr addr, char *name, int *fd, int creat_failed) { server_info si; DST_KEY *dst_key; int tsig_fd = *fd; char tsig_str[1024], secret_buf64[172]; u_char secret_buf[128]; int secret_len; si = find_server(addr); if (si == NULL || si->key_list == NULL || si->key_list->first == NULL) return(0); dst_key = si->key_list->first->key; if (tsig_fd < 0 && creat_failed == 0) { *fd = tsig_fd = creat(name, S_IRUSR); if (tsig_fd < 0) { ns_warning(ns_log_default, "write_tsig_info: creat(%s) for TSIG info failed", name); return(-1); } + (void) fchown(tsig_fd, user_id, group_id); } if (creat_failed != 0) return(-1); memset(secret_buf, 0, sizeof(secret_buf)); secret_len = dst_key_to_buffer(dst_key, secret_buf, sizeof(secret_buf)); b64_ntop(secret_buf, secret_len, secret_buf64, sizeof(secret_buf64)); sprintf(tsig_str, "%s\n%s\n%d\n%s\n", inet_ntoa(addr), dst_key->dk_key_name, dst_key->dk_alg, secret_buf64); write(tsig_fd, tsig_str, strlen(tsig_str)); return (0); } /* * Start an asynchronous zone transfer for a zone. Depends on current time * being in tt. Caller must do a sched_zone_maint(zp) after we return. */ static void startxfer(struct zoneinfo *zp) { char *argv[NSMAX*2 + 20], argv_ns[NSMAX][MAXDNAME]; int argc = 0, argc_ns = 0, i; pid_t pid; u_int cnt; char debug_str[10]; char serial_str[10]; char port_str[10]; char class_str[10]; char src_str[20]; int tsig_fd = -1; char tsig_name[MAXPATHLEN+1], *s; int tsig_ret = 0; ns_debug(ns_log_default, 1, "startxfer() %s", zp->z_origin[0] != '\0' ? zp->z_origin : "."); argv[argc++] = server_options->named_xfer; argv[argc++] = "-z"; argv[argc++] = zp->z_origin; argv[argc++] = "-f"; argv[argc++] = zp->z_source; #ifdef BIND_IXFR if (zp->z_ixfr_tmp) { argv[argc++] = "-i"; argv[argc++] = zp->z_ixfr_tmp; } #endif if (zp->z_serial != 0) { argv[argc++] = "-s"; sprintf(serial_str, "%u", zp->z_serial); argv[argc++] = serial_str; } if (zp->z_axfr_src.s_addr != 0 || server_options->axfr_src.s_addr != 0) { argv[argc++] = "-x"; argv[argc++] = strcpy(src_str, inet_ntoa( (zp->z_axfr_src.s_addr != 0) ? zp->z_axfr_src : server_options->axfr_src)); } argv[argc++] = "-C"; sprintf(class_str, "%d", zp->z_class); argv[argc++] = class_str; if (zp->z_flags & Z_SYSLOGGED) argv[argc++] = "-q"; argv[argc++] = "-P"; sprintf(port_str, "%d", ntohs(zp->z_port) != 0 ? zp->z_port : ns_port); argv[argc++] = port_str; argv[argc++] = "-T"; sprintf(tsig_name, "%s.%d", zp->z_origin, getpid()); s = tsig_name; while ((s = strchr(s, '/')) != NULL) *s = '_'; argv[argc++] = tsig_name; #ifdef STUBS if (zp->z_type == Z_STUB) argv[argc++] = "-S"; #endif #ifdef DEBUG if (debug) { argv[argc++] = "-d"; sprintf(debug_str, "%d", debug); argv[argc++] = debug_str; argv[argc++] = "-l"; argv[argc++] = _PATH_XFERDDT; if (debug > 5) { argv[argc++] = "-t"; argv[argc++] = _PATH_XFERTRACE; } } #endif if (zp->z_xaddrcnt == 0) { for (zp->z_xaddrcnt = 0; zp->z_xaddrcnt < zp->z_addrcnt; zp->z_xaddrcnt++) zp->z_xaddr[zp->z_xaddrcnt] = zp->z_addr[zp->z_xaddrcnt]; } /* * Copy the server ip addresses into argv, after converting * to ascii and saving the static inet_ntoa result. * Also, send TSIG key info into a file for the child. */ for (cnt = 0; cnt < zp->z_xaddrcnt; cnt++) { struct in_addr a; a = zp->z_xaddr[cnt]; if (aIsUs(a) && ns_port == zp->z_port) { if (!haveComplained((u_long)zp, (u_long)startxfer)) ns_notice(ns_log_default, "attempted to fetch zone %s from self (%s)", zp->z_origin, inet_ntoa(a)); continue; } argv[argc++] = strcpy(argv_ns[argc_ns++], inet_ntoa(a)); #ifdef BIND_IXFR if (zp->z_ixfr_tmp != NULL) { server_info si = find_server(a); if (si != NULL && (si->flags & SERVER_INFO_SUPPORT_IXFR) != 0) argv[argc++] = "ixfr"; else argv[argc++] = "axfr"; } #endif tsig_ret = write_tsig_info(a, tsig_name, &tsig_fd, tsig_ret); } if (tsig_fd > 0) close(tsig_fd); argv[argc] = NULL; #ifdef DEBUG if (debug >= 1) { char buffer[1024]; char *curr, *last; int len; curr = buffer; last = &buffer[sizeof buffer - 1]; /* leave room for \0 */ for (i = 0; i < argc; i++) { len = strlen(argv[i]); if (curr + len + 1 >= last) { ns_debug(ns_log_xfer_in, 1, "xfer args debug printout truncated"); break; } strncpy(curr, argv[i], len); curr += len; *curr = ' '; curr++; } *curr = '\0'; ns_debug(ns_log_xfer_in, 1, buffer); - } + } #endif /* DEBUG */ gettime(&tt); for (i = 0; i < MAX_XFERS_RUNNING; i++) if (xferstatus[i].xfer_pid == 0) break; if (i == MAX_XFERS_RUNNING) { ns_warning(ns_log_default, "startxfer: too many xfers running"); zp->z_time = tt.tv_sec + 10; (void)nxfers(zp, -1); return; } if ((pid = spawnxfer(argv, zp)) == -1) unlink(tsig_name); xferstatus[i].xfer_state = XFER_RUNNING; xferstatus[i].xfer_pid = pid; /* XXX - small race condition here if we * can't hold signals */ ns_debug(ns_log_default, 1, "started xfer child %d", pid); zp->z_flags &= ~Z_NEED_XFER; zp->z_flags |= Z_XFER_RUNNING; zp->z_xferpid = pid; xfers_running++; if (zp->z_max_transfer_time_in) zp->z_time = tt.tv_sec + zp->z_max_transfer_time_in; else zp->z_time = tt.tv_sec + server_options->max_transfer_time_in; } const char * zoneTypeString(u_int type) { static char ret[sizeof "(4294967296?)"]; /* 2^32 */ switch (type) { case Z_MASTER: return ("master"); case Z_SLAVE: return ("slave"); #ifdef STUBS case Z_STUB: return ("stub"); #endif case Z_HINT: return ("hint"); case Z_CACHE: return ("cache"); case Z_FORWARD: return ("forward"); default: sprintf(ret, "(%u?)", type); return (ret); } } #ifdef DEBUG void printzoneinfo(int zonenum, int category, int level) { struct timeval tt; struct zoneinfo *zp = &zones[zonenum]; if (debug == 0) return; if (!zp->z_origin) return; gettime(&tt); ns_debug(category, level, "zone %d: %s, class %s, type %s", zonenum, zp->z_origin[0] ? zp->z_origin : ".", p_class(zp->z_class), zoneTypeString(zp->z_type)); if (zp->z_source) ns_debug(category, level, "\tsource %s", zp->z_source); ns_debug(category, level, "\tflags %lx, serial %u, minimum %u", (u_long)zp->z_flags, zp->z_serial, zp->z_minimum); ns_debug(category, level, "\trefresh %u, retry %u, expire %u", zp->z_refresh, zp->z_retry, zp->z_expire); if (zp->z_time) ns_debug(category, level, "\tz_time %lu (now %lu, left: %lu)", zp->z_time, (u_long)tt.tv_sec, (u_long)(zp->z_time - tt.tv_sec)); else ns_debug(category, level, "\tz_time %lu", zp->z_time); #ifdef BIND_UPDATE if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC) != 0) { ns_debug(category, level, "\tdumpintvl %lu, soaincrintvl %lu deferupdcnt %lu", zp->z_dumpintvl, zp->z_soaincrintvl, zp->z_deferupdcnt); if (zp->z_soaincrtime) ns_debug(category, level, "\tz_soaincrtime %lu (now %lu, left: %lu)", zp->z_soaincrtime, (u_long)tt.tv_sec, (u_long)(zp->z_soaincrtime - tt.tv_sec)); else ns_debug(category, level, "\tz_soaincrtime %lu", zp->z_soaincrtime); if (zp->z_dumptime) ns_debug(category, level, "\tz_dumptime %lu (now %lu, left: %lu)", zp->z_dumptime, (u_long)tt.tv_sec, (u_long)(zp->z_dumptime - tt.tv_sec)); else ns_debug(category, level, "\tz_dumptime %lu", zp->z_dumptime); } #endif } #endif /* DEBUG */ /* * Remove all cached data below dname, class independent. */ void clean_cache_from(char *dname, struct hashbuf *htp) { const char *fname; struct databuf *dp, *pdp; struct namebuf *np; struct hashbuf *phtp = htp; int root_zone = 0; ns_debug(ns_log_default, 1, "clean_cache_from(%s)", dname); if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && !ns_wildcard(NAME(*np))) { for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { if (dp->d_zone == DB_Z_CACHE) dp = rm_datum(dp, np, pdp, NULL); else { pdp = dp; dp = dp->d_next; } } if (*dname == '\0') root_zone = 1; if (np->n_hash != NULL || root_zone) { struct hashbuf *h; if (root_zone) h = htp; else h = np->n_hash; (void)clean_cache(h, 1); if (h->h_cnt == 0 && !root_zone) { rm_hash(np->n_hash); np->n_hash = NULL; } } if (!root_zone && np->n_hash == NULL && np->n_data == NULL) (void) purge_node(htp, np); } } /* clean_cache(htp, all) * Scan the entire cache looking for expired TTL's on nonauthoritative * data, and remove it. if `all' is true, ignore TTL and rm everything. * notes: * this should be lazy and eventlib driven. * return: * number of deleted RRs (all=1) or RRsets (all=0). */ int clean_cache(struct hashbuf *htp, int all) { struct databuf *dp, *pdp; struct namebuf *np, *pnp, *npn; struct namebuf **npp, **nppend; int deleted = 0; nppend = htp->h_tab + htp->h_size; for (npp = htp->h_tab; npp < nppend; npp++) { for (pnp = NULL, np = *npp; np != NULL; np = npn) { again: for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { if (all && dp->d_zone == DB_Z_CACHE) { dp = rm_datum(dp, np, pdp, NULL); deleted++; } else if (dp->d_zone == DB_Z_CACHE && stale(dp)) { delete_all(np, dp->d_class, dp->d_type); deleted++; goto again; } else { pdp = dp; dp = dp->d_next; } } /*for(pdp)*/ if (np->n_hash) { /* Call recursively to remove subdomains. */ deleted += clean_cache(np->n_hash, all); /* If now empty, free it */ if (np->n_hash->h_cnt == 0) { rm_hash(np->n_hash); np->n_hash = NULL; } } if (np->n_hash == NULL && np->n_data == NULL) { npn = rm_name(np, npp, pnp); htp->h_cnt--; } else { npn = np->n_next; pnp = np; } } /*for(pnp)*/ } /*for(npp)*/ return (deleted); } /* struct namebuf * * purge_node(htp, np) * Remove entry from cache. * Prerequisites: * Node is empty and has no children. * Paramters: * htp - root of recursive hash table this node is part of. * np - the node to be deleted. * Return: * pointer to parent. */ struct namebuf * purge_node(struct hashbuf *htp, struct namebuf *np) { struct namebuf **npp, **nppend; struct namebuf *npn, *pnp, *nnp, *parent; struct hashbuf *phtp; ns_debug(ns_log_default, 3, "purge_node: cleaning cache"); INSIST(np->n_hash == NULL && np->n_data == NULL); /* Walk parent hashtable looking for ourself. */ parent = np->n_parent; if (parent != NULL) phtp = parent->n_hash; else phtp = htp; if (phtp == NULL) { /* XXX why shouldn't we panic? */ } else { nppend = phtp->h_tab + phtp->h_size; for (npp = phtp->h_tab; npp < nppend; npp++) { for (pnp = NULL, nnp = *npp; nnp != NULL; nnp = npn) { if (nnp == np) { ns_debug(ns_log_default, 3, "purge_node: found ourself"); npn = rm_name(nnp, npp, pnp); phtp->h_cnt--; } else { npn = nnp->n_next; pnp = nnp; } } } } return (parent); } void remove_zone(struct zoneinfo *zp, const char *verb) { #ifdef BIND_UPDATE /* * A dynamic zone might have changed, so we * need to dump it before removing it. */ if ((zp->z_flags & Z_DYNAMIC) != 0 && ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || (zp->z_flags & Z_NEED_DUMP) != 0)) (void) zonedump(zp, ISNOTIXFR); #endif + if ((zp->z_flags & Z_NOTIFY) != 0) + ns_stopnotify(zp->z_origin, zp->z_class); ns_stopxfrs(zp); do_reload(zp->z_origin, zp->z_type, zp->z_class, 1); ns_notice(ns_log_config, "%s zone \"%s\" (%s) %s", zoneTypeString(zp->z_type), zp->z_origin, p_class(zp->z_class), verb); free_zone_contents(zp, 1); memset(zp, 0, sizeof(*zp)); zp->z_type = z_nil; /* Pedantic; memset() did it. */ INIT_LINK(zp, z_reloadlink); free_zone(zp); } +int +purge_nonglue(const char *dname, struct hashbuf *htp, int class) { + const char *fname; + struct namebuf *np; + struct hashbuf *phtp = htp; + int root_zone = 0; + int errs = 0; + + ns_debug(ns_log_default, 1, "purge_zone(%s,%d)", dname, class); + if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && + !ns_wildcard(NAME(*np))) { + + if (*dname == '\0') + root_zone = 1; + + if (np->n_hash != NULL || root_zone) { + struct hashbuf *h; + + if (root_zone) + h = htp; + else + h = np->n_hash; + errs += purge_nonglue_2(dname, h, class, 0); + if (h->h_cnt == 0 && !root_zone) { + rm_hash(np->n_hash); + np->n_hash = NULL; + } + } + } + return (errs); +} + +static int +valid_glue(struct databuf *dp, char *name, int belowcut) { + + /* NS records are only valid glue at the zone cut */ + if (belowcut && dp->d_type == T_NS) + return(0); + + if (ISVALIDGLUE(dp)) /* T_NS/T_A/T_AAAA/T_A6 */ + return (1); + + if (belowcut) + return (0); + + /* Parent NXT record? */ + if (dp->d_type == T_NXT && !ns_samedomain((char*)dp->d_data, name) && + ns_samedomain((char*)dp->d_data, zones[dp->d_zone].z_origin)) + return (1); + + /* NOKEY is in parent zone otherwise child zone */ + if (dp->d_type == T_KEY && dp->d_size == 4 && + (dp->d_data[0] & 0xc6) == 0xc2) + return (1); + + /* NXT & KEY records may be signed */ + if (!belowcut && dp->d_type == T_SIG && + (SIG_COVERS(dp) == T_NXT || SIG_COVERS(dp) == T_KEY)) + return (1); + return (0); +} + +static int +purge_nonglue_2(const char *dname, struct hashbuf *htp, int class, + int belowcut) +{ + struct databuf *dp, *pdp; + struct namebuf *np, *pnp, *npn; + struct namebuf **npp, **nppend; + int errs = 0; + int zonecut; + char name[MAXDNAME]; + + nppend = htp->h_tab + htp->h_size; + for (npp = htp->h_tab; npp < nppend; npp++) { + for (pnp = NULL, np = *npp; np != NULL; np = npn) { + if (!bottom_of_zone(np->n_data, class)) { + zonecut = belowcut; + for (dp = np->n_data; dp != NULL; + dp = dp->d_next) { + if (match(dp, class, ns_t_ns)) { + zonecut = 1; + break; + } + } + getname(np, name, sizeof name); + for (pdp = NULL, dp = np->n_data; + dp != NULL; + (void)NULL) { + if (dp->d_class == class && + zonecut && + !valid_glue(dp, name, belowcut)) { + ns_error(ns_log_db, + "zone: %s/%s: non-glue record %s bottom of zone: %s/%s", + *dname ? dname : ".", + p_class(dp->d_class), + belowcut ? "below" : + "at", + *name ? name : ".", + p_type(dp->d_type)); + dp = rm_datum(dp, np, pdp, + NULL); + errs++; + } else { + pdp = dp; + dp = dp->d_next; + } + } + if (np->n_hash) { + /* + * call recursively to clean + * subdomains + */ + errs += purge_nonglue_2(dname, + np->n_hash, + class, + zonecut || + belowcut); + + /* if now empty, free it */ + if (np->n_hash->h_cnt == 0) { + rm_hash(np->n_hash); + np->n_hash = NULL; + } + } + } + + if (np->n_hash == NULL && np->n_data == NULL) { + npn = rm_name(np, npp, pnp); + htp->h_cnt--; + } else { + npn = np->n_next; + pnp = np; + } + } + } + return (errs); +} + void purge_zone(const char *dname, struct hashbuf *htp, int class) { const char *fname; struct databuf *dp, *pdp; struct namebuf *np; struct hashbuf *phtp = htp; int root_zone = 0; ns_debug(ns_log_default, 1, "purge_zone(%s,%d)", dname, class); if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && !ns_wildcard(NAME(*np))) { for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { if (dp->d_class == class) dp = rm_datum(dp, np, pdp, NULL); else { pdp = dp; dp = dp->d_next; } } if (*dname == '\0') root_zone = 1; if (np->n_hash != NULL || root_zone) { struct hashbuf *h; if (root_zone) h = htp; else h = np->n_hash; purge_z_2(h, class); if (h->h_cnt == 0 && !root_zone) { rm_hash(np->n_hash); np->n_hash = NULL; } } if (!root_zone && np->n_hash == NULL && np->n_data == NULL) (void) purge_node(htp, np); } } static void purge_z_2(htp, class) struct hashbuf *htp; int class; { struct databuf *dp, *pdp; struct namebuf *np, *pnp, *npn; struct namebuf **npp, **nppend; nppend = htp->h_tab + htp->h_size; for (npp = htp->h_tab; npp < nppend; npp++) { for (pnp = NULL, np = *npp; np != NULL; np = npn) { if (!bottom_of_zone(np->n_data, class)) { for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { if (dp->d_class == class) dp = rm_datum(dp, np, pdp, NULL); else { pdp = dp; dp = dp->d_next; } } if (np->n_hash) { /* call recursively to rm subdomains */ purge_z_2(np->n_hash, class); /* if now empty, free it */ if (np->n_hash->h_cnt == 0) { rm_hash(np->n_hash); np->n_hash = NULL; } } } if (np->n_hash == NULL && np->n_data == NULL) { npn = rm_name(np, npp, pnp); htp->h_cnt--; } else { npn = np->n_next; pnp = np; } } } } static int bottom_of_zone(struct databuf *dp, int class) { int ret = 0; for ((void)NULL; dp; dp = dp->d_next) { if (dp->d_class != class) continue; if (dp->d_zone == DB_Z_CACHE) continue; if (dp->d_rcode) /* This should not occur. */ continue; if (dp->d_type != T_SOA) continue; ret = 1; break; } ns_debug(ns_log_default, 3, "bottom_of_zone() == %d", ret); return (ret); } - + /* * Handle XFER limit for a nameserver. */ static int nxfers(struct zoneinfo *zp, int delta) { struct in_addr nsa; struct nameser *nsp; int ret; if (zp->z_xaddrcnt != 0) nsa = zp->z_xaddr[0]; /* first ns holds zone's xfer limit */ else if (zp->z_addrcnt != 0) nsa = zp->z_addr[0]; /* first ns holds zone's xfer limit */ else return (-1); if (!(nsp = nameserFind(nsa, NS_F_INSERT))) return (-1); /* probably ENOMEM */ ret = nsp->xfers; if (delta < 0 && -delta > ret) return (-1); /* taking more than we have */ nsp->xfers += delta; return (ret); } /* * Abort an xfer that has taken too long. */ static void abortxfer(struct zoneinfo *zp) { if (zp->z_flags & (Z_XFER_GONE|Z_XFER_ABORTED)) { int i; for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_pid == zp->z_xferpid) { xferstatus[i].xfer_pid = 0; xferstatus[i].xfer_state = XFER_IDLE; break; } } if (zp->z_flags & Z_XFER_GONE) ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; pid %lu missing", zp->z_origin, (u_long)zp->z_xferpid); else if (kill(zp->z_xferpid, SIGKILL) == -1) ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; kill pid %lu: %s", zp->z_origin, (u_long)zp->z_xferpid, strerror(errno)); else ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; second kill \ pid %lu - forgetting, processes may accumulate", zp->z_origin, (u_long)zp->z_xferpid); zp->z_xferpid = 0; xfers_running--; (void)nxfers(zp, -1); zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); } else if (kill(zp->z_xferpid, SIGTERM) == -1) { if (errno == ESRCH) /* No warning on first time, it may have just exited */ zp->z_flags |= Z_XFER_GONE; else { ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; pid %lu kill failed %s", zp->z_origin, (u_long)zp->z_xferpid, strerror(errno)); zp->z_flags |= Z_XFER_ABORTED; } } else { ns_notice(ns_log_default, "zone transfer timeout for \"%s\"; pid %lu killed", zp->z_origin, (u_long)zp->z_xferpid); zp->z_flags |= Z_XFER_ABORTED; } } /* * Process exit of xfer's. */ void reapchild(void) { int i; pid_t pid; WAIT_T status; gettime(&tt); while ((pid = (pid_t)waitpid(-1, &status, WNOHANG)) > 0) { for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_pid == pid) { xferstatus[i].xfer_status = status; xferstatus[i].xfer_state = XFER_DONE; ns_need(main_need_endxfer); break; } } } } /* * Finish processing of of finished xfers */ void endxfer() { - struct zoneinfo *zp; + struct zoneinfo *zp; int exitstatus, i; pid_t pid; WAIT_T status; gettime(&tt); for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_state != XFER_DONE) continue; pid = xferstatus[i].xfer_pid; status = xferstatus[i].xfer_status; exitstatus = WIFEXITED(status) ? WEXITSTATUS(status) : 0; for (zp = zones; zp < &zones[nzones]; zp++) { if (zp->z_xferpid != pid) continue; xfers_running--; (void) nxfers(zp, -1); zp->z_xferpid = 0; zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); ns_debug(ns_log_default, 1, "\nendxfer: child %d zone %s returned status=%d termsig=%d", pid, zp->z_origin, exitstatus, WIFSIGNALED(status) ? WTERMSIG(status) : -1); if (WIFSIGNALED(status)) { if (WTERMSIG(status) != SIGKILL) { ns_notice(ns_log_default, - "named-xfer \"%s\" exited with signal %d", - zp->z_origin[0]?zp->z_origin:".", + "named-xfer \"%s\" exited with signal %d", + zp->z_origin[0]?zp->z_origin:".", WTERMSIG(status)); } ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); } else { switch (exitstatus) { case XFER_UPTODATE: markUpToDate(zp); sched_zone_maint(zp); break; case XFER_SUCCESSAXFR: case XFER_SUCCESSAXFRIXFRFILE: zp->z_xferpid = XFER_ISAXFR; if (exitstatus == XFER_SUCCESSAXFRIXFRFILE) { zp->z_xferpid = XFER_ISAXFRIXFR; } movefile(zp->z_ixfr_tmp, zp->z_source); /* XXX should incorporate loadxfer() */ zp->z_flags |= Z_NEED_RELOAD; zp->z_flags &= ~Z_SYSLOGGED; ns_need(main_need_zoneload); break; case XFER_SUCCESSIXFR: + zp->z_flags |= Z_XFER_RUNNING; zp->z_xferpid = XFER_ISIXFR; - zp->z_log_size_ixfr++; ns_notice(ns_log_default, "IXFR Success %s", zp->z_ixfr_tmp); if (merge_logs(zp, zp->z_ixfr_tmp) >= 0) { ns_notice(ns_log_default, "IXFR Merge success %s", zp->z_ixfr_tmp); (void)unlink(zp->z_updatelog); (void)unlink(zp->z_ixfr_base); movefile(zp->z_ixfr_tmp, zp->z_ixfr_base); (void)unlink(zp->z_ixfr_tmp); if (zonedump(zp, ISIXFR) < 0) ns_warning(ns_log_db, "error in write ixfr updates to zone file %s", zp ->z_source); } else ns_notice(ns_log_default, "IXFR Merge failed %s", zp->z_ixfr_tmp); + zp->z_flags &= + ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); break; case XFER_TIMEOUT: if (!(zp->z_flags & Z_SYSLOGGED)) { zp->z_flags |= Z_SYSLOGGED; ns_notice(ns_log_default, "zoneref: Masters for secondary zone \"%s\" unreachable", zp->z_origin); } ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); break; default: if (!(zp->z_flags & Z_SYSLOGGED)) { zp->z_flags |= Z_SYSLOGGED; ns_notice(ns_log_default, "named-xfer for \"%s\" exited %d", zp->z_origin, exitstatus); } /* FALLTHROUGH */ case XFER_FAIL: zp->z_flags |= Z_SYSLOGGED; ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); break; } break; } } xferstatus[i].xfer_state = XFER_IDLE; xferstatus[i].xfer_pid = 0; } tryxfer(); } /* * Try to start some xfers - new "fair scheduler" by Bob Halley @DEC (1995) */ static void tryxfer() { static struct zoneinfo *zp = NULL; static struct zoneinfo *lastzones = NULL; static int lastnzones = 0; struct zoneinfo *startzp, *stopzp; /* initialize, and watch out for changes in zones! */ if (lastzones != zones) { if (lastzones != NULL) ns_debug(ns_log_default, 3, "zones changed: %p != %p", lastzones, zones); lastzones = zones; zp = zones; } /* did zones shrink? */ if (lastnzones > nzones) { ns_debug(ns_log_default, 3, "zones shrunk"); zp = zones; } lastnzones = nzones; - + if (zp == zones) stopzp = &zones[nzones-1]; else stopzp = zp - 1; ns_debug(ns_log_default, 3, "tryxfer start zp=%p stopzp=%p def=%d running=%d", zp, stopzp, xfers_deferred, xfers_running); startzp = zp; for (;;) { int xfers; if (!xfers_deferred || xfers_running >= server_options->transfers_in) break; if ((xfers = nxfers(zp, 0)) != -1 && xfers < server_options->transfers_per_ns && (zp->z_flags & Z_NEED_XFER)) { nxfers(zp, 1); xfers_deferred--; startxfer(zp); sched_zone_maint(zp); } if (zp == stopzp) { ns_debug(ns_log_default, 3, "tryxfer stop mark"); zp = startzp; break; } zp++; /* wrap around? */ if (zp == &zones[nzones]) zp = zones; } ns_debug(ns_log_default, 3, "tryxfer stop zp=%p", zp); } /* * Reload zones whose transfers have completed. */ void loadxfer(void) { - struct zoneinfo *zp; + struct zoneinfo *zp; u_int32_t old_serial,new_serial; char *tmpnom; int isixfr; gettime(&tt); for (zp = zones; zp < &zones[nzones]; zp++) { if (zp->z_flags & Z_NEED_RELOAD) { ns_debug(ns_log_default, 1, "loadxfer() \"%s\"", zp->z_origin[0] ? zp->z_origin : "."); zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH); /* XXX this is bad, should be done in ns_zreload() for primary changes. */ ns_stopxfrs(zp); old_serial = zp->z_serial; if (zp->z_xferpid == XFER_ISIXFR) { tmpnom = zp->z_ixfr_tmp; isixfr = ISIXFR; } else { tmpnom = zp->z_source; purge_zone(zp->z_origin, hashtab, zp->z_class); isixfr = ISNOTIXFR; } if (zp->z_xferpid == XFER_ISAXFRIXFR) { tmpnom= zp->z_source; purge_zone(zp->z_origin, hashtab, zp->z_class); isixfr = ISNOTIXFR; } if (!db_load(tmpnom, zp->z_origin, zp, NULL, isixfr)) { zp->z_flags |= Z_AUTH; + zp->z_flags &= ~Z_EXPIRED; if (isixfr == ISIXFR) { new_serial= zp ->z_serial; ns_warning(ns_log_db, "ISIXFR"); ns_warning(ns_log_db, "error in updating ixfr data base file %s from %s", zp -> z_ixfr_base, zp ->z_ixfr_tmp); - if (zonedump(zp,ISIXFR)<0) + if (zonedump(zp,ISIXFR)<0) ns_warning(ns_log_db, "error in write ixfr updates to zone file %s", zp ->z_source); } } zp->z_xferpid = 0; if (zp->z_flags & Z_TMP_FILE) (void) unlink(zp->z_source); sched_zone_maint(zp); } } } /* * Add this zone to the set of those needing transfers. */ void addxfer(struct zoneinfo *zp) { if (!(zp->z_flags & Z_NEED_XFER)) { zp->z_flags |= Z_NEED_XFER; xfers_deferred++; tryxfer(); } } /* * Mark one zone as requiring a reload. * Note that it should be called with signals blocked, * and should not allocate memory (since it can be called from a sighandler). */ const char * deferred_reload_unsafe(struct zoneinfo *zp) { INSIST(zp->z_type != z_nil); if (!zonefile_changed_p(zp)) return ("Zone file has not changed."); if (LINKED(zp, z_reloadlink)) return ("Zone is already scheduled for reloading."); APPEND(reloadingzones, zp, z_reloadlink); ns_need_unsafe(main_need_zreload); return ("Zone is now scheduled for reloading."); } /* * If we've loaded this file, and the file has not been modified and contains * no $INCLUDE, then there's no need to reload. */ int zonefile_changed_p(struct zoneinfo *zp) { struct stat sb; INSIST(zp->z_type != z_nil); return ((zp->z_flags & Z_INCLUDE) != 0 || stat(zp->z_source, &sb) == -1 || zp->z_ftime != sb.st_mtime); } int reload_master(struct zoneinfo *zp) { INSIST(zp->z_type == z_master); zp->z_flags &= ~Z_AUTH; ns_stopxfrs(zp); /* XXX what about parent zones? */ +#ifdef BIND_UPDATE + /* + * A dynamic zone might have changed, so we + * need to dump it before reloading it. + */ + if ((zp->z_flags & Z_DYNAMIC) != 0 && + ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || + (zp->z_flags & Z_NEED_DUMP) != 0)) + (void) zonedump(zp, ISNOTIXFR); +#endif purge_zone(zp->z_origin, hashtab, zp->z_class); ns_debug(ns_log_config, 1, "reloading zone"); #ifdef BIND_UPDATE if ((zp->z_flags & Z_DYNAMIC) != 0) { struct stat sb; if (stat(zp->z_source, &sb) < 0) ns_error(ns_log_config, "stat(%s) failed: %s", zp->z_source, strerror(errno)); else { if ((sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) ns_warning(ns_log_config, "dynamic zone file '%s' is writable", zp->z_source); } } #endif if (!db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR)) zp->z_flags |= Z_AUTH; zp->z_refresh = 0; /* no maintenance needed */ zp->z_time = 0; #ifdef BIND_UPDATE zp->z_lastupdate = 0; if ((zp->z_flags & Z_DYNAMIC) != 0) if (merge_logs(zp, zp->z_updatelog) == 1) return (1); #endif return (0); } /* * Called by main() when main_need_zreload has been set. Should pull one * zone off of the reloadingzones list and reload it, then if the list is * not then empty, should turn main_need_zreload on again for the next call. * It is not an error to call this when the reloadingzones list is empty. */ void ns_zreload(void) { struct zoneinfo *zp; block_signals(); if (EMPTY(reloadingzones)) { unblock_signals(); return; } zp = HEAD(reloadingzones); UNLINK(reloadingzones, zp, z_reloadlink); unblock_signals(); reload_master(zp); block_signals(); if (!EMPTY(reloadingzones)) ns_need_unsafe(main_need_zreload); unblock_signals(); } /* * Flush and reload configuration file and data base. */ void ns_reload(void) { - ns_notice(ns_log_default, "reloading nameserver"); + ns_notice(ns_log_default, "%s %snameserver", + (reconfiging != 0) ? "reconfiguring" : "reloading", + (noexpired == 1) ? "(-noexpired) " : ""); INSIST(reloading == 0); qflush(); sq_flush(NULL); reloading++; /* To force transfer if secondary and backing up. */ ns_init(conffile); time(&resettime); reloading--; ns_notice(ns_log_default, "Ready to answer queries."); +} + +/* + * Reload configuration, look for new or deleted zones, not changed ones + * also ignore expired zones. + */ +void +ns_noexpired(void) { + INSIST(noexpired == 0); + noexpired++; /* To ignore zones which are expired */ + ns_reconfig(); + noexpired--; } /* * Reload configuration, look for new or deleted zones, not changed ones. */ void ns_reconfig(void) { INSIST(reconfiging == 0); reconfiging++; /* To ignore zones which aren't new or deleted. */ ns_reload(); reconfiging--; } void make_new_zones(void) { struct zoneinfo *zp; int n; ns_debug(ns_log_config, 1, "Adding %d template zones", NEWZONES); zp = (struct zoneinfo *) memget((nzones + NEWZONES) * sizeof(struct zoneinfo)); if (zp == NULL) panic("no memory for more zones", NULL); memset(zp, 0, (nzones + NEWZONES) * sizeof(struct zoneinfo)); if (zones != NULL) { memcpy(zp, zones, nzones * sizeof(struct zoneinfo)); memput(zones, nzones * sizeof(struct zoneinfo)); } zones = zp; block_signals(); for (n = 0; n < NEWZONES; n++) { INIT_LINK(&zones[nzones], z_reloadlink); if (nzones != 0) free_zone(&zones[nzones]); nzones++; } unblock_signals(); } void free_zone(struct zoneinfo *zp) { if (LINKED(zp, z_reloadlink)) panic("freeing reloading zone", NULL); if (zp->z_type != z_nil) panic("freeing unfree zone", NULL); APPEND(freezones, zp, z_freelink); } #ifndef HAVE_SPAWNXFER static pid_t spawnxfer(char **argv, struct zoneinfo *zp) { pid_t pid = (pid_t)vfork(); if (pid == -1) { ns_error(ns_log_default, "xfer vfork: %s", strerror(errno)); zp->z_time = tt.tv_sec + 10; return (pid); } if (pid == 0) { /* Child. */ execv(server_options->named_xfer, argv); ns_error(ns_log_default, "can't exec %s: %s", server_options->named_xfer, strerror(errno)); (void)nxfers(zp, -1); _exit(XFER_FAIL); /* Avoid duplicate buffer flushes. */ } return (pid); } #endif struct zoneinfo * find_auth_zone(const char *zname, ns_class zclass) { struct zoneinfo *zp; struct hashbuf *htp; struct namebuf *np; const char *fname; int zn; zp = find_zone(zname, zclass); if (zp != NULL && (zp->z_type == z_slave || zp->z_type == z_master || zp->z_type == z_stub)) return (zp); htp = hashtab; np = nlookup(zname, &htp, &fname, 0); if (np != NULL && (zn = findMyZone(np, zclass)) != DB_Z_CACHE) return (&zones[zn]); return (NULL); } Index: head/contrib/bind/bin/named/ns_ncache.c =================================================================== --- head/contrib/bind/bin/named/ns_ncache.c (revision 60940) +++ head/contrib/bind/bin/named/ns_ncache.c (revision 60941) @@ -1,270 +1,270 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_ncache.c,v 8.26 1999/10/13 16:39:10 vixie Exp $"; +static const char rcsid[] = "$Id: ns_ncache.c,v 8.27 2000/04/21 06:54:09 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ return; \ } \ } while (0) void cache_n_resp(u_char *msg, int msglen, struct sockaddr_in from, const char *qname, int qclass, int qtype) { struct databuf *dp; HEADER *hp; u_char *cp, *eom, *rdatap; char dname[MAXDNAME]; int n, type, class, flags; u_int ancount, nscount, dlen; #ifdef RETURNSOA u_int32_t ttl; u_int16_t atype; u_char *sp, *cp1; u_char data[MAXDATA]; size_t len = sizeof data; #endif nameserIncr(from.sin_addr, nssRcvdNXD); hp = (HEADER *)msg; cp = msg + HFIXEDSZ; eom = msg + msglen; switch (ntohs(hp->qdcount)) { case 0: dname[sizeof dname - 1] = '\0'; strncpy(dname, qname, sizeof dname); if (dname[sizeof dname - 1] != '\0') { ns_debug(ns_log_ncache, 1, "qp->qname too long (%d)", strlen(qname)); hp->rcode = FORMERR; return; } class = qclass; type = qtype; break; case 1: n = dn_expand(msg, eom, cp, dname, sizeof dname); if (n < 0) { ns_debug(ns_log_ncache, 1, "Query expand name failed: cache_n_resp"); hp->rcode = FORMERR; return; } cp += n; BOUNDS_CHECK(cp, 2 * INT16SZ); GETSHORT(type, cp); GETSHORT(class, cp); if (class > CLASS_MAX) { ns_debug(ns_log_ncache, 1, "bad class in cache_n_resp"); hp->rcode = FORMERR; return; } break; default: ns_debug(ns_log_ncache, 1, "QDCOUNT>1 (%d) in cache_n_resp", ntohs(hp->qdcount)); hp->rcode = FORMERR; return; } ns_debug(ns_log_ncache, 1, "ncache: dname %s, type %d, class %d", dname, type, class); ancount = ntohs(hp->ancount); nscount = ntohs(hp->nscount); while (ancount--) { u_int32_t ttl; u_int atype, aclass; n = dn_skipname(cp, eom); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: form error"); return; } cp += n; BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); GETSHORT(atype, cp); GETSHORT(aclass, cp); if (atype != T_CNAME || aclass != class) { ns_debug(ns_log_ncache, 3, "ncache: not CNAME (%s) or wrong class (%s)", p_type(atype), p_class(aclass)); return; } GETLONG(ttl, cp); GETSHORT(dlen, cp); BOUNDS_CHECK(cp, dlen); rdatap = cp; n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: bad cname target"); return; } cp += n; if (cp != rdatap + dlen) { ns_debug(ns_log_ncache, 3, "ncache: bad cname rdata"); return; } } dp = NULL; #ifdef RETURNSOA while (nscount--) { sp = cp; /* we store NXDOMAIN as T_SOA regardless of the query type */ if (hp->rcode == NXDOMAIN) type = T_SOA; /* store ther SOA record */ n = dn_skipname(cp, msg + msglen); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: form error"); return; } cp += n; BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); GETSHORT(atype, cp); /* type */ cp += INT16SZ; /* class */ GETLONG(ttl, cp); /* ttl */ GETSHORT(dlen, cp); /* dlen */ BOUNDS_CHECK(cp, dlen); if (atype != T_SOA) { ns_debug(ns_log_ncache, 3, "ncache: type (%d) != T_SOA", atype); cp += dlen; continue; } rdatap = cp; /* origin */ n = dn_expand(msg, msg + msglen, cp, (char*)data, len); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: origin form error"); return; } cp += n; n = strlen((char*)data) + 1; cp1 = data + n; len -= n; /* mail */ n = dn_expand(msg, msg + msglen, cp, (char*)cp1, len); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: mail form error"); return; } cp += n; n = strlen((char*)cp1) + 1; cp1 += n; len -= n; n = 5 * INT32SZ; BOUNDS_CHECK(cp, n); memcpy(cp1, cp, n); /* serial, refresh, retry, expire, min */ cp1 += n; len -= n; cp += n; if (cp != rdatap + dlen) { ns_debug(ns_log_ncache, 3, "ncache: form error"); return; } /* store the zone of the soa record */ n = dn_expand(msg, msg + msglen, sp, (char*)cp1, len); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: form error 2"); return; } n = strlen((char*)cp1) + 1; cp1 += n; /* * we only want to store these long enough so that * ns_resp can find it. */ if (qtype == T_SOA && hp->rcode == NXDOMAIN) ttl = 0; dp = savedata(class, type, MIN(ttl, server_options->max_ncache_ttl) + tt.tv_sec, data, cp1 - data); break; } #endif if (dp == NULL) #ifdef STRICT_RFC2308 dp = savedata(class, type, tt.tv_sec, NULL, 0); #else dp = savedata(class, type, NTTL + tt.tv_sec, NULL, 0); #endif dp->d_zone = DB_Z_CACHE; dp->d_cred = hp->aa ? DB_C_AUTH : DB_C_ANSWER; dp->d_secure = DB_S_INSECURE; /* BEW - should be UNCHECKED */ dp->d_clev = 0; if(hp->rcode == NXDOMAIN) { dp->d_rcode = NXDOMAIN; flags = DB_NODATA|DB_NOTAUTH|DB_NOHINTS; } else { dp->d_rcode = NOERROR_NODATA; flags = DB_NOTAUTH|DB_NOHINTS; } if ((n = db_update(dname, dp, dp, NULL, flags, hashtab, from)) != OK) { ns_debug(ns_log_ncache, 1, "db_update failed (%d), cache_n_resp()", n); db_freedata(dp); return; } ns_debug(ns_log_ncache, 4, "ncache succeeded: [%s %s %s] rcode:%d ttl:%ld", dname, p_type(type), p_class(class), dp->d_rcode, (long)(dp->d_ttl - tt.tv_sec)); } Index: head/contrib/bind/bin/named/ns_notify.c =================================================================== --- head/contrib/bind/bin/named/ns_notify.c (revision 60940) +++ head/contrib/bind/bin/named/ns_notify.c (revision 60941) @@ -1,379 +1,424 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_notify.c,v 8.4 1999/10/15 19:49:04 vixie Exp $"; +static const char rcsid[] = "$Id: ns_notify.c,v 8.10 2000/04/21 06:54:09 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1994-1999 by Internet Software Consortium. + * Copyright (c) 1994-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #ifdef BIND_NOTIFY /* Types. */ struct notify { char * name; ns_class class; ns_type type; evTimerID timer; LINK(struct notify) link; }; /* Forward. */ static void sysnotify(const char *, ns_class, ns_type); static void sysnotify_slaves(const char *, const char *, ns_class, ns_type, int, int *, int *); static void sysnotify_ns(const char *, const char *, ns_class, ns_type, int, int *, int *); static void free_notify(struct notify *); static void notify_timer(evContext, void *, struct timespec, struct timespec); /* Local. */ static LIST(struct notify) pending_notifies; +static LIST(struct notify) loading_notifies; /* Public. */ /* * ns_notify(dname, class, type) * call this when a zone has changed and its slaves need to know. */ void ns_notify(const char *dname, ns_class class, ns_type type) { static const char no_room[] = "%s failed, cannot notify for zone %s"; int delay, max_delay; struct zoneinfo *zp; struct notify *ni; zp = find_auth_zone(dname, class); if (zp == NULL) { ns_warning(ns_log_notify, "no zone found for notify (\"%s\" %s %s)", (dname && *dname) ? dname : ".", p_class(class), p_type(type)); return; } + if (ns_samename(dname, zp->z_origin) != 1) { + ns_warning(ns_log_notify, + "notify not called with top of zone (\"%s\" %s %s)", + (dname && *dname) ? dname : ".", + p_class(class), p_type(type)); + return; + } if ((zp->z_flags & Z_NOTIFY) != 0) { ns_info(ns_log_notify, "suppressing duplicate notify (\"%s\" %s %s)", (dname && *dname) ? dname : ".", p_class(class), p_type(type)); return; } ni = memget(sizeof *ni); if (ni == NULL) { ns_info(ns_log_notify, no_room, "memget", dname); return; } ni->name = savestr(dname, 0); if (ni->name == NULL) { memput(ni, sizeof *ni); ni = NULL; ns_info(ns_log_notify, no_room, "memget", dname); return; } ni->class = class; ni->type = type; evInitID(&ni->timer); + if (loading != 0) { + APPEND(loading_notifies, ni, link); + return; + } + /* Delay notification for from five seconds up to fifteen minutes. */ max_delay = MIN(nzones/5, 895); max_delay = MAX(max_delay, 25); delay = 5 + (rand() % max_delay); if (evSetTimer(ev, notify_timer, ni, evAddTime(evNowTime(), evConsTime(delay, 0)), evConsTime(0, 0), &ni->timer) < 0) { ns_error(ns_log_notify, "evSetTimer() failed: %s", strerror(errno)); freestr(ni->name); memput(ni, sizeof *ni); return; } zp->z_flags |= Z_NOTIFY; APPEND(pending_notifies, ni, link); ns_debug(ns_log_notify, 3, "ns_notify(%s, %s, %s): ni %p, zp %p, delay %d", (dname && *dname) ? dname : ".", p_class(class), p_type(type), ni, zp, delay); } +void +notify_afterload() { + struct notify *ni; + + INSIST(loading == 0); + while ((ni = HEAD(loading_notifies)) != NULL) { + UNLINK(loading_notifies, ni, link); + ns_notify(ni->name, ni->class, ni->type); + freestr(ni->name); + memput(ni, sizeof *ni); + } +} + /* * ns_unnotify() * call this when all pending notifies are now considered junque. */ void ns_unnotify(void) { while (!EMPTY(pending_notifies)) { struct notify *ni = HEAD(pending_notifies); INSIST(LINKED(ni, link)); UNLINK(pending_notifies, ni, link); free_notify(ni); } } +/* + * ns_stopnotify(const char *dname, ns_class class) + * stop notifies for this particular zone. + */ +void +ns_stopnotify(const char *dname, ns_class class) { + struct notify *ni; + + ni = HEAD(pending_notifies); + while (ni != NULL && + (ni->class != class || ns_samename(ni->name, dname) != 1)) + ni = NEXT(ni, link); + + if (ni != NULL) { + UNLINK(pending_notifies, ni, link); + free_notify(ni); + } +} + /* Private. */ /* * sysnotify(dname, class, type) * cause a NOTIFY request to be sysquery()'d to each slave server * of the zone that "dname" is within. */ static void sysnotify(const char *dname, ns_class class, ns_type type) { const char *zname, *fname; + u_int32_t zserial; int nns, na, i; struct zoneinfo *zp; struct in_addr *also_addr; ns_debug(ns_log_notify, 3, "sysnotify(%s, %s, %s)", dname, p_class(class), p_type(type)); zp = find_auth_zone(dname, class); if (zp == NULL) { ns_warning(ns_log_notify, "sysnotify: can't find \"%s\" (%s)", dname, p_class(class)); return; } if (ns_samename(dname, zp->z_origin) != 1) { ns_warning(ns_log_notify, "sysnotify: not auth for zone %s", dname); return; } if (zp->z_notify == znotify_no || (zp->z_notify == znotify_use_default && NS_OPTION_P(OPTION_NONOTIFY))) return; if (zp->z_type != z_master && zp->z_type != z_slave) { ns_warning(ns_log_notify, "sysnotify: %s not master or slave", dname); return; } zname = zp->z_origin; + zserial = zp->z_serial; nns = na = 0; - if (zp->z_type == z_master) - sysnotify_slaves(dname, zname, class, type, - zp - zones, &nns, &na); + sysnotify_slaves(dname, zname, class, type, zp - zones, &nns, &na); /* * Handle any global or zone-specific also-notify clauses */ if (zp->z_notify_count != 0) { /* zone-specific also notify */ ns_debug(ns_log_notify, 3, "zone notify ns = %d", zp->z_notify_count); also_addr = zp->z_also_notify; for (i = 0; i < zp->z_notify_count; i++) { ns_debug(ns_log_notify, 4, "notifying %s", inet_ntoa(*also_addr)); sysquery(dname, class, type, also_addr, 1, ns_port, NS_NOTIFY_OP); also_addr++; } nns += zp->z_notify_count; na += zp->z_notify_count; } else if (server_options->notify_count != 0) { ns_debug(ns_log_notify, 4, "global notify ns = %d", server_options->notify_count); also_addr = server_options->also_notify; for (i = 0; i < server_options->notify_count; i++) { ns_debug(ns_log_notify, 3, "notifying %s", inet_ntoa(*also_addr)); sysquery(dname, class, type, also_addr, 1, ns_port, ns_o_notify); also_addr++; } nns += server_options->notify_count; na += server_options->notify_count; } if (nns != 0 || na != 0) ns_info(ns_log_notify, - "Sent NOTIFY for \"%s %s %s\" (%s); %d NS, %d A", - dname, p_class(class), p_type(type), zname, nns, na); + "Sent NOTIFY for \"%s %s %s %u\" (%s); %d NS, %d A", + dname, p_class(class), p_type(type), zserial, zname, nns, na); } static void sysnotify_slaves(const char *dname, const char *zname, ns_class class, ns_type type, int zn, int *nns, int *na) { const char *mname, *fname; struct hashbuf *htp; struct namebuf *np; struct databuf *dp; /* * Master. */ htp = hashtab; np = nlookup(zname, &htp, &fname, 0); if (np == NULL) { ns_warning(ns_log_notify, "sysnotify: found name \"%s\" but not zone", dname); return; } mname = NULL; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_zone == DB_Z_CACHE || !match(dp, class, ns_t_soa)) continue; if (dp->d_type == ns_t_sig) continue; if (mname) { ns_notice(ns_log_notify, "multiple SOA's for zone \"%s\"?", zname); return; } mname = (char *) dp->d_data; } if (mname == NULL) { ns_notice(ns_log_notify, "no SOA found for zone \"%s\"", zname); return; } for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_zone == DB_Z_CACHE || !match(dp, class, ns_t_ns)) continue; if (dp->d_type == ns_t_sig) continue; if (ns_samename((char*)dp->d_data, mname) == 1) continue; sysnotify_ns(dname, (char *)dp->d_data, class, type, zn, nns, na); } } static void sysnotify_ns(const char *dname, const char *aname, ns_class class, ns_type type, int zn, int *nns, int *na) { struct databuf *adp; struct namebuf *anp; const char *fname; struct in_addr nss[NSMAX]; struct hashbuf *htp; int is_us, nsc; htp = hashtab; anp = nlookup(aname, &htp, &fname, 0); nsc = 0; is_us = 0; if (anp != NULL) for (adp = anp->n_data; adp; adp = adp->d_next) { struct in_addr ina; if (!match(adp, class, T_A)) continue; if (adp->d_type == ns_t_sig) continue; ina = ina_get(adp->d_data); if (aIsUs(ina)) { is_us = 1; continue; } if (nsc < NSMAX) nss[nsc++] = ina; } /*next A*/ if (nsc == 0) { - if (!is_us) { + if (!is_us && !NS_OPTION_P(OPTION_NOFETCHGLUE)) { struct qinfo *qp; qp = sysquery(aname, class, ns_t_a, 0, 0, ns_port, ns_o_query); if (qp != NULL) qp->q_notifyzone = zn; } return; } sysquery(dname, class, type, nss, nsc, ns_port, ns_o_notify); (*nns)++; *na += nsc; } static void free_notify(struct notify *ni) { struct zoneinfo *zp; INSIST(!LINKED(ni, link)); zp = find_auth_zone(ni->name, ni->class); - if (zp != NULL) { + if (zp != NULL && ns_samename(ni->name, zp->z_origin) == 1) { INSIST((zp->z_flags & Z_NOTIFY) != 0); zp->z_flags &= ~Z_NOTIFY; } if (evTestID(ni->timer)) { evClearTimer(ev, ni->timer); evInitID(&ni->timer); } freestr(ni->name); memput(ni, sizeof *ni); } static void notify_timer(evContext ctx, void *uap, struct timespec due, struct timespec inter) { struct notify *ni = uap; INSIST(evTestID(ni->timer)); evInitID(&ni->timer); INSIST(LINKED(ni, link)); UNLINK(pending_notifies, ni, link); sysnotify(ni->name, ni->class, ni->type); free_notify(ni); } #endif /*BIND_NOTIFY*/ Index: head/contrib/bind/bin/named/ns_parser.y =================================================================== --- head/contrib/bind/bin/named/ns_parser.y (revision 60940) +++ head/contrib/bind/bin/named/ns_parser.y (revision 60941) @@ -1,1968 +1,1970 @@ %{ #if !defined(lint) && !defined(SABER) -static char rcsid[] = "$Id: ns_parser.y,v 8.51 1999/11/12 05:29:18 vixie Exp $"; +static char rcsid[] = "$Id: ns_parser.y,v 8.55 2000/04/23 02:18:59 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Global C stuff goes here. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #include "ns_parseutil.h" #include "ns_lexer.h" #define SYM_ZONE 0x010000 #define SYM_SERVER 0x020000 #define SYM_KEY 0x030000 #define SYM_ACL 0x040000 #define SYM_CHANNEL 0x050000 #define SYM_PORT 0x060000 #define SYMBOL_TABLE_SIZE 29989 /* should always be prime */ static symbol_table symtab; #define AUTH_TABLE_SIZE 397 /* should always be prime */ static symbol_table authtab = NULL; static zone_config current_zone; static int should_install; static options current_options; static int seen_options; static controls current_controls; static topology_config current_topology; static int seen_topology; static server_config current_server; static int seen_server; static char *current_algorithm; static char *current_secret; static log_config current_logging; static int current_category; static int chan_type; static int chan_level; static u_int chan_flags; static int chan_facility; static char *chan_name; static int chan_versions; static u_long chan_max_size; static log_channel lookup_channel(char *); static void define_channel(char *, log_channel); static char *canonical_name(char *); int yyparse(); %} %union { char * cp; int s_int; long num; u_long ul_int; u_int16_t us_int; struct in_addr ip_addr; ip_match_element ime; ip_match_list iml; rrset_order_list rol; rrset_order_element roe; struct dst_key * keyi; enum axfr_format axfr_fmt; } /* Lexical analyzer return values. */ %token L_EOS %token L_IPADDR %token L_NUMBER %token L_STRING %token L_QSTRING %token L_END_INCLUDE /* Include support */ %token T_INCLUDE /* Items related to the "options" statement: */ %token T_OPTIONS %token T_DIRECTORY T_PIDFILE T_NAMED_XFER %token T_DUMP_FILE T_STATS_FILE T_MEMSTATS_FILE %token T_FAKE_IQUERY T_RECURSION T_FETCH_GLUE %token T_QUERY_SOURCE T_LISTEN_ON T_PORT T_ADDRESS %token T_RRSET_ORDER T_ORDER T_NAME T_CLASS %token T_CONTROLS T_INET T_UNIX T_PERM T_OWNER T_GROUP T_ALLOW %type in_port %type maybe_port %type maybe_zero_port %type maybe_wild_port %type maybe_wild_addr %token T_DATASIZE T_STACKSIZE T_CORESIZE %token T_DEFAULT T_UNLIMITED %token T_FILES T_VERSION %token T_HOSTSTATS T_DEALLOC_ON_EXIT %token T_TRANSFERS_IN T_TRANSFERS_OUT T_TRANSFERS_PER_NS %token T_TRANSFER_FORMAT T_MAX_TRANSFER_TIME_IN %token T_SERIAL_QUERIES T_ONE_ANSWER T_MANY_ANSWERS %type transfer_format %token T_NOTIFY T_AUTH_NXDOMAIN T_MULTIPLE_CNAMES T_USE_IXFR T_MAINTAIN_IXFR_BASE %token T_CLEAN_INTERVAL T_INTERFACE_INTERVAL T_STATS_INTERVAL T_MAX_LOG_SIZE_IXFR %token T_HEARTBEAT T_USE_ID_POOL %token T_MAX_NCACHE_TTL T_HAS_OLD_CLIENTS T_RFC2308_TYPE1 %token T_LAME_TTL T_MIN_ROOTS %token T_TREAT_CR_AS_SPACE /* Items used for the "logging" statement: */ %token T_LOGGING T_CATEGORY T_CHANNEL T_SEVERITY T_DYNAMIC %token T_FILE T_VERSIONS T_SIZE %token T_SYSLOG T_DEBUG T_NULL_OUTPUT %token T_PRINT_TIME T_PRINT_CATEGORY T_PRINT_SEVERITY %type category %type category_name channel_name facility_name %type maybe_syslog_facility /* Items used for the "sortlist" statement: */ %token T_SORTLIST /* Items used for the "topology" statement: */ %token T_TOPOLOGY %type ordering_class %type ordering_type %type ordering_name %type rrset_ordering_list %type rrset_ordering_element /* ip_match_list */ %type address_match_simple address_match_element address_name %type address_match_list /* Items used for "server" statements: */ %token T_SERVER %token T_LONG_AXFR %token T_BOGUS %token T_TRANSFERS %token T_KEYS %token T_SUPPORT_IXFR /* Items used for "zone" statements: */ %token T_ZONE %type optional_class %type zone_type %token T_IN T_CHAOS T_HESIOD %token T_TYPE %token T_MASTER T_SLAVE T_STUB T_RESPONSE %token T_HINT %token T_MASTERS T_TRANSFER_SOURCE %token T_PUBKEY %token T_ALSO_NOTIFY %token T_DIALUP %token T_FILE_IXFR %token T_IXFR_TMP /* Items used for "trusted-keys" statements: */ %token T_TRUSTED_KEYS /* Items used for access control lists and "allow" clauses: */ %token T_ACL %token T_ALLOW_UPDATE T_ALLOW_QUERY T_ALLOW_TRANSFER %token T_ALLOW_RECURSION %token T_BLACKHOLE /* Items related to the "key" statement: */ %token T_SEC_KEY T_ALGID T_SECRET %type key_ref %type algorithm_id secret /* Items used for "size_spec" clauses: */ %type size_spec /* Items used for a "check-names" clause: */ %token T_CHECK_NAMES %type check_names_type %type check_names_opt %token T_WARN T_FAIL T_IGNORE /* Items used for "forward" clauses: */ %token T_FORWARD T_FORWARDERS %token T_ONLY T_FIRST T_IF_NO_ANSWER T_IF_NO_DOMAIN /* Items used for yes/no responses: */ %type yea_or_nay %token T_YES T_TRUE T_NO T_FALSE /* Miscellaneous items (used in several places): */ %type any_string %% config_file: statement_list { if (EMPTY(current_controls)) ns_ctl_defaults(¤t_controls); ns_ctl_install(¤t_controls); } ; statement_list: statement | statement_list statement ; statement: include_stmt | options_stmt L_EOS | controls_stmt L_EOS | logging_stmt L_EOS | server_stmt L_EOS | zone_stmt L_EOS | trusted_keys_stmt L_EOS | acl_stmt L_EOS | key_stmt L_EOS | L_END_INCLUDE | error L_EOS | error L_END_INCLUDE ; include_stmt: T_INCLUDE L_QSTRING L_EOS { lexer_begin_file($2, NULL); } ; /* * Options */ options_stmt: T_OPTIONS { if (seen_options) parser_error(0, "cannot redefine options"); current_options = new_options(); } '{' options '}' { if (!seen_options) set_options(current_options, 0); else free_options(current_options); current_options = NULL; seen_options = 1; } ; options: option L_EOS | options option L_EOS ; option: /* Empty */ | T_VERSION L_QSTRING { if (current_options->version != NULL) freestr(current_options->version); current_options->version = $2; } | T_DIRECTORY L_QSTRING { if (current_options->directory != NULL) freestr(current_options->directory); current_options->directory = $2; } | T_NAMED_XFER L_QSTRING { if (current_options->named_xfer != NULL) freestr(current_options->named_xfer); current_options->named_xfer = $2; } | T_PIDFILE L_QSTRING { if (current_options->pid_filename != NULL) freestr(current_options->pid_filename); current_options->pid_filename = $2; } | T_STATS_FILE L_QSTRING { if (current_options->stats_filename != NULL) freestr(current_options->stats_filename); current_options->stats_filename = $2; } | T_MEMSTATS_FILE L_QSTRING { if (current_options->memstats_filename != NULL) freestr(current_options->memstats_filename); current_options->memstats_filename = $2; } | T_DUMP_FILE L_QSTRING { if (current_options->dump_filename != NULL) freestr(current_options->dump_filename); current_options->dump_filename = $2; } | T_FAKE_IQUERY yea_or_nay { set_global_boolean_option(current_options, OPTION_FAKE_IQUERY, $2); } | T_RECURSION yea_or_nay { set_global_boolean_option(current_options, OPTION_NORECURSE, !$2); } | T_FETCH_GLUE yea_or_nay { set_global_boolean_option(current_options, OPTION_NOFETCHGLUE, !$2); } | T_NOTIFY yea_or_nay { set_global_boolean_option(current_options, OPTION_NONOTIFY, !$2); } | T_HOSTSTATS yea_or_nay { set_global_boolean_option(current_options, OPTION_HOSTSTATS, $2); } | T_DEALLOC_ON_EXIT yea_or_nay { set_global_boolean_option(current_options, OPTION_DEALLOC_ON_EXIT, $2); } | T_USE_IXFR yea_or_nay { set_global_boolean_option(current_options, OPTION_USE_IXFR, $2); } | T_MAINTAIN_IXFR_BASE yea_or_nay { set_global_boolean_option(current_options, OPTION_MAINTAIN_IXFR_BASE, $2); } | T_HAS_OLD_CLIENTS yea_or_nay { set_global_boolean_option(current_options, - OPTION_MAINTAIN_IXFR_BASE, $2); - set_global_boolean_option(current_options, OPTION_NORFC2308_TYPE1, $2); set_global_boolean_option(current_options, OPTION_NONAUTH_NXDOMAIN, !$2); } | T_AUTH_NXDOMAIN yea_or_nay { set_global_boolean_option(current_options, OPTION_NONAUTH_NXDOMAIN, !$2); } | T_MULTIPLE_CNAMES yea_or_nay { set_global_boolean_option(current_options, OPTION_MULTIPLE_CNAMES, $2); } | T_CHECK_NAMES check_names_type check_names_opt { current_options->check_names[$2] = (enum severity)$3; } | T_USE_ID_POOL yea_or_nay { set_global_boolean_option(current_options, OPTION_USE_ID_POOL, $2); } | T_RFC2308_TYPE1 yea_or_nay { set_global_boolean_option(current_options, OPTION_NORFC2308_TYPE1, !$2); } | T_LISTEN_ON maybe_port '{' address_match_list '}' { char port_string[10]; symbol_value value; (void)sprintf(port_string, "%u", $2); if (lookup_symbol(symtab, port_string, SYM_PORT, NULL)) parser_error(0, "cannot redefine listen-on for port %u", ntohs($2)); else { add_listen_on(current_options, $2, $4); value.pointer = NULL; define_symbol(symtab, savestr(port_string, 1), SYM_PORT, value, SYMBOL_FREE_KEY); } } | T_FORWARD forward_opt | T_FORWARDERS { if (current_options->fwdtab) { free_forwarders(current_options->fwdtab); current_options->fwdtab = NULL; } } '{' opt_forwarders_list '}' | T_QUERY_SOURCE query_source | T_TRANSFER_SOURCE maybe_wild_addr { current_options->axfr_src = $2; } | T_ALLOW_QUERY '{' address_match_list '}' { if (current_options->query_acl) { parser_warning(0, "options allow-query acl already set; skipping"); free_ip_match_list($3); } else current_options->query_acl = $3; } | T_ALLOW_RECURSION '{' address_match_list '}' { if (current_options->recursion_acl) { parser_warning(0, "options allow-recursion acl already set; skipping"); free_ip_match_list($3); } else current_options->recursion_acl = $3; } | T_ALLOW_TRANSFER '{' address_match_list '}' { if (current_options->transfer_acl) { parser_warning(0, "options allow-transfer acl already set; skipping"); free_ip_match_list($3); } else current_options->transfer_acl = $3; } | T_SORTLIST '{' address_match_list '}' { if (current_options->sortlist) { parser_warning(0, "options sortlist already set; skipping"); free_ip_match_list($3); } else current_options->sortlist = $3; } | T_ALSO_NOTIFY { if (current_options->also_notify) { parser_warning(0, "duplicate also-notify clause: overwriting"); free_also_notify(current_options); current_options->also_notify = NULL; } } '{' opt_also_notify_list '}' | T_BLACKHOLE '{' address_match_list '}' { if (current_options->blackhole_acl) { parser_warning(0, "options blackhole already set; skipping"); free_ip_match_list($3); } else current_options->blackhole_acl = $3; } | T_TOPOLOGY '{' address_match_list '}' { if (current_options->topology) { parser_warning(0, "options topology already set; skipping"); free_ip_match_list($3); } else current_options->topology = $3; } | size_clause { /* To get around the $$ = $1 default rule. */ } | transfer_clause | T_TRANSFER_FORMAT transfer_format { current_options->transfer_format = $2; } | T_MAX_TRANSFER_TIME_IN L_NUMBER { current_options->max_transfer_time_in = $2 * 60; } | T_SERIAL_QUERIES L_NUMBER { current_options->serial_queries = $2; } | T_CLEAN_INTERVAL L_NUMBER { current_options->clean_interval = $2 * 60; } | T_INTERFACE_INTERVAL L_NUMBER { current_options->interface_interval = $2 * 60; } | T_STATS_INTERVAL L_NUMBER { current_options->stats_interval = $2 * 60; } | T_MAX_LOG_SIZE_IXFR L_NUMBER { current_options->max_log_size_ixfr = $2; } | T_MAX_NCACHE_TTL L_NUMBER { current_options->max_ncache_ttl = $2; } | T_LAME_TTL L_NUMBER { current_options->lame_ttl = $2; } | T_HEARTBEAT L_NUMBER { current_options->heartbeat_interval = $2 * 60; } | T_DIALUP yea_or_nay { set_global_boolean_option(current_options, OPTION_NODIALUP, !$2); } | T_RRSET_ORDER '{' rrset_ordering_list '}' { if (current_options->ordering) free_rrset_order_list(current_options->ordering); current_options->ordering = $3; } | T_TREAT_CR_AS_SPACE yea_or_nay { set_global_boolean_option(current_options, OPTION_TREAT_CR_AS_SPACE, $2); } | T_MIN_ROOTS L_NUMBER { if ($2 >= 1) current_options->minroots = $2; } | error ; /* * Controls. */ controls_stmt: T_CONTROLS '{' controls '}' ; controls: control L_EOS | controls control L_EOS ; control: /* Empty */ | T_INET maybe_wild_addr T_PORT in_port T_ALLOW '{' address_match_list '}' { ns_ctl_add(¤t_controls, ns_ctl_new_inet($2, $4, $7)); } | T_UNIX L_QSTRING T_PERM L_NUMBER T_OWNER L_NUMBER T_GROUP L_NUMBER { +#ifndef NO_SOCKADDR_UN ns_ctl_add(¤t_controls, ns_ctl_new_unix($2, $4, $6, $8)); +#endif } | error ; rrset_ordering_list: rrset_ordering_element L_EOS { rrset_order_list rol; rol = new_rrset_order_list(); if ($1 != NULL) { add_to_rrset_order_list(rol, $1); } $$ = rol; } | rrset_ordering_list rrset_ordering_element L_EOS { if ($2 != NULL) { add_to_rrset_order_list($1, $2); } $$ = $1; } ; ordering_class: /* nothing */ { $$ = C_ANY; } | T_CLASS any_string { symbol_value value; if (lookup_symbol(constants, $2, SYM_CLASS, &value)) $$ = value.integer; else { parser_error(0, "unknown class '%s'; using ANY", $2); $$ = C_ANY; } freestr($2); } ; ordering_type: /* nothing */ { $$ = ns_t_any; } | T_TYPE any_string { int success; if (strcmp($2, "*") == 0) { $$ = ns_t_any; } else { $$ = __sym_ston(__p_type_syms, $2, &success); if (success == 0) { $$ = ns_t_any; parser_error(0, "unknown type '%s'; assuming ANY", $2); } } freestr($2); } ordering_name: /* nothing */ { $$ = savestr("*", 1); } | T_NAME L_QSTRING { if (strcmp(".",$2) == 0 || strcmp("*.",$2) == 0) { $$ = savestr("*", 1); freestr($2); } else { $$ = $2 ; } /* XXX Should do any more name validation here? */ } rrset_ordering_element: ordering_class ordering_type ordering_name T_ORDER L_STRING { enum ordering o; if (strlen($5) == 0) { parser_error(0, "null order name"); $$ = NULL ; } else { o = lookup_ordering($5); if (o == unknown_order) { o = (enum ordering)DEFAULT_ORDERING; parser_error(0, "invalid order name '%s'; using %s", $5, p_order(o)); } freestr($5); $$ = new_rrset_order_element($1, $2, $3, o); } } transfer_format: T_ONE_ANSWER { $$ = axfr_one_answer; } | T_MANY_ANSWERS { $$ = axfr_many_answers; } ; maybe_wild_addr: L_IPADDR { $$ = $1; } | '*' { $$.s_addr = htonl(INADDR_ANY); } ; maybe_wild_port: in_port { $$ = $1; } | '*' { $$ = htons(0); } ; query_source_address: T_ADDRESS maybe_wild_addr { current_options->query_source.sin_addr = $2; } ; query_source_port: T_PORT maybe_wild_port { current_options->query_source.sin_port = $2; } ; query_source: query_source_address | query_source_port | query_source_address query_source_port | query_source_port query_source_address ; maybe_port: /* nothing */ { $$ = htons(NS_DEFAULTPORT); } | T_PORT in_port { $$ = $2; } ; maybe_zero_port: /* nothing */ { $$ = htons(0); } | T_PORT in_port { $$ = $2; } ; yea_or_nay: T_YES { $$ = 1; } | T_TRUE { $$ = 1; } | T_NO { $$ = 0; } | T_FALSE { $$ = 0; } | L_NUMBER { if ($1 == 1 || $1 == 0) { $$ = $1; } else { parser_warning(0, "number should be 0 or 1; assuming 1"); $$ = 1; } } ; check_names_type: T_MASTER { $$ = primary_trans; } | T_SLAVE { $$ = secondary_trans; } | T_RESPONSE { $$ = response_trans; } ; check_names_opt: T_WARN { $$ = warn; } | T_FAIL { $$ = fail; } | T_IGNORE { $$ = ignore; } ; forward_opt: T_ONLY { set_global_boolean_option(current_options, OPTION_FORWARD_ONLY, 1); } | T_FIRST { set_global_boolean_option(current_options, OPTION_FORWARD_ONLY, 0); } | T_IF_NO_ANSWER { parser_warning(0, "forward if-no-answer is unimplemented"); } | T_IF_NO_DOMAIN { parser_warning(0, "forward if-no-domain is unimplemented"); } ; size_clause: T_DATASIZE size_spec { current_options->data_size = $2; } | T_STACKSIZE size_spec { current_options->stack_size = $2; } | T_CORESIZE size_spec { current_options->core_size = $2; } | T_FILES size_spec { current_options->files = $2; } ; size_spec: any_string { u_long result; if (unit_to_ulong($1, &result)) $$ = result; else { parser_error(0, "invalid unit string '%s'", $1); /* 0 means "use default" */ $$ = 0; } freestr($1); } | L_NUMBER { $$ = (u_long)$1; } | T_DEFAULT { $$ = 0; } | T_UNLIMITED { $$ = ULONG_MAX; } ; transfer_clause: T_TRANSFERS_IN L_NUMBER { current_options->transfers_in = (u_long) $2; } | T_TRANSFERS_OUT L_NUMBER { current_options->transfers_out = (u_long) $2; } | T_TRANSFERS_PER_NS L_NUMBER { current_options->transfers_per_ns = (u_long) $2; } ; opt_forwarders_list: /* nothing */ | forwarders_in_addr_list ; forwarders_in_addr_list: forwarders_in_addr L_EOS { /* nothing */ } | forwarders_in_addr_list forwarders_in_addr L_EOS { /* nothing */ } ; forwarders_in_addr: L_IPADDR { add_global_forwarder(current_options, $1); } ; opt_also_notify_list: /* nothing */ | also_notify_in_addr_list ; also_notify_in_addr_list: also_notify_in_addr L_EOS { /* nothing */ } | also_notify_in_addr_list also_notify_in_addr L_EOS { /* nothing */ } ; also_notify_in_addr: L_IPADDR { add_global_also_notify(current_options, $1); } ; /* * Logging */ logging_stmt: T_LOGGING { current_logging = begin_logging(); } '{' logging_opts_list '}' { end_logging(current_logging, 1); current_logging = NULL; } ; logging_opts_list: logging_opt L_EOS | logging_opts_list logging_opt L_EOS | error ; logging_opt: T_CATEGORY category { current_category = $2; } '{' channel_list '}' | T_CHANNEL channel_name { chan_type = log_null; chan_flags = 0; chan_level = log_info; } '{' channel_opt_list '}' { log_channel current_channel = NULL; if (lookup_channel($2) != NULL) { parser_error(0, "can't redefine channel '%s'", $2); freestr($2); } else { switch (chan_type) { case log_file: current_channel = log_new_file_channel(chan_flags, chan_level, chan_name, NULL, chan_versions, chan_max_size); + log_set_file_owner(current_channel, + user_id, group_id); freestr(chan_name); chan_name = NULL; break; case log_syslog: current_channel = log_new_syslog_channel(chan_flags, chan_level, chan_facility); break; case log_null: current_channel = log_new_null_channel(); break; default: ns_panic(ns_log_parser, 1, "unknown channel type: %d", chan_type); } if (current_channel == NULL) ns_panic(ns_log_parser, 0, "couldn't create channel"); define_channel($2, current_channel); } } ; channel_severity: any_string { symbol_value value; if (lookup_symbol(constants, $1, SYM_LOGGING, &value)) { chan_level = value.integer; } else { parser_error(0, "unknown severity '%s'", $1); chan_level = log_debug(99); } freestr($1); } | T_DEBUG { chan_level = log_debug(1); } | T_DEBUG L_NUMBER { chan_level = $2; } | T_DYNAMIC { chan_level = 0; chan_flags |= LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; } ; version_modifier: T_VERSIONS L_NUMBER { chan_versions = $2; } | T_VERSIONS T_UNLIMITED { chan_versions = LOG_MAX_VERSIONS; } ; size_modifier: T_SIZE size_spec { chan_max_size = $2; } ; maybe_file_modifiers: /* nothing */ { chan_versions = 0; chan_max_size = ULONG_MAX; } | version_modifier { chan_max_size = ULONG_MAX; } | size_modifier { chan_versions = 0; } | version_modifier size_modifier | size_modifier version_modifier ; channel_file: T_FILE L_QSTRING maybe_file_modifiers { chan_flags |= LOG_CLOSE_STREAM; chan_type = log_file; chan_name = $2; } ; facility_name: any_string { $$ = $1; } | T_SYSLOG { $$ = savestr("syslog", 1); } ; maybe_syslog_facility: /* nothing */ { $$ = LOG_DAEMON; } | facility_name { symbol_value value; if (lookup_symbol(constants, $1, SYM_SYSLOG, &value)) { $$ = value.integer; } else { parser_error(0, "unknown facility '%s'", $1); $$ = LOG_DAEMON; } freestr($1); } ; channel_syslog: T_SYSLOG maybe_syslog_facility { chan_type = log_syslog; chan_facility = $2; } ; channel_opt: channel_file { /* nothing to do */ } | channel_syslog { /* nothing to do */ } | T_NULL_OUTPUT { chan_type = log_null; } | T_SEVERITY channel_severity { /* nothing to do */ } | T_PRINT_TIME yea_or_nay { if ($2) chan_flags |= LOG_TIMESTAMP; else chan_flags &= ~LOG_TIMESTAMP; } | T_PRINT_CATEGORY yea_or_nay { if ($2) chan_flags |= LOG_PRINT_CATEGORY; else chan_flags &= ~LOG_PRINT_CATEGORY; } | T_PRINT_SEVERITY yea_or_nay { if ($2) chan_flags |= LOG_PRINT_LEVEL; else chan_flags &= ~LOG_PRINT_LEVEL; } ; channel_opt_list: channel_opt L_EOS | channel_opt_list channel_opt L_EOS | error ; channel_name: any_string | T_NULL_OUTPUT { $$ = savestr("null", 1); } ; channel: channel_name { log_channel channel; symbol_value value; if (current_category >= 0) { channel = lookup_channel($1); if (channel != NULL) { add_log_channel(current_logging, current_category, channel); } else parser_error(0, "unknown channel '%s'", $1); } freestr($1); } ; channel_list: channel L_EOS | channel_list channel L_EOS | error ; category_name: any_string | T_DEFAULT { $$ = savestr("default", 1); } | T_NOTIFY { $$ = savestr("notify", 1); } ; category: category_name { symbol_value value; if (lookup_symbol(constants, $1, SYM_CATEGORY, &value)) $$ = value.integer; else { parser_error(0, "invalid logging category '%s'", $1); $$ = -1; } freestr($1); } ; /* * Server Information */ server_stmt: T_SERVER L_IPADDR { const char *ip_printable; symbol_value value; ip_printable = inet_ntoa($2); value.pointer = NULL; if (lookup_symbol(symtab, ip_printable, SYM_SERVER, NULL)) seen_server = 1; else seen_server = 0; if (seen_server) parser_error(0, "cannot redefine server '%s'", ip_printable); else define_symbol(symtab, savestr(ip_printable, 1), SYM_SERVER, value, SYMBOL_FREE_KEY); current_server = begin_server($2); } '{' server_info_list '}' { end_server(current_server, !seen_server); } ; server_info_list: server_info L_EOS | server_info_list server_info L_EOS ; server_info: T_BOGUS yea_or_nay { set_server_option(current_server, SERVER_INFO_BOGUS, $2); } | T_SUPPORT_IXFR yea_or_nay { set_server_option(current_server, SERVER_INFO_SUPPORT_IXFR, $2); } | T_TRANSFERS L_NUMBER { set_server_transfers(current_server, (int)$2); } | T_TRANSFER_FORMAT transfer_format { set_server_transfer_format(current_server, $2); } | T_KEYS '{' key_list '}' | error ; /* * Address Matching */ address_match_list: address_match_element L_EOS { ip_match_list iml; iml = new_ip_match_list(); if ($1 != NULL) add_to_ip_match_list(iml, $1); $$ = iml; } | address_match_list address_match_element L_EOS { if ($2 != NULL) add_to_ip_match_list($1, $2); $$ = $1; } ; address_match_element: address_match_simple | '!' address_match_simple { if ($2 != NULL) ip_match_negate($2); $$ = $2; } | T_SEC_KEY L_STRING { char *key_name; struct dst_key *dst_key; key_name = canonical_name($2); if (key_name == NULL) { parser_error(0, "can't make key name '%s' canonical", $2); key_name = savestr("__bad_key__", 1); } dst_key = find_key(key_name, NULL); if (dst_key == NULL) { parser_error(0, "key \"%s\" not found", key_name); $$ = NULL; } else $$ = new_ip_match_key(dst_key); } ; address_match_simple: L_IPADDR { $$ = new_ip_match_pattern($1, 32); } | L_IPADDR '/' L_NUMBER { if ($3 < 0 || $3 > 32) { parser_error(0, "mask bits out of range; skipping"); $$ = NULL; } else { $$ = new_ip_match_pattern($1, $3); if ($$ == NULL) parser_error(0, "address/mask mismatch; skipping"); } } | L_NUMBER '/' L_NUMBER { struct in_addr ia; if ($1 > 255) { parser_error(0, "address out of range; skipping"); $$ = NULL; } else { if ($3 < 0 || $3 > 32) { parser_error(0, "mask bits out of range; skipping"); $$ = NULL; } else { ia.s_addr = htonl(($1 & 0xff) << 24); $$ = new_ip_match_pattern(ia, $3); if ($$ == NULL) parser_error(0, "address/mask mismatch; skipping"); } } } | address_name | '{' address_match_list '}' { char name[256]; /* * We want to be able to clean up this iml later so * we give it a name and treat it like any other acl. */ sprintf(name, "__internal_%p", $2); define_acl(savestr(name, 1), $2); $$ = new_ip_match_indirect($2); } ; address_name: any_string { ip_match_list iml; iml = lookup_acl($1); if (iml == NULL) { parser_error(0, "unknown ACL '%s'", $1); $$ = NULL; } else $$ = new_ip_match_indirect(iml); freestr($1); } ; /* * Keys */ key_ref: any_string { struct dst_key *dst_key; char *key_name; key_name = canonical_name($1); if (key_name == NULL) { parser_error(0, "can't make key name '%s' canonical", $1); $$ = NULL; } else { dst_key = lookup_key(key_name); if (dst_key == NULL) { parser_error(0, "unknown key '%s'", key_name); $$ = NULL; } else $$ = dst_key; freestr(key_name); } freestr($1); } ; key_list_element: key_ref { if ($1 == NULL) parser_error(0, "empty key not added to server list "); else add_server_key_info(current_server, $1); } ; key_list: key_list_element L_EOS | key_list key_list_element L_EOS | error ; key_stmt: T_SEC_KEY { current_algorithm = NULL; current_secret = NULL; } any_string '{' key_definition '}' { struct dst_key *dst_key; char *key_name; key_name = canonical_name($3); if (key_name == NULL) { parser_error(0, "can't make key name '%s' canonical", $3); } else if (lookup_key(key_name) != NULL) { parser_error(0, "can't redefine key '%s'", key_name); freestr(key_name); } else { if (current_algorithm == NULL || current_secret == NULL) { parser_error(0, "skipping bad key '%s'", key_name); freestr(key_name); } else { dst_key = new_key_info(key_name, current_algorithm, current_secret); if (dst_key != NULL) { define_key(key_name, dst_key); if (secretkey_info == NULL) secretkey_info = new_key_info_list(); add_to_key_info_list(secretkey_info, dst_key); } } } freestr($3); } ; key_definition: algorithm_id secret { current_algorithm = $1; current_secret = $2; } | secret algorithm_id { current_algorithm = $2; current_secret = $1; } | error { current_algorithm = NULL; current_secret = NULL; } ; algorithm_id: T_ALGID any_string L_EOS { $$ = $2; } ; secret: T_SECRET any_string L_EOS { $$ = $2; } ; /* * ACLs */ acl_stmt: T_ACL any_string '{' address_match_list '}' { if (lookup_acl($2) != NULL) { parser_error(0, "can't redefine ACL '%s'", $2); freestr($2); } else define_acl($2, $4); } ; /* * Zones */ zone_stmt: T_ZONE L_QSTRING optional_class { int sym_type; symbol_value value; char *zone_name; if (!seen_options) parser_error(0, "no options statement before first zone; using previous/default"); sym_type = SYM_ZONE | ($3 & 0xffff); value.pointer = NULL; zone_name = canonical_name($2); if (zone_name == NULL) { parser_error(0, "can't make zone name '%s' canonical", $2); should_install = 0; zone_name = savestr("__bad_zone__", 1); } else { if (lookup_symbol(symtab, zone_name, sym_type, NULL)) { should_install = 0; parser_error(0, "cannot redefine zone '%s' class %s", *zone_name ? zone_name : ".", p_class($3)); } else { should_install = 1; define_symbol(symtab, savestr(zone_name, 1), sym_type, value, SYMBOL_FREE_KEY); } } freestr($2); current_zone = begin_zone(zone_name, $3); } optional_zone_options_list { end_zone(current_zone, should_install); } ; optional_zone_options_list: /* Empty */ | '{' zone_option_list '}' ; optional_class: /* Empty */ { $$ = C_IN; } | any_string { symbol_value value; if (lookup_symbol(constants, $1, SYM_CLASS, &value)) $$ = value.integer; else { /* the zone validator will give the error */ $$ = C_NONE; } freestr($1); } ; zone_type: T_MASTER { $$ = Z_MASTER; } | T_SLAVE { $$ = Z_SLAVE; } | T_HINT { $$ = Z_HINT; } | T_STUB { $$ = Z_STUB; } | T_FORWARD { $$ = Z_FORWARD; } ; zone_option_list: zone_option L_EOS | zone_option_list zone_option L_EOS ; zone_option: T_TYPE zone_type { if (!set_zone_type(current_zone, $2)) parser_warning(0, "zone type already set; skipping"); } | T_FILE L_QSTRING { if (!set_zone_filename(current_zone, $2)) parser_warning(0, "zone filename already set; skipping"); } | T_FILE_IXFR L_QSTRING { if (!set_zone_ixfr_file(current_zone, $2)) parser_warning(0, "zone ixfr data base already set; skipping"); } | T_IXFR_TMP L_QSTRING { if (!set_zone_ixfr_tmp(current_zone, $2)) parser_warning(0, "zone ixfr temp filename already set; skipping"); } | T_MASTERS maybe_zero_port '{' master_in_addr_list '}' { set_zone_master_port(current_zone, $2); } | T_TRANSFER_SOURCE maybe_wild_addr { set_zone_transfer_source(current_zone, $2); } | T_CHECK_NAMES check_names_opt { if (!set_zone_checknames(current_zone, (enum severity)$2)) parser_warning(0, "zone checknames already set; skipping"); } | T_ALLOW_UPDATE '{' address_match_list '}' { if (!set_zone_update_acl(current_zone, $3)) parser_warning(0, "zone update acl already set; skipping"); } | T_ALLOW_QUERY '{' address_match_list '}' { if (!set_zone_query_acl(current_zone, $3)) parser_warning(0, "zone query acl already set; skipping"); } | T_ALLOW_TRANSFER '{' address_match_list '}' { if (!set_zone_transfer_acl(current_zone, $3)) parser_warning(0, "zone transfer acl already set; skipping"); } | T_FORWARD zone_forward_opt | T_FORWARDERS { struct zoneinfo *zp = current_zone.opaque; if (zp->z_fwdtab) { free_forwarders(zp->z_fwdtab); zp->z_fwdtab = NULL; } } '{' opt_zone_forwarders_list '}' | T_MAX_TRANSFER_TIME_IN L_NUMBER { if (!set_zone_transfer_time_in(current_zone, $2*60)) parser_warning(0, "zone max transfer time (in) already set; skipping"); } | T_MAX_LOG_SIZE_IXFR L_NUMBER { set_zone_max_log_size_ixfr(current_zone, $2); } | T_NOTIFY yea_or_nay { set_zone_notify(current_zone, $2); } | T_MAINTAIN_IXFR_BASE yea_or_nay { set_zone_maintain_ixfr_base(current_zone, $2); } | T_PUBKEY L_NUMBER L_NUMBER L_NUMBER L_QSTRING { /* flags proto alg key */ set_zone_pubkey(current_zone, $2, $3, $4, $5); } | T_PUBKEY L_STRING L_NUMBER L_NUMBER L_QSTRING { /* flags proto alg key */ char *endp; int flags = (int) strtol($2, &endp, 0); if (*endp != '\0') ns_panic(ns_log_parser, 1, "Invalid flags string: %s", $2); set_zone_pubkey(current_zone, flags, $3, $4, $5); } | T_ALSO_NOTIFY '{' opt_notify_in_addr_list '}' | T_DIALUP yea_or_nay { set_zone_dialup(current_zone, $2); } | error ; master_in_addr_list: master_in_addr L_EOS { /* nothing */ } | master_in_addr_list master_in_addr L_EOS { /* nothing */ } ; master_in_addr: L_IPADDR { add_zone_master(current_zone, $1); } ; opt_notify_in_addr_list: /* nothing */ | notify_in_addr_list ; notify_in_addr_list: notify_in_addr L_EOS { /* nothing */ } | notify_in_addr_list notify_in_addr L_EOS { /* nothing */ } ; notify_in_addr: L_IPADDR { add_zone_notify(current_zone, $1); } ; zone_forward_opt: T_ONLY { set_zone_boolean_option(current_zone, OPTION_FORWARD_ONLY, 1); } | T_FIRST { set_zone_boolean_option(current_zone, OPTION_FORWARD_ONLY, 0); } ; opt_zone_forwarders_list: /* nothing */ { set_zone_forward(current_zone); } | zone_forwarders_in_addr_list ; zone_forwarders_in_addr_list: zone_forwarders_in_addr L_EOS { /* nothing */ } | zone_forwarders_in_addr_list zone_forwarders_in_addr L_EOS { /* nothing */ } ; zone_forwarders_in_addr: L_IPADDR { add_zone_forwarder(current_zone, $1); } ; /* * Trusted Key statement */ trusted_keys_stmt: T_TRUSTED_KEYS '{' trusted_keys_list '}' { } ; trusted_keys_list: trusted_key L_EOS { /* nothing */ } | trusted_keys_list trusted_key L_EOS { /* nothing */ } ; trusted_key: L_STRING L_NUMBER L_NUMBER L_NUMBER L_QSTRING { /* name flags proto alg key */ set_trusted_key($1, $2, $3, $4, $5); } | L_STRING L_STRING L_NUMBER L_NUMBER L_QSTRING { /* name flags proto alg key */ char *endp; int flags = (int) strtol($2, &endp, 0); if (*endp != '\0') ns_panic(ns_log_parser, 1, "Invalid flags string: %s", $2); set_trusted_key($1, flags, $3, $4, $5); } ; /* * Misc. */ in_port: L_NUMBER { if ($1 < 0 || $1 > 65535) { parser_warning(0, "invalid IP port number '%d'; setting port to 0", $1); $1 = 0; } else $$ = htons($1); } ; any_string: L_STRING | L_QSTRING ; %% static char * canonical_name(char *name) { char canonical[MAXDNAME]; if (strlen(name) >= MAXDNAME) return (NULL); strcpy(canonical, name); if (makename(canonical, ".", sizeof canonical) < 0) return (NULL); return (savestr(canonical, 0)); } static void init_acls() { ip_match_element ime; ip_match_list iml; struct in_addr address; /* Create the predefined ACLs */ address.s_addr = 0U; /* ACL "any" */ ime = new_ip_match_pattern(address, 0); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl(savestr("any", 1), iml); /* ACL "none" */ ime = new_ip_match_pattern(address, 0); ip_match_negate(ime); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl(savestr("none", 1), iml); /* ACL "localhost" */ ime = new_ip_match_localhost(); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl(savestr("localhost", 1), iml); /* ACL "localnets" */ ime = new_ip_match_localnets(); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl(savestr("localnets", 1), iml); } static void free_sym_value(int type, void *value) { ns_debug(ns_log_parser, 99, "free_sym_value: type %06x value %p", type, value); type &= ~0xffff; switch (type) { case SYM_ACL: free_ip_match_list(value); break; case SYM_KEY: free_key_info(value); break; default: ns_panic(ns_log_parser, 1, "unhandled case in free_sym_value()"); /* NOTREACHED */ break; } } static log_channel lookup_channel(char *name) { symbol_value value; if (lookup_symbol(symtab, name, SYM_CHANNEL, &value)) return ((log_channel)(value.pointer)); return (NULL); } static void define_channel(char *name, log_channel channel) { symbol_value value; value.pointer = channel; define_symbol(symtab, name, SYM_CHANNEL, value, SYMBOL_FREE_KEY); } static void define_builtin_channels() { define_channel(savestr("default_syslog", 1), syslog_channel); define_channel(savestr("default_debug", 1), debug_channel); define_channel(savestr("default_stderr", 1), stderr_channel); define_channel(savestr("null", 1), null_channel); } static void parser_setup() { seen_options = 0; seen_topology = 0; symtab = new_symbol_table(SYMBOL_TABLE_SIZE, NULL); if (authtab != NULL) free_symbol_table(authtab); authtab = new_symbol_table(AUTH_TABLE_SIZE, free_sym_value); init_acls(); define_builtin_channels(); INIT_LIST(current_controls); } static void parser_cleanup() { if (symtab != NULL) free_symbol_table(symtab); symtab = NULL; /* * We don't clean up authtab here because the ip_match_lists are in * use. */ } /* * Public Interface */ ip_match_list lookup_acl(char *name) { symbol_value value; if (lookup_symbol(authtab, name, SYM_ACL, &value)) return ((ip_match_list)(value.pointer)); return (NULL); } void define_acl(char *name, ip_match_list iml) { symbol_value value; INSIST(name != NULL); INSIST(iml != NULL); value.pointer = iml; define_symbol(authtab, name, SYM_ACL, value, SYMBOL_FREE_KEY|SYMBOL_FREE_VALUE); ns_debug(ns_log_parser, 7, "acl %s", name); dprint_ip_match_list(ns_log_parser, iml, 2, "allow ", "deny "); } struct dst_key * lookup_key(char *name) { symbol_value value; if (lookup_symbol(authtab, name, SYM_KEY, &value)) return ((struct dst_key *)(value.pointer)); return (NULL); } void define_key(char *name, struct dst_key *dst_key) { symbol_value value; INSIST(name != NULL); INSIST(dst_key != NULL); value.pointer = dst_key; define_symbol(authtab, name, SYM_KEY, value, SYMBOL_FREE_VALUE); dprint_key_info(dst_key); } void parse_configuration(const char *filename) { FILE *config_stream; config_stream = fopen(filename, "r"); if (config_stream == NULL) ns_panic(ns_log_parser, 0, "can't open '%s'", filename); lexer_setup(); parser_setup(); lexer_begin_file(filename, config_stream); (void)yyparse(); lexer_end_file(); parser_cleanup(); } void parser_initialize(void) { lexer_initialize(); } void parser_shutdown(void) { if (authtab != NULL) free_symbol_table(authtab); lexer_shutdown(); } Index: head/contrib/bind/bin/named/ns_parseutil.c =================================================================== --- head/contrib/bind/bin/named/ns_parseutil.c (revision 60940) +++ head/contrib/bind/bin/named/ns_parseutil.c (revision 60941) @@ -1,244 +1,244 @@ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Global C stuff goes here. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #include "ns_parseutil.h" /* * Symbol Table */ symbol_table new_symbol_table(int size_guess, free_function free_value) { symbol_table st; st = (symbol_table)memget(sizeof (struct symbol_table)); if (st == NULL) panic("memget failed in new_symbol_table()", NULL); st->table = (symbol_entry *)memget(size_guess * sizeof *st->table); if (st->table == NULL) panic("memget failed in new_symbol_table()", NULL); memset(st->table, 0, size_guess * sizeof (symbol_entry)); st->size = size_guess; /* size_guess should be prime */ st->free_value = free_value; return (st); } void free_symbol(symbol_table st, symbol_entry ste) { if (ste->flags & SYMBOL_FREE_KEY) freestr(ste->key); if (ste->flags & SYMBOL_FREE_VALUE) (st->free_value)(ste->type, ste->value.pointer); } void free_symbol_table(symbol_table st) { int i; symbol_entry ste, ste_next; for (i = 0; i < st->size; i++) { for (ste = st->table[i]; ste != NULL; ste = ste_next) { ste_next = ste->next; free_symbol(st, ste); memput(ste, sizeof *ste); } } memput(st->table, st->size * sizeof (symbol_entry)); memput(st, sizeof *st); } void dprint_symbol_table(int level, symbol_table st) { int i; symbol_entry ste; for (i = 0; i < st->size; i++) { for (ste = st->table[i]; ste != NULL; ste = ste->next) ns_debug(ns_log_parser, level, "%7d: (%s: %d %p/%d %04x) ", i, ste->key, ste->type, ste->value.pointer, ste->value.integer, ste->flags); } } /* * P. J. Weinberger's hash function, adapted from p. 436 of * _Compilers: Principles, Techniques, and Tools_, Aho, Sethi * and Ullman, Addison-Wesley, 1986, ISBN 0-201-10088-6. */ static int symbol_hash(const char *key, int prime) { const char *s; unsigned int h = 0; unsigned int g; int c; for (s = key; *s != '\0'; s++) { c = *s; if (isascii(c) && isupper(c)) c = tolower(c); h = ( h << 4 ) + c; if ((g = ( h & 0xf0000000 )) != 0) { h = h ^ (g >> 24); h = h ^ g; } } return (h % prime); } int lookup_symbol(symbol_table st, const char *key, int type, symbol_value *value) { int hash; symbol_entry ste; hash = symbol_hash(key, st->size); for (ste = st->table[hash]; ste != NULL; ste = ste->next) if ((type == 0 || ste->type == type) && strcasecmp(ste->key, key) == 0) break; if (ste != NULL) { if (value != NULL) *value = ste->value; return (1); } return (0); } void define_symbol(symbol_table st, char *key, int type, symbol_value value, unsigned int flags) { int hash; symbol_entry ste; hash = symbol_hash(key, st->size); for (ste = st->table[hash]; ste != NULL; ste = ste->next) if ((type == 0 || ste->type == type) && strcasecmp(ste->key, key) == 0) break; if (ste == NULL) { ste = (symbol_entry)memget(sizeof *ste); if (ste == NULL) panic("memget failed in define_symbol()", NULL); ste->key = key; ste->type = type; ste->value = value; ste->flags = flags; ste->next = st->table[hash]; st->table[hash] = ste; } else { ns_debug(ns_log_parser, 7, "redefined symbol %s type %d", key, type); free_symbol(st, ste); ste->key = key; ste->value = value; ste->flags = flags; } } void undefine_symbol(symbol_table st, char *key, int type) { int hash; symbol_entry prev_ste, ste; hash = symbol_hash(key, st->size); for (prev_ste = NULL, ste = st->table[hash]; ste != NULL; prev_ste = ste, ste = ste->next) if ((type == 0 || ste->type == type) && strcasecmp(ste->key, key) == 0) break; if (ste != NULL) { free_symbol(st, ste); if (prev_ste != NULL) prev_ste->next = ste->next; else st->table[hash] = ste->next; memput(ste, sizeof *ste); } } /* * Conversion Routines */ int unit_to_ulong(char *in, u_long *out) { int c, units_done = 0; u_long result = 0L; INSIST(in != NULL); for (; (c = *in) != '\0'; in++) { if (units_done) return (0); if (isdigit(c)) { result *= 10; result += (c - '0'); } else { switch (c) { case 'k': case 'K': result *= 1024; units_done = 1; break; case 'm': case 'M': result *= (1024*1024); units_done = 1; break; case 'g': case 'G': result *= (1024*1024*1024); units_done = 1; break; default: return (0); } } } *out = result; return (1); } Index: head/contrib/bind/bin/named/ns_parseutil.h =================================================================== --- head/contrib/bind/bin/named/ns_parseutil.h (revision 60940) +++ head/contrib/bind/bin/named/ns_parseutil.h (revision 60941) @@ -1,65 +1,65 @@ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef _NS_PARSEUTIL_H #define _NS_PARSEUTIL_H /* * Symbol Table */ #define SYMBOL_FREE_KEY 0x01 #define SYMBOL_FREE_VALUE 0x02 typedef union symbol_value { void *pointer; int integer; } symbol_value; typedef void (*free_function)(int, void *); typedef struct symbol_entry { char *key; int type; symbol_value value; unsigned int flags; struct symbol_entry *next; } *symbol_entry; typedef struct symbol_table { int size; symbol_entry *table; free_function free_value; } *symbol_table; symbol_table new_symbol_table(int, free_function); void free_symbol(symbol_table, symbol_entry); void free_symbol_table(symbol_table); void dprint_symbol_table(int, symbol_table); int lookup_symbol(symbol_table, const char *, int, symbol_value *); void define_symbol(symbol_table, char *, int, symbol_value, unsigned int); void undefine_symbol(symbol_table, char *, int type); /* * Conversion Routines */ int unit_to_ulong(char *, u_long *); #endif /* !_NS_PARSEUTIL_H */ Index: head/contrib/bind/bin/named/ns_req.c =================================================================== --- head/contrib/bind/bin/named/ns_req.c (revision 60940) +++ head/contrib/bind/bin/named/ns_req.c (revision 60941) @@ -1,2100 +1,2146 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_req.c 4.47 (Berkeley) 7/1/91"; -static const char rcsid[] = "$Id: ns_req.c,v 8.104 1999/10/15 19:49:04 vixie Exp $"; +static const char rcsid[] = "$Id: ns_req.c,v 8.113 2000/04/21 06:54:11 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct addinfo { char *a_dname; /* domain name */ char *a_rname; /* referred by */ u_int16_t a_rtype; /* referred by */ u_int16_t a_type; /* type for data */ u_int16_t a_class; /* class for data */ }; + #ifndef BIND_UPDATE enum req_action { Finish, Refuse, Return }; #endif static struct addinfo addinfo[NADDRECS]; static void addname(const char *, const char *, u_int16_t, u_int16_t, u_int16_t); static void copyCharString(u_char **, const char *); static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra, struct sockaddr_in from, struct tsig_record *in_tsig); static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, u_char *msg, struct sockaddr_in from); #ifdef BIND_NOTIFY static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg,struct sockaddr_in from); #endif /* * Process request using database; assemble and send response. */ void ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, struct sockaddr_in from, int dfd) { HEADER *hp = (HEADER *) msg; u_char *cp, *eom; enum req_action action; int n, ra, has_tsig, msglen_orig, tsig_size, siglen, sig2len; u_char *tsigstart; u_char sig[TSIG_SIG_SIZE], sig2[TSIG_SIG_SIZE]; struct tsig_record *in_tsig = NULL; int error = NOERROR; DST_KEY *key; time_t tsig_time; #ifdef DEBUG if (debug > 3) { ns_debug(ns_log_packet, 3, "ns_req(from %s)", sin_ntoa(from)); - res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); + fp_nquery(msg, msglen, log_get_stream(packet_channel)); } #endif msglen_orig = msglen; siglen = sizeof(sig); tsigstart = ns_find_tsig(msg, msg + msglen); if (tsigstart == NULL) has_tsig = 0; else { char buf[MAXDNAME]; has_tsig = 1; - ns_name_ntop(tsigstart, buf, sizeof(buf)); + n = dn_expand(msg, msg + msglen, tsigstart, buf, sizeof(buf)); + if (n < 0) { + ns_debug(ns_log_default, 1, + "ns_req: bad TSIG key name", + buf); + key = NULL; + } key = find_key(buf, NULL); if (key == NULL) { error = ns_r_badkey; ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - unknown key %s", buf); } } if (has_tsig && key != NULL) { n = ns_verify(msg, &msglen, key, NULL, 0, sig, &siglen, &tsig_time, 0); if (n != 0) { hp->rcode = ns_r_notauth; /* A query should never have an error code set */ if (n == ns_r_badsig || n == ns_r_badkey || n == ns_r_badtime) { ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - query had error %s (%d) set", p_rcode(n), n); error = n; action = Return; } /* If there's a processing error just respond */ else if (n == -ns_r_badsig || n == -ns_r_badkey || n == -ns_r_badtime) { n = -n; ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - %s (%d)", p_rcode(n), n); error = n; } else { ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - FORMERR"); error = ns_r_formerr; } action = Finish; } in_tsig = memget(sizeof(struct tsig_record)); if (in_tsig == NULL) ns_panic(ns_log_default, 1, "memget failed"); in_tsig->key = key; in_tsig->siglen = siglen; memcpy(in_tsig->sig, sig, siglen); tsig_size = msglen_orig - msglen; } else if (has_tsig) { action = Finish; in_tsig = memget(sizeof(struct tsig_record)); if (in_tsig == NULL) ns_panic(ns_log_default, 1, "memget failed"); in_tsig->key = NULL; in_tsig->siglen = 0; tsig_size = msg + msglen - tsigstart; msglen = tsigstart - msg; } /* Hash some stuff so it's nice and random */ nsid_hash((u_char *)&tt, sizeof(tt)); nsid_hash(msg, (msglen > 512) ? 512 : msglen); /* * It's not a response so these bits have no business * being set. will later simplify work if we can * safely assume these are always 0 when a query * comes in. */ hp->aa = hp->ra = 0; ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); if (error == NOERROR) hp->rcode = ns_r_noerror; cp = msg + HFIXEDSZ; eom = msg + msglen; buflen -= HFIXEDSZ; free_addinfo(); /* sets addcount to zero */ dnptrs[0] = NULL; if (error == NOERROR) { switch (hp->opcode) { case ns_o_query: action = req_query(hp, &cp, eom, qsp, &buflen, &msglen, msg, dfd, &ra, from, in_tsig); break; case ns_o_iquery: action = req_iquery(hp, &cp, eom, &buflen, msg, from); break; #ifdef BIND_NOTIFY case ns_o_notify: action = req_notify(hp, &cp, eom, msg, from); break; #endif #ifdef BIND_UPDATE case ns_o_update: action = req_update(hp, cp, eom, msg, qsp, dfd, from, in_tsig); break; #endif /* BIND_UPDATE */ default: ns_debug(ns_log_default, 1, "ns_req: Opcode %d not implemented", hp->opcode); /* XXX - should syslog, limited by haveComplained */ hp->qdcount = htons(0); hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = ns_r_notimpl; action = Finish; } } if (in_tsig != NULL) { memput(in_tsig, sizeof(struct tsig_record)); in_tsig = NULL; } /* * Vector via internal opcode. */ switch (action) { case Return: return; case Refuse: hp->rcode = ns_r_refused; cp = eom; /*FALLTHROUGH*/ case Finish: /* rest of the function handles this case */ break; default: panic("ns_req: bad action variable", NULL); /*NOTREACHED*/ } /* * Apply final polish. */ hp->qr = 1; /* set Response flag */ hp->ra = ra; /* init above, may be modified by req_query */ if (!hp->tc && has_tsig > 0 && buflen < tsig_size) hp->tc = 1; /* * If there was a format error, then we don't know what the msg has. */ if (hp->rcode == ns_r_formerr) { hp->qdcount = htons(0); hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); } /* * If the query had a TSIG and the message is truncated or there was * a TSIG error, build a new message with no data and a TSIG. */ if ((hp->tc || error != NOERROR) && has_tsig > 0) { hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); cp = msg + HFIXEDSZ; cp += ns_skiprr(cp, msg + msglen, ns_s_qd, ntohs(hp->qdcount)); sig2len = sizeof(sig2); buflen += (msglen - (cp - msg)); msglen = cp - msg; n = ns_sign(msg, &msglen, msglen + buflen, error, key, sig, siglen, sig2, &sig2len, tsig_time); if (n != 0) { INSIST(0); } cp = msg + msglen; } /* Either the message is not truncated or there was no TSIG */ else { if (has_tsig > 0) buflen -= tsig_size; n = doaddinfo(hp, cp, buflen); cp += n; buflen -= n; if (has_tsig > 0) { buflen += tsig_size; sig2len = sizeof(sig2); msglen = cp - msg; n = ns_sign(msg, &msglen, msglen + buflen, error, key, sig, siglen, sig2, &sig2len, tsig_time); if (n != 0) { INSIST(0); } cp = msg + msglen; } } #ifdef DEBUG ns_debug(ns_log_default, 1, "ns_req: answer -> %s fd=%d id=%d size=%d rc=%d", sin_ntoa(from), (qsp == NULL) ? dfd : qsp->s_rfd, ntohs(hp->id), cp - msg, hp->rcode); if (debug >= 10) res_pquery(&res, msg, cp - msg, log_get_stream(packet_channel)); #endif /*DEBUG*/ if (qsp == NULL) { if (sendto(dfd, (char*)msg, cp - msg, 0, (struct sockaddr *)&from, sizeof(from)) < 0) { if (!haveComplained(ina_ulong(from.sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "ns_req: sendto(%s): %s", sin_ntoa(from), strerror(errno)); nameserIncr(from.sin_addr, nssSendtoErr); } nameserIncr(from.sin_addr, nssSentAns); if (hp->rcode == ns_r_nxdomain) nameserIncr(from.sin_addr, nssSentNXD); if (!hp->aa) nameserIncr(from.sin_addr, nssSentNaAns); } else writestream(qsp, msg, cp - msg); /* Is now a safe time? */ if (needs_prime_cache) prime_cache(); } #ifdef BIND_NOTIFY int findZonePri(const struct zoneinfo *zp, const struct sockaddr_in from) { struct in_addr ina; int i; ina = from.sin_addr; for (i = 0; (u_int)i < zp->z_addrcnt; i++) if (ina_equal(zp->z_addr[i], ina)) return (i); return (-1); } static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg, struct sockaddr_in from) { int n, type, class, zn; char dnbuf[MAXDNAME]; struct zoneinfo *zp; /* valid notify's have one question */ if (ntohs(hp->qdcount) != 1) { ns_debug(ns_log_notify, 1, "FORMERR Notify header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } n = dn_expand(msg, eom, *cpp, dnbuf, sizeof dnbuf); if (n < 0) { ns_debug(ns_log_notify, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; if (*cpp + 2 * INT16SZ > eom) { ns_debug(ns_log_notify, 1, "FORMERR notify too short"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(type, *cpp); GETSHORT(class, *cpp); ns_info(ns_log_notify, "rcvd NOTIFY(%s, %s, %s) from %s", dnbuf, p_class(class), p_type(type), sin_ntoa(from)); /* XXX - when answers are allowed, we'll need to do compression * correctly here, and we will need to check for packet underflow. */ /* Find the zone this NOTIFY refers to. */ zp = find_auth_zone(dnbuf, class); if (zp == NULL) { ns_info(ns_log_notify, "rcvd NOTIFY for \"%s\", name not one of our zones", dnbuf); hp->rcode = ns_r_servfail; return (Finish); } /* Access control. */ switch (type) { case T_SOA: if (zp->z_type != z_slave) { /* * This can come if a user did an AXFR of some zone * somewhere and that zone's server now wants to * tell us that the SOA has changed. AXFR's always * come from nonpriv ports so it isn't possible to * know whether it was the server or just "dig". * This condition can be avoided by using secure * zones since that way only real secondaries can * AXFR from you. */ ns_info(ns_log_notify, "NOTIFY(SOA) for non-secondary name (%s), from %s", dnbuf, sin_ntoa(from)); goto refuse; } if (ns_samename(dnbuf, zp->z_origin) != 1) { ns_info(ns_log_notify, "NOTIFY(SOA) for non-origin (%s), from %s", dnbuf, sin_ntoa(from)); goto refuse; } if (findZonePri(zp, from) == -1) { - ns_info(ns_log_notify, + ns_debug(ns_log_notify, 1, "NOTIFY(SOA) from non-master server (zone %s), from %s", zp->z_origin, sin_ntoa(from)); goto refuse; } break; default: /* No access requirements defined for other types. */ break; } /* The work occurs here. */ switch (type) { case T_SOA: if (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING)) { ns_info(ns_log_notify, "NOTIFY(SOA) for zone already xferring (%s)", dnbuf); goto noerror; } zp->z_time = tt.tv_sec; qserial_query(zp); sched_zone_maint(zp); break; default: /* * Unimplemented, but it's not a protocol error, just * something to be ignored. */ hp->rcode = ns_r_notimpl; return (Finish); } noerror: hp->rcode = ns_r_noerror; return (Finish); refuse: hp->rcode = ns_r_refused; return (Finish); } #endif /*BIND_NOTIFY*/ + static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra, struct sockaddr_in from, struct tsig_record *in_tsig) { int n, class, type, count, zone, foundname, founddata, omsglen, cname; int recursion_blocked_by_acl; u_int16_t id; u_int32_t serial_ixfr; int ixfr_found; int ixfr_error = 0; char dnbuf2[MAXDNAME]; u_char **dpp, *omsg, *answers, *afterq; char dnbuf[MAXDNAME], *dname; const char *fname; struct hashbuf *htp; struct databuf *nsp[NSMAX]; struct namebuf *np, *anp; struct qinfo *qp; struct zoneinfo *zp; struct databuf *dp; DST_KEY *in_key = (in_tsig != NULL) ? in_tsig->key : NULL; + + + nameserIncr(from.sin_addr, nssRcvdQ); nsp[0] = NULL; dpp = dnptrs; *dpp++ = msg; *dpp = NULL; /* * Make gcc happy. */ omsglen = 0; omsg = NULL; id = 0; recursion_blocked_by_acl = 0; /* valid queries have one question and zero answers */ if ((ntohs(hp->qdcount) != 1) || ntohs(hp->ancount) != 0 || ntohs(hp->arcount) != 0) { ns_debug(ns_log_default, 1, "FORMERR Query header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } /* * Get domain name, class, and type. */ if ((**cpp & INDIR_MASK) == 0) *dpp++ = *cpp; /* remember name for compression */ *dpp = NULL; n = dn_expand(msg, eom, *cpp, dnbuf, sizeof dnbuf); if (n < 0) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; answers = *cpp; if (*cpp + 2 * INT16SZ > eom) { ns_debug(ns_log_default, 1, "FORMERR Query message length short"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(type, *cpp); GETSHORT(class, *cpp); if (*cpp < eom && type != ns_t_ixfr) { ns_debug(ns_log_default, 6, "message length > received message"); *msglenp = *cpp - msg; } if (((ntohs(hp->nscount) != 0) && (type != ns_t_ixfr)) || - ((ntohs(hp->nscount) != 1) && (type == ns_t_ixfr))) + ((ntohs(hp->nscount) != 1) && (type == ns_t_ixfr))) { ns_debug(ns_log_default, 1, "FORMERR Query nscount wrong"); hp->rcode = ns_r_formerr; return (Finish); } afterq = *cpp; qtypeIncr(type); /* * Process query. */ if (type == ns_t_ixfr) { + ns_info(ns_log_security, "Request %s from %s", + p_type(type), sin_ntoa(from)); hp->nscount = htons(0); hp->rd = 0; /* Force IXFR queries to be non recursive. */ n = dn_expand(msg, eom, *cpp, dnbuf2, sizeof dnbuf2); if (n < 0) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; if (*cpp + 3 * INT16SZ + INT32SZ > eom) { ns_debug(ns_log_default, 1, "ran out of data in IXFR query"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(n, *cpp); if (n != ns_t_soa || ns_samename(dnbuf, dnbuf2) != 1) { ns_debug(ns_log_default, 1, "FORMERR SOA record expected"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += INT32SZ + INT16SZ * 2; /* skip class, ttl, dlen */ if (0 >= (n = dn_skipname(*cpp, eom))) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; /* mname */ if (0 >= (n = dn_skipname(*cpp, eom))) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; /* rname */ if (*cpp + 5 * INT32SZ > eom) { ns_debug(ns_log_default, 1, "ran out of data in IXFR query"); hp->rcode = ns_r_formerr; return (Finish); } GETLONG(serial_ixfr, *cpp); /* ignore other soa counters */ if ((*cpp + (4 * INT32SZ)) < eom) ns_debug(ns_log_default, 6, "ixfr: message length > received message"); /* Reset msglenp to cover just the question. */ *msglenp = afterq - msg; } *cpp = afterq; if (!ns_t_udp_p(type)) { /* Refuse request if not a TCP connection. */ if (qsp == NULL) { ns_info(ns_log_default, "rejected UDP %s from %s for \"%s\"", p_type(type), sin_ntoa(from), *dnbuf ? dnbuf : "."); return (Refuse); } /* The position of this is subtle. */ nameserIncr(from.sin_addr, nssRcvdAXFR); hp->rd = 0; /* Recursion not possible. */ } *buflenp -= *msglenp; count = 0; founddata = 0; dname = dnbuf; cname = 0; #ifdef QRYLOG if (qrylog) { ns_info(ns_log_queries, "%s/%s/%s/%s/%s", (hp->rd) ? "XX+" : "XX ", inet_ntoa(from.sin_addr), (dname[0] == '\0') ? "." : dname, p_type(type), p_class(class)); } #endif /*QRYLOG*/ try_again: foundname = 0; ns_debug(ns_log_default, 1, "req: nlookup(%s) id %d type=%d class=%d", dname, ntohs(hp->id), type, class); htp = hashtab; /* lookup relative to root */ if ((anp = np = nlookup(dname, &htp, &fname, 0)) == NULL) fname = ""; ns_debug(ns_log_default, 1, "req: %s '%s' as '%s' (cname=%d)", np == NULL ? "missed" : "found", dname, fname, cname); + + ns_debug(ns_log_default, 1, "req: %s '%s' as '%s' (cname=%d)", + np == NULL ? "missed" : "found", + dname, fname, cname); + #ifdef YPKLUDGE /* Some braindamaged resolver software will not recognize internet addresses in dot notation and send out address queries for "names" such as 128.93.8.1. This kludge will prevent those from flooding higher level servers. We simply claim to be authoritative and that the domain doesn't exist. Note that we could return the address but we don't do that in order to encourage that broken software is fixed. */ if (!np && type == T_A && class == C_IN && dname) { struct in_addr ina; if (inet_aton(dname, &ina)) { hp->rcode = ns_r_nxdomain; hp->aa = 1; ns_debug(ns_log_default, 3, "ypkludge: hit as '%s'", dname); return (Finish); } } #endif /*YPKLUDGE*/ /* * Begin Access Control Point */ zone = DB_Z_CACHE; if (np) { struct namebuf *access_np; /* * Find out which zone this will be answered from. Note * that we look for a zone with the same class as ours. * The np that we found in the database might not be the * one we asked for (i.e. dname might not equal fname). This * is OK, since if a name doesn't exist, we need to go up * the tree until we find the closest enclosing zone that * is of the same class. */ for (access_np = np; access_np != NULL; access_np = np_parent(access_np)) { dp = access_np->n_data; while (dp && dp->d_class != class) dp = dp->d_next; if (dp != NULL) { zone = dp->d_zone; break; } } } zp = &zones[zone]; ixfr_found = 0; if (type == ns_t_ixfr && zone != DB_Z_CACHE) { if (SEQ_GT(serial_ixfr, zp->z_serial)) ixfr_found = 0; else { - ixfr_error = ixfr_have_log(zp, serial_ixfr, zp->z_serial); - if (ixfr_error < 0) { - ns_debug(ns_log_default, - 1, "ixfr_have_log(%d %d) failed %d", - serial_ixfr, zp->z_serial, ixfr_error); - ixfr_found = 0; - /* Refuse IXFR and send AXFR */ - type = ns_t_axfr; - } else - ixfr_found = 1; - } + ixfr_error = ixfr_have_log(zp, serial_ixfr, + zp->z_serial); + if (ixfr_error < 0) { + ns_info(ns_log_security, "No %s log from %d for \"%s\"", + p_type(type), serial_ixfr, *dname ? dname : "."); + ns_debug(ns_log_default, + 1, "ixfr_have_log(%d %d) failed %d", + serial_ixfr, zp->z_serial, ixfr_error); + ixfr_found = 0; /* Refuse IXFR and send AXFR */ + } else if (ixfr_error == 1) { + ixfr_found = 1; + } + } + ns_debug(ns_log_default, 1, "IXFR log lowest serial: %d", + zp->z_serial_ixfr_start); } /* * If recursion is turned on, we need to check recursion ACL * if it exists - and return result to caller. */ { ip_match_list recursion_acl; recursion_acl = server_options->recursion_acl; if (!NS_OPTION_P(OPTION_NORECURSE) && recursion_acl != NULL && !ip_address_allowed(recursion_acl, from.sin_addr)) { recursion_blocked_by_acl = 1; *ra = 0; } } /* * Are queries allowed from this host? */ if (!ns_t_xfr_p(type)) { ip_match_list query_acl; if (zp->z_query_acl != NULL) query_acl = zp->z_query_acl; else query_acl = server_options->query_acl; if (query_acl != NULL && !ip_addr_or_key_allowed(query_acl, from.sin_addr, in_key)) { /* * If this is *not* a zone acl and we would not * have recursed and we have some answer return * what we have with a referral. */ if ((zp->z_query_acl == NULL) && (!hp->rd || NS_OPTION_P(OPTION_NORECURSE) || recursion_blocked_by_acl) && (ntohs(hp->ancount) != 0)) { goto fetchns; } /* * See if we would have made a referral from * an enclosing zone if we are actually in the * cache. */ if (zp->z_type == z_cache && np != NULL) { struct namebuf *access_np; zone = DB_Z_CACHE; for (access_np = np; access_np != NULL; access_np = np_parent(access_np)) { dp = access_np->n_data; while (dp && (dp->d_class != class || dp->d_zone == DB_Z_CACHE)) dp = dp->d_next; if (dp != NULL) { zone = dp->d_zone; np = access_np; break; } } zp = &zones[zone]; if (zp->z_type != z_cache && zp->z_query_acl != NULL && ip_addr_or_key_allowed(zp->z_query_acl, from.sin_addr, in_key) && (!hp->rd || recursion_blocked_by_acl || NS_OPTION_P(OPTION_NORECURSE))) { goto fetchns; } } ns_notice(ns_log_security, - "unapproved query from %s for \"%s\"", + "denied query from %s for \"%s\"", sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdUQ); return (Refuse); } } else { ip_match_list transfer_acl; /* Do they have permission to do a zone transfer? */ if (zp->z_transfer_acl != NULL) transfer_acl = zp->z_transfer_acl; else transfer_acl = server_options->transfer_acl; if (transfer_acl != NULL && !ip_addr_or_key_allowed(transfer_acl, from.sin_addr, in_key)) { ns_notice(ns_log_security, - "unapproved %s from %s for \"%s\" (acl)", + "denied %s from %s for \"%s\" (acl)", p_type(type), sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdUXFR); return (Refuse); } /* Are we master or slave? */ if (zp->z_type != z_master && zp->z_type != z_slave) { ns_notice(ns_log_security, - "unapproved %s from %s for \"%s\" (not master/slave)", + "denied %s from %s for \"%s\" (not master/slave)", p_type(type), sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdUXFR); return (Refuse); } /* Are we authoritative? */ if ((zp->z_flags & Z_AUTH) == 0) { ns_notice(ns_log_security, - "unapproved %s from %s for \"%s\" (not authoritative)", + "denied %s from %s for \"%s\" (not authoritative)", p_type(type), sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdUXFR); return (Refuse); } /* Is the name at a zone cut? */ if (ns_samename(zp->z_origin, dname) != 1) { ns_notice(ns_log_security, - "unapproved %s from %s for \"%s\" (not zone top)", + "denied %s from %s for \"%s\" (not zone top)", p_type(type), sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdUXFR); return (Refuse); } - ns_info(ns_log_security, "approved %s from %s for \"%s\"", - p_type(type), sin_ntoa(from), *dname ? dname : "."); + if (type == ns_t_ixfr) { + ns_info(ns_log_security, "approved %s from %s for \"%s\"", + (ixfr_found) ? p_type(type) : "IXFR/AXFR", + sin_ntoa(from), *dname ? dname : "."); + } else + ns_info(ns_log_security, "approved %s from %s for \"%s\"", + p_type(type), sin_ntoa(from), *dname ? dname : "."); } /* * End Access Control Point */ /* * Yow! */ if (class == ns_c_chaos && type == ns_t_txt && ns_samename(dnbuf, "VERSION.BIND") == 1) { u_char *tp; hp->ancount = htons(1); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = ns_r_noerror; hp->aa = 1; hp->ra = 0; copyCharString(cpp, "VERSION"); /* Name */ copyCharString(cpp, "BIND"); *(*cpp)++ = 0x00; PUTSHORT(T_TXT, *cpp); /* Type */ PUTSHORT(C_CHAOS, *cpp); /* Class */ PUTLONG(0, *cpp); /* TTL */ tp = *cpp; /* Temp RdLength */ PUTSHORT(0, *cpp); copyCharString(cpp, server_options->version); PUTSHORT((*cpp) - (tp + INT16SZ), tp); /* Real RdLength */ *msglenp = *cpp - msg; /* Total message length */ return (Finish); } /* * If we don't know anything about the requested name, * go look for nameservers. */ if (!np || fname != dname) goto fetchns; foundname++; answers = *cpp; count = *cpp - msg; /* The response is authoritative until we add insecure data */ hp->ad = 1; /* Look for NXDOMAIN record with appropriate class * if found return immediately */ for (dp = np->n_data; dp; dp = dp->d_next) { if (!stale(dp) && (dp->d_rcode == ns_r_nxdomain) && (dp->d_class == class)) { #ifdef RETURNSOA n = finddata(np, class, T_SOA, hp, &dname, buflenp, &count); if (n != 0) { if (count) { *cpp += n; *buflenp -= n; *msglenp += n; hp->nscount = htons((u_int16_t)count); } if (hp->rcode == NOERROR_NODATA) { /* this should not occur */ hp->rcode = ns_r_noerror; return (Finish); } } #else count = 0; #endif hp->rcode = ns_r_nxdomain; /* * XXX forcing AA all the time isn't right, but * we have to work that way by default * for compatibility with older servers. */ if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) hp->aa = 1; ns_debug(ns_log_default, 3, "NXDOMAIN aa = %d", hp->aa); if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) return (Finish); founddata = 1; goto fetchns; } } /* * If not NXDOMAIN, the NOERROR_NODATA record might be * anywhere in the chain. Have to go through the grind. */ n = finddata(np, class, type, hp, &dname, buflenp, &count); if (n == 0) { /* * NO data available. Refuse transfer requests, or * look for better servers for other requests. */ if (ns_t_xfr_p(type)) { ns_debug(ns_log_default, 1, "transfer refused: no data"); return (Refuse); } goto fetchns; } if (hp->rcode == NOERROR_NODATA) { hp->rcode = ns_r_noerror; #ifdef RETURNSOA if (count) { *cpp += n; *buflenp -= n; *msglenp += n; hp->nscount = htons(count); } #endif founddata = 1; ns_debug(ns_log_default, 1, "count = %d", count); if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) return (Finish); goto fetchns; } *cpp += n; *buflenp -= n; *msglenp += n; hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); if (fname != dname && type != T_CNAME && type != T_ANY) { if (cname++ >= MAXCNAMES) { ns_debug(ns_log_default, 3, "resp: leaving, MAXCNAMES exceeded"); hp->rcode = ns_r_servfail; return (Finish); } goto try_again; } founddata = 1; ns_debug(ns_log_default, 3, "req: foundname=%d, count=%d, founddata=%d, cname=%d", foundname, count, founddata, cname); if (ns_t_xfr_p(type)) { #ifdef BIND_UPDATE if ((zp->z_flags & Z_NEED_SOAUPDATE) != 0) if (incr_serial(zp) < 0) ns_error(ns_log_default, "error updating serial number for %s from %d", zp->z_origin, zp->z_serial); #endif /* * Just return SOA if "up to date". */ if (type == ns_t_ixfr) { hp->aa = 1; if ((SEQ_GT(serial_ixfr, zp->z_serial) || - serial_ixfr == zp->z_serial)) + serial_ixfr == zp->z_serial)) { return (Finish); + } } /* * We don't handle UDP based IXFR queries (yet). * Tell client to retry with TCP by returning SOA. */ if (qsp == NULL) return (Finish); else { if (!ixfr_found) { qsp->flags |= STREAM_AXFRIXFR; hp->qdcount = htons(1); } ns_xfr(qsp, np, zone, class, type, hp->opcode, ntohs(hp->id), serial_ixfr, in_tsig); } return (Return); } if (count > 1 && type == T_A && !NS_OPTION_P(OPTION_NORECURSE) && hp->rd) sort_response(answers, *cpp, count, &from); fetchns: /* * If we're already out of room in the response, we're done. */ if (hp->tc) return (Finish); if (hp->ancount == 0) hp->ad = 0; /* * Look for name servers to refer to and fill in the authority * section or record the address for forwarding the query * (recursion desired). */ free_nsp(nsp); nsp[0] = NULL; count = 0; switch (findns(&np, class, nsp, &count, 0)) { case NXDOMAIN: /* We are authoritative for this np. */ if (!foundname) hp->rcode = ns_r_nxdomain; ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", dname, hp->rcode); if (class != C_ANY) { hp->aa = 1; if (np && (!foundname || !founddata)) { n = doaddauth(hp, *cpp, *buflenp, np, nsp[0]); *cpp += n; *buflenp -= n; #ifdef ADDAUTH } else if (ntohs(hp->ancount) != 0) { /* don't add NS records for NOERROR NODATA as some servers can get confused */ free_nsp(nsp); switch (findns(&np, class, nsp, &count, 1)) { case NXDOMAIN: case SERVFAIL: break; default: if (np && (type != T_NS || np != anp) ) { n = add_data(np, nsp, *cpp, *buflenp, &count); if (n < 0) { hp->tc = 1; n = (-n); } *cpp += n; *buflenp -= n; hp->nscount = htons((u_int16_t) count); } } #endif /*ADDAUTH*/ } } free_nsp(nsp); return (Finish); case SERVFAIL: /* We're authoritative but the zone isn't loaded. */ if (!founddata && !(NS_ZOPTION_P(zp, OPTION_FORWARD_ONLY) && NS_ZFWDTAB(zp))) { hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } } if (!founddata && hp->rd && recursion_blocked_by_acl) { ns_notice(ns_log_security, - "unapproved recursive query from %s for %s", + "denied recursion for query from %s for %s", sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdURQ); } /* * If we successfully found the answer in the cache, * or this is not a recursive query, or we are purposely * never recursing, or recursion is prohibited by ACL, then * add the nameserver references("authority section") here * and we're done. */ if (founddata || !hp->rd || NS_OPTION_P(OPTION_NORECURSE) || recursion_blocked_by_acl) { /* * If the qtype was NS, and the np of the authority is * the same as the np of the data, we don't need to add * another copy of the answer here in the authority * section. */ if (!founddata || type != T_NS || anp != np) { n = add_data(np, nsp, *cpp, *buflenp, &count); if (n < 0) { hp->tc = 1; n = (-n); } *cpp += n; *buflenp -= n; hp->nscount = htons(ntohs(hp->nscount) + (u_int16_t)count); } free_nsp(nsp); /* Our caller will handle the Additional section. */ return (Finish); } /* * At this point, we don't have the answer, but we do * have some NS's to try. If the user would like us * to recurse, create the initial query. If a cname * is involved, we need to build a new query and save * the old one in cmsg/cmsglen. */ if (cname) { omsg = (u_char *)memget((unsigned) *msglenp); if (omsg == NULL) { ns_info(ns_log_default, "ns_req: Out Of Memory"); hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } id = hp->id; omsglen = *msglenp; memcpy(omsg, msg, omsglen); n = res_nmkquery(&res, QUERY, dname, class, type, NULL, 0, NULL, msg, *msglenp + *buflenp); if (n < 0) { ns_info(ns_log_default, "res_mkquery(%s) failed", dname); hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } *msglenp = n; } n = ns_forw(nsp, msg, *msglenp, from, qsp, dfd, &qp, dname, class, type, np, 0, in_tsig); if (n != FW_OK && cname) { memput(omsg, omsglen); omsg = NULL; } switch (n) { case FW_OK: if (cname) { qp->q_cname = cname; qp->q_cmsg = omsg; qp->q_cmsglen = omsglen; qp->q_id = id; } break; case FW_DUP: break; /* Duplicate request dropped */ case FW_NOSERVER: /* * Don't go into an infinite loop if * the admin gave root NS records in the cache * file without giving address records * for the root servers. */ if (np) { if (NAME(*np)[0] == '\0') { ns_notice(ns_log_default, "ns_req: no address for root server"); hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } for (dp = np->n_data; dp ; dp = dp->d_next) if (dp->d_zone && match(dp, class, T_NS)) break; if (dp) { /* * we know the child zone exists but are * missing glue. * * nslookup has called sysquery() to get the * missing glue. * * for UDP, drop the response and let the * client retry. for TCP, we should probably * (XXX) hold open the TCP connection for a * while in case the sysquery() comes back * soon. meanwhile we SERVFAIL. */ if (qsp) goto do_servfail; break; } np = np_parent(np); } goto fetchns; /* Try again. */ case FW_SERVFAIL: do_servfail: hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } free_nsp(nsp); return (Return); } static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, u_char *msg, struct sockaddr_in from) { int dlen, alen, n, type, class, count; char dnbuf[MAXDNAME], anbuf[PACKETSZ], *data, *fname; nameserIncr(from.sin_addr, nssRcvdIQ); if (ntohs(hp->ancount) != 1 || ntohs(hp->qdcount) != 0 || ntohs(hp->nscount) != 0 || ntohs(hp->arcount) != 0) { ns_debug(ns_log_default, 1, "FORMERR IQuery header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } /* * Skip domain name, get class, and type. */ if ((n = dn_skipname(*cpp, eom)) < 0) { ns_debug(ns_log_default, 1, "FORMERR IQuery packet name problem"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; if (*cpp + 3 * INT16SZ + INT32SZ > eom) { ns_debug(ns_log_default, 1, "FORMERR IQuery message too short"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(type, *cpp); GETSHORT(class, *cpp); *cpp += INT32SZ; /* ttl */ GETSHORT(dlen, *cpp); *cpp += dlen; if (*cpp != eom) { ns_debug(ns_log_default, 1, "FORMERR IQuery message length off"); hp->rcode = ns_r_formerr; return (Finish); } /* * not all inverse queries are handled. */ switch (type) { case T_A: if (!NS_OPTION_P(OPTION_FAKE_IQUERY) || dlen != INT32SZ) { if (dlen != INT32SZ) ns_warning(ns_log_security, "bad iquery from %s", inet_ntoa(from.sin_addr)); return (Refuse); } break; default: ns_warning(ns_log_security, "unsupported iquery type from %s", inet_ntoa(from.sin_addr)); return (Refuse); } ns_debug(ns_log_default, 1, "req: IQuery class %d type %d", class, type); fname = (char *)msg + HFIXEDSZ; alen = (char *)*cpp - fname; if ((size_t)alen > sizeof anbuf) { ns_warning(ns_log_security, "bad iquery from %s", inet_ntoa(from.sin_addr)); return (Refuse); } memcpy(anbuf, fname, alen); data = anbuf + alen - dlen; *cpp = (u_char *)fname; *buflenp -= HFIXEDSZ; count = 0; #ifdef QRYLOG if (qrylog) { char tmp[sizeof "255.255.255.255"]; strcpy(tmp, inet_ntoa(from.sin_addr)); ns_info(ns_log_queries, "XX /%s/%s/-%s", tmp, inet_ntoa(ina_get((u_char *)data)), p_type(type)); } #endif /*QRYLOG*/ /* * We can only get here if the option "fake-iquery" is on in the boot * file. * * What we do here is send back a bogus response of "[dottedquad]". * A better strategy would be to turn this into a PTR query, but that * would legitimize inverse queries in a way they do not deserve. */ sprintf(dnbuf, "[%s]", inet_ntoa(ina_get((u_char *)data))); *buflenp -= QFIXEDSZ; n = dn_comp(dnbuf, *cpp, *buflenp, NULL, NULL); if (n < 0) { hp->tc = 1; return (Finish); } *cpp += n; PUTSHORT((u_int16_t)type, *cpp); PUTSHORT((u_int16_t)class, *cpp); *buflenp -= n; count++; ns_debug(ns_log_default, 1, "req: IQuery %d records", count); hp->qdcount = htons((u_int16_t)count); if (alen > *buflenp) { hp->tc = 1; return (Finish); } memcpy(*cpp, anbuf, alen); *cpp += alen; return (Finish); } /* * Test a datum for validity and return non-zero if it is out of date. */ int stale(struct databuf *dp) { struct zoneinfo *zp = &zones[dp->d_zone]; switch (zp->z_type) { case z_master: return (0); #ifdef STUBS case z_stub: /* root stub zones have DB_F_HINT set */ if (dp->d_flags & DB_F_HINT) return (0); /* FALLTROUGH */ #endif case z_slave: /* * Check to see whether a secondary zone has expired or * time warped; if so clear authority flag for zone, * schedule the zone for immediate maintenance, and * return true. */ if ((int32_t)(tt.tv_sec - zp->z_lastupdate) > (int32_t)zp->z_expire) { ns_debug(ns_log_default, 1, "stale: secondary zone %s expired", zp->z_origin); if (!haveComplained((u_long)zp, (u_long)stale)) { ns_notice(ns_log_default, "secondary zone \"%s\" expired", zp->z_origin); } zp->z_flags &= ~Z_AUTH; if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) { zp->z_time = tt.tv_sec; sched_zone_maint(zp); } return (1); } if (zp->z_lastupdate > tt.tv_sec) { if (!haveComplained((u_long)zp, (u_long)stale)) { ns_notice(ns_log_default, "secondary zone \"%s\" time warp", zp->z_origin); } zp->z_flags &= ~Z_AUTH; if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) { zp->z_time = tt.tv_sec; sched_zone_maint(zp); } return (1); } return (0); case z_hint: case z_cache: if (dp->d_flags & DB_F_HINT || dp->d_ttl >= (u_int32_t)tt.tv_sec) return (0); ns_debug(ns_log_default, 3, "stale: ttl %d %ld (x%lx)", dp->d_ttl, (long)(dp->d_ttl - tt.tv_sec), (u_long)dp->d_flags); return (1); default: /* FALLTHROUGH */ ; } panic("stale: impossible condition", NULL); /* NOTREACHED */ return (0); /* Make gcc happy. */ } /* * Copy databuf into a resource record for replies. * Return size of RR if OK, -1 if buffer is full. */ int make_rr(const char *name, struct databuf *dp, u_char *buf, int buflen, int doadd, u_char **comp_ptrs, u_char **edp, int use_minimum) { u_char *cp; u_char *cp1, *sp; struct zoneinfo *zp; int32_t n; int16_t type = dp->d_type; u_int32_t ttl; ns_debug(ns_log_default, 5, "make_rr(%s, %lx, %lx, %d, %d) %d zone %d ttl %lu", name, (u_long)dp, (u_long)buf, buflen, doadd, dp->d_size, dp->d_zone, (u_long)dp->d_ttl); if (dp->d_rcode && dp->d_size == 0) panic("make_rr: impossible d_rcode value", NULL); zp = &zones[dp->d_zone]; /* check for outdated RR before updating comp_ptrs[] by dn_comp() */ if (zp->z_type == Z_CACHE) { if ((dp->d_flags & DB_F_HINT) != 0 || dp->d_ttl < (u_int32_t)tt.tv_sec) { ttl = 0; } else ttl = dp->d_ttl - (u_int32_t) tt.tv_sec; } else { if (dp->d_ttl != USE_MINIMUM && !use_minimum) ttl = dp->d_ttl; else ttl = zp->z_minimum; /* really default */ } buflen -= RRFIXEDSZ; if (buflen < 0) return (-1); #ifdef RETURNSOA if (dp->d_rcode) { name = (char *)dp->d_data; name += strlen(name) +1; name += strlen(name) +1; name += 5 * INT32SZ; type = T_SOA; } #endif if ((n = dn_comp(name, buf, buflen, comp_ptrs, edp)) < 0) - return (-1); + goto cleanup; cp = buf + n; buflen -= n; if (buflen < 0) - return (-1); + goto cleanup; PUTSHORT((u_int16_t)type, cp); PUTSHORT((u_int16_t)dp->d_class, cp); PUTLONG(ttl, cp); sp = cp; cp += INT16SZ; switch (type) { case T_CNAME: case T_MG: case T_MR: case T_PTR: n = dn_comp((char *)dp->d_data, cp, buflen, comp_ptrs, edp); if (n < 0) - return (-1); + goto cleanup; PUTSHORT((u_int16_t)n, sp); cp += n; break; case T_MB: case T_NS: /* Store domain name in answer */ n = dn_comp((char *)dp->d_data, cp, buflen, comp_ptrs, edp); if (n < 0) - return (-1); + goto cleanup; PUTSHORT((u_int16_t)n, sp); cp += n; if (doadd) { addname((char*)dp->d_data, name, type, T_A, dp->d_class); addname(name, name, type, T_KEY, dp->d_class); } break; case T_SOA: case T_MINFO: case T_RP: cp1 = dp->d_data; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) - return (-1); + goto cleanup; cp += n; buflen -= type == T_SOA ? n + 5 * INT32SZ : n; if (buflen < 0) - return (-1); + goto cleanup; cp1 += strlen((char *)cp1) + 1; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) - return (-1); + goto cleanup; cp += n; if (type == T_SOA) { cp1 += strlen((char *)cp1) + 1; #ifdef BIND_UPDATE if (zp->z_flags & Z_NEED_SOAUPDATE) if (incr_serial(zp) < 0) ns_error(ns_log_default, "error updating serial number for %s from %d", zp->z_origin, zp->z_serial); #endif n = 5 * INT32SZ; memcpy(cp, cp1, n); cp += n; if (doadd) addname(name, name, type, T_KEY, dp->d_class); } n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; case T_NAPTR: /* cp1 == our data/ cp == data of RR */ cp1 = dp->d_data; /* copy order */ buflen -= INT16SZ; if (buflen < 0) - return (-1); + goto cleanup; memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* copy preference */ buflen -= INT16SZ; if (buflen < 0) - return (-1); + goto cleanup; memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Flags */ n = *cp1++; ns_debug(ns_log_default, 1, "size of n at flags = %d", n); buflen -= n + 1; if (buflen < 0) - return (-1); + goto cleanup; *cp++ = n; memcpy(cp, cp1, n); cp += n; cp1 += n; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Service */ n = *cp1++; buflen -= n + 1; if (buflen < 0) - return (-1); + goto cleanup; *cp++ = n; memcpy(cp, cp1, n); cp += n; cp1 += n; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Regexp */ n = *cp1++; buflen -= n + 1; if (buflen < 0) - return (-1); + goto cleanup; *cp++ = n; memcpy(cp, cp1, n); cp += n; cp1 += n; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Replacement */ ns_debug(ns_log_default, 1, "Replacement = %s", cp1); n = dn_comp((char *)cp1, cp, buflen, dnptrs, edp); ns_debug(ns_log_default, 1, "dn_comp's n = %u", n); if (n < 0) - return (-1); + goto cleanup; cp += n; /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "saved size n = %u", n); PUTSHORT((u_int16_t)n, sp); break; case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* cp1 == our data/ cp == data of RR */ cp1 = dp->d_data; if ((buflen -= INT16SZ) < 0) - return (-1); + goto cleanup; /* copy preference */ memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; if (type == T_SRV) { buflen -= INT16SZ*2; if (buflen < 0) - return (-1); + goto cleanup; memcpy(cp, cp1, INT16SZ*2); cp += INT16SZ*2; cp1 += INT16SZ*2; } n = dn_comp((char *)cp1, cp, buflen, (type == ns_t_mx) ? comp_ptrs : NULL, (type == ns_t_mx) ? edp : NULL); if (n < 0) - return (-1); + goto cleanup; cp += n; /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); if (doadd) addname((char*)cp1, name, type, T_A, dp->d_class); break; case T_PX: cp1 = dp->d_data; if ((buflen -= INT16SZ) < 0) - return (-1); + goto cleanup; /* copy preference */ memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) - return (-1); + goto cleanup; cp += n; buflen -= n; cp1 += strlen((char *)cp1) + 1; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) - return (-1); + goto cleanup; cp += n; /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; case T_SIG: /* cp1 == our data; cp == data of target RR */ cp1 = dp->d_data; /* first just copy over the type_covered, algorithm, */ /* labels, orig ttl, two timestamps, and the footprint */ if ((dp->d_size - 18) > buflen) - return (-1); /* out of room! */ + goto cleanup; /* out of room! */ memcpy(cp, cp1, 18); cp += 18; cp1 += 18; buflen -= 18; /* then the signer's name */ n = dn_comp((char *)cp1, cp, buflen, NULL, NULL); if (n < 0) - return (-1); + goto cleanup; cp += n; buflen -= n; cp1 += strlen((char*)cp1)+1; /* finally, we copy over the variable-length signature */ n = dp->d_size - (u_int16_t)((cp1 - dp->d_data)); if (n > buflen) - return (-1); /* out of room! */ + goto cleanup; /* out of room! */ memcpy(cp, cp1, n); cp += n; - /* save data length & return */ + /* save data length & return */ n = (u_int16_t)((cp - sp) - INT16SZ); - PUTSHORT((u_int16_t)n, sp); + PUTSHORT((u_int16_t)n, sp); break; case T_NXT: cp1 = dp->d_data; n = dn_comp((char *)cp1, cp, buflen, NULL, NULL); if (n < 0) - return (-1); + goto cleanup; cp += n; buflen -=n; cp1 += strlen((char *)cp1) + 1; /* copy nxt bit map */ n = dp->d_size - (u_int16_t)((cp1 - dp->d_data)); if (n > buflen) - return (-1); /* out of room! */ + goto cleanup; /* out of room! */ memcpy(cp, cp1, n); cp += n; buflen -= n; n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; default: if ((type == T_A || type == T_AAAA) && doadd) addname(name, name, type, T_KEY, dp->d_class); if (dp->d_size > buflen) - return (-1); + goto cleanup; memcpy(cp, dp->d_data, dp->d_size); PUTSHORT((u_int16_t)dp->d_size, sp); cp += dp->d_size; } return (cp - buf); + + cleanup: + /* Rollback RR. */ + ns_name_rollback(buf, (const u_char **)comp_ptrs, + (const u_char **)edp); + return (-1); } static void addname(const char *dname, const char *rname, u_int16_t rtype, u_int16_t type, u_int16_t class) { struct addinfo *ap; int n; for (ap = addinfo, n = addcount; --n >= 0; ap++) if (ns_samename(ap->a_dname, dname) == 1 && ap->a_type == type) return; /* add domain name to additional section */ if (addcount < NADDRECS) { addcount++; ap->a_dname = savestr(dname, 1); ap->a_rname = savestr(rname, 1); ap->a_rtype = rtype; ap->a_type = type; ap->a_class = class; } } /* * Lookup addresses/keys for names in addinfo and put into the message's * additional section. */ int doaddinfo(HEADER *hp, u_char *msg, int msglen) { register struct namebuf *np; register struct databuf *dp; register struct addinfo *ap; register u_char *cp; struct hashbuf *htp; const char *fname; register int n, count; register int ns_logging; int finishedA = 0; int save_addcount = addcount; if (!addcount) return (0); ns_logging = ns_wouldlog(ns_log_default, 3); if (ns_logging) ns_debug(ns_log_default, 3, "doaddinfo() addcount = %d", addcount); if (hp->tc) { ns_debug(ns_log_default, 4, "doaddinfo(): tc already set, bailing"); return (0); } count = 0; cp = msg; loop: for (ap = addinfo; --addcount >= 0; ap++) { int foundany = 0, foundcname = 0, save_count = count, save_msglen = msglen; u_char *save_cp = cp; if ((finishedA == 1 && ap->a_type == T_A) || (finishedA == 0 && ap->a_type == T_KEY)) continue; if (ns_logging) ns_debug(ns_log_default, 3, "do additional \"%s\" (from \"%s\")", ap->a_dname, ap->a_rname); htp = hashtab; /* because "nlookup" stomps on arg. */ np = nlookup(ap->a_dname, &htp, &fname, 0); if (np == NULL || fname != ap->a_dname) goto next_rr; if (ns_logging) ns_debug(ns_log_default, 3, "found it"); /* look for the data */ (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_rcode) continue; if ((match(dp, (int)ap->a_class, T_CNAME) && dp->d_type == T_CNAME) || (match(dp, C_IN, T_CNAME) && dp->d_type == T_CNAME)) { foundcname++; break; } if (ap->a_type == T_A && !match(dp, (int)ap->a_class, T_A) && !match(dp, C_IN, T_A) && !match(dp, (int)ap->a_class, T_AAAA) && - !match(dp, C_IN, T_AAAA)) { + !match(dp, C_IN, T_AAAA) && + !match(dp, (int)ap->a_class, ns_t_a6) && + !match(dp, C_IN, ns_t_a6)) { continue; } if (ap->a_type == T_KEY && !match(dp, (int)ap->a_class, T_KEY) && !match(dp, C_IN, T_KEY)) continue; foundany++; /* * Should be smart and eliminate duplicate * data here. XXX */ if ((n = make_rr(ap->a_dname, dp, cp, msglen, 0, dnptrs, dnptrs_end, 0)) < 0) { /* truncation in the additional-data section * is not all that serious. we do not set TC, * since the answer and authority sections are * OK; however, since we're not setting TC we * have to make sure that none of the RR's for * this name go out (!TC implies that all * {name,type} appearances are complete -- and * since we only do A RR's here, the name is * the key). vixie, 23apr93 */ ns_debug(ns_log_default, 5, "addinfo: not enough room, remaining msglen = %d", save_msglen); + /* Rollback RRset. */ + ns_name_rollback(save_cp, + (const u_char **)dnptrs, + (const u_char **)dnptrs_end); cp = save_cp; msglen = save_msglen; count = save_count; break; } ns_debug(ns_log_default, 5, "addinfo: adding address data n = %d", n); cp += n; msglen -= n; count++; } next_rr: if (!NS_OPTION_P(OPTION_NOFETCHGLUE) && !foundcname && !foundany && (ap->a_type == T_A || ap->a_type == T_AAAA)) { /* ask a real server for this info */ (void) sysquery(ap->a_dname, (int)ap->a_class, ap->a_type, NULL, 0, ns_port, QUERY); } if (foundcname) { if (!haveComplained(nhash(ap->a_dname), nhash(ap->a_rname))) { ns_info(ns_log_cname, "\"%s %s %s\" points to a CNAME (%s)", ap->a_rname, p_class(ap->a_class), p_type(ap->a_rtype), ap->a_dname); } } freestr(ap->a_dname); freestr(ap->a_rname); } if (finishedA == 0) { finishedA = 1; addcount = save_addcount; goto loop; /* now do the KEYs... */ } hp->arcount = htons((u_int16_t)count); return (cp - msg); } int doaddauth(HEADER *hp, u_char *cp, int buflen, struct namebuf *np, struct databuf *dp) { char dnbuf[MAXDNAME]; int n; getname(np, dnbuf, sizeof dnbuf); if (stale(dp)) { ns_debug(ns_log_default, 1, "doaddauth: can't add stale '%s' (%d)", dnbuf, buflen); return (0); } n = make_rr(dnbuf, dp, cp, buflen, 1, dnptrs, dnptrs_end, 1); if (n <= 0) { ns_debug(ns_log_default, 1, "doaddauth: can't add oversize '%s' (%d) (n=%d)", dnbuf, buflen, n); if (n < 0) { hp->tc = 1; } return (0); } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; hp->nscount = htons(ntohs(hp->nscount) + 1); return (n); } void free_addinfo() { struct addinfo *ap; for (ap = addinfo; --addcount >= 0; ap++) { freestr(ap->a_dname); freestr(ap->a_rname); } addcount = 0; } void free_nsp(struct databuf **nsp) { while (*nsp) { DRCNTDEC(*nsp); if ((*nsp)->d_rcnt) ns_debug(ns_log_default, 3, "free_nsp: %s rcnt %d", (*nsp)->d_data, (*nsp)->d_rcnt); else { ns_debug(ns_log_default, 3, "free_nsp: %s rcnt %d delayed", (*nsp)->d_data, (*nsp)->d_rcnt); db_freedata(*nsp); /* delayed free */ } *nsp++ = NULL; } } static void copyCharString(u_char **dst, const char *src) { size_t len = strlen(src) & 0xff; *(*dst)++ = (u_char) len; memcpy(*dst, src, len); *dst += len; } Index: head/contrib/bind/bin/named/ns_resp.c =================================================================== --- head/contrib/bind/bin/named/ns_resp.c (revision 60940) +++ head/contrib/bind/bin/named/ns_resp.c (revision 60941) @@ -1,3975 +1,4003 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_resp.c 4.65 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: ns_resp.c,v 8.133 1999/11/05 04:40:57 vixie Exp $"; +static const char rcsid[] = "$Id: ns_resp.c,v 8.143 2000/05/09 07:38:38 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static u_int8_t norootlogged[MAXCLASS]; /* XXX- should be a bitmap */ static const char skipnameFailedAnswer[] = "skipname failed in answer", skipnameFailedAuth[] = "skipname failed in authority", skipnameFailedQuery[] = "skipname failed in query", outofDataQuery[] = "ran out of data in query", outofDataAnswer[] = "ran out of data in answer", notSingleQuery[] = "not exactly one query", expandFailedQuery[] = "dn_expand failed in query", expandFailedAnswer[] = "dn_expand failed in answer", expandFailedAuth[] = "dn_expand failed in authority", outofDataAuth[] = "ran out of data in authority", dlenOverrunAnswer[] = "dlen overrun in answer", dlenOverrunAuth[] = "dlen overrun in authority", dlenUnderrunAnswer[] = "dlen underrun in answer", outofDataFinal[] = "out of data in final pass", outofDataAFinal[] = "out of data after final pass", badNameFound[] = "found an invalid domain name", wrongQuestion[] = "answer to wrong question", - danglingCname[] = "dangling CNAME pointer"; + danglingCname[] = "dangling CNAME pointer", + nonRecursiveForwarder[]= "non-recursive forwarder"; struct db_list { struct db_list *db_next; struct databuf *db_dp; }; struct flush_set { char * fs_name; int fs_type; int fs_class; u_int fs_cred; struct db_list *fs_list; struct db_list *fs_last; }; static void rrsetadd(struct flush_set *, const char *, struct databuf *), rrsetupdate(struct flush_set *, int flags, struct sockaddr_in, int), flushrrset(struct flush_set *, struct sockaddr_in), free_flushset(struct flush_set *, int), check_hints(struct flush_set *); static int rrsetcmp(char *, struct db_list *, struct hashbuf *), check_root(void), check_ns(void), wanted(const struct databuf *, int, int), wantedsig(const struct databuf *, int, int), rrextract(u_char *, int, u_char *, struct databuf **, char *, int, struct sockaddr_in, char **); static void mark_bad(struct qinfo *qp, struct sockaddr_in from); static void mark_lame(struct qinfo *qp, struct sockaddr_in from); static void fast_retry(struct qinfo *qp, struct sockaddr_in from); static void add_related_additional(char *); static void free_related_additional(void); static int related_additional(char *); static void freestr_maybe(char **); static enum ordering match_order(const struct namebuf *, int, int); static int match_name(const struct namebuf *, const char *, size_t); #define MAX_RELATED 100 static int num_related = 0; static char *related[MAX_RELATED]; static char * learntFrom(struct qinfo *qp, struct sockaddr_in *server) { static char *buf = NULL; char *a, *ns, *na; struct databuf *db; int i; a = ns = na = ""; for (i = 0; (u_int)i < qp->q_naddr; i++) { if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, server->sin_addr)) { db = qp->q_addr[i].ns; if (db != NULL) { if (NS_OPTION_P(OPTION_HOSTSTATS)) { char nsbuf[20]; if (db->d_ns != NULL) { strcpy(nsbuf, inet_ntoa(db->d_ns->addr)); ns = nsbuf; } else { ns = zones[db->d_zone] .z_origin; } } if (db->d_rcode == 0) na = (char*)qp->q_addr[i].ns->d_data; } if (NS_OPTION_P(OPTION_HOSTSTATS)) { char abuf[20]; db = qp->q_addr[i].nsdata; if (db != NULL) { if (db->d_ns != NULL) { strcpy(abuf, inet_ntoa(db->d_ns->addr)); a = abuf; } else { a = zones[db->d_zone].z_origin; } } } break; } } if (a == ns && ns == na) /* all "UNKNOWN" */ return (NULL); if (*a == '\0') a = "\".\""; if (*ns == '\0') ns = "\".\""; if (*na == '\0') na = "\".\""; if (NS_OPTION_P(OPTION_HOSTSTATS)) { static const char fmt[] = " '%s': learnt (A=%s,NS=%s)"; buf = newstr(sizeof fmt + strlen(na) + strlen(a) + strlen(ns), 0); if (buf == NULL) return (NULL); sprintf(buf, fmt, na, a, ns); } else { static const char fmt[] = " '%s'"; buf = newstr(sizeof fmt + strlen(na), 0); if (buf == NULL) return (NULL); sprintf(buf, fmt, na); } return (buf); } void ns_resp(u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp) { struct qinfo *qp; HEADER *hp; struct qserv *qs = NULL; struct databuf *ns, *ns2; u_char *cp, *answers, *eom = msg + msglen; struct flush_set *flushset = NULL; int flushset_size = 0; struct sockaddr_in *nsa; struct databuf *nsp[NSMAX]; int i, c, n, qdcount, ancount, aucount, nscount, arcount, arfirst; int soacount; u_int qtype, qclass; int restart; /* flag for processing cname response */ int validanswer, dbflags; int cname, lastwascname, externalcname; int count, founddata, foundname; int buflen; int newmsglen; char name[MAXDNAME], qname[MAXDNAME], aname[MAXDNAME]; char msgbuf[MAXDNAME+100]; char *dname, tmpdomain[MAXDNAME]; const char *fname; const char *formerrmsg = "brain damage"; u_char newmsg[PACKETSZ]; u_char **dpp, *tp; time_t rtrip; struct hashbuf *htp; struct namebuf *np; struct fwdinfo *fwd; struct databuf *dp; int forcecmsg = 0; char *tname = NULL; int sendto_errno = 0; int has_tsig, oldqlen; u_char *oldqbuf; u_char *smsg; int smsglen, smsgsize, siglen; u_char sig[TSIG_SIG_SIZE]; time_t tsig_time; DST_KEY *key; nameserIncr(from.sin_addr, nssRcvdR); nsp[0] = NULL; hp = (HEADER *) msg; if ((qp = qfindid(hp->id)) == NULL ) { ns_debug(ns_log_default, 1, "DUP? dropped (id %d)", ntohs(hp->id)); nameserIncr(from.sin_addr, nssRcvdDupR); return; } if (ns_wouldlog(ns_log_default, 2)) { ns_debug(ns_log_default, 2, "Response (%s %s %s) nsid=%d id=%d", (qp->q_flags & Q_SYSTEM) ?"SYSTEM" :"USER", (qp->q_flags & Q_PRIMING) ?"PRIMING" :"NORMAL", (qp->q_flags & Q_ZSERIAL) ?"ZSERIAL" :"-", ntohs(qp->q_nsid), ntohs(qp->q_id)); } if (qp->q_nstsig == NULL) has_tsig = 0; else { int ret; ret = ns_verify(msg, &msglen, qp->q_nstsig->key, qp->q_nstsig->sig, qp->q_nstsig->siglen, NULL, NULL, &tsig_time, 0); if (ret == 0) has_tsig = 1; else { if (hp->rcode == NOERROR) hp->rcode = NOTAUTH; ns_debug(ns_log_default, 1, "resp: error bad tsig, record dropped"); return; } } /* * Here we handle high level formatting problems by parsing the header. */ qdcount = ntohs(hp->qdcount); ancount = ntohs(hp->ancount); aucount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); free_addinfo(); /* sets addcount to zero */ cp = msg + HFIXEDSZ; dpp = dnptrs; *dpp++ = msg; if ((*cp & INDIR_MASK) == 0) *dpp++ = cp; *dpp = NULL; if (qdcount == 1) { n = dn_expand(msg, eom, cp, qname, sizeof(qname)); if (n <= 0) { formerrmsg = expandFailedQuery; goto formerr; } cp += n; if (cp + 2 * INT16SZ > eom) { formerrmsg = outofDataQuery; goto formerr; } GETSHORT(qtype, cp); GETSHORT(qclass, cp); if (!ns_nameok(qp, qname, qclass, NULL, response_trans, ns_ownercontext(qtype, response_trans), qname, from.sin_addr)) { formerrmsg = badNameFound; goto refused; } if (cp > eom) { formerrmsg = outofDataQuery; goto formerr; } if (qp->q_msg && qp->q_msglen && !res_nameinquery(qname, qtype, qclass, qp->q_msg, qp->q_msg + qp->q_msglen)) { sprintf(msgbuf, "query section mismatch (%s %s %s)", qname, p_class(qclass), p_type(qtype)); formerrmsg = msgbuf; goto formerr; } if (ns_samename(qp->q_name, qname) != 1 || qp->q_class != qclass || qp->q_type != qtype) { formerrmsg = wrongQuestion; goto formerr; } } else { strcpy(qname, qp->q_name); qclass = qp->q_class; qtype = qp->q_type; } /* cp now points after the query section. */ /* * Here we handle bad responses from servers. * Several possibilities come to mind: * The server is sick and returns SERVFAIL * The server returns some garbage opcode (it's sick) * The server can't understand our query and return FORMERR * In all these cases, we drop the packet, disable retries on * this server and immediately force a retry. */ if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN) || (hp->opcode != QUERY #ifdef BIND_NOTIFY && hp->opcode != NS_NOTIFY_OP #endif )) { ns_debug(ns_log_default, 2, "resp: error (ret %d, op %d), dropped", hp->rcode, hp->opcode); switch (hp->rcode) { case SERVFAIL: nameserIncr(from.sin_addr, nssRcvdFail); break; case FORMERR: nameserIncr(from.sin_addr, nssRcvdFErr); break; default: nameserIncr(from.sin_addr, nssRcvdErr); break; } if (ns_samename(qp->q_name, qp->q_domain) == 1 && hp->rcode == SERVFAIL && hp->opcode == QUERY) mark_lame(qp, from); mark_bad(qp, from); fast_retry(qp, from); return; } if (qdcount != 1) { /* We don't generate or forward these (yet). */ formerrmsg = notSingleQuery; goto formerr; } /* * Determine if the response came from a forwarder. Packets from * anyplace not listed as a forwarder or as a server to whom we * might have forwarded the query will be dropped. * XXX - should put this in STATS somewhere. */ for (fwd = NS_ZFWDTAB(qp->q_fzone); fwd; fwd = fwd->next) - if (ina_equal(fwd->fwdaddr.sin_addr, from.sin_addr)) + if (ina_equal(fwd->fwddata->fwdaddr.sin_addr, from.sin_addr)) break; /* - * XXX: note bad ambiguity here. if one of our forwarders is also - * a delegated server for some domain, then we will not update - * the RTT information on any replies we get from those servers. - * Workaround: disable recursion on authoritative servers so that - * the ambiguity does not arise. - */ /* - * If we weren't using a forwarder, find the qinfo pointer and update + * find the qinfo pointer and update * the rtt and fact that we have called on this server before. */ - if (fwd == NULL) { + { struct timeval *stp; for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) if (ina_equal(qs->ns_addr.sin_addr, from.sin_addr)) break; if ((u_int)n >= qp->q_naddr) { if (!haveComplained(ina_ulong(from.sin_addr), (u_long)"unexpected source")) { ns_info(ns_log_default, "Response from unexpected source (%s)", sin_ntoa(from)); } /* * We don't know who this response came from so it * gets dropped on the floor. */ return; } stp = &qs->stime; /* Handle response from different (untried) interface. */ if (qs->ns != NULL && stp->tv_sec == 0) { ns = qs->ns; while (qs > qp->q_addr && (qs->stime.tv_sec == 0 || qs->ns != ns)) qs--; *stp = qs->stime; /* XXX - sometimes stp still ends up pointing to * a zero timeval, in spite of the above attempt. * Why? What should we do about it? */ /* XXX - catch aliases here */ } /* compute query round trip time */ /* XXX - avoid integer overflow, which is quite likely if stp * points to a zero timeval (see above). * rtrip is of type time_t, which we assume is at least * as big as an int. */ if ((tt.tv_sec - stp->tv_sec) > (INT_MAX-999)/1000) { rtrip = INT_MAX; } else { rtrip = ((tt.tv_sec - stp->tv_sec) * 1000 + (tt.tv_usec - stp->tv_usec) / 1000); } if (ns_wouldlog(ns_log_default,3)) { ns_debug(ns_log_default, 3, "stime %lu/%lu now %lu/%lu rtt %ld", (u_long)stp->tv_sec, (u_long)stp->tv_usec, (u_long)tt.tv_sec, (u_long)tt.tv_usec, (long)rtrip); } /* prevent floating point overflow, limit to 1000 sec */ if (rtrip > 1000000) { rtrip = 1000000; } ns = qs->nsdata; /* * Don't update nstime if this doesn't look * like an address databuf now. XXX */ if (ns && ns->d_type == T_A && ns->d_class == qs->ns->d_class) { u_long t; if (ns->d_nstime == 0) t = rtrip; else t = ns->d_nstime * ALPHA + (1 - ALPHA) * rtrip; if (t > 65535) t = 65535; ns->d_nstime = (u_int16_t)t; } /* * Record the source so that we do not use this NS again. */ if (ns && qs->ns && (qp->q_nusedns < NSMAX)) { qp->q_usedns[qp->q_nusedns++] = qs->ns; if (ns_wouldlog(ns_log_default,2)) { ns_debug(ns_log_default, 2, "NS #%d addr %s used, rtt %d", n, sin_ntoa(qs->ns_addr), ns->d_nstime); } } /* * Penalize those who had earlier chances but failed * by multiplying round-trip times by BETA (>1). * Improve nstime for unused addresses by applying GAMMA. * The GAMMA factor makes unused entries slowly * improve, so they eventually get tried again. * GAMMA should be slightly less than 1. * Watch out for records that may have timed out * and are no longer the correct type. XXX */ for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) { u_long t; ns2 = qs->nsdata; if (!ns2 || ns2 == ns) continue; if (ns2->d_type != T_A || ns2->d_class != qs->ns->d_class) /* XXX */ continue; if (qs->stime.tv_sec) { if (ns2->d_nstime == 0) t = (rtrip * BETA); else t = ns2->d_nstime * BETA + (1 - ALPHA) * rtrip; } else t = ns2->d_nstime * GAMMA; if (t > 65535) t = 65535; ns2->d_nstime = (u_int16_t)t; if (ns_wouldlog(ns_log_default,2)) { ns_debug(ns_log_default, 2, "NS #%d %s rtt now %d", n, sin_ntoa(qs->ns_addr), ns2->d_nstime); } } } #ifdef BIND_NOTIFY /* * For now, NOTIFY isn't defined for ANCOUNT!=0, AUCOUNT!=0, * or ADCOUNT!=0. Therefore the only real work to be done for * a NOTIFY-QR is to remove it from the query queue. */ if (hp->opcode == NS_NOTIFY_OP) { ns_info(ns_log_notify, "Received NOTIFY answer from %s for \"%s %s %s\"", inet_ntoa(from.sin_addr), *(qp->q_name) ? qp->q_name : ".", p_class(qp->q_class), p_type(qp->q_type)); qremove(qp); return; } #endif if ((qp->q_flags & Q_ZSERIAL) != 0) { if (hp->aa && ancount > 0 && hp->rcode == NOERROR && qtype == T_SOA && (qclass == C_IN || qclass == C_HS)) { int n; u_int type, class, dlen; u_int32_t serial; u_char *tp = cp; u_char *rdatap; n = dn_expand(msg, eom, tp, name, sizeof name); if (n < 0) { formerrmsg = expandFailedAnswer; goto formerr; } tp += n; /* name */ if (tp + 3 * INT16SZ + INT32SZ > eom) { formerrmsg = outofDataAnswer; goto formerr; } GETSHORT(type, tp); /* type */ GETSHORT(class, tp); /* class */ tp += INT32SZ; /* ttl */ GETSHORT(dlen, tp); /* dlen */ rdatap = tp; /* start of rdata */ if (!ns_nameok(qp, name, class, NULL, response_trans, ns_ownercontext(type, response_trans), name, from.sin_addr)) { formerrmsg = badNameFound; goto refused; } if (ns_samename(qname, name) != 1 || qtype != type || qclass != class) { sprintf(msgbuf, "qserial answer mismatch (%s %s %s)", name, p_class(class), p_type(type)); formerrmsg = msgbuf; goto formerr; } if (0 >= (n = dn_skipname(tp, eom))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* mname */ if (0 >= (n = dn_skipname(tp, eom))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* rname */ if (tp + 5 * INT32SZ > eom) { formerrmsg = dlenUnderrunAnswer; goto formerr; } GETLONG(serial, tp); tp += 4 * INT32SZ; /* Skip rest of SOA. */ if ((u_int)(tp - rdatap) != dlen) { formerrmsg = dlenOverrunAnswer; goto formerr; } for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) if (ina_equal(qs->ns_addr.sin_addr, from.sin_addr)) break; if (n == qp->q_naddr) { qserial_answer(qp); qremove(qp); return; } qs->serial = serial; } retry(qp); return; } /* * Non-authoritative, no answer, no error, with referral. */ - if (hp->rcode == NOERROR && !hp->aa && ancount == 0 && aucount > 0 + if (hp->rcode == NOERROR && !hp->tc && !hp->aa && + ancount == 0 && aucount > 0 #ifdef BIND_NOTIFY && hp->opcode != NS_NOTIFY_OP #endif ) { u_char *tp; - int type, class; + int type, class, dlen; + int foundns, foundsoa; #ifdef DEBUG if (debug > 0) res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); #endif /* * Since there is no answer section (ancount == 0), * we must be pointing at the authority section (aucount > 0). */ tp = cp; - n = dn_expand(msg, eom, tp, name, sizeof name); - if (n < 0) { - formerrmsg = expandFailedAuth; - goto formerr; + foundns = foundsoa = 0; + for (i = 0 ; i < aucount ; i++) { + n = dn_expand(msg, eom, tp, name, sizeof name); + if (n < 0) { + formerrmsg = expandFailedAuth; + goto formerr; + } + tp += n; + if (tp + 3 * INT16SZ + INT32SZ > eom) { + formerrmsg = outofDataAuth; + goto formerr; + } + GETSHORT(type, tp); + GETSHORT(class, tp); + tp += INT32SZ; /* ttl */ + GETSHORT(dlen, tp); + if (!ns_nameok(qp, name, class, NULL, response_trans, + ns_ownercontext(type, response_trans), + name, from.sin_addr)) { + formerrmsg = badNameFound; + goto refused; + } + /* skip rest of record */ + if (tp + dlen > eom) { + formerrmsg = outofDataAuth; + goto formerr; + } + tp += dlen; + if (type == T_NS) { + strcpy(aname, name); + foundns = 1; + } + if (type == T_SOA) + foundsoa = 1; } - tp += n; - if (tp + 2 * INT16SZ > eom) { - formerrmsg = outofDataAuth; - goto formerr; - } - GETSHORT(type, tp); - GETSHORT(class, tp); - if (!ns_nameok(qp, name, class, NULL, response_trans, - ns_ownercontext(type, response_trans), - name, from.sin_addr)) { - formerrmsg = badNameFound; - goto refused; - } /* * If the answer delegates us either to the same level in * the hierarchy or closer to the root, we consider this * server lame. Note that for now we only log the message * if the T_NS was C_IN, which is technically wrong (NS is * visible in all classes) but necessary anyway (non-IN * classes tend to not have good strong delegation graphs). */ - if (type == T_NS && ns_samedomain(qp->q_domain, name)) { - nameserIncr(from.sin_addr, nssRcvdLDel); - mark_lame(qp, from); + if (foundns && !foundsoa && + ns_samedomain(qp->q_domain, aname)) { + if (fwd == NULL) { + nameserIncr(from.sin_addr, nssRcvdLDel); + mark_lame(qp, from); + } mark_bad(qp, from); - if (class == C_IN && + if (class == C_IN && fwd == NULL && !haveComplained(ina_ulong(from.sin_addr), nhash(qp->q_domain))) { char *learnt_from = learntFrom(qp, &from); ns_info(ns_log_lame_servers, "Lame server on '%s' (in '%s'?): %s%s", qname, qp->q_domain, sin_ntoa(from), (learnt_from == NULL) ? "" : learnt_from); if (learnt_from != NULL) freestr(learnt_from); + } else if (fwd != NULL) { + if (!haveComplained(ina_ulong(from.sin_addr), + (u_long)nonRecursiveForwarder)) + ns_warning(ns_log_default, "%s: %s", + nonRecursiveForwarder, + sin_ntoa(from)); } fast_retry(qp, from); return; } } /* * Add the info received in the response to the data base. */ arfirst = ancount + aucount; c = arfirst + arcount; /* Don't return if it's a TSIG signed truncated message */ if (has_tsig > 0 && hp->tc) goto tcp_retry; /* -ve $ing non-existence of record, must handle non-authoritative * NOERRORs with c == 0. */ - if (!hp->aa && hp->rcode == NOERROR && c == 0) + if (!hp->aa && !hp->tc && hp->rcode == NOERROR && c == 0) goto return_msg; if (qp->q_flags & Q_SYSTEM) dbflags = DB_NOTAUTH | DB_NODATA; else dbflags = DB_NOTAUTH | DB_NODATA | DB_NOHINTS; count = c; if (qp->q_flags & Q_PRIMING) dbflags |= DB_PRIMING; if (hp->tc) { count -= arcount; /* truncation had to affect this */ if (!arcount) { count -= aucount; /* guess it got this too */ } if (!(arcount || aucount)) { count -= ancount; /* things are pretty grim */ } tcp_retry: /* retry using tcp provided this was not a tcp query */ if (!(qp->q_flags & Q_USEVC)) { qp->q_flags |= Q_USEVC; unsched(qp); schedretry(qp, 60); nsa = Q_NEXTADDR(qp, 0); key = tsig_key_from_addr(nsa->sin_addr); if (key != NULL) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; has_tsig = 1; qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (tcp_send(qp) != NOERROR) /* * We're probably in trouble if tcp_send * failed, but we'll try to press on because * there isn't anything else to do. */ retry(qp); if (has_tsig == 1) { memput(qp->q_msg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } return; } else if (!qsp) { /* outstanding udp response */ return; } /* XXX truncated tcp response */ ns_error(ns_log_default, "ns_resp: TCP truncated: \"%s\" %s %s from %s", qname, p_class(qclass), p_type(qtype), sin_ntoa(from)); /* mark this server as bad */ mark_bad(qp, from); /* try another server, it may have a bigger write buffer */ retry(qp); return; } tp = cp; restart = 0; validanswer = 0; nscount = 0; soacount = 0; cname = 0; lastwascname = 0; externalcname = 0; strcpy(aname, qname); if (count) { /* allocate 1 extra record for end of set detection */ flushset_size = (count + 1) * sizeof *flushset; flushset = memget(flushset_size); if (flushset == NULL) panic("flushset: out of memory", NULL); memset(flushset, 0, flushset_size); } else flushset = NULL; for (i = 0; i < count; i++) { struct databuf *dp; int type; freestr_maybe(&tname); if (cp >= eom) { free_related_additional(); if (flushset != NULL) free_flushset(flushset, flushset_size); formerrmsg = outofDataFinal; goto formerr; } n = rrextract(msg, msglen, cp, &dp, name, sizeof name, from, &tname); if (n < 0) { free_related_additional(); freestr_maybe(&tname); if (flushset != NULL) free_flushset(flushset, flushset_size); formerrmsg = outofDataFinal; if (hp->rcode == REFUSED) goto refused; else goto formerr; } cp += n; if (!dp) continue; type = dp->d_type; if (i < ancount) { /* Answer section. */ if (externalcname || ns_samename(name, aname) != 1) { if (!externalcname) ns_info(ns_log_resp_checks, "wrong ans. name (%s != %s)", name[0] ? name : ".", aname[0] ? aname : "."); else ns_debug(ns_log_resp_checks, 3, "ignoring answer '%s' after external cname", name); db_freedata(dp); continue; } if (type == T_CNAME && qtype != T_CNAME && qtype != T_ANY) { strcpy(aname, (char *)dp->d_data); if (!ns_samedomain(aname, qp->q_domain)) externalcname = 1; cname++; lastwascname = 1; } else { validanswer = 1; lastwascname = 0; } if (tname != NULL) { add_related_additional(tname); tname = NULL; } dp->d_cred = (hp->aa && ns_samename(name, qname) == 1) ? DB_C_AUTH : DB_C_ANSWER; } else { /* After answer section. */ if (lastwascname) { ns_debug(ns_log_resp_checks, 3, "last was cname, ignoring auth. and add."); db_freedata(dp); break; } if (i < arfirst) { /* Authority section. */ switch (type) { case T_NS: case T_SOA: if (!ns_samedomain(aname, name)) { ns_info(ns_log_resp_checks, - "bad referral (%s !< %s)", + "bad referral (%s !< %s) from %s", aname[0] ? aname : ".", - name[0] ? name : "."); + name[0] ? name : ".", + sin_ntoa(from)); db_freedata(dp); continue; - } else if (!ns_samedomain(name, + } else if (fwd == NULL && + !ns_samedomain(name, qp->q_domain)) { if (!externalcname) ns_info(ns_log_resp_checks, - "bad referral (%s !< %s)", + "bad referral (%s !< %s) from %s", name[0] ? name : ".", qp->q_domain[0] ? - qp->q_domain : "."); + qp->q_domain : ".", + sin_ntoa(from)); db_freedata(dp); continue; } if (type == T_NS) { nscount++; add_related_additional(tname); tname = NULL; } if (type == T_SOA) { soacount++; } break; case T_NXT: /* XXX check */ break; case T_SIG: /* XXX check that it relates to an NS or SOA or NXT */ break; default: ns_info(ns_log_resp_checks, "invalid RR type '%s' in authority section (name = '%s') from %s", p_type(type), name, sin_ntoa(from)); db_freedata(dp); continue; } dp->d_cred = (hp->aa && (cname == 0)) ? DB_C_AUTH : (qp->q_flags & Q_PRIMING) ? DB_C_ANSWER : DB_C_ADDITIONAL; } else { /* Additional section. */ switch (type) { case T_A: case T_AAAA: if (externalcname || !ns_samedomain(name, qp->q_domain)) { ns_debug(ns_log_resp_checks, 3, "ignoring additional info '%s' type %s", name, p_type(type)); db_freedata(dp); continue; } if (!related_additional(name)) { ns_info(ns_log_resp_checks, "unrelated additional info '%s' type %s from %s", name, p_type(type), sin_ntoa(from)); db_freedata(dp); continue; } break; case T_KEY: /* XXX check? */ break; case T_SIG: /* * XXX a SIG RR should relate * to some other RR in this section, * although if it's the last RR * it might be a transaction signature. */ break; default: ns_info(ns_log_resp_checks, "invalid RR type '%s' in additional section (name = '%s') from %s", p_type(type), name, sin_ntoa(from)); db_freedata(dp); continue; } dp->d_cred = (qp->q_flags & Q_PRIMING) ? DB_C_ANSWER : DB_C_ADDITIONAL; } } rrsetadd(flushset, name, dp); } free_related_additional(); freestr_maybe(&tname); if (flushset != NULL) { if ((qp->q_flags & Q_SYSTEM) && (qp->q_flags & Q_PRIMING)) { check_hints(flushset); /* before rrsetupdate */ rrsetupdate(flushset, dbflags, from, 1); } else rrsetupdate(flushset, dbflags, from, 0); free_flushset(flushset, flushset_size); } if (lastwascname && !externalcname) ns_debug(ns_log_cname, 3, "%s (%s) q(%s %s %s) %s qd(%s)", danglingCname, aname, (qname && *qname) ? qname : ".", p_class(qclass), p_type(qtype), sin_ntoa(from), qp->q_domain); if (cp > eom) { formerrmsg = outofDataAFinal; goto formerr; } if ((qp->q_flags & Q_SYSTEM) && ancount) { if ((qp->q_flags & Q_PRIMING) && !check_root()) { /* mark server as bad */ mark_bad(qp, from); fast_retry(qp, from); return; } ns_debug(ns_log_default, 3, "resp: leaving, SYSQUERY ancount %d", ancount); #ifdef BIND_NOTIFY if (qp->q_notifyzone != DB_Z_CACHE) { struct zoneinfo *zp = &zones[qp->q_notifyzone]; qp->q_notifyzone = DB_Z_CACHE; ns_notify(zp->z_origin, zp->z_class, ns_t_soa); } #endif qremove(qp); return; } if (ancount && count && !validanswer) { /* * Everything passed validation but we didn't get the * final answer. The response must have contained * a dangling CNAME. Force a restart of the query. * * Don't set restart if count==0, since this means * the response was truncated in the answer section, * causing us to set count to 0 which will cause * validanswer to be 0 as well even though the answer * section probably contained valid RRs (just not * a complete set). * XXX - this works right if we can just forward this * response to the client, but not if we found a CNAME * in a prior response and restarted the query. */ restart = 1; } if (!restart && !qp->q_cmsglen && ancount > 1 && qtype == T_A) sort_response(tp, eom, ancount, &qp->q_from); /* * An answer to a T_ANY query or a successful answer to a * regular query with no indirection, then just return answer. */ if (!restart && ancount && (qtype == T_ANY || !qp->q_cmsglen)) { ns_debug(ns_log_default, 3, "resp: got as much answer as there is"); goto return_msg; } /* * We might want to cache this negative answer. * * if ancount != 0 and rcode == NOERROR we cannot determine if the * CNAME chain has been processed to completion or not, so just * restart the query. DNS needs a NODATA return code! * * As some servers incorrectly return a NODATA indication when * there is a CNAME chain instead of NXDOMAIN, we requery to get * a definitive answer. */ if ((hp->rcode == NXDOMAIN && cname == ancount) || (hp->rcode == NOERROR && ancount == 0 && (nscount == 0 || soacount != 0) ) ) { cache_n_resp(msg, msglen, from, qp->q_name, qp->q_class, qp->q_type); if (!qp->q_cmsglen) { ns_debug(ns_log_default, 3, "resp: leaving NO: auth = %d", hp->aa); goto return_msg; } forcecmsg = 1; } /* * All messages in here need further processing. i.e. they * are either CNAMEs or we got referred again. */ count = 0; founddata = 0; dname = name; /* - * If restart==0 and ancount > 0, we should - * have some valid data because because the data in the answer - * section is owned by the query name and that passes the - * validation test by definition - * * XXX - the restart stuff doesn't work if any of the answer RRs * is not cacheable (TTL==0 or unknown RR type), since all of the * answer must pass through the cache and be re-assembled. */ - if ((forcecmsg && qp->q_cmsglen) || - ((!restart || !cname) && qp->q_cmsglen && ancount)) { + if (qp->q_cmsglen != 0) { ns_debug(ns_log_default, 1, "Cname second pass"); newmsglen = MIN(PACKETSZ, qp->q_cmsglen); memcpy(newmsg, qp->q_cmsg, newmsglen); } else { newmsglen = MIN(PACKETSZ, msglen); memcpy(newmsg, msg, newmsglen); } hp = (HEADER *) newmsg; hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = NOERROR; dnptrs[0] = newmsg; dnptrs[1] = NULL; cp = newmsg + HFIXEDSZ; /* * Keep in mind that none of this code works when QDCOUNT>1. * cp ends up pointed just past the query section in both cases. */ /* * Arrange for dname to contain the query name. The query * name can be either the original query name if restart==0 * or the target of the last CNAME if we are following a * CNAME chain and were referred. */ n = dn_expand(newmsg, newmsg + newmsglen, cp, dname, sizeof name); if (n < 0) { ns_debug(ns_log_default, 1, "dn_expand failed"); goto servfail; } if (!res_dnok(dname)) { ns_debug(ns_log_default, 1, "bad name (%s)", dname); goto servfail; } cp += n + QFIXEDSZ; buflen = sizeof(newmsg) - (cp - newmsg); cname = 0; try_again: ns_debug(ns_log_default, 1, "resp: nlookup(%s) qtype=%d", dname, qtype); foundname = 0; fname = ""; htp = hashtab; /* lookup relative to root */ np = nlookup(dname, &htp, &fname, 0); ns_debug(ns_log_default, 1, "resp: %s '%s' as '%s' (cname=%d)", np == NULL ? "missed" : "found", dname, fname, cname); if (np == NULL || fname != dname) goto fetch_ns; foundname++; answers = cp; count = cp - newmsg; /* * Look for NXDOMAIN record. */ for (dp = np->n_data; dp; dp = dp->d_next) { if (!stale(dp) && (dp->d_rcode == NXDOMAIN) && (dp->d_class == (int)qclass)) { #ifdef RETURNSOA n = finddata(np, qclass, T_SOA, hp, &dname, &buflen, &count); if ( n != 0) { if (count) { cp += n; buflen -= n; newmsglen += n; hp->nscount = htons((u_int16_t)count); } if (hp->rcode == NOERROR_NODATA) { hp->rcode = NOERROR; goto return_newmsg; } } #else count = 0; #endif hp->rcode = NXDOMAIN; /* * XXX forcing AA all the time isn't right, but * we have to work that way by default * for compatibility with older servers. */ if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) hp->aa = 1; ns_debug(ns_log_default, 3, "resp: NXDOMAIN aa = %d", hp->aa); if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) goto return_newmsg; founddata = 1; goto fetch_ns; } } n = finddata(np, qclass, qtype, hp, &dname, &buflen, &count); if (n == 0) goto fetch_ns; /* NO data available */ if (hp->rcode) { if (hp->rcode == NOERROR_NODATA) hp->rcode = NOERROR; #ifdef RETURNSOA if (count) { cp += n; buflen -= n; hp->nscount = htons((u_int16_t)count); } #endif if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) goto return_newmsg; founddata = 1; goto fetch_ns; } cp += n; buflen -= n; hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); if (fname != dname && qtype != T_CNAME && qtype != T_ANY) { cname++; goto try_again; } founddata = 1; ns_debug(ns_log_default, 3, "resp: foundname=%d, count=%d, founddata=%d, cname=%d", foundname, count, founddata, cname); if (count > 1 && qtype == T_A) sort_response(answers, cp, count, &qp->q_from); fetch_ns: if (hp->tc) goto return_newmsg; /* * Look for name servers to refer to and fill in the authority * section or record the address for forwarding the query * (recursion desired). */ free_nsp(nsp); switch (findns(&np, qclass, nsp, &count, 0)) { case NXDOMAIN: /* shouldn't happen */ ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", dname, hp->rcode); if (!foundname) hp->rcode = NXDOMAIN; if (qclass != C_ANY) { hp->aa = 1; if (np && (!foundname || !founddata)) { n = doaddauth(hp, cp, buflen, np, nsp[0]); cp += n; buflen -= n; } } goto return_newmsg; case SERVFAIL: goto servfail; } if (founddata) { hp = (HEADER *)newmsg; n = add_data(np, nsp, cp, buflen, &count); if (n < 0) { hp->tc = 1; n = (-n); } cp += n; buflen -= n; hp->nscount = htons((u_int16_t)count + ntohs(hp->nscount)); goto return_newmsg; } /* * If we get here, we don't have the answer yet and are about * to iterate to try and get it. First, infinite loop avoidance. */ if (qp->q_nqueries++ > MAXQUERIES) { ns_debug(ns_log_default, 1, "resp: MAXQUERIES exceeded (%s %s %s)", dname, p_class(qclass), p_type(qtype)); ns_info(ns_log_default, "MAXQUERIES exceeded, possible data loop in resolving (%s)", dname); goto servfail; } /* Reset the query control structure */ ns_freeqns(qp, "ns_resp"); qp->q_naddr = 0; qp->q_curaddr = 0; nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); if (qp->q_domain != NULL) freestr(qp->q_domain); getname(np, tmpdomain, sizeof tmpdomain); qp->q_domain = savestr(tmpdomain, 1); if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) n = 0; else if ((n = nslookup(nsp, qp, dname, "ns_resp")) <= 0) { if (n < 0) { if (n == -1) ns_debug(ns_log_default, 3, "resp: nslookup reports danger"); if (cname) /* a remote CNAME that does not have data */ goto return_newmsg; goto servfail; } else { ns_debug(ns_log_default, 3, "resp: no addrs found for NS's"); /* * Timeout while sysquery looks up the NS addresses. * * Hopefully we'll have them when the client asks * again. * * too bad we can't just wait for the sysquery * response to restart this query (it's too hard). * * We could try to crawl back up the tree looking * for reachable servers, but we may have just * gotten delegated down here by a response with * no A RRs for the servers. If we blindly tried * this strategy, we bang on the same server forever. */ goto timeout; } } for (n = 0; (u_int)n < qp->q_naddr; n++) qp->q_addr[n].stime.tv_sec = 0; qp->q_addr[0].stime = tt; if (cname) { if (qp->q_cname++ == MAXCNAMES) { ns_debug(ns_log_default, 3, "resp: leaving, MAXCNAMES exceeded"); goto servfail; } ns_debug(ns_log_default, 1, "q_cname = %d", qp->q_cname); ns_debug(ns_log_default, 3, "resp: building recursive query; nslookup"); if (qp->q_cmsg == NULL) { qp->q_cmsg = qp->q_msg; qp->q_cmsglen = qp->q_msglen; qp->q_cmsgsize = qp->q_msgsize; } else if (qp->q_msg != NULL) memput(qp->q_msg, qp->q_msgsize); qp->q_msg = (u_char *)memget(PACKETSZ); if (qp->q_msg == NULL) { ns_notice(ns_log_default, "resp: memget error"); goto servfail; } qp->q_msgsize = PACKETSZ; n = res_nmkquery(&res, QUERY, dname, qclass, qtype, NULL, 0, NULL, qp->q_msg, PACKETSZ); if (n < 0) { ns_info(ns_log_default, "resp: res_mkquery(%s) failed", dname); goto servfail; } if (qp->q_name != NULL) freestr(qp->q_name); qp->q_name = savestr(dname, 1); qp->q_msglen = n; hp = (HEADER *) qp->q_msg; hp->rd = 0; } else hp = (HEADER *) qp->q_msg; hp->id = qp->q_nsid = htons(nsid_next()); - if (qp->q_addr[0].forwarder) - hp->rd = 1; + hp->rd = (qp->q_addr[0].forwarder ? 1 : 0); unsched(qp); schedretry(qp, retrytime(qp)); nsa = Q_NEXTADDR(qp, 0); if (ns_wouldlog(ns_log_default,1)) { ns_debug(ns_log_default, 1, "resp: forw -> %s ds=%d nsid=%d id=%d %dms", sin_ntoa(*nsa), ds, ntohs(qp->q_nsid), ntohs(qp->q_id), (qp->q_addr[0].nsdata != NULL) ? qp->q_addr[0].nsdata->d_nstime : -1); } #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, qp->q_msglen, log_get_stream(packet_channel)); #endif key = tsig_key_from_addr(nsa->sin_addr); if (key != NULL) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; has_tsig = 1; qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (qp->q_flags & Q_USEVC) { if (tcp_send(qp) != NOERROR) { if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)tcpsendStr)) ns_info(ns_log_default, "ns_forw: tcp_send(%s) failed: %s", sin_ntoa(*nsa), strerror(errno)); } } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "ns_resp: sendto(%s): %s", sin_ntoa(*nsa), strerror(errno)); nameserIncr(nsa->sin_addr, nssSendtoErr); } if (has_tsig == 1) { memput(qp->q_msg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } hp->rd = 0; /* leave set to 0 for dup detection */ nameserIncr(nsa->sin_addr, nssSentFwdR); nameserIncr(qp->q_from.sin_addr, nssRcvdFwdR); ns_debug(ns_log_default, 3, "resp: Query sent."); free_nsp(nsp); switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: unsched(qp); schedretry(qp, (time_t) 0); } return; formerr: if (!haveComplained(ina_ulong(from.sin_addr), (u_long)formerrmsg)) ns_info(ns_log_resp_checks, "Malformed response from %s (%s)", sin_ntoa(from), formerrmsg); fast_retry(qp, from); free_nsp(nsp); return; return_msg: nameserIncr(from.sin_addr, nssRcvdFwdR); nameserIncr(qp->q_from.sin_addr, nssSentFwdR); + nameserIncr(qp->q_from.sin_addr, nssSentAns); + if (!hp->aa) + nameserIncr(qp->q_from.sin_addr, nssSentNaAns); + if (hp->rcode == NXDOMAIN) + nameserIncr(qp->q_from.sin_addr, nssSentNXD); /* The "standard" return code */ hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg(msg, msglen, qp); qremove(qp); free_nsp(nsp); return; return_newmsg: nameserIncr(qp->q_from.sin_addr, nssSentAns); if (!hp->aa) nameserIncr(qp->q_from.sin_addr, nssSentNaAns); if (hp->rcode == NXDOMAIN) nameserIncr(qp->q_from.sin_addr, nssSentNXD); n = doaddinfo(hp, cp, buflen); cp += n; buflen -= n; hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg(newmsg, cp - newmsg, qp); qremove(qp); free_nsp(nsp); return; refused: hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); hp->rcode = REFUSED; hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg((u_char *)hp, (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), qp); qremove(qp); free_nsp(nsp); return; servfail: nameserIncr(qp->q_from.sin_addr, nssSentFail); hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); hp->rcode = SERVFAIL; hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg((u_char *)hp, (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), qp); qremove(qp); free_nsp(nsp); return; timeout: if (qp->q_stream) sq_remove(qp->q_stream); qremove(qp); free_nsp(nsp); return; } #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ hp->rcode = FORMERR; \ return (-1); \ } \ } while (0) static int rrextract(u_char *msg, int msglen, u_char *rrp, struct databuf **dpp, char *dname, int namelen, struct sockaddr_in from, char **tnamep) { u_char *cp, *eom, *rdatap; u_int class, type, dlen; int n, n1, n2; u_int32_t ttl; u_char *cp1, data[MAXDATA*2]; HEADER *hp = (HEADER *)msg; enum context context; if (tnamep != NULL) *tnamep = NULL; *dpp = NULL; cp = rrp; eom = msg + msglen; if ((n = dn_expand(msg, eom, cp, dname, namelen)) < 0) { hp->rcode = FORMERR; return (-1); } cp += n; BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); GETSHORT(type, cp); GETSHORT(class, cp); if (class > CLASS_MAX) { ns_debug(ns_log_default, 3, "bad class in rrextract"); hp->rcode = FORMERR; return (-1); } GETLONG(ttl, cp); if (ttl > MAXIMUM_TTL) { ns_debug(ns_log_default, 5, "%s: converted TTL > %u to 0", dname, MAXIMUM_TTL); ttl = 0; } GETSHORT(dlen, cp); BOUNDS_CHECK(cp, dlen); rdatap = cp; if (!ns_nameok(NULL, dname, class, NULL, response_trans, ns_ownercontext(type, response_trans), dname, from.sin_addr)) { hp->rcode = REFUSED; return (-1); } ns_debug(ns_log_default, 3, "rrextract: dname %s type %d class %d ttl %d", dname, type, class, ttl); /* * Convert the resource record data into the internal * database format. * * On entry to the switch: * CP points to the RDATA section of the wire-format RR. * DLEN is its length. * The memory area at DATA is available for processing. * * On exit from the switch: * CP has been incremented past the RR. * CP1 points to the RDATA section of the database-format RR. * N contains the length of the RDATA section of the dbase-format RR. * * The new data at CP1 for length N will be copied into the database, * so it need not be in any particular storage location. */ switch (type) { case T_A: if (dlen != INT32SZ) { hp->rcode = FORMERR; return (-1); } /*FALLTHROUGH*/ case T_WKS: case T_HINFO: case T_TXT: case T_X25: case T_ISDN: case T_NSAP: case T_AAAA: case T_LOC: case T_KEY: case ns_t_cert: cp1 = cp; n = dlen; cp += n; break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, type == T_PTR ?ns_ptrcontext(dname) :domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 = data; n = strlen((char *)data) + 1; if (tnamep != NULL && (type == T_NS || type == T_MB)) *tnamep = savestr((char *)cp1, 1); break; case T_SOA: context = hostname_ctx; goto soa_rp_minfo; case T_RP: case T_MINFO: context = mailname_ctx; /* FALLTHROUGH */ soa_rp_minfo: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, context, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; /* * The next use of 'cp' is dn_expand(), so we don't have * to BOUNDS_CHECK() here. */ cp1 = data + (n = strlen((char *)data) + 1); n1 = sizeof(data) - n; if (type == T_SOA) n1 -= 5 * INT32SZ; n = dn_expand(msg, eom, cp, (char *)cp1, n1); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (type == T_RP) context = domain_ctx; else context = mailname_ctx; if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, context, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char *)cp1) + 1; if (type == T_SOA) { n = 5 * INT32SZ; BOUNDS_CHECK(cp, n); memcpy(cp1, cp, n); cp += n; cp1 += n; } n = cp1 - data; cp1 = data; break; case T_NAPTR: /* Grab weight and port. */ BOUNDS_CHECK(cp, INT16SZ*2); memcpy(data, cp, INT16SZ*2); cp1 = data + INT16SZ*2; cp += INT16SZ*2; /* Flags */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Service */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Regexp */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Replacement */ n = dn_expand(msg, eom, cp, (char *)cp1, sizeof data - (cp1 - data)); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, hostname_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; /* compute end of data */ cp1 += strlen((char *)cp1) + 1; /* compute size of data */ n = cp1 - data; cp1 = data; break; case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* grab preference */ BOUNDS_CHECK(cp, INT16SZ); memcpy(data, cp, INT16SZ); cp1 = data + INT16SZ; cp += INT16SZ; if (type == T_SRV) { /* Grab weight and port. */ BOUNDS_CHECK(cp, INT16SZ*2); memcpy(cp1, cp, INT16SZ*2); cp1 += INT16SZ*2; cp += INT16SZ*2; } /* get name */ n = dn_expand(msg, eom, cp, (char *)cp1, sizeof data - (cp1 - data)); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, hostname_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; if (tnamep != NULL) *tnamep = savestr((char *)cp1, 1); /* compute end of data */ cp1 += strlen((char *)cp1) + 1; /* compute size of data */ n = cp1 - data; cp1 = data; break; case T_PX: /* grab preference */ BOUNDS_CHECK(cp, INT16SZ); memcpy(data, cp, INT16SZ); cp1 = data + INT16SZ; cp += INT16SZ; /* get MAP822 name */ n = dn_expand(msg, eom, cp, (char *)cp1, sizeof data - INT16SZ); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; /* * The next use of 'cp' is dn_expand(), so we don't have * to BOUNDS_CHECK() here. */ cp1 += (n = strlen((char *)cp1) + 1); n1 = sizeof(data) - n; n = dn_expand(msg, eom, cp, (char *)cp1, n1); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char *)cp1) + 1; n = cp1 - data; cp1 = data; break; case T_SIG: { u_long origTTL, exptime, signtime, timetilexp, now; u_int8_t alg; /* Check signature time, expiration, and adjust TTL. */ /* This code is similar to that in db_load.c. */ /* Skip coveredType, save alg, skip labels */ BOUNDS_CHECK(cp, INT16SZ + 1 + 1 + 3*INT32SZ); cp1 = cp + INT16SZ; alg = *cp1++; cp1++; GETLONG(origTTL, cp1); GETLONG(exptime, cp1); GETLONG(signtime, cp1); now = time(NULL); /* Get current time in GMT/UTC */ /* Don't let bogus name servers increase the signed TTL */ if (ttl > origTTL) { ns_debug(ns_log_default, 3, "shrinking SIG TTL from %d to origTTL %d", ttl, origTTL); ttl = origTTL; } /* Don't let bogus signers "sign" in the future. */ if (signtime > now) { ns_debug(ns_log_default, 3, "ignoring SIG: signature date %s is in the future", p_secstodate (signtime)); return ((cp - rrp) + dlen); } /* Ignore received SIG RR's that are already expired. */ if (exptime <= now) { ns_debug(ns_log_default, 3, "ignoring SIG: expiration %s is in the past", p_secstodate (exptime)); return ((cp - rrp) + dlen); } /* Lop off the TTL at the expiration time. */ timetilexp = exptime - now; if (timetilexp < ttl) { ns_debug(ns_log_default, 3, "shrinking expiring %s SIG TTL from %d to %d", p_secstodate (exptime), ttl, timetilexp); ttl = timetilexp; } /* The following code is copied from named-xfer.c. */ cp1 = (u_char *)data; /* first just copy over the type_covered, algorithm, */ /* labels, orig ttl, two timestamps, and the footprint */ BOUNDS_CHECK(cp, 18); memcpy(cp1, cp, 18); cp += 18; cp1 += 18; /* then the signer's name */ n = dn_expand(msg, eom, cp, (char *)cp1, (sizeof data) - 18); if (n < 0 || n + NS_SIG_SIGNER > dlen) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char*)cp1)+1; /* finally, we copy over the variable-length signature. Its size is the total data length, minus what we copied. */ n = dlen - (NS_SIG_SIGNER + n); if (n > (sizeof data) - (cp1 - (u_char *)data)) { hp->rcode = FORMERR; return (-1); /* out of room! */ } switch (alg) { case NS_ALG_MD5RSA: if (n < NS_MD5RSA_MIN_SIZE || n > NS_MD5RSA_MAX_SIZE) hp->rcode = FORMERR; break; case NS_ALG_DSA: if (n != NS_DSA_SIG_SIZE) hp->rcode = FORMERR; break; default: break; } if (hp->rcode == FORMERR) return (-1); memcpy(cp1, cp, n); cp += n; cp1 += n; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; } case T_NXT: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); /* * By testing if n >= dlen, we are requiring that the type * bitmap be at least one octet. This is reasonable * because we always have to look at the 0 bit to see if * this is a "different format" NXT or not. */ if (n < 0 || n >= dlen) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; n1 = strlen((char *)data) + 1; cp1 = data + n1; /* * We don't need to BOUNDS_CHECK() cp here because we've * previously checked that 'dlen' bytes are in bounds, and * we know that n < dlen. */ n2 = dlen - n; /* * The first bit of the first octet determines the format * of the NXT record. A format for types >= 128 has not * yet been defined, so if bit zero is set, we just copy * what's there because we don't understand it. */ if ((*cp & 0x80) == 0) { /* * Bit zero is not set; this is an ordinary NXT * record. The bitmap must be at least 4 octets * because the NXT bit should be set. It should be * less than or equal to 16 octets because this NXT * format is only defined for types < 128. */ if (n2 < 4 || n2 > 16) { hp->rcode = FORMERR; return (-1); } } if (n2 > sizeof data - n1) { hp->rcode = FORMERR; return (-1); } memcpy(cp1, cp, n2); cp += n2; + cp1 += n2; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; default: ns_debug(ns_log_default, 3, "unknown type %d", type); return ((cp - rrp) + dlen); } if (cp > eom) { hp->rcode = FORMERR; return (-1); } if ((u_int)(cp - rdatap) != dlen) { ns_debug(ns_log_default, 3, "encoded rdata length is %u, but actual length was %u", dlen, (u_int)(cp - rdatap)); hp->rcode = FORMERR; return (-1); } if (n > MAXDATA) { ns_debug(ns_log_default, 1, "update type %d: %d bytes is too much data", type, n); hp->rcode = FORMERR; return (-1); } ttl += tt.tv_sec; *dpp = savedata(class, type, ttl, cp1, n); return (cp - rrp); } int send_msg(u_char *msg, int msglen, struct qinfo *qp) { HEADER *hp = (HEADER *) msg; u_char *oldmsg; int oldlen; int msgsize; int ret; if (qp->q_flags & Q_SYSTEM) return (1); if (!qp->q_stream && (msglen > PACKETSZ)) msglen = trunc_adjust(msg, msglen, PACKETSZ); if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "send_msg -> %s (%s %d) id=%d", sin_ntoa(qp->q_from), qp->q_stream == NULL ? "UDP" : "TCP", qp->q_stream == NULL ? qp->q_dfd : qp->q_stream->s_rfd, ntohs(qp->q_id)); } #ifdef DEBUG if (ns_wouldlog(ns_log_default, 4)) { struct qinfo *tqp; for (tqp = nsqhead; tqp != NULL; tqp = tqp->q_link) { ns_debug(ns_log_default, 4, "qp %#lx q_id: %d q_nsid: %d q_msglen: %d", (u_long)tqp, tqp->q_id, tqp->q_nsid, tqp->q_msglen); ns_debug(ns_log_default, 4, "\tq_naddr: %d q_curaddr: %d", tqp->q_naddr, tqp->q_curaddr); ns_debug(ns_log_default, 4, "\tq_next: %#lx q_link: %#lx", (u_long)qp->q_next, (u_long)qp->q_link); } } if (debug >= 6) res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); #endif /* DEBUG */ if (qp->q_tsig != NULL) { u_char sig[TSIG_SIG_SIZE]; int siglen = sizeof(sig); oldmsg = msg; oldlen = msglen; msgsize = msglen + TSIG_BUF_SIZE; msg = memget(msgsize); memcpy(msg, oldmsg, oldlen); ret = ns_sign(msg, &msglen, msgsize, NOERROR, qp->q_tsig->key, qp->q_tsig->sig, qp->q_tsig->siglen, sig, &siglen, 0); if (ret != 0) { INSIST(0); } } if (qp->q_stream == NULL) { /* * Don't send FORMERR to these well known ports * (loop avoidance). */ switch (ntohs(qp->q_from.sin_port)) { case 7: /* echo */ case 13: /* daytime */ case 19: /* chargen */ case 37: /* time */ if (hp->rcode == FORMERR) return (-1); default: break; } if (sendto(qp->q_dfd, (char*)msg, msglen, 0, (struct sockaddr *)&qp->q_from, sizeof(qp->q_from)) < 0) { if (!haveComplained(ina_ulong(qp->q_from.sin_addr), (u_long)sendtoStr)) #if defined(SPURIOUS_ECONNREFUSED) if (errno != ECONNREFUSED) #endif ns_info(ns_log_default, "send_msg: sendto(%s): %s", sin_ntoa(qp->q_from), strerror(errno)); nameserIncr(qp->q_from.sin_addr, nssSendtoErr); return (1); } } else writestream(qp->q_stream, (u_char*)msg, msglen); if (qp->q_tsig != NULL) memput(msg, oldlen + TSIG_BUF_SIZE); return (0); } static int root_server_p(ns_class class) { struct zoneinfo *zp = find_zone("", class); return (zp != NULL && (zp->z_type == z_master || zp->z_type == z_slave)); } void prime_cache(void) { int root = root_server_p(ns_c_in); ns_debug(ns_log_default, 1, "prime_cache: priming = %d, root = %d", priming, root); if (!priming && !root) { struct qinfo *qp = sysquery("", ns_c_in, ns_t_ns, NULL, 0, ns_port, ns_o_query); if (qp != NULL) { qp->q_flags |= (Q_SYSTEM | Q_PRIMING); priming++; } } needs_prime_cache = 0; } struct qinfo * sysquery(const char *dname, int class, int type, struct in_addr *nss, int nsc, u_int16_t port, int opcode) { struct qinfo *qp, *oqp; HEADER *hp; char tmpdomain[MAXDNAME]; struct namebuf *np = NULL; struct databuf *nsp[NSMAX]; struct hashbuf *htp1; struct hashbuf *htp2; struct hashbuf *htp3; struct sockaddr_in *nsa; const char *fname; int n, count; int sendto_errno = 0; u_char *oldqbuf; int oldqlen, has_tsig; u_char *smsg; int smsglen, smsgsize, siglen; u_char sig[TSIG_SIG_SIZE]; DST_KEY *key; nsp[0] = NULL; ns_debug(ns_log_default, 3, "sysquery(%s, %d, %d, %#x, %d, %d)", dname, class, type, nss, nsc, ntohs(port)); - qp = qnew(dname, class, type); + qp = qnew(dname, class, type, (nss != NULL && nsc != 0) ? 0 : 1); if (nss != NULL && nsc != 0) np = NULL; else if (!NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) { htp1 = hashtab; htp2 = hashtab; htp3 = fcachetab; if (priming && dname[0] == '\0') { np = NULL; } else if (((np = nlookup(dname, &htp1, &fname, 0)) == NULL) && ((np = nlookup("", &htp2, &fname, 0)) == NULL) && ((np = nlookup("", &htp3, &fname, 0)) == NULL)) { ns_info(ns_log_default, "sysquery: nlookup error on %s?", dname); err1: ns_freeqry(qp); return (NULL); } n = findns(&np, class, nsp, &count, 0); switch (n) { case NXDOMAIN: case SERVFAIL: ns_info(ns_log_default, "sysquery: findns error (%s) on %s?", n == NXDOMAIN ? "NXDOMAIN" : "SERVFAIL", dname); err2: free_nsp(nsp); goto err1; } } /* Build new qinfo struct. */ qp->q_cmsg = qp->q_msg = NULL; qp->q_dfd = ds; if (nss == NULL || nsc == 0) nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; qp->q_flags |= Q_SYSTEM; getname(np, tmpdomain, sizeof tmpdomain); qp->q_domain = savestr(tmpdomain, 1); if ((qp->q_msg = (u_char *)memget(PACKETSZ)) == NULL) { ns_notice(ns_log_default, "sysquery: memget failed"); goto err2; } qp->q_msgsize = PACKETSZ; n = res_nmkquery(&res, opcode, dname, class, type, NULL, 0, NULL, qp->q_msg, PACKETSZ); if (n < 0) { ns_info(ns_log_default, "sysquery: res_mkquery(%s) failed", dname); goto err2; } qp->q_msglen = n; hp = (HEADER *) qp->q_msg; hp->id = qp->q_nsid = htons(nsid_next()); hp->rd = (qp->q_addr[qp->q_curaddr].forwarder ? 1 : 0); /* First check for an already pending query for this data. */ for (oqp = nsqhead; oqp != NULL; oqp = oqp->q_link) { if ((oqp != qp) && (oqp->q_msglen == qp->q_msglen) && memcmp(oqp->q_msg+2, qp->q_msg + 2, qp->q_msglen - 2) == 0 ) { #ifdef BIND_NOTIFY /* XXX - need fancier test to suppress duplicate * NOTIFYs to the same server (compare nss?) */ if (opcode != NS_NOTIFY_OP) #endif /*BIND_NOTIFY*/ { ns_debug(ns_log_default, 3, "sysquery: duplicate"); goto err2; } } } if (nss != NULL && nsc != 0) { int i; struct qserv *qs; for (i = 0, qs = qp->q_addr; i < nsc; i++, qs++) { qs->ns_addr.sin_family = AF_INET; qs->ns_addr.sin_addr = nss[i]; qs->ns_addr.sin_port = port; qs->ns = NULL; qs->nsdata = NULL; qs->stime = tt; qs->forwarder = 0; qs->nretry = 0; } qp->q_naddr = nsc; } else if (!NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) { fetch_a: count = nslookup(nsp, qp, dname, "sysquery"); if (count <= 0) { if (count < 0) { if (n == -1) ns_info(ns_log_default, "sysquery: nslookup reports danger (%s)", dname); goto err2; } else if (np && NAME(*np)[0] == '\0') { /* * It's not too serious if we don't have * the root server addresses if we have to * go through a forwarder anyway. Don't * bother to log it, since prime_cache() * won't do anything about it as currently * implemented. * * XXX - should we skip setting * needs_prime_cache as well? * * XXX - what happens when we implement * selective forwarding? */ if (!NS_OPTION_P(OPTION_FORWARD_ONLY)) ns_warning(ns_log_default, "sysquery: no addrs found for root NS (%s)", dname); if (class == C_IN && !priming) needs_prime_cache = 1; goto err2; } if (np) { free_nsp(nsp); nsp[0] = NULL; np = np_parent(np); n = findns(&np, class, nsp, &count, 0); switch (n) { case NXDOMAIN: /*FALLTHROUGH*/ case SERVFAIL: ns_info(ns_log_default, "sysquery: findns error (%d) on %s?", n, dname); goto err2; } getname(np, tmpdomain, sizeof tmpdomain); if (qp->q_domain != NULL) freestr(qp->q_domain); qp->q_domain = savestr(tmpdomain, 1); goto fetch_a; } goto err2; } } schedretry(qp, retrytime(qp)); qp->q_addr[0].stime = tt; /* XXX - why not every? */ nsa = Q_NEXTADDR(qp, 0); ns_debug(ns_log_default, 1, "sysquery: send -> %s dfd=%d nsid=%d id=%d retry=%ld", sin_ntoa(*nsa), qp->q_dfd, ntohs(qp->q_nsid), ntohs(qp->q_id), (long)qp->q_time); #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, qp->q_msglen, log_get_stream(packet_channel)); #endif key = tsig_key_from_addr(nsa->sin_addr); if (key != NULL) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; has_tsig = 1; qp->q_nstsig = new_tsig(key, sig, siglen); /* BEW? */ } else { INSIST(0); has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (sendto(qp->q_dfd, (char*)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "sysquery: sendto(%s): %s", sin_ntoa(*nsa), strerror(errno)); nameserIncr(nsa->sin_addr, nssSendtoErr); } if (has_tsig == 1) { memput(qp->q_msg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } nameserIncr(nsa->sin_addr, nssSentSysQ); free_nsp(nsp); switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: unsched(qp); schedretry(qp, (time_t) 0); } return (qp); } /* * Check the list of root servers after receiving a response * to a query for the root servers. */ static int check_root() { struct databuf *dp, *pdp; struct namebuf *np; int count = 0; priming = 0; for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) if (NAME(*np)[0] == '\0') break; if (np == NULL) { ns_notice(ns_log_default, "check_root: Can't find root!"); return (0); } for (dp = np->n_data; dp != NULL; dp = dp->d_next) if (dp->d_type == T_NS) count++; ns_debug(ns_log_default, 1, "%d root servers", count); if (count < server_options->minroots) { ns_notice(ns_log_default, "check_root: %d root servers after query to root server < min", count); return (0); } pdp = NULL; dp = np->n_data; while (dp != NULL) { if (dp->d_type == T_NS && dp->d_zone == DB_Z_CACHE && dp->d_ttl < (u_int32_t)tt.tv_sec) { ns_debug(ns_log_default, 1, "deleting old root server '%s'", dp->d_data); dp = rm_datum(dp, np, pdp, NULL); /* SHOULD DELETE FROM HINTS ALSO */ continue; } pdp = dp; dp = dp->d_next; } if (check_ns()) return (1); else { priming = 1; return (0); } } /* * Check the root to make sure that for each NS record we have a A RR */ static int check_ns() { struct databuf *dp, *tdp; struct namebuf *np, *tnp; struct hashbuf *htp; char *dname; int found_arr; const char *fname; time_t curtime; int servers = 0, rrsets = 0; ns_debug(ns_log_default, 2, "check_ns()"); curtime = (u_int32_t) tt.tv_sec; for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) { if (NAME(*np)[0] != '\0') continue; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { int cnames = 0; if (dp->d_rcode) continue; if (dp->d_type != T_NS) continue; servers++; /* look for A records */ dname = (caddr_t) dp->d_data; htp = hashtab; tnp = nlookup(dname, &htp, &fname, 0); if (tnp == NULL || fname != dname) { ns_debug(ns_log_default, 3, "check_ns: %s: not found %s %#lx", dname, fname, (u_long)tnp); sysquery(dname, dp->d_class, T_A, NULL, 0, ns_port, QUERY); continue; } /* look for name server addresses */ found_arr = 0; (void)delete_stale(tnp); for (tdp = tnp->n_data; tdp != NULL; tdp = tdp->d_next) { if (tdp->d_rcode) continue; if (tdp->d_type == T_CNAME) cnames++; if (tdp->d_type != T_A || tdp->d_class != dp->d_class) continue; if ((tdp->d_zone == DB_Z_CACHE) && (tdp->d_ttl < (u_int32_t)curtime)) { ns_debug(ns_log_default, 3, "check_ns: stale entry '%s'", NAME(*tnp)); found_arr = 0; break; } found_arr++; } if (found_arr) rrsets++; else if (cnames > 0) ns_info(ns_log_default, "Root NS %s -> CNAME %s", NAME(*np), NAME(*tnp)); else sysquery(dname, dp->d_class, T_A, NULL, 0, ns_port, QUERY); } } ns_debug(ns_log_default, 2, "check_ns: %d %d", servers, rrsets); return ((servers <= 2) ? (rrsets == servers) : ((rrsets * 2) >= servers) ); } /* int findns(npp, class, nsp, countp, flag) * Find NS's or an SOA * npp, class: * dname whose most enclosing NS is wanted * nsp, countp: * result array and count; array will also be NULL terminated * flag: * boolean: we're being called from ADDAUTH, bypass authority checks * return value: * NXDOMAIN: we are authoritative for this {dname,class} * *countp is bogus, but nsp[] has a single SOA returned in it. * SERVFAIL: we are auth but zone isn't loaded; or, no root servers found * *countp and nsp[] are bogus. * OK: we are not authoritative, and here are the NS records we found. * *countp and nsp[] return NS records of interest. */ int findns(struct namebuf **npp, int class, struct databuf **nsp, int *countp, int flag) { struct namebuf *np = *npp; struct databuf *dp; struct databuf **nspp; struct hashbuf *htp; nsp[0] = NULL; if (priming && (np == NULL || NAME(*np)[0] == '\0')) htp = fcachetab; else htp = hashtab; try_again: if (htp == fcachetab && class == C_IN && !priming) /* * XXX - do we want to set needs_prime_cache if * OPTION_FORWARD_ONLY? */ needs_prime_cache = 1; if (np == NULL) { /* find the root */ for (np = htp->h_tab[0]; np != NULL; np = np->n_next) if (NAME(*np)[0] == '\0') break; } while (np != NULL) { ns_debug(ns_log_default, 5, "findns: np %#x '%s'", np, NAME(*np)); /* Look first for SOA records. */ #ifdef ADDAUTH if (!flag) #endif for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_zone != DB_Z_CACHE && ((zones[dp->d_zone].z_type == Z_PRIMARY) || (zones[dp->d_zone].z_type == Z_SECONDARY)) && match(dp, class, T_SOA) && dp->d_type == T_SOA) { ns_debug(ns_log_default, 3, "findns: SOA found"); if (zones[dp->d_zone].z_flags & Z_AUTH) { *npp = np; nsp[0] = dp; nsp[1] = NULL; DRCNTINC(dp); return (NXDOMAIN); } else { /* XXX: zone isn't loaded but we're * primary or secondary for it. * should we fwd this? */ return (SERVFAIL); } } } /* If no SOA records, look for NS records. */ nspp = &nsp[0]; *nspp = NULL; (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, T_NS)) continue; if (dp->d_rcode) continue; /* * Don't use records that may become invalid to * reference later when we do the rtt computation. * Never delete our safety-belt information! * * XXX: this is horribly bogus. */ if ((dp->d_zone == DB_Z_CACHE) && (dp->d_ttl < (u_int32_t)tt.tv_sec) && !(dp->d_flags & DB_F_HINT)) { ns_debug(ns_log_default, 1, "findns: stale entry '%s'", NAME(*np)); /* * We may have already added NS databufs * and are going to throw them away. Fix * reference counts. We don't need to free * them here as we just got them from the * cache. */ while (nspp > &nsp[0]) { nspp--; DRCNTDEC(*nspp); } nsp[0] = NULL; goto try_parent; } if (nspp < &nsp[NSMAX-1]) { *nspp++ = dp; DRCNTINC(dp); } } *countp = nspp - nsp; if (*countp > 0) { ns_debug(ns_log_default, 3, "findns: %d NS's added for '%s'", *countp, NAME(*np)); *nspp = NULL; *npp = np; return (OK); /* Success, got some NS's */ } try_parent: np = np_parent(np); } if (htp == hashtab) { htp = fcachetab; goto try_again; } ns_debug(ns_log_default, 1, "findns: No root nameservers for class %s?", p_class(class)); if ((unsigned)class < MAXCLASS && norootlogged[class] == 0) { norootlogged[class] = 1; ns_info(ns_log_default, "No root nameservers for class %s", p_class(class)); } return (SERVFAIL); } /* * Extract RR's from the given node that match class and type. * Return number of bytes added to response. * If no matching data is found, then 0 is returned. */ int finddata(struct namebuf *np, int class, int type, HEADER *hp, char **dnamep, int *lenp, int *countp) { struct databuf *dp; char *cp; int buflen, n, count = 0; char *new_dnamep = NULL; int defer = 0, found_count = 0, choice, i; struct databuf **found = NULL; struct databuf **tmpfound = NULL; int foundcname; int stalecount; int ret = 0; stalecount = delete_stale(np); /* We don't want to return cached SIG records when asked for SIGs, * since we may have an incomplete set. */ if (type == T_SIG && findMyZone(np, class) == DB_Z_CACHE) return(0); if (type != T_ANY && type != T_PTR && type != T_NXT) { found = memget((stalecount + 1) * sizeof *found); tmpfound = memget((stalecount + 1) * sizeof *tmpfound); if (found == NULL || tmpfound == NULL) ns_panic(ns_log_default, 1, "finddata: out of memory"); defer = 1; } buflen = *lenp; #ifdef DEBUG if (buflen > PACKETSZ) ns_debug(ns_log_default, 1, "finddata(): buflen=%d", buflen); #endif cp = ((char *)hp) + *countp; foundcname = 0; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!wanted(dp, class, type)) { if (type == T_CNAME && class == dp->d_class) { /* any data means no CNAME exists */ if (dp->d_type != T_NXT && dp->d_type != T_KEY && dp->d_type != T_SIG) { ret = 0; goto done; } } continue; } if (dp->d_cred == DB_C_ADDITIONAL) { #ifdef NOADDITIONAL continue; #else /* we want to expire additional data very * quickly. current strategy is to cut 5% * off each time it is accessed. this makes * stale(dp) true earlier when this datum is * used often. */ dp->d_ttl = tt.tv_sec + 0.95 * (int) (dp->d_ttl - tt.tv_sec); #endif } /* -ve $ing stuff, anant@isi.edu * if we have a -ve $ed record, change the rcode on the * header to reflect that */ if (dp->d_rcode == NOERROR_NODATA) { if (count != 0) { /* * This should not happen, yet it does... */ ns_info(ns_log_default, "NODATA & data for \"%s\" type %d class %d", *dnamep, type, class); continue; } if (type == T_ANY) continue; hp->rcode = NOERROR_NODATA; if (dp->d_size == 0) { /* !RETURNSOA */ ret = 1; goto done; } } if (dp->d_rcode == NXDOMAIN) { if (count != 0) { /* * This should not happen, yet it might... */ ns_info(ns_log_default, "NXDOMAIN & data for \"%s\" type %d class %d", *dnamep, type, class); continue; } hp->rcode = NXDOMAIN; if (dp->d_size == 0) { /* !RETURNSOA */ ret = 1; goto done; } } /* Don't put anything but key or sig RR's in response to requests for key or sig */ if (((type == T_SIG) || (type == T_KEY)) && (!((dp->d_type == T_SIG) || (dp->d_type == T_KEY))) ) continue; if (!defer) { if (foundcname != 0 && dp->d_type == T_CNAME) continue; if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; if (dp->d_type == T_CNAME) { foundcname = 1; #define FOLLOWCNAME(type) \ (type != T_KEY) && (type != T_SIG) && (type != T_NXT) && (type != T_ANY) /* don't alias if querying for key, sig, nxt, or any */ if (FOLLOWCNAME(type)) new_dnamep = (char *)dp->d_data; } } else { if (dp->d_type == T_CNAME) foundcname = 1; found[found_count++] = dp; } } if (found_count == 0 && count == 0) { ret = 0; goto done; } /* * If the query type was SIG or ANY we will have returned the SIG * records already. */ if (type != T_SIG && type != T_ANY) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!wantedsig(dp, class, type)) continue; if (dp->d_cred == DB_C_ADDITIONAL) { #ifdef NOADDITIONAL continue; #else /* we want to expire additional data very * quickly. current strategy is to cut 5% * off each time it is accessed. this makes * stale(dp) true earlier when this datum is * used often. */ dp->d_ttl = tt.tv_sec + 0.95 * (int) (dp->d_ttl - tt.tv_sec); #endif } if (!defer) { if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } else found[found_count++] = dp; } } if (defer && found_count > 0) { int first_sig; int non_sig_count; int sig_count; /* number of SIG records in found */ int idx, jdx; enum ordering order; order = match_order(np, class, foundcname ? T_CNAME : type); /* shuffle the SIG records down to the bottom of the array * as we need to make sure they get packed last, no matter * what the ordering is. We're sure to maintain the * original ordering within the two sets of records (so * that fixed_order can work). * First we pack the non-SIG records into the temp array. */ for (idx = jdx = 0 ; idx < found_count ; idx++) { if (found[idx]->d_type != T_SIG) { tmpfound[jdx++] = found[idx]; } } non_sig_count = jdx; sig_count = found_count - jdx; first_sig = jdx ; /* now shift the SIG records down to the end of the array * and copy in the non-SIG records */ for (i = idx = found_count - 1 ; idx >= 0 ; idx--) { if (i < non_sig_count) { found[i] = tmpfound[i]; i--; } else if (found[idx]->d_type == T_SIG) { found[i--] = found[idx] ; } } foundcname = 0; switch (order) { case fixed_order: for (i = 0; i < found_count; i++) { dp = found[i]; if (foundcname != 0 && dp->d_type == T_CNAME) continue; if (dp->d_type == T_CNAME) { foundcname = 1; if (FOLLOWCNAME(type)) { new_dnamep = (char *)dp->d_data; } } if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } break; case random_order: { /* first we shuffle the non-SIG records */ int iters = non_sig_count; for (i = 0; i < iters; i++) { choice = ((u_int)rand()>>3) % non_sig_count; non_sig_count--; dp = found[choice]; found[choice] = found[non_sig_count]; if (foundcname != 0 && dp->d_type == T_CNAME) continue; if (dp->d_type == T_CNAME) { foundcname = 1; if (FOLLOWCNAME(type)) { new_dnamep = (char *)dp->d_data; } } if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } /* now shuffle the SIG records */ iters = sig_count; for (i = 0; i < iters; i++) { choice = ((u_int)rand()>>3) % sig_count; choice += first_sig; sig_count--; dp = found[choice]; found[choice] = found[sig_count + first_sig]; if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } break; } case cyclic_order: /* first we do the non-SIG records */ - choice = ((u_int)rand()>>3) % non_sig_count; + if (non_sig_count > 0) + choice = ((u_int)rand()>>3) % non_sig_count; + else + choice = 0; for (i = 0; i < non_sig_count ; i++) { dp = found[(i + choice) % non_sig_count]; if (foundcname != 0 && dp->d_type == T_CNAME) continue; if (dp->d_type == T_CNAME) { foundcname = 1; if (FOLLOWCNAME(type)) { new_dnamep = (char *)dp->d_data; } } if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } /* now do the SIG record rotation. */ if (sig_count > 0) { choice = ((u_int)rand()>>3) % sig_count; choice += first_sig; i = choice; do { dp = found[i]; if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; i++; if (i >= found_count) i = first_sig; } while (i != choice); } break; default: ns_warning(ns_log_default, "finddata: unknown ordering: %d", order); break; } } if (new_dnamep != NULL) *dnamep = new_dnamep; ns_debug(ns_log_default, 3, "finddata: added %d class %d type %d RRs", count, class, type); ret = *lenp - buflen; done: if (found != NULL) memput(found, (stalecount + 1) * sizeof *found); if (tmpfound != NULL) memput(tmpfound, (stalecount + 1) * sizeof *tmpfound); *countp = count; return (ret); } /* * Do we want this data record based on the class and type? */ static int wanted(const struct databuf *dp, int class, int type) { const u_char *cp; int coveredType; time_t expiration; #ifdef DEBUG char pclass[15], ptype[15]; #endif #ifdef DEBUG strcpy(pclass, p_class(class)); strcpy(ptype, p_type(type)); ns_debug(ns_log_default, 3, "wanted(%#x, %s %s) [%s %s]", dp, pclass, ptype, p_class(dp->d_class), p_type(dp->d_type)); #endif if (dp->d_class != class && class != C_ANY) return (0); /* * Must check SIG for expiration below, other matches * return OK here. */ if (type == dp->d_type && (type != T_SIG)) return (1); /* For a T_ANY query, we do not want to return -ve $ed RRs. */ if (type == T_ANY && dp->d_rcode == NOERROR_NODATA) return (0); /* First, look at the type of RR. */ switch (dp->d_type) { /* Cases to deal with: T_ANY search, return all unexpired SIGs. T_SIG search, return all unexpired SIGs. T_ search, return all unexp SIG s. */ case T_SIG: cp = dp->d_data; GETSHORT(coveredType, cp); cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ GETLONG(expiration,cp); if (type == T_ANY || type == T_SIG) { if (expiration > time(0)) return (1); /* Unexpired matching SIG */ } return (0); /* We don't return this SIG. */ case T_ANY: return (1); case T_CNAME: if (dp->d_rcode != NOERROR_NODATA) return (1); else break; } /* OK, now look at the type of query. */ if (type == ns_t_any) return (1); else if (type == ns_t_mailb) switch (dp->d_type) { case T_MR: case T_MB: case T_MG: case T_MINFO: return (1); } else if (ns_t_xfr_p(type)) { /* * This is used to validate transfer requests, not * generate transfer responses. Is there an SOA? */ if (dp->d_type == ns_t_soa && dp->d_zone != DB_Z_CACHE && (zones[dp->d_zone].z_flags & Z_AUTH)) return (1); } return (0); } static int wantedsig(const struct databuf *dp, int class, int type) { const u_char *cp; int coveredType; time_t expiration; #ifdef DEBUG char pclass[15], ptype[15]; #endif #ifdef DEBUG strcpy(pclass, p_class(class)); strcpy(ptype, p_type(type)); ns_debug(ns_log_default, 3, "wantedtsig(%#x, %s %s) [%s %s]", dp, pclass, ptype, p_class(dp->d_class), p_type(dp->d_type)); #endif if (dp->d_class != class && class != C_ANY) return (0); if (dp->d_type != T_SIG || dp->d_rcode != 0) return (0); cp = dp->d_data; GETSHORT(coveredType, cp); cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ GETLONG(expiration,cp); if (expiration < time(0)) return (0); if (type == T_ANY || type == T_SIG || type == coveredType) return (1); if (type == ns_t_mailb) { switch (coveredType) { case T_MR: case T_MB: case T_MG: case T_MINFO: return (1); } } return (0); } /* * Add RR entries from dpp array to a query/response. * Return the number of bytes added or negative the amount * added if truncation occured. Typically you are * adding NS records to a response. */ int add_data(struct namebuf *np, struct databuf **dpp, u_char *cp, int buflen, int *countp) { struct databuf *dp; char dname[MAXDNAME]; int n, bytes; bytes = *countp = 0; getname(np, dname, sizeof(dname)); for (dp = *dpp++; dp != NULL; dp = *dpp++) { if (stale(dp)) continue; /* ignore old cache entry */ if (dp->d_rcode) continue; if ((n = make_rr(dname, dp, cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) return (-bytes); /* Truncation */ cp += n; buflen -= n; bytes += n; (*countp)++; } return (bytes); } static void rrsetadd(struct flush_set *flushset, const char *name, struct databuf *dp) { struct flush_set *fs = flushset; struct db_list *dbl; while (fs->fs_name && ( ns_samename(fs->fs_name,name) != 1 || (fs->fs_class != dp->d_class) || (fs->fs_type != dp->d_type) || (fs->fs_cred != dp->d_cred))) { fs++; } if (!fs->fs_name) { fs->fs_name = savestr(name, 1); fs->fs_class = dp->d_class; fs->fs_type = dp->d_type; fs->fs_cred = dp->d_cred; fs->fs_list = NULL; fs->fs_last = NULL; } dbl = (struct db_list *)memget(sizeof(struct db_list)); if (!dbl) panic("rrsetadd: out of memory", NULL); dbl->db_next = NULL; dbl->db_dp = dp; if (fs->fs_last == NULL) fs->fs_list = dbl; else fs->fs_last->db_next = dbl; fs->fs_last = dbl; } static int ttlcheck(const char *name, struct db_list *dbl, int update) { int type = dbl->db_dp->d_type; int class = dbl->db_dp->d_class; struct hashbuf *htp = hashtab; const char *fname; struct namebuf *np; struct db_list *dbp = dbl; struct databuf *dp; u_int32_t ttl = 0; /* Make gcc happy. */ int first; np = nlookup(name, &htp, &fname, 0); if (np == NULL || fname != name || ns_wildcard(NAME(*np))) return (1); /* check that all the ttl's we have are the same, if not return 1 */ first = 1; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; if (first) { /* we can't update zone data so return early */ if (dp->d_zone != DB_Z_CACHE) return (0); ttl = dp->d_ttl; first = 0; } else if (ttl != dp->d_ttl) return (1); } /* there are no records of this type in the cache */ if (first) return(1); /* * the ttls of all records we have in the cache are the same * if the ttls differ in the new set we don't want it. */ /* check that all the ttl's we have are the same, if not return 0 */ first = 1; while (dbp) { if (first) { ttl = dbp->db_dp->d_ttl; first = 0; } else if (ttl != dbp->db_dp->d_ttl) { return(0); } dbp = dbp->db_next; } /* update ttl if required */ if (update) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; if (dp->d_ttl > ttl) break; dp->d_ttl = ttl; fixttl(dp); } } return(1); } /* * lookup rrset in table and compare to dbl * tri state result * -1: lookup failed * 0: rrsets same * 1: rrsets differ */ static int rrsetcmp(char * name, struct db_list * dbl, struct hashbuf * table) { int type = dbl->db_dp->d_type; int class = dbl->db_dp->d_class; struct hashbuf *htp = table; const char *fname; struct namebuf *np; struct db_list *dbp = dbl; struct databuf *dp; int exists = 0; np = nlookup(name, &htp, &fname, 0); if (np == NULL || fname != name || ns_wildcard(NAME(*np))) { ns_debug(ns_log_default, 3, "rrsetcmp: name not in database"); return (-1); } /* check that all entries in dbl are in the cache */ while (dbp) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; exists = 1; if (!db_cmp(dp, dbp->db_dp) #ifdef NOADDITIONAL && ((dp->d_cred == dbp->db_dp->d_cred) || (dp->d_cred != DB_C_ADDITIONAL)) #endif ) break; } if (!dp) { ns_debug(ns_log_default, 3, "rrsetcmp: %srecord%s in database", exists ? "" : "no ", exists ? " not" : "s"); return (exists ? 1 : -1); } dbp = dbp->db_next; } /* Check that all cache entries are in the list. */ for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; #ifdef NCACHE if (dp->d_rcode) return (1); #endif dbp = dbl; while (dbp) { if (!db_cmp(dp, dbp->db_dp)) break; dbp = dbp->db_next; } if (!dbp) { ns_debug(ns_log_default, 3, "rrsetcmp: record not in rrset"); return (1); } } ns_debug(ns_log_default, 3, "rrsetcmp: rrsets matched"); return (0); } /* * verify incoming answer against what we already have in the hints * issue warnings / errors if differences detected. */ static void check_hints(struct flush_set * flushset) { struct zoneinfo *zp; struct flush_set *fs; struct db_list *dbp; /* We don't use hints when in forward only mode */ if (NS_OPTION_P(OPTION_FORWARD_ONLY)) return; /* find "." NS rrset and hence class */ for (fs = flushset; fs->fs_name != NULL; fs++) { if ((fs->fs_name[0] != '\0') || (fs->fs_type != ns_t_ns)) continue; /* see if we are a root server */ zp = find_zone(fs->fs_name, fs->fs_class); if (zp != NULL && (zp->z_type == z_master || zp->z_type == z_slave)) return; switch (rrsetcmp(fs->fs_name, fs->fs_list, fcachetab)) { case -1: ns_error(ns_log_default, "check_hints: no NS records for class %d in hints", fs->fs_class); break; case 1: ns_warning(ns_log_default, "check_hints: root NS list in hints for class %d does not match root NS list", fs->fs_class); break; case 0: break; default: ns_error(ns_log_default, "check_hints: unexpected response from rrsetcmp"); break; } break; } if (fs->fs_name == NULL) /* no root NS records */ return; dbp = fs->fs_list; while (dbp) { /* for each NS find A rrset in answer and check */ for (fs = flushset; fs->fs_name != NULL; fs++) { if (ns_samename(fs->fs_name, (char *)dbp->db_dp->d_data) != 1 || fs->fs_type != ns_t_a) continue; switch (rrsetcmp(fs->fs_name, fs->fs_list, fcachetab)) { case -1: ns_error(ns_log_default, "check_hints: no A records for %s class %d in hints", fs->fs_name[0] ? fs->fs_name : ".", fs->fs_class); break; case 1: ns_warning(ns_log_default, "check_hints: A records for %s class %d do not match hint records", fs->fs_name[0] ? fs->fs_name : ".", fs->fs_class); break; case 0: break; default: ns_error(ns_log_default, "check_hints: unexpected response from rrsetcmp"); break; } break; } if (fs->fs_name == NULL) ns_debug(ns_log_default, 2, "check_hints: no A records for %s", dbp->db_dp->d_data); dbp = dbp->db_next; } } static void rrsetupdate(struct flush_set * flushset, int flags, struct sockaddr_in from, int updatettl) { struct flush_set *fs = flushset; struct db_list *dbp, *odbp; int n; void *state = NULL; while (fs->fs_name) { ns_debug(ns_log_default, 2, "rrsetupdate: %s", fs->fs_name[0] ? fs->fs_name : "."); if ((n = rrsetcmp(fs->fs_name, fs->fs_list, hashtab)) && ttlcheck(fs->fs_name, fs->fs_list, 0)) { if (n > 0) flushrrset(fs, from); dbp = fs->fs_list; while (dbp) { n = db_set_update(fs->fs_name, dbp->db_dp, &state, flags, &hashtab, from, NULL, 0, NULL); ns_debug(ns_log_default, 3, "rrsetupdate: %s %d", fs->fs_name[0] ? fs->fs_name : ".", n); odbp = dbp; dbp = dbp->db_next; memput(odbp, sizeof *odbp); } ns_debug(ns_log_default, 3, "rrsetupdate: %s %d", fs->fs_name[0] ? fs->fs_name : ".", n); } else { if ((n == 0) && updatettl) (void)ttlcheck(fs->fs_name,fs->fs_list, 1); dbp = fs->fs_list; while (dbp) { db_freedata(dbp->db_dp); odbp = dbp; dbp = dbp->db_next; memput(odbp, sizeof *odbp); } } fs->fs_list = NULL; fs++; } n = db_set_update(NULL, NULL, &state, flags, &hashtab, from, NULL, 0, NULL); } static void flushrrset(struct flush_set * fs, struct sockaddr_in from) { struct databuf *dp; int n; ns_debug(ns_log_default, 2, "flushrrset(%s, %s, %s, %d)", fs->fs_name[0]?fs->fs_name:".", p_type(fs->fs_type), p_class(fs->fs_class), fs->fs_cred); dp = savedata(fs->fs_class, fs->fs_type, 0, NULL, 0); dp->d_zone = DB_Z_CACHE; dp->d_cred = fs->fs_cred; dp->d_clev = 0; do { n = db_update(fs->fs_name, dp, NULL, NULL, DB_DELETE, hashtab, from); ns_debug(ns_log_default, 3, "flushrrset: %d", n); } while (n == OK); db_freedata(dp); } static void free_flushset(struct flush_set *flushset, int flushset_size) { struct flush_set *fs; for (fs = flushset; fs->fs_name != NULL; fs++) freestr(fs->fs_name); memput(flushset, flushset_size); } /* * This is best thought of as a "cache invalidate" function. * It is called whenever a piece of data is determined to have * become invalid either through a timeout or a validation * failure. It is better to have no information, than to * have partial information you pass off as complete. */ void delete_all(struct namebuf *np, int class, int type) { struct databuf *dp, *pdp; ns_debug(ns_log_default, 3, "delete_all(%#x:\"%s\" %s %s)", np, NAME(*np), p_class(class), p_type(type)); pdp = NULL; dp = np->n_data; while (dp != NULL) { if (dp->d_zone == DB_Z_CACHE && (dp->d_flags & DB_F_HINT) == 0 && match(dp, class, type)) { dp = rm_datum(dp, np, pdp, NULL); continue; } pdp = dp; dp = dp->d_next; } } /* delete_stale(np) * for all RRs associated with this name, check for staleness (& delete) * arguments: * np = pointer to namebuf to be cleaned. * returns: * number of RRs associated with this name. * side effects: * delete_all() can be called, freeing memory and relinking chains. */ int delete_stale(np) struct namebuf *np; { struct databuf *dp; int count; again: count = 0; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_zone == DB_Z_CACHE && stale(dp)) { delete_all(np, dp->d_class, dp->d_type); goto again; } count++; } return (count); } /* * Adjust answer message so that it fits in outlen. Set tc if required. * * If outlen = msglen, can be used to verify qdcount, ancount, nscount * and arcount. * * return new length */ int trunc_adjust(u_char *msg, int msglen, int outlen) { register HEADER *hp; u_int qdcount, ancount, nscount, arcount, dlen; u_char *cp = msg, *cp1, *eom_in, *eom_out; int n; eom_in = msg + msglen; eom_out = msg + outlen; hp = (HEADER *)msg; qdcount = ntohs(hp->qdcount); ancount = ntohs(hp->ancount); nscount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); cp += HFIXEDSZ; while ((qdcount || ancount || nscount || arcount) && cp < eom_in && cp < eom_out) { cp1 = cp; /* use temporary in case we break */ n = dn_skipname(cp1, eom_in); if (n < 0) break; cp1 += n + 2 * INT16SZ; /* type, class */ if (!qdcount) { cp1 += INT32SZ; /* ttl */ if (cp1 + INT16SZ > eom_in) break; GETSHORT(dlen, cp1); cp1 += dlen; } if (cp1 > eom_in || cp1 > eom_out) break; cp = cp1; if (qdcount) qdcount--; else if (ancount) ancount--; else if (nscount) nscount--; else arcount--; } if (qdcount || ancount || nscount || arcount) { ns_debug(ns_log_default, 1, "trunc_adjust:%s %d %d %d %d %d, %d %d %d %d %d", hp->tc?" tc":"", msglen, ntohs(hp->qdcount), ntohs(hp->ancount), ntohs(hp->nscount), ntohs(hp->arcount), cp-msg, qdcount, ancount, nscount, arcount); hp->tc = 1; hp->qdcount = htons(ntohs(hp->qdcount) - qdcount); hp->ancount = htons(ntohs(hp->ancount) - ancount); hp->nscount = htons(ntohs(hp->nscount) - nscount); hp->arcount = htons(ntohs(hp->arcount) - arcount); } ENSURE(cp <= eom_out); return (cp - msg); } /* * mark the server "from" bad in the qp structure so it won't be retried. */ static void mark_bad(struct qinfo *qp, struct sockaddr_in from) { int i; for (i = 0; i < (int)qp->q_naddr; i++) if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr)) qp->q_addr[i].nretry = MAXRETRY; } static void mark_lame(struct qinfo *qp, struct sockaddr_in from) { int i; for (i = 0; i < (int)qp->q_naddr; i++) if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr) && qp->q_addr[i].ns != NULL) { qp->q_addr[i].ns->d_flags |= DB_F_LAME; db_lame_add(qp->q_domain, (char*)qp->q_addr[i].ns->d_data, tt.tv_sec + server_options->lame_ttl); } } /* * Retry the message if and only if from matches where the query was * last sent to. The code does not handle responses sent from the * wrong interface an a multihomed server. */ static void fast_retry(struct qinfo *qp, struct sockaddr_in from) { if (ina_equal(qp->q_addr[qp->q_curaddr].ns_addr.sin_addr, from.sin_addr)) retry(qp); } static void add_related_additional(char *name) { int i; if (num_related >= MAX_RELATED - 1) return; for (i = 0; i < num_related; i++) if (ns_samename(name, related[i]) == 1) { freestr(name); return; } related[num_related++] = name; } static void free_related_additional() { int i; for (i = 0; i < num_related; i++) freestr(related[i]); num_related = 0; } static int related_additional(char *name) { int i; for (i = 0; i < num_related; i++) if (ns_samename(name, related[i]) == 1) return (1); return (0); } static void freestr_maybe(char **tname) { if (tname == NULL || *tname == NULL) return; freestr(*tname); *tname = NULL; } /* * Match a request namebuf against the configured rrset-order info. First * match wins. There is an implicit '*.' at the front to the ordering names. */ static enum ordering match_order(const struct namebuf *np, int class, int type) { rrset_order_list orders = server_options->ordering; rrset_order_element roe; if (orders == NULL) return (DEFAULT_ORDERING); for (roe = orders->first ; roe != NULL ; roe = roe->next) { if (roe->class != C_ANY && roe->class != class) continue; if (roe->type != T_ANY && roe->type != type) continue; if (match_name(np, roe->name, strlen(roe->name)) == 0) { return (roe->order); } } /* none matched so use default */ return (DEFAULT_ORDERING); } /* Do a simple compare of the NP data against the given NAME, recursively * looking at the NP parent if necessary. NAMELEN is the length of the NAME * that needs to be matched. Matching happen from right to left. Returns -1 * on failure, on success the index of the first character of the matched * portion of the string is returned. In the first level call a return * value of 0 is of interest. */ static int match_name(const struct namebuf *np, const char *name, size_t namelen) { int matched ; if (name[0] == '*' && name[1] == '\0') return 0; if (np->n_parent != NULL) { /* recurse to end of np list */ matched = match_name(np->n_parent,name,namelen); } else { matched = namelen; } if (matched > 0) { int labellen = NAMELEN(*np); char pch; const char *start; if (labellen > matched) { return -1; } else if (labellen < matched) { /* string is longer than this namebuf's data, so make sure there's a period before the end of the match so we don't just match a suffix. */ start = name + (matched - labellen); pch = start[-1]; if (pch != '.') { return -1; } } else { start = name ; } if (strncasecmp(start, NAME(*np), labellen) == 0) { /* looking good. tell our caller what portion of the tail of string has been matched */ if (start == name) return (0) ; else return (start - name - 1); /* matched '.' too */ } else { return (-1); } } return (matched); } Index: head/contrib/bind/bin/named/ns_signal.c =================================================================== --- head/contrib/bind/bin/named/ns_signal.c (revision 60940) +++ head/contrib/bind/bin/named/ns_signal.c (revision 60941) @@ -1,264 +1,264 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_main.c 4.55 (Berkeley) 7/1/91"; -static const char rcsid[] = "$Id: ns_signal.c,v 8.11 1999/10/13 16:39:12 vixie Exp $"; +static const char rcsid[] = "$Id: ns_signal.c,v 8.12 2000/04/21 06:54:12 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1989, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #ifdef SVR4 /* XXX */ # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* Forward. */ static SIG_FN onhup(int); static SIG_FN onintr(int); static SIG_FN setdumpflg(int); static SIG_FN setIncrDbgFlg(int); static SIG_FN setNoDbgFlg(int); static SIG_FN setQrylogFlg(int); static SIG_FN setstatsflg(int); static SIG_FN discard_pipe(int); static SIG_FN setreapflg(int); /* Data. */ static struct { int sig; SIG_FN (*hand)(int); } sighandlers[] = { #ifdef DEBUG { SIGUSR1, setIncrDbgFlg }, { SIGUSR2, setNoDbgFlg }, #endif #if defined(SIGWINCH) && defined(QRYLOG) { SIGWINCH, setQrylogFlg }, #endif #if defined(SIGXFSZ) { SIGXFSZ, onhup }, /* Wierd DEC Hesiodism, harmless. */ #endif { SIGINT, setdumpflg }, { SIGILL, setstatsflg }, { SIGHUP, onhup }, { SIGCHLD, setreapflg }, { SIGPIPE, discard_pipe }, { SIGTERM, onintr } }; static sigset_t mask; static int blocked = 0; /* Private. */ static SIG_FN onhup(int sig) { ns_need_unsafe(main_need_reload); } static SIG_FN onintr(int sig) { ns_need_unsafe(main_need_exit); } static SIG_FN setdumpflg(int sig) { ns_need_unsafe(main_need_dump); } #ifdef DEBUG static SIG_FN setIncrDbgFlg(int sig) { desired_debug++; ns_need_unsafe(main_need_debug); } static SIG_FN setNoDbgFlg(int sig) { desired_debug = 0; ns_need_unsafe(main_need_debug); } #endif /*DEBUG*/ #if defined(QRYLOG) && defined(SIGWINCH) static SIG_FN setQrylogFlg(int sig) { ns_need_unsafe(main_need_qrylog); } #endif /*QRYLOG && SIGWINCH*/ static SIG_FN setstatsflg(int sig) { ns_need_unsafe(main_need_statsdump); } static SIG_FN discard_pipe(int sig) { #ifdef SIGPIPE_ONE_SHOT int saved_errno = errno; struct sigaction sa; memset(&sa, 0, sizeof sa); sa.sa_mask = mask; sa.sa_handler = discard_pipe; if (sigaction(SIGPIPE, &sa, NULL) < 0) ns_error(ns_log_os, "sigaction failed in discard_pipe: %s", strerror(errno)); errno = saved_errno; #endif } static SIG_FN setreapflg(int sig) { ns_need_unsafe(main_need_reap); } /* Public. */ void init_signals(void) { int sh; /* The mask of all our handlers will block all our other handlers. */ (void)sigemptyset(&mask); for (sh = 0; sh < sizeof sighandlers / sizeof sighandlers[0]; sh++) sigaddset(&mask, sighandlers[sh].sig); /* Install our signal handlers with that shared mask. */ for (sh = 0; sh < sizeof sighandlers / sizeof sighandlers[0]; sh++) { struct sigaction sa; memset(&sa, 0, sizeof sa); sa.sa_mask = mask; sa.sa_handler = sighandlers[sh].hand; if (sigaction(sighandlers[sh].sig, &sa, NULL) < 0) ns_error(ns_log_os, "sigaction failed in set_signal_handler(%d): %s", sighandlers[sh].sig, strerror(errno)); } } void block_signals(void) { INSIST(!blocked); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) ns_panic(ns_log_os, 1, "sigblock failed: %s", strerror(errno)); blocked = 1; } void unblock_signals(void) { INSIST(blocked); if (sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) ns_panic(ns_log_os, 1, "sigblock failed: %s", strerror(errno)); blocked = 0; } Index: head/contrib/bind/bin/named/ns_sort.c =================================================================== --- head/contrib/bind/bin/named/ns_sort.c (revision 60940) +++ head/contrib/bind/bin/named/ns_sort.c (revision 60941) @@ -1,410 +1,410 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_sort.c 4.10 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: ns_sort.c,v 8.5 1999/10/13 16:39:12 vixie Exp $"; +static const char rcsid[] = "$Id: ns_sort.c,v 8.6 2000/04/21 06:54:13 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Sorting should really be handled by the resolver, but: * 1) There are too many brain dead resolvers out there that can't be replaced. * 2) It would be a pain to individually configure all those resolvers anyway. * * Here's the scoop: * * To enable address sorting in responses, you need to supply the sortlist * statement in the config file. The sortlist statement takes an * address match list and interprets it even more specially than the * topology statement does. * * Each top level statement in the sortlist must itself be an explicit * address match list with one or two elements. The first element * (which may be an IP address, an IP prefix, an ACL name or nested * address match list) of each top level list is checked against the * source address of the query until a match is found. * * Once the source address of the query has been matched, if the top level * statement contains only one element, the actual primitive element that * matched the source address is used to select the address in the response * to move to the beginning of the response. If the statement is a list * of two elements, then the second element is treated like the address * match list in a topology statement. Each top level element is assigned * a distance and the address in the response with the minimum distance is * moved to the beginning of the response. * * In the following example, any queries received from any of the addresses * of the host itself will get responses preferring addresses on any of * the locally connected networks. Next most preferred are addresses on * the 192.168.1/24 network, and after that either the 192.168.2/24 or * 192.168.3/24 network with no preference shown between these two networks. * Queries received from a host on the 192.168.1/24 network will prefer * other addresses on that network to the 192.168.2/24 and 192.168.3/24 * networks. Queries received from a host on the 192.168.4/24 or the * 192.168.5/24 network will only prefer other addresses on their * directly connected networks. * * sortlist { * { * localhost; * { * localnets; * 192.168.1/24; * { 192,168.2/24; 192.168.3/24; }; * }; * }; * { * 192.168.1/24; * { * 192.168.1/24; * { 192.168.2/24; 192.168.3/24; }; * }; * }; * { * 192.168.2/24; * { * 192.168.2/24; * { 192.168.1/24; 192.168.3/24; }; * }; * }; * { * 192.168.3/24; * { * 192.168.3/24; * { 192.168.1/24; 192.168.2/24; }; * }; * }; * { * { 192.168.4/24; 192.168.5/24; }; * }; * }; * * * The following example will give reasonable behaviour for the local host * and hosts on directly connected networks. It is similar to the behavior * of the address sort in BIND 4.9.x. Responses sent to queries from the * local host will favor any of the directly connected networks. Responses * sent to queries from any other hosts on a directly connected network will * prefer addresses on that same network. Responses to other queries will * not be sorted. * * sortlist { * { localhost; localnets; }; * { localnets; }; * }; * * XXX - it wouldb e nice to have an ACL called "source" that matched the * source address of a query so that a host could be configured to * automatically prefer itself, and an ACL called "sourcenet", that * would return the primitive IP match element that matched the source * address so that you could do: * { localnets; { sourcenet; { other stuff ...}; }; * and automatically get similar behaviour to what you get with: * { localnets; }; * */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static int sort_rr(u_char *cp, u_char *eom, int ancount, ip_match_list iml); static int ip_match_address_elt(ip_match_list, struct in_addr, ip_match_element *); void sort_response(u_char *cp, u_char *eom, int ancount, struct sockaddr_in *from) { struct in_addr address; struct ip_match_element imelement; ip_match_element imetl, imematch, imeprimitive; struct ip_match_list imlist; ip_match_list iml; int indirect, matched; if (server_options->sortlist == NULL) return; if (from->sin_family != AF_INET) return; address = from->sin_addr; for (imetl = server_options->sortlist->first; imetl != NULL; imetl = imetl->next) { if (imetl->type == ip_match_indirect) imematch = imetl->u.indirect.list->first; else /* * allow a bare pattern as a top level statement * and treat it like {pattern;}; */ imematch = imetl; switch (imematch->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: imematch->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: imematch->u.indirect.list = local_networks; indirect = 1; break; default: panic("unexpected ime type in ip_match_address()", NULL); } if (indirect) { imeprimitive = NULL; matched = ip_match_address_elt(imematch->u.indirect.list, address, &imeprimitive); if (matched >= 0) { if (imematch->flags & IP_MATCH_NEGATE) /* Don't sort */ return; } else continue; } else { if (ina_onnet(address, imematch->u.direct.address, imematch->u.direct.mask)) { if (imematch->flags & IP_MATCH_NEGATE) /* Don't sort */ return; else imeprimitive = imematch; } else continue; } if (imetl != imematch && imematch->next != NULL) { /* * Not a bare pattern at the top level, but a two * element list */ switch (imematch->next->type) { case ip_match_pattern: case ip_match_localhost: case ip_match_localnets: imelement = *(imematch->next); imelement.next = NULL; iml = &imlist; iml->first = iml->last = &imelement; break; case ip_match_indirect: iml = imematch->next->u.indirect.list; break; default: panic("unexpected ime type in ip_match_address()", NULL); } } else if (imeprimitive) { imelement = *imeprimitive; imelement.next = NULL; iml = &imlist; iml->first = iml->last = &imelement; } else { /* Don't sort because we'd just use "any" */ return; } sort_rr(cp, eom, ancount, iml); break; } return; } static int sort_rr(u_char *cp, u_char *eom, int ancount, ip_match_list iml) { int type, class, dlen, n, c, distance, closest; struct in_addr inaddr; u_char *rr1 = NULL, *rrbest, *cpstart; rr1 = NULL; cpstart = cp; for (c = ancount; c > 0; --c) { n = dn_skipname(cp, eom); if (n < 0) return (1); /* bogus, stop processing */ cp += n; if (cp + QFIXEDSZ > eom) return (1); GETSHORT(type, cp); GETSHORT(class, cp); cp += INT32SZ; GETSHORT(dlen, cp); if (dlen > eom - cp) return (1); /* bogus, stop processing */ switch (type) { case T_A: switch (class) { case C_IN: case C_HS: memcpy((char *)&inaddr, cp, INADDRSZ); /* Find the address with the minimum distance */ if (rr1 == NULL) { rr1 = cp; rrbest = cp; closest = distance_of_address(iml, inaddr); } else { distance = distance_of_address(iml, inaddr); if (distance < closest) { rrbest = cp; closest = distance; } } break; } break; } cp += dlen; } if (rr1 != rrbest && rr1 != NULL) { memcpy((char *)&inaddr, rrbest, INADDRSZ); memcpy(rrbest, rr1, INADDRSZ); memcpy(rr1, (char *)&inaddr, INADDRSZ); } return (0); } /* * Just like ip_match_address(), but also returns a pointer to the primitive * element that matched. */ static int ip_match_address_elt(ip_match_list iml, struct in_addr address, ip_match_element *imep) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; default: panic("unexpected ime type in ip_match_address()", NULL); } if (indirect) { ret = ip_match_address_elt(ime->u.indirect.list, address, imep); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { *imep = ime; if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } Index: head/contrib/bind/bin/named/ns_stats.c =================================================================== --- head/contrib/bind/bin/named/ns_stats.c (revision 60940) +++ head/contrib/bind/bin/named/ns_stats.c (revision 60941) @@ -1,396 +1,402 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_stats.c 4.10 (Berkeley) 6/27/90"; -static const char rcsid[] = "$Id: ns_stats.c,v 8.27 1999/10/13 16:39:12 vixie Exp $"; +static const char rcsid[] = "$Id: ns_stats.c,v 8.30 2000/04/23 02:18:59 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef HAVE_GETRUSAGE /* XXX */ #include #include #endif #include "named.h" static u_long typestats[T_ANY+1]; static void nameserStats(FILE *); void ns_stats() { time_t timenow = time(NULL); FILE *f; int i; ns_notice(ns_log_statistics, "dumping nameserver stats"); if (!(f = fopen(server_options->stats_filename, "a"))) { ns_notice(ns_log_statistics, "cannot open stat file, \"%s\"", server_options->stats_filename); return; } + (void) fchown(fileno(f), user_id, group_id); fprintf(f, "+++ Statistics Dump +++ (%ld) %s", (long)timenow, checked_ctime(&timenow)); fprintf(f, "%ld\ttime since boot (secs)\n", (long)(timenow - boottime)); fprintf(f, "%ld\ttime since reset (secs)\n", (long)(timenow - resettime)); /* query type statistics */ fprintf(f, "%lu\tUnknown query types\n", (u_long)typestats[0]); for (i = 1; i < T_ANY+1; i++) fprintf(f, "%lu\t%s queries\n", typestats[i], p_type(i)); /* name server statistics */ nameserStats(f); fprintf(f, "--- Statistics Dump --- (%ld) %s", (long)timenow, checked_ctime(&timenow)); (void) my_fclose(f); /* Now do the memory statistics file */ if (!(f = fopen(server_options->memstats_filename, "a"))) { - ns_notice(ns_log_statistics, "cannot open memstat file, \"%s\"", + ns_notice(ns_log_statistics, "cannot open memstat file, \"%s\"", server_options->memstats_filename); return; } + (void) fchown(fileno(f), user_id, group_id); fprintf(f, "+++ Memory Statistics Dump +++ (%ld) %s", (long)timenow, checked_ctime(&timenow)); fprintf(f, "%ld\ttime since boot (secs)\n", (long)(timenow - boottime)); fprintf(f, "%ld\ttime since reset (secs)\n", (long)(timenow - resettime)); fprintf(f, "++ Memory Statistics ++\n"); memstats(f); fprintf(f, "-- Memory Statistics --\n"); fprintf(f, "--- Memory Statistics Dump --- (%ld) %s", (long)timenow, checked_ctime(&timenow)); (void) my_fclose(f); ns_notice(ns_log_statistics, "done dumping nameserver stats"); } void qtypeIncr(qtype) int qtype; { if (qtype < T_A || qtype > T_ANY) qtype = 0; /* bad type */ typestats[qtype]++; } static tree *nameserTree; static int nameserInit; static FILE *nameserStatsFile; static const char *statNames[nssLast] = { "RR", /* sent us an answer */ "RNXD", /* sent us a negative response */ "RFwdR", /* sent us a response we had to fwd */ "RDupR", /* sent us an extra answer */ "RFail", /* sent us a SERVFAIL */ "RFErr", /* sent us a FORMERR */ "RErr", /* sent us some other error */ "RAXFR", /* sent us an AXFR */ "RLame", /* sent us a lame delegation */ "ROpts", /* sent us some IP options */ "SSysQ", /* sent them a sysquery */ "SAns", /* sent them an answer */ "SFwdQ", /* fwdd a query to them */ "SDupQ", /* sent them a retry */ "SErr", /* sent failed (in sendto) */ "RQ", /* sent us a query */ "RIQ", /* sent us an inverse query */ "RFwdQ", /* sent us a query we had to fwd */ "RDupQ", /* sent us a retry */ "RTCP", /* sent us a query using TCP */ "SFwdR", /* fwdd a response to them */ "SFail", /* sent them a SERVFAIL */ "SFErr", /* sent them a FORMERR */ "SNaAns", /* sent them a non autoritative answer */ "SNXD", /* sent them a negative response */ + "RUQ", /* sent us an unapproved query */ + "RURQ", /* sent us an unapproved recursive query */ + "RUXFR", /* sent us an unapproved AXFR or IXFR */ + "RUUpd", /* sent us an unapproved update */ }; /* * Note that addresses in network byte order always have the high byte first. * XXX - this is horribly IPv4 dependent, but it's performance critical. */ static int nameserCompar(const tree_t t1, const tree_t t2) { u_char *p1 = (u_char *)t1, *p2 = (u_char *)t2; int i; for (i = INADDRSZ; i > 0; i--) { u_char c1 = *p1++, c2 = *p2++; if (c1 < c2) return (-1); if (c1 > c2) return (1); } return (0); } struct nameser * nameserFind(addr, flags) struct in_addr addr; int flags; { struct nameser dummy; struct nameser *ns; if (!nameserInit) { tree_init(&nameserTree); nameserInit++; } dummy.addr = addr; ns = (struct nameser *)tree_srch(&nameserTree, nameserCompar, (tree_t)&dummy); if (ns == NULL && (flags & NS_F_INSERT) != 0) { ns = (struct nameser *)memget(sizeof(struct nameser)); if (ns == NULL) { nomem: if (!haveComplained((u_long)nameserFind, 0)) ns_notice(ns_log_statistics, "nameserFind: memget failed; %s", strerror(errno)); return (NULL); } memset(ns, 0, sizeof *ns); ns->addr = addr; if (!tree_add(&nameserTree, nameserCompar, (tree_t)ns, NULL)) { int save = errno; memput(ns, sizeof *ns); errno = save; goto nomem; } } return (ns); } static void nameserStatsOut(f, stats) FILE *f; u_long stats[]; { int i; const char *pre = "\t"; for (i = 0; i < (int)nssLast; i++) { fprintf(f, "%s%lu", pre, (u_long)stats[i]); pre = ((i+1) % 5) ? " " : " "; } fputc('\n', f); } static void nameserStatsHdr(f) FILE *f; { int i; const char *pre = "\t"; fprintf(f, "(Legend)\n"); for (i = 0; i < (int)nssLast; i++) { fprintf(f, "%s%s", pre, statNames[i] ? statNames[i] : ""); pre = ((i+1) % 5) ? "\t" : "\n\t"; } fputc('\n', f); } static int nameserStatsTravUAR(t) tree_t t; { struct nameser *ns = (struct nameser *)t; fprintf(nameserStatsFile, "[%s]\n", /* : rtt %u */ inet_ntoa(ns->addr) /*, ns->rtt*/ ); nameserStatsOut(nameserStatsFile, ns->stats); return (1); } static void nameserStats(f) FILE *f; { nameserStatsFile = f; fprintf(f, "++ Name Server Statistics ++\n"); nameserStatsHdr(f); fprintf(f, "(Global)\n"); nameserStatsOut(f, globalStats); if (NS_OPTION_P(OPTION_HOSTSTATS)) tree_trav(&nameserTree, nameserStatsTravUAR); fprintf(f, "-- Name Server Statistics --\n"); nameserStatsFile = NULL; } void ns_logstats(evContext ctx, void *uap, struct timespec due, struct timespec inter) { char buffer[1024]; char buffer2[32], header[64]; time_t timenow = time(NULL); int i; #ifdef HAVE_GETRUSAGE struct rusage usage, childu; #endif /*HAVE_GETRUSAGE*/ #ifdef HAVE_GETRUSAGE # define tv_float(tv) ((tv).tv_sec + ((tv).tv_usec / 1000000.0)) getrusage(RUSAGE_SELF, &usage); getrusage(RUSAGE_CHILDREN, &childu); sprintf(buffer, "CPU=%gu/%gs CHILDCPU=%gu/%gs", tv_float(usage.ru_utime), tv_float(usage.ru_stime), tv_float(childu.ru_utime), tv_float(childu.ru_stime)); ns_info(ns_log_statistics, "USAGE %lu %lu %s", (u_long)timenow, (u_long)boottime, buffer); # undef tv_float #endif /*HAVE_GETRUSAGE*/ sprintf(header, "NSTATS %lu %lu", (u_long)timenow, (u_long)boottime); strcpy(buffer, header); for (i = 0; i < T_ANY+1; i++) { if (typestats[i]) { sprintf(buffer2, " %s=%lu", p_type(i), typestats[i]); if (strlen(buffer) + strlen(buffer2) > sizeof(buffer) - 1) { ns_info(ns_log_statistics, buffer); strcpy(buffer, header); } strcat(buffer, buffer2); } } ns_info(ns_log_statistics, buffer); sprintf(header, "XSTATS %lu %lu", (u_long)timenow, (u_long)boottime); strcpy(buffer, header); for (i = 0; i < (int)nssLast; i++) { sprintf(buffer2, " %s=%lu", statNames[i]?statNames[i]:"?", (u_long)globalStats[i]); if (strlen(buffer) + strlen(buffer2) > sizeof(buffer) - 1) { ns_info(ns_log_statistics, buffer); strcpy(buffer, header); } strcat(buffer, buffer2); } ns_info(ns_log_statistics, buffer); } static void nameserFree(void *uap) { struct nameser *ns = uap; memput(ns, sizeof *ns); } void ns_freestats(void) { if (nameserTree == NULL) return; tree_mung(&nameserTree, nameserFree); nameserInit = 0; } Index: head/contrib/bind/bin/named/ns_udp.c =================================================================== --- head/contrib/bind/bin/named/ns_udp.c (revision 60940) +++ head/contrib/bind/bin/named/ns_udp.c (revision 60941) @@ -1,124 +1,124 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_udp.c,v 8.8 1999/10/13 16:39:13 vixie Exp $"; +static const char rcsid[] = "$Id: ns_udp.c,v 8.9 2000/04/21 06:54:13 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" void ns_udp() { #if defined(CHECK_UDP_SUM) || defined(FIX_UDP_SUM) struct nlist nl[2]; int fd; int sum; u_long res, offset; nl[0].n_name = UDPSUM; nl[1].n_name = 0; if (nlist(KSYMS, nl)) { ns_debug(ns_log_default, 1, "ns_udp: nlist (%s,%s) failed", KSYMS, UDPSUM); return; } ns_debug(ns_log_default, 1, "ns_udp: %s %d %lu (%ld)", nl[0].n_name, nl[0].n_type, nl[0].n_value, nl[0].n_value); if (!nl[0].n_type) return; if ((fd = open(KMEM, O_RDWR, 0)) < 0) { ns_debug(ns_log_default, 1, "ns_udp: open %s failed: %s", KMEM, strerror(errno)); return; } offset = nl[0].n_value; #ifdef KMAP offset &= ((~0UL)>>1); #endif res = lseek(fd, offset, SEEK_SET); if (res != offset) { ns_debug(ns_log_default, 1, "ns_udp: lseek %lu failed %lu: %s", offset, res, strerror(errno)); goto cleanup; } if (read(fd, &sum, sizeof(sum)) != sizeof(sum)) { ns_debug(ns_log_default, 1, "ns_udp: read failed: %s", strerror(errno)); goto cleanup; } ns_debug(ns_log_default, 1, "ns_udp: %d", sum); if (sum == 0) { #ifdef FIX_UDP_SUM sum = 1; lseek(fd, offset, SEEK_SET); if (res != offset) { ns_debug(ns_log_default, 1, "ns_udp: lseek %lu failed %lu: %s", offset, res, strerror(errno)); goto cleanup; } if (write(fd, &sum, sizeof(sum)) != sizeof(sum)) { ns_debug(ns_log_default, 1, "ns_udp: write failed: %s", strerror(errno)); goto cleanup; } ns_warning(ns_log_default, "ns_udp: check sums turned on"); #else ns_panic(ns_log_default, 0, "ns_udp: checksums NOT turned on, exiting"); #endif } cleanup: close(fd); #endif } Index: head/contrib/bind/bin/named/ns_update.c =================================================================== --- head/contrib/bind/bin/named/ns_update.c (revision 60940) +++ head/contrib/bind/bin/named/ns_update.c (revision 60941) @@ -1,3004 +1,3019 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_update.c,v 8.68 1999/11/05 04:40:58 vixie Exp $"; +static const char rcsid[] = "$Id: ns_update.c,v 8.78 2000/04/23 02:19:00 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on the Dynamic DNS reference implementation by Viraj Bais * */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static ns_updque curupd; #define WRITEABLE_MASK (S_IWUSR | S_IWGRP | S_IWOTH) /* XXXRTH almost all funcs. in here should be static! map rdata_dump to db_to_textual map rdata_expand to wire_to_db make a textual_to_db and use it in merge_logs? replace all this "map" stuff with the new routines (from 4.9.5 I think) */ /* from ns_req.c */ static struct map m_opcode[] = { { "nxdomain", NXDOMAIN }, { "yxdomain", YXDOMAIN }, { "nxrrset", NXRRSET }, { "yxrrset", YXRRSET }, { "delete", DELETE }, { "add", ADD }, }; #define M_OPCODE_CNT (sizeof(m_opcode) / sizeof(struct map)) /* XXXRTH workaround map difficulties */ #define M_CLASS_CNT m_class_cnt #define M_TYPE_CNT m_type_cnt static char *opcodes[] = { "delete", "add", "", "nxdomain", "", "", "yxdomain", "yxrrset", "nxrrset", "", "", }; /* from db_load.c */ static struct map m_section[] = { { "zone", S_ZONE }, { "prereq", S_PREREQ }, { "update", S_UPDATE }, { "reserved", S_ADDT }, }; #define M_SECTION_CNT (sizeof(m_section) / sizeof(struct map)) /* Forward. */ static int rdata_expand(const u_char *, const u_char *, const u_char *, u_int, size_t, u_char *, size_t); static FILE * open_transaction_log(struct zoneinfo *zp) { - FILE *fp; - - fp = fopen(zp->z_updatelog, "a+"); + FILE *fp = fopen(zp->z_updatelog, "a+"); + if (fp == NULL) { ns_error(ns_log_update, "can't open %s: %s", zp->z_updatelog, strerror(errno)); return (NULL); } + (void) fchown(fileno(fp), user_id, group_id); + if (fseek(fp, 0L, SEEK_END) != 0) { + ns_error(ns_log_update, "can't fseek(%s, 0, SEEK_END)", + zp->z_updatelog); + fclose(fp); + return (NULL); + } if (ftell(fp) == 0L) { fprintf(fp, "%s", LogSignature); + zp->z_serial_ixfr_start = get_serial(zp); } + else + zp->z_serial_ixfr_start = 0; return (fp); } static FILE * open_ixfr_log(struct zoneinfo *zp) { - FILE *fp; - - fp = fopen(zp->z_ixfr_base, "a+"); + FILE *fp = fopen(zp->z_ixfr_base, "a+"); + if (fp == NULL) { ns_error(ns_log_update, "can't open %s: %s", zp->z_ixfr_base, strerror(errno)); return (NULL); } + (void) fchown(fileno(fp), user_id, group_id); + if (fseek(fp, 0L, SEEK_END) != 0) { + ns_error(ns_log_update, "can't fseek(%s, 0, SEEK_END)", + zp->z_ixfr_base); + fclose(fp); + return (NULL); + } if (ftell(fp) == 0L) { fprintf(fp, "%s", LogSignature); } return (fp); } static int close_transaction_log(struct zoneinfo *zp, FILE *fp) { if (fflush(fp) == EOF) { ns_error(ns_log_update, "fflush() of %s failed: %s", zp->z_updatelog, strerror(errno)); return (-1); } if (fsync(fileno(fp)) < 0) { ns_error(ns_log_update, "fsync() of %s failed: %s", zp->z_updatelog, strerror(errno)); return (-1); } if (fclose(fp) == EOF) { ns_error(ns_log_update, "fclose() of %s failed: %s", zp->z_updatelog, strerror(errno)); return (-1); } return (0); } static int close_ixfr_log(struct zoneinfo *zp, FILE *fp) { if (fflush(fp) == EOF) { ns_error(ns_log_update, "fflush() of %s failed: %s", zp->z_ixfr_base, strerror(errno)); fclose(fp); return (-1); } if (fsync(fileno(fp)) < 0) { ns_error(ns_log_update, "fsync() of %s failed: %s", zp->z_ixfr_base, strerror(errno)); fclose(fp); return (-1); } if (fclose(fp) == EOF) { ns_error(ns_log_update, "fclose() of %s failed: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } return (0); } /* * return true if 'db' had been added. */ static int was_added(const ns_updque *updlist, struct databuf *dp) { ns_updrec *rrecp; for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) if (rrecp->r_section == S_UPDATE && rrecp->r_dp == dp) return (1); return (0); } /* * return true if 'db' had been deleted. */ static int was_deleted(const ns_updque *updlist, struct databuf *dp) { ns_updrec *rrecp; struct databuf *adp; for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) if (rrecp->r_section == S_UPDATE && rrecp->r_deldp != NULL) { adp = rrecp->r_deldp; do { if (adp == dp) return (1); } while ((adp = adp->d_next) != NULL); } return (0); } /* * printupdatelog(srcaddr, updlist, hp, zp, old_serial) * append an ascii form to the zone's transaction log file. */ static void printupdatelog(struct sockaddr_in srcaddr, const ns_updque *updlist, HEADER *hp, struct zoneinfo *zp, u_int32_t old_serial) { struct databuf *dp; struct map *mp; ns_updrec *rrecp; int opcode; char time[25]; FILE *fp, *ifp; if (EMPTY(*updlist)) return; fp = open_transaction_log(zp); if (fp == NULL) return; ifp = open_ixfr_log(zp); if (ifp == NULL) { (void) close_transaction_log(zp, fp); return; } sprintf(time, "at %lu", (u_long)tt.tv_sec); fprintf(fp, "[DYNAMIC_UPDATE] id %u from %s %s (named pid %ld):\n", ntohs(hp->id), sin_ntoa(srcaddr), time, (long)getpid()); fprintf(ifp, "[DYNAMIC_UPDATE] id %u from %s %s (named pid %ld):\n", ntohs(hp->id), sin_ntoa(srcaddr), time, (long)getpid()); for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) { INSIST(zp == &zones[rrecp->r_zone]); switch (rrecp->r_section) { case S_ZONE: fprintf(fp, "zone:\torigin %s class %s serial %u\n", zp->z_origin, p_class(zp->z_class), old_serial); fprintf(ifp, "zone:\torigin %s class %s serial %u\n", zp->z_origin, p_class(zp->z_class), old_serial); break; case S_PREREQ: opcode = rrecp->r_opcode; fprintf(fp, "prereq:\t{%s} %s. %s ", opcodes[opcode], rrecp->r_dname, p_class(zp->z_class)); if (opcode == NXRRSET || opcode == YXRRSET) { fprintf(fp, "%s ", p_type(rrecp->r_type)); if ((dp = rrecp->r_dp) && dp->d_size > 0) { dp->d_class = zp->z_class; (void) rdata_dump(dp, fp); } } fprintf(fp, "\n"); break; case S_UPDATE: opcode = rrecp->r_opcode; /* * Translate all deletes into explict actions by * looking at what was actually deleted from the * zone for the ixfr log. */ dp = rrecp->r_deldp; while (dp != NULL) { if (dp->d_rcode == 0 && !was_added(updlist, dp)) { fprintf(ifp, "update:\t{%s} %s. %u %s %s ", "delete", rrecp->r_dname, dp->d_ttl, p_class(dp->d_class), p_type(dp->d_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n"); } dp = dp->d_next; } /* * Only successful adds should be recorded. * Don't add changes that are undone later. * SOA additions performed later. */ if (opcode == ADD && (dp = rrecp->r_dp) != NULL && dp->d_type != T_SOA && (dp->d_mark & D_MARK_ADDED) != 0 && !was_deleted(updlist, dp)) { fprintf(ifp, "update:\t{%s} %s. ", opcodes[opcode], rrecp->r_dname); fprintf(ifp, "%u ", rrecp->r_ttl); fprintf(ifp, "%s ", p_class(zp->z_class)); fprintf(ifp, "%s ", p_type(rrecp->r_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n"); } /* Update log. */ fprintf(fp, "update:\t{%s} %s. ", opcodes[opcode], rrecp->r_dname); if (opcode == ADD) fprintf(fp, "%u ", rrecp->r_ttl); fprintf(fp, "%s ", p_class(zp->z_class)); if (rrecp->r_type != T_ANY) fprintf(fp, "%s ", p_type(rrecp->r_type)); if ((dp = rrecp->r_dp) && dp->d_size > 0) { dp->d_class = zp->z_class; (void) rdata_dump(dp, fp); } fprintf(fp, "\n"); break; case S_ADDT: break; default: ns_panic(ns_log_update, 1, "printupdatelog - impossible condition"); /*NOTREACHED*/ } } /* * SOA additions must be last in this update as they * (or [INCR_SERIAL]) terminate an IXFR chunk. Only the last SOA * addition will be emitted for any dynamic update regardless * of the number of SOA changes in the update. */ for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) { INSIST(zp == &zones[rrecp->r_zone]); switch (rrecp->r_section) { case S_UPDATE: opcode = rrecp->r_opcode; if (opcode == ADD && (dp = rrecp->r_dp) != NULL && dp->d_type == T_SOA && (dp->d_mark & D_MARK_ADDED) != 0 && !was_deleted(updlist, dp)) { fprintf(ifp, "update:\t{%s} %s. ", opcodes[opcode], rrecp->r_dname); fprintf(ifp, "%u ", rrecp->r_ttl); fprintf(ifp, "%s ", p_class(zp->z_class)); fprintf(ifp, "%s ", p_type(rrecp->r_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n[END_DELTA]\n"); } break; default: break; } } fprintf(fp, "\n"); (void) close_transaction_log(zp, fp); (void) close_ixfr_log(zp, ifp); } static void cancel_soa_update(struct zoneinfo *zp) { ns_debug(ns_log_update, 3, "cancel_soa_update for %s", zp->z_origin); zp->z_flags &= ~Z_NEED_SOAUPDATE; zp->z_soaincrtime = 0; zp->z_updatecnt = 0; } /* * Figure out when a SOA serial number update should happen. * Returns non-zero if the caller should call sched_zone_maint(zp). */ int schedule_soa_update(struct zoneinfo *zp, int numupdated) { (void) gettime(&tt); zp->z_flags |= Z_NEED_SOAUPDATE; /* * Only z_deferupdcnt updates are allowed before we force * a serial update. */ zp->z_updatecnt += numupdated; if (zp->z_updatecnt >= zp->z_deferupdcnt) { - if (incr_serial(zp) < 0) { - ns_error(ns_log_update, - "error updating serial number for %s from %d", - zp->z_origin, zp->z_serial); - } else - return (0); - /* - * Note we continue scheduling if for some reason - * incr_serial fails. - */ + if (zp->z_soaincrtime > tt.tv_sec) { + zp->z_soaincrtime = tt.tv_sec; + return (1); + } } if (zp->z_soaincrintvl > 0) { /* We want automatic updates in this zone. */ if (zp->z_soaincrtime > 0) { /* Already scheduled. */ ns_debug(ns_log_update, 3, "schedule_soa_update('%s'): already scheduled", zp->z_origin); return (0); } else { /* First update since the soa was last incremented. */ zp->z_updatecnt = numupdated; zp->z_soaincrtime = tt.tv_sec + zp->z_soaincrintvl; /* * Never schedule soaincrtime to occur after * dumptime. */ if (zp->z_soaincrtime > zp->z_dumptime) zp->z_soaincrtime = zp->z_dumptime; ns_debug(ns_log_update, 3, "schedule_soa_update('%s'): scheduled for %lu", zp->z_origin, (u_long)zp->z_soaincrtime); return (1); } } return (0); } /* * Figure out when a zone dump should happen. * Returns non-zero if the caller should call sched_zone_maint(zp). */ int schedule_dump(struct zoneinfo *zp) { time_t half; (void) gettime(&tt); zp->z_flags |= Z_NEED_DUMP; if (zp->z_dumpintvl > 0) { /* We want automatic dumping in this zone. */ if (zp->z_dumptime > 0) { /* Already scheduled. */ ns_debug(ns_log_update, 3, "schedule_dump('%s'): already scheduled", zp->z_origin); return (0); } else { /* * Set new dump time for dynamic zone. Use a random * number in the last half of the dump limit; we want * it to be substantially correct while still * preventing dump synchronization among various * dynamic zones. */ half = (zp->z_dumpintvl + 1) / 2; zp->z_dumptime = tt.tv_sec + half + (rand() % half); /* * Never schedule soaincrtime to occur after * dumptime. */ if (zp->z_soaincrtime > zp->z_dumptime) zp->z_soaincrtime = zp->z_dumptime; ns_debug(ns_log_update, 3, "schedule_dump('%s'): scheduled for %lu", zp->z_origin, (u_long)zp->z_dumptime); return (1); } } return (0); } /* * int * process_prereq(rec, rcodep) * Process one prerequisite. * returns: * >0 prerequisite was satisfied. * =0 prerequisite was not satisfied, or an error occurred. * side effects: * sets *rcodep if an error occurs or prerequisite isn't satisfied. */ static int process_prereq(ns_updrec *ur, int *rcodep, u_int16_t zclass) { const char *dname = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; u_int32_t ttl = ur->r_ttl; struct databuf *rdp = ur->r_dp; const char *fname; struct hashbuf *htp; struct namebuf *np; struct databuf *dp; /* * An element in the list might have already been * processed if it is in the same RRset as a previous * RRset Exists (value dependent) prerequisite. */ if (rdp && (rdp->d_mark & D_MARK_FOUND) != 0) { /* Already processed. */ return (1); } if (ttl != 0) { ns_debug(ns_log_update, 1, "process_prereq: ttl!=0 in prereq section"); *rcodep = FORMERR; return (0); } htp = hashtab; np = nlookup(dname, &htp, &fname, 0); /* * Matching by wildcard not allowed here. * We need to post check for a wildcard match. */ if (fname != dname || (np != NULL && ns_wildcard(NAME(*np)) && (dname[0] != '*' || (dname[1] != '.' && dname[1] != '\0')))) np = NULL; if (class == C_ANY) { if (rdp->d_size) { ns_debug(ns_log_update, 1, "process_prereq: empty rdata required in prereq section with class=ANY"); *rcodep = FORMERR; return (0); } if (type == T_ANY) { /* Name is in use. */ ur->r_opcode = YXDOMAIN; if (np == NULL || np->n_data == NULL) { /* * Name does not exist or is * an empty nonterminal. */ ns_debug(ns_log_update, 1, "process_prereq: %s not in use", dname); *rcodep = NXDOMAIN; return (0); } } else { /* RRset exists (value independent). */ int found = 0; ur->r_opcode = YXRRSET; if (np != NULL) for (dp = np->n_data; dp && !found; dp = dp->d_next) if (match(dp, class, type) && dp->d_type == type) found = 1; if (!found) { ns_debug(ns_log_update, 1, "process_prereq: RRset (%s,%s,%s) does not exist", dname, p_type(type), p_class(zclass)); *rcodep = NXRRSET; return (0); } } } else if (class == C_NONE) { if (rdp->d_size) { ns_debug(ns_log_update, 1, "process_prereq: empty rdata required in prereq section with class=NONE"); *rcodep = FORMERR; return (0); } if (type == T_ANY) { /* Name is not in use. */ ur->r_opcode = NXDOMAIN; if (np != NULL && np->n_data != NULL) { /* * Name exists and is not an * empty nonterminal. */ ns_debug(ns_log_update, 1, "process_prereq: %s exists", dname); *rcodep = YXDOMAIN; return (0); } } else { /* RRset does not exist. */ int found = 0; ur->r_opcode = NXRRSET; class = zclass; if (np != NULL) for (dp = np->n_data; dp && !found; dp = dp->d_next) if (match(dp, class, type)) found = 1; if (found) { ns_debug(ns_log_update, 1, "process_prereq: RRset (%s,%s) exists", dname, p_type(type)); *rcodep = YXRRSET; return (0); } } } else if (class == zclass) { /* * RRset exists (value dependent). * * Check for RRset equality also. */ ns_updrec *tmp; ur->r_opcode = YXRRSET; if (!rdp) { ns_debug(ns_log_update, 1, "process_prereq: nonempty rdata required in prereq section with class=%s", p_class(class)); *rcodep = FORMERR; return (0); } - htp = hashtab; - np = nlookup(dname, &htp, &fname, 0); if (np == NULL || fname != dname) { *rcodep = NXRRSET; return (0); } for (dp = np->n_data; dp; dp = dp->d_next) { if (match(dp, class, type) && dp->d_type == type) { int found = 0; for (tmp = ur; tmp != NULL && !found; tmp = NEXT(tmp, r_link)) { if (tmp->r_section != S_PREREQ) break; if (!db_cmp(dp, tmp->r_dp)) { tmp->r_dp->d_mark |= D_MARK_FOUND; found = 1; } } if (!found) { *rcodep = NXRRSET; return (0); } } } for (tmp = ur; tmp != NULL; tmp = NEXT(tmp, r_link)) if (tmp->r_section == S_PREREQ && ns_samename(dname, tmp->r_dname) == 1 && tmp->r_class == class && tmp->r_type == type && (ur->r_dp->d_mark & D_MARK_FOUND) == 0) { *rcodep = NXRRSET; return (0); } else { tmp->r_opcode = YXRRSET; } } else { ns_debug(ns_log_update, 1, "process_prereq: incorrect class %s", p_class(class)); *rcodep = FORMERR; return (0); } /* Through the gauntlet, and out. */ return (1); } static int prescan_nameok(ns_updrec *ur, int *rcodep, u_int16_t zclass, struct zoneinfo *zp) { const char *dname = ur->r_dname; const char *owner = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; char *cp = (char *)ur->r_dp->d_data; enum context context; int ret = 1; /* We don't care about deletes */ if (ur->r_class != zclass) return (1); context = ns_ownercontext(type, primary_trans); if (!ns_nameok(NULL, owner, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; switch (type) { case ns_t_soa: context = hostname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_rp: context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_minfo: context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_ns: context = hostname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_ptr: context = ns_ptrcontext(owner); if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_naptr: /* * Order (2) * Preference (2) * Flags (1) */ cp += 5; /* Service (txt) */ cp += strlen(cp) + 1; /* Pattern (txt) */ cp += strlen(cp) + 1; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_srv: cp += 4; /* FALLTHROUGH */ case ns_t_mx: case ns_t_afsdb: case ns_t_rt: case ns_t_kx: cp += 2; context = hostname_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_px: cp += 2; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_sig: /* * Type covered (2) * Alg (1) * * Labels (1) * ttl (4) * expires (4) * signed (4) * footprint (2) */ cp += 18; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; case ns_t_nxt: context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, primary_trans, context, owner, inaddr_any)) goto refused; break; default: break; } return (1); refused: *rcodep = REFUSED; return (0); } /* * int * prescan_update(ur, rcodep) * Process one prerequisite. * returns: * >0 update looks OK (format wise; who knows if it will succeed?) * =0 update has something wrong with it. * side effects: * sets *rcodep if an error occurs or prerequisite isn't satisfied. */ static int prescan_update(ns_updrec *ur, int *rcodep, u_int16_t zclass) { const char *dname = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; u_int32_t ttl = ur->r_ttl; struct databuf *rdp = ur->r_dp; const char *fname; struct hashbuf *htp; struct namebuf *np; if (class == zclass) { if (!ns_t_rr_p(type)) { ns_debug(ns_log_update, 1, "prescan_update: invalid type (%s)", p_type(type)); *rcodep = FORMERR; return (0); } + if (ttl > MAXIMUM_TTL) { + ns_debug(ns_log_update, 1, + "prescan_update: invalid ttl (%u)", ttl); + *rcodep = FORMERR; + return (0); + } } else if (class == C_ANY) { if (ttl != 0 || rdp->d_size || (!ns_t_rr_p(type) && type != T_ANY)) { ns_debug(ns_log_update, 1, "prescan_update: formerr(#2)"); *rcodep = FORMERR; return (0); } } else if (class == C_NONE) { if (ttl != 0 || !ns_t_rr_p(type)) { ns_debug(ns_log_update, 1, "prescan_update: formerr(#3) %d %s", ttl, p_type(type)); *rcodep = FORMERR; return (0); } } else { ns_debug(ns_log_update, 1, "prescan_update: invalid class (%s)", p_class(class)); *rcodep = FORMERR; return (0); } /* No format errors found. */ return (1); } /* * int * process_updates(updlist, rcodep, from) * Process prerequisites and apply updates from the list to the database. * returns: * number of successful updates, 0 if none were successful. * side effects: * *rcodep gets the transaction return code. * can schedule maintainance for zone dumps and soa.serial# increments. */ static int process_updates(const ns_updque *updlist, int *rcodep, struct sockaddr_in from) { int i, j, n, dbflags, matches, zonenum; int numupdated = 0, soaupdated = 0, schedmaint = 0; u_int16_t zclass; ns_updrec *ur; const char *fname; struct databuf *dp, *savedp; struct zoneinfo *zp; int zonelist[MAXDNAME]; *rcodep = SERVFAIL; if (EMPTY(*updlist)) return (0); ur = HEAD(*updlist); if (ur->r_section == S_ZONE) { zclass = ur->r_class; zonenum = ur->r_zone; zp = &zones[zonenum]; } else { ns_debug(ns_log_update, 1, "process_updates: missing zone record"); return (0); } /* Process prereq records and prescan update records. */ for (ur = HEAD(*updlist); ur != NULL; ur = NEXT(ur, r_link)) { const char * dname = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; u_int32_t ttl = ur->r_ttl; struct databuf *rdp = ur->r_dp; u_int section = ur->r_section; ns_debug(ns_log_update, 3, "process_update: record section=%s, dname=%s, \ class=%s, type=%s, ttl=%d, dp=0x%0x", p_section(section, ns_o_update), dname, p_class(class), p_type(type), ttl, rdp); matches = findzone(dname, zclass, MAXDNAME, zonelist, MAXDNAME); ur->r_zone = 0; for (j = 0; j < matches && !ur->r_zone; j++) if (zonelist[j] == zonenum) ur->r_zone = zonelist[j]; if (!ur->r_zone || (section != S_ADDT && type == T_SOA && ns_samename(dname, zp->z_origin) != 1)) { ns_debug(ns_log_update, 1, "process_updates: record does not belong to the zone %s", zones[zonenum].z_origin); *rcodep = NOTZONE; return (0); } switch (section) { case S_ZONE: break; case S_PREREQ: if (!process_prereq(ur, rcodep, zclass)) return (0); /* *rcodep has been set. */ ns_debug(ns_log_update, 3, "prerequisite satisfied"); break; case S_UPDATE: if (!prescan_update(ur, rcodep, zclass)) return (0); /* *rcodep has been set. */ if (!prescan_nameok(ur, rcodep, zclass, zp)) return (0); /* *rcodep has been set. */ ns_debug(ns_log_update, 3, "update prescan succeeded"); break; case S_ADDT: break; default: ns_panic(ns_log_update, 1, "process_updates: impossible section"); /* NOTREACHED */ } } /* Now process the records in update section. */ for (ur = HEAD(*updlist); ur != NULL; ur = NEXT(ur, r_link)) { const char * dname = ur->r_dname; u_int16_t class = ur->r_class; if (ur->r_section != S_UPDATE) continue; dbflags = 0; savedp = NULL; dp = ur->r_dp; if (class == zp->z_class) { /* ADD databuf dp to hash table */ /* * Handling of various SOA/WKS/CNAME scenarios * is done in db_update(). */ ur->r_opcode = ADD; dbflags |= DB_NODATA | DB_REPLACE; n = db_update(dname, dp, dp, &savedp, dbflags, hashtab, from); if (!((n == OK) || ((zp->z_xferpid == XFER_ISIXFR) && (n == DATAEXISTS)))) { ns_debug(ns_log_update, 3, "process_updates: failed to add databuf (%d)", n); } else { ns_debug(ns_log_update, 3, "process_updates: added databuf 0x%0x", dp); dp->d_mark = D_MARK_ADDED; numupdated++; if (dp->d_type == T_SOA) soaupdated = 1; } } else if (class == C_ANY || class == C_NONE) { /* * DELETE databuf's matching dp from the hash table. * * handling of various SOA/NS scenarios done * in db_update(). */ ur->r_opcode = DELETE; /* * we know we're deleting now, and db_update won't * match with class==C_NONE, so we use the zone's * class. */ if (class == C_NONE) ur->r_dp->d_class = zp->z_class; dbflags |= DB_DELETE; n = db_update(dname, dp, NULL, &savedp, dbflags, hashtab, from); if (!((n == OK) || ((zp->z_xferpid == XFER_ISIXFR) && (n == NODATA)))) { ns_debug(ns_log_update, 3, "process_updates: delete failed"); } else { ns_debug(ns_log_update, 3, "process_updates: delete succeeded"); numupdated++; } } /* * Even an addition could have caused some deletions like * replacing old SOA or CNAME or WKS record or records of * lower cred/clev. * * We need to save the deleted databuf's in case we wish to * abort this update transaction and roll back all updates * applied from this packet. */ ur->r_deldp = savedp; } /* * If we got here, things are OK, so set rcodep to indicate so. */ *rcodep = NOERROR; if (!numupdated) return (0); /* * schedule maintenance for dumps and SOA.serial# increment * (this also sets Z_NEED_DUMP and Z_NEED_SOAUPDATE appropriately) */ schedmaint = 0; if (schedule_dump(zp)) schedmaint = 1; if (soaupdated) { /* * SOA updated by this update transaction, so * we need to set the zone serial number, stop any * automatic updates that may be pending, and send out * a NOTIFY message. */ zp->z_serial = get_serial_unchecked(zp); cancel_soa_update(zp); schedmaint = 1; #ifdef BIND_NOTIFY if (!loading) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif } else { if (schedule_soa_update(zp, numupdated)) schedmaint = 1; } if (schedmaint) sched_zone_maint(zp); return (numupdated); } static enum req_action req_update_private(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, struct qstream *qsp, int dfd, struct sockaddr_in from, struct tsig_record *in_tsig) { char dnbuf[MAXDNAME], *dname; u_int zocount, prcount, upcount, adcount, class, type, dlen; u_int32_t ttl; int i, n, cnt, found, matches, zonenum, numupdated = 0; int rcode = NOERROR; u_int c, section; u_char rdata[MAXDATA]; struct qinfo *qp; struct databuf *dp, *nsp[NSMAX]; struct databuf **nspp = &nsp[0]; struct zoneinfo *zp; ns_updrec *rrecp; int zonelist[MAXDNAME]; int should_use_tcp; u_int32_t old_serial; int unapproved_ip = 0; int tsig_len; DST_KEY *in_key = (in_tsig != NULL) ? in_tsig->key : NULL; nsp[0] = NULL; zocount = ntohs(hp->qdcount); prcount = ntohs(hp->ancount); upcount = ntohs(hp->nscount); adcount = ntohs(hp->arcount); /* Process zone section. */ ns_debug(ns_log_update, 3, "req_update: section ZONE, count %d", zocount); if ((n = dn_expand(msg, eom, cp, dnbuf, sizeof(dnbuf))) < 0) { ns_debug(ns_log_update, 1, "req_update: expand name failed"); hp->rcode = FORMERR; return (Finish); } dname = dnbuf; cp += n; if (cp + 2 * INT16SZ > eom) { ns_debug(ns_log_update, 1, "req_update: too short"); hp->rcode = FORMERR; return (Finish); } GETSHORT(type, cp); GETSHORT(class, cp); if (zocount != 1 || type != T_SOA) { ns_debug(ns_log_update, 1, "req_update: incorrect count or type for zone section: %d", zocount); hp->rcode = FORMERR; return (Finish); } matches = findzone(dname, class, 0, zonelist, MAXDNAME); if (matches == 1) { zonenum = zonelist[0]; zp = &zones[zonenum]; if (zp->z_class != (int)class || (zp->z_type != z_master && zp->z_type != z_slave)) matches = 0; } if (matches != 1) { ns_debug(ns_log_update, 1, "req_update: non-authoritative server for %s", dname); hp->rcode = NOTAUTH; return (Finish); } /* * Begin Access Control Point */ if (!ip_addr_or_key_allowed(zp->z_update_acl, from.sin_addr, in_key)) { - ns_notice(ns_log_security, "unapproved update from %s for %s", + ns_notice(ns_log_security, "denied update from %s for %s", sin_ntoa(from), *dname ? dname : "."); + nameserIncr(from.sin_addr, nssRcvdUUpd); return (Refuse); } /* * End Access Control Point */ /* we should be authoritative */ if (!(zp->z_flags & Z_AUTH)) { ns_debug(ns_log_update, 1, "req_update: zone %s: Z_AUTH not set", dname); hp->rcode = NOTAUTH; return (Finish); } if (zp->z_type == Z_SECONDARY) { /* * XXX The code below is broken. - * Until fixed, we just refuse. + * Until fixed, we just return NOTIMPL. */ #if 1 - return (Refuse); + hp->rcode = ns_r_notimpl; + return (Finish); #else /* We are a slave for this zone, forward it to the master. */ for (cnt = 0; cnt < zp->z_addrcnt; cnt++) *nspp++ = savedata(zp->z_class, T_A, USE_MINIMUM, (u_char *)&zp->z_addr[cnt].s_addr, INT32SZ); *nspp = NULL; /* * If the request came in over TCP, forward it over TCP */ should_use_tcp = (qsp != NULL); if (in_tsig != NULL) { tsig_len = ns_skiprr(eom, eom + TSIG_BUF_SIZE, ns_s_ar, 1); eom += tsig_len; } n = ns_forw(nsp, msg, eom-msg, from, qsp, dfd, &qp, dname, class, type, NULL, should_use_tcp, NULL); if (in_tsig != NULL) eom -= tsig_len; free_nsp(nsp); switch (n) { - case FW_OK: - case FW_DUP: + case FW_OK: + case FW_DUP: return (Return); - case FW_NOSERVER: + case FW_NOSERVER: /* should not happen */ - case FW_SERVFAIL: + case FW_SERVFAIL: hp->rcode = SERVFAIL; return (Finish); } #endif } /* * We are the primary master server for this zone, * proceed further and process update packet */ if (!(zp->z_flags & Z_DYNAMIC)) { ns_debug(ns_log_update, 1, "req_update: dynamic flag not set for zone %s", dname); return (Refuse); } old_serial = get_serial(zp); ns_debug(ns_log_update, 3, "req_update: update request for zone %s, class %s", zp->z_origin, p_class(class)); rrecp = res_mkupdrec(S_ZONE, dname, class, type, 0); rrecp->r_zone = zonenum; APPEND(curupd, rrecp, r_link); /* * Parse the prerequisite and update sections for format errors. */ for (i = 0; (u_int)i < prcount + upcount; i++) { if ((n = dn_expand(msg, eom, cp, dnbuf, sizeof(dnbuf))) < 0) { ns_debug(ns_log_update, 1, "req_update: expand name failed"); hp->rcode = FORMERR; return (Finish); } dname = dnbuf; cp += n; if (cp + RRFIXEDSZ > eom) { ns_debug(ns_log_update, 1, "req_update: overrun in answer"); hp->rcode = FORMERR; return (Finish); } GETSHORT(type, cp); GETSHORT(class, cp); if (class > CLASS_MAX) { ns_debug(ns_log_update, 1, "req_update: bad class"); hp->rcode = FORMERR; return (Finish); } GETLONG(ttl, cp); GETSHORT(dlen, cp); n = 0; dp = NULL; if (dlen > 0) { if (cp + dlen > eom) { ns_debug(ns_log_update, 1, "req_update: bad dlen"); hp->rcode = FORMERR; return (Finish); } n = rdata_expand(msg, eom, cp, type, dlen, rdata, sizeof rdata); if (n == 0 || n > MAXDATA) { ns_debug(ns_log_update, 1, "req_update: failed to expand record"); hp->rcode = FORMERR; return (Finish); } cp += dlen; } section = ((u_int)i < prcount) ? S_PREREQ : S_UPDATE; rrecp = res_mkupdrec(section, dname, class, type, ttl); dp = savedata(class, type, ttl, rdata, n); dp->d_zone = zonenum; dp->d_cred = DB_C_ZONE; dp->d_secure = DB_S_INSECURE; /* should be UNCHECKED */ dp->d_clev = nlabels(zp->z_origin); /* XXX - also record in dp->d_ns, which host this came from */ rrecp->r_dp = dp; /* Append the current record to the end of list of records. */ APPEND(curupd, rrecp, r_link); - if (cp > eom) { + if (cp > eom) { ns_info(ns_log_update, "Malformed response from %s (overrun)", inet_ntoa(from.sin_addr)); hp->rcode = FORMERR; return (Finish); } } /* Now process all parsed records in the prereq and update sections. */ numupdated = process_updates(&curupd, &rcode, from); hp->rcode = rcode; if (numupdated <= 0) { if (rcode != NOERROR) ns_error(ns_log_update, "error processing update packet (%s) id %d from %s", p_rcode(rcode), ntohs(hp->id), sin_ntoa(from)); return (Finish); } /* * Stop any outbound zone transfers. * (Eventlib is synchronous for this.) */ ns_stopxfrs(zp); /* Make a log of the update. */ (void) printupdatelog(from, &curupd, hp, zp, old_serial); return (Finish); } void free_rrecp(ns_updque *updlist, int rcode, struct sockaddr_in from) { ns_updrec *rrecp, *first_rrecp, *next_rrecp; struct databuf *dp, *tmpdp; char *dname, *msg; if (rcode == NOERROR) { first_rrecp = HEAD(*updlist); msg = "free_rrecp: update transaction succeeded, cleaning up"; } else { first_rrecp = TAIL(*updlist); msg = "free_rrecp: update transaction aborted, rolling back"; } ns_debug(ns_log_update, 1, msg); for (rrecp = first_rrecp; rrecp != NULL; rrecp = next_rrecp) { if (rcode == NOERROR) next_rrecp = NEXT(rrecp, r_link); else next_rrecp = PREV(rrecp, r_link); if (rrecp->r_section != S_UPDATE) { if (rrecp->r_dp) db_freedata(rrecp->r_dp); res_freeupdrec(rrecp); continue; } dname = rrecp->r_dname; dp = rrecp->r_dp; if ((dp->d_mark & D_MARK_ADDED) != 0) { if (rcode == NOERROR) { /* * This databuf is now a part of hashtab, * or has been deleted by a subsequent update. * Either way, we must not free it. */ dp->d_mark &= ~D_MARK_ADDED; } else { /* Delete the databuf. */ if (db_update(dname, dp, NULL, NULL, DB_DELETE, hashtab, from) != OK) { ns_error(ns_log_update, "free_rrecp: failed to delete databuf: dname=%s, type=%s", dname, p_type(dp->d_type)); } else { ns_debug(ns_log_update, 3, "free_rrecp: deleted databuf 0x%0x", dp); /* * XXXRTH * * We used to db_freedata() here, * but I removed it because 'dp' was * part of a hashtab before we called * db_update(), and since our delete * has succeeded, it should have been * freed. */ } } } else { /* * Databuf's matching this were deleted by this * update, or were never executed (because we bailed * out early). */ db_freedata(dp); } /* Process deleted databuf's. */ dp = rrecp->r_deldp; while (dp != NULL) { tmpdp = dp; dp = dp->d_next; if (rcode == NOERROR) { if (tmpdp->d_rcnt) ns_debug(ns_log_update, 1, "free_rrecp: type = %d, rcnt = %d", p_type(tmpdp->d_type), tmpdp->d_rcnt); else { tmpdp->d_next = NULL; db_freedata(tmpdp); } } else { /* Add the databuf back. */ tmpdp->d_mark &= ~D_MARK_DELETED; if (db_update(dname, tmpdp, tmpdp, NULL, DB_REPLACE, hashtab, from) != OK) { ns_error(ns_log_update, "free_rrecp: failed to add back databuf: dname=%s, type=%s", dname, p_type(tmpdp->d_type)); } else { ns_debug(ns_log_update, 3, "free_rrecp: added back databuf 0x%0x", tmpdp); } } } res_freeupdrec(rrecp); } INIT_LIST(*updlist); } enum req_action req_update(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, struct qstream *qsp, int dfd, struct sockaddr_in from, struct tsig_record *in_tsig) { enum req_action ret; INIT_LIST(curupd); ret = req_update_private(hp, cp, eom, msg, qsp, dfd, from, in_tsig); free_rrecp(&curupd, ret == Refuse ? ns_r_refused : hp->rcode, from); if (ret == Finish) { hp->qdcount = hp->ancount = hp->nscount = hp->arcount = 0; memset(msg + HFIXEDSZ, 0, (eom - msg) - HFIXEDSZ); } return (ret); } /* * expand rdata portion of a compressed resource record at cp into cp1 * and return the length of the expanded rdata (length of the compressed * rdata is "dlen"). */ static int rdata_expand(const u_char *msg, const u_char *eom, const u_char *cp, u_int type, size_t dlen, u_char *cp1, size_t size) { const u_char *cpinit = cp; const u_char *cp1init = cp1; int n, i, n1; switch (type) { case T_A: case T_AAAA: if ((type == T_A && dlen != INT32SZ) || (type == T_AAAA && dlen != NS_IN6ADDRSZ)) return (0); /*FALLTHROUGH*/ case T_WKS: case T_HINFO: case T_TXT: case T_X25: case T_ISDN: case T_NSAP: case T_LOC: case T_KEY: case ns_t_cert: if (size < dlen) return (0); memcpy(cp1, cp, dlen); return (dlen); case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0 || (u_int)n != dlen) return (0); return (strlen((char *)cp1) + 1); case T_MINFO: case T_SOA: case T_RP: /* Get two compressed domain names. */ for (i = 0; i < 2; i++) { n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; size -= n; } if (type == T_SOA) { n = 5 * INT32SZ; if (size < (size_t)n || cp + n > eom) return(0); size -= n; memcpy(cp1, cp, n); cp += n; cp1 += n; } if (cp != cpinit + dlen) return (0); return (cp1 - cp1init); case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* Grab preference. */ if (size < INT16SZ || cp + INT16SZ > eom) return (0); size -= INT16SZ; memcpy(cp1, cp, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; if (type == T_SRV) { if (size < INT16SZ*2 || cp + INT16SZ*2 > eom) return (0); size -= INT16SZ*2; /* Grab weight and port. */ memcpy(cp1, cp, INT16SZ*2); cp1 += INT16SZ*2; cp += INT16SZ*2; } /* Get name. */ n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; if (cp != cpinit + dlen) return (0); return (cp1 - cp1init); case T_PX: /* Grab preference. */ if (size < INT16SZ || cp + INT16SZ > eom) return (0); size -= INT16SZ; memcpy(cp1, cp, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; /* Get MAP822 name. */ n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; size -= n; n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; if (cp != cpinit + dlen) return (0); return (cp1 - cp1init); case T_SIG: if (dlen < SIG_HDR_SIZE || size < dlen) return (0); memcpy(cp1, cp, SIG_HDR_SIZE); size -= SIG_HDR_SIZE; cp += SIG_HDR_SIZE; cp1 += SIG_HDR_SIZE; n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0 || n + SIG_HDR_SIZE > dlen) return (0); cp += n; n1 = dlen - n - SIG_HDR_SIZE; n = strlen((char *)cp1) + 1; cp1 += n; if (size < n1) return (0); memcpy(cp1, cp, n1); cp1 += n1; return (cp1 - cp1init); case T_NXT: n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0 || (u_int)n >= dlen) return (0); size -= n; cp += n; n1 = dlen - n; n = strlen((char *)cp1) + 1; cp1 += n; /* * The first bit of the first octet determines the format * of the NXT record. A format for types >= 128 has not * yet been defined, so if bit zero is set, we just copy * what's there because we don't understand it. */ if ((*cp & 0x80) == 0) { /* * Bit zero is not set; this is an ordinary NXT * record. The bitmap must be at least 4 octets * because the NXT bit should be set. It should be * less than or equal to 16 octets because this NXT * format is only defined for types < 128. */ if (n1 < 4 || n1 > 16) return (0); } if (n1 > size) return (0); memcpy(cp1, cp, n1); cp1 += n1; return (cp1 - cp1init); default: ns_debug(ns_log_update, 3, "unknown type %d", type); return (0); } } /* * Print out rdata portion of a resource record from a databuf into a file. * * XXX - similar code in db_dump() should be replaced by a call to this * function. */ void rdata_dump(struct databuf *dp, FILE *fp) { u_int32_t n, addr; u_char *cp, *end; int i, j; const char *proto; u_char *savecp; char temp_base64[NS_MD5RSA_MAX_BASE64]; u_int16_t keyflags; u_char *sigdata, *certdata; cp = (u_char *)dp->d_data; switch (dp->d_type) { case T_A: switch (dp->d_class) { case C_IN: case C_HS: GETLONG(n, cp); n = htonl(n); fputs(inet_ntoa(*(struct in_addr *)&n), fp); break; } if (dp->d_nstime) fprintf(fp, ";\tNT=%d", dp->d_nstime); break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: fprintf(fp, "%s.", cp); break; case T_NS: cp = (u_char *)dp->d_data; if (cp[0] == '\0') fprintf(fp, ".\t"); else fprintf(fp, "%s.", cp); break; case T_HINFO: case T_ISDN: if ((n = *cp++) != '\0') { fprintf(fp, "\"%.*s\"", (int)n, cp); cp += n; } else fprintf(fp, "\"\""); if ((n = *cp++) != '\0') fprintf(fp, " \"%.*s\"", (int)n, cp); else fprintf(fp, " \"\""); break; case T_SOA: fprintf(fp, "%s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s. ( ", cp); #if defined(RETURNSOA) && defined(NCACHE) if (dp->d_rcode == NXDOMAIN) fputs(";", fp); #endif cp += strlen((char *)cp) + 1; GETLONG(n, cp); fprintf(fp, "%u", n); GETLONG(n, cp); fprintf(fp, " %u", n); GETLONG(n, cp); fprintf(fp, " %u", n); GETLONG(n, cp); fprintf(fp, " %u", n); GETLONG(n, cp); fprintf(fp, " %u )", n); #if defined(RETURNSOA) && defined(NCACHE) if (dp->d_rcode == NXDOMAIN) fprintf(fp, ";%s.;NXDOMAIN;\t-$", cp); #endif break; case T_MX: case T_AFSDB: case T_RT: GETSHORT(n, cp); fprintf(fp, "%u", n); fprintf(fp, " %s.", cp); break; case T_SRV: GETSHORT(n, cp); /* priority */ fprintf(fp, "%u ", n); GETSHORT(n, cp); /* weight */ fprintf(fp, "%u ", n); GETSHORT(n, cp); /* port */ fprintf(fp, "%u ", n); fprintf(fp, " %s.", cp); break; case T_PX: GETSHORT(n, cp); fprintf(fp, "%u", n); fprintf(fp, " %s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s.", cp); break; case T_TXT: case T_X25: end = (u_char *)dp->d_data + dp->d_size; (void) putc('"', fp); while (cp < end) { if ((n = *cp++) != '\0') { for (j = n; j > 0 && cp < end; j--) if ((*cp < ' ') || (*cp > '~')) { fprintf(fp, "\\%03.3d", *cp++); } else if (*cp == '\\' || *cp =='"') { putc('\\', fp); putc(*cp++, fp); } else (void) putc(*cp++, fp); } if (cp != end) fputs("\" \"", fp); } /* XXXVIX need to keep the segmentation (see 4.9.5). */ (void) fputs("\"", fp); break; case T_NSAP: (void) fputs(inet_nsap_ntoa(dp->d_size, dp->d_data, NULL), fp); break; case T_LOC: (void) fputs(loc_ntoa(dp->d_data, NULL), fp); break; case T_WKS: GETLONG(addr, cp); addr = htonl(addr); fputs(inet_ntoa(*(struct in_addr *)&addr), fp); proto = protocolname((u_char)*cp); cp += sizeof(char); fprintf(fp, "%s ", proto); i = 0; while(cp < (u_char *)dp->d_data + dp->d_size) { j = *cp++; do { if (j & 0200) fprintf(fp, " %s", servicename(i, proto)); j <<= 1; } while (++i & 07); } break; case T_MINFO: case T_RP: fprintf(fp, "%s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s.", cp); break; case T_KEY: savecp = cp; /* save the beginning */ /*>>> Flags (unsigned_16) */ NS_GET16(keyflags,cp); fprintf(fp, "0x%04x ", keyflags); /*>>> Protocol (8-bit decimal) */ fprintf(fp, "%3u ", *cp++); /*>>> Algorithm id (8-bit decimal) */ fprintf(fp, "%3u ", *cp++); /*>>> Public-Key Data (multidigit BASE64) */ /* containing ExponentLen, Exponent, and Modulus */ i = b64_ntop(cp, dp->d_size - (cp - savecp), temp_base64, sizeof temp_base64); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case T_SIG: sigdata = cp; /* RRtype (char *) */ NS_GET16(n,cp); fprintf(fp, "%s ", p_type(n)); /* Algorithm id (8-bit decimal) */ fprintf(fp, "%d ", *cp++); /* Labels (8-bit decimal) (not saved in file) */ /* XXXX FIXME -- check value and print err if bad */ cp++; /* OTTL (u_long) */ NS_GET32(n, cp); fprintf(fp, "%u ", n); /* Texp (u_long) */ NS_GET32(n, cp); fprintf(fp, "%s ", p_secstodate (n)); /* Tsig (u_long) */ NS_GET32(n, cp); fprintf(fp, "%s ", p_secstodate (n)); /* Kfootprint (unsigned_16) */ NS_GET16(n, cp); fprintf(fp, "%u ", n); /* Signer's Name (char *) */ fprintf(fp, "%s ", cp); cp += strlen((char *)cp) + 1; /* Signature (base64 of any length) */ i = b64_ntop(cp, dp->d_size - (cp - sigdata), temp_base64, sizeof temp_base64); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case T_NXT: fprintf(fp, "%s.", cp); n = strlen ((char *)cp) + 1; cp += n; i = 8 * (dp->d_size - n); /* How many bits? */ for (n = 0; n < (u_int32_t)i; n++) { if (NS_NXT_BIT_ISSET(n, cp)) fprintf(fp," %s",__p_type(n)); } break; case ns_t_cert: certdata = cp; NS_GET16(n,cp); fprintf(fp, "%d ", n); /* cert type */ NS_GET16(n,cp); fprintf(fp, "%d %d ", n, *cp++); /* tag & alg */ /* Certificate (base64 of any length) */ i = b64_ntop(cp, dp->d_size - (cp - certdata), temp_base64, sizeof(temp_base64)); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case ns_t_aaaa: { char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; (void) fputs(inet_ntop(AF_INET6, dp->d_data, t, sizeof t), fp); break; } default: fprintf(fp, "\t;?d_type=%d?", dp->d_type); } } /* * Return the number of authoritative zones that "dname" could belong to by * stripping up to "depth" labels from dname. Up to the first "maxzones" * authoritative zone numbers will be stored in "zonelist", ordered * deepest match first. */ int findzone(const char *dname, int class, int depth, int *zonelist, int maxzones){ char *tmpdname; char tmpdnamebuf[MAXDNAME]; char *zonename, *cp; int tmpdnamelen, zonenamelen, zonenum, i, j, c; int matches = 0; int escaped, found, done; ns_debug(ns_log_update, 4, "findzone(dname=%s, class=%d, depth=%d, \ zonelist=0x%x, maxzones=%d)", dname, class, depth, zonelist, maxzones); #ifdef DEBUG if (debug >= 5) { ns_debug(ns_log_update, 5, "zone dump:"); for (zonenum = 1; zonenum < nzones; zonenum++) printzoneinfo(zonenum, ns_log_update, 5); } #endif strcpy(tmpdnamebuf, dname); tmpdname = tmpdnamebuf; /* * The code to handle trailing dots and escapes is adapted * from ns_samedomain(). */ tmpdnamelen = strlen(tmpdname); /* * Ignore a trailing label separator (i.e. an unescaped dot) * in 'tmpdname'. */ if (tmpdnamelen && tmpdname[tmpdnamelen-1] == '.') { escaped = 0; /* note this loop doesn't get executed if tmpdnamelen==1 */ for (j = tmpdnamelen - 2; j >= 0; j--) if (tmpdname[j] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else { break; } if (!escaped) { tmpdnamelen--; tmpdname[tmpdnamelen] = '\0'; } } for (done = i = 0; i <= depth && !done; i++) { for (zonenum = 1; zonenum < nzones; zonenum++) { if (zones[zonenum].z_type == z_nil) continue; if (zones[zonenum].z_class != class) continue; zonename = zones[zonenum].z_origin; zonenamelen = strlen(zonename); /* * Ignore a trailing label separator * (i.e. an unescaped dot) in 'zonename'. */ if (zonenamelen && zonename[zonenamelen-1] == '.') { escaped = 0; for (j = zonenamelen - 2; j >= 0; j--) if (zonename[j] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else { break; } if (!escaped) zonenamelen--; } if (tmpdnamelen != zonenamelen) continue; ns_debug(ns_log_update, 5, "about to strncasecmp('%s', '%s', %d)", tmpdname, zonename, tmpdnamelen); /* XXXRTH I'm doing a special test for zonenamelen == 0 because I worry that some implementations of strncasecmp might not handle comparisions where n==0 correctly */ if (zonenamelen == 0 || !strncasecmp(tmpdname, zonename, tmpdnamelen)) { ns_debug(ns_log_update, 5, "match"); zonelist[matches++] = zonenum; if (matches == maxzones) { /* XXX should signal error */ return (matches); } } } /* * Strip off the first label if we're not already at * the root label. */ if (*tmpdname != '\0') { for (escaped = found = 0; (c = *tmpdname) && !found; tmpdname++) { if (!escaped && (c == '.')) /* * Note the loop increment will * make tmpdname point past the '.' * before the '!found' test causes * us to exit the loop. */ found = 1; if (escaped) escaped = 0; else if (c == '\\') escaped = 1; } } else done = 1; tmpdnamelen = strlen(tmpdname); } ns_debug(ns_log_update, 4, "findzone: returning %d match(es)", matches); return (matches); } /* * reapply lost updates from log file for the zone to the zone * * returns -1 on error, 0 on success, 1 if dump reload needed */ int merge_logs(struct zoneinfo *zp, char *logname) { char origin[MAXDNAME], data[MAXDATA], dnbuf[MAXDNAME], sclass[3]; char buf[BUFSIZ], buf2[100]; FILE *fp; u_int32_t serial, ttl, old_serial, new_serial; char *dname, *cp, *cp1; int type, class; int i, c, section, opcode, matches, zonenum, err, multiline; int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; int id, rcode = NOERROR; u_int32_t n; struct map *mp; ns_updrec *rrecp; struct databuf *dp; struct in_addr ina; int zonelist[MAXDNAME]; struct stat st; u_char *serialp; struct sockaddr_in empty_from; int datasize; unsigned long l; empty_from.sin_family = AF_INET; empty_from.sin_addr.s_addr = htonl(INADDR_ANY); empty_from.sin_port = htons(0); /* XXX - much of this stuff is similar to that in nsupdate.c * getword_str() was used in nsupdate.c for reasons described there * getword() is used here just to be consistent with db_load() */ ns_debug(ns_log_update, 3, "merge_logs(%s)", logname); /* If there is no log file, just return. */ if (stat(logname, &st) < 0) { if (errno != ENOENT) ns_error(ns_log_update, "unexpected stat(%s) failure: %s", logname, strerror(errno)); return (-1); } fp = fopen(logname, "r"); if (fp == NULL) { ns_error(ns_log_update, "fopen(%s) failed: %s", logname, strerror(errno)); return (-1); } /* * See if we really have a log file -- it might be a zone dump * that was in the process of being movefiled, or it might * be garbage! */ if (fgets(buf, sizeof(buf), fp)==NULL) { ns_error(ns_log_update, "fgets() from %s failed: %s", logname, strerror(errno)); fclose(fp); return (-1); } if (strcmp(buf, DumpSignature) == 0) { /* It's a dump; finish movefile that was interrupted. */ ns_info(ns_log_update, "completing interrupted dump movefile for %s", zp->z_source); fclose(fp); if (movefile(logname, zp->z_source) < 0) { ns_error(ns_log_update, "movefile(%s,%s) failed: %s :1", logname, zp->z_source, strerror(errno)); fclose(fp); return (-1); } /* Finally, tell caller to reload zone. */ return (1); } if (strcmp(buf, LogSignature) != 0) { /* Not a dump and not a log; complain and then bail out. */ ns_error(ns_log_update, "invalid log file %s", logname); fclose(fp); return (-1); } ns_debug(ns_log_update, 3, "merging logs for %s from %s", zp->z_origin, logname); lineno = 1; INIT_LIST(curupd); for (;;) { err = 0; if (!getword(buf, sizeof buf, fp, 0)) { if (lineno == (nonempty_lineno + 1) && !(feof(fp))) { /* * End of a nonempty line inside an update * packet or not inside an update packet. */ continue; } /* * Empty line or EOF. * * Marks completion of current update packet. */ inside_next = 0; prev_pktdone = 1; cont = 1; } else { nonempty_lineno = lineno; } if (!strcasecmp(buf, "[DYNAMIC_UPDATE]") || !strcasecmp(buf, "[IXFR_UPDATE]")) { err = 0; rcode = NOERROR; cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *)cp, "id %d", &id)) id = -1; inside_next = 1; prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { /* XXXRTH not enough error checking here */ cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *)cp, "from %u to %u", &old_serial, &new_serial)) { ns_error(ns_log_update, "incr_serial problem with %s", logname); } else { serial = get_serial(zp); if (serial != old_serial) { ns_error(ns_log_update, "serial number mismatch (log=%u, zone=%u) in %s", old_serial, serial, logname); } else { set_serial(zp, new_serial); /* * The zone has changed; make sure * a dump is scheduled. */ (void)schedule_dump(zp); sched_zone_maint(zp); ns_info(ns_log_update, "set serial to %u (log file %s)", new_serial, logname); } } prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[END_DELTA]")) { prev_pktdone = 1; cont = 1; } if (prev_pktdone) { if (!EMPTY(curupd)) { n = process_updates(&curupd, &rcode, empty_from); if (n > 0) ns_info(ns_log_update, "successfully merged update id %d from log file %s", id, logname); else { ns_error(ns_log_update, "error merging update id %d from log file %s", id, logname); return(-1); } free_rrecp(&curupd, rcode, empty_from); } prev_pktdone = 0; if (feof(fp)) break; } if (cont) { cont = 0; continue; } if (!inside_next) continue; /* * inside the same update packet, * continue accumulating records. */ section = -1; n = strlen(buf); if (buf[n-1] == ':') buf[--n] = '\0'; for (mp = m_section; mp < m_section+M_SECTION_CNT; mp++) if (!strcasecmp(buf, mp->token)) { section = mp->val; break; } ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; switch (section) { case S_ZONE: cp = fgets(buf, sizeof buf, fp); if (!cp) *buf = '\0'; n = sscanf(cp, "origin %s class %s serial %ul", origin, sclass, &serial); if (n != 3 || ns_samename(origin, zp->z_origin) != 1) err++; if (cp) lineno++; if (!err && serial != zp->z_serial) { ns_error(ns_log_update, "serial number mismatch in update id %d (log=%u, zone=%u) in %s", id, serial, zp->z_serial, logname); inside_next = 0; err++; } if (!err && inside_next) { int success; dname = origin; type = T_SOA; class = sym_ston(__p_class_syms, sclass, &success); if (!success) { err++; break; } matches = findzone(dname, class, 0, zonelist, MAXDNAME); if (matches) zonenum = zonelist[0]; else err++; } break; case S_PREREQ: case S_UPDATE: /* Operation code. */ if (!getword(buf, sizeof buf, fp, 0)) { err++; break; } opcode = -1; if (buf[0] == '{') { n = strlen(buf); for (i = 0; (u_int32_t)i < n; i++) buf[i] = buf[i+1]; if (buf[n-2] == '}') buf[n-2] = '\0'; } for (mp = m_opcode; mp < m_opcode+M_OPCODE_CNT; mp++) if (!strcasecmp(buf, mp->token)) { opcode = mp->val; break; } if (opcode == -1) { err++; break; } /* Owner's domain name. */ if (!getword((char *)dnbuf, sizeof dnbuf, fp, 0)) { err++; break; } n = strlen((char *)dnbuf) - 1; if (dnbuf[n] == '.') dnbuf[n] = '\0'; dname = dnbuf; ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; (void) getword(buf, sizeof buf, fp, 1); if (isdigit(buf[0])) { /* ttl */ if (ns_parse_ttl(buf, &l) < 0) { err++; break; } ttl = l; (void) getword(buf, sizeof buf, fp, 1); } /* possibly class */ if (buf[0] != '\0') { int success; int maybe_class; maybe_class = sym_ston(__p_class_syms, buf, &success); if (success) { class = maybe_class; (void) getword(buf, sizeof buf, fp, 1); } } /* possibly type */ if (buf[0] != '\0') { int success; int maybe_type; maybe_type = sym_ston(__p_type_syms, buf, &success); if (success) { type = maybe_type; (void) getword(buf, sizeof buf, fp, 1); } } if (buf[0] != '\0') /* possibly rdata */ /* * Convert the ascii data 'buf' to the proper * format based on the type and pack into * 'data'. * * XXX - same as in db_load(), * consolidation needed */ switch (type) { case T_A: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); n = INT32SZ; break; case T_HINFO: case T_ISDN: n = strlen(buf); data[0] = n; memcpy(data+1, buf, n); n++; if (!getword(buf, sizeof buf, fp, 0)) { i = 0; } else { endline(fp); i = strlen(buf); } data[n] = i; memcpy(data+n+1, buf, i); break; case T_SOA: case T_MINFO: case T_RP: (void) strcpy(data, buf); cp = data + strlen(data) -1; *(cp++) = 0; /* ditch dot */ if (!getword((char *)cp, sizeof data - (cp - data), fp, 1)) { err++; break; } cp += strlen((char *)cp) -1; *(cp++) = 0; /* ditch dot */ if (type != T_SOA) { n = cp - data; break; } else n = cp - data; if (class != zp->z_class || ns_samename(dname, zp->z_origin) != 1) { err++; break; } c = getnonblank(fp, logname); if (c == '(') { multiline = 1; } else { multiline = 0; ungetc(c, fp); } n = getnum(fp, logname, GETNUM_SERIAL); if (getnum_error) { err++; break; } PUTLONG(n, cp); for (i = 0; i < 4; i++) { if (getttl(fp, logname, lineno, &n, &multiline) <= 0) { err++; break; } PUTLONG(n, cp); } if (multiline && (getnonblank(fp, logname) != ')')) { err++; break; } n = cp - data; endline(fp); break; case T_WKS: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); *cp = (char)getprotocol(fp, logname ); n = INT32SZ + sizeof(char); n = getservices((int)n, data, fp, logname); break; case T_NS: case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: (void) strcpy(data, buf); if (makename(data, origin, sizeof(data)) == -1) { err++; break; } n = strlen(data) + 1; break; case T_MX: case T_AFSDB: case T_RT: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ cp = data; PUTSHORT((u_int16_t)n, cp); if (!getword(buf, sizeof(buf), fp, 1)) { err++; break; } (void) strcpy((char *)cp, buf); if (makename((char *)cp, origin, sizeof(data) - (cp-data)) == -1) { err++; break; } /* advance pointer to end of data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - data); break; case T_PX: n = 0; data[0] = '\0'; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); cp = data; PUTSHORT((u_int16_t)n, cp); for (i = 0; i < 2; i++) { if (!getword(buf, sizeof(buf), fp, 0)) { err++; break; } (void) strcpy((char *)cp, buf); cp += strlen((char *)cp) + 1; } n = cp - data; break; case T_TXT: case T_X25: i = strlen(buf); cp = data; datasize = sizeof data; cp1 = buf; while (i > MAXCHARSTRING) { if (datasize <= MAXCHARSTRING){ ns_error(ns_log_update, "record too big"); fclose(fp); return (-1); } datasize -= MAXCHARSTRING; *cp++ = (char)MAXCHARSTRING; memcpy(cp, cp1, MAXCHARSTRING); cp += MAXCHARSTRING; cp1 += MAXCHARSTRING; i -= MAXCHARSTRING; } if (datasize < i + 1) { ns_error(ns_log_update, "record too big"); fclose(fp); return (-1); } *cp++ = i; memcpy(cp, cp1, i); cp += i; n = cp - data; endline(fp); /* XXXVIX: segmented texts 4.9.5 */ break; case T_NSAP: n = inet_nsap_addr(buf, (u_char *)data, sizeof data); endline(fp); break; case T_LOC: cp = buf + (n = strlen(buf)); *cp = ' '; cp++; while ((i = getc(fp), *cp = i, i != EOF) && *cp != '\n' && (n < MAXDATA)) { cp++; n++; } if (*cp == '\n') ungetc(*cp, fp); *cp = '\0'; n = loc_aton(buf, (u_char *)data); if (n == 0) { err++; break; } endline(fp); break; case ns_t_sig: case ns_t_key: case ns_t_nxt: case ns_t_cert: { char * errmsg = NULL; int s; s = parse_sec_rdata(buf, sizeof(buf), 1, (u_char *)data, sizeof(data), fp, zp, dnbuf, ttl, type, domain_ctx, primary_trans, &errmsg); if (s < 0) { err++; break; } break; } default: err++; } if (section == S_PREREQ) { ttl = 0; if (opcode == NXDOMAIN) { class = C_NONE; type = T_ANY; n = 0; } else if (opcode == YXDOMAIN) { class = C_ANY; type = T_ANY; n = 0; } else if (opcode == NXRRSET) { class = C_NONE; n = 0; } else if (opcode == YXRRSET) { if (n == 0) class = C_ANY; } } else { /* section == S_UPDATE */ if (opcode == DELETE) { - ttl = 0; + ttl = 0; if (n == 0) { class = C_ANY; if (type == -1) type = T_ANY; - /* WTF? C_NONE or C_ANY _must_ be the case if - * we really are to delete this. If - * C_NONE is used, according to process_updates(), - * the class is gotten from the zone's class. - * This still isn't perfect, but it will at least - * work. - * - * Question: What is so special about the class - * of the update while we are deleting?? - */ + /* WTF? C_NONE or C_ANY _must_ be the case if + * we really are to delete this. If + * C_NONE is used, according to process_updates(), + * the class is gotten from the zone's class. + * This still isn't perfect, but it will at least + * work. + * + * Question: What is so special about the class + * of the update while we are deleting?? + */ } else /* if (zp->z_xferpid != XFER_ISIXFR) */ { class = C_NONE; } } } break; case S_ADDT: default: ns_debug(ns_log_update, 1, "cannot interpret section: %d", section); inside_next = 0; err++; } if (err) { inside_next = 0; ns_debug(ns_log_update, 1, "merge of update id %d failed due to error at line %d", id, lineno); free_rrecp(&curupd, FORMERR, empty_from); continue; } rrecp = res_mkupdrec(section, dname, class, type, ttl); if (section != S_ZONE) { dp = savedata(class, type, ttl, (u_char *)data, n); dp->d_zone = zonenum; dp->d_cred = DB_C_ZONE; dp->d_clev = nlabels(zp->z_origin); dp->d_secure = DB_S_INSECURE; /* should be UNCHECKED */ rrecp->r_dp = dp; } else { rrecp->r_zone = zonenum; } APPEND(curupd, rrecp, r_link); } /* for (;;) */ fclose(fp); return (0); } /* * Create a disk database to back up zones */ int zonedump(struct zoneinfo *zp, int mode) { FILE *fp; const char *fname; struct hashbuf *htp; char *op; struct stat st; char tmp_name[MAXPATHLEN]; int escaped; char c; /* * We must check to see if Z_NEED_SOAUPDATE is set, and if so * we must do it. This won't be the case normally * (when called from ns_maint()), but it is possible if we're * exiting named. */ if (zp->z_flags & Z_NEED_SOAUPDATE) { u_int32_t serial, old_serial; old_serial = get_serial(zp); serial = old_serial + 1; if (serial == 0) serial = 1; set_serial(zp, serial); } /* Only dump zone if there is a cache specified */ if (zp->z_source && *(zp->z_source)) { ns_debug(ns_log_update, 1, "zonedump(%s)", zp->z_source); if (strlen(zp->z_source)+strlen(DumpSuffix) >= MAXPATHLEN) { ns_error(ns_log_update, "filename %s too long in zonedump", zp->z_source); /* * This problem won't ever get better, so we * clear the "need dump" flag. */ zp->z_flags &= ~Z_NEED_DUMP; return (-1); } (void)sprintf(tmp_name, "%s%s", zp->z_source, DumpSuffix); if ((fp = write_open(tmp_name)) == NULL) { ns_error(ns_log_update, "fopen() of %s failed: %s", tmp_name, strerror(errno)); return (-1); } fprintf(fp, "%s", DumpSignature); op = zp->z_origin; escaped = 0; while (*op && (((c = *op++) != '.') || escaped)) escaped = (c == '\\') && !escaped; gettime(&tt); htp = hashtab; if (nlookup(zp->z_origin, &htp, &fname, 0) != NULL) { if (db_dump(htp, fp, zp-zones, op) != OK) { ns_error(ns_log_update, "error dumping zone file %s", zp->z_source); (void)fclose(fp); return (-1); } } if (fflush(fp) == EOF) { ns_error(ns_log_update, "fflush() of %s failed: %s", tmp_name, strerror(errno)); fclose(fp); return (-1); } if (fsync(fileno(fp)) < 0) { ns_error(ns_log_update, "fsync() of %s failed: %s", tmp_name, strerror(errno)); fclose(fp); return (-1); } if (fclose(fp) == EOF) { ns_error(ns_log_update, "fclose() of %s failed: %s", tmp_name, strerror(errno)); return (-1); } /* * Try to make read only, so people will be less likely to * edit dynamic domains. */ if (stat(tmp_name, &st) < 0) { ns_error(ns_log_update, "stat(%s) failed, pressing on: %s", tmp_name, strerror(errno)); } else { zp->z_ftime = st.st_mtime; st.st_mode &= ~WRITEABLE_MASK; if (chmod(tmp_name, st.st_mode) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", tmp_name, st.st_mode, strerror(errno)); } if (mode == ISIXFR) { - if (movefile(tmp_name, zp->z_ixfr_tmp) < 0) { - ns_error(ns_log_update, "movefile(%s,%s) failed: %s :2", - tmp_name, zp->z_ixfr_tmp, strerror(errno)); - return (-1); - } + if (movefile(tmp_name, zp->z_ixfr_tmp) < 0) { + ns_error(ns_log_update, "movefile(%s,%s) failed: %s :2", + tmp_name, zp->z_ixfr_tmp, strerror(errno)); + return (-1); + } if (chmod(zp->z_source, 0644) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", zp->z_source, st.st_mode, strerror(errno)); - if (movefile(zp->z_ixfr_tmp, zp->z_source) < 0) { - ns_error(ns_log_update, "movefile(%s,%s) failed: %s :3", - zp->z_ixfr_tmp, zp->z_source, - strerror(errno)); - return (-1); - } + if (movefile(zp->z_ixfr_tmp, zp->z_source) < 0) { + ns_error(ns_log_update, "movefile(%s,%s) failed: %s :3", + zp->z_ixfr_tmp, zp->z_source, + strerror(errno)); + return (-1); + } st.st_mode &= ~WRITEABLE_MASK; if (chmod(zp->z_source, st.st_mode) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", zp->z_source, st.st_mode, strerror(errno)); } else if (mode == ISNOTIXFR) { if (movefile(tmp_name, zp->z_updatelog) < 0) { ns_error(ns_log_update, "movefile(%s,%s) failed: %s :4", tmp_name, zp->z_updatelog, strerror(errno)); return (-1); } if (movefile(zp->z_updatelog, zp->z_source) < 0) { ns_error(ns_log_update, "movefile(%s,%s) failed: %s:5", zp->z_updatelog, zp->z_source, strerror(errno)); return (-1); } } else { if (movefile(tmp_name, zp->z_source) < 0) { - ns_error(ns_log_update, "movefile(%s,%s) failed: % s :6", tmp_name, zp->z_source, strerror(errno)); - return (-1); - } + ns_error(ns_log_update, "movefile(%s,%s) failed: % s :6", tmp_name, zp->z_source, strerror(errno)); + return (-1); + } } } else ns_debug(ns_log_update, 1, "zonedump: no zone to dump"); zp->z_flags &= ~Z_NEED_DUMP; zp->z_dumptime = 0; return (0); } struct databuf * findzonesoa(struct zoneinfo *zp) { - struct hashbuf *htp; - struct namebuf *np; - struct databuf *dp; - const char *fname; + struct hashbuf *htp; + struct namebuf *np; + struct databuf *dp; + const char *fname; htp = hashtab; - np = nlookup(zp->z_origin, &htp, &fname, 0); - if (np == NULL || fname != zp->z_origin) - return (NULL); + np = nlookup(zp->z_origin, &htp, &fname, 0); + if (np == NULL || fname != zp->z_origin) + return (NULL); foreach_rr(dp, np, T_SOA, zp->z_class, zp - zones) return (dp); return (NULL); } u_char * findsoaserial(u_char *data) { char *cp = (char *)data; cp += strlen(cp) + 1; /* Nameserver. */ cp += strlen(cp) + 1; /* Mailbox. */ return ((u_char *)cp); } u_int32_t get_serial_unchecked(struct zoneinfo *zp) { struct databuf *dp; u_char *cp; u_int32_t ret; dp = findzonesoa(zp); if (!dp) ns_panic(ns_log_update, 1, "get_serial_unchecked(%s): can't locate zone SOA", zp->z_origin); cp = findsoaserial(dp->d_data); GETLONG(ret, cp); return (ret); } u_int32_t get_serial(struct zoneinfo *zp) { u_int32_t ret; ret = get_serial_unchecked(zp); if (ret != zp->z_serial) ns_panic(ns_log_update, 1, "get_serial(%s): db and zone serial numbers differ", zp->z_origin); return (ret); } void set_serial(struct zoneinfo *zp, u_int32_t serial) { struct databuf *dp; u_char *cp; dp = findzonesoa(zp); if (!dp) ns_panic(ns_log_update, 1, "set_serial(%s): can't locate zone SOA", zp->z_origin); cp = findsoaserial(dp->d_data); PUTLONG(serial, cp); zp->z_serial = serial; zp->z_flags &= ~Z_NEED_SOAUPDATE; zp->z_soaincrtime = 0; zp->z_updatecnt = 0; #ifdef BIND_NOTIFY if (!loading) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif /* * Note: caller is responsible for scheduling a dump. */ } /* * Increment serial number in zoneinfo structure and hash table SOA databuf */ int incr_serial(struct zoneinfo *zp) { u_int32_t serial, old_serial; FILE *fp, *ifp; time_t t; struct databuf *dp, *olddp; unsigned char *cp; old_serial = get_serial(zp); - serial = old_serial + 1; + serial = old_serial + 1; if (serial == 0) serial = 1; set_serial(zp, serial); (void) gettime(&tt); t = (time_t)tt.tv_sec; fp = open_transaction_log(zp); if (fp == NULL) return (-1); fprintf(fp, "[INCR_SERIAL] from %u to %u %s\n", old_serial, serial, checked_ctime(&t)); if (close_transaction_log(zp, fp)<0) return (-1); ifp = open_ixfr_log(zp); if (ifp == NULL) return (-1); dp = findzonesoa(zp); if (dp) { olddp = memget(DATASIZE(dp->d_size)); if (olddp != NULL) { memcpy(olddp, dp, DATASIZE(dp->d_size)); cp = findsoaserial(olddp->d_data); PUTLONG(old_serial, cp); fprintf(ifp, "update: {delete} %s. %u %s %s ", zp->z_origin, dp->d_ttl, p_class(dp->d_class), p_type(dp->d_type)); (void) rdata_dump(olddp, ifp); fprintf(ifp, "\n"); memput(olddp, DATASIZE(dp->d_size)); } fprintf(ifp, "update: {add} %s. %u %s %s ", zp->z_origin, dp->d_ttl, p_class(dp->d_class), p_type(dp->d_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n"); } fprintf(ifp, "[END_DELTA]\n"); if (close_ixfr_log(zp, ifp)<0) return (-1); /* * This shouldn't happen, but we check to be sure. */ if (!(zp->z_flags & Z_NEED_DUMP)) { ns_warning(ns_log_update, "incr_serial: Z_NEED_DUMP not set for zone '%s'", zp->z_origin); (void)schedule_dump(zp); } sched_zone_maint(zp); return (0); } void dynamic_about_to_exit(void) { - struct zoneinfo *zp; + struct zoneinfo *zp; ns_debug(ns_log_update, 1, "shutting down; dumping zones that need it"); for (zp = zones; zp < &zones[nzones]; zp++) { if ((zp->z_flags & Z_DYNAMIC) && ((zp->z_flags & Z_NEED_SOAUPDATE) || (zp->z_flags & Z_NEED_DUMP))) (void)zonedump(zp, ISNOTIXFR); } } Index: head/contrib/bind/bin/named/ns_xfr.c =================================================================== --- head/contrib/bind/bin/named/ns_xfr.c (revision 60940) +++ head/contrib/bind/bin/named/ns_xfr.c (revision 60941) @@ -1,832 +1,860 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_xfr.c,v 8.55 1999/10/13 16:39:13 vixie Exp $"; +static const char rcsid[] = "$Id: ns_xfr.c,v 8.62 2000/04/24 05:20:51 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static struct qs_x_lev *sx_freelev(struct qs_x_lev *lev); static int sx_flush(struct qstream *qsp), sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp), sx_nsrrs(struct qstream *qsp), sx_allrrs(struct qstream *qsp), sx_pushlev(struct qstream *qsp, struct namebuf *np); static struct databuf *db_next(struct databuf *dp); /* * void * ns_xfr(qsp, znp, zone, class, type, opcode, id, serial_ixfr, in_tsig) * Initiate a concurrent (event driven) outgoing zone transfer. */ void ns_xfr(struct qstream *qsp, struct namebuf *znp, int zone, int class, int type, int opcode, int id, u_int32_t serial_ixfr, struct tsig_record *in_tsig) { server_info si; #ifdef SO_SNDBUF static const int sndbuf = XFER_BUFSIZE * 2; #endif #ifdef SO_SNDLOWAT static const int sndlowat = XFER_BUFSIZE; #endif - ns_updrec *changes; + ns_deltalist *changes; switch (type) { case ns_t_axfr: /*FALLTHROUGH*/ case ns_t_ixfr: #ifdef BIND_ZXFR case ns_t_zxfr: #endif ns_info(ns_log_xfer_out, "zone transfer (%s) of \"%s\" (%s) to %s", p_type(type), zones[zone].z_origin, p_class(class), sin_ntoa(qsp->s_from)); break; default: ns_warning(ns_log_xfer_out, "unsupported XFR (type %s) of \"%s\" (%s) to %s", p_type(type), zones[zone].z_origin, p_class(class), sin_ntoa(qsp->s_from)); goto abort; } #ifdef SO_SNDBUF /* * The default seems to be 4K, and we'd like it to have enough room * to parallelize sending the pushed data with accumulating more * write() data from us. */ (void) setsockopt(qsp->s_rfd, SOL_SOCKET, SO_SNDBUF, (char *)&sndbuf, sizeof sndbuf); #endif #ifdef SO_SNDLOWAT /* * We don't want select() to show writability 'til we can write * an XFER_BUFSIZE block of data. */ (void) setsockopt(qsp->s_rfd, SOL_SOCKET, SO_SNDLOWAT, (char *)&sndlowat, sizeof sndlowat); #endif if (sq_openw(qsp, 64*1024) == -1) goto abort; memset(&qsp->xfr, 0, sizeof qsp->xfr); qsp->xfr.top.axfr = znp; qsp->xfr.zone = zone; qsp->xfr.class = class; + if (qsp->flags & STREAM_AXFRIXFR) + type = ns_t_axfr; qsp->xfr.type = type; qsp->xfr.id = id; qsp->xfr.opcode = opcode; qsp->xfr.msg = memget(XFER_BUFSIZE); if (!qsp->xfr.msg) goto abort; qsp->xfr.eom = qsp->xfr.msg + XFER_BUFSIZE; qsp->xfr.cp = NULL; qsp->xfr.state = s_x_firstsoa; zones[zone].z_numxfrs++; qsp->flags |= STREAM_AXFR; #ifdef BIND_ZXFR if (type == ns_t_zxfr) { enum { rd = 0, wr = 1 }; int z[2]; pid_t p; if (pipe(z) < 0) { ns_error(ns_log_xfer_out, "pipe: %s", strerror(errno)); goto abort; } p = vfork(); if (p < 0) { ns_error(ns_log_xfer_out, "vfork: %s", strerror(errno)); goto abort; } if (p == 0) { /* Child. */ dup2(z[rd], STDIN_FILENO); dup2(qsp->s_rfd, STDOUT_FILENO); execlp("gzip", "gzip", NULL); ns_error(ns_log_xfer_out, "execlp: %s", strerror(errno)); _exit(1); } ns_info(ns_log_xfer_out, "zxfr gzip pid %lu", p); /* Parent. */ dup2(z[wr], qsp->s_rfd); close(z[wr]); close(z[rd]); /* When a ZXFR completes, there can be no more requests. */ qsp->flags |= STREAM_DONE_CLOSE; } #endif si = find_server(qsp->s_from.sin_addr); if (si != NULL && si->transfer_format != axfr_use_default) qsp->xfr.transfer_format = si->transfer_format; else qsp->xfr.transfer_format = server_options->transfer_format; if (in_tsig == NULL) qsp->xfr.tsig_state = NULL; else { qsp->xfr.tsig_state = memget(sizeof(ns_tcp_tsig_state)); ns_sign_tcp_init(in_tsig->key, in_tsig->sig, in_tsig->siglen, qsp->xfr.tsig_state); qsp->xfr.tsig_skip = 0; } if (type == ns_t_ixfr) { changes = ixfr_get_change_list(&zones[zone], serial_ixfr, zones[zone].z_serial); - if (changes != NULL) - { + ixfr_log_maint(&zones[zone], 1); + if (changes != NULL) { qsp->xfr.serial = serial_ixfr; qsp->xfr.top.ixfr = changes; } - else - type = ns_t_axfr; - } + else { + qsp->xfr.top.ixfr = NULL; + goto abort; + } + } else { if (sx_pushlev(qsp, znp) < 0) { abort: (void) shutdown(qsp->s_rfd, 2); sq_remove(qsp); return; } - if (type != ns_t_ixfr) + } + if (type != ns_t_ixfr) { + ns_debug(ns_log_default, 3, "sq_writeh sx_sendsoa (%s)", + zones[zone].z_origin); (void) sq_writeh(qsp, sx_sendsoa); - else + } else { + ns_debug(ns_log_default, 3, "sq_writeh sx_send_ixfr (%s)", + zones[zone].z_origin); (void) sq_writeh(qsp, sx_send_ixfr); - + } } /* * void * ns_stopxfrs(zp) * Stop (abort, reset) all transfers of the zone specified by 'zp'. */ void ns_stopxfrs(struct zoneinfo *zp) { struct qstream *this, *next; u_int zone = (u_int)(zp - zones); + ns_debug(ns_log_default, 3, "ns_stopxfrs (%s)", zp->z_origin); + for (this = streamq; this; this = next) { next = this->s_next; if (this->xfr.zone == zone) { (void) shutdown(this->s_rfd, 2); sq_remove(this); } } INSIST(zp->z_numxfrs == 0); } /* * void * ns_freexfr(qsp) * Free all xfr-related dynamic data associated with qsp. */ void ns_freexfr(struct qstream *qsp) { + ns_delta *dp; + ns_updrec *rp; + if (qsp->xfr.msg != NULL) { memput(qsp->xfr.msg, XFER_BUFSIZE); qsp->xfr.msg = NULL; } + if (qsp->xfr.type == ns_t_ixfr && qsp->xfr.top.ixfr != NULL) { + while ((dp = HEAD(*qsp->xfr.top.ixfr)) != NULL) { + UNLINK(*qsp->xfr.top.ixfr, dp, d_link); + while ((rp = HEAD(dp->d_changes)) != NULL) { + UNLINK(dp->d_changes, rp, r_link); + if (rp->r_dp != NULL) + db_freedata(rp->r_dp); + rp->r_dp = NULL; + res_freeupdrec(rp); + } + memput(dp, sizeof *dp); + } + memput(qsp->xfr.top.ixfr, sizeof *qsp->xfr.top.ixfr); + qsp->xfr.top.ixfr = NULL; + } while (qsp->xfr.lev) qsp->xfr.lev = sx_freelev(qsp->xfr.lev); zones[qsp->xfr.zone].z_numxfrs--; - qsp->flags &= ~STREAM_AXFR; + qsp->flags &= ~(STREAM_AXFR | STREAM_AXFRIXFR); } /* * u_char * - * renew_msg(msg) + * sx_newmsg(msg) * init the header of a message, reset the compression pointers, and * reset the write pointer to the first byte following the header. */ void sx_newmsg(struct qstream *qsp) { HEADER *hp = (HEADER *)qsp->xfr.msg; memset(hp, 0, HFIXEDSZ); hp->id = htons(qsp->xfr.id); hp->opcode = qsp->xfr.opcode; hp->qr = 1; hp->rcode = NOERROR; qsp->xfr.ptrs[0] = qsp->xfr.msg; qsp->xfr.ptrs[1] = NULL; qsp->xfr.cp = qsp->xfr.msg + HFIXEDSZ; qsp->xfr.eom = qsp->xfr.msg + XFER_BUFSIZE; if (qsp->xfr.tsig_state != NULL) qsp->xfr.eom -= TSIG_BUF_SIZE; } /* * int * sx_flush(qsp) * flush the intermediate buffer out to the stream IO system. * return: * passed through from sq_write(). */ static int sx_flush(struct qstream *qsp) { int ret; #ifdef DEBUG if (debug >= 10) res_pquery(&res, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg, log_get_stream(packet_channel)); #endif if (qsp->xfr.tsig_state != NULL && qsp->xfr.tsig_skip == 0) { int msglen = qsp->xfr.cp - qsp->xfr.msg; ns_sign_tcp(qsp->xfr.msg, &msglen, qsp->xfr.eom - qsp->xfr.msg, NOERROR, qsp->xfr.tsig_state, qsp->xfr.state == s_x_done); if (qsp->xfr.state == s_x_done) { memput(qsp->xfr.tsig_state, sizeof(ns_tcp_tsig_state)); qsp->xfr.tsig_state = NULL; } qsp->xfr.cp = qsp->xfr.msg + msglen; } ret = sq_write(qsp, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg); if (ret >= 0) { qsp->xfr.cp = NULL; qsp->xfr.tsig_skip = 0; } else qsp->xfr.tsig_skip = 1; return (ret); } /* * int * sx_addrr(qsp, name, dp) * add name/dp's RR to the current assembly message. if it won't fit, - * write current message out, renew the message, and then RR should fit. + * write current message out, renew the message, and then RR *must* fit. * return: * -1 = the sx_flush() failed so we could not queue the full message. * 0 = one way or another, everything is fine. * side effects: * on success, the ANCOUNT is incremented and the pointers are advanced. */ static int sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { HEADER *hp = (HEADER *)qsp->xfr.msg; u_char **edp = qsp->xfr.ptrs + sizeof qsp->xfr.ptrs / sizeof(u_char*); int n, type; if (qsp->xfr.cp != NULL) { if (qsp->xfr.transfer_format == axfr_one_answer && sx_flush(qsp) < 0) return (-1); } if (qsp->xfr.cp == NULL) sx_newmsg(qsp); /* * Add question to first answer. */ - if (qsp->xfr.state == s_x_firstsoa && dp->d_type == T_SOA ) { - if ((qsp->xfr.type == ns_t_ixfr) || (qsp->flags & STREAM_AXFRIXFR)) { - n = dn_comp(dname, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, - qsp->xfr.ptrs, edp); - if (n > 0 && (qsp->xfr.cp + n + INT16SZ * 2) <= qsp->xfr.eom) { - qsp->xfr.cp += n; - type = (qsp->xfr.type == ns_t_zxfr) ? - ns_t_axfr : qsp->xfr.type; - PUTSHORT((u_int16_t) type, qsp->xfr.cp); - PUTSHORT((u_int16_t) qsp->xfr.class, qsp->xfr.cp); - hp->qdcount = htons(ntohs(hp->qdcount) + 1); - } + if (qsp->xfr.state == s_x_firstsoa && dp->d_type == T_SOA) { + n = dn_comp(dname, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, + qsp->xfr.ptrs, edp); + if (n > 0 && (qsp->xfr.cp + n + INT16SZ * 2) <= qsp->xfr.eom) { + qsp->xfr.cp += n; + if (qsp->xfr.type == ns_t_zxfr) + type = ns_t_axfr; + else if ((qsp->flags & STREAM_AXFRIXFR) != 0) + type = ns_t_ixfr; + else + type = qsp->xfr.type; + PUTSHORT((u_int16_t) type, qsp->xfr.cp); + PUTSHORT((u_int16_t) qsp->xfr.class, qsp->xfr.cp); + hp->qdcount = htons(ntohs(hp->qdcount) + 1); } } n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); if (n < 0) { if (sx_flush(qsp) < 0) return (-1); if (qsp->xfr.cp == NULL) sx_newmsg(qsp); n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); INSIST(n >= 0); } hp->ancount = htons(ntohs(hp->ancount) + 1); qsp->xfr.cp += n; return (0); } /* * int * sx_soarr(qsp) * add the SOA RR's at the current level's top np to the assembly message. * return: * 0 = success * -1 = write buffer full, cannot continue at this time * side effects: * if progress was made, header and pointers will be advanced. */ int sx_soarr(struct qstream *qsp) { struct databuf *dp; int added_soa = 0; foreach_rr(dp, qsp->xfr.top.axfr, T_SOA, qsp->xfr.class, qsp->xfr.zone) { if (sx_addrr(qsp, zones[qsp->xfr.zone].z_origin, dp) < 0) { /* RR wouldn't fit. Bail out. */ return (-1); } added_soa = 1; break; } if (added_soa == 0) ns_panic(ns_log_xfer_out, 1, "no SOA at zone top"); if (qsp->xfr.state == s_x_firstsoa) { foreach_rr(dp, qsp->xfr.top.axfr, T_SIG, qsp->xfr.class, qsp->xfr.zone) { if (SIG_COVERS(dp) != T_SOA) continue; if (sx_addrr(qsp, zones[qsp->xfr.zone].z_origin, dp) < 0) { /* RR wouldn't fit. Bail out. */ return (-1); } } } return (0); } /* * int * sx_nsrrs(qsp) - * add the NS RR's at the current level's current np, - * to the assembly message - * This function also adds the SIG(NS), KEY, SIG(KEY), NXT, SIG(NXT) - * the reason for this these records are part of the delegation. - * + * add the NS RR's at the current level's current np to the assembly msg. + * This function also adds the SIG(NS), KEY, SIG(KEY), NXT, SIG(NXT), + * since these records are also part of the delegation (see DNSSEC). * return: * >1 = number of NS RRs added, note that there may be more * 0 = success, there are no more NS RRs at this level * -1 = write buffer full, cannot continue at this time * side effects: * if progress was made, header and pointers will be advanced. * note: * this is meant for AXFR, which includes glue as part of the answer * sections. this is different from and incompatible with the additional * data of a referral response. */ static int sx_nsrrs(struct qstream *qsp) { struct databuf *dp, *tdp, *gdp; struct namebuf *gnp, *tnp, *top; struct hashbuf *htp; const char *fname; - int rrcount, class; + int class; class = qsp->xfr.class; top = qsp->xfr.top.axfr; - rrcount = 0; for ((void)NULL; (dp = qsp->xfr.lev->dp) != NULL; qsp->xfr.lev->dp = db_next(dp)) { - /* XYZZY foreach_rr? */ if (dp->d_class != class && class != C_ANY) continue; if (dp->d_rcode) continue; /* * It might not be in the same zone, if we are authoritative * for both parent and child, but it does have to be a zone. * * XXX: this is sort of a bug, since it means we merge the * @ NS RRset into our parent's zone. But that is what * db_load() does, so for now we have no choice. */ if (dp->d_zone == DB_Z_CACHE) continue; if (dp->d_type != T_NS && dp->d_type != T_KEY && dp->d_type != T_NXT && dp->d_type != T_SIG) continue; if (dp->d_type == T_SIG && ((SIG_COVERS(dp) != T_NS) && (SIG_COVERS(dp) != T_KEY) && (SIG_COVERS(dp) != T_NXT))) continue; if (!(qsp->xfr.lev->flags & SXL_GLUING)) { if (sx_addrr(qsp, qsp->xfr.lev->dname, dp) < 0) { /* RR wouldn't fit. Bail out. */ return (-1); } if (dp->d_type != T_NS) /* no glue processing */ continue; - rrcount++; /* only count NS records */ + /* Remember we have found a zone cut */ + if (qsp->xfr.top.axfr != qsp->xfr.lev->np) + qsp->xfr.lev->flags |= SXL_ZONECUT; } /* * Glue the sub domains together by sending the address * records for the sub domain name servers along if necessary. * Glue is necessary if the server is in any zone delegated * from the current (top) zone. Such a delegated zone might * or might not be that referred to by the NS record now * being handled. */ htp = hashtab; gnp = nlookup((char *)dp->d_data, &htp, &fname, 0); if (gnp == NULL || fname != (char *)dp->d_data) continue; for (tnp = gnp; tnp != NULL && tnp != top; tnp = tnp->n_parent) (void)NULL; if (tnp == NULL && NAME(*top)[0] != '\0') continue; /* name server is not below top domain */ for (tnp = gnp; tnp != NULL && tnp != top; tnp = tnp->n_parent) { foreach_rr(tdp, tnp, T_NS, class, DB_Z_CACHE) break; /* If we found a zone cut, we're outta here. */ if (tdp != NULL) break; } /* If name server is not in a delegated zone, skip it. */ if (tnp == top || (tnp == NULL && NAME(*top)[0] == '\0')) continue; /* Now we know glue records are needed. Send them. */ qsp->xfr.lev->flags |= SXL_GLUING; foreach_rr(gdp, gnp, T_A, class, DB_Z_CACHE) if (sx_addrr(qsp, fname, gdp) < 0) { /* * Rats. We already sent the NS RR, too. * Note that SXL_GLUING is being left on. */ return (-1); } /* for IPv6 glue AAAA record transfer */ /* patched by yasuhiro@nic.ad.jp, 1999/5/23 */ foreach_rr(gdp, gnp, T_AAAA, class, DB_Z_CACHE) if (sx_addrr(qsp, fname, gdp) < 0) { /* * Rats. We already sent the NS RR, too. * Note that SXL_GLUING is being left on. */ return (-1); } + foreach_rr(gdp, gnp, ns_t_a6, class, DB_Z_CACHE) + if (sx_addrr(qsp, fname, gdp) < 0) { + /* + * Rats. We already sent the NS RR, too. + * Note that SXL_GLUING is being left on. + */ + return (-1); + } qsp->xfr.lev->flags &= ~SXL_GLUING; } - return (rrcount); + return (0); } /* * int * sx_allrrs(qsp) * add the non-(SOA,NS) RR's at the current level's current np, * to the assembly message * do not add the DNSSEC types KEY and NXT as the delegation check * wrote these types out. * return: * >0 = number of RR's added, note that there may be more * 0 = success, there are no more RRs at this level * -1 = write buffer full, cannot continue at this time * side effects: * if progress was made, header and pointers will be advanced. * note: * this is meant for AXFR, which includes glue as part of the answer * sections. this is different from and incompatible with the additional * data of a referral response. */ static int sx_allrrs(struct qstream *qsp) { struct databuf *dp; struct namebuf *top; int rrcount, class; u_int zone; class = qsp->xfr.class; top = qsp->xfr.top.axfr; zone = qsp->xfr.zone; rrcount = 0; for ((void)NULL; (dp = qsp->xfr.lev->dp) != NULL; qsp->xfr.lev->dp = db_next(dp)) { - /* XYZZY foreach_rr? */ if (dp->d_class != class && class != C_ANY) continue; if (dp->d_rcode) continue; if (dp->d_zone != zone || stale(dp)) continue; if (dp->d_type == T_SOA || dp->d_type == T_NS || dp->d_type == T_NXT || dp->d_type == T_KEY) continue; if (dp->d_type == T_SIG && (SIG_COVERS(dp) == T_SOA || SIG_COVERS(dp) == T_NS || SIG_COVERS(dp) == T_KEY || SIG_COVERS(dp) == T_NXT)) continue; INSIST(!(qsp->xfr.lev->flags & SXL_GLUING)); if (sx_addrr(qsp, qsp->xfr.lev->dname, dp) < 0) { /* RR wouldn't fit. Bail out. */ return (-1); } rrcount++; } return (rrcount); } /* * void * sx_sendlev(qsp) * send all the RRs at the current level (really a domain name), and * do a decomposed recursion to get all subdomains up to and including * but not exceeding bottom zone cuts. * side effects: * advances qsp->xfr pointers. changes qsp->xfr.lev quite often. * causes messages to be sent to a remote TCP client. changes the * qsp->xfr.state at the end of the topmost level. changes the * qsp->xfr.lev->state several times per domain name. */ void sx_sendlev(struct qstream *qsp) { struct qs_x_lev *lev; - int rrcount; again: lev = qsp->xfr.lev; switch (lev->state) { case sxl_ns: { while (lev->dp) { /* Was the child zone reloaded under us? */ if ((lev->dp->d_flags & DB_F_ACTIVE) == 0) { (void) shutdown(qsp->s_rfd, 2); sq_remove(qsp); return; } - rrcount = sx_nsrrs(qsp); /* If we can't pack this one in, come back later. */ - if (rrcount < 0) + if (sx_nsrrs(qsp) < 0) return; - /* - * NS RRs other than those at the - * zone top are zone cuts. - */ - if (rrcount > 0 && qsp->xfr.top.axfr != lev->np) - lev->flags |= SXL_ZONECUT; } /* No more DP's for the NS RR pass on this NP. */ if (lev->flags & SXL_ZONECUT) { /* Zone cut, so go directly to end of level. */ break; } /* No NS RR's, so it's safe to send other types. */ lev->state = sxl_all; lev->dp = lev->np->n_data; if (lev->dp) DRCNTINC(lev->dp); goto again; } case sxl_all: { while (lev->dp) { /* Was a record updated under us? */ if ((lev->dp->d_flags & DB_F_ACTIVE) == 0) { (void) shutdown(qsp->s_rfd, 2); sq_remove(qsp); return; } /* If we can't pack this one in, come back later. */ if (sx_allrrs(qsp) < 0) return; } /* No more non-NS DP's for this NP, do subdomains. */ lev->state = sxl_sub; goto again; } case sxl_sub: { struct namebuf *np; /* Get next in-use hash chain if we're not following one. */ while (lev->nnp == NULL) { /* If no, or no more subdomains, end of level. */ if (lev->npp == NULL || lev->npp == lev->npe) break; lev->nnp = *lev->npp++; } /* If we encountered the end of the level, we're outta here. */ if ((np = lev->nnp) == NULL) break; /* Next time, we'll do the following NP, or the next chain. */ lev->nnp = np->n_next; /* Skip our own NP if it appears as a subdom (as in root). */ if (np != lev->np) sx_pushlev(qsp, np); goto again; } default: abort(); } /* End of level. Pop it off the stack. */ if ((qsp->xfr.lev = sx_freelev(lev)) == NULL) { /* End of topmost level. */ qsp->xfr.state = s_x_lastsoa; sq_writeh(qsp, sx_sendsoa); return; } goto again; } /* * void * sx_sendsoa(qsp) * send either the first or last SOA needed for an AXFR. * side effects: * changes qsp->xfr.state. adds RR to output buffer. */ void sx_sendsoa(struct qstream *qsp) { HEADER * hp = (HEADER *) qsp->xfr.msg; if (sx_soarr(qsp) == -1) return; /* No state change, come back here later. */ hp->aa = 1; switch (qsp->xfr.state) { case s_x_firstsoa: { /* Next thing to do is send the zone. */ qsp->xfr.state = s_x_zone; sq_writeh(qsp, sx_sendlev); break; } case s_x_lastsoa: { /* Next thing to do is go back and wait for another query. */ qsp->xfr.state = s_x_done; (void)sx_flush(qsp); sq_writeh(qsp, sq_flushw); break; } default: { ns_panic(ns_log_xfer_out, 1, "unexpected state %d in sx_sendsoa", qsp->xfr.state); } } } /* int * sx_pushlev(qsp, np) * manage the decomposed recursion. set up for a new level (domain). * returns: * 0 = success * -1 = failure (check errno) */ static int sx_pushlev(struct qstream *qsp, struct namebuf *np) { struct qs_x_lev *new = memget(sizeof *new); struct hashbuf *htp; if (!new) { errno = ENOMEM; return (-1); } memset(new, 0, sizeof *new); new->state = sxl_ns; new->np = np; new->dp = np->n_data; if (new->dp) DRCNTINC(new->dp); getname(np, new->dname, sizeof new->dname); /* * We find the subdomains by looking in the hash table for this * domain, but the root domain needs special treatment, because * of the following wart in the database design: * * The top level hash table (pointed to by the global `hashtab' * variable) contains pointers to the namebuf's for the root as * well as for the top-level domains below the root, in contrast * to the usual situation where a hash table contains entries * for domains at the same level. The n_hash member of the * namebuf for the root domain is NULL instead of pointing to a * hashbuf for the top-level domains. The n_parent members of * the namebufs for the top-level domains are NULL instead of * pointing to the namebuf for the root. * * We work around the wart as follows: * * If we are not dealing with the root zone then we just set * htp = np->n_hash, pointing to the hash table for the current * domain, and we walk through the hash table as usual, * processing the namebufs for all the subdomains. * * If we are dealing with the root zone, then we set * htp = hashtab, pointing to the global hash table (because * there is no hash table associated with the root domain's * namebuf. While we walk this hash table, we take care not to * recursively process the entry for the root namebuf. * * (apb@und nov1990) */ htp = ((new->dname[0] == '\0') ? hashtab : np->n_hash); if (htp) { new->npp = htp->h_tab; new->npe = htp->h_tab + htp->h_size; } else { new->npp = NULL; new->npe = NULL; } new->nnp = NULL; new->next = qsp->xfr.lev; qsp->xfr.lev = new; return (0); } /* * qs_x_lev * * sx_freelev(lev) * free the memory occupied by a level descriptor * return: * pointer to "next" level descriptor */ static struct qs_x_lev * sx_freelev(struct qs_x_lev *lev) { struct qs_x_lev *next = lev->next; if (lev->dp) { DRCNTDEC(lev->dp); if (lev->dp->d_rcnt == 0) db_freedata(lev->dp); } memput(lev, sizeof *lev); return (next); } static struct databuf * db_next(struct databuf *dp) { struct databuf *next = dp->d_next; DRCNTDEC(dp); if (dp->d_rcnt == 0) db_freedata(dp); if (next) DRCNTINC(next); return (next); } Index: head/contrib/bind/bin/named/pathtemplate.h =================================================================== --- head/contrib/bind/bin/named/pathtemplate.h (revision 60940) +++ head/contrib/bind/bin/named/pathtemplate.h (revision 60941) @@ -1,74 +1,78 @@ /* - * $Id: pathtemplate.h,v 8.4 1999/01/08 19:28:30 vixie Exp $ + * $Id: pathtemplate.h,v 8.6 2000/04/21 06:54:15 vixie Exp $ */ /* - * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include #ifndef _PATH_CONF #define _PATH_CONF "%DESTETC%/named.conf" #endif #ifndef _PATH_DEBUG #define _PATH_DEBUG "named.run" #endif #ifndef _PATH_DUMPFILE #define _PATH_DUMPFILE "named_dump.db" #endif #ifndef _PATH_NAMED #define _PATH_NAMED "%DESTSBIN%/named" #endif #ifndef _PATH_PIDFILE #define _PATH_PIDFILE "%DESTRUN%/named.pid" #endif #ifndef _PATH_NDCSOCK +#ifdef NEED_SECURE_DIRECTORY +#define _PATH_NDCSOCK "%DESTRUN%/ndc.d/ndc" +#else #define _PATH_NDCSOCK "%DESTRUN%/ndc" +#endif #endif #ifndef _PATH_STATS #define _PATH_STATS "named.stats" #endif #ifndef _PATH_MEMSTATS #define _PATH_MEMSTATS "named.memstats" #endif #ifndef _PATH_TMPXFER #define _PATH_TMPXFER "xfer.ddt.XXXXXX" #endif #ifndef _PATH_XFER #define _PATH_XFER "%DESTEXEC%/named-xfer" #endif #ifndef _PATH_XFERTRACE #define _PATH_XFERTRACE "xfer.trace" #endif #ifndef _PATH_XFERDDT #define _PATH_XFERDDT "xfer.ddt" #endif #ifndef _PATH_DEVNULL #define _PATH_DEVNULL "/dev/null" #endif Index: head/contrib/bind/bin/ndc/ndc.c =================================================================== --- head/contrib/bind/bin/ndc/ndc.c (revision 60940) +++ head/contrib/bind/bin/ndc/ndc.c (revision 60941) @@ -1,698 +1,709 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ndc.c,v 1.13 1999/10/13 16:39:16 vixie Exp $"; +static const char rcsid[] = "$Id: ndc.c,v 1.14 2000/02/04 08:28:32 vixie Exp $"; #endif /* not lint */ /* * Portions Copyright (c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "pathnames.h" typedef union { struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN struct sockaddr_un un; +#endif } sockaddr_t; typedef void (*closure)(void *, const char *, int); static const char * program = "amnesia"; static enum { e_channel, e_signals } mode = e_channel; static char * channel = _PATH_NDCSOCK; static const char helpfmt[] = "\t%-16s\t%s\n"; static const char * pidfile = _PATH_PIDFILE; static sockaddr_t client, server; static int quiet = 0, tracing = 0, silent = 0, client_set = 0; static int debug = 0, errors = 0, doneflag, exitflag; static int logger_show = 1; static evContext ev; static char cmd[1000]; static const char * named_path = _PATH_NAMED; static int slashcmd(void); static void slashhelp(void); static int builtincmd(void); static void command(void); static int running(int, pid_t *); static void command_channel(void); static void channel_loop(char *, int, closure, void *); static void getpid_closure(void *, const char *, int); static void banner(struct ctl_cctx *, void *, const char *, u_int); static void done(struct ctl_cctx *, void *, const char *, u_int); static void logger(enum ctl_severity, const char *fmt, ...); static void command_signals(void); static void stop_named(pid_t); static void start_named(const char *, int); static int fgetpid(const char *, pid_t *); static int get_sockaddr(char *, sockaddr_t *); static size_t impute_addrlen(const struct sockaddr *); static void vtrace(const char *, va_list); static void trace(const char *, ...); static void result(const char *, ...); static void fatal(const char *, ...); static void verror(const char *, va_list); static void error(const char *, ...); static void usage(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: usage error: ", program); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); fatal("usage: %s \ [-l localsock] [-c channel] [-p pidfile] [-n namedpath] \ [-dqst] [command [args]]\n\ ", program); } /* Public. */ int main(int argc, char *argv[], char *envp[]) { char *p; int ch; if ((program = strrchr(argv[0], '/')) != NULL) program++; else program = argv[0]; while ((ch = getopt(argc, argv, "c:p:l:n:dqst")) != -1) { switch (ch) { case 'c': channel = optarg; mode = e_channel; break; case 'p': pidfile = optarg; mode = e_signals; break; case 'l': if (!get_sockaddr(optarg, &client)) usage("bad local socket (%s)", optarg); client_set++; break; case 'n': named_path = optarg; break; case 'd': tracing++; debug++; break; case 'q': quiet++; break; case 's': silent++; break; case 't': tracing++; break; default: usage("unrecognized command option (%c)", ch); /* NOTREACHED */ } } if (mode != e_channel && client_set) usage("the -l flag is only valid for control channels"); if (mode == e_channel) { if (!get_sockaddr(channel, &server)) usage("bad channel name (%s)", channel); if (evCreate(&ev) < 0) fatal("evCreate - %s", strerror(errno)); } *(p = cmd) = '\0'; for (argc -= optind, argv += optind; argc > 0; argc--, argv++) { size_t t = strlen(*argv); if ((p - cmd) + t + 2 > sizeof cmd) usage("command too long"); strcpy(p, *argv); p += t; if (argv[1] != NULL) *p++ = ' '; *p = '\0'; } if (cmd[0] != '\0') { command(); } else { if (!quiet) result("Type help -or- /h if you need help."); for (exitflag = 0; !exitflag; (void)NULL) { if (!quiet) { printf("%s> ", program); fflush(stdout); } if (!fgets(cmd, sizeof cmd, stdin)) { if (!quiet) result("EOF"); exitflag++; continue; } if (cmd[strlen(cmd) - 1] == '\n') cmd[strlen(cmd) - 1] = '\0'; if (cmd[0] == '\0') continue; if (slashcmd()) continue; command(); } } if (mode == e_channel) evDestroy(ev); exit(errors != 0); } /* Private. */ static int slashcmd(void) { if (strncasecmp(cmd, "/help", strlen(cmd)) == 0) slashhelp(); else if (strncasecmp(cmd, "/exit", strlen(cmd)) == 0) exitflag++; else if (strncasecmp(cmd, "/trace", strlen(cmd)) == 0) result("tracing now %s", (tracing = !tracing) ? "on" : "off"); else if (strncasecmp(cmd, "/debug", strlen(cmd)) == 0) result("debugging now %s", (debug = !debug) ? "on" : "off"); else if (strncasecmp(cmd, "/quiet", strlen(cmd)) == 0) result("%s is now %s", program, (quiet = !quiet) ? "quiet" : "noisy"); else if (strncasecmp(cmd, "/silent", strlen(cmd)) == 0) result("%s is now %s", program, (silent = !silent) ? "silent" : "gregarious"); else return (0); return (1); } static void slashhelp(void) { printf(helpfmt, "/h(elp)", "this text"); printf(helpfmt, "/e(xit)", "leave this program"); printf(helpfmt, "/t(race)", "toggle tracing (protocol and system events)"); printf(helpfmt, "/d(ebug)", "toggle debugging (internal program events)"); printf(helpfmt, "/q(uiet)", "toggle quietude (prompts and results)"); printf(helpfmt, "/s(ilent)", "toggle silence (suppresses nonfatal errors)"); } static int builtincmd(void) { static const char spaces[] = " \t"; char *rest, *syscmd; pid_t pid; int save_quiet = quiet; int len; quiet = 1; len = strcspn(cmd, spaces); rest = cmd + len; if (*rest != '\0') { rest++; rest += strspn(rest, spaces); } syscmd = malloc(strlen(named_path) + sizeof " " + strlen(rest)); if (syscmd == NULL) fatal("malloc() failed - %s", strerror(errno)); strcpy(syscmd, named_path); if (*rest != '\0') { strcat(syscmd, " "); strcat(syscmd, rest); } if (strncasecmp(cmd, "start", len) == 0) { if (running(debug, &pid)) error("name server already running? (pid %ld)", (long)pid); else start_named(syscmd, save_quiet); quiet = save_quiet; free(syscmd); return (1); } else if (strncasecmp(cmd, "restart", len) == 0) { if (!running(debug, &pid)) error("name server was not running (warning only)"); else stop_named(pid); start_named(syscmd, save_quiet); quiet = save_quiet; free(syscmd); return (1); } quiet = save_quiet; free(syscmd); return (0); } static void builtinhelp(void) { printf(helpfmt, "start", "start the server"); printf(helpfmt, "restart", "stop server if any, start a new one"); } static void command(void) { if (builtincmd()) return; switch (mode) { case e_channel: command_channel(); break; case e_signals: command_signals(); break; default: abort(); } } static int running(int show, pid_t *pidp) { pid_t pid; switch (mode) { case e_channel: pid = 0; channel_loop("getpid", show, getpid_closure, &pid); if (pid != 0) { if (tracing) result("pid %ld is running", (long)pid); *pidp = pid; return (1); } break; case e_signals: if (fgetpid(pidfile, pidp)) { if (tracing) result("pid %ld is running", (long)pid); return (1); } break; default: abort(); } if (show) error("pid not valid or server not running"); return (0); } static void getpid_closure(void *uap, const char *text, int flags) { pid_t *pidp = uap; const char *cp; flags = flags; if ((cp = strchr(text, '<')) != NULL) { long l = 0; char ch; while ((ch = *++cp) != '\0' && ch != '>' && isdigit(ch)) l *= 10, l += (ch - '0'); if (ch == '>') { *pidp = (pid_t)l; return; } } error("response does not contain pid (%s)", text); } static void command_channel(void) { int helping = (strcasecmp(cmd, "help") == 0); int save_quiet = quiet; if (helping) quiet = 0; channel_loop(cmd, !quiet, NULL, NULL); quiet = save_quiet; } struct args { const char *cmd; closure cl; void *ua; }; static void channel_loop(char *cmdtext, int show, closure cl, void *ua) { struct ctl_cctx *ctl; struct sockaddr *client_addr; struct args a; evEvent e; int save_logger_show = logger_show; if (!client_set) client_addr = NULL; else client_addr = (struct sockaddr *)&client; a.cmd = cmdtext; a.cl = cl; a.ua = ua; logger_show = show; ctl = ctl_client(ev, client_addr, impute_addrlen(client_addr), (struct sockaddr *)&server, impute_addrlen((struct sockaddr *)&server), banner, &a, 15, logger); if (ctl == NULL) { if (show) error("cannot connect to command channel (%s)", channel); } else { doneflag = 0; while (evGetNext(ev, &e, EV_WAIT) == 0) if (evDispatch(ev, e) < 0 || doneflag) break; ctl_endclient(ctl); } logger_show = save_logger_show; } static void banner(struct ctl_cctx *ctl, void *uap, const char *msg, u_int flags) { struct args *a = uap; if (msg == NULL) { trace("EOF"); doneflag = 1; return; } trace("%s", msg); if ((flags & CTL_MORE) != 0) return; if (ctl_command(ctl, a->cmd, strlen(a->cmd), done, a) < 0) { error("ctl_command failed - %s", strerror(errno)); doneflag = 1; } } static void done(struct ctl_cctx *ctl, void *uap, const char *msg, u_int flags) { struct args *a = uap; if (msg == NULL) { trace("EOF"); doneflag = 1; return; } if (!tracing && !quiet && strlen(msg) > 4) result("%s", msg + 4); trace("%s", msg); if (a->cl) (a->cl)(a->ua, msg, flags); if ((flags & CTL_MORE) == 0) doneflag = 1; } static void logger(enum ctl_severity ctlsev, const char *format, ...) { va_list args; va_start(args, format); switch (ctlsev) { case ctl_debug: /* FALLTHROUGH */ case ctl_warning: if (debug) vtrace(format, args); break; case ctl_error: if (logger_show) verror(format, args); break; default: abort(); } va_end(args); } static struct cmdsig { const char * cmd; int sig; const char * help; } cmdsigs[] = { { "dumpdb", SIGINT, "dump cache database to a file" }, { "reload", SIGHUP, "reload configuration file" }, { "stats", SIGILL, "dump statistics to a file" }, { "trace", SIGUSR1, "increment trace level" }, { "notrace", SIGUSR2, "turn off tracing" }, #ifdef SIGWINCH { "querylog", SIGWINCH, "toggle query logging" }, { "qrylog", SIGWINCH, "alias for querylog" }, #endif { NULL, 0 } }; static void command_signals(void) { struct cmdsig *cmdsig; pid_t pid; int sig; if (strcasecmp(cmd, "help") == 0) { printf(helpfmt, "help", "this output"); printf(helpfmt, "status", "check for running server"); printf(helpfmt, "stop", "stop the server"); builtinhelp(); for (cmdsig = cmdsigs; cmdsig->cmd != NULL; cmdsig++) printf(helpfmt, cmdsig->cmd, cmdsig->help); } else if (strcasecmp(cmd, "status") == 0) { if (!fgetpid(pidfile, &pid)) error("pid not valid or server not running"); else result("pid %ld is running", (long)pid); } else if (strcasecmp(cmd, "stop") == 0) { if (!fgetpid(pidfile, &pid)) error("name server not running"); else stop_named(pid); } else { for (cmdsig = cmdsigs; cmdsig->cmd != NULL; cmdsig++) if (strcasecmp(cmd, cmdsig->cmd) == 0) break; if (cmdsig->cmd == NULL) error("unrecognized command (%s)", cmd); else if (!fgetpid(pidfile, &pid)) error("can't get pid (%s)", pidfile); else if (kill(pid, cmdsig->sig) < 0) error("kill() failed - %s", strerror(errno)); else trace("pid %ld sig %d OK", (long)pid, cmdsig->sig); } } static void stop_named(pid_t pid) { int n; trace("stopping named (pid %ld)", (long)pid); switch (mode) { case e_signals: if (kill(pid, SIGTERM) < 0) { error("kill(%ld, SIGTERM) failed - %s", (long)pid, strerror(errno)); return; } trace("SIGTERM ok, waiting for death"); break; case e_channel: channel_loop("stop", tracing, NULL, NULL); break; default: abort(); } for (n = 0; n < 10; n++) { if (kill(pid, 0) != 0) { trace("named (pid %ld) is dead", (long)pid); return; } sleep(1); } error("named (pid %ld) didn't die", (long)pid); } static void start_named(const char *syscmd, int local_quiet) { pid_t pid; if (system(syscmd) != 0) error("could not start new name server (%s)", syscmd); else { sleep(3); if (!running(0, &pid)) error("name server has not started (yet?)"); else if (!local_quiet) result("new pid is %ld", (long)pid); } } static int fgetpid(const char *f, pid_t *pid) { FILE *fp; int try; long t; for (try = 0; try < 5; try++) { trace("pidfile is \"%s\" (try #%d)", pidfile, try + 1); if ((fp = fopen(pidfile, "r")) == NULL) trace("pid file (%s) unavailable - %s", pidfile, strerror(errno)); else if (fscanf(fp, "%ld\n", &t) != 1) trace("pid file (%s) format is bad", pidfile); else if (*pid = (pid_t)t, fclose(fp), kill(*pid, 0) < 0) trace("pid file (%s) contains unusable pid (%d) - %s", pidfile, *pid, strerror(errno)); else { trace("pid is %ld", (long)*pid); return (1); } sleep(1); } trace("pid not found"); return (0); } static int get_sockaddr(char *name, sockaddr_t *addr) { char *slash; +#ifndef NO_SOCKADDR_UN if (name[0] == '/') { memset(&addr->un, '\0', sizeof addr->un); addr->un.sun_family = AF_UNIX; strncpy(addr->un.sun_path, name, sizeof addr->un.sun_path - 1); addr->un.sun_path[sizeof addr->un.sun_path - 1] = '\0'; - } else if ((slash = strrchr(name, '/')) != NULL) { - *slash = '\0'; + } else +#endif + if ((slash = strrchr(name, '/')) != NULL) { + char *ibuf = malloc(slash - name + 1); + if (!ibuf) + usage("no memory for IP address (%s)", name); + memcpy(ibuf, name, slash - name); + ibuf[slash - name] = '\0'; memset(&addr->in, '\0', sizeof addr->in); - if (!inet_pton(AF_INET, name, &addr->in.sin_addr)) + if (!inet_pton(AF_INET, ibuf, &addr->in.sin_addr)) usage("bad ip address (%s)", name); if ((addr->in.sin_port = htons(atoi(slash+1))) == 0) usage("bad ip port (%s)", slash+1); addr->in.sin_family = AF_INET; - *slash = ':'; - } else { - return (0); + free (ibuf); + } else { + return (0); } return (1); } static size_t impute_addrlen(const struct sockaddr *sa) { if (sa == 0) return (0); switch (sa->sa_family) { case AF_INET: return (sizeof(struct sockaddr_in)); +#ifndef NO_SOCKADDR_UN case AF_UNIX: return (sizeof(struct sockaddr_un)); +#endif default: abort(); } } static void vtrace(const char *fmt, va_list ap) { if (tracing) { fprintf(stdout, "%s: [", program); vfprintf(stdout, fmt, ap); fputs("]\n", stdout); } } static void trace(const char *fmt, ...) { va_list args; va_start(args, fmt); vtrace(fmt, args); va_end(args); } static void result(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stdout, fmt, args); fputc('\n', stdout); va_end(args); } static void fatal(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: fatal error: ", program); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); exit(1); } static void verror(const char *fmt, va_list ap) { fprintf(stderr, "%s: error: ", program); vfprintf(stderr, fmt, ap); fputc('\n', stderr); errors++; } static void error(const char *fmt, ...) { va_list args; va_start(args, fmt); if (silent) vtrace(fmt, args); else verror(fmt, args); va_end(args); } Index: head/contrib/bind/bin/nslookup/list.c =================================================================== --- head/contrib/bind/bin/nslookup/list.c (revision 60940) +++ head/contrib/bind/bin/nslookup/list.c (revision 60941) @@ -1,701 +1,650 @@ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef lint static const char sccsid[] = "@(#)list.c 5.23 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: list.c,v 8.21 1999/10/15 19:49:08 vixie Exp $"; +static const char rcsid[] = "$Id: list.c,v 8.23 2000/03/30 23:25:34 vixie Exp $"; #endif /* not lint */ /* ******************************************************************************* * * list.c -- * * Routines to obtain info from name and finger servers. * * Adapted from 4.3BSD BIND ns_init.c and from finger.c. * ******************************************************************************* */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "res.h" extern char *pager; typedef union { HEADER qb1; u_char qb2[PACKETSZ]; } querybuf; extern HostInfo *defaultPtr; extern HostInfo curHostInfo; extern int curHostValid; extern int queryType; extern int queryClass; static int sockFD = -1; int ListSubr(); /* * During a listing to a file, hash marks are printed * every HASH_SIZE records. */ #define HASH_SIZE 50 /* ******************************************************************************* * * ListHosts -- * ListHostsByType -- * * Requests the name server to do a zone transfer so we * find out what hosts it knows about. * * For ListHosts, there are five types of output: * - Internet addresses (default) * - cpu type and operating system (-h option) * - canonical and alias names (-a option) * - well-known service names (-s option) * - ALL records (-d option) * ListHostsByType prints records of the default type or of a speicific * type. * * To see all types of information sorted by name, do the following: * ls -d domain.edu > file - * view file * * Results: * SUCCESS the listing was successful. * ERROR the server could not be contacted because * a socket could not be obtained or an error * occured while receiving, or the output file * could not be opened. * ******************************************************************************* */ void ListHostsByType(char *string, int putToFile) { char *namePtr, name[NAME_LEN], option[NAME_LEN]; int i, j, qtype, result; /* * Parse the command line. It maybe of the form "ls -t domain" * or "ls -t type domain". */ /* simulate sscanf(string, " ls -t %s %s", option, name) */ i = matchString(" ls -t ", string); if (i > 0) { j = pickString(string + i, option, sizeof option); if (j > 0) { j = pickString(string + i + j, name, sizeof name); if (j > 0) i = 2; else i = 1; } else { i = 0; } } if (putToFile && i == 2 && name[0] == '>') i--; if (i == 2) { qtype = StringToType(option, -1, stderr); if (qtype == -1) return; namePtr = name; } else if (i == 1) { namePtr = option; qtype = queryType; } else { fprintf(stderr, "*** ls: invalid request %s\n", string); return; } result = ListSubr(qtype, namePtr, putToFile ? string : NULL); if (result != SUCCESS) fprintf(stderr, "*** Can't list domain %s: %s\n", namePtr, DecodeError(result)); } void ListHosts(char *string, int putToFile) { char *namePtr, name[NAME_LEN], option[NAME_LEN]; int i, j, qtype, result; /* * Parse the command line. It maybe of the form "ls domain", * "ls -X domain". */ /* simulate i = sscanf(string, " ls %s %s", option, name) */ i = matchString(" ls ", string); if (i > 0) { j = pickString(string + i, option, sizeof option); if (j > 0) { j = pickString(string + i + j, name, sizeof name); if (j > 0) i = 2; else i = 1; } else { i = 0; } } if (putToFile && i == 2 && name[0] == '>') i--; if (i == 2) { if (strcmp("-a", option) == 0) qtype = T_CNAME; else if (strcmp("-h", option) == 0) qtype = T_HINFO; else if (strcmp("-m", option) == 0) qtype = T_MX; else if (strcmp("-p", option) == 0) qtype = T_PX; else if (strcmp("-s", option) == 0) qtype = T_WKS; else if (strcmp("-d", option) == 0) qtype = T_ANY; else if (strcmp("-n", option) == 0) qtype = T_NAPTR; else qtype = T_A; namePtr = name; } else if (i == 1) { namePtr = option; qtype = T_A; } else { fprintf(stderr, "*** ls: invalid request %s\n",string); return; } result = ListSubr(qtype, namePtr, putToFile ? string : NULL); if (result != SUCCESS) fprintf(stderr, "*** Can't list domain %s: %s\n", namePtr, DecodeError(result)); } int ListSubr(int qtype, char *domain, char *cmd) { static u_char *answer = NULL; static int answerLen = 0; ns_msg handle; querybuf buf; struct sockaddr_in sin; HEADER *headerPtr; int msglen, amtToRead, numRead, n, count, soacnt; u_int len; int numAnswers = 0; int numRecords = 0; u_char tmp[INT16SZ], *cp; char soaname[2][NAME_LEN], file[PATH_MAX]; enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING } error = NO_ERRORS; /* * Create a query packet for the requested domain name. */ msglen = res_nmkquery(&res, QUERY, domain, queryClass, T_AXFR, NULL, 0, 0, buf.qb2, sizeof buf); if (msglen < 0) { if (_res.options & RES_DEBUG) fprintf(stderr, "*** ls: res_nmkquery failed\n"); return (ERROR); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(nsport); /* * Check to see if we have the address of the server or the * address of a server who knows about this domain. * * For now, just use the first address in the list. XXX. */ if (defaultPtr->addrList != NULL) sin.sin_addr = *(struct in_addr *) defaultPtr->addrList[0]; else sin.sin_addr = *(struct in_addr *) defaultPtr->servers[0]->addrList[0]; /* * Set up a virtual circuit to the server. */ sockFD = socket(AF_INET, SOCK_STREAM, 0); if (sockFD < 0) { perror("ls: socket"); return (ERROR); } if (connect(sockFD, (struct sockaddr *)&sin, sizeof(sin)) < 0) { int e; if (errno == ECONNREFUSED) e = NO_RESPONSE; else { perror("ls: connect"); e = ERROR; } (void) close(sockFD); sockFD = -1; return (e); } /* * Send length & message for zone transfer */ ns_put16(msglen, tmp); if (write(sockFD, (char *)tmp, INT16SZ) != INT16SZ || write(sockFD, (char *)buf.qb2, msglen) != msglen) { perror("ls: write"); (void) close(sockFD); sockFD = -1; return(ERROR); } fprintf(stdout,"[%s]\n", (defaultPtr->addrList != NULL) ? defaultPtr->name : defaultPtr->servers[0]->name); if (cmd == NULL) { filePtr = stdout; } else { filePtr = OpenFile(cmd, file, sizeof file); if (filePtr == NULL) { fprintf(stderr, "*** Can't open %s for writing\n", file); (void) close(sockFD); sockFD = -1; return (ERROR); } fprintf(filePtr, "> %s\n", cmd); fprintf(filePtr, "[%s]\n", (defaultPtr->addrList != NULL) ? defaultPtr->name : defaultPtr->servers[0]->name); } soacnt = 0; while (soacnt < 2) { /* * Read the length of the response. */ cp = tmp; amtToRead = INT16SZ; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_LEN; break; } len = ns_get16(tmp); if (len == 0) break; /* nothing left to read */ /* * If the server sent too much data to fit the existing * buffer, allocate a new one. */ if (len > (u_int)answerLen) { if (answerLen != 0) free(answer); answerLen = len; answer = (u_char *)Malloc(answerLen); } /* * Read the response. */ amtToRead = len; cp = answer; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_MSG; break; } if (ns_initparse(answer, cp - answer, &handle) < 0) { perror("ns_initparse"); error = ERR_PRINTING; break; } if (ns_msg_getflag(handle, ns_f_rcode) != ns_r_noerror || ns_msg_count(handle, ns_s_an) == 0) { /* Signalled protocol error, or empty message. */ error = ERR_PRINTING; break; } for (;;) { static char origin[NS_MAXDNAME], name_ctx[NS_MAXDNAME]; const char *name; char buf[2048]; /* XXX need to malloc/realloc. */ ns_rr rr; if (ns_parserr(&handle, ns_s_an, -1, &rr)) { if (errno != ENODEV) { perror("ns_parserr"); error = ERR_PRINTING; } break; } name = ns_rr_name(rr); if (origin[0] == '\0' && name[0] != '\0') { - fprintf(filePtr, "$ORIGIN %s.\n", name); - strcpy(origin, name); + if (strcmp(name, ".") != 0) + strcpy(origin, name); + fprintf(filePtr, "$ORIGIN %s.\n", origin); + if (strcmp(name, ".") == 0) + strcpy(origin, name); + strcpy(name_ctx, "@"); } if (qtype == T_ANY || ns_rr_type(rr) == qtype) { if (ns_sprintrr(&handle, &rr, name_ctx, origin, buf, sizeof buf) < 0) { perror("ns_sprintrr"); error = ERR_PRINTING; break; } strcpy(name_ctx, name); numRecords++; fputs(buf, filePtr); fputc('\n', filePtr); } if (ns_rr_type(rr) == T_SOA) { strcpy(soaname[soacnt], name); if (soacnt == 0) soacnt = 1; else if (ns_samename(soaname[0], soaname[1]) == 1) { soacnt = 2; /* This means we're finished. * But we've to reset origin and * name_ctx now ! */ origin[0] = name_ctx[0] ='\0'; } } } if (error != NO_ERRORS) break; numAnswers++; if (cmd != NULL && ((numAnswers % HASH_SIZE) == 0)) { fprintf(stdout, "#"); fflush(stdout); } } if (cmd != NULL) fprintf(stdout, "%sReceived %d answer%s (%d record%s).\n", (numAnswers >= HASH_SIZE) ? "\n" : "", numAnswers, (numAnswers != 1) ? "s" : "", numRecords, (numRecords != 1) ? "s" : ""); (void) close(sockFD); sockFD = -1; if (cmd != NULL && filePtr != NULL) { fclose(filePtr); filePtr = NULL; } switch (error) { case NO_ERRORS: return (SUCCESS); case ERR_READING_LEN: return (ERROR); case ERR_PRINTING: return (ERROR); case ERR_READING_MSG: headerPtr = (HEADER *) answer; fprintf(stderr,"*** ls: error receiving zone transfer:\n"); fprintf(stderr, " result: %s, answers = %d, authority = %d, additional = %d\n", p_rcode(headerPtr->rcode), ntohs(headerPtr->ancount), ntohs(headerPtr->nscount), ntohs(headerPtr->arcount)); return (ERROR); default: return (ERROR); } -} - -/* - ******************************************************************************* - * - * ViewList -- - * - * A hack to view the output of the ls command in sorted - * order using more. - * - ******************************************************************************* - */ - -void -ViewList(char *string) { - char file[PATH_MAX]; - char command[PATH_MAX]; - int i, j; - char soafile[PATH_MAX]; - - /* sscanf(string, " view %s", file); */ - i = matchString(" view ", string); - if (i > 0) { - j = pickString(string + i, file, sizeof file); - if (j == 0) { - fprintf(stderr, "*** invalid file name: %s\n", string + i); - return ; - } - } - - if ( !mktemp(strcpy(soafile,"/var/tmp/nslookup_tmpXXXXXX"))) { - fprintf(stderr, "*** cannot create temp file\n"); - return ; - } - (void)sprintf(command, "sed '\ -/^$/,${\ -/@/,$d\ -}\ -/^[^ ]/{\ -h\ -s/^\\([^ ]* *\\).*/\\1/\ -x\ -}\ -1,/^$/{\ -w %s\ -d\ -}\ -/^ /{\ -G\ -s/^ *//\ -s/^\\(.*\\)\\n\\(.*\\)$/\\2\\1/\ -}' %s | sort | (cat %s -; rm %s) | %s", - soafile, file, soafile, soafile, pager); - system(command); } /* ******************************************************************************* * * Finger -- * * Connects with the finger server for the current host * to request info on the specified person (long form) * who is on the system (short form). * * Results: * SUCCESS the finger server was contacted. * ERROR the server could not be contacted because * a socket could not be obtained or connected * to or the service could not be found. * ******************************************************************************* */ Finger(string, putToFile) char *string; int putToFile; { struct servent *sp; struct sockaddr_in sin; FILE *f; int c; int lastc; char name[NAME_LEN]; char file[PATH_MAX]; int i; /* * We need a valid current host info to get an inet address. */ if (!curHostValid) { fprintf(stderr, "Finger: no current host defined.\n"); return (ERROR); } /* simulate: sscanf("finger %s") ; */ i = matchString(" finger ", string); if (i > 0) { i = pickString(string + i, name, sizeof name); if (i > 0) { i = 1 ; } /* note that if the argument to the finger command is bigger than sizeof name it will be treated as if there was no argument. */ } if (i == 1) { if (putToFile && (name[0] == '>')) { name[0] = '\0'; } } else { name[0] = '\0'; } sp = getservbyname("finger", "tcp"); if (sp == 0) { fprintf(stderr, "Finger: unknown service\n"); return (ERROR); } memset(&sin, 0, sizeof sin); sin.sin_family = curHostInfo.addrType; sin.sin_port = sp->s_port; memcpy(&sin.sin_addr, curHostInfo.addrList[0], curHostInfo.addrLen); /* * Set up a virtual circuit to the host. */ sockFD = socket(curHostInfo.addrType, SOCK_STREAM, 0); if (sockFD < 0) { fflush(stdout); perror("finger: socket"); return (ERROR); } if (connect(sockFD, (struct sockaddr *)&sin, sizeof (sin)) < 0) { fflush(stdout); perror("finger: connect"); close(sockFD); sockFD = -1; return (ERROR); } if (!putToFile) { filePtr = stdout; } else { filePtr = OpenFile(string, file, sizeof file); if (filePtr == NULL) { fprintf(stderr, "*** Can't open %s for writing\n", file); close(sockFD); sockFD = -1; return(ERROR); } fprintf(filePtr,"> %s\n", string); } fprintf(filePtr, "[%s]\n", curHostInfo.name); if (name[0] != '\0') { write(sockFD, "/W ", 3); } write(sockFD, name, strlen(name)); write(sockFD, "\r\n", 2); f = fdopen(sockFD, "r"); lastc = '\n'; while ((c = getc(f)) != EOF) { switch (c) { case 0210: case 0211: case 0212: case 0214: c -= 0200; break; case 0215: c = '\n'; break; } putc(lastc = c, filePtr); } if (lastc != '\n') { putc('\n', filePtr); } putc('\n', filePtr); close(sockFD); sockFD = -1; if (putToFile) { fclose(filePtr); filePtr = NULL; } return (SUCCESS); } void ListHost_close() { if (sockFD != -1) { (void) close(sockFD); sockFD = -1; } } Index: head/contrib/bind/bin/nslookup/nslookup.help =================================================================== --- head/contrib/bind/bin/nslookup/nslookup.help (revision 60940) +++ head/contrib/bind/bin/nslookup/nslookup.help (revision 60941) @@ -1,34 +1,33 @@ -$Id: nslookup.help,v 8.4 1996/10/25 18:09:41 vixie Exp $ +$Id: nslookup.help,v 8.5 2000/03/30 23:25:35 vixie Exp $ Commands: (identifiers are shown in uppercase, [] means optional) NAME - print info about the host/domain NAME using default server NAME1 NAME2 - as above, but use NAME2 as server help or ? - print info on common commands; see nslookup(1) for details set OPTION - set an option all - print options, current server and host [no]debug - print debugging information [no]d2 - print exhaustive debugging information [no]defname - append domain name to each query [no]recurse - ask for recursive answer to query [no]vc - always use a virtual circuit domain=NAME - set default domain name to NAME srchlist=N1[/N2/.../N6] - set domain to N1 and search list to N1,N2, etc. root=NAME - set root server to NAME retry=X - set number of retries to X timeout=X - set initial time-out interval to X seconds querytype=X - set query type, e.g., A,ANY,CNAME,HINFO,MX,PX,NS,PTR,SOA,TXT,WKS,SRV,NAPTR port=X - set port number to send query on type=X - synonym for querytype class=X - set query class to one of IN (Internet), CHAOS, HESIOD or ANY server NAME - set default server to NAME, using current default server lserver NAME - set default server to NAME, using initial server finger [USER] - finger the optional USER at the current default host root - set current default server to the root ls [opt] DOMAIN [> FILE] - list addresses in DOMAIN (optional: output to FILE) -a - list canonical names and aliases -h - list HINFO (CPU type and operating system) -s - list well-known services -d - list all records -t TYPE - list records of the given type (e.g., A,CNAME,MX, etc.) -view FILE - sort an 'ls' output file and view it with more exit - exit the program, ^D also exits Index: head/contrib/bind/bin/nsupdate/nsupdate.c =================================================================== --- head/contrib/bind/bin/nsupdate/nsupdate.c (revision 60940) +++ head/contrib/bind/bin/nsupdate/nsupdate.c (revision 60941) @@ -1,689 +1,691 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: nsupdate.c,v 8.21 1999/10/19 22:22:59 cyarnell Exp $"; +static const char rcsid[] = "$Id: nsupdate.c,v 8.23 2000/02/04 07:51:04 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "../named/db_defs.h" /* XXX all of this stuff should come from libbind.a */ /* * Map class and type names to number */ struct map { char token[10]; int val; }; struct map class_strs[] = { { "in", C_IN }, { "chaos", C_CHAOS }, { "hs", C_HS }, }; #define M_CLASS_CNT (sizeof(class_strs) / sizeof(struct map)) struct map type_strs[] = { { "a", T_A }, { "ns", T_NS }, { "cname", T_CNAME }, { "soa", T_SOA }, { "mb", T_MB }, { "mg", T_MG }, { "mr", T_MR }, { "null", T_NULL }, { "wks", T_WKS }, { "ptr", T_PTR }, { "hinfo", T_HINFO }, { "minfo", T_MINFO }, { "mx", T_MX }, { "txt", T_TXT }, { "rp", T_RP }, { "afsdb", T_AFSDB }, { "x25", T_X25 }, { "isdn", T_ISDN }, { "rt", T_RT }, { "nsap", T_NSAP }, { "nsap_ptr", T_NSAP_PTR }, { "sig", T_SIG }, { "key", T_KEY }, { "px", T_PX }, { "loc", T_LOC }, { "nxt", T_NXT }, { "eid", T_EID }, { "nimloc", T_NIMLOC }, { "srv", T_SRV }, { "atma", T_ATMA }, { "naptr", T_NAPTR }, { "kx", ns_t_kx }, { "cert", ns_t_cert }, { "aaaa", ns_t_aaaa }, }; #define M_TYPE_CNT (sizeof(type_strs) / sizeof(struct map)) struct map section_strs[] = { { "zone", S_ZONE }, { "prereq", S_PREREQ }, { "update", S_UPDATE }, { "reserved", S_ADDT }, }; #define M_SECTION_CNT (sizeof(section_strs) / sizeof(struct map)) struct map opcode_strs[] = { { "nxdomain", NXDOMAIN }, { "yxdomain", YXDOMAIN }, { "nxrrset", NXRRSET }, { "yxrrset", YXRRSET }, { "delete", DELETE }, { "add", ADD }, }; #define M_OPCODE_CNT (sizeof(opcode_strs) / sizeof(struct map)) static int getcharstring(char *, char *, int, int, int); static char *progname; static void usage(void); static int getword_str(char *, int, char **, char *); static struct __res_state res; int dns_findprimary (res_state, char *, struct ns_tsig_key *, char *, int, struct in_addr *); /* * format of file read by nsupdate is kept the same as the log * file generated by updates, so that the log file can be fed * to nsupdate to reconstruct lost updates. * * file is read on line at a time using fgets() rather than * one word at a time using getword() so that it is easy to * adapt nsupdate to read piped input from other scripts * * overloading of class/type has to be deferred to res_update() * because class is needed by res_update() to determined the * zone to which a resource record belongs */ int main(argc, argv) int argc; char **argv; { FILE *fp = NULL; char buf[BUFSIZ], buf2[BUFSIZ], hostbuf[100], filebuf[100]; char dnbuf[MAXDNAME], data[MAXDATA]; u_char packet[PACKETSZ], answer[PACKETSZ]; char *host = hostbuf, *batchfile = filebuf; char *r_dname, *cp, *startp, *endp, *svstartp; char section[15], opcode[10]; int i, c, n, n1, inside, lineno = 0, vc = 0, debug = 0, r_size, r_section, r_opcode, prompt = 0, ret = 0, stringtobin = 0; int16_t r_class, r_type; u_int32_t r_ttl; struct map *mp; ns_updrec *rrecp; ns_updque listuprec; struct in_addr hostaddr; extern int getopt(); extern char *optarg; extern int optind, opterr, optopt; ns_tsig_key key; char *keyfile=NULL, *keyname=NULL, *p, *pp; int file_major, file_minor, alg; progname = argv[0]; while ((c = getopt(argc, argv, "dsvk:n:")) != -1) { switch (c) { case 'v': vc = 1; break; case 'd': debug = 1; break; case 's': stringtobin = 1; break; case 'k': { /* -k keydir:keyname */ char *colon; if ((colon=strchr(optarg, ':'))==NULL) { fprintf(stderr, "key option argument should be keydir:keyname\n"); exit(1); } keyname=colon+1; keyfile=optarg; *colon='\0'; break; } case 'n': keyname=optarg; break; default: usage(); } } + INIT_LIST(listuprec); + if (keyfile) { #ifdef PARSE_KEYFILE if ((fp=fopen(keyfile, "r"))==NULL) { perror("open keyfile"); exit(1); } /* now read the header info from the file */ if ((i=fread(buf, 1, BUFSIZ, fp)) < 5) { fclose(fp); exit(1); } fclose(fp); fp=NULL; p=buf; n=strlen(p); /* get length of strings */ n1=strlen("Private-key-format: v"); if (n1 > n || strncmp(buf, "Private-key-format: v", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ sscanf((char *)p, "%d.%d", &file_major, &file_minor); /* should do some error checking with these someday */ while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Algorithm: "); if (n1 > n || strncmp(p, "Algorithm: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ if (sscanf((char *)p, "%d", &alg)!=1) { fprintf(stderr, "Invalid key file format\n"); exit(1); } while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Key: "); if (n1 > n || strncmp(p, "Key: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ pp=p; while (*pp++!='\n'); /* skip to end of line, terminate it */ *--pp='\0'; key.data=malloc(1024*sizeof(char)); key.len=b64_pton(p, key.data, 1024); strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); #else /* use the dst* routines to parse the key files * * This requires that both the .key and the .private files * exist in your cwd, so the keyfile parmeter here is * assumed to be a path in which the K*.{key,private} files * exist. */ DST_KEY *dst_key; char cwd[PATH_MAX+1]; if (getcwd(cwd, PATH_MAX)==NULL) { perror("unable to get current directory"); exit(1); } if (chdir(keyfile)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", keyfile, strerror(errno)); exit(1); } dst_init(); dst_key = dst_read_key(keyname, 0 /* not used for private keys */, KEY_HMAC_MD5, DST_PRIVATE); if (!dst_key) { fprintf(stderr, "dst_read_key: error reading key\n"); exit(1); } key.data=malloc(1024*sizeof(char)); dst_key_to_buffer(dst_key, key.data, 1024); key.len=dst_key->dk_key_size; strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); if (chdir(cwd)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", cwd, strerror(errno)); exit(1); } #endif } if ((argc - optind) == 0) { /* no file specified, read from stdin */ ret = system("tty -s"); if (ret == 0) /* terminal */ prompt = 1; else /* stdin redirect from a file or a pipe */ prompt = 0; } else { /* file specified, open it */ /* XXX - currently accepts only one filename */ if ((fp = fopen(argv[optind], "r")) == NULL) { fprintf(stderr, "error opening file: %s\n", argv[optind]); exit (1); } } for (;;) { inside = 1; if (prompt) fprintf(stdout, "> "); if (!fp) cp = fgets(buf, sizeof buf, stdin); else cp = fgets(buf, sizeof buf, fp); if (cp == NULL) /* EOF */ break; lineno++; /* get rid of the trailing newline */ n = strlen(buf); buf[--n] = '\0'; startp = cp; endp = strchr(cp, ';'); if (endp != NULL) endp--; else endp = cp + n - 1; /* verify section name */ if (!getword_str(section, sizeof section, &startp, endp)) { /* empty line */ inside = 0; } if (inside) { /* inside the same update packet, * continue accumulating records */ r_section = -1; n1 = strlen(section); if (section[n1-1] == ':') section[--n1] = '\0'; for (mp = section_strs; mp < section_strs+M_SECTION_CNT; mp++) if (!strcasecmp(section, mp->token)) { r_section = mp->val; break; } if (r_section == -1) { fprintf(stderr, "incorrect section name: %s\n", section); exit (1); } if (r_section == S_ZONE) { fprintf(stderr, "section ZONE not permitted\n"); exit (1); } /* read operation code */ if (!getword_str(opcode, sizeof opcode, &startp, endp)) { fprintf(stderr, "failed to read operation code\n"); exit (1); } r_opcode = -1; if (opcode[0] == '{') { n1 = strlen(opcode); for (i = 0; i < n1; i++) opcode[i] = opcode[i+1]; if (opcode[n1-2] == '}') opcode[n1-2] = '\0'; } for (mp = opcode_strs; mp < opcode_strs+M_OPCODE_CNT; mp++) { if (!strcasecmp(opcode, mp->token)) { r_opcode = mp->val; break; } } if (r_opcode == -1) { fprintf(stderr, "incorrect operation code: %s\n", opcode); exit (1); } /* read owner's domain name */ if (!getword_str(dnbuf, sizeof dnbuf, &startp, endp)) { fprintf(stderr, "failed to read owner name\n"); exit (1); } r_dname = dnbuf; - r_ttl = 0; + r_ttl = (r_opcode == ADD) ? -1 : 0; r_type = -1; r_class = C_IN; /* default to IN */ r_size = 0; (void) getword_str(buf2, sizeof buf2, &startp, endp); if (isdigit(buf2[0])) { /* ttl */ r_ttl = strtoul(buf2, 0, 10); if (errno == ERANGE && r_ttl == ULONG_MAX) { fprintf(stderr, "oversized ttl: %s\n", buf2); exit (1); } (void) getword_str(buf2, sizeof buf2, &startp, endp); } if (buf2[0]) { /* possibly class */ for (mp = class_strs; mp < class_strs+M_CLASS_CNT; mp++) { if (!strcasecmp(buf2, mp->token)) { r_class = mp->val; (void) getword_str(buf2, sizeof buf2, &startp, endp); break; } } } /* * type and rdata field may or may not be required depending * on the section and operation */ switch (r_section) { case S_PREREQ: if (r_ttl) { fprintf(stderr, "nonzero ttl in prereq section: %ul\n", r_ttl); r_ttl = 0; } switch (r_opcode) { case NXDOMAIN: case YXDOMAIN: if (buf2[0]) { fprintf (stderr, "invalid field: %s, ignored\n", buf2); exit (1); } break; case NXRRSET: case YXRRSET: if (buf2[0]) for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++) if (!strcasecmp(buf2, mp->token)) { r_type = mp->val; break; } if (r_type == -1) { fprintf (stderr, "invalid type for RRset: %s\n", buf2); exit (1); } if (r_opcode == NXRRSET) break; /* * for RRset exists (value dependent) case, * nonempty rdata field will be present. * simply copy the whole string now and let * res_update() interpret the various fields * depending on type */ cp = startp; while (cp <= endp && isspace(*cp)) cp++; r_size = endp - cp + 1; break; default: fprintf (stderr, "unknown operation in prereq section\"%s\"\n", opcode); exit (1); } break; case S_UPDATE: switch (r_opcode) { case DELETE: r_ttl = 0; r_type = T_ANY; /* read type, if specified */ if (buf2[0]) for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++) if (!strcasecmp(buf2, mp->token)) { r_type = mp->val; svstartp = startp; (void) getword_str(buf2, sizeof buf2, &startp, endp); if (buf2[0]) /* unget preference */ startp = svstartp; break; } /* read rdata portion, if specified */ cp = startp; while (cp <= endp && isspace(*cp)) cp++; r_size = endp - cp + 1; break; case ADD: - if (r_ttl == 0) { + if (r_ttl == -1) { fprintf (stderr, "ttl must be specified for record to be added: %s\n", buf); exit (1); } /* read type */ if (buf2[0]) for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++) if (!strcasecmp(buf2, mp->token)) { r_type = mp->val; break; } if (r_type == -1) { fprintf(stderr, "invalid type for record to be added: %s\n", buf2); exit (1); } /* read rdata portion */ cp = startp; while (cp < endp && isspace(*cp)) cp++; r_size = endp - cp + 1; if (r_size <= 0) { fprintf(stderr, "nonempty rdata field needed to add the record at line %d\n", lineno); exit (1); } break; default: fprintf(stderr, "unknown operation in update section \"%s\"\n", opcode); exit (1); } break; default: fprintf(stderr, "unknown section identifier \"%s\"\n", section); exit (1); } if ( !(rrecp = res_mkupdrec(r_section, r_dname, r_class, r_type, r_ttl)) || (r_size > 0 && !(rrecp->r_data = (u_char *)malloc(r_size))) ) { if (rrecp) res_freeupdrec(rrecp); fprintf(stderr, "saverrec error\n"); exit (1); } if (stringtobin) { switch(r_opcode) { case T_HINFO: if (!getcharstring(buf,(char *)data,2,2,lineno)) exit(1); cp = data; break; case T_ISDN: if (!getcharstring(buf,(char *)data,1,2,lineno)) exit(1); cp = data; break; case T_TXT: if (!getcharstring(buf,(char *)data,1,0,lineno)) exit(1); cp = data; break; case T_X25: if (!getcharstring(buf,(char *)data,1,1,lineno)) exit(1); cp = data; break; default: break; } } rrecp->r_opcode = r_opcode; rrecp->r_size = r_size; (void) strncpy((char *)rrecp->r_data, cp, r_size); APPEND(listuprec, rrecp, r_link); } else { /* end of an update packet */ (void) res_ninit(&res); if (vc) res.options |= RES_USEVC | RES_STAYOPEN; if (debug) res.options |= RES_DEBUG; if (!EMPTY(listuprec)) { n = res_nupdate(&res, HEAD(listuprec), keyfile != NULL ? &key : NULL); if (n < 0) fprintf(stderr, "failed update packet\n"); while (!EMPTY(listuprec)) { ns_updrec *tmprrecp = HEAD(listuprec); UNLINK(listuprec, tmprrecp, r_link); if (tmprrecp->r_size != 0) free((char *)tmprrecp->r_data); res_freeupdrec(tmprrecp); } } } } /* for */ return (0); } static void usage() { fprintf(stderr, "Usage: %s [ -k keydir:keyname ] [-d] [-v] [file]\n", progname); exit(1); } /* * Get a whitespace delimited word from a string (not file) * into buf. modify the start pointer to point after the * word in the string. */ static int getword_str(char *buf, int size, char **startpp, char *endp) { char *cp; int c; for (cp = buf; *startpp <= endp; ) { c = **startpp; if (isspace(c) || c == '\0') { if (cp != buf) /* trailing whitespace */ break; else { /* leading whitespace */ (*startpp)++; continue; } } (*startpp)++; if (cp >= buf+size-1) break; *cp++ = (u_char)c; } *cp = '\0'; return (cp != buf); } #define MAXCHARSTRING 255 static int getcharstring(char *buf, char *data, int minfields, int maxfields, int lineno) { int nfield = 0, n = 0, i; do { nfield++; i = 0; if (*buf == '"') { buf++; while(buf[i] && buf[i] != '"') i++; } else { while(isspace(*buf)) i++; } if (i > MAXCHARSTRING) { fprintf(stderr, "%d: RDATA field %d too long", lineno, nfield); return(0); } if (n + i + 1 > MAXDATA) { fprintf(stderr, "%d: total RDATA too long", lineno); return(0); } data[n]=i; memmove(data + 1 + n, buf, i); buf += i + 1; n += i + 1; while(*buf && isspace(*buf)) buf++; } while (nfield < maxfields && *buf); if (nfield < minfields) { fprintf(stderr, "%d: expected %d RDATA fields, only saw %d", lineno, minfields, nfield); return (0); } return (n); } Index: head/contrib/bind/include/arpa/nameser.h =================================================================== --- head/contrib/bind/include/arpa/nameser.h (revision 60940) +++ head/contrib/bind/include/arpa/nameser.h (revision 60941) @@ -1,556 +1,559 @@ /* * Copyright (c) 1983, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* - * $Id: nameser.h,v 8.36 1999/10/15 19:49:08 vixie Exp $ + * $Id: nameser.h,v 8.37 2000/03/30 21:16:49 vixie Exp $ */ #ifndef _ARPA_NAMESER_H_ #define _ARPA_NAMESER_H_ #define BIND_4_COMPAT #include #if (!defined(BSD)) || (BSD < 199306) # include #else # include #endif #include /* * Revision information. This is the release date in YYYYMMDD format. * It can change every day so the right thing to do with it is use it * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not * compare for equality; rather, use it to determine whether your libbind.a * contains a new enough lib/nameser/ to support the feature you need. */ #define __NAMESER 19991006 /* New interface version stamp. */ /* * Define constants based on RFC 883, RFC 1034, RFC 1035 */ #define NS_PACKETSZ 512 /* maximum packet size */ #define NS_MAXDNAME 1025 /* maximum domain name */ #define NS_MAXCDNAME 255 /* maximum compressed domain name */ #define NS_MAXLABEL 63 /* maximum length of domain label */ #define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ #define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ #define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ #define NS_INT32SZ 4 /* #/bytes of data in a u_int32_t */ #define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */ #define NS_INT8SZ 1 /* #/bytes of data in a u_int8_t */ #define NS_INADDRSZ 4 /* IPv4 T_A */ #define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */ #define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */ #define NS_DEFAULTPORT 53 /* For both TCP and UDP. */ /* * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord() * in synch with it. */ typedef enum __ns_sect { ns_s_qd = 0, /* Query: Question. */ ns_s_zn = 0, /* Update: Zone. */ ns_s_an = 1, /* Query: Answer. */ ns_s_pr = 1, /* Update: Prerequisites. */ ns_s_ns = 2, /* Query: Name servers. */ ns_s_ud = 2, /* Update: Update. */ ns_s_ar = 3, /* Query|Update: Additional records. */ ns_s_max = 4 } ns_sect; /* * This is a message handle. It is caller allocated and has no dynamic data. * This structure is intended to be opaque to all but ns_parse.c, thus the * leading _'s on the member names. Use the accessor functions, not the _'s. */ typedef struct __ns_msg { const u_char *_msg, *_eom; u_int16_t _id, _flags, _counts[ns_s_max]; const u_char *_sections[ns_s_max]; ns_sect _sect; int _rrnum; const u_char *_ptr; } ns_msg; /* Private data structure - do not use from outside library. */ struct _ns_flagdata { int mask, shift; }; extern struct _ns_flagdata _ns_flagdata[]; /* Accessor macros - this is part of the public interface. */ #define ns_msg_getflag(handle, flag) ( \ ((handle)._flags & _ns_flagdata[flag].mask) \ >> _ns_flagdata[flag].shift \ ) #define ns_msg_id(handle) ((handle)._id + 0) #define ns_msg_base(handle) ((handle)._msg + 0) #define ns_msg_end(handle) ((handle)._eom + 0) #define ns_msg_size(handle) ((handle)._eom - (handle)._msg) #define ns_msg_count(handle, section) ((handle)._counts[section] + 0) /* * This is a parsed record. It is caller allocated and has no dynamic data. */ typedef struct __ns_rr { char name[NS_MAXDNAME]; u_int16_t type; u_int16_t rr_class; u_int32_t ttl; u_int16_t rdlength; const u_char * rdata; } ns_rr; /* Accessor macros - this is part of the public interface. */ #define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".") #define ns_rr_type(rr) ((ns_type)((rr).type + 0)) #define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0)) #define ns_rr_ttl(rr) ((rr).ttl + 0) #define ns_rr_rdlen(rr) ((rr).rdlength + 0) #define ns_rr_rdata(rr) ((rr).rdata + 0) /* * These don't have to be in the same order as in the packet flags word, * and they can even overlap in some cases, but they will need to be kept * in synch with ns_parse.c:ns_flagdata[]. */ typedef enum __ns_flag { ns_f_qr, /* Question/Response. */ ns_f_opcode, /* Operation code. */ ns_f_aa, /* Authoritative Answer. */ ns_f_tc, /* Truncation occurred. */ ns_f_rd, /* Recursion Desired. */ ns_f_ra, /* Recursion Available. */ ns_f_z, /* MBZ. */ ns_f_ad, /* Authentic Data (DNSSEC). */ ns_f_cd, /* Checking Disabled (DNSSEC). */ ns_f_rcode, /* Response code. */ ns_f_max } ns_flag; /* * Currently defined opcodes. */ typedef enum __ns_opcode { ns_o_query = 0, /* Standard query. */ ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */ ns_o_status = 2, /* Name server status query (unsupported). */ /* Opcode 3 is undefined/reserved. */ ns_o_notify = 4, /* Zone change notification. */ ns_o_update = 5, /* Zone update message. */ ns_o_max = 6 } ns_opcode; /* * Currently defined response codes. */ typedef enum __ns_rcode { ns_r_noerror = 0, /* No error occurred. */ ns_r_formerr = 1, /* Format error. */ ns_r_servfail = 2, /* Server failure. */ ns_r_nxdomain = 3, /* Name error. */ ns_r_notimpl = 4, /* Unimplemented. */ ns_r_refused = 5, /* Operation refused. */ /* these are for BIND_UPDATE */ ns_r_yxdomain = 6, /* Name exists */ ns_r_yxrrset = 7, /* RRset exists */ ns_r_nxrrset = 8, /* RRset does not exist */ ns_r_notauth = 9, /* Not authoritative for zone */ ns_r_notzone = 10, /* Zone of record different from zone section */ ns_r_max = 11, /* The following are TSIG extended errors */ ns_r_badsig = 16, ns_r_badkey = 17, ns_r_badtime = 18 } ns_rcode; /* BIND_UPDATE */ typedef enum __ns_update_operation { ns_uop_delete = 0, ns_uop_add = 1, ns_uop_max = 2 } ns_update_operation; /* * This structure is used for TSIG authenticated messages */ struct ns_tsig_key { char name[NS_MAXDNAME], alg[NS_MAXDNAME]; unsigned char *data; int len; }; typedef struct ns_tsig_key ns_tsig_key; /* * This structure is used for TSIG authenticated TCP messages */ struct ns_tcp_tsig_state { int counter; struct dst_key *key; void *ctx; unsigned char sig[NS_PACKETSZ]; int siglen; }; typedef struct ns_tcp_tsig_state ns_tcp_tsig_state; #define NS_TSIG_FUDGE 300 #define NS_TSIG_TCP_COUNT 100 #define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT" #define NS_TSIG_ERROR_NO_TSIG -10 #define NS_TSIG_ERROR_NO_SPACE -11 #define NS_TSIG_ERROR_FORMERR -12 /* * Currently defined type values for resources and queries. */ typedef enum __ns_type { ns_t_invalid = 0, /* Cookie. */ ns_t_a = 1, /* Host address. */ ns_t_ns = 2, /* Authoritative server. */ ns_t_md = 3, /* Mail destination. */ ns_t_mf = 4, /* Mail forwarder. */ ns_t_cname = 5, /* Canonical name. */ ns_t_soa = 6, /* Start of authority zone. */ ns_t_mb = 7, /* Mailbox domain name. */ ns_t_mg = 8, /* Mail group member. */ ns_t_mr = 9, /* Mail rename name. */ ns_t_null = 10, /* Null resource record. */ ns_t_wks = 11, /* Well known service. */ ns_t_ptr = 12, /* Domain name pointer. */ ns_t_hinfo = 13, /* Host information. */ ns_t_minfo = 14, /* Mailbox information. */ ns_t_mx = 15, /* Mail routing information. */ ns_t_txt = 16, /* Text strings. */ ns_t_rp = 17, /* Responsible person. */ ns_t_afsdb = 18, /* AFS cell database. */ ns_t_x25 = 19, /* X_25 calling address. */ ns_t_isdn = 20, /* ISDN calling address. */ ns_t_rt = 21, /* Router. */ ns_t_nsap = 22, /* NSAP address. */ ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ ns_t_sig = 24, /* Security signature. */ ns_t_key = 25, /* Security key. */ ns_t_px = 26, /* X.400 mail mapping. */ ns_t_gpos = 27, /* Geographical position (withdrawn). */ ns_t_aaaa = 28, /* Ip6 Address. */ ns_t_loc = 29, /* Location Information. */ ns_t_nxt = 30, /* Next domain (security). */ ns_t_eid = 31, /* Endpoint identifier. */ ns_t_nimloc = 32, /* Nimrod Locator. */ ns_t_srv = 33, /* Server Selection. */ ns_t_atma = 34, /* ATM Address */ ns_t_naptr = 35, /* Naming Authority PoinTeR */ ns_t_kx = 36, /* Key Exchange */ ns_t_cert = 37, /* Certification record */ ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ ns_t_sink = 40, /* Kitchen sink (experimentatl) */ ns_t_opt = 41, /* EDNS0 option (meta-RR) */ ns_t_tsig = 250, /* Transaction signature. */ ns_t_ixfr = 251, /* Incremental zone transfer. */ ns_t_axfr = 252, /* Transfer zone of authority. */ ns_t_mailb = 253, /* Transfer mailbox records. */ ns_t_maila = 254, /* Transfer mail agent records. */ ns_t_any = 255, /* Wildcard match. */ ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ ns_t_max = 65536 } ns_type; /* Exclusively a QTYPE? (not also an RTYPE) */ #define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \ (t) == ns_t_mailb || (t) == ns_t_maila) /* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */ #define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt) /* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */ #define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t)) #define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr) #define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \ (t) == ns_t_zxfr) /* * Values for class field */ typedef enum __ns_class { ns_c_invalid = 0, /* Cookie. */ ns_c_in = 1, /* Internet. */ ns_c_2 = 2, /* unallocated/unsupported. */ ns_c_chaos = 3, /* MIT Chaos-net. */ ns_c_hs = 4, /* MIT Hesiod. */ /* Query class values which do not appear in resource records */ ns_c_none = 254, /* for prereq. sections in update requests */ ns_c_any = 255, /* Wildcard match. */ ns_c_max = 65536 } ns_class; /* DNSSEC constants. */ typedef enum __ns_key_types { ns_kt_rsa = 1, /* key type RSA/MD5 */ ns_kt_dh = 2, /* Diffie Hellman */ ns_kt_dsa = 3, /* Digital Signature Standard (MANDATORY) */ ns_kt_private = 254 /* Private key type starts with OID */ } ns_key_types; typedef enum __ns_cert_types { cert_t_pkix = 1, /* PKIX (X.509v3) */ cert_t_spki = 2, /* SPKI */ cert_t_pgp = 3, /* PGP */ cert_t_url = 253, /* URL private type */ cert_t_oid = 254 /* OID private type */ } ns_cert_types; /* Flags field of the KEY RR rdata. */ #define NS_KEY_TYPEMASK 0xC000 /* Mask for "type" bits */ #define NS_KEY_TYPE_AUTH_CONF 0x0000 /* Key usable for both */ #define NS_KEY_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */ #define NS_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */ #define NS_KEY_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */ /* The type bits can also be interpreted independently, as single bits: */ #define NS_KEY_NO_AUTH 0x8000 /* Key unusable for authentication */ #define NS_KEY_NO_CONF 0x4000 /* Key unusable for confidentiality */ #define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */ #define NS_KEY_EXTENDED_FLAGS 0x1000 /* reserved - must be zero */ #define NS_KEY_RESERVED4 0x0800 /* reserved - must be zero */ #define NS_KEY_RESERVED5 0x0400 /* reserved - must be zero */ #define NS_KEY_NAME_TYPE 0x0300 /* these bits determine the type */ #define NS_KEY_NAME_USER 0x0000 /* key is assoc. with user */ #define NS_KEY_NAME_ENTITY 0x0200 /* key is assoc. with entity eg host */ #define NS_KEY_NAME_ZONE 0x0100 /* key is zone key */ #define NS_KEY_NAME_RESERVED 0x0300 /* reserved meaning */ #define NS_KEY_RESERVED8 0x0080 /* reserved - must be zero */ #define NS_KEY_RESERVED9 0x0040 /* reserved - must be zero */ #define NS_KEY_RESERVED10 0x0020 /* reserved - must be zero */ #define NS_KEY_RESERVED11 0x0010 /* reserved - must be zero */ #define NS_KEY_SIGNATORYMASK 0x000F /* key can sign RR's of same name */ #define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \ NS_KEY_RESERVED4 | \ NS_KEY_RESERVED5 | \ NS_KEY_RESERVED8 | \ NS_KEY_RESERVED9 | \ NS_KEY_RESERVED10 | \ NS_KEY_RESERVED11 ) #define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */ /* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */ #define NS_ALG_MD5RSA 1 /* MD5 with RSA */ #define NS_ALG_DH 2 /* Diffie Hellman KEY */ #define NS_ALG_DSA 3 /* DSA KEY */ #define NS_ALG_DSS NS_ALG_DSA #define NS_ALG_EXPIRE_ONLY 253 /* No alg, no security */ #define NS_ALG_PRIVATE_OID 254 /* Key begins with OID giving alg */ /* Protocol values */ /* value 0 is reserved */ #define NS_KEY_PROT_TLS 1 #define NS_KEY_PROT_EMAIL 2 #define NS_KEY_PROT_DNSSEC 3 #define NS_KEY_PROT_IPSEC 4 #define NS_KEY_PROT_ANY 255 /* Signatures */ #define NS_MD5RSA_MIN_BITS 512 /* Size of a mod or exp in bits */ #define NS_MD5RSA_MAX_BITS 2552 /* Total of binary mod and exp */ #define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3) /* Max length of text sig block */ #define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4) #define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8) #define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8) #define NS_DSA_SIG_SIZE 41 #define NS_DSA_MIN_SIZE 213 #define NS_DSA_MAX_BYTES 405 /* Offsets into SIG record rdata to find various values */ #define NS_SIG_TYPE 0 /* Type flags */ #define NS_SIG_ALG 2 /* Algorithm */ #define NS_SIG_LABELS 3 /* How many labels in name */ #define NS_SIG_OTTL 4 /* Original TTL */ #define NS_SIG_EXPIR 8 /* Expiration time */ #define NS_SIG_SIGNED 12 /* Signature time */ #define NS_SIG_FOOT 16 /* Key footprint */ #define NS_SIG_SIGNER 18 /* Domain name of who signed it */ /* How RR types are represented as bit-flags in NXT records */ #define NS_NXT_BITS 8 #define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_MAX 127 /* * Inline versions of get/put short/long. Pointer is advanced. */ #define NS_GET16(s, cp) do { \ register u_char *t_cp = (u_char *)(cp); \ (s) = ((u_int16_t)t_cp[0] << 8) \ | ((u_int16_t)t_cp[1]) \ ; \ (cp) += NS_INT16SZ; \ } while (0) #define NS_GET32(l, cp) do { \ register u_char *t_cp = (u_char *)(cp); \ (l) = ((u_int32_t)t_cp[0] << 24) \ | ((u_int32_t)t_cp[1] << 16) \ | ((u_int32_t)t_cp[2] << 8) \ | ((u_int32_t)t_cp[3]) \ ; \ (cp) += NS_INT32SZ; \ } while (0) #define NS_PUT16(s, cp) do { \ register u_int16_t t_s = (u_int16_t)(s); \ register u_char *t_cp = (u_char *)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += NS_INT16SZ; \ } while (0) #define NS_PUT32(l, cp) do { \ register u_int32_t t_l = (u_int32_t)(l); \ register u_char *t_cp = (u_char *)(cp); \ *t_cp++ = t_l >> 24; \ *t_cp++ = t_l >> 16; \ *t_cp++ = t_l >> 8; \ *t_cp = t_l; \ (cp) += NS_INT32SZ; \ } while (0) /* * ANSI C identifier hiding for bind's lib/nameser. */ #define ns_get16 __ns_get16 #define ns_get32 __ns_get32 #define ns_put16 __ns_put16 #define ns_put32 __ns_put32 #define ns_initparse __ns_initparse #define ns_skiprr __ns_skiprr #define ns_parserr __ns_parserr #define ns_sprintrr __ns_sprintrr #define ns_sprintrrf __ns_sprintrrf #define ns_format_ttl __ns_format_ttl #define ns_parse_ttl __ns_parse_ttl #define ns_datetosecs __ns_datetosecs #define ns_name_ntol __ns_name_ntol #define ns_name_ntop __ns_name_ntop #define ns_name_pton __ns_name_pton #define ns_name_unpack __ns_name_unpack #define ns_name_pack __ns_name_pack #define ns_name_compress __ns_name_compress #define ns_name_uncompress __ns_name_uncompress #define ns_name_skip __ns_name_skip +#define ns_name_rollback __ns_name_rollback #define ns_sign __ns_sign #define ns_sign_tcp __ns_sign_tcp #define ns_sign_tcp_init __ns_sign_tcp_init #define ns_find_tsig __ns_find_tsig #define ns_verify __ns_verify #define ns_verify_tcp __ns_verify_tcp #define ns_verify_tcp_init __ns_verify_tcp_init #define ns_samedomain __ns_samedomain #define ns_subdomain __ns_subdomain #define ns_makecanon __ns_makecanon #define ns_samename __ns_samename __BEGIN_DECLS u_int ns_get16 __P((const u_char *)); u_long ns_get32 __P((const u_char *)); void ns_put16 __P((u_int, u_char *)); void ns_put32 __P((u_long, u_char *)); int ns_initparse __P((const u_char *, int, ns_msg *)); int ns_skiprr __P((const u_char *, const u_char *, ns_sect, int)); int ns_parserr __P((ns_msg *, ns_sect, int, ns_rr *)); int ns_sprintrr __P((const ns_msg *, const ns_rr *, const char *, const char *, char *, size_t)); int ns_sprintrrf __P((const u_char *, size_t, const char *, ns_class, ns_type, u_long, const u_char *, size_t, const char *, const char *, char *, size_t)); int ns_format_ttl __P((u_long, char *, size_t)); int ns_parse_ttl __P((const char *, u_long *)); u_int32_t ns_datetosecs __P((const char *cp, int *errp)); int ns_name_ntol __P((const u_char *, u_char *, size_t)); int ns_name_ntop __P((const u_char *, char *, size_t)); int ns_name_pton __P((const char *, u_char *, size_t)); int ns_name_unpack __P((const u_char *, const u_char *, const u_char *, u_char *, size_t)); int ns_name_pack __P((const u_char *, u_char *, int, const u_char **, const u_char **)); int ns_name_uncompress __P((const u_char *, const u_char *, const u_char *, char *, size_t)); int ns_name_compress __P((const char *, u_char *, size_t, const u_char **, const u_char **)); int ns_name_skip __P((const u_char **, const u_char *)); +void ns_name_rollback __P((const u_char *, const u_char **, + const u_char **)); int ns_sign __P((u_char *, int *, int, int, void *, const u_char *, int, u_char *, int *, time_t)); int ns_sign_tcp __P((u_char *, int *, int, int, ns_tcp_tsig_state *, int)); int ns_sign_tcp_init __P((void *, const u_char *, int, ns_tcp_tsig_state *)); u_char *ns_find_tsig __P((u_char *, u_char *)); int ns_verify __P((u_char *, int *, void *, const u_char *, int, u_char *, int *, time_t *, int)); int ns_verify_tcp __P((u_char *, int *, ns_tcp_tsig_state *, int)); int ns_verify_tcp_init __P((void *, const u_char *, int, ns_tcp_tsig_state *)); int ns_samedomain __P((const char *, const char *)); int ns_subdomain __P((const char *, const char *)); int ns_makecanon __P((const char *, char *, size_t)); int ns_samename __P((const char *, const char *)); __END_DECLS #ifdef BIND_4_COMPAT #include #endif #endif /* !_ARPA_NAMESER_H_ */ Index: head/contrib/bind/include/isc/logging.h =================================================================== --- head/contrib/bind/include/isc/logging.h (revision 60940) +++ head/contrib/bind/include/isc/logging.h (revision 60941) @@ -1,98 +1,102 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef LOGGING_H #define LOGGING_H +#include #include #include +#include #define log_critical (-5) #define log_error (-4) #define log_warning (-3) #define log_notice (-2) #define log_info (-1) #define log_debug(level) (level) typedef enum { log_syslog, log_file, log_null } log_channel_type; #define LOG_MAX_VERSIONS 99 #define LOG_CLOSE_STREAM 0x0001 #define LOG_TIMESTAMP 0x0002 #define LOG_TRUNCATE 0x0004 #define LOG_USE_CONTEXT_LEVEL 0x0008 #define LOG_PRINT_LEVEL 0x0010 #define LOG_REQUIRE_DEBUG 0x0020 #define LOG_CHANNEL_BROKEN 0x0040 #define LOG_PRINT_CATEGORY 0x0080 #define LOG_CHANNEL_OFF 0x0100 typedef struct log_context *log_context; typedef struct log_channel *log_channel; #define LOG_OPTION_DEBUG 0x01 #define LOG_OPTION_LEVEL 0x02 #define log_open_stream __log_open_stream #define log_close_stream __log_close_stream #define log_get_stream __log_get_stream #define log_get_filename __log_get_filename #define log_check_channel __log_check_channel #define log_check __log_check #define log_vwrite __log_vwrite #define log_write __log_write #define log_new_context __log_new_context #define log_free_context __log_free_context #define log_add_channel __log_add_channel #define log_remove_channel __log_remove_channel #define log_option __log_option #define log_category_is_active __log_category_is_active #define log_new_syslog_channel __log_new_syslog_channel #define log_new_file_channel __log_new_file_channel +#define log_set_file_owner __log_set_file_owner #define log_new_null_channel __log_new_null_channel #define log_inc_references __log_inc_references #define log_dec_references __log_dec_references #define log_get_channel_type __log_get_channel_type #define log_free_channel __log_free_channel FILE * log_open_stream(log_channel); int log_close_stream(log_channel); FILE * log_get_stream(log_channel); char * log_get_filename(log_channel); int log_check_channel(log_context, int, log_channel); int log_check(log_context, int, int); void log_vwrite(log_context, int, int, const char *, va_list args); void log_write(log_context, int, int, const char *, ...); int log_new_context(int, char **, log_context *); void log_free_context(log_context); int log_add_channel(log_context, int, log_channel); int log_remove_channel(log_context, int, log_channel); int log_option(log_context, int, int); int log_category_is_active(log_context, int); log_channel log_new_syslog_channel(unsigned int, int, int); log_channel log_new_file_channel(unsigned int, int, char *, FILE *, unsigned int, unsigned long); +int log_set_file_owner(log_channel, uid_t, gid_t); log_channel log_new_null_channel(void); int log_inc_references(log_channel); int log_dec_references(log_channel); log_channel_type log_get_channel_type(log_channel); int log_free_channel(log_channel); #endif /* !LOGGING_H */ Index: head/contrib/bind/include/resolv.h =================================================================== --- head/contrib/bind/include/resolv.h (revision 60940) +++ head/contrib/bind/include/resolv.h (revision 60941) @@ -1,397 +1,407 @@ /* * Copyright (c) 1983, 1987, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * @(#)resolv.h 8.1 (Berkeley) 6/2/93 - * $Id: resolv.h,v 8.29 1999/10/07 08:24:14 vixie Exp $ + * $Id: resolv.h,v 8.31 2000/03/30 20:16:50 vixie Exp $ */ #ifndef _RESOLV_H_ #define _RESOLV_H_ #include #if (!defined(BSD)) || (BSD < 199306) # include #else # include #endif #include #include /* * Revision information. This is the release date in YYYYMMDD format. * It can change every day so the right thing to do with it is use it * in preprocessor commands such as "#if (__RES > 19931104)". Do not * compare for equality; rather, use it to determine whether your resolver * is new enough to contain a certain feature. */ #define __RES 19991006 /* * This used to be defined in res_query.c, now it's in herror.c. * [XXX no it's not. It's in irs/irs_data.c] * It was * never extern'd by any *.h file before it was placed here. For thread * aware programs, the last h_errno value set is stored in res->h_errno. * * XXX: There doesn't seem to be a good reason for exposing RES_SET_H_ERRNO * (and __h_errno_set) to the public via . * XXX: __h_errno_set is really part of IRS, not part of the resolver. * If somebody wants to build and use a resolver that doesn't use IRS, * what do they do? Perhaps something like * #ifdef WANT_IRS * # define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x) * #else * # define RES_SET_H_ERRNO(r,x) (h_errno = (r)->res_h_errno = (x)) * #endif */ #define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x) struct __res_state; /* forward */ void __h_errno_set(struct __res_state *res, int err); /* * Resolver configuration file. * Normally not present, but may contain the address of the * inital name server(s) to query and the domain search list. */ #ifndef _PATH_RESCONF #define _PATH_RESCONF "/etc/resolv.conf" #endif typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error } res_sendhookact; typedef res_sendhookact (*res_send_qhook)__P((struct sockaddr_in * const *ns, const u_char **query, int *querylen, u_char *ans, int anssiz, int *resplen)); typedef res_sendhookact (*res_send_rhook)__P((const struct sockaddr_in *ns, const u_char *query, int querylen, u_char *ans, int anssiz, int *resplen)); struct res_sym { int number; /* Identifying number, like T_MX */ char * name; /* Its symbolic name, like "MX" */ char * humanname; /* Its fun name, like "mail exchanger" */ }; /* * Global defines and variables for resolver stub. */ #define MAXNS 3 /* max # name servers we'll track */ #define MAXDFLSRCH 3 /* # default domain levels to try */ #define MAXDNSRCH 6 /* max # domains in search path */ #define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */ #define RES_TIMEOUT 5 /* min. seconds between retries */ #define MAXRESOLVSORT 10 /* number of net to sort on */ #define RES_MAXNDOTS 15 /* should reflect bit field size */ #define RES_MAXRETRANS 30 /* only for resolv.conf/RES_OPTIONS */ #define RES_MAXRETRY 5 /* only for resolv.conf/RES_OPTIONS */ #define RES_DFLRETRY 2 /* Default #/tries. */ +#define RES_MAXTIME 65535 /* Infinity, in milliseconds. */ struct __res_state { int retrans; /* retransmition time interval */ int retry; /* number of times to retransmit */ u_long options; /* option flags - see below. */ int nscount; /* number of name servers */ struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */ #define nsaddr nsaddr_list[0] /* for backward compatibility */ u_short id; /* current message id */ char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */ char defdname[256]; /* default domain (deprecated) */ u_long pfcode; /* RES_PRF_ flags - see below. */ unsigned ndots:4; /* threshold for initial abs. query */ unsigned nsort:4; /* number of elements in sort_list[] */ char unused[3]; struct { struct in_addr addr; u_int32_t mask; } sort_list[MAXRESOLVSORT]; res_send_qhook qhook; /* query hook */ res_send_rhook rhook; /* response hook */ int res_h_errno; /* last one set for this context */ - int _sock; /* PRIVATE: for res_send i/o */ + int _vcsock; /* PRIVATE: for res_send VC i/o */ u_int _flags; /* PRIVATE: see below */ - char pad[52]; /* On an i386 this means 512b total. */ + union { + char pad[52]; /* On an i386 this means 512b total. */ + struct { + u_int16_t nscount; + u_int16_t nstimes[MAXNS]; /* ms. */ + int nssocks[MAXNS]; + struct sockaddr_in nsaddrs[MAXNS]; + } _ext; + } _u; }; typedef struct __res_state *res_state; /* * Resolver flags (used to be discrete per-module statics ints). */ #define RES_F_VC 0x00000001 /* socket is TCP */ #define RES_F_CONN 0x00000002 /* socket is connected */ /* res_findzonecut() options */ #define RES_EXHAUSTIVE 0x00000001 /* always do all queries */ /* * Resolver options (keep these in synch with res_debug.c, please) */ #define RES_INIT 0x00000001 /* address initialized */ #define RES_DEBUG 0x00000002 /* print debug messages */ #define RES_AAONLY 0x00000004 /* authoritative answers only (!IMPL)*/ #define RES_USEVC 0x00000008 /* use virtual circuit */ #define RES_PRIMARY 0x00000010 /* query primary server only (!IMPL) */ #define RES_IGNTC 0x00000020 /* ignore trucation errors */ #define RES_RECURSE 0x00000040 /* recursion desired */ #define RES_DEFNAMES 0x00000080 /* use default domain name */ #define RES_STAYOPEN 0x00000100 /* Keep TCP socket open */ #define RES_DNSRCH 0x00000200 /* search up local domain tree */ #define RES_INSECURE1 0x00000400 /* type 1 security disabled */ #define RES_INSECURE2 0x00000800 /* type 2 security disabled */ #define RES_NOALIASES 0x00001000 /* shuts off HOSTALIASES feature */ #define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */ #define RES_ROTATE 0x00004000 /* rotate ns list after each query */ #define RES_NOCHECKNAME 0x00008000 /* do not check names for sanity. */ #define RES_KEEPTSIG 0x00010000 /* do not strip TSIG records */ +#define RES_BLAST 0x00020000 /* blast all recursive servers */ #define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH) /* * Resolver "pfcode" values. Used by dig. */ #define RES_PRF_STATS 0x00000001 #define RES_PRF_UPDATE 0x00000002 #define RES_PRF_CLASS 0x00000004 #define RES_PRF_CMD 0x00000008 #define RES_PRF_QUES 0x00000010 #define RES_PRF_ANS 0x00000020 #define RES_PRF_AUTH 0x00000040 #define RES_PRF_ADD 0x00000080 #define RES_PRF_HEAD1 0x00000100 #define RES_PRF_HEAD2 0x00000200 #define RES_PRF_TTLID 0x00000400 #define RES_PRF_HEADX 0x00000800 #define RES_PRF_QUERY 0x00001000 #define RES_PRF_REPLY 0x00002000 #define RES_PRF_INIT 0x00004000 /* 0x00008000 */ /* Things involving an internal (static) resolver context. */ #ifdef _REENTRANT extern struct __res_state *__res_state(void); #define _res (*__res_state()) #else #ifndef __BIND_NOSTATIC extern struct __res_state _res; #endif #endif #ifndef __BIND_NOSTATIC #define fp_nquery __fp_nquery #define fp_query __fp_query #define hostalias __hostalias #define p_query __p_query #define res_close __res_close #define res_init __res_init #define res_isourserver __res_isourserver #define res_mkquery __res_mkquery #define res_query __res_query #define res_querydomain __res_querydomain #define res_search __res_search #define res_send __res_send #define res_sendsigned __res_sendsigned __BEGIN_DECLS void fp_nquery __P((const u_char *, int, FILE *)); void fp_query __P((const u_char *, FILE *)); const char * hostalias __P((const char *)); void p_query __P((const u_char *)); void res_close __P((void)); int res_init __P((void)); int res_isourserver __P((const struct sockaddr_in *)); int res_mkquery __P((int, const char *, int, int, const u_char *, int, const u_char *, u_char *, int)); int res_query __P((const char *, int, int, u_char *, int)); int res_querydomain __P((const char *, const char *, int, int, u_char *, int)); int res_search __P((const char *, int, int, u_char *, int)); int res_send __P((const u_char *, int, u_char *, int)); int res_sendsigned __P((const u_char *, int, ns_tsig_key *, u_char *, int)); __END_DECLS #endif #if !defined(SHARED_LIBBIND) || defined(LIB) /* * If libbind is a shared object (well, DLL anyway) * these externs break the linker when resolv.h is * included by a lib client (like named) * Make them go away if a client is including this * */ extern const struct res_sym __p_key_syms[]; extern const struct res_sym __p_cert_syms[]; extern const struct res_sym __p_class_syms[]; extern const struct res_sym __p_type_syms[]; extern const struct res_sym __p_rcode_syms[]; #endif /* SHARED_LIBBIND */ #define b64_ntop __b64_ntop #define b64_pton __b64_pton #define dn_comp __dn_comp #define dn_count_labels __dn_count_labels #define dn_expand __dn_expand #define dn_skipname __dn_skipname #define fp_resstat __fp_resstat #define loc_aton __loc_aton #define loc_ntoa __loc_ntoa #define p_cdname __p_cdname #define p_cdnname __p_cdnname #define p_class __p_class #define p_fqname __p_fqname #define p_fqnname __p_fqnname #define p_option __p_option #define p_secstodate __p_secstodate #define p_section __p_section #define p_time __p_time #define p_type __p_type #define p_rcode __p_rcode #define putlong __putlong #define putshort __putshort #define res_dnok __res_dnok #define res_findzonecut __res_findzonecut #define res_hnok __res_hnok #define res_hostalias __res_hostalias #define res_mailok __res_mailok #define res_nameinquery __res_nameinquery #define res_nclose __res_nclose #define res_ninit __res_ninit #define res_nmkquery __res_nmkquery #define res_npquery __res_npquery #define res_nquery __res_nquery #define res_nquerydomain __res_nquerydomain #define res_nsearch __res_nsearch #define res_nsend __res_nsend #define res_nsendsigned __res_nsendsigned #define res_nisourserver __res_nisourserver #define res_ownok __res_ownok #define res_queriesmatch __res_queriesmatch #define res_randomid __res_randomid #define sym_ntop __sym_ntop #define sym_ntos __sym_ntos #define sym_ston __sym_ston __BEGIN_DECLS int res_hnok __P((const char *)); int res_ownok __P((const char *)); int res_mailok __P((const char *)); int res_dnok __P((const char *)); int sym_ston __P((const struct res_sym *, const char *, int *)); const char * sym_ntos __P((const struct res_sym *, int, int *)); const char * sym_ntop __P((const struct res_sym *, int, int *)); int b64_ntop __P((u_char const *, size_t, char *, size_t)); int b64_pton __P((char const *, u_char *, size_t)); int loc_aton __P((const char *ascii, u_char *binary)); const char * loc_ntoa __P((const u_char *binary, char *ascii)); int dn_skipname __P((const u_char *, const u_char *)); void putlong __P((u_int32_t, u_char *)); void putshort __P((u_int16_t, u_char *)); const char * p_class __P((int)); const char * p_time __P((u_int32_t)); const char * p_type __P((int)); const char * p_rcode __P((int)); const u_char * p_cdnname __P((const u_char *, const u_char *, int, FILE *)); const u_char * p_cdname __P((const u_char *, const u_char *, FILE *)); const u_char * p_fqnname __P((const u_char *cp, const u_char *msg, int, char *, int)); const u_char * p_fqname __P((const u_char *, const u_char *, FILE *)); const char * p_option __P((u_long option)); char * p_secstodate __P((u_long)); int dn_count_labels __P((const char *)); int dn_comp __P((const char *, u_char *, int, u_char **, u_char **)); int dn_expand __P((const u_char *, const u_char *, const u_char *, char *, int)); u_int res_randomid __P((void)); int res_nameinquery __P((const char *, int, int, const u_char *, const u_char *)); int res_queriesmatch __P((const u_char *, const u_char *, const u_char *, const u_char *)); const char * p_section __P((int section, int opcode)); /* Things involving a resolver context. */ int res_ninit __P((res_state)); int res_nisourserver __P((const res_state, const struct sockaddr_in *)); void fp_resstat __P((const res_state, FILE *)); void res_npquery __P((const res_state, const u_char *, int, FILE *)); const char * res_hostalias __P((const res_state, const char *, char *, size_t)); int res_nquery __P((res_state, const char *, int, int, u_char *, int)); int res_nsearch __P((res_state, const char *, int, int, u_char *, int)); int res_nquerydomain __P((res_state, const char *, const char *, int, int, u_char *, int)); int res_nmkquery __P((res_state, int, const char *, int, int, const u_char *, int, const u_char *, u_char *, int)); int res_nsend __P((res_state, const u_char *, int, u_char *, int)); int res_nsendsigned __P((res_state, const u_char *, int, ns_tsig_key *, u_char *, int)); int res_findzonecut __P((res_state, const char *, ns_class, int, char *, size_t, struct in_addr *, int)); void res_nclose __P((res_state)); __END_DECLS #endif /* !_RESOLV_H_ */ Index: head/contrib/bind/lib/Makefile =================================================================== --- head/contrib/bind/lib/Makefile (revision 60940) +++ head/contrib/bind/lib/Makefile (revision 60941) @@ -1,109 +1,109 @@ # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. # $Id: Makefile,v 8.22 1999/06/08 01:42:57 vixie Exp $ -SUBDIRS = resolv irs isc bsd inet nameser dst +SUBDIRS = resolv irs isc bsd inet nameser dst cylink dnssafe # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos SHELL= /bin/sh O=o A=a DESTDIR= DESTINC= /usr/local/bind/include DESTLIB= /usr/local/bind/lib TOP= .. INCL= ${TOP}/include PORTINCL= ${TOP}/port/${SYSTYPE}/include LIBBIND= ${TOP}/lib/libbind.${A} LIBBINDR= ${TOP}/lib/libbind_r.${A} LIBPORT= ${TOP}/port/libport.${A} RANLIB= ranlib AR= ar cru INSTALL= install CDEBUG= -g REENTRANT=-D_REENTRANT INSTALL_EXEC= INSTALL_LIB=-o bin -g bin # Warning: this MARGS has RANLIB=: to prevent submakes from running ranlib MARGS = "SYSTYPE=${SYSTYPE}" "SHELL=${SHELL}" "A=${A}" "O=${O}" \ "CC=${CC}" "LEX=${LEX}" "YACC=${YACC}" "CDEBUG=${CDEBUG}" \ "SYSLIBS=${SYSLIBS}" "LDFLAGS=${LDFLAGS}" \ "DESTDIR=${DESTDIR}" "DESTMAN=${DESTMAN}" \ "DESTBIN=${DESTBIN}" "DESTSBIN=${DESTSBIN}" "DESTEXEC=${DESTEXEC}" \ "DESTLIB=${DESTLIB}" "DESTINC=${DESTINC}" "DESTHELP=${DESTHELP}" \ "RANLIB=:" "AR=${AR}" "ARPREF=${ARPREF}" "ARSUFF=${ARSUFF}" \ "INCL=../${INCL}" "PORTINCL=../${PORTINCL}" "EXE=${EXE}" \ "LIBBIND=../${LIBBIND}" "LIBPORT=../${LIBPORT}" \ "INSTALL=${INSTALL}" "CPPFLAGS=${CPPFLAGS}" "TOP=../${TOP}" \ "REENTRANT=${REENTRANT}" "INSTALL_LIB=${INSTALL_LIB}" \ "INSTALL_EXEC=${INSTALL_EXEC}" "BOUNDS=${BOUNDS}" LIB = libbind.${A} LIBTS = ${TOP}/lib/libbind.ts LIBR = libbind_r.${A} LIBRTS = ${TOP}/lib/libbindr.ts all depend clean install distclean:: @for x in ${SUBDIRS}; do \ (cd $$x; pwd; ${MAKE} ${MARGS} $@); \ done all:: ${LIBTS} ${LIBRTS} ${LIBRTS}: ${LIBBINDR} ${RANLIB} ${LIBBINDR} sleep 1 && touch ${LIBRTS} ${LIBTS}: ${LIBBIND} ${RANLIB} ${LIBBIND} sleep 1 && touch ${LIBTS} distclean:: clean clean:: FRC rm -f *~ *.BAK *.CKP *.orig rm -f ${LIBBIND} ${LIBTS} rm -f ${LIBBINDR} ${LIBRTS} install:: ${DESTDIR}${DESTLIB} ${DESTDIR}${DESTLIB}/${LIB} install:: ${DESTDIR}${DESTLIB} ${DESTDIR}${DESTLIB}/${LIBR} ${DESTDIR}${DESTLIB}: mkdir -p ${DESTDIR}${DESTLIB} ${DESTDIR}${DESTLIB}/${LIBR}: ${LIBBINDR} ${INSTALL} -c ${INSTALL_LIB} -m 644 ${LIBBINDR} \ ${DESTDIR}${DESTLIB}/${LIBR} ( cd ${DESTDIR}${DESTLIB} ; ${RANLIB} ${LIBR} ) ${DESTDIR}${DESTLIB}/${LIB}: ${LIBBIND} ${INSTALL} -c ${INSTALL_LIB} -m 644 ${LIBBIND} \ ${DESTDIR}${DESTLIB}/${LIB} ( cd ${DESTDIR}${DESTLIB} ; ${RANLIB} ${LIB} ) links:: FRC @set -e; for x in ${SUBDIRS}; do \ ( mkdir $$x; cd $$x; pwd; ln -s ../SRC/$$x SRC; \ cp SRC/Makefile Makefile; chmod +w Makefile; \ ${MAKE} ${MARGS} links; \ ); \ done FRC: Index: head/contrib/bind/lib/dst/Makefile =================================================================== --- head/contrib/bind/lib/dst/Makefile (revision 60940) +++ head/contrib/bind/lib/dst/Makefile (revision 60941) @@ -1,96 +1,97 @@ # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. -# $Id: Makefile,v 1.13 1999/03/07 09:33:47 vixie Exp $ +# $Id: Makefile,v 1.16 2000/02/29 03:38:21 vixie Exp $ # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos DESTDIR = DESTLIB = /usr/local/lib O=o A=a CC= cc LD= ld SHELL= /bin/sh CDEBUG= -g TOP= ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.${A} LIBBINDR = ../${TOP}/lib/libbind_r.${A} CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} LD_LIBFLAGS= -x -r AR= ar cru RANLIB= ranlib INSTALL= install INSTALL_EXEC= INSTALL_LIB=-o bin -g bin THREADED= threaded HDRS= md5.h md5_locl.h SRCS= dst_api.c prandom.c rsaref_link.c support.c bsafe_link.c \ cylink_link.c hmac_link.c md5_dgst.c eay_dss_link.c OBJS= dst_api.${O} prandom.${O} rsaref_link.${O} support.${O} \ bsafe_link.${O} cylink_link.${O} hmac_link.${O} md5_dgst.${O} \ eay_dss_link.${O} -CRYPTINCL= -CRYPTFLAGS= -DHMAC_MD5 -DUSE_MD5 +CRYPTINCL= -I../cylink -I../dnssafe +CRYPTFLAGS= -DCYLINK_DSS -DHMAC_MD5 -DUSE_MD5 -DDNSSAFE all: ${LIBBIND} ${LIBBIND}: ${OBJS} ( cd ${THREADED} ; \ ${AR} ${LIBBINDR} ${ARPREF} ${OBJS} ${ARSUFF} ; \ ${RANLIB} ${LIBBINDR} ) ${AR} ${LIBBIND} ${ARPREF} ${OBJS} ${ARSUFF} ${RANLIB} ${LIBBIND} .c.${O}: - if test ! -d ${THREADED} ; then mkdir ${THREADED} ; fi + if test ! -d ${THREADED} ; then mkdir ${THREADED} ; else true ; fi ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} ${REENTRANT} ${CRYPTINCL} ${CRYPTFLAGS} -c $*.c -o ${THREADED}/$*.${O} - -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} -o a.out && \ - ${LDS} mv a.out ${THREADED}/$*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} \ + -o ${THREADED}/$*.out && \ + ${LDS} mv ${THREADED}/$*.out ${THREADED}/$*.${O} ${CC} ${CPPFLAGS} ${CFLAGS} ${CRYPTINCL} ${CRYPTFLAGS} -c $*.c - -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o a.out && \ - ${LDS} mv a.out $*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o $*.out && \ + ${LDS} mv $*.out $*.${O} $(SRCS):: $(HDRS) distclean: clean clean: FRC rm -f .depend a.out core ${LIB} tags rm -f *.${O} *.BAK *.CKP *~ rm -f prand_conf.h rm -f ${THREADED}/*.${O} - -rmdir ${THREADED} + -if test -d ${THREADED} ; then rmdir ${THREADED}; else true; fi depend: FRC mkdep -I${INCL} -I${PORTINCL} ${CPPFLAGS} ${SRCS} links: FRC @set -e; ln -s SRC/*.[ch] SRC/*.pl . install: FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/lib/dst/dst_api.c =================================================================== --- head/contrib/bind/lib/dst/dst_api.c (revision 60940) +++ head/contrib/bind/lib/dst/dst_api.c (revision 60941) @@ -1,1068 +1,1068 @@ #ifndef LINT -static const char rcsid[] = "$Header: /proj/cvs/isc/bind/src/lib/dst/dst_api.c,v 1.13 1999/10/13 16:39:22 vixie Exp $"; +static const char rcsid[] = "$Header: /proj/cvs/isc/bind/src/lib/dst/dst_api.c,v 1.14 2000/02/28 07:51:50 vixie Exp $"; #endif /* * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. * * Permission to use, copy modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. */ /* * This file contains the interface between the DST API and the crypto API. * This is the only file that needs to be changed if the crypto system is * changed. Exported functions are: * void dst_init() Initialize the toolkit * int dst_check_algorithm() Function to determines if alg is suppored. * int dst_compare_keys() Function to compare two keys for equality. * int dst_sign_data() Incremental signing routine. * int dst_verify_data() Incremental verify routine. * int dst_generate_key() Function to generate new KEY * DST_KEY *dst_read_key() Function to retrieve private/public KEY. * void dst_write_key() Function to write out a key. * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST * KEY structure. * int dst_key_to_dnskey() Function to return a public key in DNS * format binary * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY * int *dst_key_to_buffer() Writes out DST_KEY key matterial in buffer * void dst_free_key() Releases all memory referenced by key structure */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dst_internal.h" #include "port_after.h" /* static variables */ static int done_init = 0; dst_func *dst_t_func[DST_MAX_ALGS]; char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n"; char *dst_path = ""; /* internal I/O functions */ static DST_KEY *dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg); static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id, int in_alg); static int dst_s_write_public_key(const DST_KEY *key); static int dst_s_write_private_key(const DST_KEY *key); /* internal function to set up data structure */ static DST_KEY *dst_s_get_key_struct(const char *name, const int alg, const int flags, const int protocol, const int bits); /* * dst_init * This function initializes the Digital Signature Toolkit. * Right now, it just checks the DSTKEYPATH environment variable. * Parameters * none * Returns * none */ void dst_init() { char *s; int len; if (done_init != 0) return; done_init = 1; s = getenv("DSTKEYPATH"); len = 0; if (s) { struct stat statbuf; len = strlen(s); if (len > PATH_MAX) { EREPORT(("%s is longer than %d characters, ignoring\n", s, PATH_MAX)); } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { EREPORT(("%s is not a valid directory\n", s)); } else { dst_path = (char *) malloc(len + 2); memcpy(dst_path, s, len + 1); if (dst_path[strlen(dst_path) - 1] != '/') { dst_path[strlen(dst_path) + 1] = 0; dst_path[strlen(dst_path)] = '/'; } } } memset(dst_t_func, 0, sizeof(dst_t_func)); /* first one is selected */ dst_bsafe_init(); dst_rsaref_init(); dst_hmac_md5_init(); dst_eay_dss_init(); dst_cylink_init(); } /* * dst_check_algorithm * This function determines if the crypto system for the specified * algorithm is present. * Parameters * alg 1 KEY_RSA * 3 KEY_DSA * 157 KEY_HMAC_MD5 * future algorithms TBD and registered with IANA. * Returns * 1 - The algorithm is available. * 0 - The algorithm is not available. */ int dst_check_algorithm(const int alg) { return (dst_t_func[alg] != NULL); } /* * dst_s_get_key_struct * This function allocates key structure and fills in some of the * fields of the structure. * Parameters: * name: the name of the key * alg: the algorithm number * flags: the dns flags of the key * protocol: the dns protocol of the key * bits: the size of the key * Returns: * NULL if error * valid pointer otherwise */ static DST_KEY * dst_s_get_key_struct(const char *name, const int alg, const int flags, const int protocol, const int bits) { DST_KEY *new_key = NULL; if (dst_check_algorithm(alg)) /* make sure alg is available */ new_key = (DST_KEY *) malloc(sizeof(*new_key)); if (new_key == NULL) return (NULL); memset(new_key, 0, sizeof(*new_key)); new_key->dk_key_name = strdup(name); new_key->dk_alg = alg; new_key->dk_flags = flags; new_key->dk_proto = protocol; new_key->dk_KEY_struct = NULL; new_key->dk_key_size = bits; new_key->dk_func = dst_t_func[alg]; return (new_key); } /* * dst_compare_keys * Compares two keys for equality. * Parameters * key1, key2 Two keys to be compared. * Returns * 0 The keys are equal. * non-zero The keys are not equal. */ int dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2) { if (key1 == key2) return (0); if (key1 == NULL || key2 == NULL) return (4); if (key1->dk_alg != key2->dk_alg) return (1); if (key1->dk_key_size != key2->dk_key_size) return (2); if (key1->dk_id != key2->dk_id) return (3); return (key1->dk_func->compare(key1, key2)); } /* * dst_sign_data * An incremental signing function. Data is signed in steps. * First the context must be initialized (SIG_MODE_INIT). * Then data is hashed (SIG_MODE_UPDATE). Finally the signature * itself is created (SIG_MODE_FINAL). This function can be called * once with INIT, UPDATE and FINAL modes all set, or it can be * called separately with a different mode set for each step. The * UPDATE step can be repeated. * Parameters * mode A bit mask used to specify operation(s) to be performed. * SIG_MODE_INIT 1 Initialize digest * SIG_MODE_UPDATE 2 Add data to digest * SIG_MODE_FINAL 4 Generate signature * from signature * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL * data Data to be signed. * len The length in bytes of data to be signed. * in_key Contains a private key to sign with. * KEY structures should be handled (created, converted, * compared, stored, freed) by the DST. * signature * The location to which the signature will be written. * sig_len Length of the signature field in bytes. * Return * 0 Successfull INIT or Update operation * >0 success FINAL (sign) operation * <0 failure */ int dst_sign_data(const int mode, DST_KEY *in_key, void **context, const u_char *data, const int len, u_char *signature, const int sig_len) { DUMP(data, mode, len, "dst_sign_data()"); if (mode & SIG_MODE_FINAL && (in_key->dk_KEY_struct == NULL || signature == NULL)) return (MISSING_KEY_OR_SIGNATURE); if (in_key->dk_func && in_key->dk_func->sign) return (in_key->dk_func->sign(mode, in_key, context, data, len, signature, sig_len)); return (UNKNOWN_KEYALG); } /* * dst_verify_data * An incremental verify function. Data is verified in steps. * First the context must be initialized (SIG_MODE_INIT). * Then data is hashed (SIG_MODE_UPDATE). Finally the signature * is verified (SIG_MODE_FINAL). This function can be called * once with INIT, UPDATE and FINAL modes all set, or it can be * called separately with a different mode set for each step. The * UPDATE step can be repeated. * Parameters * mode Operations to perform this time. * SIG_MODE_INIT 1 Initialize digest * SIG_MODE_UPDATE 2 add data to digest * SIG_MODE_FINAL 4 verify signature * SIG_MODE_ALL * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL) * data Data to pass through the hash function. * len Length of the data in bytes. * in_key Key for verification. * signature Location of signature. * sig_len Length of the signature in bytes. * Returns * 0 Verify success * Non-Zero Verify Failure */ int dst_verify_data(const int mode, DST_KEY *in_key, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len) { DUMP(data, mode, len, "dst_verify_data()"); if (mode & SIG_MODE_FINAL && (in_key->dk_KEY_struct == NULL || signature == NULL)) return (MISSING_KEY_OR_SIGNATURE); if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL) return (UNSUPPORTED_KEYALG); return (in_key->dk_func->verify(mode, in_key, context, data, len, signature, sig_len)); } /* * dst_read_private_key * Access a private key. First the list of private keys that have * already been read in is searched, then the key accessed on disk. * If the private key can be found, it is returned. If the key cannot * be found, a null pointer is returned. The options specify required * key characteristics. If the private key requested does not have * these characteristics, it will not be read. * Parameters * in_keyname The private key name. * in_id The id of the private key. * options DST_FORCE_READ Read from disk - don't use a previously * read key. * DST_CAN_SIGN The key must be useable for signing. * DST_NO_AUTHEN The key must be useable for authentication. * DST_STANDARD Return any key * Returns * NULL If there is no key found in the current directory or * this key has not been loaded before. * !NULL Success - KEY structure returned. */ DST_KEY * dst_read_key(const char *in_keyname, const u_int16_t in_id, const int in_alg, const int type) { char keyname[PATH_MAX]; DST_KEY *dg_key = NULL, *pubkey = NULL; if (!dst_check_algorithm(in_alg)) { /* make sure alg is available */ EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n", in_alg)); return (NULL); } - if ((type && (DST_PUBLIC | DST_PRIVATE)) == 0) + if ((type & (DST_PUBLIC | DST_PRIVATE)) == 0) return (NULL); if (in_keyname == NULL) { EREPORT(("dst_read_private_key(): Null key name passed in\n")); return (NULL); } else strcpy(keyname, in_keyname); /* before I read in the public key, check if it is allowed to sign */ if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL) return (NULL); if (type == DST_PUBLIC) return pubkey; if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg, pubkey->dk_flags, pubkey->dk_proto, 0))) return (dg_key); /* Fill in private key and some fields in the general key structure */ if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id, pubkey->dk_alg) == 0) dg_key = dst_free_key(dg_key); pubkey = dst_free_key(pubkey); return (dg_key); } int dst_write_key(const DST_KEY *key, const int type) { int pub = 0, priv = 0; if (key == NULL) return (0); if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ EREPORT(("dst_write_key(): Algorithm %d not suppored\n", key->dk_alg)); return (UNSUPPORTED_KEYALG); } if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0) return (0); if (type & DST_PUBLIC) if ((pub = dst_s_write_public_key(key)) < 0) return (pub); if (type & DST_PRIVATE) if ((priv = dst_s_write_private_key(key)) < 0) return (priv); return (priv+pub); } /* * dst_write_private_key * Write a private key to disk. The filename will be of the form: * Kdk_name>+dk_alg>+dk_id>.. * If there is already a file with this name, an error is returned. * * Parameters * key A DST managed key structure that contains * all information needed about a key. * Return * >= 0 Correct behavior. Returns length of encoded key value * written to disk. * < 0 error. */ static int dst_s_write_private_key(const DST_KEY *key) { u_char encoded_block[RAW_KEY_SIZE]; char file[PATH_MAX]; int len; FILE *fp; /* First encode the key into the portable key format */ if (key == NULL) return (-1); if (key->dk_KEY_struct == NULL) return (0); /* null key has no private key */ if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) { EREPORT(("dst_write_private_key(): Unsupported operation %d\n", key->dk_alg)); return (-5); } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block, sizeof(encoded_block))) <= 0) { EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len)); return (-8); } /* Now I can create the file I want to use */ dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg, PRIVATE_KEY, PATH_MAX); /* Do not overwrite an existing file */ if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) { int nn; if ((nn = fwrite(encoded_block, 1, len, fp)) != len) { EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n", file, out_len, nn, errno)); return (-5); } fclose(fp); } else { EREPORT(("dst_write_private_key(): Can not create file %s\n" ,file)); return (-6); } memset(encoded_block, 0, len); return (len); } /* * * dst_read_public_key * Read a public key from disk and store in a DST key structure. * Parameters * in_name K. is the * filename of the key file to be read. * Returns * NULL If the key does not exist or no name is supplied. * NON-NULL Initalized key structure if the key exists. */ static DST_KEY * dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg) { int flags, proto, alg, len, dlen; int c; char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace; u_char deckey[RAW_KEY_SIZE]; FILE *fp; if (in_name == NULL) { EREPORT(("dst_read_public_key(): No key name given\n")); return (NULL); } if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY, PATH_MAX) == -1) { EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n", in_name, in_id, PUBLIC_KEY)); return (NULL); } /* * Open the file and read it's formatted contents up to key * File format: * domain.name [ttl] [IN] KEY * flags, proto, alg stored as decimal (or hex numbers FIXME). * (FIXME: handle parentheses for line continuation.) */ if ((fp = dst_s_fopen(name, "r", 0)) == NULL) { EREPORT(("dst_read_public_key(): Public Key not found %s\n", name)); return (NULL); } /* Skip domain name, which ends at first blank */ while ((c = getc(fp)) != EOF) if (isspace(c)) break; /* Skip blank to get to next field */ while ((c = getc(fp)) != EOF) if (!isspace(c)) break; /* Skip optional TTL -- if initial digit, skip whole word. */ if (isdigit(c)) { while ((c = getc(fp)) != EOF) if (isspace(c)) break; while ((c = getc(fp)) != EOF) if (!isspace(c)) break; } /* Skip optional "IN" */ if (c == 'I' || c == 'i') { while ((c = getc(fp)) != EOF) if (isspace(c)) break; while ((c = getc(fp)) != EOF) if (!isspace(c)) break; } /* Locate and skip "KEY" */ if (c != 'K' && c != 'k') { EREPORT(("\"KEY\" doesn't appear in file: %s", name)); return NULL; } while ((c = getc(fp)) != EOF) if (isspace(c)) break; while ((c = getc(fp)) != EOF) if (!isspace(c)) break; ungetc(c, fp); /* return the charcter to the input field */ /* Handle hex!! FIXME. */ if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) { EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n" ,name)); return (NULL); } /* read in the key string */ fgets(enckey, sizeof(enckey), fp); /* If we aren't at end-of-file, something is wrong. */ while ((c = getc(fp)) != EOF) if (!isspace(c)) break; if (!feof(fp)) { EREPORT(("Key too long in file: %s", name)); return NULL; } fclose(fp); if ((len = strlen(enckey)) <= 0) return (NULL); /* discard \n */ enckey[--len] = '\0'; /* remove leading spaces */ for (notspace = (char *) enckey; isspace(*notspace); len--) notspace++; dlen = b64_pton(notspace, deckey, sizeof(deckey)); if (dlen < 0) { EREPORT(("dst_read_public_key: bad return from b64_pton = %d", dlen)); return (NULL); } /* store key and info in a key structure that is returned */ /* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey, dlen);*/ return dst_buffer_to_key(in_name, alg, flags, proto, deckey, dlen); } /* * dst_write_public_key * Write a key to disk in DNS format. * Parameters * key Pointer to a DST key structure. * Returns * 0 Failure * 1 Success */ static int dst_s_write_public_key(const DST_KEY *key) { FILE *fp; char filename[PATH_MAX]; u_char out_key[RAW_KEY_SIZE]; char enc_key[RAW_KEY_SIZE]; int len = 0; memset(out_key, 0, sizeof(out_key)); if (key == NULL) { EREPORT(("dst_write_public_key(): No key specified \n")); return (0); } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0) return (0); /* Make the filename */ if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id, key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) { EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n", key->dk_key_name, key->dk_id, PUBLIC_KEY)); return (0); } /* create public key file */ if ((fp = dst_s_fopen(filename, "w+", 0644)) == NULL) { EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n", filename, errno)); return (0); } /*write out key first base64 the key data */ if (key->dk_flags & DST_EXTEND_FLAG) b64_ntop(&out_key[6], len - 6, enc_key, sizeof(enc_key)); else b64_ntop(&out_key[4], len - 4, enc_key, sizeof(enc_key)); fprintf(fp, "%s IN KEY %d %d %d %s\n", key->dk_key_name, key->dk_flags, key->dk_proto, key->dk_alg, enc_key); fclose(fp); return (1); } /* * dst_dnskey_to_public_key * This function converts the contents of a DNS KEY RR into a DST * key structure. * Paramters * len Length of the RDATA of the KEY RR RDATA * rdata A pointer to the the KEY RR RDATA. * in_name Key name to be stored in key structure. * Returns * NULL Failure * NON-NULL Success. Pointer to key structure. * Caller's responsibility to free() it. */ DST_KEY * dst_dnskey_to_key(const char *in_name, const u_char *rdata, const int len) { DST_KEY *key_st; int alg ; int start = DST_KEY_START; if (rdata == NULL || len <= DST_KEY_ALG) /* no data */ return (NULL); alg = (u_int8_t) rdata[DST_KEY_ALG]; if (!dst_check_algorithm(alg)) { /* make sure alg is available */ EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n", alg)); return (NULL); } if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL) return (NULL); if (in_name == NULL) return (NULL); key_st->dk_flags = dst_s_get_int16(rdata); key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT]; if (key_st->dk_flags & DST_EXTEND_FLAG) { u_int32_t ext_flags; ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]); key_st->dk_flags = key_st->dk_flags | (ext_flags << 16); start += 2; } /* * now point to the begining of the data representing the encoding * of the key */ if (key_st->dk_func && key_st->dk_func->from_dns_key) { if (key_st->dk_func->from_dns_key(key_st, &rdata[start], len - start) > 0) return (key_st); } else EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n", alg)); SAFE_FREE(key_st); return (key_st); } /* * dst_public_key_to_dnskey * Function to encode a public key into DNS KEY wire format * Parameters * key Key structure to encode. * out_storage Location to write the encoded key to. * out_len Size of the output array. * Returns * <0 Failure * >=0 Number of bytes written to out_storage */ int dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage, const int out_len) { u_int16_t val; int loc = 0; int enc_len = 0; if (key == NULL) return (-1); if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n", key->dk_alg)); return (UNSUPPORTED_KEYALG); } memset(out_storage, 0, out_len); val = (u_int16_t)(key->dk_flags & 0xffff); dst_s_put_int16(out_storage, val); loc += 2; out_storage[loc++] = (u_char) key->dk_proto; out_storage[loc++] = (u_char) key->dk_alg; if (key->dk_flags > 0xffff) { /* Extended flags */ val = (u_int16_t)((key->dk_flags >> 16) & 0xffff); dst_s_put_int16(&out_storage[loc], val); loc += 2; } if (key->dk_KEY_struct == NULL) return (loc); if (key->dk_func && key->dk_func->to_dns_key) { enc_len = key->dk_func->to_dns_key(key, (u_char *) &out_storage[loc], out_len - loc); if (enc_len > 0) return (enc_len + loc); else return (-1); } else EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n", key->dk_alg)); return (-1); } /* * dst_buffer_to_key * Function to encode a string of raw data into a DST key * Parameters * alg The algorithm (HMAC only) * key A pointer to the data * keylen The length of the data * Returns * NULL an error occurred * NON-NULL the DST key */ DST_KEY * dst_buffer_to_key(const char *key_name, /* name of the key */ const int alg, /* algorithm */ const int flags, /* dns flags */ const int protocol, /* dns protocol */ const u_char *key_buf, /* key in dns wire fmt */ const int key_len) /* size of key */ { DST_KEY *dkey = NULL; if (!dst_check_algorithm(alg)) { /* make sure alg is available */ EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg)); return (NULL); } dkey = dst_s_get_key_struct(key_name, alg, flags, protocol, -1); if (dkey == NULL) return (NULL); if (dkey->dk_func != NULL && dkey->dk_func->from_dns_key != NULL) { if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) { EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n")); return (dst_free_key(dkey)); } return (dkey); } return (NULL); } int dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len) { int len; /* this function will extrac the secret of HMAC into a buffer */ if(key == NULL) return (0); if(key->dk_func != NULL && key->dk_func != NULL) { len = key->dk_func->to_dns_key(key, out_buff, buf_len); if (len < 0) return (0); return (len); } return (0); } /* * dst_s_read_private_key_file * Function reads in private key from a file. * Fills out the KEY structure. * Parameters * name Name of the key to be read. * pk_key Structure that the key is returned in. * in_id Key identifier (tag) * Return * 1 if everthing works * 0 if there is any problem */ static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id, int in_alg) { int cnt, alg, len, major, minor, file_major, file_minor; int id; char filename[PATH_MAX]; u_char in_buff[RAW_KEY_SIZE], *p; FILE *fp; if (name == NULL || pk_key == NULL) { EREPORT(("dst_read_private_key_file(): No key name given\n")); return (0); } /* Make the filename */ if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY, PATH_MAX) == -1) { EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n", name, in_id, PRIVATE_KEY)); return (0); } /* first check if we can find the key file */ if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) { EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n", filename, dst_path[0] ? dst_path : (char *) getcwd(NULL, PATH_MAX - 1))); return (0); } /* now read the header info from the file */ if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) { fclose(fp); EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n", filename)); return (0); } /* decrypt key */ fclose(fp); if (memcmp(in_buff, "Private-key-format: v", 20) != 0) goto fail; len = cnt; p = in_buff; if (!dst_s_verify_str((const char **) &p, "Private-key-format: v")) { EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name)); goto fail; } /* read in file format */ sscanf((char *)p, "%d.%d", &file_major, &file_minor); sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor); if (file_major < 1) { EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n", file_major, file_minor, name)); goto fail; } else if (file_major > major || file_minor > minor) EREPORT(( "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n", name, file_major, file_minor)); while (*p++ != '\n') ; /* skip to end of line */ if (!dst_s_verify_str((const char **) &p, "Algorithm: ")) goto fail; if (sscanf((char *)p, "%d", &alg) != 1) goto fail; while (*p++ != '\n') ; /* skip to end of line */ if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name)) SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name)); pk_key->dk_key_name = (char *) strdup(name); /* allocate and fill in key structure */ if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL) goto fail; id = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, &in_buff[len] - p); if (id < 0) goto fail; /* Make sure the actual key tag matches the input tag used in the filename */ if (id != in_id) { EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id)); goto fail; } pk_key->dk_id = (u_int16_t) id; pk_key->dk_alg = alg; memset(in_buff, 0, cnt); return (1); fail: memset(in_buff, 0, cnt); return (0); } /* * dst_generate_key * Generate and store a public/private keypair. * Keys will be stored in formatted files. * Parameters * name Name of the new key. Used to create key files * K++.public and K++.private. * bits Size of the new key in bits. * exp What exponent to use: * 0 use exponent 3 * non-zero use Fermant4 * flags The default value of the DNS Key flags. * The DNS Key RR Flag field is defined in RFC 2065, * section 3.3. The field has 16 bits. * protocol * Default value of the DNS Key protocol field. * The DNS Key protocol field is defined in RFC 2065, * section 3.4. The field has 8 bits. * alg What algorithm to use. Currently defined: * KEY_RSA 1 * KEY_DSA 3 * KEY_HMAC 157 * out_id The key tag is returned. * * Return * NULL Failure * non-NULL the generated key pair * Caller frees the result, and its dk_name pointer. */ DST_KEY * dst_generate_key(const char *name, const int bits, const int exp, const int flags, const int protocol, const int alg) { DST_KEY *new_key = NULL; int res; if (name == NULL) return (NULL); if (!dst_check_algorithm(alg)) { /* make sure alg is available */ EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg)); return (NULL); } new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits); if (new_key == NULL) return (NULL); if (bits == 0) /* null key we are done */ return (new_key); if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) { EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n", alg)); return (dst_free_key(new_key)); } if ((res = new_key->dk_func->generate(new_key, exp)) <= 0) { EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n", new_key->dk_key_name, new_key->dk_alg, new_key->dk_key_size, exp)); return (dst_free_key(new_key)); } return (new_key); } /* * dst_free_key * Release all data structures pointed to by a key structure. * Parameters * f_key Key structure to be freed. */ DST_KEY * dst_free_key(DST_KEY *f_key) { if (f_key == NULL) return (f_key); if (f_key->dk_func && f_key->dk_func->destroy) f_key->dk_KEY_struct = f_key->dk_func->destroy(f_key->dk_KEY_struct); else { EREPORT(("dst_free_key(): Unknown key alg %d\n", f_key->dk_alg)); free(f_key->dk_KEY_struct); /* SHOULD NOT happen */ } if (f_key->dk_KEY_struct) { free(f_key->dk_KEY_struct); f_key->dk_KEY_struct = NULL; } if (f_key->dk_key_name) SAFE_FREE(f_key->dk_key_name); SAFE_FREE(f_key); return (NULL); } /* * dst_sig_size * Return the maximim size of signature from the key specified in bytes * Parameters * key * Returns * bytes */ int dst_sig_size(DST_KEY *key) { switch (key->dk_alg) { case KEY_HMAC_MD5: return (16); case KEY_HMAC_SHA1: return (20); case KEY_RSA: return (key->dk_key_size + 7) / 8; case KEY_DSA: return (40); default: EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg)); return -1; } } /* * dst_random * function that multiplexes number of random number generators * Parameters * mode: select the random number generator * wanted is how many bytes of random data are requested * outran is a buffer of size at least wanted for the output data * * Returns * number of bytes written to outran */ int dst_random(const int mode, int wanted, u_char *outran) { u_int32_t *buff = NULL, *bp = NULL; int i; if (wanted <= 0 || outran == NULL) return (0); switch (mode) { case DST_RAND_SEMI: bp = buff = (u_int32_t *) malloc(wanted+sizeof(u_int32_t)); for (i = 0; i < wanted; i+= sizeof(u_int32_t), bp++) { *bp = dst_s_quick_random(i); } memcpy(outran, buff, wanted); SAFE_FREE(buff); return (wanted); case DST_RAND_STD: return (dst_s_semi_random(outran, wanted)); case DST_RAND_KEY: return (dst_s_random(outran, wanted)); case DST_RAND_DSS: default: /* need error case here XXX OG */ return (0); } } Index: head/contrib/bind/lib/inet/Makefile =================================================================== --- head/contrib/bind/lib/inet/Makefile (revision 60940) +++ head/contrib/bind/lib/inet/Makefile (revision 60941) @@ -1,93 +1,94 @@ # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. -# $Id: Makefile,v 8.16 1999/03/03 08:07:16 vixie Exp $ +# $Id: Makefile,v 8.19 2000/02/29 03:38:22 vixie Exp $ # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos DESTDIR = DESTLIB = /usr/local/lib O=o A=a CC= cc LD= ld SHELL= /bin/sh CDEBUG= -g TOP= ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.${A} LIBBINDR = ../${TOP}/lib/libbind_r.${A} CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} LD_LIBFLAGS= -x -r AR= ar cru RANLIB= ranlib INSTALL= install INSTALL_EXEC= INSTALL_LIB=-o bin -g bin THREADED= threaded SRCS= nsap_addr.c inet_addr.c inet_ntop.c inet_pton.c \ inet_ntoa.c inet_neta.c inet_net_ntop.c inet_net_pton.c \ inet_cidr_ntop.c inet_cidr_pton.c \ inet_lnaof.c inet_makeaddr.c inet_netof.c inet_network.c OBJS= nsap_addr.${O} inet_addr.${O} inet_ntop.${O} inet_pton.${O} \ inet_ntoa.${O} inet_neta.${O} inet_net_ntop.${O} inet_net_pton.${O} \ inet_cidr_ntop.${O} inet_cidr_pton.${O} \ inet_lnaof.${O} inet_makeaddr.${O} inet_netof.${O} inet_network.${O} all: ${LIBBIND} ${LIBBIND}: ${OBJS} ( cd ${THREADED} ; \ ${AR} ${LIBBINDR} ${ARPREF} ${OBJS} ${ARSUFF} ; \ ${RANLIB} ${LIBBINDR} ) ${AR} ${LIBBIND} ${ARPREF} ${OBJS} ${ARSUFF} ${RANLIB} ${LIBBIND} .c.${O}: - if test ! -d ${THREADED} ; then mkdir ${THREADED} ; fi + if test ! -d ${THREADED} ; then mkdir ${THREADED} ; else true ; fi ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} ${REENTRANT} -c $*.c \ -o ${THREADED}/$*.${O} - -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} -o a.out && \ - ${LDS} mv a.out ${THREADED}/$*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} \ + -o ${THREADED}/$*.out && \ + ${LDS} mv ${THREADED}/$*.out ${THREADED}/$*.${O} ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} -c $*.c - -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o a.out && \ - ${LDS} mv a.out $*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o $*.out && \ + ${LDS} mv $*.out $*.${O} distclean: clean clean: FRC rm -f .depend a.out core ${LIB} tags rm -f *.${O} *.BAK *.CKP *~ rm -f ${THREADED}/*.${O} - -rmdir ${THREADED} + -if test -d ${THREADED} ; then rmdir ${THREADED}; else true; fi depend: FRC mkdep -I${INCL} -I${PORTINCL} ${CPPFLAGS} ${SRCS} links: FRC @set -e; ln -s SRC/*.[ch] . install: FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/lib/irs/Makefile =================================================================== --- head/contrib/bind/lib/irs/Makefile (revision 60940) +++ head/contrib/bind/lib/irs/Makefile (revision 60941) @@ -1,117 +1,118 @@ # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. -# $Id: Makefile,v 8.16 1999/02/22 02:47:58 vixie Exp $ +# $Id: Makefile,v 8.19 2000/02/29 03:38:22 vixie Exp $ # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos DESTDIR = DESTLIB = /usr/local/lib O=o A=a CC= cc LD= ld SHELL= /bin/sh CDEBUG= -g TOP= ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.${A} LIBBINDR = ../${TOP}/lib/libbind_r.${A} CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} # -D__BIND_NOSTATIC -Wimplicit LD_LIBFLAGS= -x -r AR= ar cru RANLIB= ranlib INSTALL= install INSTALL_EXEC= INSTALL_LIB=-o bin -g bin THREADED= threaded SRCS= dns.c dns_gr.c dns_ho.c dns_nw.c dns_pr.c dns_pw.c \ dns_sv.c gai_strerror.c gen.c gen_gr.c gen_ho.c \ gen_ng.c gen_nw.c gen_pr.c gen_pw.c gen_sv.c \ getaddrinfo.c getgrent.c getgrent_r.c gethostent.c \ gethostent_r.c getnameinfo.c getnetent.c getnetent_r.c \ getnetgrent.c getnetgrent_r.c getprotoent.c \ getprotoent_r.c getpwent.c getpwent_r.c getservent.c \ getservent_r.c hesiod.c irs_data.c \ irp.c irp_gr.c irp_ho.c irp_ng.c irp_nw.c \ irp_pr.c irp_pw.c irp_sv.c irpmarshall.c \ lcl.c lcl_gr.c \ lcl_ho.c lcl_ng.c lcl_nw.c lcl_pr.c lcl_pw.c \ lcl_sv.c nis.c nis_gr.c nis_ho.c nis_ng.c nis_nw.c \ nis_pr.c nis_pw.c nis_sv.c nul_ng.c util.c OBJS= dns.${O} dns_gr.${O} dns_ho.${O} dns_nw.${O} dns_pr.${O} dns_pw.${O} \ dns_sv.${O} gai_strerror.${O} gen.${O} gen_gr.${O} gen_ho.${O} \ gen_ng.${O} gen_nw.${O} gen_pr.${O} gen_pw.${O} gen_sv.${O} \ getaddrinfo.${O} getgrent.${O} getgrent_r.${O} gethostent.${O} \ gethostent_r.${O} getnameinfo.${O} getnetent.${O} getnetent_r.${O} \ getnetgrent.${O} getnetgrent_r.${O} getprotoent.${O} \ getprotoent_r.${O} getpwent.${O} getpwent_r.${O} getservent.${O} \ getservent_r.${O} hesiod.${O} irs_data.${O} \ irp.${O} irp_gr.${O} irp_ho.${O} irp_ng.${O} irp_nw.${O} \ irp_pr.${O} irp_pw.${O} irp_sv.${O} irpmarshall.${O} \ lcl.${O} lcl_gr.${O} \ lcl_ho.${O} lcl_ng.${O} lcl_nw.${O} lcl_pr.${O} lcl_pw.${O} \ lcl_sv.${O} nis.${O} nis_gr.${O} nis_ho.${O} nis_ng.${O} nis_nw.${O} \ nis_pr.${O} nis_pw.${O} nis_sv.${O} nul_ng.${O} util.${O} all: ${LIBBIND} ${LIBBIND}: ${OBJS} -( cd ${THREADED} ; \ ${AR} ${LIBBINDR} ${ARPREF} ${OBJS} ${ARSUFF} ; \ ${RANLIB} ${LIBBINDR} ) ${AR} ${LIBBIND} ${ARPREF} ${OBJS} ${ARSUFF} ${RANLIB} ${LIBBIND} .c.${O}: - if test ! -d ${THREADED} ; then mkdir ${THREADED} ; fi + if test ! -d ${THREADED} ; then mkdir ${THREADED} ; else true ; fi -(${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} ${REENTRANT} -c $*.c \ -o ${THREADED}/$*.${O} ; \ - ${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} && \ - ${LDS} mv a.out ${THREADED}/$*.${O}) + ${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} \ + -o ${THREADED}/$*.out && \ + ${LDS} mv ${THREADED}/$*.out ${THREADED}/$*.${O}) ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} -c $*.c - -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o a.out && \ - ${LDS} mv a.out $*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o $*.out && \ + ${LDS} mv $*.out $*.${O} distclean: clean clean: FRC rm -f .depend a.out core ${LIB} tags rm -f *.${O} *.BAK *.CKP *~ rm -f ${THREADED}/*.${O} - -rmdir ${THREADED} + -if test -d ${THREADED} ; then rmdir ${THREADED}; else true; fi depend: FRC mkdep -I${INCL} -I${PORTINCL} ${CPPFLAGS} ${SRCS} links: FRC @set -e; ln -s SRC/*.[ch] . testirpd: testirpd.o ${LIBBIND} ${CC} ${CDEBUG} ${LDFLAGS} -o testirpd testirpd.o ${LIBBIND} ${SYSLIBS} install: FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/lib/irs/dns.c =================================================================== --- head/contrib/bind/lib/irs/dns.c (revision 60940) +++ head/contrib/bind/lib/irs/dns.c (revision 60941) @@ -1,151 +1,151 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns.c,v 1.14 1999/01/18 07:46:47 vixie Exp $"; +static const char rcsid[] = "$Id: dns.c,v 1.15 2000/02/28 07:52:16 vixie Exp $"; #endif /* * dns.c --- this is the top-level accessor function for the dns */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "hesiod.h" #include "dns_p.h" /* forward */ static void dns_close(struct irs_acc *); static struct __res_state * dns_res_get(struct irs_acc *); static void dns_res_set(struct irs_acc *, struct __res_state *, void (*)(void *)); /* public */ struct irs_acc * irs_dns_acc(const char *options) { struct irs_acc *acc; struct dns_p *dns; if (!(acc = memget(sizeof *acc))) { errno = ENOMEM; return (NULL); } memset(acc, 0x5e, sizeof *acc); if (!(dns = memget(sizeof *dns))) { errno = ENOMEM; memput(acc, sizeof *acc); return (NULL); } memset(dns, 0x5e, sizeof *dns); dns->res = NULL; dns->free_res = NULL; if (hesiod_init(&dns->hes_ctx) < 0) { /* * We allow the dns accessor class to initialize * despite hesiod failing to initialize correctly, * since dns host queries don't depend on hesiod. */ dns->hes_ctx = NULL; } acc->private = dns; #ifdef WANT_IRS_GR acc->gr_map = irs_dns_gr; #else acc->gr_map = NULL; #endif #ifdef WANT_IRS_PW acc->pw_map = irs_dns_pw; #else acc->pw_map = NULL; #endif acc->sv_map = irs_dns_sv; acc->pr_map = irs_dns_pr; acc->ho_map = irs_dns_ho; acc->nw_map = irs_dns_nw; acc->ng_map = irs_nul_ng; acc->res_get = dns_res_get; acc->res_set = dns_res_set; acc->close = dns_close; return (acc); } /* methods */ static struct __res_state * dns_res_get(struct irs_acc *this) { struct dns_p *dns = (struct dns_p *)this->private; if (dns->res == NULL) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (res == NULL) return (NULL); memset(dns->res, 0, sizeof *dns->res); dns_res_set(this, res, free); } - if ((dns->res->options | RES_INIT) == 0 && + if ((dns->res->options & RES_INIT) == 0 && res_ninit(dns->res) < 0) return (NULL); return (dns->res); } static void dns_res_set(struct irs_acc *this, struct __res_state *res, void (*free_res)(void *)) { struct dns_p *dns = (struct dns_p *)this->private; if (dns->res && dns->free_res) { res_nclose(dns->res); (*dns->free_res)(dns->res); } dns->res = res; dns->free_res = free_res; } static void dns_close(struct irs_acc *this) { struct dns_p *dns; dns = (struct dns_p *)this->private; if (dns->res && dns->free_res) (*dns->free_res)(dns->res); if (dns->hes_ctx) hesiod_end(dns->hes_ctx); memput(dns, sizeof *dns); memput(this, sizeof *this); } Index: head/contrib/bind/lib/irs/dns_ho.c =================================================================== --- head/contrib/bind/lib/irs/dns_ho.c (revision 60940) +++ head/contrib/bind/lib/irs/dns_ho.c (revision 60941) @@ -1,706 +1,717 @@ /* * Copyright (c) 1985, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* from gethostnamadr.c 8.1 (Berkeley) 6/4/93 */ /* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns_ho.c,v 1.26 1999/10/15 19:49:09 vixie Exp $"; +static const char rcsid[] = "$Id: dns_ho.c,v 1.28 2000/04/20 07:47:54 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "dns_p.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) sprintf x #endif /* Definitions. */ #define MAXALIASES 35 #define MAXADDRS 35 #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ #else #define MAXPACKET 1024 #endif #define BOUNDS_CHECK(ptr, count) \ if ((ptr) + (count) > eom) { \ had_error++; \ continue; \ } else (void)0 struct pvt { struct hostent host; char * h_addr_ptrs[MAXADDRS + 1]; char * host_aliases[MAXALIASES]; char hostbuf[8*1024]; u_char host_addr[16]; /* IPv4 or IPv6 */ struct __res_state *res; void (*free_res)(void *); }; typedef union { int32_t al; char ac; } align; static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; /* Note: the IPv6 loopback address is in the "tunnel" space */ static const u_char v6local[] = { 0,0, 0,1 }; /* last 4 bytes of IPv6 addr */ /* Forwards. */ static void ho_close(struct irs_ho *this); static struct hostent * ho_byname(struct irs_ho *this, const char *name); static struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af); static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, int len, int af); static struct hostent * ho_next(struct irs_ho *this); static void ho_rewind(struct irs_ho *this); static void ho_minimize(struct irs_ho *this); static struct __res_state * ho_res_get(struct irs_ho *this); static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)); static void map_v4v6_hostent(struct hostent *hp, char **bp, int *len); static void addrsort(res_state, char **, int); static struct hostent * gethostans(struct irs_ho *this, const u_char *ansbuf, int anslen, const char *qname, int qtype, int af, int size); static int init(struct irs_ho *this); /* Exports. */ struct irs_ho * irs_dns_ho(struct irs_acc *this) { struct irs_ho *ho; struct pvt *pvt; if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); if (!(ho = memget(sizeof *ho))) { memput(pvt, sizeof *pvt); errno = ENOMEM; return (NULL); } memset(ho, 0x5e, sizeof *ho); ho->private = pvt; ho->close = ho_close; ho->byname = ho_byname; ho->byname2 = ho_byname2; ho->byaddr = ho_byaddr; ho->next = ho_next; ho->rewind = ho_rewind; ho->minimize = ho_minimize; ho->res_get = ho_res_get; ho->res_set = ho_res_set; return (ho); } /* Methods. */ static void ho_close(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; ho_minimize(this); if (pvt->res && pvt->free_res) (*pvt->free_res)(pvt->res); if (pvt) memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct hostent * ho_byname(struct irs_ho *this, const char *name) { struct pvt *pvt = (struct pvt *)this->private; struct hostent *hp; if (init(this) == -1) return (NULL); if (pvt->res->options & RES_USE_INET6) { hp = ho_byname2(this, name, AF_INET6); if (hp) return (hp); } return (ho_byname2(this, name, AF_INET)); } static struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af) { struct pvt *pvt = (struct pvt *)this->private; int n, size, type; u_char buf[MAXPACKET]; char tmp[NS_MAXDNAME]; const char *cp; if (init(this) == -1) return (NULL); switch (af) { case AF_INET: size = INADDRSZ; type = T_A; break; case AF_INET6: size = IN6ADDRSZ; type = T_AAAA; break; default: RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = EAFNOSUPPORT; return (NULL); } /* * if there aren't any dots, it could be a user-level alias. * this is also done in res_nquery() since we are not the only * function that looks up host names. */ if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name, tmp, sizeof tmp))) name = cp; if ((n = res_nsearch(pvt->res, name, C_IN, type, buf, sizeof buf)) < 0) return (NULL); return (gethostans(this, buf, n, name, type, af, size)); } static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { struct pvt *pvt = (struct pvt *)this->private; const u_char *uaddr = addr; char qbuf[MAXDNAME+1], *qp; u_char buf[MAXPACKET]; struct hostent *hp; int n, size; if (init(this) == -1) return (NULL); if (af == AF_INET6 && len == IN6ADDRSZ && (!memcmp(uaddr, mapped, sizeof mapped) || (!memcmp(uaddr, tunnelled, sizeof tunnelled) && memcmp(&uaddr[sizeof tunnelled], v6local, sizeof(v6local))))) { /* Unmap. */ addr = (char *)addr + sizeof mapped; uaddr += sizeof mapped; af = AF_INET; len = INADDRSZ; } switch (af) { case AF_INET: size = INADDRSZ; break; case AF_INET6: size = IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } if (size > len) { errno = EINVAL; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } switch (af) { case AF_INET: (void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); break; case AF_INET6: qp = qbuf; for (n = IN6ADDRSZ - 1; n >= 0; n--) { qp += SPRINTF((qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf)); } strcpy(qp, "ip6.int"); break; default: abort(); } n = res_nquery(pvt->res, qbuf, C_IN, T_PTR, buf, sizeof buf); if (n < 0) return (NULL); hp = gethostans(this, buf, n, qbuf, T_PTR, af, size); if (!hp) return (NULL); /* H_ERRNO was set by gethostans() */ memcpy(pvt->host_addr, addr, len); pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; pvt->h_addr_ptrs[1] = NULL; if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) { map_v4v6_address((char*)pvt->host_addr, (char*)pvt->host_addr); pvt->host.h_addrtype = AF_INET6; pvt->host.h_length = IN6ADDRSZ; } RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); return (hp); } static struct hostent * ho_next(struct irs_ho *this) { return (NULL); } static void ho_rewind(struct irs_ho *this) { /* NOOP */ } static void ho_minimize(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res) res_nclose(pvt->res); } static struct __res_state * ho_res_get(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (!res) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); ho_res_set(this, res, free); } return (pvt->res); } static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res && pvt->free_res) { res_nclose(pvt->res); (*pvt->free_res)(pvt->res); } pvt->res = res; pvt->free_res = free_res; } /* Private. */ static struct hostent * gethostans(struct irs_ho *this, const u_char *ansbuf, int anslen, const char *qname, int qtype, int af, int size) { struct pvt *pvt = (struct pvt *)this->private; int type, class, buflen, ancount, qdcount, n, haveanswer, had_error; int (*name_ok)(const char *); const HEADER *hp; const u_char *eom; const u_char *cp; - const char *tname; + const char *tname, **tap; char *bp, **ap, **hap; char tbuf[MAXDNAME+1]; tname = qname; eom = ansbuf + anslen; switch (qtype) { case T_A: case T_AAAA: name_ok = res_hnok; break; case T_PTR: name_ok = res_dnok; break; default: abort(); } pvt->host.h_addrtype = af; pvt->host.h_length = size; pvt->host.h_name = NULL; /* * Find first satisfactory answer. */ if (ansbuf + HFIXEDSZ > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } hp = (HEADER *)ansbuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); bp = pvt->hostbuf; buflen = sizeof pvt->hostbuf; cp = ansbuf + HFIXEDSZ; if (qdcount != 1) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } n = dn_expand(ansbuf, eom, cp, bp, buflen); if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } cp += n + QFIXEDSZ; if (cp > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } if (qtype == T_A || qtype == T_AAAA) { /* res_nsend() has already verified that the query name is the * same as the one we sent; this just gets the expanded name * (i.e., with the succeeding search-domain tacked on). */ n = strlen(bp) + 1; /* for the \0 */ if (n > MAXHOSTNAMELEN) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } pvt->host.h_name = bp; bp += n; buflen -= n; /* The qname can be abbreviated, but h_name is now absolute. */ qname = pvt->host.h_name; } ap = pvt->host_aliases; *ap = NULL; pvt->host.h_aliases = pvt->host_aliases; hap = pvt->h_addr_ptrs; *hap = NULL; pvt->host.h_addr_list = pvt->h_addr_ptrs; haveanswer = 0; had_error = 0; while (ancount-- > 0 && cp < eom && !had_error) { n = dn_expand(ansbuf, eom, cp, bp, buflen); if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) { had_error++; continue; } cp += n; /* name */ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); type = ns_get16(cp); cp += INT16SZ; /* type */ class = ns_get16(cp); cp += INT16SZ + INT32SZ; /* class, TTL */ n = ns_get16(cp); cp += INT16SZ; /* len */ BOUNDS_CHECK(cp, n); if (class != C_IN) { cp += n; continue; } if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { if (ap >= &pvt->host_aliases[MAXALIASES-1]) continue; n = dn_expand(ansbuf, eom, cp, tbuf, sizeof tbuf); if (n < 0 || !maybe_ok(pvt->res, tbuf, name_ok)) { had_error++; continue; } cp += n; /* Store alias. */ *ap++ = bp; n = strlen(bp) + 1; /* for the \0 */ bp += n; buflen -= n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > buflen || n > MAXHOSTNAMELEN) { had_error++; continue; } strcpy(bp, tbuf); pvt->host.h_name = bp; bp += n; buflen -= n; continue; } if (qtype == T_PTR && type == T_CNAME) { n = dn_expand(ansbuf, eom, cp, tbuf, sizeof tbuf); if (n < 0 || !maybe_dnok(pvt->res, tbuf)) { had_error++; continue; } cp += n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > buflen) { had_error++; continue; } strcpy(bp, tbuf); tname = bp; bp += n; buflen -= n; continue; } if (type != qtype) { cp += n; continue; } switch (type) { case T_PTR: if (ns_samename(tname, bp) != 1) { cp += n; continue; } n = dn_expand(ansbuf, eom, cp, bp, buflen); if (n < 0 || !maybe_hnok(pvt->res, bp) || n >= MAXHOSTNAMELEN) { had_error++; break; } cp += n; if (!haveanswer) pvt->host.h_name = bp; else if (ap < &pvt->host_aliases[MAXALIASES-1]) *ap++ = bp; else n = -1; if (n != -1) { n = strlen(bp) + 1; /* for the \0 */ bp += n; buflen -= n; } break; case T_A: case T_AAAA: if (ns_samename(pvt->host.h_name, bp) != 1) { cp += n; continue; } if (n != pvt->host.h_length) { cp += n; continue; } if (!haveanswer) { int nn; nn = strlen(bp) + 1; /* for the \0 */ if (nn >= MAXHOSTNAMELEN) { cp += n; had_error++; continue; } pvt->host.h_name = bp; bp += nn; buflen -= nn; } - + /* Ensure alignment. */ bp += sizeof(align) - ((u_long)bp % sizeof(align)); - + /* Avoid overflows. */ if (bp + n >= &pvt->hostbuf[sizeof pvt->hostbuf]) { had_error++; continue; } if (hap >= &pvt->h_addr_ptrs[MAXADDRS-1]) { cp += n; continue; } + /* Suppress duplicates. */ + for (tap = (const char **)pvt->h_addr_ptrs; + *tap != NULL; + tap++) + if (memcmp(*tap, cp, n) == 0) + break; + if (*tap != NULL) { + cp += n; + continue; + } + /* Store address. */ memcpy(*hap++ = bp, cp, n); + *hap = NULL; bp += n; cp += n; break; default: abort(); } if (!had_error) haveanswer++; } if (haveanswer) { *ap = NULL; - *hap = NULL; if (pvt->res->nsort && haveanswer > 1 && qtype == T_A) addrsort(pvt->res, pvt->h_addr_ptrs, haveanswer); if (!pvt->host.h_name) { n = strlen(qname) + 1; /* for the \0 */ if (n > buflen || n >= MAXHOSTNAMELEN) goto no_recovery; strcpy(bp, qname); pvt->host.h_name = bp; bp += n; buflen -= n; } if (pvt->res->options & RES_USE_INET6) map_v4v6_hostent(&pvt->host, &bp, &buflen); RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); return (&pvt->host); } no_recovery: RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } static void map_v4v6_hostent(struct hostent *hp, char **bpp, int *lenp) { char **ap; if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) return; hp->h_addrtype = AF_INET6; hp->h_length = IN6ADDRSZ; for (ap = hp->h_addr_list; *ap; ap++) { int i = sizeof(align) - ((u_long)*bpp % sizeof(align)); if (*lenp < (i + IN6ADDRSZ)) { /* Out of memory. Truncate address list here. */ *ap = NULL; return; } *bpp += i; *lenp -= i; map_v4v6_address(*ap, *bpp); *ap = *bpp; *bpp += IN6ADDRSZ; *lenp -= IN6ADDRSZ; } } static void addrsort(res_state statp, char **ap, int num) { int i, j, needsort = 0, aval[MAXADDRS]; char **p; p = ap; for (i = 0; i < num; i++, p++) { for (j = 0 ; (unsigned)j < statp->nsort; j++) if (statp->sort_list[j].addr.s_addr == (((struct in_addr *)(*p))->s_addr & statp->sort_list[j].mask)) break; aval[i] = j; if (needsort == 0 && i > 0 && j < aval[i-1]) needsort = i; } if (!needsort) return; while (needsort < num) { for (j = needsort - 1; j >= 0; j--) { if (aval[j] > aval[j+1]) { char *hp; i = aval[j]; aval[j] = aval[j+1]; aval[j+1] = i; hp = ap[j]; ap[j] = ap[j+1]; ap[j+1] = hp; } else break; } needsort++; } } static int init(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res && !ho_res_get(this)) return (-1); if (((pvt->res->options & RES_INIT) == 0) && res_ninit(pvt->res) == -1) return (-1); return (0); } Index: head/contrib/bind/lib/irs/dns_sv.c =================================================================== --- head/contrib/bind/lib/irs/dns_sv.c (revision 60940) +++ head/contrib/bind/lib/irs/dns_sv.c (revision 60941) @@ -1,285 +1,286 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns_sv.c,v 1.17 1999/09/04 22:06:14 vixie Exp $"; +static const char rcsid[] = "$Id: dns_sv.c,v 1.19 2000/03/30 22:53:56 vixie Exp $"; #endif /* Imports */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "hesiod.h" #include "dns_p.h" /* Definitions */ struct pvt { struct dns_p * dns; struct servent serv; char * svbuf; struct __res_state * res; void (*free_res)(void *); }; /* Forward. */ static void sv_close(struct irs_sv *); static struct servent * sv_byname(struct irs_sv *, const char *, const char *); static struct servent * sv_byport(struct irs_sv *, int, const char *); static struct servent * sv_next(struct irs_sv *); static void sv_rewind(struct irs_sv *); static void sv_minimize(struct irs_sv *); static struct __res_state * sv_res_get(struct irs_sv *); static void sv_res_set(struct irs_sv *, struct __res_state *, void (*)(void *)); static struct servent * parse_hes_list(struct irs_sv *, char **, const char *); /* Public */ struct irs_sv * irs_dns_sv(struct irs_acc *this) { struct dns_p *dns = (struct dns_p *)this->private; struct irs_sv *sv; struct pvt *pvt; if (!dns || !dns->hes_ctx) { errno = ENODEV; return (NULL); } if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); pvt->dns = dns; if (!(sv = memget(sizeof *sv))) { memput(pvt, sizeof *pvt); errno = ENOMEM; return (NULL); } memset(sv, 0x5e, sizeof *sv); sv->private = pvt; sv->byname = sv_byname; sv->byport = sv_byport; sv->next = sv_next; sv->rewind = sv_rewind; sv->close = sv_close; sv->minimize = sv_minimize; - sv->res_get = sv_res_get; - sv->res_set = sv_res_set; + sv->res_get = NULL; /* sv_res_get; */ + sv->res_set = NULL; /* sv_res_set; */ return (sv); } /* Methods */ static void sv_close(struct irs_sv *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->serv.s_aliases) free(pvt->serv.s_aliases); if (pvt->svbuf) free(pvt->svbuf); if (pvt->res && pvt->free_res) (*pvt->free_res)(pvt->res); memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct servent * sv_byname(struct irs_sv *this, const char *name, const char *proto) { struct pvt *pvt = (struct pvt *)this->private; struct dns_p *dns = pvt->dns; struct servent *s; char **hes_list; if (!(hes_list = hesiod_resolve(dns->hes_ctx, name, "service"))) return (NULL); s = parse_hes_list(this, hes_list, proto); hesiod_free_list(dns->hes_ctx, hes_list); return (s); } static struct servent * sv_byport(struct irs_sv *this, int port, const char *proto) { struct pvt *pvt = (struct pvt *)this->private; struct dns_p *dns = pvt->dns; struct servent *s; char portstr[16]; char **hes_list; - sprintf(portstr, "%d", port); + sprintf(portstr, "%d", ntohs(port)); if (!(hes_list = hesiod_resolve(dns->hes_ctx, portstr, "port"))) return (NULL); s = parse_hes_list(this, hes_list, proto); hesiod_free_list(dns->hes_ctx, hes_list); return (s); } static struct servent * sv_next(struct irs_sv *this) { errno = ENODEV; return (NULL); } static void sv_rewind(struct irs_sv *this) { /* NOOP */ } /* Private */ static struct servent * parse_hes_list(struct irs_sv *this, char **hes_list, const char *proto) { struct pvt *pvt = (struct pvt *)this->private; char *p, *cp, **cpp, **new; int proto_len; int num = 0; int max = 0; for (cpp = hes_list; *cpp; cpp++) { cp = *cpp; /* Strip away comments, if any. */ if ((p = strchr(cp, '#'))) *p = 0; /* Check to make sure the protocol matches. */ p = cp; while (*p && !isspace(*p)) p++; if (!*p) continue; - proto_len = strlen(proto); - if (strncasecmp(++p, proto, proto_len) != 0) - continue; - if (p[proto_len] && !isspace(p[proto_len])) - continue; - + if (proto) { + proto_len = strlen(proto); + if (strncasecmp(++p, proto, proto_len) != 0) + continue; + if (p[proto_len] && !isspace(p[proto_len])) + continue; + } /* OK, we've got a live one. Let's parse it for real. */ if (pvt->svbuf) free(pvt->svbuf); pvt->svbuf = strdup(cp); p = pvt->svbuf; pvt->serv.s_name = p; while (*p && !isspace(*p)) p++; if (!*p) continue; *p++ = '\0'; pvt->serv.s_proto = p; while (*p && !isspace(*p)) p++; if (!*p) continue; *p++ = '\0'; pvt->serv.s_port = htons((u_short) atoi(p)); while (*p && !isspace(*p)) p++; if (*p) *p++ = '\0'; while (*p) { if ((num + 1) >= max || !pvt->serv.s_aliases) { max += 10; new = realloc(pvt->serv.s_aliases, max * sizeof(char *)); if (!new) { errno = ENOMEM; goto cleanup; } pvt->serv.s_aliases = new; } pvt->serv.s_aliases[num++] = p; while (*p && !isspace(*p)) p++; if (*p) *p++ = '\0'; } if (!pvt->serv.s_aliases) pvt->serv.s_aliases = malloc(sizeof(char *)); if (!pvt->serv.s_aliases) goto cleanup; pvt->serv.s_aliases[num] = NULL; return (&pvt->serv); } cleanup: if (pvt->serv.s_aliases) { free(pvt->serv.s_aliases); pvt->serv.s_aliases = NULL; } if (pvt->svbuf) { free(pvt->svbuf); pvt->svbuf = NULL; } return (NULL); } static void sv_minimize(struct irs_sv *this) { /* NOOP */ } static struct __res_state * sv_res_get(struct irs_sv *this) { struct pvt *pvt = (struct pvt *)this->private; struct dns_p *dns = pvt->dns; return (__hesiod_res_get(dns->hes_ctx)); } static void sv_res_set(struct irs_sv *this, struct __res_state * res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; struct dns_p *dns = pvt->dns; __hesiod_res_set(dns->hes_ctx, res, free_res); } Index: head/contrib/bind/lib/irs/gethostent.c =================================================================== --- head/contrib/bind/lib/irs/gethostent.c (revision 60940) +++ head/contrib/bind/lib/irs/gethostent.c (revision 60941) @@ -1,860 +1,891 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: gethostent.c,v 1.25 1999/10/19 22:27:20 cyarnell Exp $"; +static const char rcsid[] = "$Id: gethostent.c,v 1.27 2000/04/20 07:10:33 vixie Exp $"; #endif /* Imports */ #include "port_before.h" #if !defined(__BIND_NOSTATIC) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "irs_data.h" /* Definitions */ struct pvt { char * aliases[1]; char * addrs[2]; char addr[NS_IN6ADDRSZ]; char name[NS_MAXDNAME + 1]; struct hostent host; }; /* Forward */ static struct net_data *init(void); static void freepvt(struct net_data *); static struct hostent *fakeaddr(const char *, int, struct net_data *); /* Public */ struct hostent * gethostbyname(const char *name) { struct net_data *net_data = init(); return (gethostbyname_p(name, net_data)); } struct hostent * gethostbyname2(const char *name, int af) { struct net_data *net_data = init(); return (gethostbyname2_p(name, af, net_data)); } struct hostent * gethostbyaddr(const char *addr, int len, int af) { struct net_data *net_data = init(); return (gethostbyaddr_p(addr, len, af, net_data)); } struct hostent * gethostent() { struct net_data *net_data = init(); return (gethostent_p(net_data)); } void sethostent(int stayopen) { struct net_data *net_data = init(); sethostent_p(stayopen, net_data); } void endhostent() { struct net_data *net_data = init(); endhostent_p(net_data); } /* Shared private. */ struct hostent * gethostbyname_p(const char *name, struct net_data *net_data) { struct hostent *hp; if (!net_data) return (NULL); if (net_data->res->options & RES_USE_INET6) { hp = gethostbyname2_p(name, AF_INET6, net_data); if (hp) return (hp); } return (gethostbyname2_p(name, AF_INET, net_data)); } struct hostent * gethostbyname2_p(const char *name, int af, struct net_data *net_data) { struct irs_ho *ho; char tmp[NS_MAXDNAME]; struct hostent *hp; const char *cp; char **hap; if (!net_data || !(ho = net_data->ho)) return (NULL); if (net_data->ho_stayopen && net_data->ho_last) { if (ns_samename(name, net_data->ho_last->h_name) == 1) return (net_data->ho_last); for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++) if (ns_samename(name, *hap) == 1) return (net_data->ho_last); } if (!strchr(name, '.') && (cp = res_hostalias(net_data->res, name, tmp, sizeof tmp))) name = cp; if ((hp = fakeaddr(name, af, net_data)) != NULL) return (hp); net_data->ho_last = (*ho->byname2)(ho, name, af); if (!net_data->ho_stayopen) endhostent(); return (net_data->ho_last); } struct hostent * gethostbyaddr_p(const char *addr, int len, int af, struct net_data *net_data) { struct irs_ho *ho; char **hap; if (!net_data || !(ho = net_data->ho)) return (NULL); if (net_data->ho_stayopen && net_data->ho_last && net_data->ho_last->h_length == len) for (hap = net_data->ho_last->h_addr_list; hap && *hap; hap++) if (!memcmp(addr, *hap, len)) return (net_data->ho_last); net_data->ho_last = (*ho->byaddr)(ho, addr, len, af); if (!net_data->ho_stayopen) endhostent(); return (net_data->ho_last); } struct hostent * gethostent_p(struct net_data *net_data) { struct irs_ho *ho; struct hostent *hp; if (!net_data || !(ho = net_data->ho)) return (NULL); while ((hp = (*ho->next)(ho)) != NULL && hp->h_addrtype == AF_INET6 && (net_data->res->options & RES_USE_INET6) == 0) continue; net_data->ho_last = hp; return (net_data->ho_last); } void sethostent_p(int stayopen, struct net_data *net_data) { struct irs_ho *ho; if (!net_data || !(ho = net_data->ho)) return; freepvt(net_data); (*ho->rewind)(ho); net_data->ho_stayopen = (stayopen != 0); if (stayopen == 0) net_data_minimize(net_data); } void endhostent_p(struct net_data *net_data) { struct irs_ho *ho; if ((net_data != NULL) && ((ho = net_data->ho) != NULL)) (*ho->minimize)(ho); } #if !defined(HAS_INET6_STRUCTS) || defined(MISSING_IN6ADDR_ANY) static const struct in6_addr in6addr_any; #endif #ifndef IN6_IS_ADDR_V4COMPAT static const unsigned char in6addr_compat[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \ ((x)->s6_addr[12] != 0 || \ (x)->s6_addr[13] != 0 || \ (x)->s6_addr[14] != 0 || \ ((x)->s6_addr[15] != 0 && \ (x)->s6_addr[15] != 1))) #endif #ifndef IN6_IS_ADDR_V4MAPPED #define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12)) #endif static const unsigned char in6addr_mapped[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; static int scan_interfaces(int *, int *); static struct hostent *copyandmerge(struct hostent *, struct hostent *, int, int *); /* * Public functions */ /* * AI_V4MAPPED + AF_INET6 * If no IPv6 address then a query for IPv4 and map returned values. * * AI_ALL + AI_V4MAPPED + AF_INET6 * Return IPv6 and IPv4 mapped. * * AI_ADDRCONFIG * Only return IPv6 / IPv4 address if there is an interface of that * type active. */ struct hostent * getipnodebyname(const char *name, int af, int flags, int *error_num) { int have_v4 = 1, have_v6 = 1; struct in_addr in4; struct in6_addr in6; struct hostent he, *he1 = NULL, *he2 = NULL, *he3; int v4 = 0, v6 = 0; struct net_data *net_data = init(); u_long options; int tmp_err; if (net_data == NULL) { *error_num = NO_RECOVERY; return (NULL); } /* If we care about active interfaces then check. */ if ((flags & AI_ADDRCONFIG) != 0) if (scan_interfaces(&have_v4, &have_v6) == -1) { *error_num = NO_RECOVERY; return (NULL); } /* Check for literal address. */ if ((v4 = inet_pton(AF_INET, name, &in4)) != 1) v6 = inet_pton(AF_INET6, name, &in6); /* Impossible combination? */ if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) || (af == AF_INET && v6 == 1) || (have_v4 == 0 && v4 == 1) || (have_v6 == 0 && v6 == 1) || (have_v4 == 0 && af == AF_INET) || (have_v6 == 0 && af == AF_INET6)) { *error_num = HOST_NOT_FOUND; return (NULL); } /* Literal address? */ if (v4 == 1 || v6 == 1) { char *addr_list[2]; char *aliases[1]; he.h_name = (char *)name; he.h_addr_list = addr_list; he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6; he.h_addr_list[1] = NULL; he.h_aliases = aliases; he.h_aliases[0] = NULL; he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ; he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6; return (copyandmerge(&he, NULL, af, error_num)); } options = net_data->res->options; net_data->res->options &= ~RES_USE_INET6; tmp_err = NO_RECOVERY; if (have_v6 && af == AF_INET6) { he2 = gethostbyname2_p(name, AF_INET6, net_data); if (he2 != NULL) { he1 = copyandmerge(he2, NULL, af, error_num); if (he1 == NULL) return (NULL); he2 = NULL; } else { tmp_err = net_data->res->res_h_errno; } } if (have_v4 && ((af == AF_INET) || (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 && (he1 == NULL || (flags & AI_ALL) != 0)))) { he2 = gethostbyname2_p(name, AF_INET, net_data); if (he1 == NULL && he2 == NULL) { *error_num = net_data->res->res_h_errno; return (NULL); } } else *error_num = tmp_err; net_data->res->options = options; he3 = copyandmerge(he1, he2, af, error_num); if (he1 != NULL) freehostent(he1); return (he3); } struct hostent * getipnodebyaddr(const void *src, size_t len, int af, int *error_num) { struct hostent *he1, *he2; struct net_data *net_data = init(); /* Sanity Checks. */ if (src == NULL) { *error_num = NO_RECOVERY; return (NULL); } switch (af) { case AF_INET: if (len != INADDRSZ) { *error_num = NO_RECOVERY; return (NULL); } break; case AF_INET6: if (len != IN6ADDRSZ) { *error_num = NO_RECOVERY; return (NULL); } break; default: *error_num = NO_RECOVERY; return (NULL); } /* * Lookup IPv4 and IPv4 mapped/compatible addresses */ if ((af == AF_INET6 && IN6_IS_ADDR_V4COMPAT((struct in6_addr *)src)) || (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)src)) || (af == AF_INET)) { const char *cp = src; if (af == AF_INET6) cp += 12; he1 = gethostbyaddr_p(cp, 4, AF_INET, net_data); if (he1 == NULL) { *error_num = net_data->res->res_h_errno; return (NULL); } he2 = copyandmerge(he1, NULL, af, error_num); if (he2 == NULL) return (NULL); /* * Restore original address if mapped/compatible. */ if (af == AF_INET6) memcpy(he1->h_addr, src, len); return (he2); } /* * Lookup IPv6 address. */ if (memcmp((struct in6_addr *)src, &in6addr_any, 16) == 0) { *error_num = HOST_NOT_FOUND; return (NULL); } he1 = gethostbyaddr_p(src, 16, AF_INET6, net_data); if (he1 == NULL) { *error_num = net_data->res->res_h_errno; return (NULL); } return (copyandmerge(he1, NULL, af, error_num)); } void freehostent(struct hostent *he) { char **cpp; int names = 1; int addresses = 1; memput(he->h_name, strlen(he->h_name) + 1); cpp = he->h_addr_list; while (*cpp != NULL) { memput(*cpp, (he->h_addrtype == AF_INET) ? INADDRSZ : IN6ADDRSZ); *cpp = NULL; cpp++; addresses++; } cpp = he->h_aliases; while (*cpp != NULL) { memput(*cpp, strlen(*cpp) + 1); cpp++; names++; } memput(he->h_aliases, sizeof(char *) * (names)); memput(he->h_addr_list, sizeof(char *) * (addresses)); memput(he, sizeof *he); } /* * Private */ /* * Scan the interface table and set have_v4 and have_v6 depending * upon whether there are IPv4 and IPv6 interface addresses. * * Returns: * 0 on success * -1 on failure. */ static int scan_interfaces(int *have_v4, int *have_v6) { - struct ifconf ifc; - struct ifreq ifreq; +#ifndef SIOCGLIFCONF +/* map new to old */ +#define SIOCGLIFCONF SIOCGIFCONF +#define lifc_len ifc_len +#define lifc_buf ifc_buf + struct ifconf lifc; +#else +#define SETFAMILYFLAGS + struct lifconf lifc; +#endif + +#ifndef SIOCGLIFADDR +/* map new to old */ +#define SIOCGLIFADDR SIOCGIFADDR +#endif + +#ifndef SIOCGLIFFLAGS +#define SIOCGLIFFLAGS SIOCGIFFLAGS +#define lifr_addr ifr_addr +#define lifr_name ifr_name +#define lifr_flags ifr_flags +#define ss_family sa_family + struct ifreq lifreq; +#else + struct lifreq lifreq; +#endif struct in_addr in4; struct in6_addr in6; char *buf = NULL, *cp, *cplim; static int bufsiz = 4095; int s, cpsize, n; /* Set to zero. Used as loop terminators below. */ *have_v4 = *have_v6 = 0; /* Get interface list from system. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) goto err_ret; /* * Grow buffer until large enough to contain all interface * descriptions. */ for (;;) { buf = memget(bufsiz); if (buf == NULL) goto err_ret; - ifc.ifc_len = bufsiz; - ifc.ifc_buf = buf; +#ifdef SETFAMILYFLAGS + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_flags = 0; +#endif + lifc.lifc_len = bufsiz; + lifc.lifc_buf = buf; #ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF /* * This is a fix for IRIX OS in which the call to ioctl with * the flag SIOCGIFCONF may not return an entry for all the * interfaces like most flavors of Unix. */ - if (emul_ioctl(&ifc) >= 0) + if (emul_ioctl(&lifc) >= 0) break; #else - if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) { + if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) { /* * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If - * ifc.ifc_len is too near to the end of the + * lifc.lifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ - if (ifc.ifc_len + 2 * sizeof(ifreq) < bufsiz) + if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz) break; } #endif if ((n == -1) && errno != EINVAL) goto err_ret; if (bufsiz > 1000000) goto err_ret; memput(buf, bufsiz); bufsiz += 4096; } /* Parse system's interface list. */ - cplim = buf + ifc.ifc_len; /* skip over if's with big ifr_addr's */ + cplim = buf + lifc.lifc_len; /* skip over if's with big ifr_addr's */ for (cp = buf; (*have_v4 == 0 || *have_v6 == 0) && cp < cplim; cp += cpsize) { - memcpy(&ifreq, cp, sizeof ifreq); + memcpy(&lifreq, cp, sizeof lifreq); #ifdef HAVE_SA_LEN #ifdef FIX_ZERO_SA_LEN - if (ifreq.ifr_addr.sa_len == 0) - ifreq.ifr_addr.sa_len = 16; + if (lifreq.lifr_addr.sa_len == 0) + lifreq.lifr_addr.sa_len = 16; #endif #ifdef HAVE_MINIMUM_IFREQ - cpsize = sizeof ifreq; - if (ifreq.ifr_addr.sa_len > sizeof (struct sockaddr)) - cpsize += (int)ifreq.ifr_addr.sa_len - + cpsize = sizeof lifreq; + if (lifreq.lifr_addr.sa_len > sizeof (struct sockaddr)) + cpsize += (int)lifreq.lifr_addr.sa_len - (int)(sizeof (struct sockaddr)); #else - cpsize = sizeof ifreq.ifr_name + ifreq.ifr_addr.sa_len; + cpsize = sizeof lifreq.lifr_name + lifreq.lifr_addr.sa_len; #endif /* HAVE_MINIMUM_IFREQ */ #elif defined SIOCGIFCONF_ADDR - cpsize = sizeof ifreq; + cpsize = sizeof lifreq; #else - cpsize = sizeof ifreq.ifr_name; + cpsize = sizeof lifreq.lifr_name; /* XXX maybe this should be a hard error? */ - if (ioctl(s, SIOCGIFADDR, (char *)&ifreq) < 0) + if (ioctl(s, SOICGLIFADDR, (char *)&lifreq) < 0) continue; #endif - switch (ifreq.ifr_addr.sa_family) { + switch (lifreq.lifr_addr.ss_family) { case AF_INET: if (*have_v4 == 0) { memcpy(&in4, &((struct sockaddr_in *) - &ifreq.ifr_addr)->sin_addr, sizeof in4); + &lifreq.lifr_addr)->sin_addr, + sizeof in4); if (in4.s_addr == INADDR_ANY) break; - n = ioctl(s, SIOCGIFFLAGS, (char *)&ifreq); + n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq); if (n < 0) break; - if ((ifreq.ifr_flags & IFF_UP) == 0) + if ((lifreq.lifr_flags & IFF_UP) == 0) break; *have_v4 = 1; } break; case AF_INET6: if (*have_v6 == 0) { memcpy(&in6, &((struct sockaddr_in6 *) - &ifreq.ifr_addr)->sin6_addr, sizeof in6); + &lifreq.lifr_addr)->sin6_addr, sizeof in6); if (memcmp(&in6, &in6addr_any, sizeof in6) == 0) break; - n = ioctl(s, SIOCGIFFLAGS, (char *)&ifreq); + n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq); if (n < 0) break; - if ((ifreq.ifr_flags & IFF_UP) == 0) + if ((lifreq.lifr_flags & IFF_UP) == 0) break; *have_v6 = 1; } break; } } if (buf != NULL) memput(buf, bufsiz); close(s); + /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ return (0); err_ret: if (buf != NULL) memput(buf, bufsiz); if (s != -1) close(s); + /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ return (-1); } static struct hostent * copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num) { struct hostent *he = NULL; int addresses = 1; /* NULL terminator */ int names = 1; /* NULL terminator */ int len = 0; char **cpp, **npp; /* * Work out array sizes; */ if (he1 != NULL) { cpp = he1->h_addr_list; while (*cpp != NULL) { addresses++; cpp++; } cpp = he1->h_aliases; while (*cpp != NULL) { names++; cpp++; } } if (he2 != NULL) { cpp = he2->h_addr_list; while (*cpp != NULL) { addresses++; cpp++; } if (he1 == NULL) { cpp = he2->h_aliases; while (*cpp != NULL) { names++; cpp++; } } } if (addresses == 1) { *error_num = NO_ADDRESS; return (NULL); } he = memget(sizeof *he); if (he == NULL) goto no_recovery; he->h_addr_list = memget(sizeof(char *) * (addresses)); if (he->h_addr_list == NULL) goto cleanup0; memset(he->h_addr_list, 0, sizeof(char *) * (addresses)); /* copy addresses */ npp = he->h_addr_list; if (he1 != NULL) { cpp = he1->h_addr_list; while (*cpp != NULL) { *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); if (*npp == NULL) goto cleanup1; /* convert to mapped if required */ if (af == AF_INET6 && he1->h_addrtype == AF_INET) { memcpy(*npp, in6addr_mapped, sizeof in6addr_mapped); memcpy(*npp + sizeof in6addr_mapped, *cpp, INADDRSZ); } else { memcpy(*npp, *cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); } cpp++; npp++; } } if (he2 != NULL) { cpp = he2->h_addr_list; while (*cpp != NULL) { *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); if (*npp == NULL) goto cleanup1; /* convert to mapped if required */ if (af == AF_INET6 && he2->h_addrtype == AF_INET) { memcpy(*npp, in6addr_mapped, sizeof in6addr_mapped); memcpy(*npp + sizeof in6addr_mapped, *cpp, INADDRSZ); } else { memcpy(*npp, *cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); } cpp++; npp++; } } he->h_aliases = memget(sizeof(char *) * (names)); if (he->h_aliases == NULL) goto cleanup1; memset(he->h_aliases, 0, sizeof(char *) * (names)); /* copy aliases */ npp = he->h_aliases; cpp = (he1 != NULL) ? he1->h_aliases : he2->h_aliases; while (*cpp != NULL) { len = strlen (*cpp) + 1; *npp = memget(len); if (*npp == NULL) goto cleanup2; strcpy(*npp, *cpp); npp++; cpp++; } /* copy hostname */ he->h_name = memget(strlen((he1 != NULL) ? he1->h_name : he2->h_name) + 1); if (he->h_name == NULL) goto cleanup2; strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name); /* set address type and length */ he->h_addrtype = af; he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ; return(he); cleanup2: cpp = he->h_aliases; while (*cpp != NULL) { memput(*cpp, strlen(*cpp) + 1); cpp++; } memput(he->h_aliases, sizeof(char *) * (names)); cleanup1: cpp = he->h_addr_list; while (*cpp != NULL) { memput(*cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); *cpp = NULL; cpp++; } memput(he->h_addr_list, sizeof(char *) * (addresses)); cleanup0: memput(he, sizeof *he); no_recovery: *error_num = NO_RECOVERY; return (NULL); } static struct net_data * init() { struct net_data *net_data; if (!(net_data = net_data_init(NULL))) goto error; if (!net_data->ho) { net_data->ho = (*net_data->irs->ho_map)(net_data->irs); if (!net_data->ho || !net_data->res) { error: errno = EIO; if (net_data && net_data->res) RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL); } return (net_data); } static void freepvt(struct net_data *net_data) { if (net_data->ho_data) { free(net_data->ho_data); net_data->ho_data = NULL; } } static struct hostent * fakeaddr(const char *name, int af, struct net_data *net_data) { struct pvt *pvt; freepvt(net_data); net_data->ho_data = malloc(sizeof (struct pvt)); if (!net_data->ho_data) { errno = ENOMEM; RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } pvt = net_data->ho_data; /* * Unlike its forebear(inet_aton), our friendly inet_pton() is strict * in its interpretation of its input, and it will only return "1" if * the input string is a formally valid(and thus unambiguous with * respect to host names) internet address specification for this AF. * * This means "telnet 0xdeadbeef" and "telnet 127.1" are dead now. */ if (inet_pton(af, name, pvt->addr) != 1) { RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); return (NULL); } strncpy(pvt->name, name, NS_MAXDNAME); pvt->name[NS_MAXDNAME] = '\0'; if (af == AF_INET && (net_data->res->options & RES_USE_INET6) != 0) { map_v4v6_address(pvt->addr, pvt->addr); af = AF_INET6; } pvt->host.h_addrtype = af; switch(af) { case AF_INET: pvt->host.h_length = NS_INADDRSZ; break; case AF_INET6: pvt->host.h_length = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } pvt->host.h_name = pvt->name; pvt->host.h_aliases = pvt->aliases; pvt->aliases[0] = NULL; pvt->addrs[0] = (char *)pvt->addr; pvt->addrs[1] = NULL; pvt->host.h_addr_list = pvt->addrs; RES_SET_H_ERRNO(net_data->res, NETDB_SUCCESS); return (&pvt->host); } #ifdef grot /* for future use in gethostbyaddr(), for "SUNSECURITY" */ struct hostent *rhp; char **haddr; u_long old_options; char hname2[MAXDNAME+1]; if (af == AF_INET) { /* * turn off search as the name should be absolute, * 'localhost' should be matched by defnames */ strncpy(hname2, hp->h_name, MAXDNAME); hname2[MAXDNAME] = '\0'; old_options = net_data->res->options; net_data->res->options &= ~RES_DNSRCH; net_data->res->options |= RES_DEFNAMES; if (!(rhp = gethostbyname(hname2))) { net_data->res->options = old_options; RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); return (NULL); } net_data->res->options = old_options; for (haddr = rhp->h_addr_list; *haddr; haddr++) if (!memcmp(*haddr, addr, INADDRSZ)) break; if (!*haddr) { RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); return (NULL); } } #endif /* grot */ #endif /*__BIND_NOSTATIC*/ Index: head/contrib/bind/lib/irs/getnameinfo.c =================================================================== --- head/contrib/bind/lib/irs/getnameinfo.c (revision 60940) +++ head/contrib/bind/lib/irs/getnameinfo.c (revision 60941) @@ -1,230 +1,225 @@ /* * Issues to be discussed: * - Thread safe-ness must be checked * - Return values. There seems to be no standard for return value (RFC2133) * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by WIDE Project and * its contributors. * 4. Neither the name of the project 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 THE PROJECT 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 PROJECT 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. */ #include #include #include #include #include #include #include #include #include #include #define SUCCESS 0 #define ANY 0 #define YES 1 #define NO 0 /* * Note that a_off will be dynamically adjusted so that to be consistent * with the definition of sockaddr_in{,6}. * The value presented below is just a guess. */ static struct afd { int a_af; int a_addrlen; int a_socklen; int a_off; } afdl [] = { /* first entry is linked last... */ {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 4 /*XXX*/}, {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 8 /*XXX*/}, {0, 0, 0}, }; struct sockinet { u_char si_len; u_char si_family; u_short si_port; }; #define ENI_NOSOCKET 0 #define ENI_NOSERVNAME 1 #define ENI_NOHOSTNAME 2 #define ENI_MEMORY 3 #define ENI_SYSTEM 4 #define ENI_FAMILY 5 #define ENI_SALEN 6 int getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) const struct sockaddr *sa; size_t salen; char *host; size_t hostlen; char *serv; size_t servlen; int flags; { struct afd *afd; struct servent *sp; struct hostent *hp; u_short port; #ifdef HAVE_SA_LEN int len; #endif int family, i; char *addr, *p; - u_long v4a; u_char pfx; static int firsttime = 1; static char numserv[512]; static char numaddr[512]; /* dynamically adjust a_off */ if (firsttime) { struct afd *p; u_char *q; struct sockaddr_in sin; struct sockaddr_in6 sin6; for (p = &afdl[0]; p->a_af; p++) { switch (p->a_af) { case PF_INET: q = (u_char *)&sin.sin_addr.s_addr; p->a_off = q - (u_char *)&sin; break; case PF_INET6: q = (u_char *)&sin6.sin6_addr.s6_addr; p->a_off = q - (u_char *)&sin6; break; default: break; } } firsttime = 0; } if (sa == NULL) return ENI_NOSOCKET; #ifdef HAVE_SA_LEN len = sa->sa_len; if (len != salen) return ENI_SALEN; #endif family = sa->sa_family; for (i = 0; afdl[i].a_af; i++) if (afdl[i].a_af == family) { afd = &afdl[i]; goto found; } return ENI_FAMILY; found: if (salen != afd->a_socklen) return ENI_SALEN; port = ((struct sockinet *)sa)->si_port; /* network byte order */ addr = (char *)sa + afd->a_off; if (serv == NULL || servlen == 0) { /* what we should do? */ } else if (flags & NI_NUMERICSERV) { snprintf(numserv, strlen(numserv), "%d", ntohs(port)); if (strlen(numserv) > servlen) return ENI_MEMORY; strcpy(serv, numserv); } else { sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); if (sp) { if (strlen(sp->s_name) > servlen) return ENI_MEMORY; strcpy(serv, sp->s_name); } else return ENI_NOSERVNAME; } switch (sa->sa_family) { case AF_INET: - v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr; - if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) - flags |= NI_NUMERICHOST; - v4a >>= IN_CLASSA_NSHIFT; - if (v4a == 0 || v4a == IN_LOOPBACKNET) + if (ntohl(*(u_long *)addr) >> IN_CLASSA_NSHIFT == 0) flags |= NI_NUMERICHOST; break; case AF_INET6: - pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0]; + pfx = *addr; if (pfx == 0 || pfx == 0xfe || pfx == 0xff) flags |= NI_NUMERICHOST; break; } if (host == NULL || hostlen == 0) { /* what should we do? */ } else if (flags & NI_NUMERICHOST) { if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) == NULL) return ENI_SYSTEM; if (strlen(numaddr) > hostlen) return ENI_MEMORY; strcpy(host, numaddr); } else { hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); if (hp) { if (flags & NI_NOFQDN) { p = strchr(hp->h_name, '.'); if (p) *p = '\0'; } if (strlen(hp->h_name) > hostlen) return ENI_MEMORY; strcpy(host, hp->h_name); } else { if (flags & NI_NAMEREQD) return ENI_NOHOSTNAME; if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) == NULL) return ENI_NOHOSTNAME; if (strlen(numaddr) > hostlen) return ENI_MEMORY; strcpy(host, numaddr); } } return SUCCESS; } Index: head/contrib/bind/lib/irs/getpwent.c =================================================================== --- head/contrib/bind/lib/irs/getpwent.c (revision 60940) +++ head/contrib/bind/lib/irs/getpwent.c (revision 60941) @@ -1,199 +1,199 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: getpwent.c,v 1.20 1999/10/13 16:39:31 vixie Exp $"; +static const char rcsid[] = "$Id: getpwent.c,v 1.21 2000/02/21 21:40:56 vixie Exp $"; #endif /* Imports */ #include "port_before.h" #if !defined(WANT_IRS_PW) || defined(__BIND_NOSTATIC) static int __bind_irs_pw_unneeded; #else #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_data.h" /* Forward */ static struct net_data * init(void); /* Public */ struct passwd * getpwent(void) { struct net_data *net_data = init(); return (getpwent_p(net_data)); } struct passwd * getpwnam(const char *name) { struct net_data *net_data = init(); return (getpwnam_p(name, net_data)); } struct passwd * getpwuid(uid_t uid) { struct net_data *net_data = init(); return (getpwuid_p(uid, net_data)); } int setpassent(int stayopen) { struct net_data *net_data = init(); return (setpassent_p(stayopen, net_data)); } #ifdef SETPWENT_VOID void setpwent() { struct net_data *net_data = init(); setpwent_p(net_data); } #else int setpwent() { struct net_data *net_data = init(); return (setpwent_p(net_data)); } #endif void endpwent() { struct net_data *net_data = init(); - return (endpwent_p(net_data)); + endpwent_p(net_data); } /* Shared private. */ struct passwd * getpwent_p(struct net_data *net_data) { struct irs_pw *pw; if (!net_data || !(pw = net_data->pw)) return (NULL); net_data->pw_last = (*pw->next)(pw); return (net_data->pw_last); } struct passwd * getpwnam_p(const char *name, struct net_data *net_data) { struct irs_pw *pw; if (!net_data || !(pw = net_data->pw)) return (NULL); if (net_data->pw_stayopen && net_data->pw_last && !strcmp(net_data->pw_last->pw_name, name)) return (net_data->pw_last); net_data->pw_last = (*pw->byname)(pw, name); if (!net_data->pw_stayopen) endpwent(); return (net_data->pw_last); } struct passwd * getpwuid_p(uid_t uid, struct net_data *net_data) { struct irs_pw *pw; if (!net_data || !(pw = net_data->pw)) return (NULL); if (net_data->pw_stayopen && net_data->pw_last && net_data->pw_last->pw_uid == uid) return (net_data->pw_last); net_data->pw_last = (*pw->byuid)(pw, uid); if (!net_data->pw_stayopen) endpwent(); return (net_data->pw_last); } int setpassent_p(int stayopen, struct net_data *net_data) { struct irs_pw *pw; if (!net_data || !(pw = net_data->pw)) return (0); (*pw->rewind)(pw); net_data->pw_stayopen = (stayopen != 0); if (stayopen == 0) net_data_minimize(net_data); return (1); } #ifdef SETPWENT_VOID void setpwent_p(struct net_data *net_data) { (void) setpassent_p(0, net_data); } #else int setpwent_p(struct net_data *net_data) { return (setpassent_p(0, net_data)); } #endif void endpwent_p(struct net_data *net_data) { struct irs_pw *pw; if ((net_data != NULL) && ((pw = net_data->pw) != NULL)) (*pw->minimize)(pw); } /* Private */ static struct net_data * init() { struct net_data *net_data; if (!(net_data = net_data_init(NULL))) goto error; if (!net_data->pw) { net_data->pw = (*net_data->irs->pw_map)(net_data->irs); if (!net_data->pw || !net_data->res) { error: errno = EIO; return (NULL); } (*net_data->pw->res_set)(net_data->pw, net_data->res, NULL); } return (net_data); } #endif /* WANT_IRS_PW */ Index: head/contrib/bind/lib/irs/hesiod.c =================================================================== --- head/contrib/bind/lib/irs/hesiod.c (revision 60940) +++ head/contrib/bind/lib/irs/hesiod.c (revision 60941) @@ -1,503 +1,504 @@ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: hesiod.c,v 1.20 1999/02/22 04:09:06 vixie Exp $"; +static const char rcsid[] = "$Id: hesiod.c,v 1.21 2000/02/28 14:51:08 vixie Exp $"; #endif /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * This file is primarily maintained by and . */ /* * hesiod.c --- the core portion of the hesiod resolver. * * This file is derived from the hesiod library from Project Athena; * It has been extensively rewritten by Theodore Ts'o to have a more * thread-safe interface. */ /* Imports */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "pathnames.h" #include "hesiod.h" #include "hesiod_p.h" /* Forward */ int hesiod_init(void **context); void hesiod_end(void *context); char * hesiod_to_bind(void *context, const char *name, const char *type); char ** hesiod_resolve(void *context, const char *name, const char *type); void hesiod_free_list(void *context, char **list); static int parse_config_file(struct hesiod_p *ctx, const char *filename); static char ** get_txt_records(struct hesiod_p *ctx, int class, const char *name); static int init(struct hesiod_p *ctx); /* Public */ /* * This function is called to initialize a hesiod_p. */ int hesiod_init(void **context) { struct hesiod_p *ctx; char *cp; ctx = malloc(sizeof(struct hesiod_p)); if (ctx == 0) { errno = ENOMEM; return (-1); } ctx->LHS = NULL; ctx->RHS = NULL; + ctx->res = NULL; if (parse_config_file(ctx, _PATH_HESIOD_CONF) < 0) { #ifdef DEF_RHS /* * Use compiled in defaults. */ ctx->LHS = malloc(strlen(DEF_LHS)+1); ctx->RHS = malloc(strlen(DEF_RHS)+1); if (ctx->LHS == 0 || ctx->RHS == 0) { errno = ENOMEM; goto cleanup; } strcpy(ctx->LHS, DEF_LHS); strcpy(ctx->RHS, DEF_RHS); #else goto cleanup; #endif } /* * The default RHS can be overridden by an environment * variable. */ if ((cp = getenv("HES_DOMAIN")) != NULL) { if (ctx->RHS) free(ctx->RHS); ctx->RHS = malloc(strlen(cp)+2); if (!ctx->RHS) { errno = ENOMEM; goto cleanup; } if (cp[0] == '.') strcpy(ctx->RHS, cp); else { strcpy(ctx->RHS, "."); strcat(ctx->RHS, cp); } } /* * If there is no default hesiod realm set, we return an * error. */ if (!ctx->RHS) { errno = ENOEXEC; goto cleanup; } #if 0 if (res_ninit(ctx->res) < 0) goto cleanup; #endif *context = ctx; return (0); cleanup: hesiod_end(ctx); return (-1); } /* * This function deallocates the hesiod_p */ void hesiod_end(void *context) { struct hesiod_p *ctx = (struct hesiod_p *) context; int save_errno = errno; if (ctx->res) res_nclose(ctx->res); if (ctx->RHS) free(ctx->RHS); if (ctx->LHS) free(ctx->LHS); if (ctx->res && ctx->free_res) (*ctx->free_res)(ctx->res); free(ctx); errno = save_errno; } /* * This function takes a hesiod (name, type) and returns a DNS * name which is to be resolved. */ char * hesiod_to_bind(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname; char **rhs_list = NULL; const char *RHS, *cp; /* Decide what our RHS is, and set cp to the end of the actual name. */ if ((cp = strchr(name, '@')) != NULL) { if (strchr(cp + 1, '.')) RHS = cp + 1; else if ((rhs_list = hesiod_resolve(context, cp + 1, "rhs-extension")) != NULL) RHS = *rhs_list; else { errno = ENOENT; return (NULL); } } else { RHS = ctx->RHS; cp = name + strlen(name); } /* * Allocate the space we need, including up to three periods and * the terminating NUL. */ if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) + (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) { errno = ENOMEM; if (rhs_list) hesiod_free_list(context, rhs_list); return NULL; } /* Now put together the DNS name. */ memcpy(bindname, name, cp - name); bindname[cp - name] = '\0'; strcat(bindname, "."); strcat(bindname, type); if (ctx->LHS) { if (ctx->LHS[0] != '.') strcat(bindname, "."); strcat(bindname, ctx->LHS); } if (RHS[0] != '.') strcat(bindname, "."); strcat(bindname, RHS); if (rhs_list) hesiod_free_list(context, rhs_list); return (bindname); } /* * This is the core function. Given a hesiod (name, type), it * returns an array of strings returned by the resolver. */ char ** hesiod_resolve(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname = hesiod_to_bind(context, name, type); char **retvec; if (bindname == NULL) return (NULL); if (init(ctx) == -1) { free(bindname); return (NULL); } if ((retvec = get_txt_records(ctx, C_IN, bindname))) { free(bindname); return (retvec); } if (errno != ENOENT) return (NULL); retvec = get_txt_records(ctx, C_HS, bindname); free(bindname); return (retvec); } void hesiod_free_list(void *context, char **list) { char **p; for (p = list; *p; p++) free(*p); free(list); } /* * This function parses the /etc/hesiod.conf file */ static int parse_config_file(struct hesiod_p *ctx, const char *filename) { char *key, *data, *cp, **cpp; char buf[MAXDNAME+7]; FILE *fp; /* * Clear the existing configuration variable, just in case * they're set. */ if (ctx->RHS) free(ctx->RHS); if (ctx->LHS) free(ctx->LHS); ctx->RHS = ctx->LHS = 0; /* * Now open and parse the file... */ if (!(fp = fopen(filename, "r"))) return (-1); while (fgets(buf, sizeof(buf), fp) != NULL) { cp = buf; if (*cp == '#' || *cp == '\n' || *cp == '\r') continue; while(*cp == ' ' || *cp == '\t') cp++; key = cp; while(*cp != ' ' && *cp != '\t' && *cp != '=') cp++; *cp++ = '\0'; while(*cp == ' ' || *cp == '\t' || *cp == '=') cp++; data = cp; while(*cp != ' ' && *cp != '\n' && *cp != '\r') cp++; *cp++ = '\0'; if (strcmp(key, "lhs") == 0) cpp = &ctx->LHS; else if (strcmp(key, "rhs") == 0) cpp = &ctx->RHS; else continue; *cpp = malloc(strlen(data) + 1); if (!*cpp) { errno = ENOMEM; goto cleanup; } strcpy(*cpp, data); } fclose(fp); return (0); cleanup: fclose(fp); if (ctx->RHS) free(ctx->RHS); if (ctx->LHS) free(ctx->LHS); ctx->RHS = ctx->LHS = 0; return (-1); } /* * Given a DNS class and a DNS name, do a lookup for TXT records, and * return a list of them. */ static char ** get_txt_records(struct hesiod_p *ctx, int class, const char *name) { struct { int type; /* RR type */ int class; /* RR class */ int dlen; /* len of data section */ u_char *data; /* pointer to data */ } rr; HEADER *hp; u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP]; u_char *cp, *erdata, *eom; char *dst, *edst, **list; int ancount, qdcount; int i, j, n, skip; /* * Construct the query and send it. */ n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0, NULL, qbuf, MAX_HESRESP); if (n < 0) { errno = EMSGSIZE; return (NULL); } n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP); if (n < 0) { errno = ECONNREFUSED; return (NULL); } if (n < HFIXEDSZ) { errno = EMSGSIZE; return (NULL); } /* * OK, parse the result. */ hp = (HEADER *) abuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); cp = abuf + sizeof(HEADER); eom = abuf + n; /* Skip query, trying to get to the answer section which follows. */ for (i = 0; i < qdcount; i++) { skip = dn_skipname(cp, eom); if (skip < 0 || cp + skip + QFIXEDSZ > eom) { errno = EMSGSIZE; return (NULL); } cp += skip + QFIXEDSZ; } list = malloc((ancount + 1) * sizeof(char *)); if (!list) { errno = ENOMEM; return (NULL); } j = 0; for (i = 0; i < ancount; i++) { skip = dn_skipname(cp, eom); if (skip < 0) { errno = EMSGSIZE; goto cleanup; } cp += skip; if (cp + 3 * INT16SZ + INT32SZ > eom) { errno = EMSGSIZE; goto cleanup; } rr.type = ns_get16(cp); cp += INT16SZ; rr.class = ns_get16(cp); cp += INT16SZ + INT32SZ; /* skip the ttl, too */ rr.dlen = ns_get16(cp); cp += INT16SZ; if (cp + rr.dlen > eom) { errno = EMSGSIZE; goto cleanup; } rr.data = cp; cp += rr.dlen; if (rr.class != class || rr.type != T_TXT) continue; if (!(list[j] = malloc(rr.dlen))) goto cleanup; dst = list[j++]; edst = dst + rr.dlen; erdata = rr.data + rr.dlen; cp = rr.data; while (cp < erdata) { n = (unsigned char) *cp++; if (cp + n > eom || dst + n > edst) { errno = EMSGSIZE; goto cleanup; } memcpy(dst, cp, n); cp += n; dst += n; } if (cp != erdata) { errno = EMSGSIZE; goto cleanup; } *dst = '\0'; } list[j] = NULL; if (j == 0) { errno = ENOENT; goto cleanup; } return (list); cleanup: for (i = 0; i < j; i++) free(list[i]); free(list); return (NULL); } struct __res_state * __hesiod_res_get(void *context) { struct hesiod_p *ctx = context; if (!ctx->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (res == NULL) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); __hesiod_res_set(ctx, res, free); } return (ctx->res); } void __hesiod_res_set(void *context, struct __res_state *res, void (*free_res)(void *)) { struct hesiod_p *ctx = context; if (ctx->res && ctx->free_res) { res_nclose(ctx->res); (*ctx->free_res)(ctx->res); } ctx->res = res; ctx->free_res = free_res; } static int init(struct hesiod_p *ctx) { if (!ctx->res && !__hesiod_res_get(ctx)) return (-1); if (((ctx->res->options & RES_INIT) == 0) && (res_ninit(ctx->res) == -1)) return (-1); return (0); } Index: head/contrib/bind/lib/irs/irp.c =================================================================== --- head/contrib/bind/lib/irs/irp.c (revision 60940) +++ head/contrib/bind/lib/irs/irp.c (revision 60941) @@ -1,583 +1,590 @@ /* * Copyright (c) 1996, 1998 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: irp.c,v 8.5 1999/10/13 17:11:18 vixie Exp $"; +static const char rcsid[] = "$Id: irp.c,v 8.6 2000/02/04 08:28:33 vixie Exp $"; #endif /* Imports */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "irs_p.h" #include "irp_p.h" #include "port_after.h" /* Forward. */ static void irp_close(struct irs_acc *); #define LINEINCR 128 #if !defined(SUN_LEN) #define SUN_LEN(su) \ (sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path)) #endif /* Public */ /* send errors to syslog if true. */ int irp_log_errors = 1; /* * This module handles the irp module connection to irpd. * * The client expects a synchronous interface to functions like * getpwnam(3), so we can't use the ctl_* i/o library on this end of * the wire (it's used in the server). */ /* * irs_acc *irs_irp_acc(const char *options); * * Initialize the irp module. */ struct irs_acc * irs_irp_acc(const char *options) { struct irs_acc *acc; struct irp_p *irp; if (!(acc = memget(sizeof *acc))) { errno = ENOMEM; return (NULL); } memset(acc, 0x5e, sizeof *acc); if (!(irp = memget(sizeof *irp))) { errno = ENOMEM; free(acc); return (NULL); } irp->inlast = 0; irp->incurr = 0; irp->fdCxn = -1; acc->private = irp; #ifdef WANT_IRS_GR acc->gr_map = irs_irp_gr; #else acc->gr_map = NULL; #endif #ifdef WANT_IRS_PW acc->pw_map = irs_irp_pw; #else acc->pw_map = NULL; #endif acc->sv_map = irs_irp_sv; acc->pr_map = irs_irp_pr; acc->ho_map = irs_irp_ho; acc->nw_map = irs_irp_nw; acc->ng_map = irs_irp_ng; acc->close = irp_close; return (acc); } int irs_irp_connection_setup(struct irp_p *cxndata, int *warned) { if (irs_irp_is_connected(cxndata)) { return (0); } else if (irs_irp_connect(cxndata) != 0) { if (warned != NULL && !*warned) { syslog(LOG_ERR, "irpd connection failed: %m\n"); (*warned)++; } return (-1); } return (0); } /* * int irs_irp_connect(void); * * Sets up the connection to the remote irpd server. * * Returns: * * 0 on success, -1 on failure. * */ int irs_irp_connect(struct irp_p *pvt) { int flags; struct sockaddr *addr; struct sockaddr_in iaddr; +#ifndef NO_SOCKADDR_UN struct sockaddr_un uaddr; +#endif long ipaddr; const char *irphost; int code; char text[256]; int socklen = 0; if (pvt->fdCxn != -1) { perror("fd != 1"); return (-1); } +#ifndef NO_SOCKADDR_UN memset(&uaddr, 0, sizeof uaddr); +#endif memset(&iaddr, 0, sizeof iaddr); irphost = getenv(IRPD_HOST_ENV); if (irphost == NULL) { irphost = "127.0.0.1"; } +#ifndef NO_SOCKADDR_UN if (irphost[0] == '/') { addr = (struct sockaddr *)&uaddr; strncpy(uaddr.sun_path, irphost, sizeof uaddr.sun_path); uaddr.sun_family = AF_UNIX; socklen = SUN_LEN(&uaddr); #ifdef HAVE_SA_LEN uaddr.sun_len = socklen; #endif - } else { + } else +#endif + { if (inet_pton(AF_INET, irphost, &ipaddr) != 1) { errno = EADDRNOTAVAIL; perror("inet_pton"); return (-1); } addr = (struct sockaddr *)&iaddr; socklen = sizeof iaddr; #ifdef HAVE_SA_LEN iaddr.sin_len = socklen; #endif iaddr.sin_family = AF_INET; iaddr.sin_port = htons(IRPD_PORT); iaddr.sin_addr.s_addr = ipaddr; } pvt->fdCxn = socket(addr->sa_family, SOCK_STREAM, PF_UNSPEC); if (pvt->fdCxn < 0) { perror("socket"); return (-1); } if (connect(pvt->fdCxn, addr, socklen) != 0) { perror("connect"); return (-1); } flags = fcntl(pvt->fdCxn, F_GETFL, 0); if (flags < 0) { close(pvt->fdCxn); perror("close"); return (-1); } #if 0 flags |= O_NONBLOCK; if (fcntl(pvt->fdCxn, F_SETFL, flags) < 0) { close(pvt->fdCxn); perror("fcntl"); return (-1); } #endif code = irs_irp_read_response(pvt, text, sizeof text); if (code != IRPD_WELCOME_CODE) { if (irp_log_errors) { syslog(LOG_WARNING, "Connection failed: %s", text); } irs_irp_disconnect(pvt); return (-1); } return (0); } /* * int irs_irp_is_connected(struct irp_p *pvt); * * Returns: * * Non-zero if streams are setup to remote. * */ int irs_irp_is_connected(struct irp_p *pvt) { return (pvt->fdCxn >= 0); } /* * void * irs_irp_disconnect(struct irp_p *pvt); * * Closes streams to remote. */ void irs_irp_disconnect(struct irp_p *pvt) { if (pvt->fdCxn != -1) { close(pvt->fdCxn); pvt->fdCxn = -1; } } int irs_irp_read_line(struct irp_p *pvt, char *buffer, int len) { char *realstart = &pvt->inbuffer[0]; char *p, *start, *end; int spare; int i; int buffpos = 0; int left = len - 1; while (left > 0) { start = p = &pvt->inbuffer[pvt->incurr]; end = &pvt->inbuffer[pvt->inlast]; while (p != end && *p != '\n') p++; if (p == end) { /* Found no newline so shift data down if necessary * and append new data to buffer */ if (start > realstart) { memmove(realstart, start, end - start); pvt->inlast = end - start; start = realstart; pvt->incurr = 0; end = &pvt->inbuffer[pvt->inlast]; } spare = sizeof (pvt->inbuffer) - pvt->inlast; p = end; i = read(pvt->fdCxn, end, spare); if (i < 0) { close(pvt->fdCxn); pvt->fdCxn = -1; return (buffpos > 0 ? buffpos : -1); } else if (i == 0) { return (buffpos); } end += i; pvt->inlast += i; while (p != end && *p != '\n') p++; } if (p == end) { /* full buffer and still no newline */ i = sizeof pvt->inbuffer; } else { /* include newline */ i = p - start + 1; } if (i > left) i = left; memcpy(buffer + buffpos, start, i); pvt->incurr += i; buffpos += i; buffer[buffpos] = '\0'; if (p != end) { left = 0; } else { left -= i; } } #if 0 fprintf(stderr, "read line: %s\n", buffer); #endif return (buffpos); } /* * int irp_read_response(struct irp_p *pvt); * * Returns: * * The number found at the beginning of the line read from * FP. 0 on failure(0 is not a legal response code). The * rest of the line is discarded. * */ int irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen) { char line[1024]; int code; char *p; if (irs_irp_read_line(pvt, line, sizeof line) <= 0) { return (0); } p = strchr(line, '\n'); if (p == NULL) { return (0); } if (sscanf(line, "%d", &code) != 1) { code = 0; } else if (text != NULL && textlen > 0) { p = line; while (isspace(*p)) p++; while (isdigit(*p)) p++; while (isspace(*p)) p++; strncpy(text, p, textlen - 1); p[textlen - 1] = '\0'; } return (code); } /* * char *irp_read_body(struct irp_p *pvt, size_t *size); * * Read in the body of a response. Terminated by a line with * just a dot on it. Lines should be terminated with a CR-LF * sequence, but we're nt piccky if the CR is missing. * No leading dot escaping is done as the protcol doesn't * use leading dots anywhere. * * Returns: * * Pointer to null-terminated buffer allocated by memget. * *SIZE is set to the length of the buffer. * */ char * irs_irp_read_body(struct irp_p *pvt, size_t *size) { char line[1024]; u_int linelen; size_t len = LINEINCR; char *buffer = memget(len); int idx = 0; for (;;) { if (irs_irp_read_line(pvt, line, sizeof line) <= 0 || strchr(line, '\n') == NULL) goto death; linelen = strlen(line); if (line[linelen - 1] != '\n') goto death; /* We're not strict about missing \r. Should we be?? */ if (linelen > 2 && line[linelen - 2] == '\r') { line[linelen - 2] = '\n'; line[linelen - 1] = '\0'; linelen--; } if (linelen == 2 && line[0] == '.') { *size = len; buffer[idx] = '\0'; return (buffer); } if (linelen > (len - (idx + 1))) { char *p = memget(len + LINEINCR); if (p == NULL) goto death; memcpy(p, buffer, len); memput(buffer, len); buffer = p; len += LINEINCR; } memcpy(buffer + idx, line, linelen); idx += linelen; } death: memput(buffer, len); return (NULL); } /* * int irs_irp_get_full_response(struct irp_p *pvt, int *code, * char **body, size_t *bodylen); * * Gets the response to a command. If the response indicates * there's a body to follow(code % 10 == 1), then the * body buffer is allcoated with memget and stored in * *BODY. The length of the allocated body buffer is stored * in *BODY. The caller must give the body buffer back to * memput when done. The results code is stored in *CODE. * * Returns: * * 0 if a result was read. -1 on some sort of failure. * */ int irs_irp_get_full_response(struct irp_p *pvt, int *code, char *text, size_t textlen, char **body, size_t *bodylen) { int result = irs_irp_read_response(pvt, text, textlen); *body = NULL; if (result == 0) { return (-1); } *code = result; /* Code that matches 2xx is a good result code. * Code that matches xx1 means there's a response body coming. */ if ((result / 100) == 2 && (result % 10) == 1) { *body = irs_irp_read_body(pvt, bodylen); if (*body == NULL) { return (-1); } } return (0); } /* * int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...); * * Sends command to remote connected via the PVT * struture. FMT and args after it are fprintf-like * arguments for formatting. * * Returns: * * 0 on success, -1 on failure. */ int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...) { va_list ap; char buffer[1024]; int pos = 0; int i, todo; if (pvt->fdCxn < 0) { return (-1); } va_start(ap, fmt); todo = vsprintf(buffer, fmt, ap); if (todo > sizeof buffer - 2) { syslog(LOG_CRIT, "memory overrun in irs_irp_send_command()"); exit(1); } strcat(buffer, "\r\n"); todo = strlen(buffer); while (todo > 0) { i = write(pvt->fdCxn, buffer + pos, todo); #if 0 /* XXX brister */ fprintf(stderr, "Wrote: \""); fwrite(buffer + pos, sizeof (char), todo, stderr); fprintf(stderr, "\"\n"); #endif if (i < 0) { close(pvt->fdCxn); pvt->fdCxn = -1; return (-1); } todo -= i; } va_end(ap); return (0); } /* Methods */ /* * void irp_close(struct irs_acc *this) * */ static void irp_close(struct irs_acc *this) { struct irp_p *irp = (struct irp_p *)this->private; if (irp != NULL) { irs_irp_disconnect(irp); memput(irp, sizeof *irp); } memput(this, sizeof *this); } Index: head/contrib/bind/lib/irs/lcl.c =================================================================== --- head/contrib/bind/lib/irs/lcl.c (revision 60940) +++ head/contrib/bind/lib/irs/lcl.c (revision 60941) @@ -1,138 +1,138 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: lcl.c,v 1.15 1999/10/13 16:39:32 vixie Exp $"; +static const char rcsid[] = "$Id: lcl.c,v 1.16 2000/02/28 07:52:16 vixie Exp $"; #endif /* Imports */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "lcl_p.h" /* Forward. */ static void lcl_close(struct irs_acc *); static struct __res_state * lcl_res_get(struct irs_acc *); static void lcl_res_set(struct irs_acc *, struct __res_state *, void (*)(void *)); /* Public */ struct irs_acc * irs_lcl_acc(const char *options) { struct irs_acc *acc; struct lcl_p *lcl; if (!(acc = memget(sizeof *acc))) { errno = ENOMEM; return (NULL); } memset(acc, 0x5e, sizeof *acc); if (!(lcl = memget(sizeof *lcl))) { errno = ENOMEM; free(acc); return (NULL); } memset(lcl, 0x5e, sizeof *lcl); lcl->res = NULL; lcl->free_res = NULL; acc->private = lcl; #ifdef WANT_IRS_GR acc->gr_map = irs_lcl_gr; #else acc->gr_map = NULL; #endif #ifdef WANT_IRS_PW acc->pw_map = irs_lcl_pw; #else acc->pw_map = NULL; #endif acc->sv_map = irs_lcl_sv; acc->pr_map = irs_lcl_pr; acc->ho_map = irs_lcl_ho; acc->nw_map = irs_lcl_nw; acc->ng_map = irs_lcl_ng; acc->res_get = lcl_res_get; acc->res_set = lcl_res_set; acc->close = lcl_close; return (acc); } /* Methods */ static struct __res_state * lcl_res_get(struct irs_acc *this) { struct lcl_p *lcl = (struct lcl_p *)this->private; if (lcl->res == NULL) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (res == NULL) return (NULL); memset(res, 0, sizeof *res); lcl_res_set(this, res, free); } - if ((lcl->res->options | RES_INIT) == 0 && + if ((lcl->res->options & RES_INIT) == 0 && res_ninit(lcl->res) < 0) return (NULL); return (lcl->res); } static void lcl_res_set(struct irs_acc *this, struct __res_state *res, void (*free_res)(void *)) { struct lcl_p *lcl = (struct lcl_p *)this->private; if (lcl->res && lcl->free_res) { res_nclose(lcl->res); (*lcl->free_res)(lcl->res); } lcl->res = res; lcl->free_res = free_res; } static void lcl_close(struct irs_acc *this) { struct lcl_p *lcl = (struct lcl_p *)this->private; if (lcl) { if (lcl->free_res) (*lcl->free_res)(lcl->res); memput(lcl, sizeof *lcl); } memput(this, sizeof *this); } Index: head/contrib/bind/lib/irs/nis.c =================================================================== --- head/contrib/bind/lib/irs/nis.c (revision 60940) +++ head/contrib/bind/lib/irs/nis.c (revision 60941) @@ -1,149 +1,149 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: nis.c,v 1.13 1999/01/18 07:46:58 vixie Exp $"; +static const char rcsid[] = "$Id: nis.c,v 1.14 2000/02/28 07:52:16 vixie Exp $"; #endif /* Imports */ #include "port_before.h" #ifdef WANT_IRS_NIS #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "hesiod.h" #include "nis_p.h" /* Forward */ static void nis_close(struct irs_acc *); static struct __res_state * nis_res_get(struct irs_acc *); static void nis_res_set(struct irs_acc *, struct __res_state *, void (*)(void *)); /* Public */ struct irs_acc * irs_nis_acc(const char *options) { struct nis_p *nis; struct irs_acc *acc; char *domain; if (yp_get_default_domain(&domain) != 0) return (NULL); if (!(nis = memget(sizeof *nis))) { errno = ENOMEM; return (NULL); } memset(nis, 0, sizeof *nis); if (!(acc = memget(sizeof *acc))) { memput(nis, sizeof *nis); errno = ENOMEM; return (NULL); } memset(acc, 0x5e, sizeof *acc); acc->private = nis; nis->domain = strdup(domain); #ifdef WANT_IRS_GR acc->gr_map = irs_nis_gr; #else acc->gr_map = NULL; #endif #ifdef WANT_IRS_PW acc->pw_map = irs_nis_pw; #else acc->pw_map = NULL; #endif acc->sv_map = irs_nis_sv; acc->pr_map = irs_nis_pr; acc->ho_map = irs_nis_ho; acc->nw_map = irs_nis_nw; acc->ng_map = irs_nis_ng; acc->res_get = nis_res_get; acc->res_set = nis_res_set; acc->close = nis_close; return (acc); } /* Methods */ static struct __res_state * nis_res_get(struct irs_acc *this) { struct nis_p *nis = (struct nis_p *)this->private; if (nis->res == NULL) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (res == NULL) return (NULL); memset(res, 0, sizeof *res); nis_res_set(this, res, free); } - if ((nis->res->options | RES_INIT) == 0 && + if ((nis->res->options & RES_INIT) == 0 && res_ninit(nis->res) < 0) return (NULL); return (nis->res); } static void nis_res_set(struct irs_acc *this, struct __res_state *res, void (*free_res)(void *)) { struct nis_p *nis = (struct nis_p *)this->private; if (nis->res && nis->free_res) { res_nclose(nis->res); (*nis->free_res)(nis->res); } nis->res = res; nis->free_res = free_res; } static void nis_close(struct irs_acc *this) { struct nis_p *nis = (struct nis_p *)this->private; if (nis->res && nis->free_res) (*nis->free_res)(nis->res); free(nis->domain); memput(nis, sizeof *nis); memput(this, sizeof *this); } #endif /*WANT_IRS_NIS*/ Index: head/contrib/bind/lib/irs/util.c =================================================================== --- head/contrib/bind/lib/irs/util.c (revision 60940) +++ head/contrib/bind/lib/irs/util.c (revision 60941) @@ -1,107 +1,107 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: util.c,v 1.10 1999/01/08 19:25:11 vixie Exp $"; +static const char rcsid[] = "$Id: util.c,v 1.11 2000/02/04 08:28:33 vixie Exp $"; #endif #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) sprintf x #endif void map_v4v6_address(const char *src, char *dst) { u_char *p = (u_char *)dst; - char tmp[INADDRSZ]; + char tmp[NS_INADDRSZ]; int i; /* Stash a temporary copy so our caller can update in place. */ - memcpy(tmp, src, INADDRSZ); + memcpy(tmp, src, NS_INADDRSZ); /* Mark this ipv6 addr as a mapped ipv4. */ for (i = 0; i < 10; i++) *p++ = 0x00; *p++ = 0xff; *p++ = 0xff; /* Retrieve the saved copy and we're done. */ - memcpy((void*)p, tmp, INADDRSZ); + memcpy((void*)p, tmp, NS_INADDRSZ); } int make_group_list(struct irs_gr *this, const char *name, gid_t basegid, gid_t *groups, int *ngroups) { struct group *grp; int i, ng; int ret, maxgroups; ret = -1; ng = 0; maxgroups = *ngroups; /* * When installing primary group, duplicate it; * the first element of groups is the effective gid * and will be overwritten when a setgid file is executed. */ if (ng >= maxgroups) goto done; groups[ng++] = basegid; if (ng >= maxgroups) goto done; groups[ng++] = basegid; /* * Scan the group file to find additional groups. */ (*this->rewind)(this); while ((grp = (*this->next)(this)) != NULL) { if (grp->gr_gid == basegid) continue; for (i = 0; grp->gr_mem[i]; i++) { if (!strcmp(grp->gr_mem[i], name)) { if (ng >= maxgroups) goto done; groups[ng++] = grp->gr_gid; break; } } } ret = 0; done: *ngroups = ng; return (ret); } Index: head/contrib/bind/lib/isc/Makefile =================================================================== --- head/contrib/bind/lib/isc/Makefile (revision 60940) +++ head/contrib/bind/lib/isc/Makefile (revision 60941) @@ -1,95 +1,96 @@ # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. -# $Id: Makefile,v 8.22 1999/02/22 02:47:58 vixie Exp $ +# $Id: Makefile,v 8.25 2000/02/29 03:38:23 vixie Exp $ # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos DESTDIR = DESTLIB = /usr/local/lib O=o A=a CC= cc LD= ld SHELL= /bin/sh CDEBUG= -g TOP= ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.${A} LIBBINDR = ../${TOP}/lib/libbind_r.${A} CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} # -Wimplicit LD_LIBFLAGS= -x -r AR= ar cru RANLIB= ranlib INSTALL= install INSTALL_EXEC= INSTALL_LIB=-o bin -g bin THREADED= threaded SRCS= tree.c base64.c bitncmp.c assertions.c \ memcluster.c logging.c heap.c \ ctl_p.c ctl_srvr.c ctl_clnt.c \ eventlib.c ev_connects.c ev_files.c \ ev_timers.c ev_streams.c ev_waits.c OBJS= tree.${O} base64.${O} bitncmp.${O} assertions.${O} \ memcluster.${O} logging.${O} heap.${O} \ ctl_p.${O} ctl_srvr.${O} ctl_clnt.${O} \ eventlib.${O} ev_connects.${O} ev_files.${O} \ ev_timers.${O} ev_streams.${O} ev_waits.${O} all: ${LIBBIND} ${LIBBIND}: ${OBJS} ( cd ${THREADED} ; \ ${AR} ${LIBBINDR} ${ARPREF} ${OBJS} ${ARSUFF} ; \ ${RANLIB} ${LIBBINDR} ) ${AR} ${LIBBIND} ${ARPREF} ${OBJS} ${ARSUFF} ${RANLIB} ${LIBBIND} .c.${O}: - if test ! -d ${THREADED} ; then mkdir ${THREADED} ; fi + if test ! -d ${THREADED} ; then mkdir ${THREADED} ; else true ; fi ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} ${REENTRANT} -c $*.c \ -o ${THREADED}/$*.${O} - -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} -o a.out && \ - ${LDS} mv a.out ${THREADED}/$*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} \ + -o ${THREADED}/$*.out && \ + ${LDS} mv ${THREADED}/$*.out ${THREADED}/$*.${O} ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} -c $*.c - -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o a.out && \ - ${LDS} mv a.out $*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o $*.out && \ + ${LDS} mv $*.out $*.${O} distclean: clean clean: FRC rm -f .depend a.out core ${LIB} tags rm -f *.${O} *.BAK *.CKP *~ rm -f ${THREADED}/*.${O} - -rmdir ${THREADED} + -if test -d ${THREADED} ; then rmdir ${THREADED}; else true; fi depend: FRC mkdep -I${INCL} -I${PORTINCL} ${CPPFLAGS} ${SRCS} links: FRC @set -e; ln -s SRC/*.[ch] . install: FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/lib/isc/ctl_p.c =================================================================== --- head/contrib/bind/lib/isc/ctl_p.c (revision 60940) +++ head/contrib/bind/lib/isc/ctl_p.c (revision 60941) @@ -1,156 +1,160 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ctl_p.c,v 8.6 1999/10/13 16:39:34 vixie Exp $"; +static const char rcsid[] = "$Id: ctl_p.c,v 8.7 2000/02/04 08:28:33 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1998,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Extern. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctl_p.h" #include "port_after.h" /* Constants. */ const char * const ctl_sevnames[] = { "debug", "warning", "error" }; /* Public. */ /* * ctl_logger() * if ctl_startup()'s caller didn't specify a logger, this one * is used. this pollutes stderr with all kinds of trash so it will * probably never be used in real applications. */ void ctl_logger(enum ctl_severity severity, const char *format, ...) { va_list ap; static const char me[] = "ctl_logger"; fprintf(stderr, "%s(%s): ", me, ctl_sevnames[severity]); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); } int ctl_bufget(struct ctl_buf *buf, ctl_logfunc logger) { static const char me[] = "ctl_bufget"; REQUIRE(!allocated_p(*buf) && buf->used == 0); buf->text = memget(MAX_LINELEN); if (!allocated_p(*buf)) { (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); return (-1); } buf->used = 0; return (0); } void ctl_bufput(struct ctl_buf *buf) { REQUIRE(allocated_p(*buf)); memput(buf->text, MAX_LINELEN); buf->text = NULL; buf->used = 0; } const char * ctl_sa_ntop(const struct sockaddr *sa, char *buf, size_t size, ctl_logfunc logger) { static const char me[] = "ctl_sa_ntop"; static const char punt[] = "[0].-1"; char tmp[sizeof "255.255.255.255"]; switch (sa->sa_family) { case AF_INET: { const struct sockaddr_in *in = (struct sockaddr_in *) sa; if (inet_ntop(in->sin_family, &in->sin_addr, tmp, sizeof tmp) == NULL) { (*logger)(ctl_error, "%s: inet_ntop(%u %04x %08x): %s", me, in->sin_family, in->sin_port, in->sin_addr.s_addr, strerror(errno)); return (punt); } if (strlen(tmp) + sizeof "[].65535" > size) { (*logger)(ctl_error, "%s: buffer overflow", me); return (punt); } (void) sprintf(buf, "[%s].%u", tmp, ntohs(in->sin_port)); return (buf); } +#ifndef NO_SOCKADDR_UN case AF_UNIX: { const struct sockaddr_un *un = (struct sockaddr_un *) sa; int x = sizeof un->sun_path; if (x > size) x = size; strncpy(buf, un->sun_path, x - 1); buf[x - 1] = '\0'; return (buf); } +#endif default: return (punt); } } void ctl_sa_copy(const struct sockaddr *src, struct sockaddr *dst) { switch (src->sa_family) { case AF_INET: *((struct sockaddr_in *)dst) = *((struct sockaddr_in *)src); break; +#ifndef NO_SOCKADDR_UN case AF_UNIX: *((struct sockaddr_un *)dst) = *((struct sockaddr_un *)src); break; +#endif default: *dst = *src; break; } } Index: head/contrib/bind/lib/isc/ctl_srvr.c =================================================================== --- head/contrib/bind/lib/isc/ctl_srvr.c (revision 60940) +++ head/contrib/bind/lib/isc/ctl_srvr.c (revision 60941) @@ -1,744 +1,751 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ctl_srvr.c,v 8.21 1999/10/17 08:41:57 cyarnell Exp $"; +static const char rcsid[] = "$Id: ctl_srvr.c,v 8.23 2000/02/04 08:28:33 vixie Exp $"; #endif /* not lint */ /* * Copyright (c) 1998,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Extern. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctl_p.h" #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) ((size_t)sprintf x) #endif /* Macros. */ #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL) #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \ tmp, sizeof tmp, ctx->logger) /* Types. */ enum state { available = 0, initializing, writing, reading, reading_data, processing, idling, quitting, closing }; union sa_un { struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN struct sockaddr_un un; +#endif }; struct ctl_sess { LINK(struct ctl_sess) link; struct ctl_sctx * ctx; enum state state; int sock; union sa_un sa; evFileID rdID; evStreamID wrID; evTimerID rdtiID; evTimerID wrtiID; struct ctl_buf inbuf; struct ctl_buf outbuf; const struct ctl_verb * verb; u_int helpcode; void * respctx; u_int respflags; ctl_srvrdone donefunc; void * uap; void * csctx; }; struct ctl_sctx { evContext ev; void * uctx; u_int unkncode; u_int timeoutcode; const struct ctl_verb * verbs; const struct ctl_verb * connverb; int sock; int max_sess; int cur_sess; struct timespec timeout; ctl_logfunc logger; evConnID acID; LIST(struct ctl_sess) sess; }; /* Forward. */ static void ctl_accept(evContext, void *, int, const void *, int, const void *, int); static void ctl_close(struct ctl_sess *); static void ctl_new_state(struct ctl_sess *, enum state, const char *); static void ctl_start_read(struct ctl_sess *); static void ctl_stop_read(struct ctl_sess *); static void ctl_readable(evContext, void *, int, int); static void ctl_rdtimeout(evContext, void *, struct timespec, struct timespec); static void ctl_wrtimeout(evContext, void *, struct timespec, struct timespec); static void ctl_docommand(struct ctl_sess *); static void ctl_writedone(evContext, void *, int, int); static void ctl_morehelp(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, void *, void *); static void ctl_signal_done(struct ctl_sctx *, struct ctl_sess *); /* Private data. */ static const char * state_names[] = { "available", "initializing", "writing", "reading", "reading_data", "processing", "idling", "quitting", "closing" }; static const char space[] = " "; static const struct ctl_verb fakehelpverb = { "fakehelp", ctl_morehelp }; /* Public. */ /* * void * ctl_server() * create, condition, and start a listener on the control port. */ struct ctl_sctx * ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len, const struct ctl_verb *verbs, u_int unkncode, u_int timeoutcode, u_int timeout, int backlog, int max_sess, ctl_logfunc logger, void *uctx) { static const char me[] = "ctl_server"; static const int on = 1; const struct ctl_verb *connverb; struct ctl_sctx *ctx; int save_errno; if (logger == NULL) logger = ctl_logger; for (connverb = verbs; connverb->name != NULL && connverb->func != NULL; connverb++) if (connverb->name[0] == '\0') break; if (connverb->func == NULL) { (*logger)(ctl_error, "%s: no connection verb found", me); return (NULL); } ctx = memget(sizeof *ctx); if (ctx == NULL) { (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); return (NULL); } ctx->ev = lev; ctx->uctx = uctx; ctx->unkncode = unkncode; ctx->timeoutcode = timeoutcode; ctx->verbs = verbs; ctx->timeout = evConsTime(timeout, 0); ctx->logger = logger; ctx->connverb = connverb; ctx->max_sess = max_sess; ctx->cur_sess = 0; INIT_LIST(ctx->sess); ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); if (ctx->sock > evHighestFD(ctx->ev)) { ctx->sock = -1; errno = ENOTSOCK; } if (ctx->sock < 0) { save_errno = errno; (*ctx->logger)(ctl_error, "%s: socket: %s", me, strerror(errno)); memput(ctx, sizeof *ctx); errno = save_errno; return (NULL); } if (ctx->sock > evHighestFD(lev)) { close(ctx->sock); (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD"); errno = ENFILE; memput(ctx, sizeof *ctx); return (NULL); } #ifdef NO_UNIX_REUSEADDR if (sap->sa_family != AF_UNIX) #endif if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on) != 0) { (*ctx->logger)(ctl_warning, "%s: setsockopt(REUSEADDR): %s", me, strerror(errno)); } if (bind(ctx->sock, sap, sap_len) < 0) { + char tmp[MAX_NTOP]; save_errno = errno; - (*ctx->logger)(ctl_error, "%s: bind: %s", me, strerror(errno)); + (*ctx->logger)(ctl_error, "%s: bind: %s: %s", + me, ctl_sa_ntop((struct sockaddr *)sap, + tmp, sizeof tmp, ctx->logger), + strerror(save_errno)); close(ctx->sock); memput(ctx, sizeof *ctx); errno = save_errno; return (NULL); } if (fcntl(ctx->sock, F_SETFD, 1) < 0) { (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, strerror(errno)); } if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx, &ctx->acID) < 0) { save_errno = errno; (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s", me, (void *)ctx->sock, strerror(errno)); close(ctx->sock); memput(ctx, sizeof *ctx); errno = save_errno; return (NULL); } (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d", me, ctx, ctx->sock); return (ctx); } /* * void * ctl_endserver(ctx) * if the control listener is open, close it. clean out all eventlib * stuff. close all active sessions. */ void ctl_endserver(struct ctl_sctx *ctx) { static const char me[] = "ctl_endserver"; struct ctl_sess *this, *next; (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p", me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess); if (ctx->acID.opaque != NULL) { (void)evCancelConn(ctx->ev, ctx->acID); ctx->acID.opaque = NULL; } if (ctx->sock != -1) { (void) close(ctx->sock); ctx->sock = -1; } for (this = HEAD(ctx->sess); this != NULL; this = next) { next = NEXT(this, link); ctl_close(this); } memput(ctx, sizeof *ctx); } /* * If body is non-NULL then it we add a "." line after it. * Caller must have escaped lines with leading ".". */ void ctl_response(struct ctl_sess *sess, u_int code, const char *text, u_int flags, void *respctx, ctl_srvrdone donefunc, void *uap, const char *body, size_t bodylen) { static const char me[] = "ctl_response"; struct iovec iov[3], *iovp = iov; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP], *pc; int n; REQUIRE(sess->state == initializing || sess->state == processing || sess->state == reading_data || sess->state == writing); REQUIRE(sess->wrtiID.opaque == NULL); REQUIRE(sess->wrID.opaque == NULL); ctl_new_state(sess, writing, me); sess->donefunc = donefunc; sess->uap = uap; if (!allocated_p(sess->outbuf) && ctl_bufget(&sess->outbuf, ctx->logger) < 0) { (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer", me, address_expr); goto untimely; } if (sizeof "000-\r\n" + strlen(text) > MAX_LINELEN) { (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing", me, address_expr); goto untimely; } sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n", code, (flags & CTL_MORE) != 0 ? '-' : ' ', text)); for (pc = sess->outbuf.text, n = 0; n < sess->outbuf.used-2; pc++, n++) if (!isascii(*pc) || !isprint(*pc)) *pc = '\040'; *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used); if (body != NULL) { *iovp++ = evConsIovec((char *)body, bodylen); *iovp++ = evConsIovec(".\r\n", 3); } (*ctx->logger)(ctl_debug, "%s: [%d] %s", me, sess->outbuf.used, sess->outbuf.text); if (evWrite(ctx->ev, sess->sock, iov, iovp - iov, ctl_writedone, sess, &sess->wrID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me, address_expr, strerror(errno)); goto untimely; } if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout, &sess->wrtiID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, address_expr, strerror(errno)); goto untimely; } if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me, address_expr, strerror(errno)); untimely: ctl_signal_done(ctx, sess); ctl_close(sess); return; } sess->respctx = respctx; sess->respflags = flags; } void ctl_sendhelp(struct ctl_sess *sess, u_int code) { static const char me[] = "ctl_sendhelp"; struct ctl_sctx *ctx = sess->ctx; sess->helpcode = code; sess->verb = &fakehelpverb; ctl_morehelp(ctx, sess, NULL, me, CTL_MORE, (void *)ctx->verbs, NULL); } void * ctl_getcsctx(struct ctl_sess *sess) { return (sess->csctx); } void * ctl_setcsctx(struct ctl_sess *sess, void *csctx) { void *old = sess->csctx; sess->csctx = csctx; return (old); } /* Private functions. */ static void ctl_accept(evContext lev, void *uap, int fd, const void *lav, int lalen, const void *rav, int ralen) { static const char me[] = "ctl_accept"; struct ctl_sctx *ctx = uap; struct ctl_sess *sess = NULL; char tmp[MAX_NTOP]; if (fd < 0) { (*ctx->logger)(ctl_error, "%s: accept: %s", me, strerror(errno)); return; } if (ctx->cur_sess == ctx->max_sess) { (*ctx->logger)(ctl_error, "%s: %s: too many control sessions", me, ctl_sa_ntop((struct sockaddr *)rav, tmp, sizeof tmp, ctx->logger)); (void) close(fd); return; } sess = memget(sizeof *sess); if (sess == NULL) { (*ctx->logger)(ctl_error, "%s: memget: %s", me, strerror(errno)); (void) close(fd); return; } if (fcntl(fd, F_SETFD, 1) < 0) { (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, strerror(errno)); } ctx->cur_sess++; APPEND(ctx->sess, sess, link); sess->ctx = ctx; sess->sock = fd; sess->wrID.opaque = NULL; sess->rdID.opaque = NULL; sess->wrtiID.opaque = NULL; sess->rdtiID.opaque = NULL; sess->respctx = NULL; sess->csctx = NULL; if (((struct sockaddr *)rav)->sa_family == AF_UNIX) ctl_sa_copy((struct sockaddr *)lav, (struct sockaddr *)&sess->sa); else ctl_sa_copy((struct sockaddr *)rav, (struct sockaddr *)&sess->sa); sess->donefunc = NULL; buffer_init(sess->inbuf); buffer_init(sess->outbuf); sess->state = available; ctl_new_state(sess, initializing, me); sess->verb = ctx->connverb; (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)", me, address_expr, sess->sock); (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0, (struct sockaddr *)rav, ctx->uctx); } static void ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason) { static const char me[] = "ctl_new_state"; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)", me, address_expr, state_names[sess->state], state_names[new_state], reason); sess->state = new_state; } static void ctl_close(struct ctl_sess *sess) { static const char me[] = "ctl_close"; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; REQUIRE(sess->state == initializing || sess->state == writing || sess->state == reading || sess->state == processing || sess->state == reading_data || sess->state == idling); REQUIRE(sess->sock != -1); if (sess->state == reading || sess->state == reading_data) ctl_stop_read(sess); else if (sess->state == writing) { if (sess->wrID.opaque != NULL) { (void) evCancelRW(ctx->ev, sess->wrID); sess->wrID.opaque = NULL; } if (sess->wrtiID.opaque != NULL) { (void) evClearIdleTimer(ctx->ev, sess->wrtiID); sess->wrtiID.opaque = NULL; } } ctl_new_state(sess, closing, me); (void) close(sess->sock); if (allocated_p(sess->inbuf)) ctl_bufput(&sess->inbuf); if (allocated_p(sess->outbuf)) ctl_bufput(&sess->outbuf); (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)", me, address_expr, sess->sock); UNLINK(ctx->sess, sess, link); memput(sess, sizeof *sess); ctx->cur_sess--; } static void ctl_start_read(struct ctl_sess *sess) { static const char me[] = "ctl_start_read"; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; REQUIRE(sess->state == initializing || sess->state == writing || sess->state == processing || sess->state == idling); REQUIRE(sess->rdtiID.opaque == NULL); REQUIRE(sess->rdID.opaque == NULL); sess->inbuf.used = 0; if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout, &sess->rdtiID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, address_expr, strerror(errno)); ctl_close(sess); return; } if (evSelectFD(ctx->ev, sess->sock, EV_READ, ctl_readable, sess, &sess->rdID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me, address_expr, strerror(errno)); return; } ctl_new_state(sess, reading, me); } static void ctl_stop_read(struct ctl_sess *sess) { static const char me[] = "ctl_stop_read"; struct ctl_sctx *ctx = sess->ctx; REQUIRE(sess->state == reading || sess->state == reading_data); REQUIRE(sess->rdID.opaque != NULL); (void) evDeselectFD(ctx->ev, sess->rdID); sess->rdID.opaque = NULL; if (sess->rdtiID.opaque != NULL) { (void) evClearIdleTimer(ctx->ev, sess->rdtiID); sess->rdtiID.opaque = NULL; } ctl_new_state(sess, idling, me); } static void ctl_readable(evContext lev, void *uap, int fd, int evmask) { static const char me[] = "ctl_readable"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char *eos, tmp[MAX_NTOP]; ssize_t n; REQUIRE(sess != NULL); REQUIRE(fd >= 0); REQUIRE(evmask == EV_READ); REQUIRE(sess->state == reading || sess->state == reading_data); evTouchIdleTimer(lev, sess->rdtiID); if (!allocated_p(sess->inbuf) && ctl_bufget(&sess->inbuf, ctx->logger) < 0) { (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer", me, address_expr); ctl_close(sess); return; } - n = read(sess->sock, sess->inbuf.text, MAX_LINELEN - sess->inbuf.used); + n = read(sess->sock, sess->inbuf.text + sess->inbuf.used, + MAX_LINELEN - sess->inbuf.used); if (n <= 0) { (*ctx->logger)(ctl_debug, "%s: %s: read: %s", me, address_expr, (n == 0) ? "Unexpected EOF" : strerror(errno)); ctl_close(sess); return; } sess->inbuf.used += n; eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used); if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') { eos[-1] = '\0'; if ((sess->respflags & CTL_DATA) != 0) { INSIST(sess->verb != NULL); (*sess->verb->func)(sess->ctx, sess, sess->verb, sess->inbuf.text, CTL_DATA, sess->respctx, sess->ctx->uctx); } else { ctl_stop_read(sess); ctl_docommand(sess); } sess->inbuf.used -= ((eos - sess->inbuf.text) + 1); if (sess->inbuf.used == 0) ctl_bufput(&sess->inbuf); else memmove(sess->inbuf.text, eos + 1, sess->inbuf.used); return; } if (sess->inbuf.used == MAX_LINELEN) { (*ctx->logger)(ctl_error, "%s: %s: line too long, closing", me, address_expr); ctl_close(sess); } } static void ctl_wrtimeout(evContext lev, void *uap, struct timespec due, struct timespec itv) { static const char me[] = "ctl_wrtimeout"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; REQUIRE(sess->state == writing); sess->wrtiID.opaque = NULL; (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing", me, address_expr); if (sess->wrID.opaque != NULL) { (void) evCancelRW(ctx->ev, sess->wrID); sess->wrID.opaque = NULL; } ctl_signal_done(ctx, sess); ctl_new_state(sess, processing, me); ctl_close(sess); } static void ctl_rdtimeout(evContext lev, void *uap, struct timespec due, struct timespec itv) { static const char me[] = "ctl_rdtimeout"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; REQUIRE(sess->state == reading); sess->rdtiID.opaque = NULL; (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing", me, address_expr); if (sess->state == reading || sess->state == reading_data) ctl_stop_read(sess); ctl_signal_done(ctx, sess); ctl_new_state(sess, processing, me); ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL, NULL, NULL, NULL, 0); } static void ctl_docommand(struct ctl_sess *sess) { static const char me[] = "ctl_docommand"; char *name, *rest, tmp[MAX_NTOP]; struct ctl_sctx *ctx = sess->ctx; const struct ctl_verb *verb; REQUIRE(allocated_p(sess->inbuf)); (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]", me, address_expr, sess->inbuf.text, (u_int)sess->inbuf.used); ctl_new_state(sess, processing, me); name = sess->inbuf.text + strspn(sess->inbuf.text, space); rest = name + strcspn(name, space); if (*rest != '\0') { *rest++ = '\0'; rest += strspn(rest, space); } for (verb = ctx->verbs; verb != NULL && verb->name != NULL && verb->func != NULL; verb++) if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0) break; if (verb != NULL && verb->name != NULL && verb->func != NULL) { sess->verb = verb; (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx); } else { char buf[1100]; if (sizeof "Unrecognized command \"\" (args \"\")" + strlen(name) + strlen(rest) > sizeof buf) strcpy(buf, "Unrecognized command (buf ovf)"); else sprintf(buf, "Unrecognized command \"%s\" (args \"%s\")", name, rest); ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL, NULL, 0); } } static void ctl_writedone(evContext lev, void *uap, int fd, int bytes) { static const char me[] = "ctl_writedone"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; int save_errno = errno; REQUIRE(sess->state == writing); REQUIRE(fd == sess->sock); REQUIRE(sess->wrtiID.opaque != NULL); sess->wrID.opaque = NULL; (void) evClearIdleTimer(ctx->ev, sess->wrtiID); sess->wrtiID.opaque = NULL; if (bytes < 0) { (*ctx->logger)(ctl_error, "%s: %s: %s", me, address_expr, strerror(save_errno)); ctl_close(sess); return; } INSIST(allocated_p(sess->outbuf)); ctl_bufput(&sess->outbuf); if ((sess->respflags & CTL_EXIT) != 0) { ctl_signal_done(ctx, sess); ctl_close(sess); return; } else if ((sess->respflags & CTL_MORE) != 0) { INSIST(sess->verb != NULL); (*sess->verb->func)(sess->ctx, sess, sess->verb, "", CTL_MORE, sess->respctx, sess->ctx->uctx); } else { ctl_signal_done(ctx, sess); ctl_start_read(sess); } } static void ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *text, u_int respflags, void *respctx, void *uctx) { struct ctl_verb *this = respctx, *next = this + 1; REQUIRE(!lastverb_p(this)); REQUIRE((respflags & CTL_MORE) != 0); if (lastverb_p(next)) respflags &= ~CTL_MORE; ctl_response(sess, sess->helpcode, this->help, respflags, next, NULL, NULL, NULL, 0); } static void ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) { if (sess->donefunc != NULL) { (*sess->donefunc)(ctx, sess, sess->uap); sess->donefunc = NULL; } } Index: head/contrib/bind/lib/isc/ev_connects.c =================================================================== --- head/contrib/bind/lib/isc/ev_connects.c (revision 60940) +++ head/contrib/bind/lib/isc/ev_connects.c (revision 60941) @@ -1,337 +1,349 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* ev_connects.c - implement asynch connect/accept for the eventlib * vix 16sep96 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: ev_connects.c,v 8.25 1999/10/07 20:44:04 vixie Exp $"; +static const char rcsid[] = "$Id: ev_connects.c,v 8.26 2000/02/04 08:28:34 vixie Exp $"; #endif /* Import. */ #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include "eventlib_p.h" #include "port_after.h" /* Macros. */ #define GETXXXNAME(f, s, sa, len) ( \ (f((s), (&sa), (&len)) >= 0) ? 0 : \ (errno != EAFNOSUPPORT && errno != EOPNOTSUPP) ? -1 : ( \ memset(&(sa), 0, sizeof (sa)), \ (len) = sizeof (sa), \ (sa).sa_family = AF_UNIX, \ 0 \ ) \ ) /* Forward. */ static void listener(evContext ctx, void *uap, int fd, int evmask); static void connector(evContext ctx, void *uap, int fd, int evmask); /* Public. */ int evListen(evContext opaqueCtx, int fd, int maxconn, evConnFunc func, void *uap, evConnID *id) { evContext_p *ctx = opaqueCtx.opaque; evConn *new; int mode; OKNEW(new); new->flags = EV_CONN_LISTEN; OK(mode = fcntl(fd, F_GETFL, NULL)); /* side effect: validate fd. */ /* * Remember the nonblocking status. We assume that either evSelectFD * has not been done to this fd, or that if it has then the caller * will evCancelConn before they evDeselectFD. If our assumptions * are not met, then we might restore the old nonblocking status * incorrectly. */ if ((mode & PORT_NONBLOCK) == 0) { OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK)); new->flags |= EV_CONN_BLOCK; } OK(listen(fd, maxconn)); if (evSelectFD(opaqueCtx, fd, EV_READ, listener, new, &new->file) < 0){ int save = errno; FREE(new); errno = save; return (-1); } new->flags |= EV_CONN_SELECTED; new->func = func; new->uap = uap; new->fd = fd; if (ctx->conns != NULL) ctx->conns->prev = new; new->prev = NULL; new->next = ctx->conns; ctx->conns = new; if (id) id->opaque = new; return (0); } int evConnect(evContext opaqueCtx, int fd, void *ra, int ralen, evConnFunc func, void *uap, evConnID *id) { evContext_p *ctx = opaqueCtx.opaque; evConn *new; OKNEW(new); new->flags = 0; /* Do the select() first to get the socket into nonblocking mode. */ if (evSelectFD(opaqueCtx, fd, EV_MASK_ALL, connector, new, &new->file) < 0) { int save = errno; FREE(new); errno = save; return (-1); } new->flags |= EV_CONN_SELECTED; if (connect(fd, ra, ralen) < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINPROGRESS) { int save = errno; (void) evDeselectFD(opaqueCtx, new->file); FREE(new); errno = save; return (-1); } /* No error, or EWOULDBLOCK. select() tells when it's ready. */ new->func = func; new->uap = uap; new->fd = fd; if (ctx->conns != NULL) ctx->conns->prev = new; new->prev = NULL; new->next = ctx->conns; ctx->conns = new; if (id) id->opaque = new; return (0); } int evCancelConn(evContext opaqueCtx, evConnID id) { evContext_p *ctx = opaqueCtx.opaque; evConn *this = id.opaque; evAccept *acc, *nxtacc; int mode; if ((this->flags & EV_CONN_SELECTED) != 0) (void) evDeselectFD(opaqueCtx, this->file); if ((this->flags & EV_CONN_BLOCK) != 0) { mode = fcntl(this->fd, F_GETFL, NULL); if (mode == -1) { if (errno != EBADF) return (-1); } else OK(fcntl(this->fd, F_SETFL, mode | PORT_NONBLOCK)); } /* Unlink from ctx->conns. */ if (this->prev != NULL) this->prev->next = this->next; else ctx->conns = this->next; if (this->next != NULL) this->next->prev = this->prev; /* * Remove `this' from the ctx->accepts list (zero or more times). */ for (acc = HEAD(ctx->accepts), nxtacc = NULL; acc != NULL; acc = nxtacc) { nxtacc = NEXT(acc, link); if (acc->conn == this) { UNLINK(ctx->accepts, acc, link); close(acc->fd); FREE(acc); } } /* Wrap up and get out. */ FREE(this); return (0); } int evHold(evContext opaqueCtx, evConnID id) { evConn *this = id.opaque; if ((this->flags & EV_CONN_LISTEN) == 0) { errno = EINVAL; return (-1); } if ((this->flags & EV_CONN_SELECTED) == 0) return (0); this->flags &= ~EV_CONN_SELECTED; return (evDeselectFD(opaqueCtx, this->file)); } int evUnhold(evContext opaqueCtx, evConnID id) { evConn *this = id.opaque; int ret; if ((this->flags & EV_CONN_LISTEN) == 0) { errno = EINVAL; return (-1); } if ((this->flags & EV_CONN_SELECTED) != 0) return (0); ret = evSelectFD(opaqueCtx, this->fd, EV_READ, listener, this, &this->file); if (ret == 0) this->flags |= EV_CONN_SELECTED; return (ret); } int evTryAccept(evContext opaqueCtx, evConnID id, int *sys_errno) { evContext_p *ctx = opaqueCtx.opaque; evConn *conn = id.opaque; evAccept *new; if ((conn->flags & EV_CONN_LISTEN) == 0) { errno = EINVAL; return (-1); } OKNEW(new); new->conn = conn; new->ralen = sizeof new->ra; - new->fd = accept(conn->fd, &new->ra, &new->ralen); + new->fd = accept(conn->fd, &new->ra.sa, &new->ralen); if (new->fd > ctx->highestFD) { close(new->fd); new->fd = -1; new->ioErrno = ENOTSOCK; } if (new->fd >= 0) { new->lalen = sizeof new->la; - if (GETXXXNAME(getsockname, new->fd, new->la, new->lalen) < 0) { + if (GETXXXNAME(getsockname, new->fd, new->la.sa, new->lalen) < 0) { new->ioErrno = errno; (void) close(new->fd); new->fd = -1; } else new->ioErrno = 0; } else { new->ioErrno = errno; if (errno == EAGAIN || errno == EWOULDBLOCK) { FREE(new); return (-1); } } APPEND(ctx->accepts, new, link); *sys_errno = new->ioErrno; return (0); } /* Private. */ static void listener(evContext opaqueCtx, void *uap, int fd, int evmask) { evContext_p *ctx = opaqueCtx.opaque; evConn *conn = uap; - struct sockaddr la, ra; - int new, lalen, ralen; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } la, ra; + int new, lalen = 0, ralen; REQUIRE((evmask & EV_READ) != 0); ralen = sizeof ra; - new = accept(fd, &ra, &ralen); + new = accept(fd, &ra.sa, &ralen); if (new > ctx->highestFD) { close(new); new = -1; errno = ENOTSOCK; } if (new >= 0) { lalen = sizeof la; - if (GETXXXNAME(getsockname, new, la, lalen) < 0) { + if (GETXXXNAME(getsockname, new, la.sa, lalen) < 0) { int save = errno; (void) close(new); errno = save; new = -1; } } else if (errno == EAGAIN || errno == EWOULDBLOCK) return; - (*conn->func)(opaqueCtx, conn->uap, new, &la, lalen, &ra, ralen); + (*conn->func)(opaqueCtx, conn->uap, new, &la.sa, lalen, &ra.sa, ralen); } static void connector(evContext opaqueCtx, void *uap, int fd, int evmask) { evConn *conn = uap; - struct sockaddr la, ra; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } la, ra; int lalen, ralen; char buf[1]; void *conn_uap; evConnFunc conn_func; evConnID id; int socket_errno = 0; int optlen; lalen = sizeof la; ralen = sizeof ra; conn_uap = conn->uap; conn_func = conn->func; id.opaque = conn; #ifdef SO_ERROR optlen = sizeof socket_errno; if (fd < 0 && getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (char *)&socket_errno, &optlen) < 0) socket_errno = errno; else errno = socket_errno; #endif if (evCancelConn(opaqueCtx, id) < 0 || socket_errno || #ifdef NETREAD_BROKEN 0 || #else read(fd, buf, 0) < 0 || #endif - GETXXXNAME(getsockname, fd, la, lalen) < 0 || - GETXXXNAME(getpeername, fd, ra, ralen) < 0) { + GETXXXNAME(getsockname, fd, la.sa, lalen) < 0 || + GETXXXNAME(getpeername, fd, ra.sa, ralen) < 0) { int save = errno; (void) close(fd); /* XXX closing caller's fd */ errno = save; fd = -1; } - (*conn_func)(opaqueCtx, conn_uap, fd, &la, lalen, &ra, ralen); + (*conn_func)(opaqueCtx, conn_uap, fd, &la.sa, lalen, &ra.sa, ralen); } Index: head/contrib/bind/lib/isc/ev_waits.c =================================================================== --- head/contrib/bind/lib/isc/ev_waits.c (revision 60940) +++ head/contrib/bind/lib/isc/ev_waits.c (revision 60941) @@ -1,244 +1,246 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* ev_waits.c - implement deferred function calls for the eventlib * vix 05dec95 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: ev_waits.c,v 8.9 1999/10/13 17:11:20 vixie Exp $"; +static const char rcsid[] = "$Id: ev_waits.c,v 8.10 2000/02/04 07:25:50 vixie Exp $"; #endif #include "port_before.h" #include "fd_setsize.h" #include #include #include #include "eventlib_p.h" #include "port_after.h" /* Forward. */ static void print_waits(evContext_p *ctx); static evWaitList * evNewWaitList(evContext_p *); static void evFreeWaitList(evContext_p *, evWaitList *); static evWaitList * evGetWaitList(evContext_p *, const void *, int); /* Public. */ /* * Enter a new wait function on the queue. */ int evWaitFor(evContext opaqueCtx, const void *tag, evWaitFunc func, void *uap, evWaitID *id) { evContext_p *ctx = opaqueCtx.opaque; evWait *new; evWaitList *wl = evGetWaitList(ctx, tag, 1); OKNEW(new); new->func = func; new->uap = uap; new->tag = tag; new->next = NULL; if (wl->last != NULL) { wl->last->next = new; } else { wl->first = new; } wl->last = new; if (id != NULL) id->opaque = new; if (ctx->debug >= 9) print_waits(ctx); return (0); } /* * Mark runnable all waiting functions having a certain tag. */ int evDo(evContext opaqueCtx, const void *tag) { evContext_p *ctx = opaqueCtx.opaque; evWaitList *wl = evGetWaitList(ctx, tag, 0); evWait *first; if (!wl) { errno = ENOENT; return (-1); } first = wl->first; INSIST(first != NULL); if (ctx->waitDone.last != NULL) ctx->waitDone.last->next = first; else ctx->waitDone.first = first; ctx->waitDone.last = wl->last; evFreeWaitList(ctx, wl); return (0); } /* * Remove a waiting (or ready to run) function from the queue. */ int evUnwait(evContext opaqueCtx, evWaitID id) { evContext_p *ctx = opaqueCtx.opaque; evWait *this, *prev; evWaitList *wl; int found = 0; this = id.opaque; INSIST(this != NULL); wl = evGetWaitList(ctx, this->tag, 0); if (wl != NULL) { for (prev = NULL, this = wl->first; this != NULL; prev = this, this = this->next) if (this == (evWait *)id.opaque) { found = 1; if (prev != NULL) prev->next = this->next; else wl->first = this->next; if (wl->last == this) wl->last = prev; if (wl->first == NULL) evFreeWaitList(ctx, wl); break; } } if (!found) { /* Maybe it's done */ for (prev = NULL, this = ctx->waitDone.first; this != NULL; prev = this, this = this->next) if (this == (evWait *)id.opaque) { found = 1; if (prev != NULL) prev->next = this->next; else ctx->waitDone.first = this->next; if (ctx->waitDone.last == this) ctx->waitDone.last = prev; break; } } if (!found) { errno = ENOENT; return (-1); } FREE(this); if (ctx->debug >= 9) print_waits(ctx); return (0); } int evDefer(evContext opaqueCtx, evWaitFunc func, void *uap) { evContext_p *ctx = opaqueCtx.opaque; evWait *new; OKNEW(new); new->func = func; new->uap = uap; new->tag = NULL; new->next = NULL; if (ctx->waitDone.last != NULL) ctx->waitDone.last->next = new; else ctx->waitDone.first = new; ctx->waitDone.last = new; if (ctx->debug >= 9) print_waits(ctx); return (0); } /* Private. */ static void print_waits(evContext_p *ctx) { evWaitList *wl; evWait *this; evPrintf(ctx, 9, "wait waiting:\n"); for (wl = ctx->waitLists; wl != NULL; wl = wl->next) { INSIST(wl->first != NULL); evPrintf(ctx, 9, " tag %#x:", wl->first->tag); for (this = wl->first; this != NULL; this = this->next) evPrintf(ctx, 9, " %#x", this); evPrintf(ctx, 9, "\n"); } evPrintf(ctx, 9, "wait done:"); for (this = ctx->waitDone.first; this != NULL; this = this->next) evPrintf(ctx, 9, " %#x", this); evPrintf(ctx, 9, "\n"); } static evWaitList * evNewWaitList(evContext_p *ctx) { evWaitList *new; NEW(new); if (new == NULL) return (NULL); new->first = new->last = NULL; new->prev = NULL; new->next = ctx->waitLists; + if (new->next != NULL) + new->next->prev = new; ctx->waitLists = new; return (new); } static void evFreeWaitList(evContext_p *ctx, evWaitList *this) { INSIST(this != NULL); if (this->prev != NULL) this->prev->next = this->next; else ctx->waitLists = this->next; if (this->next != NULL) this->next->prev = this->prev; FREE(this); } static evWaitList * evGetWaitList(evContext_p *ctx, const void *tag, int should_create) { evWaitList *this; for (this = ctx->waitLists; this != NULL; this = this->next) { if (this->first != NULL && this->first->tag == tag) break; } if (this == NULL && should_create) this = evNewWaitList(ctx); return (this); } Index: head/contrib/bind/lib/isc/eventlib.c =================================================================== --- head/contrib/bind/lib/isc/eventlib.c (revision 60940) +++ head/contrib/bind/lib/isc/eventlib.c (revision 60941) @@ -1,675 +1,676 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* eventlib.c - implement glue for the eventlib * vix 09sep95 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: eventlib.c,v 1.44 1999/10/13 17:11:20 vixie Exp $"; +static const char rcsid[] = "$Id: eventlib.c,v 1.45 2000/02/04 07:25:39 vixie Exp $"; #endif #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include #include #include #include "eventlib_p.h" #include "port_after.h" /* Forward. */ #ifdef NEED_PSELECT static int pselect(int, void *, void *, void *, struct timespec *, const sigset_t *); #endif /* Public. */ int evCreate(evContext *opaqueCtx) { evContext_p *ctx; /* Make sure the memory heap is initialized. */ if (meminit(0, 0) < 0 && errno != EEXIST) return (-1); OKNEW(ctx); /* Global. */ ctx->cur = NULL; /* Debugging. */ ctx->debug = 0; ctx->output = NULL; /* Connections. */ ctx->conns = NULL; INIT_LIST(ctx->accepts); /* Files. */ ctx->files = NULL; FD_ZERO(&ctx->rdNext); FD_ZERO(&ctx->wrNext); FD_ZERO(&ctx->exNext); FD_ZERO(&ctx->nonblockBefore); ctx->fdMax = -1; ctx->fdNext = NULL; ctx->fdCount = 0; /* Invalidate {rd,wr,ex}Last. */ ctx->highestFD = FD_SETSIZE - 1; #ifdef EVENTLIB_TIME_CHECKS ctx->lastFdCount = 0; #endif memset(ctx->fdTable, 0, sizeof ctx->fdTable); /* Streams. */ ctx->streams = NULL; ctx->strDone = NULL; ctx->strLast = NULL; /* Timers. */ ctx->lastEventTime = evNowTime(); #ifdef EVENTLIB_TIME_CHECKS ctx->lastSelectTime = ctx->lastEventTime; #endif ctx->timers = evCreateTimers(ctx); if (ctx->timers == NULL) return (-1); /* Waits. */ ctx->waitLists = NULL; ctx->waitDone.first = ctx->waitDone.last = NULL; ctx->waitDone.prev = ctx->waitDone.next = NULL; opaqueCtx->opaque = ctx; return (0); } void evSetDebug(evContext opaqueCtx, int level, FILE *output) { evContext_p *ctx = opaqueCtx.opaque; ctx->debug = level; ctx->output = output; } int evDestroy(evContext opaqueCtx) { evContext_p *ctx = opaqueCtx.opaque; int revs = 424242; /* Doug Adams. */ evWaitList *this_wl, *next_wl; evWait *this_wait, *next_wait; /* Connections. */ while (revs-- > 0 && ctx->conns != NULL) { evConnID id; id.opaque = ctx->conns; (void) evCancelConn(opaqueCtx, id); } INSIST(revs >= 0); /* Streams. */ while (revs-- > 0 && ctx->streams != NULL) { evStreamID id; id.opaque = ctx->streams; (void) evCancelRW(opaqueCtx, id); } /* Files. */ while (revs-- > 0 && ctx->files != NULL) { evFileID id; id.opaque = ctx->files; (void) evDeselectFD(opaqueCtx, id); } INSIST(revs >= 0); /* Timers. */ evDestroyTimers(ctx); /* Waits. */ for (this_wl = ctx->waitLists; revs-- > 0 && this_wl != NULL; this_wl = next_wl) { next_wl = this_wl->next; for (this_wait = this_wl->first; revs-- > 0 && this_wait != NULL; this_wait = next_wait) { next_wait = this_wait->next; FREE(this_wait); } FREE(this_wl); } for (this_wait = ctx->waitDone.first; revs-- > 0 && this_wait != NULL; this_wait = next_wait) { next_wait = this_wait->next; FREE(this_wait); } FREE(ctx); return (0); } int evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) { evContext_p *ctx = opaqueCtx.opaque; struct timespec nextTime; evTimer *nextTimer; evEvent_p *new; int x, pselect_errno, timerPast; #ifdef EVENTLIB_TIME_CHECKS struct timespec interval; #endif /* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */ x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0); if (x != 1) ERR(EINVAL); /* Get the time of day. We'll do this again after select() blocks. */ ctx->lastEventTime = evNowTime(); again: /* Finished accept()'s do not require a select(). */ if (!EMPTY(ctx->accepts)) { OKNEW(new); new->type = Accept; new->u.accept.this = HEAD(ctx->accepts); UNLINK(ctx->accepts, HEAD(ctx->accepts), link); opaqueEv->opaque = new; return (0); } /* Stream IO does not require a select(). */ if (ctx->strDone != NULL) { OKNEW(new); new->type = Stream; new->u.stream.this = ctx->strDone; ctx->strDone = ctx->strDone->nextDone; if (ctx->strDone == NULL) ctx->strLast = NULL; opaqueEv->opaque = new; return (0); } /* Waits do not require a select(). */ if (ctx->waitDone.first != NULL) { OKNEW(new); new->type = Wait; new->u.wait.this = ctx->waitDone.first; ctx->waitDone.first = ctx->waitDone.first->next; if (ctx->waitDone.first == NULL) ctx->waitDone.last = NULL; opaqueEv->opaque = new; return (0); } /* Get the status and content of the next timer. */ if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) { nextTime = nextTimer->due; timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0); } else timerPast = 0; /* Make gcc happy. */ evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount); if (ctx->fdCount == 0) { static const struct timespec NoTime = {0, 0L}; enum { JustPoll, Block, Timer } m; struct timespec t, *tp; /* Are there any events at all? */ if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1) ERR(ENOENT); /* Figure out what select()'s timeout parameter should be. */ if ((options & EV_POLL) != 0) { m = JustPoll; t = NoTime; tp = &t; } else if (nextTimer == NULL) { m = Block; /* ``t'' unused. */ tp = NULL; } else if (timerPast) { m = JustPoll; t = NoTime; tp = &t; } else { m = Timer; /* ``t'' filled in later. */ tp = &t; } #ifdef EVENTLIB_TIME_CHECKS if (ctx->debug > 0) { interval = evSubTime(ctx->lastEventTime, ctx->lastSelectTime); if (interval.tv_sec > 0) evPrintf(ctx, 1, "time between pselect() %u.%09u count %d\n", interval.tv_sec, interval.tv_nsec, ctx->lastFdCount); } #endif do { /* XXX need to copy only the bits we are using. */ ctx->rdLast = ctx->rdNext; ctx->wrLast = ctx->wrNext; ctx->exLast = ctx->exNext; if (m == Timer) { INSIST(tp == &t); t = evSubTime(nextTime, ctx->lastEventTime); } evPrintf(ctx, 4, "pselect(%d, 0x%lx, 0x%lx, 0x%lx, %d.%09ld)\n", ctx->fdMax+1, (u_long)ctx->rdLast.fds_bits[0], (u_long)ctx->wrLast.fds_bits[0], (u_long)ctx->exLast.fds_bits[0], tp ? tp->tv_sec : -1, tp ? tp->tv_nsec : -1); /* XXX should predict system's earliness and adjust. */ x = pselect(ctx->fdMax+1, &ctx->rdLast, &ctx->wrLast, &ctx->exLast, tp, NULL); pselect_errno = errno; evPrintf(ctx, 4, "select() returns %d (err: %s)\n", x, (x == -1) ? strerror(errno) : "none"); /* Anything but a poll can change the time. */ if (m != JustPoll) ctx->lastEventTime = evNowTime(); /* Select() likes to finish about 10ms early. */ } while (x == 0 && m == Timer && evCmpTime(ctx->lastEventTime, nextTime) < 0); #ifdef EVENTLIB_TIME_CHECKS ctx->lastSelectTime = ctx->lastEventTime; #endif if (x < 0) { if (pselect_errno == EINTR) { if ((options & EV_NULL) != 0) goto again; OKNEW(new); new->type = Null; /* No data. */ opaqueEv->opaque = new; return (0); } if (pselect_errno == EBADF) { for (x = 0; x <= ctx->fdMax; x++) { struct stat sb; if (FD_ISSET(x, &ctx->rdNext) == 0 && FD_ISSET(x, &ctx->wrNext) == 0 && FD_ISSET(x, &ctx->exNext) == 0) continue; if (fstat(x, &sb) == -1 && errno == EBADF) evPrintf(ctx, 1, "EBADF: %d\n", x); } abort(); } ERR(pselect_errno); } - if (x == 0 && (nextTimer && !timerPast) && (options & EV_POLL)) + if (x == 0 && (nextTimer == NULL || !timerPast) && + (options & EV_POLL)) ERR(EWOULDBLOCK); ctx->fdCount = x; #ifdef EVENTLIB_TIME_CHECKS ctx->lastFdCount = x; #endif } INSIST(nextTimer || ctx->fdCount); /* Timers go first since we'd like them to be accurate. */ if (nextTimer && !timerPast) { /* Has anything happened since we blocked? */ timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0); } if (nextTimer && timerPast) { OKNEW(new); new->type = Timer; new->u.timer.this = nextTimer; opaqueEv->opaque = new; return (0); } /* No timers, so there should be a ready file descriptor. */ x = 0; while (ctx->fdCount > 0) { evFile *fid; int fd, eventmask; if (ctx->fdNext == NULL) { if (++x == 2) { /* * Hitting the end twice means that the last * select() found some FD's which have since * been deselected. * * On some systems, the count returned by * selects is the total number of bits in * all masks that are set, and on others it's * the number of fd's that have some bit set, * and on others, it's just broken. We * always assume that it's the number of * bits set in all masks, because that's what * the man page says it should do, and * the worst that can happen is we do an * extra select(). */ ctx->fdCount = 0; break; } ctx->fdNext = ctx->files; } fid = ctx->fdNext; ctx->fdNext = fid->next; fd = fid->fd; eventmask = 0; if (FD_ISSET(fd, &ctx->rdLast)) eventmask |= EV_READ; if (FD_ISSET(fd, &ctx->wrLast)) eventmask |= EV_WRITE; if (FD_ISSET(fd, &ctx->exLast)) eventmask |= EV_EXCEPT; eventmask &= fid->eventmask; if (eventmask != 0) { if ((eventmask & EV_READ) != 0) { FD_CLR(fd, &ctx->rdLast); ctx->fdCount--; } if ((eventmask & EV_WRITE) != 0) { FD_CLR(fd, &ctx->wrLast); ctx->fdCount--; } if ((eventmask & EV_EXCEPT) != 0) { FD_CLR(fd, &ctx->exLast); ctx->fdCount--; } OKNEW(new); new->type = File; new->u.file.this = fid; new->u.file.eventmask = eventmask; opaqueEv->opaque = new; return (0); } } if (ctx->fdCount < 0) { /* * select()'s count is off on a number of systems, and * can result in fdCount < 0. */ evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount); ctx->fdCount = 0; } /* We get here if the caller deselect()'s an FD. Gag me with a goto. */ goto again; } int evDispatch(evContext opaqueCtx, evEvent opaqueEv) { evContext_p *ctx = opaqueCtx.opaque; evEvent_p *ev = opaqueEv.opaque; #ifdef EVENTLIB_TIME_CHECKS void *func; struct timespec start_time; struct timespec interval; #endif #ifdef EVENTLIB_TIME_CHECKS if (ctx->debug > 0) start_time = evNowTime(); #endif ctx->cur = ev; switch (ev->type) { case Accept: { evAccept *this = ev->u.accept.this; evPrintf(ctx, 5, "Dispatch.Accept: fd %d -> %d, func %#x, uap %#x\n", this->conn->fd, this->fd, this->conn->func, this->conn->uap); errno = this->ioErrno; (this->conn->func)(opaqueCtx, this->conn->uap, this->fd, &this->la, this->lalen, &this->ra, this->ralen); #ifdef EVENTLIB_TIME_CHECKS func = this->conn->func; #endif break; } case File: { evFile *this = ev->u.file.this; int eventmask = ev->u.file.eventmask; evPrintf(ctx, 5, "Dispatch.File: fd %d, mask 0x%x, func %#x, uap %#x\n", this->fd, this->eventmask, this->func, this->uap); (this->func)(opaqueCtx, this->uap, this->fd, eventmask); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Stream: { evStream *this = ev->u.stream.this; evPrintf(ctx, 5, "Dispatch.Stream: fd %d, func %#x, uap %#x\n", this->fd, this->func, this->uap); errno = this->ioErrno; (this->func)(opaqueCtx, this->uap, this->fd, this->ioDone); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Timer: { evTimer *this = ev->u.timer.this; evPrintf(ctx, 5, "Dispatch.Timer: func %#x, uap %#x\n", this->func, this->uap); (this->func)(opaqueCtx, this->uap, this->due, this->inter); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Wait: { evWait *this = ev->u.wait.this; evPrintf(ctx, 5, "Dispatch.Wait: tag %#x, func %#x, uap %#x\n", this->tag, this->func, this->uap); (this->func)(opaqueCtx, this->uap, this->tag); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Null: { /* No work. */ #ifdef EVENTLIB_TIME_CHECKS func = NULL; #endif break; } default: { abort(); } } #ifdef EVENTLIB_TIME_CHECKS if (ctx->debug > 0) { interval = evSubTime(evNowTime(), start_time); /* * Complain if it took longer than 50 milliseconds. * * We call getuid() to make an easy to find mark in a kernel * trace. */ if (interval.tv_sec > 0 || interval.tv_nsec > 50000000) evPrintf(ctx, 1, "dispatch interval %u.%09u uid %d type %d func %p\n", interval.tv_sec, interval.tv_nsec, getuid(), ev->type, func); } #endif ctx->cur = NULL; evDrop(opaqueCtx, opaqueEv); return (0); } void evDrop(evContext opaqueCtx, evEvent opaqueEv) { evContext_p *ctx = opaqueCtx.opaque; evEvent_p *ev = opaqueEv.opaque; switch (ev->type) { case Accept: { FREE(ev->u.accept.this); break; } case File: { /* No work. */ break; } case Stream: { evStreamID id; id.opaque = ev->u.stream.this; (void) evCancelRW(opaqueCtx, id); break; } case Timer: { evTimer *this = ev->u.timer.this; evTimerID opaque; /* Check to see whether the user func cleared the timer. */ if (heap_element(ctx->timers, this->index) != this) { evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n"); break; } /* * Timer is still there. Delete it if it has expired, * otherwise set it according to its next interval. */ if (this->inter.tv_sec == 0 && this->inter.tv_nsec == 0L) { opaque.opaque = this; (void) evClearTimer(opaqueCtx, opaque); } else { opaque.opaque = this; (void) evResetTimer(opaqueCtx, opaque, this->func, this->uap, evAddTime(ctx->lastEventTime, this->inter), this->inter); } break; } case Wait: { FREE(ev->u.wait.this); break; } case Null: { /* No work. */ break; } default: { abort(); } } FREE(ev); } int evMainLoop(evContext opaqueCtx) { evEvent event; int x; while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0) if ((x = evDispatch(opaqueCtx, event)) < 0) break; return (x); } int evHighestFD(evContext opaqueCtx) { evContext_p *ctx = opaqueCtx.opaque; return (ctx->highestFD); } void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (ctx->output != NULL && ctx->debug >= level) { vfprintf(ctx->output, fmt, ap); fflush(ctx->output); } va_end(ap); } #ifdef NEED_PSELECT /* XXX needs to move to the porting library. */ static int pselect(int nfds, void *rfds, void *wfds, void *efds, struct timespec *tsp, const sigset_t *sigmask) { struct timeval tv, *tvp; sigset_t sigs; int n; if (tsp) { tvp = &tv; tv = evTimeVal(*tsp); } else tvp = NULL; if (sigmask) sigprocmask(SIG_SETMASK, sigmask, &sigs); n = select(nfds, rfds, wfds, efds, tvp); if (sigmask) sigprocmask(SIG_SETMASK, &sigs, NULL); if (tsp) *tsp = evTimeSpec(tv); return (n); } #endif Index: head/contrib/bind/lib/isc/eventlib_p.h =================================================================== --- head/contrib/bind/lib/isc/eventlib_p.h (revision 60940) +++ head/contrib/bind/lib/isc/eventlib_p.h (revision 60941) @@ -1,199 +1,213 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* eventlib_p.h - private interfaces for eventlib * vix 09sep95 [initial] * - * $Id: eventlib_p.h,v 1.27 1999/06/03 20:36:05 vixie Exp $ + * $Id: eventlib_p.h,v 1.28 2000/02/04 08:28:34 vixie Exp $ */ #ifndef _EVENTLIB_P_H #define _EVENTLIB_P_H #include #include #include +#include +#include #define EVENTLIB_DEBUG 1 #include #include #include #include #include #include #include #include #define EV_MASK_ALL (EV_READ | EV_WRITE | EV_EXCEPT) #define ERR(e) return (errno = (e), -1) #define OK(x) if ((x) < 0) ERR(errno); else (void)NULL #define NEW(p) if (((p) = memget(sizeof *(p))) != NULL) \ FILL(p); \ else \ (void)NULL; #define OKNEW(p) if (!((p) = memget(sizeof *(p)))) { \ errno = ENOMEM; \ return (-1); \ } else \ FILL(p) #define FREE(p) memput((p), sizeof *(p)) #if EVENTLIB_DEBUG #define FILL(p) memset((p), 0xF5, sizeof *(p)) #else #define FILL(p) #endif typedef struct evConn { evConnFunc func; void * uap; int fd; int flags; #define EV_CONN_LISTEN 0x0001 /* Connection is a listener. */ #define EV_CONN_SELECTED 0x0002 /* evSelectFD(conn->file). */ #define EV_CONN_BLOCK 0x0004 /* Listener fd was blocking. */ evFileID file; struct evConn * prev; struct evConn * next; } evConn; typedef struct evAccept { int fd; - struct sockaddr la; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } la; int lalen; - struct sockaddr ra; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } ra; int ralen; int ioErrno; evConn * conn; LINK(struct evAccept) link; } evAccept; typedef struct evFile { evFileFunc func; void * uap; int fd; int eventmask; int preemptive; struct evFile * prev; struct evFile * next; struct evFile * fdprev; struct evFile * fdnext; } evFile; typedef struct evStream { evStreamFunc func; void * uap; evFileID file; evTimerID timer; int flags; #define EV_STR_TIMEROK 0x0001 /* IFF timer valid. */ int fd; struct iovec * iovOrig; int iovOrigCount; struct iovec * iovCur; int iovCurCount; int ioTotal; int ioDone; int ioErrno; struct evStream *prevDone, *nextDone; struct evStream *prev, *next; } evStream; typedef struct evTimer { evTimerFunc func; void * uap; struct timespec due, inter; int index; } evTimer; typedef struct evWait { evWaitFunc func; void * uap; const void * tag; struct evWait * next; } evWait; typedef struct evWaitList { evWait * first; evWait * last; struct evWaitList * prev; struct evWaitList * next; } evWaitList; typedef struct evEvent_p { enum { Accept, File, Stream, Timer, Wait, Free, Null } type; union { struct { evAccept *this; } accept; struct { evFile *this; int eventmask; } file; struct { evStream *this; } stream; struct { evTimer *this; } timer; struct { evWait *this; } wait; struct { struct evEvent_p *next; } free; struct { const void *placeholder; } null; } u; } evEvent_p; typedef struct { /* Global. */ const evEvent_p *cur; /* Debugging. */ int debug; FILE *output; /* Connections. */ evConn *conns; LIST(evAccept) accepts; /* Files. */ evFile *files, *fdNext; fd_set rdLast, rdNext; fd_set wrLast, wrNext; fd_set exLast, exNext; fd_set nonblockBefore; int fdMax, fdCount, highestFD; evFile *fdTable[FD_SETSIZE]; #ifdef EVENTLIB_TIME_CHECKS struct timespec lastSelectTime; int lastFdCount; #endif /* Streams. */ evStream *streams; evStream *strDone, *strLast; /* Timers. */ struct timespec lastEventTime; heap_context timers; /* Waits. */ evWaitList *waitLists; evWaitList waitDone; } evContext_p; /* eventlib.c */ #define evPrintf __evPrintf void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...); /* ev_timers.c */ #define evCreateTimers __evCreateTimers heap_context evCreateTimers(const evContext_p *); #define evDestroyTimers __evDestroyTimers void evDestroyTimers(const evContext_p *); /* ev_waits.c */ #define evFreeWait __evFreeWait evWait *evFreeWait(evContext_p *ctx, evWait *old); #endif /*_EVENTLIB_P_H*/ Index: head/contrib/bind/lib/isc/logging.c =================================================================== --- head/contrib/bind/lib/isc/logging.c (revision 60940) +++ head/contrib/bind/lib/isc/logging.c (revision 60941) @@ -1,686 +1,700 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: logging.c,v 8.24 1999/10/13 16:39:34 vixie Exp $"; +static const char rcsid[] = "$Id: logging.c,v 8.26 2000/04/23 02:19:02 vixie Exp $"; #endif /* not lint */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef VSPRINTF_CHAR # define VSPRINTF(x) strlen(vsprintf/**/x) #else # define VSPRINTF(x) ((size_t)vsprintf x) #endif #include "logging_p.h" static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT }; static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char *level_text[] = { "info: ", "notice: ", "warning: ", "error: ", "critical: " }; static void version_rename(log_channel chan) { unsigned int ver; char old_name[PATH_MAX+1]; char new_name[PATH_MAX+1]; ver = chan->out.file.versions; if (ver < 1) return; if (ver > LOG_MAX_VERSIONS) ver = LOG_MAX_VERSIONS; /* * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100) */ if (strlen(chan->out.file.name) > (PATH_MAX-3)) return; for (ver--; ver > 0; ver--) { sprintf(old_name, "%s.%d", chan->out.file.name, ver-1); sprintf(new_name, "%s.%d", chan->out.file.name, ver); (void)rename(old_name, new_name); } sprintf(new_name, "%s.0", chan->out.file.name); (void)rename(chan->out.file.name, new_name); } FILE * log_open_stream(log_channel chan) { FILE *stream; int fd, flags; struct stat sb; int regular; if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (NULL); } /* * Don't open already open streams */ if (chan->out.file.stream != NULL) return (chan->out.file.stream); if (stat(chan->out.file.name, &sb) < 0) { if (errno != ENOENT) { syslog(LOG_ERR, "log_open_stream: stat of %s failed: %s", chan->out.file.name, strerror(errno)); chan->flags |= LOG_CHANNEL_BROKEN; return (NULL); } regular = 1; } else regular = (sb.st_mode & S_IFREG); if (chan->out.file.versions) { if (!regular) { syslog(LOG_ERR, "log_open_stream: want versions but %s isn't a regular file", chan->out.file.name); chan->flags |= LOG_CHANNEL_BROKEN; errno = EINVAL; return (NULL); } } flags = O_WRONLY|O_CREAT|O_APPEND; - if (chan->flags & LOG_TRUNCATE) { + if ((chan->flags & LOG_TRUNCATE) != 0) { if (regular) { (void)unlink(chan->out.file.name); flags |= O_EXCL; } else { syslog(LOG_ERR, "log_open_stream: want truncation but %s isn't a regular file", chan->out.file.name); chan->flags |= LOG_CHANNEL_BROKEN; errno = EINVAL; return (NULL); } } fd = open(chan->out.file.name, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd < 0) { syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s", chan->out.file.name, strerror(errno)); chan->flags |= LOG_CHANNEL_BROKEN; return (NULL); } stream = fdopen(fd, "a"); if (stream == NULL) { syslog(LOG_ERR, "log_open_stream: fdopen() failed"); chan->flags |= LOG_CHANNEL_BROKEN; return (NULL); } + (void) fchown(fd, chan->out.file.owner, chan->out.file.group); chan->out.file.stream = stream; return (stream); } int log_close_stream(log_channel chan) { FILE *stream; if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (0); } stream = chan->out.file.stream; chan->out.file.stream = NULL; if (stream != NULL && fclose(stream) == EOF) return (-1); return (0); } FILE * log_get_stream(log_channel chan) { if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (NULL); } return (chan->out.file.stream); } char * log_get_filename(log_channel chan) { if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (NULL); } return (chan->out.file.name); } int log_check_channel(log_context lc, int level, log_channel chan) { int debugging, chan_level; REQUIRE(lc != NULL); debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); /* * If not debugging, short circuit debugging messages very early. */ if (level > 0 && !debugging) return (0); if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0) return (0); /* Some channels only log when debugging is on. */ if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging) return (0); /* Some channels use the global level. */ if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) { chan_level = lc->level; } else chan_level = chan->level; if (level > chan_level) return (0); return (1); } int log_check(log_context lc, int category, int level) { log_channel_list lcl; int debugging; REQUIRE(lc != NULL); debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); /* * If not debugging, short circuit debugging messages very early. */ if (level > 0 && !debugging) return (0); if (category < 0 || category > lc->num_categories) category = 0; /* use default */ lcl = lc->categories[category]; if (lcl == NULL) { category = 0; lcl = lc->categories[0]; } for ( /* nothing */; lcl != NULL; lcl = lcl->next) { if (log_check_channel(lc, level, lcl->channel)) return (1); } return (0); } void log_vwrite(log_context lc, int category, int level, const char *format, va_list args) { log_channel_list lcl; int pri, debugging, did_vsprintf = 0; int original_category; FILE *stream; log_channel chan; struct timeval tv; struct tm *local_tm; char *category_name; char *level_str; char time_buf[256]; char level_buf[256]; REQUIRE(lc != NULL); debugging = (lc->flags & LOG_OPTION_DEBUG); /* * If not debugging, short circuit debugging messages very early. */ if (level > 0 && !debugging) return; if (category < 0 || category > lc->num_categories) category = 0; /* use default */ original_category = category; lcl = lc->categories[category]; if (lcl == NULL) { category = 0; lcl = lc->categories[0]; } /* * Get the current time and format it. */ time_buf[0]='\0'; if (gettimeofday(&tv, NULL) < 0) { syslog(LOG_INFO, "gettimeofday failed in log_vwrite()"); } else { #ifdef HAVE_TIME_R localtime_r((time_t *)&tv.tv_sec, &local_tm); #else local_tm = localtime((time_t *)&tv.tv_sec); #endif if (local_tm != NULL) { sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ", local_tm->tm_mday, months[local_tm->tm_mon], local_tm->tm_year+1900, local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec, (long)tv.tv_usec/1000); } } /* * Make a string representation of the current category and level */ if (lc->category_names != NULL && lc->category_names[original_category] != NULL) category_name = lc->category_names[original_category]; else category_name = ""; if (level >= log_critical) { if (level >= 0) { sprintf(level_buf, "debug %d: ", level); level_str = level_buf; } else level_str = level_text[-level-1]; } else { sprintf(level_buf, "level %d: ", level); level_str = level_buf; } /* * Write the message to channels. */ for ( /* nothing */; lcl != NULL; lcl = lcl->next) { chan = lcl->channel; if (!log_check_channel(lc, level, chan)) continue; if (!did_vsprintf) { if (VSPRINTF((lc->buffer, format, args)) > LOG_BUFFER_SIZE) { syslog(LOG_CRIT, "memory overrun in log_vwrite()"); exit(1); } did_vsprintf = 1; } switch (chan->type) { case log_syslog: if (level >= log_critical) pri = (level >= 0) ? 0 : -level; else pri = -log_critical; syslog(chan->out.facility|syslog_priority[pri], "%s%s%s%s", (chan->flags & LOG_TIMESTAMP) ? time_buf : "", (chan->flags & LOG_PRINT_CATEGORY) ? category_name : "", (chan->flags & LOG_PRINT_LEVEL) ? level_str : "", lc->buffer); break; case log_file: stream = chan->out.file.stream; if (stream == NULL) { stream = log_open_stream(chan); if (stream == NULL) break; } if (chan->out.file.max_size != ULONG_MAX) { long pos; pos = ftell(stream); if (pos >= 0 && (unsigned long)pos > chan->out.file.max_size) { /* * try to roll over the log files, * ignoring all all return codes * except the open (we don't want * to write any more anyway) */ log_close_stream(chan); version_rename(chan); stream = log_open_stream(chan); if (stream == NULL) break; } } fprintf(stream, "%s%s%s%s\n", (chan->flags & LOG_TIMESTAMP) ? time_buf : "", (chan->flags & LOG_PRINT_CATEGORY) ? category_name : "", (chan->flags & LOG_PRINT_LEVEL) ? level_str : "", lc->buffer); fflush(stream); break; case log_null: break; default: syslog(LOG_ERR, "unknown channel type in log_vwrite()"); } } } void log_write(log_context lc, int category, int level, const char *format, ...) { va_list args; va_start(args, format); log_vwrite(lc, category, level, format, args); va_end(args); } /* * Functions to create, set, or destroy contexts */ int log_new_context(int num_categories, char **category_names, log_context *lc) { log_context nlc; nlc = memget(sizeof (struct log_context)); if (nlc == NULL) { errno = ENOMEM; return (-1); } nlc->num_categories = num_categories; nlc->category_names = category_names; nlc->categories = memget(num_categories * sizeof (log_channel_list)); if (nlc->categories == NULL) { memput(nlc, sizeof (struct log_context)); errno = ENOMEM; return (-1); } memset(nlc->categories, '\0', num_categories * sizeof (log_channel_list)); nlc->flags = 0U; nlc->level = 0; *lc = nlc; return (0); } void log_free_context(log_context lc) { log_channel_list lcl, lcl_next; log_channel chan; int i; REQUIRE(lc != NULL); for (i = 0; i < lc->num_categories; i++) for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) { lcl_next = lcl->next; chan = lcl->channel; (void)log_free_channel(chan); memput(lcl, sizeof (struct log_channel_list)); } memput(lc->categories, lc->num_categories * sizeof (log_channel_list)); memput(lc, sizeof (struct log_context)); } int log_add_channel(log_context lc, int category, log_channel chan) { log_channel_list lcl; if (lc == NULL || category < 0 || category >= lc->num_categories) { errno = EINVAL; return (-1); } lcl = memget(sizeof (struct log_channel_list)); if (lcl == NULL) { errno = ENOMEM; return(-1); } lcl->channel = chan; lcl->next = lc->categories[category]; lc->categories[category] = lcl; chan->references++; return (0); } int log_remove_channel(log_context lc, int category, log_channel chan) { log_channel_list lcl, prev_lcl, next_lcl; int found = 0; if (lc == NULL || category < 0 || category >= lc->num_categories) { errno = EINVAL; return (-1); } for (prev_lcl = NULL, lcl = lc->categories[category]; lcl != NULL; lcl = next_lcl) { next_lcl = lcl->next; if (lcl->channel == chan) { log_free_channel(chan); if (prev_lcl != NULL) prev_lcl->next = next_lcl; else lc->categories[category] = next_lcl; memput(lcl, sizeof (struct log_channel_list)); /* * We just set found instead of returning because * the channel might be on the list more than once. */ found = 1; } else prev_lcl = lcl; } if (!found) { errno = ENOENT; return (-1); } return (0); } int log_option(log_context lc, int option, int value) { if (lc == NULL) { errno = EINVAL; return (-1); } switch (option) { case LOG_OPTION_DEBUG: if (value) lc->flags |= option; else lc->flags &= ~option; break; case LOG_OPTION_LEVEL: lc->level = value; break; default: errno = EINVAL; return (-1); } return (0); } int log_category_is_active(log_context lc, int category) { if (lc == NULL) { errno = EINVAL; return (-1); } if (category >= 0 && category < lc->num_categories && lc->categories[category] != NULL) return (1); return (0); } log_channel log_new_syslog_channel(unsigned int flags, int level, int facility) { log_channel chan; chan = memget(sizeof (struct log_channel)); if (chan == NULL) { errno = ENOMEM; return (NULL); } chan->type = log_syslog; chan->flags = flags; chan->level = level; chan->out.facility = facility; chan->references = 0; return (chan); } log_channel log_new_file_channel(unsigned int flags, int level, char *name, FILE *stream, unsigned int versions, unsigned long max_size) { log_channel chan; chan = memget(sizeof (struct log_channel)); if (chan == NULL) { errno = ENOMEM; return (NULL); } chan->type = log_file; chan->flags = flags; chan->level = level; if (name != NULL) { size_t len; len = strlen(name); /* * Quantize length to a multiple of 256. There's space for the * NUL, since if len is a multiple of 256, the size chosen will * be the next multiple. */ chan->out.file.name_size = ((len / 256) + 1) * 256; chan->out.file.name = memget(chan->out.file.name_size); if (chan->out.file.name == NULL) { memput(chan, sizeof (struct log_channel)); errno = ENOMEM; return (NULL); } /* This is safe. */ strcpy(chan->out.file.name, name); } else { chan->out.file.name_size = 0; chan->out.file.name = NULL; } chan->out.file.stream = stream; chan->out.file.versions = versions; chan->out.file.max_size = max_size; + chan->out.file.owner = getuid(); + chan->out.file.group = getgid(); chan->references = 0; return (chan); +} + +int +log_set_file_owner(log_channel chan, uid_t owner, gid_t group) { + if (chan->type != log_file) { + errno = EBADF; + return (-1); + } + chan->out.file.owner = owner; + chan->out.file.group = group; + return (0); } log_channel log_new_null_channel() { log_channel chan; chan = memget(sizeof (struct log_channel)); if (chan == NULL) { errno = ENOMEM; return (NULL); } chan->type = log_null; chan->flags = LOG_CHANNEL_OFF; chan->level = log_info; chan->references = 0; return (chan); } int log_inc_references(log_channel chan) { if (chan == NULL) { errno = EINVAL; return (-1); } chan->references++; return (0); } int log_dec_references(log_channel chan) { if (chan == NULL || chan->references <= 0) { errno = EINVAL; return (-1); } chan->references--; return (0); } log_channel_type log_get_channel_type(log_channel chan) { REQUIRE(chan != NULL); return (chan->type); } int log_free_channel(log_channel chan) { if (chan == NULL || chan->references <= 0) { errno = EINVAL; return (-1); } chan->references--; if (chan->references == 0) { if (chan->type == log_file) { if ((chan->flags & LOG_CLOSE_STREAM) && chan->out.file.stream != NULL) (void)fclose(chan->out.file.stream); if (chan->out.file.name != NULL) memput(chan->out.file.name, chan->out.file.name_size); } memput(chan, sizeof (struct log_channel)); } return (0); } Index: head/contrib/bind/lib/isc/logging.mdoc =================================================================== --- head/contrib/bind/lib/isc/logging.mdoc (revision 60940) +++ head/contrib/bind/lib/isc/logging.mdoc (revision 60941) @@ -1,1044 +1,1052 @@ -.\" $Id: logging.mdoc,v 8.3 1999/01/08 19:25:41 vixie Exp $ +.\" $Id: logging.mdoc,v 8.4 2000/04/23 02:19:02 vixie Exp $ .\" .\"Copyright (c) 1995-1999 by Internet Software Consortium .\" .\"Permission to use, copy, modify, and distribute this software for any .\"purpose with or without fee is hereby granted, provided that the above .\"copyright notice and this permission notice appear in all copies. .\" .\"THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS .\"ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES .\"OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE .\"CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL .\"DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR .\"PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS .\"ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\"SOFTWARE. .\" .\" The following six UNCOMMENTED lines are required. .Dd January 1, 1996 .\"Os OPERATING_SYSTEM [version/release] .Os BSD 4 .\"Dt DOCUMENT_TITLE [section number] [volume] .Dt LOGGING @SYSCALL_EXT@ .Sh NAME .Nm log_open_stream , .Nm log_close_stream , .Nm log_get_stream , .Nm log_get_filename , .Nm log_vwrite , .Nm log_write , .Nm log_new_context , .Nm log_free_context , .Nm log_add_channel , .Nm log_remove_channel , .Nm log_option , .Nm log_category_is_active , .Nm log_new_syslog_channel , .Nm log_new_file_channel , +.Nm log_set_file_owner , .Nm log_new_null_channel , .Nm log_inc_references , .Nm log_dec_references , .Nm log_free_channel .Nd logging system .Sh SYNOPSIS .Fd #include .Ft FILE * .Fn log_open_stream "log_channel chan" .Ft int .Fn log_close_stream "log_channel chan" .Ft FILE * .Fn log_get_stream "log_channel chan" .Ft char * .Fn log_get_filename "log_channel chan" .Ft void .Fn log_vwrite "log_context lc" "int category" "int level" \ "const char *format" va_list args" .Ft void .Fn log_write "log_context lc" "int category" "int level" \ "const char *format" "..." .Ft int .Fn log_check_channel "log_context lc" "int level" "log_channel chan" .Ft int .Fn log_check "log_context lc" "int category" "int level" .Ft int .Fn log_new_context "int num_categories" "char **category_names" \ "log_context *lc" .Ft void .Fn log_free_context "log_context lc" .Ft int .Fn log_add_channel "log_context lc" "int category" "log_channel chan" .Ft int .Fn log_remove_channel "log_context lc" "int category" "log_channel chan" .Ft int .Fn log_option "log_context lc" "int option" "int value" .Ft int .Fn log_category_is_active "log_context lc" "int category" .Ft log_channel .Fn log_new_syslog_channel "unsigned int flags" "int level" "int facility" .Ft log_channel .Fn log_new_file_channel "unsigned int flags" "int level" \ "char *name" "FILE *stream" "unsigned int versions" \ "unsigned long max_size" +.Ft int +.Fn log_set_file_owner "log_channel chan" "uid_t owner" "gid_t group" .Ft log_channel .Fn log_new_null_channel "void" .Ft int .Fn log_inc_references "log_channel chan" .Ft int .Fn log_dec_references "log_channel chan" .Ft int .Fn log_free_channel "log_channel chan" .Sh DESCRIPTION The .Sy ISC .Nm logging library is flexible logging system which is based upon a set of concepts: .Nm logging channels , .Nm categories , and .Nm logging contexts . .Pp The basic building block is the .Dq Nm logging channel , which includes a .Nm priority (logging level), which type of logging is to occur, and other flags and information associated with technical aspects of the logging. The set of priorities which are supported is shown below, in the section .Sx Message Priorities . A priority sets a threshold for message logging; a logging channel will .Em only log those messages which are .Em at least as important as its priority indicates. (The fact that .Dq more important means .Dq more negative , under the current scheme, is an implementation detail; if a channel has a priority of .Dv log_error , then it will .Em not log messages with the .Dv log_warning priority, but it .Em will log messages with the .Dv log_error or .Dv log_critical priority.) .Pp The .Nm logging channel also has an indication of the type of logging performed. Currently, the supported .Nm logging types include (see also .Sx Logging Types , below): .Bl -tag -width "log_syslog" -compact -offset indent .It Dv log_syslog for .Xr syslog 3 Ns -style logging .It Dv log_file for use of a file .It Dv log_null for .Em no logging .El A new logging channel is created by calling either .Fn log_new_syslog_channel , .Fn log_new_file_channel , or .Fn log_new_null_channel , respectively. When a channel is no longer to be used, it can be freed using .Fn log_free_channel . .Pp Both .Dv log_syslog and .Dv log_file channel types can include more information; for instance, a .Dv log_syslog Ns -type channel allows the specification of a .Xr syslog 3 Ns -style .Dq facility , and a .Dv log_file Ns -type channels allows the caller to set a maximum file size and number of versions. (See .Fn log_new_syslog_channel or .Fn log_new_file_channel , below.) Additionally, once a logging channel of type .Dv log_file is defined, the functions .Fn log_open_stream and .Fn log_close_stream can open or close the stream associated with the logging channel's logging filename. The .Fn log_get_stream and .Fn log_get_filename functions return the stream or filename, respectively, of such a logging -channel. +channel. Also unique to logging channels of type +.Dv log_file +is the +.Fn log_set_file_owner +function, which tells the logging system what user and group ought to own +newly created files (which is only effective if the caller is privileged.) .Pp Callers provide .Dq Nm categories , determining both the number of such categories and any (optional) names. Categories are like array indexes in C; if the caller declares .Dq Va n categories, then they are considered to run from 0 to .Va n-1 ; with this scheme, a category number would be invalid if it were negative or greater than/equal to .Va n . Each category can have its own list of .Nm logging channels associated with it; we say that such a channel is .Dq in the particular category. .Sy NOTE: Individual logging channels can appear in more than one category. .Pp A .Dq Nm logging context is the set of all .Nm logging channels associated with the context's .Nm categories; thus, a particular .Nm category scheme is associated with a particular .Nm logging context. .Sy NOTE: A logging channel may appear in more than one logging context, and in multiple categories within each logging context. .Pp Use .Fn log_add_channel and .Fn log_remove_channel to add or remove a logging channel to some category in a logging context. To see if a given category in a logging context is being used, use the Boolean test .Fn log_category_is_active . .Pp A .Nm logging context can also have a .Nm priority (logging level) and various flags associated with the whole context; in order to alter the flags or change the priority of a context, use .Fn log_option . .Ss Message Priorities Currently, five .Nm priorities (logging levels) are supported (they can also be found in the header file): .Bd -literal -offset indent #define log_critical (-5) #define log_error (-4) #define log_warning (-3) #define log_notice (-2) #define log_info (-1) .Ed .Pp In the current implementation, logging messages which have a level greater than 0 are considered to be debugging messages. .Ss Logging Types The three different .Nm logging types currently supported are different values of the enumerated type .Ft log_output_type (these are also listed in the header file): .Bd -literal -offset indent typedef enum { log_syslog, log_file, log_null } log_output_type; .Ed .Ss Logging Channel Flags There are several flags which can be set on a logging channel; the flags and their meanings are as follows (they are also found in the header file): .Bl -tag -width "LOG_USE_CONTEXT_LEVEL " -offset indent .It Dv LOG_CHANNEL_BROKEN This is set only when some portion of .Fn log_open_stream fails: .Xr open 2 or .Xr fdopen 3 fail; .Xr stat 2 fails in a .Dq bad way; versioning or truncation is requested on a non-normal file. .It Dv LOG_CHANNEL_OFF This is set for channels opened by .Fn log_new_null_channel . .It Dv LOG_CLOSE_STREAM If this flag is set, then .Fn log_free_channel will free a .No non- Dv NULL stream of a logging channel which is being .Xr free 3 Ns -d (if the logging channel is of type .Dv log_file , of course). .It Dv LOG_PRINT_CATEGORY If set, .Fn log_vwrite will insert the category name, if available, into logging messages which are logged to channels of type .Dv log_syslog or .Dv log_file . .It Dv LOG_PRINT_LEVEL If set, .Fn log_vwrite will insert a string identifying the message priority level into the information logged to channels of type .Dv log_syslog or .Dv log_file . .It Dv LOG_REQUIRE_DEBUG Only log debugging messages (i.e., those with a priority greater than zero). .It Dv LOG_TIMESTAMP If set, .Fn log_vwrite will insert a timestamp into logging messages which are logged to channels of type .Dv log_syslog or .Dv log_file . .It Dv LOG_TRUNCATE Truncate logging file when re-opened ( .Fn log_open_stream will .Xr unlink 2 the file and then .Xr open 2 a new file of the same name with the .Dv O_EXCL bit set). .It Dv LOG_USE_CONTEXT_LEVEL Use the logging context's priority or logging level, rather than the logging channel's own priority. This can be useful for those channels which are included in multiple logging contexts. .El .Ss FUNCTION DESCRIPTIONS The function .Fn log_open_stream is for use with channels which log to a file; i.e., logging channels with a .Va type field set to .Dq Dv log_file . If the logging channel pointed to by .Dq Fa chan is valid, it attempts to open (and return) the stream associated with that channel. If the stream is already opened, then it is returned; otherwise, .Xr stat 2 is used to test the filename for the stream. .Pp At this point, if the logging file is supposed to have different .Va versions (i.e., incremented version numbers; higher numbers indicate older versions of the logging file). If so, then any existing versions are .Xr rename 2 Ns -d to have one version-number higher than previously, and the .Dq current filename for the stream is set to the .Dq \&.0 form of the name. Next, if the logging file is supposed to be truncated (i.e., the .Dv LOG_TRUNCATE bit of the .Va flags field of the logging channel structure is set), then any file with the .Dq current filename for the stream is .X4 unlink 2 Ns -d . .Sy NOTE: If the logging file is .Em not a regular file, and either of the above operations (version numbering or truncation) is supposed to take place, a .Dv NULL file pointer is returned. .Pp Finally, the filename associated with the logging channel is .Xr open 2 Ns -d using the appropriate flags and a mode which sets the read/write permissions for the user, group, and others. The file descriptor returned by .Xr open 2 is then passed to .Xr fopen 3 , with the append mode set, and the stream returned by this call is stored in the .Fa chan structure and returned. .Pp If .Fn log_open_stream fails at any point, then the .Dv LOG_CHANNEL_BROKEN bit of the .Va flags field of the logging channel pointed to by .Fa chan is set, a .Dv NULL is returned, and .Va errno contains pertinent information. .Pp The .Fn log_close_stream function closes the stream associated with the logging channel pointed to by .Dq Fa chan (if .Fa chan is valid and the stream exists and can be closed properly by .Xr fclose 3 ) . The stream is set to .Dv NULL even if the call to .Xr fclose 3 fails. .Pp The function .Fn log_get_stream returns the stream associated with the logging channel pointed to by .Dq Fa chan , if it is .No non- Ns Dv NULL and specifies a logging channel which has a .Dv FILE * or stream associated with it. .Pp The .Fn log_get_filename function returns the name of the file associated with the logging channel pointed to by .Dq Fa chan , if it is .No non- Ns Dv NULL and specifies a logging channel which has a file associated with it. .Pp The .Fn log_vwrite function performs the actual logging of a message to the various logging channels of a logging context .Fa lc . The message consists of an .Xr fprint 3 Ns -style .Fa format and its associated .Fa args (if any); it will be written to all logging channels in the given .Fa category which have a priority set to .Fa level or any .Em less important priority value. If the .Fa category is not valid or has no logging channels, then the category defaults to 0. .Pp There are a number of conditions under which a call to .Fn log_vwrite will not result in actually logging the message: if there is no logging channel at even the default category (0), or if a given channel is either .Dq broken or .Dq off (i.e., its flags have .Dv LOG_CHANNEL_BROKEN or .Dv LOG_CHANNEL_OFF set, respectively), or if the logging channel channel is of type .Dv log_null . Additionally, if the logging channel's flag has .Dv LOG_REQUIRE_DEBUG set and the message is not a debugging message (i.e., has a level greater than 0), then it will not be logged. Finally, if the message's priority is less important than the channel's logging level (the priority threshold), will not be logged. .Sy NOTE: If a logging channel's flag has .Dv LOG_USE_CONTEXT_LEVEL set, it will use the logging context's priority, rather than its own. .Pp If all of these hurdles are passed, then only .Dv log_syslog and .Dv log_file channels actually can have logging. For channels which use .Xr syslog 3 , the channel's .Xr syslog 3 facility is used in conjunction with a potentially modified form of the message's priority level, since .Xr syslog 3 has its own system of priorities .Pq Pa /usr/include/syslog.h . All debug messages (priority >= 0) are mapped to .Xr syslog 3 Ns 's .Dv LOG_DEBUG priority, all messages .Dq more important than .Dv log_critical are mapped to .Dv LOG_CRIT , and the priorities corresponding to the ones listed in the section .Sx Message Priorities are given the obvious corresponding .Xr syslog 3 priority. .Pp For .Dv log_file type logging channels, if the file size is greater than the maximum file size, then no logging occurs. (The same thing happens if a .Dv NULL stream is encountered and .Fn log_open_stream fails to open the channel's stream.) .Pp For both logging to normal files and logging via .Xr syslog 3 , the value of the flags .Dv LOG_TIMESTAMP , .Dv LOG_PRINT_CATEGORY , and .Dv LOG_PRINT_LEVEL are used in determining whether or not these items are included in the logged information. .Pp The .Fn log_write function is merely a front-end to a call to .Fn log_vwrite ; see the description of that function, above, for more information. .Pp .Fn log_check and .Fn log_check_channel are used to see if a contemplated logging call will actually generate any output, which is useful when creating a log message involves non-trivial work. .Fn log_check will return non-zero if a call to .Fn log_vwrite with the given .Fa category and .Fa level would generate output on any channels, and zero otherwise. .Fn log_check_channel will return non-zero if writing to the .Fa chan at the given .Fa level would generate output. .Pp The function .Fn log_new_context creates a new .Nm logging context , and stores this in the .Dq Va opaque field of the argument .Dq Fa lc , and opaque structure used internally. This new .Nm context will include the .Dq Fa num_categories and .Dq Fa category_names which are supplied; the latter can be .Dv NULL . .Sy NOTE: Since .Dq Fa category_names is used directly, it .Em must not be freed by the caller, if it is .No non- Ns Dv NULL . The initial logging flags and priority are both set to zero. .Pp The .Fn log_free_context function is used to free the opaque structure .Dq Va lc.opaque and its components. .Sy NOTE: The .Dq Va opaque field of .Dq Fa lc .Em must be .No non- Ns Dv NULL . For each of the various .Dq categories (indicated by the .Dq Va num_categories which were in the corresponding call to .Fn log_new_context ) associated with the given .Nm logging context , .Em all of the .Nm logging channels are .Xr free 3 Ns -d . The opaque structure itself is then .Xr free 3 Ns -d , and .Dq Va lc.opaque is set to .Dv NULL . .Pp .Sy NOTE: The function .Fn log_free_context does .Em not free the memory associated with .Fa category_names , since the logging library did not allocate the memory for it, originally; it was supplied in the call to .Fn log_new_context . .Pp The function .Fn log_add_channel adds the .Nm logging channel .Dq Fa chan to the list of logging channels in the given .Fa category of the .Nm logging context .Dq Fa lc . No checking is performed to see whether or not .Fa chan is already present in the given .Fa category , so multiple instances in a single .Fa category can occur (but see .Fn log_remove_channel , below). .Pp The .Fn log_remove_channel function removes .Em all occurrences of the .Nm logging channel .Dq Fa chan from the list of logging channels in the given .Fa category of the .Nm logging context .Dq Fa lc . It also attempts to free the channel by calling .Fn log_free_channel (see its description, below). .Pp The .Fn log_option function is used to change the .Fa option of the indicated logging context .Fa lc to the given .Fa value . The .Fa option can be either .Dv LOG_OPTION_LEVEL or .Dv LOG_OPTION_DEBUG ; in the first case, the log context's debugging level is reset to the indicated level. If the .Fa option is .Dv LOG_OPTION_DEBUG , then a non-zero .Fa value results in setting the debug flag of the logging context, while a zero .Fa value means that the debug flag is reset. .Pp The .Fn log_category_is_active test returns a 1 if the given .Fa category of the indicated logging context .Fa lc has at least one logging channel, and 0, otherwise. .Pp The functions .Fn log_new_syslog_channel , .Fn log_new_file_channel , and .Fn log_new_null_channel create a new channel of the type specified (thus, the difference in arguments); the .Dq Va type field of the new .Dq Ft struct log_channel is always set to the appropriate value. .Pp The .Fn log_new_syslog_channel function .Xr malloc 3 Ns -s a new .Ft struct log_channel of .Va type .Dv log_syslog , i.e., a logging channel which will use .Xr syslog 3 . The new structure is filled out with the .Dq Fa flags , .Dq Fa level , and .Dq Fa facility which are given; the .Va references field is initialized to zero. See .Sx Logging Channel Flags and .Sx Message Priorities , above, or the header file for information about acceptable values for .Dq Fa flags , and .Dq Fa level . The .Dq Fa facility . can be any valid .Xr syslog 3 facility; see the appropriate system header file or manpage for more information. .Pp .Ft log_channel .Fn log_new_file_channel "unsigned int flags" "int level" \ "char *name" "FILE *stream" "unsigned int versions" \ "unsigned long max_size" .Pp .Fn log_new_null_channel .Pp The functions .Fn log_inc_references and .Fn log_dec_references increment or decrements, respectively, the .Va references field of the logging channel pointed to by .Dq Fa chan , if it is a valid channel (and if the .Va references field is strictly positive, in the case of .Fn log_dec_references ) . These functions are meant to track changes in the number of different clients which refer to the given logging channel. .Pp The .Fn log_free_channel function frees the field of the logging channel pointed to by .Dq Fa chan if there are no more outstanding references to it. If the channel uses a file, the stream is .Xr fclose 3 Ns -d (if the .Dv LOG_CLOSE_STREAM flag is set), and the filename, if .No non- Ns Dv NULL , is .Xr free 3 Ns -d before .Dq Fa chan is .Xr free 3 Ns -d . .Pp .\" The following requests should be uncommented and .\" used where appropriate. This next request is .\" for sections 2 and 3 function return values only. .Sh RETURN VALUES .\" This next request is for sections 1, 6, 7 & 8 only .Bl -tag -width "log_category_is_active()" .It Fn log_open_stream .Dv NULL is returned under any of several error conditions: a) if .Dq Fa chan is either .Dv NULL or a .No non- Ns Dv log_file channel .Pq Va errno No is set to Dv EINVAL ; b) if either versioning or truncation is requested for a non-normal file .Pq Va errno No is set to Dv EINVAL ; c) if any of .Xr stat 2 , .Xr open 2 , or .Xr fdopen 3 fails .Po Va errno is set by the call which failed .Pc . If some value other than .Dv NULL is returned, then it is a valid logging stream (either newly-opened or already-open). .It Fn log_close_stream -1 if the stream associated with .Dq Fa chan is .No non- Ns Dv NULL and the call to .Xr fclose 3 fails. 0 if successful or the logging channel pointed to by .Dq Fa chan is invalid (i.e., .Dv NULL or not a logging channel which has uses a file); in the latter case, .Va errno is set to .Dv EINVAL . .It Fn log_get_stream .Dv NULL under the same conditions as those under which .Fn log_close_stream , above, returns 0 (including the setting of .Va errno ) . Otherwise, the stream associated with the logging channel is returned. .It Fn log_get_filename .Dv NULL under the same conditions as those under which .Fn log_close_stream , above, returns 0 (including the setting of .Va errno ) . Otherwise, the name of the file associated with the logging channel is returned. .It Fn log_new_context -1 if .Xr malloc 3 fails .Pq with Va errno No set to Dv ENOMEM . Otherwise, 0, with .Dq Va lc->opaque containing the new structures and information. .It Fn log_add_channel -1 if a) either .Dq Va lc.opaque is .Dv NULL or .Fa category is invalid (negative or greater than or equal to .Va lcp->num_categories ), with .Va errno set to .Dv EINVAL ; b) .Xr malloc 3 fails .Pq with Va errno No set to Dv ENOMEM . Otherwise, 0. .It Fn log_remove_channel -1 if a) either .Dq Va lc.opaque is .Dv NULL or .Fa category is invalid, as under failure condition a) for .Fn log_add_channel , above, including the setting of .Va errno ; b) no channel numbered .Fa chan is found in the logging context indicated by .Fa lc .Pq with Va errno No set to Dv ENOENT . Otherwise, 0. .It Fn log_option -1 if a) .Dq Va lc.opaque is .Dv NULL , b) .Fa option specifies an unknown logging option ; in either case, .Va errno is set to .Dv EINVAL . Otherwise, 0. .It Fn log_category_is_active -1 if .Dq Va lc.opaque is .Dv NULL .Pq with Va errno No set to Dv EINVAL ; 1 if the .Fa category number is valid and there are logging channels in this .Fa category within the indicated logging context; 0 if the .Fa category number is invalid or there are no logging channels in this .Fa category within the indicated logging context. .It Fn log_new_syslog_channel .Dv NULL if .Xr malloc 3 fails .Pq with Va errno No set to ENOMEM ; otherwise, a valid .Dv log_syslog Ns -type .Ft log_channel . .It Fn log_new_file_channel .Dv NULL if .Xr malloc 3 fails .Pq with Va errno No set to ENOMEM ; otherwise, a valid .Dv log_file Ns -type .Ft log_channel . .It Fn log_new_null_channel .Dv NULL if .Xr malloc 3 fails .Pq with Va errno No set to ENOMEM ; otherwise, a valid .Dv log_null Ns -type .Ft log_channel . .It Fn log_inc_references -1 if .Dq Fa chan is .Dv NULL .Pq with Va errno set to Dv EINVAL . Otherwise, 0. .It Fn log_dec_references -1 if .Dq Fa chan is .Dv NULL or its .Va references field is already <= 0 .Pq with Va errno set to Dv EINVAL . Otherwise, 0. .It Fn log_free_channel -1 under the same conditions as .Fn log_dec_references , above, including the setting of .Va errno ; 0 otherwise. .El .\" .Sh ENVIRONMENT .Sh FILES .Bl -tag -width "isc/logging.h" .It Pa isc/logging.h include file for logging library .It Pa syslog.h .Xr syslog 3 Ns -style priorities .El .\" .Sh EXAMPLES .\" This next request is for sections 1, 6, 7 & 8 only .\" (command return values (to shell) and .\" fprintf/stderr type diagnostics) .\" .Sh DIAGNOSTICS .\" The next request is for sections 2 and 3 error .\" and signal handling only. .Sh ERRORS This table shows which functions can return the indicated error in the .Va errno variable; see the .Sx RETURN VALUES section, above, for more information. .Bl -tag -width "(any0other0value)0" .It Dv EINVAL .Fn log_open_stream , .Fn log_close_stream , .Fn log_get_stream , .Fn log_get_filename , .Fn log_add_channel , .Fn log_remove_channel , .Fn log_option , .Fn log_category_is_active , .Fn log_inc_references , .Fn log_dec_references , .Fn log_free_channel . .It Dv ENOENT .Fn log_remove_channel . .It Dv ENOMEM .Fn log_new_context , .Fn log_add_channel , .Fn log_new_syslog_channel , .Fn log_new_file_channel , .Fn log_new_null_channel . .It (any other value) returned via a pass-through of an error code from .Xr stat 2 , .Xr open 2 , or .Xr fdopen 3 , which can occur in .Fn log_open_stream and functions which call it .Pq currently, only Fn log_vwrite . .El .Pp Additionally, .Fn log_vwrite and .Fn log_free_context will fail via .Fn assert if .Dq Va lc.opaque is .Dv NULL . The function .Fn log_vwrite can also exit with a critical error logged via .Xr syslog 3 indicating a memory overrun .Sh SEE ALSO .Xr @INDOT@named @SYS_OPS_EXT@ , .Xr syslog 3 . The HTML documentation includes a file, .Pa logging.html , which has more information about this logging system. .\" .Sh STANDARDS .\" .Sh HISTORY .Sh AUTHORS Bob Halley...TODO .\" .Sh BUGS Index: head/contrib/bind/lib/isc/logging_p.h =================================================================== --- head/contrib/bind/lib/isc/logging_p.h (revision 60940) +++ head/contrib/bind/lib/isc/logging_p.h (revision 60941) @@ -1,58 +1,60 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef LOGGING_P_H #define LOGGING_P_H typedef struct log_file_desc { char *name; size_t name_size; FILE *stream; unsigned int versions; unsigned long max_size; + uid_t owner; + gid_t group; } log_file_desc; typedef union log_output { int facility; log_file_desc file; } log_output; struct log_channel { int level; /* don't log messages > level */ log_channel_type type; log_output out; unsigned int flags; int references; }; typedef struct log_channel_list { log_channel channel; struct log_channel_list *next; } *log_channel_list; #define LOG_BUFFER_SIZE 20480 struct log_context { int num_categories; char **category_names; log_channel_list *categories; int flags; int level; char buffer[LOG_BUFFER_SIZE]; }; #endif /* !LOGGING_P_H */ Index: head/contrib/bind/lib/nameser/Makefile =================================================================== --- head/contrib/bind/lib/nameser/Makefile (revision 60940) +++ head/contrib/bind/lib/nameser/Makefile (revision 60941) @@ -1,90 +1,91 @@ # # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. # -# $Id: Makefile,v 8.16 1999/09/07 08:47:28 vixie Exp $ +# $Id: Makefile,v 8.19 2000/02/29 03:38:23 vixie Exp $ # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos DESTDIR = DESTLIB = /usr/local/lib O=o A=a CC= cc LD= ld SHELL= /bin/sh CDEBUG= -g TOP= ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.${A} LIBBINDR = ../${TOP}/lib/libbind_r.${A} CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} LD_LIBFLAGS= -x -r AR= ar cru RANLIB= ranlib INSTALL= install INSTALL_EXEC= INSTALL_LIB=-o bin -g bin THREADED= threaded SRCS= ns_parse.c ns_print.c ns_netint.c ns_ttl.c ns_name.c \ ns_sign.c ns_verify.c ns_date.c ns_samedomain.c OBJS= ns_parse.${O} ns_print.${O} ns_netint.${O} ns_ttl.${O} ns_name.${O} \ ns_sign.${O} ns_verify.${O} ns_date.${O} ns_samedomain.${O} all: ${LIBBIND} ${LIBBIND}: ${OBJS} ( cd ${THREADED} ; \ ${AR} ${LIBBINDR} ${ARPREF} ${OBJS} ${ARSUFF} ; \ ${RANLIB} ${LIBBINDR} ) ${AR} ${LIBBIND} ${ARPREF} ${OBJS} ${ARSUFF} ${RANLIB} ${LIBBIND} .c.${O}: - if test ! -d ${THREADED} ; then mkdir ${THREADED} ; fi + if test ! -d ${THREADED} ; then mkdir ${THREADED} ; else true ; fi ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} ${REENTRANT} -c $*.c \ -o ${THREADED}/$*.${O} - -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} -o a.out && \ - ${LDS} mv a.out ${THREADED}/$*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} \ + -o ${THREADED}/$*.out && \ + ${LDS} mv ${THREADED}/$*.out ${THREADED}/$*.${O} ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} -c $*.c - -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o a.out && \ - ${LDS} mv a.out $*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o $*.out && \ + ${LDS} mv $*.out $*.${O} distclean: clean clean: FRC rm -f .depend a.out core ${LIB} tags rm -f *.${O} *.BAK *.CKP *~ rm -f ${THREADED}/*.${O} - -rmdir ${THREADED} + -if test -d ${THREADED} ; then rmdir ${THREADED}; else true; fi depend: FRC mkdep -I${INCL} -I${PORTINCL} ${CPPFLAGS} ${SRCS} links: FRC @set -e; ln -s SRC/*.[ch] . install: FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/lib/nameser/ns_name.c =================================================================== --- head/contrib/bind/lib/nameser/ns_name.c (revision 60940) +++ head/contrib/bind/lib/nameser/ns_name.c (revision 60941) @@ -1,640 +1,670 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef lint -static const char rcsid[] = "$Id: ns_name.c,v 8.12 1999/10/13 17:11:23 vixie Exp $"; +static const char rcsid[] = "$Id: ns_name.c,v 8.15 2000/03/30 22:53:46 vixie Exp $"; #endif #include "port_before.h" #include #include #include #include #include #include #include #include "port_after.h" /* Data. */ static const char digits[] = "0123456789"; /* Forward. */ static int special(int); static int printable(int); static int dn_find(const u_char *, const u_char *, const u_char * const *, const u_char * const *); /* Public. */ /* * ns_name_ntop(src, dst, dstsiz) * Convert an encoded domain name to printable ascii as per RFC1035. * return: * Number of bytes written to buffer, or -1 (with errno set) * notes: * The root is returned as "." * All other domains are returned in non absolute form */ int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) { const u_char *cp; char *dn, *eom; u_char c; u_int n; cp = src; dn = dst; eom = dst + dstsiz; while ((n = *cp++) != 0) { if ((n & NS_CMPRSFLGS) != 0) { /* Some kind of compression pointer. */ errno = EMSGSIZE; return (-1); } if (dn != dst) { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '.'; } if (dn + n >= eom) { errno = EMSGSIZE; return (-1); } for ((void)NULL; n > 0; n--) { c = *cp++; if (special(c)) { if (dn + 1 >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\\'; *dn++ = (char)c; } else if (!printable(c)) { if (dn + 3 >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\\'; *dn++ = digits[c / 100]; *dn++ = digits[(c % 100) / 10]; *dn++ = digits[c % 10]; } else { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = (char)c; } } } if (dn == dst) { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '.'; } if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\0'; return (dn - dst); } /* * ns_name_pton(src, dst, dstsiz) * Convert a ascii string into an encoded domain name as per RFC1035. * return: * -1 if it fails * 1 if string was fully qualified * 0 is string was not fully qualified * notes: * Enforces label and domain length limits. */ int ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { u_char *label, *bp, *eom; int c, n, escaped; char *cp; escaped = 0; bp = dst; eom = dst + dstsiz; label = bp++; while ((c = *src++) != 0) { if (escaped) { if ((cp = strchr(digits, c)) != NULL) { n = (cp - digits) * 100; if ((c = *src++) == 0 || (cp = strchr(digits, c)) == NULL) { errno = EMSGSIZE; return (-1); } n += (cp - digits) * 10; if ((c = *src++) == 0 || (cp = strchr(digits, c)) == NULL) { errno = EMSGSIZE; return (-1); } n += (cp - digits); if (n > 255) { errno = EMSGSIZE; return (-1); } c = n; } escaped = 0; } else if (c == '\\') { escaped = 1; continue; } else if (c == '.') { c = (bp - label - 1); if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ errno = EMSGSIZE; return (-1); } if (label >= eom) { errno = EMSGSIZE; return (-1); } *label = c; /* Fully qualified ? */ if (*src == '\0') { if (c != 0) { if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = '\0'; } if ((bp - dst) > MAXCDNAME) { errno = EMSGSIZE; return (-1); } return (1); } if (c == 0 || *src == '.') { errno = EMSGSIZE; return (-1); } label = bp++; continue; } if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = (u_char)c; } c = (bp - label - 1); if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ errno = EMSGSIZE; return (-1); } if (label >= eom) { errno = EMSGSIZE; return (-1); } *label = c; if (c != 0) { if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = 0; } if ((bp - dst) > MAXCDNAME) { /* src too big */ errno = EMSGSIZE; return (-1); } return (0); } /* * ns_name_ntol(src, dst, dstsiz) * Convert a network strings labels into all lowercase. * return: * Number of bytes written to buffer, or -1 (with errno set) * notes: * Enforces label and domain length limits. */ int ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) { const u_char *cp; u_char *dn, *eom; u_char c; u_int n; cp = src; dn = dst; eom = dst + dstsiz; while ((n = *cp++) != 0) { if ((n & NS_CMPRSFLGS) != 0) { /* Some kind of compression pointer. */ errno = EMSGSIZE; return (-1); } *dn++ = n; if (dn + n >= eom) { errno = EMSGSIZE; return (-1); } for ((void)NULL; n > 0; n--) { c = *cp++; if (isupper(c)) *dn++ = tolower(c); else *dn++ = c; } } *dn++ = '\0'; return (dn - dst); } /* * ns_name_unpack(msg, eom, src, dst, dstsiz) * Unpack a domain name from a message, source may be compressed. * return: * -1 if it fails, or consumed octets if it succeeds. */ int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, u_char *dst, size_t dstsiz) { const u_char *srcp, *dstlim; u_char *dstp; int n, len, checked; len = -1; checked = 0; dstp = dst; srcp = src; dstlim = dst + dstsiz; if (srcp < msg || srcp >= eom) { errno = EMSGSIZE; return (-1); } /* Fetch next label in domain name. */ while ((n = *srcp++) != 0) { /* Check for indirection. */ switch (n & NS_CMPRSFLGS) { case 0: /* Limit checks. */ if (dstp + n + 1 >= dstlim || srcp + n >= eom) { errno = EMSGSIZE; return (-1); } checked += n + 1; *dstp++ = n; memcpy(dstp, srcp, n); dstp += n; srcp += n; break; case NS_CMPRSFLGS: if (srcp >= eom) { errno = EMSGSIZE; return (-1); } if (len < 0) len = srcp - src + 1; srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); if (srcp < msg || srcp >= eom) { /* Out of range. */ errno = EMSGSIZE; return (-1); } checked += 2; /* * Check for loops in the compressed name; * if we've looked at the whole message, * there must be a loop. */ if (checked >= eom - msg) { errno = EMSGSIZE; return (-1); } break; default: errno = EMSGSIZE; return (-1); /* flag error */ } } *dstp = '\0'; if (len < 0) len = srcp - src; return (len); } /* * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) * Pack domain name 'domain' into 'comp_dn'. * return: * Size of the compressed name, or -1. * notes: * 'dnptrs' is an array of pointers to previous compressed names. * dnptrs[0] is a pointer to the beginning of the message. The array * ends with NULL. * 'lastdnptr' is a pointer to the end of the array pointed to * by 'dnptrs'. * Side effects: * The list of pointers in dnptrs is updated for labels inserted into * the message as we compress the name. If 'dnptr' is NULL, we don't * try to compress names. If 'lastdnptr' is NULL, we don't update the * list. */ int ns_name_pack(const u_char *src, u_char *dst, int dstsiz, const u_char **dnptrs, const u_char **lastdnptr) { u_char *dstp; const u_char **cpp, **lpp, *eob, *msg; const u_char *srcp; - int n, l; + int n, l, first = 1; srcp = src; dstp = dst; eob = dstp + dstsiz; lpp = cpp = NULL; if (dnptrs != NULL) { if ((msg = *dnptrs++) != NULL) { for (cpp = dnptrs; *cpp != NULL; cpp++) (void)NULL; lpp = cpp; /* end of list to search */ } } else msg = NULL; /* make sure the domain we are about to add is legal */ l = 0; do { n = *srcp; if ((n & NS_CMPRSFLGS) != 0) { errno = EMSGSIZE; return (-1); } l += n + 1; if (l > MAXCDNAME) { errno = EMSGSIZE; return (-1); } srcp += n + 1; } while (n != 0); /* from here on we need to reset compression pointer array on error */ srcp = src; do { /* Look to see if we can use pointers. */ n = *srcp; if (n != 0 && msg != NULL) { l = dn_find(srcp, msg, (const u_char * const *)dnptrs, (const u_char * const *)lpp); if (l >= 0) { if (dstp + 1 >= eob) { goto cleanup; } *dstp++ = (l >> 8) | NS_CMPRSFLGS; *dstp++ = l % 256; return (dstp - dst); } /* Not found, save it. */ if (lastdnptr != NULL && cpp < lastdnptr - 1 && - (dstp - msg) < 0x4000) { + (dstp - msg) < 0x4000 && first) { *cpp++ = dstp; *cpp = NULL; + first = 0; } } /* copy label to buffer */ if (n & NS_CMPRSFLGS) { /* Should not happen. */ goto cleanup; } if (dstp + 1 + n >= eob) { goto cleanup; } memcpy(dstp, srcp, n + 1); srcp += n + 1; dstp += n + 1; } while (n != 0); if (dstp > eob) { cleanup: if (msg != NULL) *lpp = NULL; errno = EMSGSIZE; return (-1); } return (dstp - dst); } /* * ns_name_uncompress(msg, eom, src, dst, dstsiz) * Expand compressed domain name to presentation format. * return: * Number of bytes read out of `src', or -1 (with errno set). * note: * Root domain returns as "." not "". */ int ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, char *dst, size_t dstsiz) { u_char tmp[NS_MAXCDNAME]; int n; if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) return (-1); if (ns_name_ntop(tmp, dst, dstsiz) == -1) return (-1); return (n); } /* * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) * Compress a domain name into wire format, using compression pointers. * return: * Number of bytes consumed in `dst' or -1 (with errno set). * notes: * 'dnptrs' is an array of pointers to previous compressed names. * dnptrs[0] is a pointer to the beginning of the message. * The list ends with NULL. 'lastdnptr' is a pointer to the end of the * array pointed to by 'dnptrs'. Side effect is to update the list of * pointers for labels inserted into the message as we compress the name. * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' * is NULL, we don't update the list. */ int ns_name_compress(const char *src, u_char *dst, size_t dstsiz, const u_char **dnptrs, const u_char **lastdnptr) { u_char tmp[NS_MAXCDNAME]; if (ns_name_pton(src, tmp, sizeof tmp) == -1) return (-1); return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); } /* + * Reset dnptrs so that there are no active references to pointers at or + * after src. + */ +void +ns_name_rollback(const u_char *src, const u_char **dnptrs, + const u_char **lastdnptr) +{ + while (dnptrs < lastdnptr && *dnptrs != NULL) { + if (*dnptrs >= src) { + *dnptrs = NULL; + break; + } + dnptrs++; + } +} + +/* * ns_name_skip(ptrptr, eom) * Advance *ptrptr to skip over the compressed name it points at. * return: * 0 on success, -1 (with errno set) on failure. */ int ns_name_skip(const u_char **ptrptr, const u_char *eom) { const u_char *cp; u_int n; cp = *ptrptr; while (cp < eom && (n = *cp++) != 0) { /* Check for indirection. */ switch (n & NS_CMPRSFLGS) { case 0: /* normal case, n == len */ cp += n; continue; case NS_CMPRSFLGS: /* indirection */ cp++; break; default: /* illegal type */ errno = EMSGSIZE; return (-1); } break; } if (cp > eom) { errno = EMSGSIZE; return (-1); } *ptrptr = cp; return (0); } /* Private. */ /* * special(ch) * Thinking in noninternationalized USASCII (per the DNS spec), * is this characted special ("in need of quoting") ? * return: * boolean. */ static int special(int ch) { switch (ch) { case 0x22: /* '"' */ case 0x2E: /* '.' */ case 0x3B: /* ';' */ case 0x5C: /* '\\' */ /* Special modifiers in zone files. */ case 0x40: /* '@' */ case 0x24: /* '$' */ return (1); default: return (0); } } /* * printable(ch) * Thinking in noninternationalized USASCII (per the DNS spec), * is this character visible and not a space when printed ? * return: * boolean. */ static int printable(int ch) { return (ch > 0x20 && ch < 0x7f); } /* * Thinking in noninternationalized USASCII (per the DNS spec), * convert this character to lower case if it's upper case. */ static int mklower(int ch) { if (ch >= 0x41 && ch <= 0x5A) return (ch + 0x20); return (ch); } /* * dn_find(domain, msg, dnptrs, lastdnptr) * Search for the counted-label name in an array of compressed names. * return: * offset from msg if found, or -1. * notes: * dnptrs is the pointer to the first name on the list, * not the pointer to the start of the message. */ static int dn_find(const u_char *domain, const u_char *msg, const u_char * const *dnptrs, const u_char * const *lastdnptr) { const u_char *dn, *cp, *sp; const u_char * const *cpp; u_int n; for (cpp = dnptrs; cpp < lastdnptr; cpp++) { - dn = domain; - sp = cp = *cpp; - while ((n = *cp++) != 0) { - /* - * check for indirection - */ - switch (n & NS_CMPRSFLGS) { - case 0: /* normal case, n == len */ - if (n != *dn++) - goto next; - for ((void)NULL; n > 0; n--) - if (mklower(*dn++) != mklower(*cp++)) + sp = *cpp; + /* + * terminate search on: + * root label + * compression pointer + * unusable offset + */ + while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && + (sp - msg) < 0x4000) { + dn = domain; + cp = sp; + while ((n = *cp++) != 0) { + /* + * check for indirection + */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + if (n != *dn++) goto next; - /* Is next root for both ? */ - if (*dn == '\0' && *cp == '\0') - return (sp - msg); - if (*dn) - continue; - goto next; + for ((void)NULL; n > 0; n--) + if (mklower(*dn++) != + mklower(*cp++)) + goto next; + /* Is next root for both ? */ + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; - case NS_CMPRSFLGS: /* indirection */ - cp = msg + (((n & 0x3f) << 8) | *cp); - break; + case NS_CMPRSFLGS: /* indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + break; - default: /* illegal type */ - errno = EMSGSIZE; - return (-1); + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } } + next: + sp += *sp + 1; } - next: ; } errno = ENOENT; return (-1); } Index: head/contrib/bind/lib/nameser/ns_print.c =================================================================== --- head/contrib/bind/lib/nameser/ns_print.c (revision 60940) +++ head/contrib/bind/lib/nameser/ns_print.c (revision 60941) @@ -1,818 +1,821 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef lint -static const char rcsid[] = "$Id: ns_print.c,v 8.17 1999/10/19 02:06:54 gson Exp $"; +static const char rcsid[] = "$Id: ns_print.c,v 8.18 2000/02/29 05:48:12 vixie Exp $"; #endif /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) ((size_t)sprintf x) #endif /* Forward. */ static size_t prune_origin(const char *name, const char *origin); static int charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen); static int addname(const u_char *msg, size_t msglen, const u_char **p, const char *origin, char **buf, size_t *buflen); static void addlen(size_t len, char **buf, size_t *buflen); static int addstr(const char *src, size_t len, char **buf, size_t *buflen); static int addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen); /* Proto. */ u_int16_t dst_s_dns_key_id(const u_char *, const int); /* Macros. */ #define T(x) \ do { \ if ((x) < 0) \ return (-1); \ } while (0) /* Public. */ /* * int * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen) * Convert an RR to presentation format. * return: * Number of characters written to buf, or -1 (check errno). */ int ns_sprintrr(const ns_msg *handle, const ns_rr *rr, const char *name_ctx, const char *origin, char *buf, size_t buflen) { int n; n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle), ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr), ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr), name_ctx, origin, buf, buflen); return (n); } /* * int * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen, * name_ctx, origin, buf, buflen) * Convert the fields of an RR into presentation format. * return: * Number of characters written to buf, or -1 (check errno). */ int ns_sprintrrf(const u_char *msg, size_t msglen, const char *name, ns_class class, ns_type type, u_long ttl, const u_char *rdata, size_t rdlen, const char *name_ctx, const char *origin, char *buf, size_t buflen) { const char *obuf = buf; const u_char *edata = rdata + rdlen; int spaced = 0; const char *comment; char tmp[100]; int len, x; /* * Owner. */ if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) { T(addstr("\t\t\t", 3, &buf, &buflen)); } else { len = prune_origin(name, origin); if (len == 0) { T(addstr("@\t\t\t", 4, &buf, &buflen)); } else { T(addstr(name, len, &buf, &buflen)); - /* Origin not used and no trailing dot? */ - if ((!origin || !origin[0] || name[len] == '\0') && - name[len - 1] != '.') { + /* Origin not used or not root, and no trailing dot? */ + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + name[len] == '\0')) && name[len - 1] != '.') { T(addstr(".", 1, &buf, &buflen)); len++; } T(spaced = addtab(len, 24, spaced, &buf, &buflen)); } } /* * TTL, Class, Type. */ T(x = ns_format_ttl(ttl, buf, buflen)); addlen(x, &buf, &buflen); len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type))); T(addstr(tmp, len, &buf, &buflen)); T(spaced = addtab(x + len, 16, spaced, &buf, &buflen)); /* * RData. */ switch (type) { case ns_t_a: if (rdlen != NS_INADDRSZ) goto formerr; (void) inet_ntop(AF_INET, rdata, buf, buflen); addlen(strlen(buf), &buf, &buflen); break; case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ns: case ns_t_ptr: T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; case ns_t_hinfo: case ns_t_isdn: /* First word. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Second word, optional in ISDN records. */ if (type == ns_t_isdn && rdata == edata) break; T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; break; case ns_t_soa: { u_long t; /* Server name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Administrator name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" (\n", 3, &buf, &buflen)); spaced = 0; if ((edata - rdata) != 5*NS_INT32SZ) goto formerr; /* Serial number. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); len = SPRINTF((tmp, "%lu", t)); T(addstr(tmp, len, &buf, &buflen)); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; serial\n", 9, &buf, &buflen)); spaced = 0; /* Refresh interval. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; refresh\n", 10, &buf, &buflen)); spaced = 0; /* Retry interval. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; retry\n", 8, &buf, &buflen)); spaced = 0; /* Expiry. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; expiry\n", 9, &buf, &buflen)); spaced = 0; /* Minimum TTL. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(addstr(" )", 2, &buf, &buflen)); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; minimum\n", 10, &buf, &buflen)); break; } case ns_t_mx: case ns_t_afsdb: case ns_t_rt: { u_int t; if (rdlen < NS_INT16SZ) goto formerr; /* Priority. */ t = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u ", t)); T(addstr(tmp, len, &buf, &buflen)); /* Target. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_px: { u_int t; if (rdlen < NS_INT16SZ) goto formerr; /* Priority. */ t = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u ", t)); T(addstr(tmp, len, &buf, &buflen)); /* Name1. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Name2. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_x25: T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; break; case ns_t_txt: while (rdata < edata) { T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; if (rdata < edata) T(addstr(" ", 1, &buf, &buflen)); } break; case ns_t_nsap: { char t[255*3]; (void) inet_nsap_ntoa(rdlen, rdata, t); T(addstr(t, strlen(t), &buf, &buflen)); break; } case ns_t_aaaa: if (rdlen != NS_IN6ADDRSZ) goto formerr; (void) inet_ntop(AF_INET6, rdata, buf, buflen); addlen(strlen(buf), &buf, &buflen); break; case ns_t_loc: { char t[255]; /* XXX protocol format checking? */ (void) loc_ntoa(rdata, t); T(addstr(t, strlen(t), &buf, &buflen)); break; } case ns_t_naptr: { u_int order, preference; char t[50]; if (rdlen < 2*NS_INT16SZ) goto formerr; /* Order, Precedence. */ order = ns_get16(rdata); rdata += NS_INT16SZ; preference = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((t, "%u %u ", order, preference)); T(addstr(t, len, &buf, &buflen)); /* Flags. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Service. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Regexp. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len < 0) return (-1); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Server. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_srv: { u_int priority, weight, port; char t[50]; if (rdlen < NS_INT16SZ*3) goto formerr; /* Priority, Weight, Port. */ priority = ns_get16(rdata); rdata += NS_INT16SZ; weight = ns_get16(rdata); rdata += NS_INT16SZ; port = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((t, "%u %u %u ", priority, weight, port)); T(addstr(t, len, &buf, &buflen)); /* Server. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_minfo: case ns_t_rp: /* Name1. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Name2. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; case ns_t_wks: { int n, lcnt; if (rdlen < NS_INT32SZ + 1) goto formerr; /* Address. */ (void) inet_ntop(AF_INET, rdata, buf, buflen); addlen(strlen(buf), &buf, &buflen); rdata += NS_INADDRSZ; /* Protocol. */ len = SPRINTF((tmp, " %u ( ", *rdata)); T(addstr(tmp, len, &buf, &buflen)); rdata += NS_INT8SZ; /* Bit map. */ n = 0; lcnt = 0; while (rdata < edata) { u_int c = *rdata++; do { if (c & 0200) { if (lcnt == 0) { T(addstr("\n\t\t\t\t", 5, &buf, &buflen)); lcnt = 10; spaced = 0; } len = SPRINTF((tmp, "%d ", n)); T(addstr(tmp, len, &buf, &buflen)); lcnt--; } c <<= 1; } while (++n & 07); } T(addstr(")", 1, &buf, &buflen)); break; } case ns_t_key: { char base64_key[NS_MD5RSA_MAX_BASE64]; u_int keyflags, protocol, algorithm, key_id; const char *leader; int n; if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ) goto formerr; /* Key flags, Protocol, Algorithm. */ key_id = dst_s_dns_key_id(rdata, edata-rdata); keyflags = ns_get16(rdata); rdata += NS_INT16SZ; protocol = *rdata++; algorithm = *rdata++; len = SPRINTF((tmp, "0x%04x %u %u", keyflags, protocol, algorithm)); T(addstr(tmp, len, &buf, &buflen)); /* Public key data. */ len = b64_ntop(rdata, edata - rdata, base64_key, sizeof base64_key); if (len < 0) goto formerr; if (len > 15) { T(addstr(" (", 2, &buf, &buflen)); leader = "\n\t\t"; spaced = 0; } else leader = " "; for (n = 0; n < len; n += 48) { T(addstr(leader, strlen(leader), &buf, &buflen)); T(addstr(base64_key + n, MIN(len - n, 48), &buf, &buflen)); } if (len > 15) T(addstr(" )", 2, &buf, &buflen)); n = SPRINTF((tmp, " ; key_tag= %u", key_id)); T(addstr(tmp, n, &buf, &buflen)); break; } case ns_t_sig: { char base64_key[NS_MD5RSA_MAX_BASE64]; u_int type, algorithm, labels, footprint; const char *leader; u_long t; int n; if (rdlen < 22) goto formerr; /* Type covered, Algorithm, Label count, Original TTL. */ type = ns_get16(rdata); rdata += NS_INT16SZ; algorithm = *rdata++; labels = *rdata++; t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s %d %d %lu ", p_type(type), algorithm, labels, t)); T(addstr(tmp, len, &buf, &buflen)); if (labels > (u_int)dn_count_labels(name)) goto formerr; /* Signature expiry. */ t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s ", p_secstodate(t))); T(addstr(tmp, len, &buf, &buflen)); /* Time signed. */ t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s ", p_secstodate(t))); T(addstr(tmp, len, &buf, &buflen)); /* Signature Footprint. */ footprint = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u ", footprint)); T(addstr(tmp, len, &buf, &buflen)); /* Signer's name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); /* Signature. */ len = b64_ntop(rdata, edata - rdata, base64_key, sizeof base64_key); if (len > 15) { T(addstr(" (", 2, &buf, &buflen)); leader = "\n\t\t"; spaced = 0; } else leader = " "; if (len < 0) goto formerr; for (n = 0; n < len; n += 48) { T(addstr(leader, strlen(leader), &buf, &buflen)); T(addstr(base64_key + n, MIN(len - n, 48), &buf, &buflen)); } if (len > 15) T(addstr(" )", 2, &buf, &buflen)); break; } case ns_t_nxt: { int n, c; /* Next domain name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); /* Type bit map. */ n = edata - rdata; for (c = 0; c < n*8; c++) if (NS_NXT_BIT_ISSET(c, rdata)) { len = SPRINTF((tmp, " %s", p_type(c))); T(addstr(tmp, len, &buf, &buflen)); } break; } case ns_t_cert: { u_int c_type, key_tag, alg; int n, siz; char base64_cert[8192], *leader, tmp[40]; c_type = ns_get16(rdata); rdata += NS_INT16SZ; key_tag = ns_get16(rdata); rdata += NS_INT16SZ; alg = (u_int) *rdata++; len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg)); T(addstr(tmp, len, &buf, &buflen)); siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ if (siz > sizeof(base64_cert) * 3/4) { char *str = "record too long to print"; T(addstr(str, strlen(str), &buf, &buflen)); } else { len = b64_ntop(rdata, edata-rdata, base64_cert, siz); if (len < 0) goto formerr; else if (len > 15) { T(addstr(" (", 2, &buf, &buflen)); leader = "\n\t\t"; spaced = 0; } else leader = " "; for (n = 0; n < len; n += 48) { T(addstr(leader, strlen(leader), &buf, &buflen)); T(addstr(base64_cert + n, MIN(len - n, 48), &buf, &buflen)); } if (len > 15) T(addstr(" )", 2, &buf, &buflen)); } break; } case ns_t_tsig: { /* BEW - need to complete this */ int n; T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); rdata += 8; /* time */ n = ns_get16(rdata); rdata += INT16SZ; rdata += n; /* sig */ n = ns_get16(rdata); rdata += INT16SZ; /* original id */ sprintf(buf, "%d", ns_get16(rdata)); rdata += INT16SZ; addlen(strlen(buf), &buf, &buflen); break; } default: comment = "unknown RR type"; goto hexify; } return (buf - obuf); formerr: comment = "RR format error"; hexify: { int n, m; char *p; len = SPRINTF((tmp, "\\#(\t\t; %s", comment)); T(addstr(tmp, len, &buf, &buflen)); while (rdata < edata) { p = tmp; p += SPRINTF((p, "\n\t")); spaced = 0; n = MIN(16, edata - rdata); for (m = 0; m < n; m++) p += SPRINTF((p, "%02x ", rdata[m])); T(addstr(tmp, p - tmp, &buf, &buflen)); if (n < 16) { T(addstr(")", 1, &buf, &buflen)); T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen)); } p = tmp; p += SPRINTF((p, "; ")); for (m = 0; m < n; m++) *p++ = (isascii(rdata[m]) && isprint(rdata[m])) ? rdata[m] : '.'; T(addstr(tmp, p - tmp, &buf, &buflen)); rdata += n; } return (buf - obuf); } } /* Private. */ /* * size_t * prune_origin(name, origin) * Find out if the name is at or under the current origin. * return: * Number of characters in name before start of origin, * or length of name if origin does not match. * notes: * This function should share code with samedomain(). */ static size_t prune_origin(const char *name, const char *origin) { const char *oname = name; while (*name != '\0') { if (origin != NULL && ns_samename(name, origin) == 1) return (name - oname - (name > oname)); while (*name != '\0') { if (*name == '\\') { name++; /* XXX need to handle \nnn form. */ if (*name == '\0') break; } else if (*name == '.') { name++; break; } name++; } } return (name - oname); } /* * int * charstr(rdata, edata, buf, buflen) * Format a into the presentation buffer. * return: * Number of rdata octets consumed * 0 for protocol format error * -1 for output buffer error * side effects: * buffer is advanced on success. */ static int charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) { const u_char *odata = rdata; size_t save_buflen = *buflen; char *save_buf = *buf; if (addstr("\"", 1, buf, buflen) < 0) goto enospc; if (rdata < edata) { int n = *rdata; if (rdata + 1 + n <= edata) { rdata++; while (n-- > 0) { if (strchr("\n\"\\", *rdata) != NULL) if (addstr("\\", 1, buf, buflen) < 0) goto enospc; if (addstr((const char *)rdata, 1, buf, buflen) < 0) goto enospc; rdata++; } } } if (addstr("\"", 1, buf, buflen) < 0) goto enospc; return (rdata - odata); enospc: errno = ENOSPC; *buf = save_buf; *buflen = save_buflen; return (-1); } static int addname(const u_char *msg, size_t msglen, const u_char **pp, const char *origin, char **buf, size_t *buflen) { size_t newlen, save_buflen = *buflen; char *save_buf = *buf; int n; n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen); if (n < 0) goto enospc; /* Guess. */ newlen = prune_origin(*buf, origin); - if ((origin == NULL || origin[0] == '\0' || (*buf)[newlen] == '\0') && - (newlen == 0 || (*buf)[newlen - 1] != '.')) { - /* No trailing dot. */ - if (newlen + 2 > *buflen) - goto enospc; /* No room for ".\0". */ - (*buf)[newlen++] = '.'; - (*buf)[newlen] = '\0'; - } if (newlen == 0) { /* Use "@" instead of name. */ if (newlen + 2 > *buflen) goto enospc; /* No room for "@\0". */ (*buf)[newlen++] = '@'; (*buf)[newlen] = '\0'; + } else { + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') { + /* No trailing dot. */ + if (newlen + 2 > *buflen) + goto enospc; /* No room for ".\0". */ + (*buf)[newlen++] = '.'; + (*buf)[newlen] = '\0'; + } } *pp += n; addlen(newlen, buf, buflen); **buf = '\0'; return (newlen); enospc: errno = ENOSPC; *buf = save_buf; *buflen = save_buflen; return (-1); } static void addlen(size_t len, char **buf, size_t *buflen) { INSIST(len <= *buflen); *buf += len; *buflen -= len; } static int addstr(const char *src, size_t len, char **buf, size_t *buflen) { if (len >= *buflen) { errno = ENOSPC; return (-1); } memcpy(*buf, src, len); addlen(len, buf, buflen); **buf = '\0'; return (0); } static int addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) { size_t save_buflen = *buflen; char *save_buf = *buf; int t; if (spaced || len >= target - 1) { T(addstr(" ", 2, buf, buflen)); spaced = 1; } else { for (t = (target - len - 1) / 8; t >= 0; t--) if (addstr("\t", 1, buf, buflen) < 0) { *buflen = save_buflen; *buf = save_buf; return (-1); } spaced = 0; } return (spaced); } Index: head/contrib/bind/lib/nameser/ns_verify.c =================================================================== --- head/contrib/bind/lib/nameser/ns_verify.c (revision 60940) +++ head/contrib/bind/lib/nameser/ns_verify.c (revision 60941) @@ -1,471 +1,479 @@ /* * Copyright (c) 1999 by Internet Software Consortium, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef lint -static const char rcsid[] = "$Id: ns_verify.c,v 8.11 1999/10/15 21:06:51 vixie Exp $"; +static const char rcsid[] = "$Id: ns_verify.c,v 8.13 2000/03/29 15:55:00 bwelling Exp $"; #endif /* Import. */ #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Private. */ #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ return (NS_TSIG_ERROR_FORMERR); \ } \ } while (0) /* Public. */ u_char * ns_find_tsig(u_char *msg, u_char *eom) { HEADER *hp = (HEADER *)msg; int n, type; u_char *cp = msg, *start; if (msg == NULL || eom == NULL || msg > eom) return (NULL); if (cp + HFIXEDSZ >= eom) return (NULL); if (hp->arcount == 0) return (NULL); cp += HFIXEDSZ; n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount)); if (n < 0) return (NULL); cp += n; n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount)); if (n < 0) return (NULL); cp += n; n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount)); if (n < 0) return (NULL); cp += n; n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1); if (n < 0) return (NULL); cp += n; start = cp; n = dn_skipname(cp, eom); if (n < 0) return (NULL); cp += n; if (cp + INT16SZ >= eom) return (NULL); GETSHORT(type, cp); if (type != ns_t_tsig) return (NULL); return (start); } /* ns_verify * Parameters: * statp res stuff * msg received message * msglen length of message * key tsig key used for verifying. * querysig (response), the signature in the query * querysiglen (response), the length of the signature in the query * sig (query), a buffer to hold the signature * siglen (query), input - length of signature buffer * output - length of signature * * Errors: * - bad input (-1) * - invalid dns message (NS_TSIG_ERROR_FORMERR) * - TSIG is not present (NS_TSIG_ERROR_NO_TSIG) * - key doesn't match (-ns_r_badkey) * - TSIG verification fails with BADKEY (-ns_r_badkey) * - TSIG verification fails with BADSIG (-ns_r_badsig) * - TSIG verification fails with BADTIME (-ns_r_badtime) * - TSIG verification succeeds, error set to BAKEY (ns_r_badkey) * - TSIG verification succeeds, error set to BADSIG (ns_r_badsig) * - TSIG verification succeeds, error set to BADTIME (ns_r_badtime) */ int ns_verify(u_char *msg, int *msglen, void *k, const u_char *querysig, int querysiglen, u_char *sig, int *siglen, time_t *timesigned, int nostrip) { HEADER *hp = (HEADER *)msg; DST_KEY *key = (DST_KEY *)k; u_char *cp = msg, *eom; char name[MAXDNAME], alg[MAXDNAME]; u_char *recstart, *rdatastart; u_char *sigstart, *otherstart; int n; int error; u_int16_t type, length; u_int16_t fudge, sigfieldlen, id, otherfieldlen; dst_init(); if (msg == NULL || msglen == NULL || *msglen < 0) return (-1); eom = msg + *msglen; recstart = ns_find_tsig(msg, eom); if (recstart == NULL) return (NS_TSIG_ERROR_NO_TSIG); cp = recstart; /* Read the key name. */ n = dn_expand(msg, eom, cp, name, MAXDNAME); if (n < 0) return (NS_TSIG_ERROR_FORMERR); cp += n; /* Read the type. */ BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); GETSHORT(type, cp); if (type != ns_t_tsig) return (NS_TSIG_ERROR_NO_TSIG); /* Skip the class and TTL, save the length. */ cp += INT16SZ + INT32SZ; GETSHORT(length, cp); if (eom - cp != length) return (NS_TSIG_ERROR_FORMERR); /* Read the algorithm name. */ rdatastart = cp; n = dn_expand(msg, eom, cp, alg, MAXDNAME); if (n < 0) return (NS_TSIG_ERROR_FORMERR); if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) return (-ns_r_badkey); cp += n; /* Read the time signed and fudge. */ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); cp += INT16SZ; GETLONG((*timesigned), cp); GETSHORT(fudge, cp); /* Read the signature. */ BOUNDS_CHECK(cp, INT16SZ); GETSHORT(sigfieldlen, cp); BOUNDS_CHECK(cp, sigfieldlen); sigstart = cp; cp += sigfieldlen; /* Read the original id and error. */ BOUNDS_CHECK(cp, 2*INT16SZ); GETSHORT(id, cp); GETSHORT(error, cp); /* Parse the other data. */ BOUNDS_CHECK(cp, INT16SZ); GETSHORT(otherfieldlen, cp); BOUNDS_CHECK(cp, otherfieldlen); otherstart = cp; cp += otherfieldlen; if (cp != eom) return (NS_TSIG_ERROR_FORMERR); /* Verify that the key used is OK. */ if (key != NULL) { if (key->dk_alg != KEY_HMAC_MD5) return (-ns_r_badkey); if (error != ns_r_badsig && error != ns_r_badkey) { if (ns_samename(key->dk_key_name, name) != 1) return (-ns_r_badkey); } } hp->arcount = htons(ntohs(hp->arcount) - 1); /* * Do the verification. */ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { void *ctx; u_char buf[MAXDNAME]; + u_char buf2[MAXDNAME]; /* Digest the query signature, if this is a response. */ dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); if (querysiglen > 0 && querysig != NULL) { u_int16_t len_n = htons(querysiglen); dst_verify_data(SIG_MODE_UPDATE, key, &ctx, (u_char *)&len_n, INT16SZ, NULL, 0); dst_verify_data(SIG_MODE_UPDATE, key, &ctx, querysig, querysiglen, NULL, 0); } /* Digest the message. */ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg, NULL, 0); /* Digest the key name. */ - n = ns_name_ntol(recstart, buf, sizeof(buf)); + n = ns_name_pton(name, buf2, sizeof(buf2)); + if (n < 0) + return (-1); + n = ns_name_ntol(buf2, buf, sizeof(buf)); + if (n < 0) + return (-1); dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); /* Digest the class and TTL. */ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, recstart + dn_skipname(recstart, eom) + INT16SZ, INT16SZ + INT32SZ, NULL, 0); /* Digest the algorithm. */ - n = ns_name_ntol(rdatastart, buf, sizeof(buf)); + n = ns_name_pton(alg, buf2, sizeof(buf2)); + if (n < 0) + return (-1); + n = ns_name_ntol(buf2, buf, sizeof(buf)); + if (n < 0) + return (-1); dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); /* Digest the time signed and fudge. */ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, rdatastart + dn_skipname(rdatastart, eom), INT16SZ + INT32SZ + INT16SZ, NULL, 0); /* Digest the error and other data. */ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, otherstart - INT16SZ - INT16SZ, otherfieldlen + INT16SZ + INT16SZ, NULL, 0); n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, sigstart, sigfieldlen); if (n < 0) return (-ns_r_badsig); if (sig != NULL && siglen != NULL) { if (*siglen < sigfieldlen) return (NS_TSIG_ERROR_NO_SPACE); memcpy(sig, sigstart, sigfieldlen); *siglen = sigfieldlen; } } else { if (sigfieldlen > 0) return (NS_TSIG_ERROR_FORMERR); if (sig != NULL && siglen != NULL) *siglen = 0; } /* Reset the counter, since we still need to check for badtime. */ hp->arcount = htons(ntohs(hp->arcount) + 1); /* Verify the time. */ if (abs((*timesigned) - time(NULL)) > fudge) return (-ns_r_badtime); if (nostrip == 0) { *msglen = recstart - msg; hp->arcount = htons(ntohs(hp->arcount) - 1); } if (error != NOERROR) return (error); return (0); } int ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen, ns_tcp_tsig_state *state) { dst_init(); if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) return (-1); state->counter = -1; state->key = k; if (state->key->dk_alg != KEY_HMAC_MD5) return (-ns_r_badkey); if (querysiglen > sizeof(state->sig)) return (-1); memcpy(state->sig, querysig, querysiglen); state->siglen = querysiglen; return (0); } int ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state, int required) { HEADER *hp = (HEADER *)msg; u_char *recstart, *rdatastart, *sigstart; int sigfieldlen, otherfieldlen; u_char *cp, *eom = msg + *msglen, *cp2; char name[MAXDNAME], alg[MAXDNAME]; u_char buf[MAXDNAME]; int n, type, length, fudge, id, error; time_t timesigned; if (msg == NULL || msglen == NULL || state == NULL) return (-1); state->counter++; if (state->counter == 0) return (ns_verify(msg, msglen, state->key, state->sig, state->siglen, state->sig, &state->siglen, ×igned, 0)); if (state->siglen > 0) { u_int16_t siglen_n = htons(state->siglen); dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx, NULL, 0, NULL, 0); dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, (u_char *)&siglen_n, INT16SZ, NULL, 0); dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, state->sig, state->siglen, NULL, 0); state->siglen = 0; } cp = recstart = ns_find_tsig(msg, eom); if (recstart == NULL) { if (required) return (NS_TSIG_ERROR_NO_TSIG); dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen, NULL, 0); return (0); } hp->arcount = htons(ntohs(hp->arcount) - 1); dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, recstart - msg, NULL, 0); /* Read the key name. */ n = dn_expand(msg, eom, cp, name, MAXDNAME); if (n < 0) return (NS_TSIG_ERROR_FORMERR); cp += n; /* Read the type. */ BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); GETSHORT(type, cp); if (type != ns_t_tsig) return (NS_TSIG_ERROR_NO_TSIG); /* Skip the class and TTL, save the length. */ cp += INT16SZ + INT32SZ; GETSHORT(length, cp); if (eom - cp != length) return (NS_TSIG_ERROR_FORMERR); /* Read the algorithm name. */ rdatastart = cp; n = dn_expand(msg, eom, cp, alg, MAXDNAME); if (n < 0) return (NS_TSIG_ERROR_FORMERR); if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) return (-ns_r_badkey); cp += n; /* Verify that the key used is OK. */ if ((ns_samename(state->key->dk_key_name, name) != 1 || state->key->dk_alg != KEY_HMAC_MD5)) return (-ns_r_badkey); /* Read the time signed and fudge. */ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); cp += INT16SZ; GETLONG(timesigned, cp); GETSHORT(fudge, cp); /* Read the signature. */ BOUNDS_CHECK(cp, INT16SZ); GETSHORT(sigfieldlen, cp); BOUNDS_CHECK(cp, sigfieldlen); sigstart = cp; cp += sigfieldlen; /* Read the original id and error. */ BOUNDS_CHECK(cp, 2*INT16SZ); GETSHORT(id, cp); GETSHORT(error, cp); /* Parse the other data. */ BOUNDS_CHECK(cp, INT16SZ); GETSHORT(otherfieldlen, cp); BOUNDS_CHECK(cp, otherfieldlen); cp += otherfieldlen; if (cp != eom) return (NS_TSIG_ERROR_FORMERR); /* * Do the verification. */ /* Digest the time signed and fudge. */ cp2 = buf; PUTSHORT(0, cp2); /* Top 16 bits of time. */ PUTLONG(timesigned, cp2); PUTSHORT(NS_TSIG_FUDGE, cp2); dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, buf, cp2 - buf, NULL, 0); n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, sigstart, sigfieldlen); if (n < 0) return (-ns_r_badsig); - - if (sigfieldlen > sizeof(state->sig)) - return (ns_r_badsig); if (sigfieldlen > sizeof(state->sig)) return (NS_TSIG_ERROR_NO_SPACE); memcpy(state->sig, sigstart, sigfieldlen); state->siglen = sigfieldlen; /* Verify the time. */ if (abs(timesigned - time(NULL)) > fudge) return (-ns_r_badtime); *msglen = recstart - msg; if (error != NOERROR) return (error); return (0); } Index: head/contrib/bind/lib/resolv/Makefile =================================================================== --- head/contrib/bind/lib/resolv/Makefile (revision 60940) +++ head/contrib/bind/lib/resolv/Makefile (revision 60941) @@ -1,93 +1,94 @@ # Copyright (c) 1996,1999 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS # ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE # CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR # PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. -# $Id: Makefile,v 8.30 1999/10/07 08:24:15 vixie Exp $ +# $Id: Makefile,v 8.33 2000/02/29 03:38:24 vixie Exp $ # these are only appropriate for BSD 4.4 or derivatives, and are used in # development. normal builds will be done in the top level directory and # this Makefile will be invoked with a lot of overrides for the following: SYSTYPE= bsdos DESTDIR = DESTLIB = /usr/local/lib O=o A=a CC= cc LD= ld SHELL= /bin/sh CDEBUG= -g TOP= ../.. INCL = ${TOP}/include PORTINCL = ${TOP}/port/${SYSTYPE}/include LIBBIND = ${TOP}/lib/libbind.${A} LIBBINDR = ../${TOP}/lib/libbind_r.${A} CFLAGS= ${CDEBUG} -I${PORTINCL} -I${INCL} # -D__BIND_NOSTATIC LD_LIBFLAGS= -x -r AR= ar cru RANLIB= ranlib INSTALL= install INSTALL_EXEC= INSTALL_LIB=-o bin -g bin THREADED= threaded SRCS= herror.c res_debug.c res_data.c res_comp.c res_init.c \ res_mkquery.c res_query.c res_send.c res_sendsigned.c \ res_mkupdate.c res_update.c \ res_findzonecut.c OBJS= herror.${O} res_debug.${O} res_data.${O} res_comp.${O} res_init.${O} \ res_mkquery.${O} res_query.${O} res_send.${O} res_sendsigned.${O} \ res_mkupdate.${O} res_update.${O} \ res_findzonecut.${O} all: ${LIBBIND} ${LIBBIND}: ${OBJS} ( cd ${THREADED} ; \ ${AR} ${LIBBINDR} ${ARPREF} ${OBJS} ${ARSUFF} ; \ ${RANLIB} ${LIBBINDR} ) ${AR} ${LIBBIND} ${ARPREF} ${OBJS} ${ARSUFF} ${RANLIB} ${LIBBIND} .c.${O}: - if test ! -d ${THREADED} ; then mkdir ${THREADED} ; fi + if test ! -d ${THREADED} ; then mkdir ${THREADED} ; else true ; fi ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} ${REENTRANT} -c $*.c \ -o ${THREADED}/$*.${O} - -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} -o a.out && \ - ${LDS} mv a.out ${THREADED}/$*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} ${THREADED}/$*.${O} \ + -o ${THREADED}/$*.out && \ + ${LDS} mv ${THREADED}/$*.out ${THREADED}/$*.${O} ${CC} ${CPPFLAGS} ${CFLAGS} ${BOUNDS} -c $*.c - -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o a.out && \ - ${LDS} mv a.out $*.${O} + -${LDS} ${LD} ${LD_LIBFLAGS} $*.${O} -o $*.out && \ + ${LDS} mv $*.out $*.${O} distclean: clean clean: FRC rm -f .depend a.out core ${LIB} tags rm -f *.${O} *.BAK *.CKP *~ rm -f ${THREADED}/*.${O} - -rmdir ${THREADED} + -if test -d ${THREADED} ; then rmdir ${THREADED}; else true; fi depend: FRC mkdep -I${INCL} -I${PORTINCL} ${CPPFLAGS} ${SRCS} links: FRC @set -e; ln -s SRC/*.[ch] . install: FRC: # DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. Index: head/contrib/bind/lib/resolv/res_debug.c =================================================================== --- head/contrib/bind/lib/resolv/res_debug.c (revision 60940) +++ head/contrib/bind/lib/resolv/res_debug.c (revision 60941) @@ -1,1023 +1,1045 @@ /* * Copyright (c) 1985 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_debug.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: res_debug.c,v 8.32 1999/10/13 16:39:39 vixie Exp $"; +static const char rcsid[] = "$Id: res_debug.c,v 8.34 2000/02/29 05:30:55 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) sprintf x #endif extern const char *_res_opcodes[]; extern const char *_res_sectioncodes[]; /* * Print the current options. */ void fp_resstat(const res_state statp, FILE *file) { u_long mask; fprintf(file, ";; res options:"); for (mask = 1; mask != 0; mask <<= 1) if (statp->options & mask) fprintf(file, " %s", p_option(mask)); putc('\n', file); } static void do_section(const res_state statp, ns_msg *handle, ns_sect section, int pflag, FILE *file) { int n, sflag, rrnum; - char buf[2048]; /* XXX need to malloc */ + static int buflen = 2048; + char *buf; ns_opcode opcode; ns_rr rr; /* * Print answer records. */ sflag = (statp->pfcode & pflag); if (statp->pfcode && !sflag) return; + buf = malloc(buflen); + if (buf == NULL) { + fprintf(file, ";; memory allocation failure\n"); + return; + } + opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode); rrnum = 0; for (;;) { if (ns_parserr(handle, section, rrnum, &rr)) { if (errno != ENODEV) fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); else if (rrnum > 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) putc('\n', file); - return; + goto cleanup; } if (rrnum == 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) fprintf(file, ";; %s SECTION:\n", p_section(section, opcode)); if (section == ns_s_qd) fprintf(file, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr), p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr))); else { n = ns_sprintrr(handle, &rr, NULL, NULL, - buf, sizeof buf); + buf, buflen); if (n < 0) { + if (errno == ENOSPC) { + free(buf); + buf = NULL; + if (buflen < 131072) + buf = malloc(buflen += 1024); + if (buf == NULL) { + fprintf(file, + ";; memory allocation failure\n"); + return; + } + continue; + } fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); - return; + goto cleanup; } fputs(buf, file); fputc('\n', file); } rrnum++; } + cleanup: + if (buf != NULL) + free(buf); } /* * Print the contents of a query. * This is intended to be primarily a debugging routine. */ void res_pquery(const res_state statp, const u_char *msg, int len, FILE *file) { ns_msg handle; int qdcount, ancount, nscount, arcount; u_int opcode, rcode, id; if (ns_initparse(msg, len, &handle) < 0) { fprintf(file, ";; ns_initparse: %s\n", strerror(errno)); return; } opcode = ns_msg_getflag(handle, ns_f_opcode); rcode = ns_msg_getflag(handle, ns_f_rcode); id = ns_msg_id(handle); qdcount = ns_msg_count(handle, ns_s_qd); ancount = ns_msg_count(handle, ns_s_an); nscount = ns_msg_count(handle, ns_s_ns); arcount = ns_msg_count(handle, ns_s_ar); /* * Print header fields. */ if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX) || rcode) fprintf(file, ";; ->>HEADER<<- opcode: %s, status: %s, id: %d\n", _res_opcodes[opcode], p_rcode(rcode), id); if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX)) putc(';', file); if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD2)) { fprintf(file, "; flags:"); if (ns_msg_getflag(handle, ns_f_qr)) fprintf(file, " qr"); if (ns_msg_getflag(handle, ns_f_aa)) fprintf(file, " aa"); if (ns_msg_getflag(handle, ns_f_tc)) fprintf(file, " tc"); if (ns_msg_getflag(handle, ns_f_rd)) fprintf(file, " rd"); if (ns_msg_getflag(handle, ns_f_ra)) fprintf(file, " ra"); if (ns_msg_getflag(handle, ns_f_z)) fprintf(file, " ??"); if (ns_msg_getflag(handle, ns_f_ad)) fprintf(file, " ad"); if (ns_msg_getflag(handle, ns_f_cd)) fprintf(file, " cd"); } if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD1)) { fprintf(file, "; %s: %d", p_section(ns_s_qd, opcode), qdcount); fprintf(file, ", %s: %d", p_section(ns_s_an, opcode), ancount); fprintf(file, ", %s: %d", p_section(ns_s_ns, opcode), nscount); fprintf(file, ", %s: %d", p_section(ns_s_ar, opcode), arcount); } if ((!statp->pfcode) || (statp->pfcode & (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) { putc('\n',file); } /* * Print the various sections. */ do_section(statp, &handle, ns_s_qd, RES_PRF_QUES, file); do_section(statp, &handle, ns_s_an, RES_PRF_ANS, file); do_section(statp, &handle, ns_s_ns, RES_PRF_AUTH, file); do_section(statp, &handle, ns_s_ar, RES_PRF_ADD, file); if (qdcount == 0 && ancount == 0 && nscount == 0 && arcount == 0) putc('\n', file); } const u_char * p_cdnname(const u_char *cp, const u_char *msg, int len, FILE *file) { char name[MAXDNAME]; int n; if ((n = dn_expand(msg, msg + len, cp, name, sizeof name)) < 0) return (NULL); if (name[0] == '\0') putc('.', file); else fputs(name, file); return (cp + n); } const u_char * p_cdname(const u_char *cp, const u_char *msg, FILE *file) { return (p_cdnname(cp, msg, PACKETSZ, file)); } /* Return a fully-qualified domain name from a compressed name (with length supplied). */ const u_char * p_fqnname(cp, msg, msglen, name, namelen) const u_char *cp, *msg; int msglen; char *name; int namelen; { int n, newlen; if ((n = dn_expand(msg, cp + msglen, cp, name, namelen)) < 0) return (NULL); newlen = strlen(name); if (newlen == 0 || name[newlen - 1] != '.') { if (newlen + 1 >= namelen) /* Lack space for final dot */ return (NULL); else strcpy(name + newlen, "."); } return (cp + n); } /* XXX: the rest of these functions need to become length-limited, too. */ const u_char * p_fqname(const u_char *cp, const u_char *msg, FILE *file) { char name[MAXDNAME]; const u_char *n; n = p_fqnname(cp, msg, MAXCDNAME, name, sizeof name); if (n == NULL) return (NULL); fputs(name, file); return (n); } /* * Names of RR classes and qclasses. Classes and qclasses are the same, except * that C_ANY is a qclass but not a class. (You can ask for records of class * C_ANY, but you can't have any records of that class in the database.) */ const struct res_sym __p_class_syms[] = { {C_IN, "IN"}, {C_CHAOS, "CHAOS"}, {C_HS, "HS"}, {C_HS, "HESIOD"}, {C_ANY, "ANY"}, {C_NONE, "NONE"}, {C_IN, (char *)0} }; /* * Names of message sections. */ const struct res_sym __p_default_section_syms[] = { {ns_s_qd, "QUERY"}, {ns_s_an, "ANSWER"}, {ns_s_ns, "AUTHORITY"}, {ns_s_ar, "ADDITIONAL"}, {0, (char *)0} }; const struct res_sym __p_update_section_syms[] = { {S_ZONE, "ZONE"}, {S_PREREQ, "PREREQUISITE"}, {S_UPDATE, "UPDATE"}, {S_ADDT, "ADDITIONAL"}, {0, (char *)0} }; const struct res_sym __p_key_syms[] = { {NS_ALG_MD5RSA, "RSA", "RSA KEY with MD5 hash"}, {NS_ALG_DH, "DH", "Diffie Hellman"}, {NS_ALG_DSA, "DSA", "Digital Signature Algorithm"}, {NS_ALG_EXPIRE_ONLY, "EXPIREONLY", "No algorithm"}, {NS_ALG_PRIVATE_OID, "PRIVATE", "Algorithm obtained from OID"}, {0, NULL, NULL} }; const struct res_sym __p_cert_syms[] = { {cert_t_pkix, "PKIX", "PKIX (X.509v3) Certificate"}, {cert_t_spki, "SPKI", "SPKI certificate"}, {cert_t_pgp, "PGP", "PGP certificate"}, {cert_t_url, "URL", "URL Private"}, {cert_t_oid, "OID", "OID Private"}, {0, NULL, NULL} }; /* * Names of RR types and qtypes. Types and qtypes are the same, except * that T_ANY is a qtype but not a type. (You can ask for records of type * T_ANY, but you can't have any records of that type in the database.) */ const struct res_sym __p_type_syms[] = { {ns_t_a, "A", "address"}, {ns_t_ns, "NS", "name server"}, {ns_t_md, "MD", "mail destination (deprecated)"}, {ns_t_mf, "MF", "mail forwarder (deprecated)"}, {ns_t_cname, "CNAME", "canonical name"}, {ns_t_soa, "SOA", "start of authority"}, {ns_t_mb, "MB", "mailbox"}, {ns_t_mg, "MG", "mail group member"}, {ns_t_mr, "MR", "mail rename"}, {ns_t_null, "NULL", "null"}, {ns_t_wks, "WKS", "well-known service (deprecated)"}, {ns_t_ptr, "PTR", "domain name pointer"}, {ns_t_hinfo, "HINFO", "host information"}, {ns_t_minfo, "MINFO", "mailbox information"}, {ns_t_mx, "MX", "mail exchanger"}, {ns_t_txt, "TXT", "text"}, {ns_t_rp, "RP", "responsible person"}, {ns_t_afsdb, "AFSDB", "DCE or AFS server"}, {ns_t_x25, "X25", "X25 address"}, {ns_t_isdn, "ISDN", "ISDN address"}, {ns_t_rt, "RT", "router"}, {ns_t_nsap, "NSAP", "nsap address"}, {ns_t_nsap_ptr, "NSAP_PTR", "domain name pointer"}, {ns_t_sig, "SIG", "signature"}, {ns_t_key, "KEY", "key"}, {ns_t_px, "PX", "mapping information"}, {ns_t_gpos, "GPOS", "geographical position (withdrawn)"}, {ns_t_aaaa, "AAAA", "IPv6 address"}, {ns_t_loc, "LOC", "location"}, {ns_t_nxt, "NXT", "next valid name (unimplemented)"}, {ns_t_eid, "EID", "endpoint identifier (unimplemented)"}, {ns_t_nimloc, "NIMLOC", "NIMROD locator (unimplemented)"}, {ns_t_srv, "SRV", "server selection"}, {ns_t_atma, "ATMA", "ATM address (unimplemented)"}, {ns_t_tsig, "TSIG", "transaction signature"}, {ns_t_ixfr, "IXFR", "incremental zone transfer"}, {ns_t_axfr, "AXFR", "zone transfer"}, {ns_t_zxfr, "ZXFR", "compressed zone transfer"}, {ns_t_mailb, "MAILB", "mailbox-related data (deprecated)"}, {ns_t_maila, "MAILA", "mail agent (deprecated)"}, {ns_t_naptr, "NAPTR", "URN Naming Authority"}, {ns_t_kx, "KX", "Key Exchange"}, {ns_t_cert, "CERT", "Certificate"}, {ns_t_any, "ANY", "\"any\""}, {0, NULL, NULL} }; /* * Names of DNS rcodes. */ const struct res_sym __p_rcode_syms[] = { {ns_r_noerror, "NOERROR", "no error"}, {ns_r_formerr, "FORMERR", "format error"}, {ns_r_servfail, "SERVFAIL", "server failed"}, {ns_r_nxdomain, "NXDOMAIN", "no such domain name"}, {ns_r_notimpl, "NOTIMP", "not implemented"}, {ns_r_refused, "REFUSED", "refused"}, {ns_r_yxdomain, "YXDOMAIN", "domain name exists"}, {ns_r_yxrrset, "YXRRSET", "rrset exists"}, {ns_r_nxrrset, "NXRRSET", "rrset doesn't exist"}, {ns_r_notauth, "NOTAUTH", "not authoritative"}, {ns_r_notzone, "NOTZONE", "Not in zone"}, {ns_r_max, "", ""}, {ns_r_badsig, "BADSIG", "bad signature"}, {ns_r_badkey, "BADKEY", "bad key"}, {ns_r_badtime, "BADTIME", "bad time"}, {0, NULL, NULL} }; int sym_ston(const struct res_sym *syms, const char *name, int *success) { for ((void)NULL; syms->name != 0; syms++) { if (strcasecmp (name, syms->name) == 0) { if (success) *success = 1; return (syms->number); } } if (success) *success = 0; return (syms->number); /* The default value. */ } const char * sym_ntos(const struct res_sym *syms, int number, int *success) { static char unname[20]; for ((void)NULL; syms->name != 0; syms++) { if (number == syms->number) { if (success) *success = 1; return (syms->name); } } sprintf(unname, "%d", number); /* XXX nonreentrant */ if (success) *success = 0; return (unname); } const char * sym_ntop(const struct res_sym *syms, int number, int *success) { static char unname[20]; for ((void)NULL; syms->name != 0; syms++) { if (number == syms->number) { if (success) *success = 1; return (syms->humanname); } } sprintf(unname, "%d", number); /* XXX nonreentrant */ if (success) *success = 0; return (unname); } /* * Return a string for the type. */ const char * p_type(int type) { return (sym_ntos(__p_type_syms, type, (int *)0)); } /* * Return a string for the type. */ const char * p_section(int section, int opcode) { const struct res_sym *symbols; switch (opcode) { case ns_o_update: symbols = __p_update_section_syms; break; default: symbols = __p_default_section_syms; break; } return (sym_ntos(symbols, section, (int *)0)); } /* * Return a mnemonic for class. */ const char * p_class(int class) { return (sym_ntos(__p_class_syms, class, (int *)0)); } /* * Return a mnemonic for an option */ const char * p_option(u_long option) { static char nbuf[40]; switch (option) { case RES_INIT: return "init"; case RES_DEBUG: return "debug"; case RES_AAONLY: return "aaonly(unimpl)"; case RES_USEVC: return "usevc"; case RES_PRIMARY: return "primry(unimpl)"; case RES_IGNTC: return "igntc"; case RES_RECURSE: return "recurs"; case RES_DEFNAMES: return "defnam"; case RES_STAYOPEN: return "styopn"; case RES_DNSRCH: return "dnsrch"; case RES_INSECURE1: return "insecure1"; case RES_INSECURE2: return "insecure2"; /* XXX nonreentrant */ default: sprintf(nbuf, "?0x%lx?", (u_long)option); return (nbuf); } } /* * Return a mnemonic for a time to live. */ const char * p_time(u_int32_t value) { static char nbuf[40]; /* XXX nonreentrant */ if (ns_format_ttl(value, nbuf, sizeof nbuf) < 0) sprintf(nbuf, "%u", value); return (nbuf); } /* * Return a string for the rcode. */ const char * p_rcode(int rcode) { return (sym_ntos(__p_rcode_syms, rcode, (int *)0)); } /* * routines to convert between on-the-wire RR format and zone file format. * Does not contain conversion to/from decimal degrees; divide or multiply * by 60*60*1000 for that. */ static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000, 1000000,10000000,100000000,1000000000}; /* takes an XeY precision/size value, returns a string representation. */ static const char * precsize_ntoa(prec) u_int8_t prec; { static char retbuf[sizeof "90000000.00"]; /* XXX nonreentrant */ unsigned long val; int mantissa, exponent; mantissa = (int)((prec >> 4) & 0x0f) % 10; exponent = (int)((prec >> 0) & 0x0f) % 10; val = mantissa * poweroften[exponent]; (void) sprintf(retbuf, "%ld.%.2ld", val/100, val%100); return (retbuf); } /* converts ascii size/precision X * 10**Y(cm) to 0xXY. moves pointer. */ static u_int8_t precsize_aton(strptr) char **strptr; { unsigned int mval = 0, cmval = 0; u_int8_t retval = 0; char *cp; int exponent; int mantissa; cp = *strptr; while (isdigit(*cp)) mval = mval * 10 + (*cp++ - '0'); if (*cp == '.') { /* centimeters */ cp++; if (isdigit(*cp)) { cmval = (*cp++ - '0') * 10; if (isdigit(*cp)) { cmval += (*cp++ - '0'); } } } cmval = (mval * 100) + cmval; for (exponent = 0; exponent < 9; exponent++) if (cmval < poweroften[exponent+1]) break; mantissa = cmval / poweroften[exponent]; if (mantissa > 9) mantissa = 9; retval = (mantissa << 4) | exponent; *strptr = cp; return (retval); } /* converts ascii lat/lon to unsigned encoded 32-bit number. moves pointer. */ static u_int32_t latlon2ul(latlonstrptr,which) char **latlonstrptr; int *which; { char *cp; u_int32_t retval; int deg = 0, min = 0, secs = 0, secsfrac = 0; cp = *latlonstrptr; while (isdigit(*cp)) deg = deg * 10 + (*cp++ - '0'); while (isspace(*cp)) cp++; if (!(isdigit(*cp))) goto fndhemi; while (isdigit(*cp)) min = min * 10 + (*cp++ - '0'); while (isspace(*cp)) cp++; if (!(isdigit(*cp))) goto fndhemi; while (isdigit(*cp)) secs = secs * 10 + (*cp++ - '0'); if (*cp == '.') { /* decimal seconds */ cp++; if (isdigit(*cp)) { secsfrac = (*cp++ - '0') * 100; if (isdigit(*cp)) { secsfrac += (*cp++ - '0') * 10; if (isdigit(*cp)) { secsfrac += (*cp++ - '0'); } } } } while (!isspace(*cp)) /* if any trailing garbage */ cp++; while (isspace(*cp)) cp++; fndhemi: switch (*cp) { case 'N': case 'n': case 'E': case 'e': retval = ((unsigned)1<<31) + (((((deg * 60) + min) * 60) + secs) * 1000) + secsfrac; break; case 'S': case 's': case 'W': case 'w': retval = ((unsigned)1<<31) - (((((deg * 60) + min) * 60) + secs) * 1000) - secsfrac; break; default: retval = 0; /* invalid value -- indicates error */ break; } switch (*cp) { case 'N': case 'n': case 'S': case 's': *which = 1; /* latitude */ break; case 'E': case 'e': case 'W': case 'w': *which = 2; /* longitude */ break; default: *which = 0; /* error */ break; } cp++; /* skip the hemisphere */ while (!isspace(*cp)) /* if any trailing garbage */ cp++; while (isspace(*cp)) /* move to next field */ cp++; *latlonstrptr = cp; return (retval); } /* converts a zone file representation in a string to an RDATA on-the-wire * representation. */ int loc_aton(ascii, binary) const char *ascii; u_char *binary; { const char *cp, *maxcp; u_char *bcp; u_int32_t latit = 0, longit = 0, alt = 0; u_int32_t lltemp1 = 0, lltemp2 = 0; int altmeters = 0, altfrac = 0, altsign = 1; u_int8_t hp = 0x16; /* default = 1e6 cm = 10000.00m = 10km */ u_int8_t vp = 0x13; /* default = 1e3 cm = 10.00m */ u_int8_t siz = 0x12; /* default = 1e2 cm = 1.00m */ int which1 = 0, which2 = 0; cp = ascii; maxcp = cp + strlen(ascii); lltemp1 = latlon2ul(&cp, &which1); lltemp2 = latlon2ul(&cp, &which2); switch (which1 + which2) { case 3: /* 1 + 2, the only valid combination */ if ((which1 == 1) && (which2 == 2)) { /* normal case */ latit = lltemp1; longit = lltemp2; } else if ((which1 == 2) && (which2 == 1)) { /* reversed */ longit = lltemp1; latit = lltemp2; } else { /* some kind of brokenness */ return (0); } break; default: /* we didn't get one of each */ return (0); } /* altitude */ if (*cp == '-') { altsign = -1; cp++; } if (*cp == '+') cp++; while (isdigit(*cp)) altmeters = altmeters * 10 + (*cp++ - '0'); if (*cp == '.') { /* decimal meters */ cp++; if (isdigit(*cp)) { altfrac = (*cp++ - '0') * 10; if (isdigit(*cp)) { altfrac += (*cp++ - '0'); } } } alt = (10000000 + (altsign * (altmeters * 100 + altfrac))); while (!isspace(*cp) && (cp < maxcp)) /* if trailing garbage or m */ cp++; while (isspace(*cp) && (cp < maxcp)) cp++; if (cp >= maxcp) goto defaults; siz = precsize_aton(&cp); while (!isspace(*cp) && (cp < maxcp)) /* if trailing garbage or m */ cp++; while (isspace(*cp) && (cp < maxcp)) cp++; if (cp >= maxcp) goto defaults; hp = precsize_aton(&cp); while (!isspace(*cp) && (cp < maxcp)) /* if trailing garbage or m */ cp++; while (isspace(*cp) && (cp < maxcp)) cp++; if (cp >= maxcp) goto defaults; vp = precsize_aton(&cp); defaults: bcp = binary; *bcp++ = (u_int8_t) 0; /* version byte */ *bcp++ = siz; *bcp++ = hp; *bcp++ = vp; PUTLONG(latit,bcp); PUTLONG(longit,bcp); PUTLONG(alt,bcp); return (16); /* size of RR in octets */ } /* takes an on-the-wire LOC RR and formats it in a human readable format. */ const char * loc_ntoa(binary, ascii) const u_char *binary; char *ascii; { static char *error = "?"; static char tmpbuf[sizeof "1000 60 60.000 N 1000 60 60.000 W -12345678.00m 90000000.00m 90000000.00m 90000000.00m"]; const u_char *cp = binary; int latdeg, latmin, latsec, latsecfrac; int longdeg, longmin, longsec, longsecfrac; char northsouth, eastwest; int altmeters, altfrac, altsign; const u_int32_t referencealt = 100000 * 100; int32_t latval, longval, altval; u_int32_t templ; u_int8_t sizeval, hpval, vpval, versionval; char *sizestr, *hpstr, *vpstr; versionval = *cp++; if (ascii == NULL) ascii = tmpbuf; if (versionval) { (void) sprintf(ascii, "; error: unknown LOC RR version"); return (ascii); } sizeval = *cp++; hpval = *cp++; vpval = *cp++; GETLONG(templ, cp); latval = (templ - ((unsigned)1<<31)); GETLONG(templ, cp); longval = (templ - ((unsigned)1<<31)); GETLONG(templ, cp); if (templ < referencealt) { /* below WGS 84 spheroid */ altval = referencealt - templ; altsign = -1; } else { altval = templ - referencealt; altsign = 1; } if (latval < 0) { northsouth = 'S'; latval = -latval; } else northsouth = 'N'; latsecfrac = latval % 1000; latval = latval / 1000; latsec = latval % 60; latval = latval / 60; latmin = latval % 60; latval = latval / 60; latdeg = latval; if (longval < 0) { eastwest = 'W'; longval = -longval; } else eastwest = 'E'; longsecfrac = longval % 1000; longval = longval / 1000; longsec = longval % 60; longval = longval / 60; longmin = longval % 60; longval = longval / 60; longdeg = longval; altfrac = altval % 100; altmeters = (altval / 100) * altsign; if ((sizestr = strdup(precsize_ntoa(sizeval))) == NULL) sizestr = error; if ((hpstr = strdup(precsize_ntoa(hpval))) == NULL) hpstr = error; if ((vpstr = strdup(precsize_ntoa(vpval))) == NULL) vpstr = error; sprintf(ascii, "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %d.%.2dm %sm %sm %sm", latdeg, latmin, latsec, latsecfrac, northsouth, longdeg, longmin, longsec, longsecfrac, eastwest, altmeters, altfrac, sizestr, hpstr, vpstr); if (sizestr != error) free(sizestr); if (hpstr != error) free(hpstr); if (vpstr != error) free(vpstr); return (ascii); } /* Return the number of DNS hierarchy levels in the name. */ int dn_count_labels(const char *name) { int i, len, count; len = strlen(name); for (i = 0, count = 0; i < len; i++) { /* XXX need to check for \. or use named's nlabels(). */ if (name[i] == '.') count++; } /* don't count initial wildcard */ if (name[0] == '*') if (count) count--; /* don't count the null label for root. */ /* if terminating '.' not found, must adjust */ /* count to include last label */ if (len > 0 && name[len-1] != '.') count++; return (count); } /* * Make dates expressed in seconds-since-Jan-1-1970 easy to read. * SIG records are required to be printed like this, by the Secure DNS RFC. */ char * p_secstodate (u_long secs) { /* XXX nonreentrant */ static char output[15]; /* YYYYMMDDHHMMSS and null */ time_t clock = secs; struct tm *time; #ifdef HAVE_TIME_R gmtime_r(&clock, &time); #else time = gmtime(&clock); #endif time->tm_year += 1900; time->tm_mon += 1; sprintf(output, "%04d%02d%02d%02d%02d%02d", time->tm_year, time->tm_mon, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); return (output); } Index: head/contrib/bind/lib/resolv/res_debug.h =================================================================== --- head/contrib/bind/lib/resolv/res_debug.h (revision 60940) +++ head/contrib/bind/lib/resolv/res_debug.h (revision 60941) @@ -1,34 +1,34 @@ /* * Copyright (c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef _RES_DEBUG_H_ #define _RES_DEBUG_H_ #ifndef DEBUG # define Dprint(cond, args) /*empty*/ # define DprintQ(cond, args, query, size) /*empty*/ -# define Aerror(file, string, error, address) /*empty*/ -# define Perror(file, string, error) /*empty*/ +# define Aerror(statp, file, string, error, address) /*empty*/ +# define Perror(statp, file, string, error) /*empty*/ #else # define Dprint(cond, args) if (cond) {fprintf args;} else {} # define DprintQ(cond, args, query, size) if (cond) {\ fprintf args;\ res_pquery(statp, query, size, stdout);\ } else {} #endif #endif /* _RES_DEBUG_H_ */ Index: head/contrib/bind/lib/resolv/res_findzonecut.c =================================================================== --- head/contrib/bind/lib/resolv/res_findzonecut.c (revision 60940) +++ head/contrib/bind/lib/resolv/res_findzonecut.c (revision 60941) @@ -1,596 +1,601 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: res_findzonecut.c,v 8.8 1999/10/15 19:49:11 vixie Exp $"; +static const char rcsid[] = "$Id: res_findzonecut.c,v 8.9 1999/12/21 09:33:34 cyarnell Exp $"; #endif /* not lint */ /* * Copyright (c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Data structures. */ typedef struct rr_a { LINK(struct rr_a) link; struct in_addr addr; } rr_a; typedef LIST(rr_a) rrset_a; typedef struct rr_ns { LINK(struct rr_ns) link; const char * name; rrset_a addrs; } rr_ns; typedef LIST(rr_ns) rrset_ns; /* Forward. */ static int satisfy(res_state, const char *, rrset_ns *, struct in_addr *, int); static int add_addrs(res_state, rr_ns *, struct in_addr *, int); static int get_soa(res_state, const char *, ns_class, char *, size_t, char *, size_t, rrset_ns *); static int get_ns(res_state, const char *, ns_class, rrset_ns *); static int get_glue(res_state, ns_class, rrset_ns *); static int save_ns(res_state, ns_msg *, ns_sect, const char *, ns_class, rrset_ns *); static int save_a(res_state, ns_msg *, ns_sect, const char *, ns_class, rrset_a *); static void free_nsrrset(rrset_ns *); static void free_nsrr(rrset_ns *, rr_ns *); static rr_ns * find_ns(rrset_ns *, const char *); static int do_query(res_state, const char *, ns_class, ns_type, u_char *, ns_msg *); static void dprintf(const char *, ...); /* Macros. */ #define DPRINTF(x) do {\ int save_errno = errno; \ if ((statp->options & RES_DEBUG) != 0) dprintf x; \ errno = save_errno; \ } while (0) /* Public. */ /* * int * res_findzonecut(res, dname, class, zname, zsize, addrs, naddrs) * find enclosing zone for a , and some server addresses * parameters: * res - resolver context to work within (is modified) * dname - domain name whose enclosing zone is desired * class - class of dname (and its enclosing zone) * zname - found zone name * zsize - allocated size of zname * addrs - found server addresses * naddrs - max number of addrs * return values: * < 0 - an error occurred (check errno) * = 0 - zname is now valid, but addrs[] wasn't changed * > 0 - zname is now valid, and return value is number of addrs[] found * notes: * this function calls res_nsend() which means it depends on correctly * functioning recursive nameservers (usually defined in /etc/resolv.conf * or its local equivilent). * * we start by asking for an SOA. if we get one as an * answer, that just means is a zone top, which is fine. * more than likely we'll be told to go pound sand, in the form of a * negative answer. * * note that we are not prepared to deal with referrals since that would * only come from authority servers and our correctly functioning local * recursive server would have followed the referral and got us something * more definite. * * if the authority section contains an SOA, this SOA should also be the * closest enclosing zone, since any intermediary zone cuts would've been * returned as referrals and dealt with by our correctly functioning local * recursive name server. but an SOA in the authority section should NOT * match our dname (since that would have been returned in the answer * section). an authority section SOA has to be "above" our dname. * * we cannot fail to find an SOA in this way. ultimately we'll return * a zname indicating the root zone if that's the closest enclosing zone. * however, since authority section SOA's were once optional, it's * possible that we'll have to go hunting for the enclosing SOA by * ripping labels off the front of our dname -- this is known as "doing * it the hard way." * * ultimately we want some server addresses, which are ideally the ones * pertaining to the SOA.MNAME, but only if there is a matching NS RR. * so the second phase (after we find an SOA) is to go looking for the * NS RRset for that SOA's zone. * * no answer section processed by this code is allowed to contain CNAME * or DNAME RR's. for the SOA query this means we strip a label and * keep going. for the NS and A queries this means we just give up. */ int res_findzonecut(res_state statp, const char *dname, ns_class class, int opts, char *zname, size_t zsize, struct in_addr *addrs, int naddrs) { char mname[NS_MAXDNAME]; u_long save_pfcode; rrset_ns nsrrs; int n; DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d", dname, p_class(class), (long)zsize, naddrs)); save_pfcode = statp->pfcode; statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX | RES_PRF_QUES | RES_PRF_ANS | RES_PRF_AUTH | RES_PRF_ADD; INIT_LIST(nsrrs); DPRINTF(("get the soa, and see if it has enough glue")); if ((n = get_soa(statp, dname, class, zname, zsize, mname, sizeof mname, &nsrrs)) < 0 || ((opts & RES_EXHAUSTIVE) == 0 && (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) goto done; DPRINTF(("get the ns rrset and see if it has enough glue")); if ((n = get_ns(statp, zname, class, &nsrrs)) < 0 || ((opts & RES_EXHAUSTIVE) == 0 && (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) goto done; DPRINTF(("get the missing glue and see if it's finally enough")); if ((n = get_glue(statp, class, &nsrrs)) >= 0) n = satisfy(statp, mname, &nsrrs, addrs, naddrs); done: DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK")); free_nsrrset(&nsrrs); statp->pfcode = save_pfcode; return (n); } /* Private. */ static int satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp, struct in_addr *addrs, int naddrs) { rr_ns *nsrr; int n, x; n = 0; nsrr = find_ns(nsrrsp, mname); if (nsrr != NULL) { x = add_addrs(statp, nsrr, addrs, naddrs); addrs += x; naddrs -= x; n += x; } for (nsrr = HEAD(*nsrrsp); nsrr != NULL && naddrs > 0; nsrr = NEXT(nsrr, link)) if (ns_samename(nsrr->name, mname) != 1) { x = add_addrs(statp, nsrr, addrs, naddrs); addrs += x; naddrs -= x; n += x; } DPRINTF(("satisfy(%s): %d", mname, n)); return (n); } static int add_addrs(res_state statp, rr_ns *nsrr, struct in_addr *addrs, int naddrs) { rr_a *arr; int n = 0; for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) { if (naddrs <= 0) return (0); *addrs++ = arr->addr; naddrs--; n++; } DPRINTF(("add_addrs: %d", n)); return (n); } static int get_soa(res_state statp, const char *dname, ns_class class, char *zname, size_t zsize, char *mname, size_t msize, rrset_ns *nsrrsp) { char tname[NS_MAXDNAME]; u_char resp[NS_PACKETSZ]; int n, i, ancount, nscount; ns_sect sect; ns_msg msg; u_int rcode; /* * Find closest enclosing SOA, even if it's for the root zone. */ /* First canonicalize dname (exactly one unescaped trailing "."). */ if (ns_makecanon(dname, tname, sizeof tname) < 0) return (-1); dname = tname; /* Now grovel the subdomains, hunting for an SOA answer or auth. */ for (;;) { /* Leading or inter-label '.' are skipped here. */ while (*dname == '.') dname++; /* Is there an SOA? */ n = do_query(statp, dname, class, ns_t_soa, resp, &msg); if (n < 0) { DPRINTF(("get_soa: do_query('%s', %s) failed (%d)", dname, p_class(class), n)); return (-1); } if (n > 0) { DPRINTF(("get_soa: CNAME or DNAME found")); sect = ns_s_max, n = 0; } else { rcode = ns_msg_getflag(msg, ns_f_rcode); ancount = ns_msg_count(msg, ns_s_an); nscount = ns_msg_count(msg, ns_s_ns); if (ancount > 0 && rcode == ns_r_noerror) sect = ns_s_an, n = ancount; else if (nscount > 0) sect = ns_s_ns, n = nscount; else sect = ns_s_max, n = 0; } for (i = 0; i < n; i++) { const char *t; const u_char *rdata; int rdlen; ns_rr rr; if (ns_parserr(&msg, sect, i, &rr) < 0) { DPRINTF(("get_soa: ns_parserr(%s, %d) failed", p_section(sect, ns_o_query), i)); return (-1); } if (ns_rr_type(rr) == ns_t_cname || ns_rr_type(rr) == ns_t_dname) break; if (ns_rr_type(rr) != ns_t_soa || ns_rr_class(rr) != class) continue; t = ns_rr_name(rr); switch (sect) { case ns_s_an: if (ns_samedomain(dname, t) == 0) { DPRINTF(("get_soa: ns_samedomain('%s', '%s') == 0", dname, t)); errno = EPROTOTYPE; return (-1); } break; case ns_s_ns: if (ns_samename(dname, t) == 1 || ns_samedomain(dname, t) == 0) { DPRINTF(("get_soa: ns_samename() || !ns_samedomain('%s', '%s')", dname, t)); errno = EPROTOTYPE; return (-1); } break; default: abort(); } if (strlen(t) + 1 > zsize) { DPRINTF(("get_soa: zname(%d) too small (%d)", zsize, strlen(t) + 1)); errno = EMSGSIZE; return (-1); } strcpy(zname, t); rdata = ns_rr_rdata(rr); rdlen = ns_rr_rdlen(rr); if (ns_name_uncompress(resp, ns_msg_end(msg), rdata, mname, msize) < 0) { DPRINTF(("get_soa: ns_name_uncompress failed")); return (-1); } if (save_ns(statp, &msg, ns_s_ns, zname, class, nsrrsp) < 0) { DPRINTF(("get_soa: save_ns failed")); return (-1); } return (0); } /* If we're out of labels, then not even "." has an SOA! */ if (*dname == '\0') break; /* Find label-terminating "."; top of loop will skip it. */ while (*dname != '.') { if (*dname == '\\') if (*++dname == '\0') { errno = EMSGSIZE; return (-1); } dname++; } } DPRINTF(("get_soa: out of labels")); errno = EDESTADDRREQ; return (-1); } static int get_ns(res_state statp, const char *zname, ns_class class, rrset_ns *nsrrsp) { u_char resp[NS_PACKETSZ]; ns_msg msg; int n; /* Go and get the NS RRs for this zone. */ n = do_query(statp, zname, class, ns_t_ns, resp, &msg); if (n != 0) { DPRINTF(("get_ns: do_query('zname', %s) failed (%d)", zname, p_class(class), n)); return (-1); } /* Remember the NS RRs and associated A RRs that came back. */ if (save_ns(statp, &msg, ns_s_an, zname, class, nsrrsp) < 0) { DPRINTF(("get_ns save_ns('%s', %s) failed", zname, p_class(class))); return (-1); } return (0); } static int get_glue(res_state statp, ns_class class, rrset_ns *nsrrsp) { rr_ns *nsrr, *nsrr_n; /* Go and get the A RRs for each empty NS RR on our list. */ for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) { u_char resp[NS_PACKETSZ]; ns_msg msg; int n; nsrr_n = NEXT(nsrr, link); if (EMPTY(nsrr->addrs)) { n = do_query(statp, nsrr->name, class, ns_t_a, resp, &msg); - if (n != 0) { + if (n < 0) { DPRINTF(("get_glue: do_query('%s', %s') failed", - nsrr->name, p_class(class), n)); + nsrr->name, p_class(class))); return (-1); + } + if (n > 0) { + DPRINTF(( + "get_glue: do_query('%s', %s') CNAME or DNAME found", + nsrr->name, p_class(class))); } if (save_a(statp, &msg, ns_s_an, nsrr->name, class, &nsrr->addrs) < 0) { DPRINTF(("get_glue: save_r('%s', %s) failed", nsrr->name, p_class(class))); return (-1); } /* If it's still empty, it's just chaff. */ if (EMPTY(nsrr->addrs)) { DPRINTF(("get_glue: removing empty '%s' NS", nsrr->name)); free_nsrr(nsrrsp, nsrr); } } } return (0); } static int save_ns(res_state statp, ns_msg *msg, ns_sect sect, const char *owner, ns_class class, rrset_ns *nsrrsp) { int i; for (i = 0; i < ns_msg_count(*msg, sect); i++) { char tname[MAXDNAME]; const u_char *rdata; rr_ns *nsrr; ns_rr rr; int rdlen; if (ns_parserr(msg, sect, i, &rr) < 0) { DPRINTF(("save_ns: ns_parserr(%s, %d) failed", p_section(sect, ns_o_query), i)); return (-1); } if (ns_rr_type(rr) != ns_t_ns || ns_rr_class(rr) != class || ns_samename(ns_rr_name(rr), owner) != 1) continue; nsrr = find_ns(nsrrsp, ns_rr_name(rr)); if (nsrr == NULL) { nsrr = malloc(sizeof *nsrr); if (nsrr == NULL) { DPRINTF(("save_ns: malloc failed")); return (-1); } rdata = ns_rr_rdata(rr); rdlen = ns_rr_rdlen(rr); if (ns_name_uncompress(ns_msg_base(*msg), ns_msg_end(*msg), rdata, tname, sizeof tname) < 0) { DPRINTF(("save_ns: ns_name_uncompress failed")); free(nsrr); return (-1); } nsrr->name = strdup(tname); if (nsrr->name == NULL) { DPRINTF(("save_ns: strdup failed")); free(nsrr); return (-1); } INIT_LIST(nsrr->addrs); APPEND(*nsrrsp, nsrr, link); } if (save_a(statp, msg, ns_s_ar, nsrr->name, class, &nsrr->addrs) < 0) { DPRINTF(("save_ns: save_r('%s', %s) failed", nsrr->name, p_class(class))); return (-1); } } return (0); } static int save_a(res_state statp, ns_msg *msg, ns_sect sect, const char *owner, ns_class class, rrset_a *arrsp) { int i; for (i = 0; i < ns_msg_count(*msg, sect); i++) { ns_rr rr; rr_a *arr; if (ns_parserr(msg, sect, i, &rr) < 0) { DPRINTF(("save_a: ns_parserr(%s, %d) failed", p_section(sect, ns_o_query), i)); return (-1); } if (ns_rr_type(rr) != ns_t_a || ns_rr_class(rr) != class || ns_samename(ns_rr_name(rr), owner) != 1 || ns_rr_rdlen(rr) != NS_INADDRSZ) continue; arr = malloc(sizeof *arr); if (arr == NULL) { DPRINTF(("save_a: malloc failed")); return (-1); } memcpy(&arr->addr, ns_rr_rdata(rr), NS_INADDRSZ); APPEND(*arrsp, arr, link); } return (0); } static void free_nsrrset(rrset_ns *nsrrsp) { rr_ns *nsrr; while ((nsrr = HEAD(*nsrrsp)) != NULL) free_nsrr(nsrrsp, nsrr); } static void free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) { rr_a *arr; while ((arr = HEAD(nsrr->addrs)) != NULL) { UNLINK(nsrr->addrs, arr, link); free(arr); } free((char *)nsrr->name); UNLINK(*nsrrsp, nsrr, link); free(nsrr); } static rr_ns * find_ns(rrset_ns *nsrrsp, const char *dname) { rr_ns *nsrr; for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link)) if (ns_samename(nsrr->name, dname) == 1) return (nsrr); return (NULL); } static int do_query(res_state statp, const char *dname, ns_class class, ns_type qtype, u_char *resp, ns_msg *msg) { u_char req[NS_PACKETSZ]; int i, n; n = res_nmkquery(statp, ns_o_query, dname, class, qtype, NULL, 0, NULL, req, NS_PACKETSZ); if (n < 0) { DPRINTF(("do_query: res_nmkquery failed")); return (-1); } n = res_nsend(statp, req, n, resp, NS_PACKETSZ); if (n < 0) { DPRINTF(("do_query: res_nsend failed")); return (-1); } if (n == 0) { DPRINTF(("do_query: res_nsend returned 0")); errno = EMSGSIZE; return (-1); } if (ns_initparse(resp, n, msg) < 0) { DPRINTF(("do_query: ns_initparse failed")); return (-1); } n = 0; for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) { ns_rr rr; if (ns_parserr(msg, ns_s_an, i, &rr) < 0) { DPRINTF(("do_query: ns_parserr failed")); return (-1); } n += (ns_rr_class(rr) == class && (ns_rr_type(rr) == ns_t_cname || ns_rr_type(rr) == ns_t_dname)); } return (n); } static void dprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fputs(";; res_findzonecut: ", stderr); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); } Index: head/contrib/bind/lib/resolv/res_init.c =================================================================== --- head/contrib/bind/lib/resolv/res_init.c (revision 60940) +++ head/contrib/bind/lib/resolv/res_init.c (revision 60941) @@ -1,481 +1,502 @@ /* * Copyright (c) 1985, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_init.c 8.1 (Berkeley) 6/7/93"; -static const char rcsid[] = "$Id: res_init.c,v 8.13 1999/10/13 16:39:40 vixie Exp $"; +static const char rcsid[] = "$Id: res_init.c,v 8.16 2000/05/09 07:10:12 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Options. Should all be left alone. */ #define RESOLVSORT -#define RFC1535 #define DEBUG static void res_setoptions __P((res_state, const char *, const char *)); #ifdef RESOLVSORT static const char sort_mask[] = "/&"; #define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL) static u_int32_t net_mask __P((struct in_addr)); #endif #if !defined(isascii) /* XXX - could be a function */ # define isascii(c) (!(c & 0200)) #endif /* * Resolver state default settings. */ /* * Set up default settings. If the configuration file exist, the values * there will have precedence. Otherwise, the server address is set to * INADDR_ANY and the default domain name comes from the gethostname(). * * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1 * rather than INADDR_ANY ("0.0.0.0") as the default name server address * since it was noted that INADDR_ANY actually meant ``the first interface * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface, * it had to be "up" in order for you to reach your own name server. It * was later decided that since the recommended practice is to always * install local static routes through 127.0.0.1 for all your network * interfaces, that we could solve this problem without a code change. * * The configuration file should always be used, since it is the only way * to specify a default domain. If you are running a server on your local * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1" * in the configuration file. * * Return 0 if completes successfully, -1 on error */ int res_ninit(res_state statp) { extern int __res_vinit(res_state, int); return (__res_vinit(statp, 0)); } /* This function has to be reachable by res_data.c but not publically. */ int __res_vinit(res_state statp, int preinit) { register FILE *fp; register char *cp, **pp; register int n; char buf[BUFSIZ]; int nserv = 0; /* number of nameserver records read from file */ int haveenv = 0; int havesearch = 0; #ifdef RESOLVSORT int nsort = 0; char *net; #endif -#ifndef RFC1535 int dots; -#endif if (!preinit) { statp->retrans = RES_TIMEOUT; statp->retry = RES_DFLRETRY; statp->options = RES_DEFAULT; statp->id = res_randomid(); } #ifdef USELOOPBACK statp->nsaddr.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1); #else statp->nsaddr.sin_addr.s_addr = INADDR_ANY; #endif statp->nsaddr.sin_family = AF_INET; statp->nsaddr.sin_port = htons(NAMESERVER_PORT); statp->nscount = 1; statp->ndots = 1; statp->pfcode = 0; - statp->_sock = -1; + statp->_vcsock = -1; statp->_flags = 0; statp->qhook = NULL; statp->rhook = NULL; + statp->_u._ext.nscount = 0; /* Allow user to override the local domain definition */ if ((cp = getenv("LOCALDOMAIN")) != NULL) { (void)strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; haveenv++; /* * Set search list to be blank-separated strings * from rest of env value. Permits users of LOCALDOMAIN * to still have a search list, and anyone to set the * one that they want to use as an individual (even more * important now that the rfc1535 stuff restricts searches) */ cp = statp->defdname; pp = statp->dnsrch; *pp++ = cp; for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) { if (*cp == '\n') /* silly backwards compat */ break; else if (*cp == ' ' || *cp == '\t') { *cp = 0; n = 1; } else if (n) { *pp++ = cp; n = 0; havesearch = 1; } } /* null terminate last domain if there are excess */ while (*cp != '\0' && *cp != ' ' && *cp != '\t' && *cp != '\n') cp++; *cp = '\0'; *pp++ = 0; } #define MATCH(line, name) \ (!strncmp(line, name, sizeof(name) - 1) && \ (line[sizeof(name) - 1] == ' ' || \ line[sizeof(name) - 1] == '\t')) if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) { /* read the config file */ while (fgets(buf, sizeof(buf), fp) != NULL) { /* skip comments */ if (*buf == ';' || *buf == '#') continue; /* read default domain name */ if (MATCH(buf, "domain")) { if (haveenv) /* skip if have from environ */ continue; cp = buf + sizeof("domain") - 1; while (*cp == ' ' || *cp == '\t') cp++; if ((*cp == '\0') || (*cp == '\n')) continue; strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; if ((cp = strpbrk(statp->defdname, " \t\n")) != NULL) *cp = '\0'; havesearch = 0; continue; } /* set search list */ if (MATCH(buf, "search")) { if (haveenv) /* skip if have from environ */ continue; cp = buf + sizeof("search") - 1; while (*cp == ' ' || *cp == '\t') cp++; if ((*cp == '\0') || (*cp == '\n')) continue; strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; if ((cp = strchr(statp->defdname, '\n')) != NULL) *cp = '\0'; /* * Set search list to be blank-separated strings * on rest of line. */ cp = statp->defdname; pp = statp->dnsrch; *pp++ = cp; for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) { if (*cp == ' ' || *cp == '\t') { *cp = 0; n = 1; } else if (n) { *pp++ = cp; n = 0; } } /* null terminate last domain if there are excess */ while (*cp != '\0' && *cp != ' ' && *cp != '\t') cp++; *cp = '\0'; *pp++ = 0; havesearch = 1; continue; } /* read nameservers to query */ if (MATCH(buf, "nameserver") && nserv < MAXNS) { struct in_addr a; cp = buf + sizeof("nameserver") - 1; while (*cp == ' ' || *cp == '\t') cp++; if ((*cp != '\0') && (*cp != '\n') && inet_aton(cp, &a)) { statp->nsaddr_list[nserv].sin_addr = a; statp->nsaddr_list[nserv].sin_family = AF_INET; statp->nsaddr_list[nserv].sin_port = htons(NAMESERVER_PORT); nserv++; } continue; } #ifdef RESOLVSORT if (MATCH(buf, "sortlist")) { struct in_addr a; cp = buf + sizeof("sortlist") - 1; while (nsort < MAXRESOLVSORT) { while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\0' || *cp == '\n' || *cp == ';') break; net = cp; while (*cp && !ISSORTMASK(*cp) && *cp != ';' && isascii(*cp) && !isspace(*cp)) cp++; n = *cp; *cp = 0; if (inet_aton(net, &a)) { statp->sort_list[nsort].addr = a; if (ISSORTMASK(n)) { *cp++ = n; net = cp; while (*cp && *cp != ';' && isascii(*cp) && !isspace(*cp)) cp++; n = *cp; *cp = 0; if (inet_aton(net, &a)) { statp->sort_list[nsort].mask = a.s_addr; } else { statp->sort_list[nsort].mask = net_mask(statp->sort_list[nsort].addr); } } else { statp->sort_list[nsort].mask = net_mask(statp->sort_list[nsort].addr); } nsort++; } *cp = n; } continue; } #endif if (MATCH(buf, "options")) { res_setoptions(statp, buf + sizeof("options") - 1, "conf"); continue; } } if (nserv > 1) statp->nscount = nserv; #ifdef RESOLVSORT statp->nsort = nsort; #endif (void) fclose(fp); } if (statp->defdname[0] == 0 && gethostname(buf, sizeof(statp->defdname) - 1) == 0 && (cp = strchr(buf, '.')) != NULL) strcpy(statp->defdname, cp + 1); /* find components of local domain that might be searched */ if (havesearch == 0) { pp = statp->dnsrch; *pp++ = statp->defdname; *pp = NULL; -#ifndef RFC1535 dots = 0; for (cp = statp->defdname; *cp; cp++) dots += (*cp == '.'); cp = statp->defdname; while (pp < statp->dnsrch + MAXDFLSRCH) { if (dots < LOCALDOMAINPARTS) break; cp = strchr(cp, '.') + 1; /* we know there is one */ *pp++ = cp; dots--; } *pp = NULL; #ifdef DEBUG if (statp->options & RES_DEBUG) { printf(";; res_init()... default dnsrch list:\n"); for (pp = statp->dnsrch; *pp; pp++) printf(";;\t%s\n", *pp); printf(";;\t..END..\n"); } #endif -#endif /* !RFC1535 */ } if ((cp = getenv("RES_OPTIONS")) != NULL) res_setoptions(statp, cp, "env"); statp->options |= RES_INIT; return (0); } static void res_setoptions(res_state statp, const char *options, const char *source) { const char *cp = options; int i; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_setoptions(\"%s\", \"%s\")...\n", options, source); #endif while (*cp) { /* skip leading and inner runs of spaces */ while (*cp == ' ' || *cp == '\t') cp++; /* search for and process individual options */ if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) { i = atoi(cp + sizeof("ndots:") - 1); if (i <= RES_MAXNDOTS) statp->ndots = i; else statp->ndots = RES_MAXNDOTS; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\tndots=%d\n", statp->ndots); #endif } else if (!strncmp(cp, "timeout:", sizeof("timeout:") - 1)) { i = atoi(cp + sizeof("timeout:") - 1); if (i <= RES_MAXRETRANS) statp->retrans = i; else statp->retrans = RES_MAXRETRANS; } else if (!strncmp(cp, "attempts:", sizeof("attempts:") - 1)){ i = atoi(cp + sizeof("attempts:") - 1); if (i <= RES_MAXRETRY) statp->retry = i; else statp->retry = RES_MAXRETRY; } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) { #ifdef DEBUG if (!(statp->options & RES_DEBUG)) { printf(";; res_setoptions(\"%s\", \"%s\")..\n", options, source); statp->options |= RES_DEBUG; } printf(";;\tdebug\n"); #endif } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) { statp->options |= RES_USE_INET6; } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) { statp->options |= RES_ROTATE; } else if (!strncmp(cp, "no-check-names", sizeof("no-check-names") - 1)) { statp->options |= RES_NOCHECKNAME; } else { /* XXX - print a warning here? */ } /* skip to next run of spaces */ while (*cp && *cp != ' ' && *cp != '\t') cp++; } } #ifdef RESOLVSORT /* XXX - should really support CIDR which means explicit masks always. */ static u_int32_t net_mask(in) /* XXX - should really use system's version of this */ struct in_addr in; { register u_int32_t i = ntohl(in.s_addr); if (IN_CLASSA(i)) return (htonl(IN_CLASSA_NET)); else if (IN_CLASSB(i)) return (htonl(IN_CLASSB_NET)); return (htonl(IN_CLASSC_NET)); } #endif u_int res_randomid(void) { struct timeval now; gettimeofday(&now, NULL); return (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid())); +} + +/* + * This routine is for closing the socket if a virtual circuit is used and + * the program wants to close it. This provides support for endhostent() + * which expects to close the socket. + * + * This routine is not expected to be user visible. + */ +void +res_nclose(res_state statp) { + int ns; + + if (statp->_vcsock >= 0) { + (void) close(statp->_vcsock); + statp->_vcsock = -1; + statp->_flags &= ~(RES_F_VC | RES_F_CONN); + } + for (ns = 0; ns < statp->_u._ext.nscount; ns++) { + if (statp->_u._ext.nssocks[ns] != -1) { + (void) close(statp->_u._ext.nssocks[ns]); + statp->_u._ext.nssocks[ns] = -1; + } + } + statp->_u._ext.nscount = 0; } Index: head/contrib/bind/lib/resolv/res_query.c =================================================================== --- head/contrib/bind/lib/resolv/res_query.c (revision 60940) +++ head/contrib/bind/lib/resolv/res_query.c (revision 60941) @@ -1,396 +1,407 @@ /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: res_query.c,v 8.19 1999/10/15 19:49:11 vixie Exp $"; +static const char rcsid[] = "$Id: res_query.c,v 8.20 2000/02/29 05:39:12 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Options. Leave them on. */ #define DEBUG #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ #else #define MAXPACKET 1024 #endif /* * Formulate a normal query, send, and await answer. * Returned answer is placed in supplied buffer "answer". * Perform preliminary check of answer, returning success only * if no error is indicated and the answer count is nonzero. * Return the size of the response on success, -1 on error. * Error number is left in H_ERRNO. * * Caller must parse answer and determine whether it answers the question. */ int res_nquery(res_state statp, const char *name, /* domain name */ int class, int type, /* class and type of query */ u_char *answer, /* buffer to put answer */ int anslen) /* size of answer buffer */ { u_char buf[MAXPACKET]; HEADER *hp = (HEADER *) answer; int n; hp->rcode = NOERROR; /* default */ #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, buf, sizeof(buf)); if (n <= 0) { #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n"); #endif RES_SET_H_ERRNO(statp, NO_RECOVERY); return (n); } n = res_nsend(statp, buf, n, answer, anslen); if (n < 0) { #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query: send error\n"); #endif RES_SET_H_ERRNO(statp, TRY_AGAIN); return (n); } if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount)); #endif switch (hp->rcode) { case NXDOMAIN: RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); break; case SERVFAIL: RES_SET_H_ERRNO(statp, TRY_AGAIN); break; case NOERROR: RES_SET_H_ERRNO(statp, NO_DATA); break; case FORMERR: case NOTIMP: case REFUSED: default: RES_SET_H_ERRNO(statp, NO_RECOVERY); break; } return (-1); } return (n); } /* * Formulate a normal query, send, and retrieve answer in supplied buffer. * Return the size of the response on success, -1 on error. * If enabled, implement search rules until answer or unrecoverable failure * is detected. Error code, if any, is left in H_ERRNO. */ int res_nsearch(res_state statp, const char *name, /* domain name */ int class, int type, /* class and type of query */ u_char *answer, /* buffer to put answer */ int anslen) /* size of answer */ { const char *cp, * const *domain; HEADER *hp = (HEADER *) answer; char tmp[NS_MAXDNAME]; u_int dots; - int trailing_dot, ret; + int trailing_dot, ret, saved_herrno; int got_nodata = 0, got_servfail = 0, root_on_list = 0; + int tried_as_is = 0; errno = 0; RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /* True if we never query. */ dots = 0; for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.'); trailing_dot = 0; if (cp > name && *--cp == '.') trailing_dot++; /* If there aren't any dots, it could be a user-level alias. */ if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) return (res_nquery(statp, cp, class, type, answer, anslen)); /* - * If there are enough dots in the name, do no searching. - * (The threshold can be set with the "ndots" option.) + * If there are enough dots in the name, let's just give it a + * try 'as is'. The threshold can be set with the "ndots" option. + * Also, query 'as is', if there is a trailing dot in the name. */ - if (dots >= statp->ndots || trailing_dot) - return (res_nquerydomain(statp, name, NULL, class, type, - answer, anslen)); + saved_herrno = -1; + if (dots >= statp->ndots || trailing_dot) { + ret = res_nquerydomain(statp, name, NULL, class, type, + answer, anslen); + if (ret > 0 || trailing_dot) + return (ret); + saved_herrno = h_errno; + tried_as_is++; + } /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ if ((!dots && (statp->options & RES_DEFNAMES) != 0) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0)) { int done = 0; for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++) { if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; ret = res_nquerydomain(statp, name, *domain, class, type, answer, anslen); if (ret > 0) return (ret); /* * If no server present, give up. * If name isn't found in this domain, * keep trying higher domains in the search list * (if that's enabled). * On a NO_DATA error, keep trying, otherwise * a wildcard entry of another type could keep us * from finding this entry higher in the domain. * If we get some other error (negative answer or * server failure), then stop searching up, * but try the input name below in case it's * fully-qualified. */ if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(statp, TRY_AGAIN); return (-1); } switch (statp->res_h_errno) { case NO_DATA: got_nodata++; /* FALLTHROUGH */ case HOST_NOT_FOUND: /* keep trying */ break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) { /* try next search element, if any */ got_servfail++; break; } /* FALLTHROUGH */ default: /* anything else implies that we're done */ done++; } /* if we got here for some reason other than DNSRCH, * we only wanted one iteration of the loop, so stop. */ if ((statp->options & RES_DNSRCH) == 0) done++; } } /* - * If the name has any dots at all, and "." is not on the search - * list, then try an as-is query now. + * If the name has any dots at all, and no earlier 'as-is' query + * for the name, and "." is not on the search list, then try an as-is + * query now. */ - if (statp->ndots) { + if (statp->ndots && !(tried_as_is || root_on_list)) { ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen); if (ret > 0) return (ret); } /* if we got here, we didn't satisfy the search. * if we did an initial full query, return that query's H_ERRNO * (note that we wouldn't be here if that query had succeeded). * else if we ever got a nodata, send that back as the reason. * else send back meaningless H_ERRNO, that being the one from * the last DNSRCH we did. */ - if (got_nodata) + if (saved_herrno != -1) + RES_SET_H_ERRNO(statp, saved_herrno); + else if (got_nodata) RES_SET_H_ERRNO(statp, NO_DATA); else if (got_servfail) RES_SET_H_ERRNO(statp, TRY_AGAIN); return (-1); } /* * Perform a call on res_query on the concatenation of name and domain, * removing a trailing dot from name if domain is NULL. */ int res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, /* class and type of query */ u_char *answer, /* buffer to put answer */ int anslen) /* size of answer */ { char nbuf[MAXDNAME]; const char *longname = nbuf; int n, d; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"", class, type); #endif if (domain == NULL) { /* * Check for trailing '.'; * copy without '.' if present. */ n = strlen(name); if (n >= MAXDNAME) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } n--; if (n >= 0 && name[n] == '.') { strncpy(nbuf, name, n); nbuf[n] = '\0'; } else longname = name; } else { n = strlen(name); d = strlen(domain); if (n + d + 1 >= MAXDNAME) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } sprintf(nbuf, "%s.%s", name, domain); } return (res_nquery(statp, longname, class, type, answer, anslen)); } const char * res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) { char *file, *cp1, *cp2; char buf[BUFSIZ]; FILE *fp; if (statp->options & RES_NOALIASES) return (NULL); file = getenv("HOSTALIASES"); if (file == NULL || (fp = fopen(file, "r")) == NULL) return (NULL); setbuf(fp, NULL); buf[sizeof(buf) - 1] = '\0'; while (fgets(buf, sizeof(buf), fp)) { for (cp1 = buf; *cp1 && !isspace(*cp1); ++cp1) ; if (!*cp1) break; *cp1 = '\0'; if (ns_samename(buf, name) == 1) { while (isspace(*++cp1)) ; if (!*cp1) break; for (cp2 = cp1 + 1; *cp2 && !isspace(*cp2); ++cp2) ; *cp2 = '\0'; strncpy(dst, cp1, siz - 1); dst[siz - 1] = '\0'; fclose(fp); return (dst); } } fclose(fp); return (NULL); } Index: head/contrib/bind/lib/resolv/res_send.c =================================================================== --- head/contrib/bind/lib/resolv/res_send.c (revision 60940) +++ head/contrib/bind/lib/resolv/res_send.c (revision 60941) @@ -1,890 +1,859 @@ /* * Copyright (c) 1985, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 THE REGENTS 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 REGENTS 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: res_send.c,v 8.36 1999/10/15 19:49:11 vixie Exp $"; +static const char rcsid[] = "$Id: res_send.c,v 8.38 2000/03/30 20:16:51 vixie Exp $"; #endif /* LIBC_SCCS and not lint */ /* * Send query to name server and wait for reply. */ #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Options. Leave them on. */ #define DEBUG #include "res_debug.h" +#define EXT(res) ((res)->_u._ext) + +static const int highestFD = FD_SETSIZE - 1; + +/* Forward. */ + +static int send_vc(res_state, const u_char *, int, + u_char *, int, int *, int); +static int send_dg(res_state, const u_char *, int, + u_char *, int, int *, int, + int *, int *); +static void Aerror(const res_state, FILE *, const char *, int, + struct sockaddr_in); +static void Perror(const res_state, FILE *, const char *, int); +static int sock_eq(struct sockaddr_in *, struct sockaddr_in *); #ifdef NEED_PSELECT static int pselect(int, void *, void *, void *, struct timespec *, const sigset_t *); #endif -#define CHECK_SRVR_ADDR - -#ifdef DEBUG - static void - Aerror(const res_state statp, FILE *file, const char *string, int error, - struct sockaddr_in address) - { - int save = errno; +/* Reachover. */ - if ((statp->options & RES_DEBUG) != 0) { - char tmp[sizeof "255.255.255.255"]; - - fprintf(file, "res_send: %s ([%s].%u): %s\n", - string, - inet_ntop(address.sin_family, &address.sin_addr, - tmp, sizeof tmp), - ntohs(address.sin_port), - strerror(error)); - } - errno = save; - } - static void - Perror(const res_state statp, FILE *file, const char *string, int error) { - int save = errno; - - if ((statp->options & RES_DEBUG) != 0) - fprintf(file, "res_send: %s: %s\n", - string, strerror(error)); - errno = save; - } -#endif - -static int cmpsock(struct sockaddr_in *a1, struct sockaddr_in *a2); void res_pquery(const res_state, const u_char *, int, FILE *); +/* Public. */ + /* int * res_isourserver(ina) * looks up "ina" in _res.ns_addr_list[] * returns: * 0 : not found * >0 : found * author: * paul vixie, 29may94 */ int res_ourserver_p(const res_state statp, const struct sockaddr_in *inp) { struct sockaddr_in ina; int ns; ina = *inp; - for (ns = 0; ns < statp->nscount; ns++) { + for (ns = 0; ns < statp->nscount; ns++) { const struct sockaddr_in *srv = &statp->nsaddr_list[ns]; if (srv->sin_family == ina.sin_family && srv->sin_port == ina.sin_port && (srv->sin_addr.s_addr == INADDR_ANY || srv->sin_addr.s_addr == ina.sin_addr.s_addr)) return (1); } return (0); } /* int * res_nameinquery(name, type, class, buf, eom) * look for (name,type,class) in the query section of packet (buf,eom) * requires: * buf + HFIXEDSZ <= eom * returns: * -1 : format error * 0 : not found * >0 : found * author: * paul vixie, 29may94 */ int res_nameinquery(const char *name, int type, int class, const u_char *buf, const u_char *eom) { const u_char *cp = buf + HFIXEDSZ; int qdcount = ntohs(((HEADER*)buf)->qdcount); while (qdcount-- > 0) { char tname[MAXDNAME+1]; int n, ttype, tclass; n = dn_expand(buf, eom, cp, tname, sizeof tname); if (n < 0) return (-1); cp += n; if (cp + 2 * INT16SZ > eom) return (-1); ttype = ns_get16(cp); cp += INT16SZ; tclass = ns_get16(cp); cp += INT16SZ; if (ttype == type && tclass == class && ns_samename(tname, name) == 1) return (1); } return (0); } /* int * res_queriesmatch(buf1, eom1, buf2, eom2) * is there a 1:1 mapping of (name,type,class) * in (buf1,eom1) and (buf2,eom2)? * returns: * -1 : format error * 0 : not a 1:1 mapping * >0 : is a 1:1 mapping * author: * paul vixie, 29may94 */ int res_queriesmatch(const u_char *buf1, const u_char *eom1, const u_char *buf2, const u_char *eom2) { const u_char *cp = buf1 + HFIXEDSZ; int qdcount = ntohs(((HEADER*)buf1)->qdcount); if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2) return (-1); /* * Only header section present in replies to * dynamic update packets. */ - if ( (((HEADER *)buf1)->opcode == ns_o_update) && - (((HEADER *)buf2)->opcode == ns_o_update) ) + if ((((HEADER *)buf1)->opcode == ns_o_update) && + (((HEADER *)buf2)->opcode == ns_o_update)) return (1); if (qdcount != ntohs(((HEADER*)buf2)->qdcount)) return (0); while (qdcount-- > 0) { char tname[MAXDNAME+1]; int n, ttype, tclass; n = dn_expand(buf1, eom1, cp, tname, sizeof tname); if (n < 0) return (-1); cp += n; if (cp + 2 * INT16SZ > eom1) return (-1); ttype = ns_get16(cp); cp += INT16SZ; tclass = ns_get16(cp); cp += INT16SZ; if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) return (0); } return (1); } int res_nsend(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz) { - HEADER *hp = (HEADER *) buf; - HEADER *anhp = (HEADER *) ans; - int gotsomewhere, connreset, terrno, try, v_circuit, resplen, ns, n; - u_int badns; /* XXX NSMAX can't exceed #/bits in this variable */ - static int highestFD = FD_SETSIZE - 1; + int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; + if (statp->nscount == 0) { + errno = ESRCH; + return (-1); + } if (anssiz < HFIXEDSZ) { errno = EINVAL; return (-1); } DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), (stdout, ";; res_send()\n"), buf, buflen); v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; gotsomewhere = 0; - connreset = 0; terrno = ETIMEDOUT; - badns = 0; /* - * Some callers want to even out the load on their resolver list. + * If the ns_addr_list in the resolver context has changed, then + * invalidate our cached copy and the associated timing data. */ - if (statp->nscount > 0 && (statp->options & RES_ROTATE) != 0) { + if (EXT(statp).nscount != 0) { + int needclose = 0; + + if (EXT(statp).nscount != statp->nscount) + needclose++; + else + for (ns = 0; ns < statp->nscount; ns++) + if (!sock_eq(&statp->nsaddr_list[ns], + &EXT(statp).nsaddrs[ns])) { + needclose++; + break; + } + if (needclose) + res_nclose(statp); + } + + /* + * Maybe initialize our private copy of the ns_addr_list. + */ + if (EXT(statp).nscount == 0) { + for (ns = 0; ns < statp->nscount; ns++) { + EXT(statp).nsaddrs[ns] = statp->nsaddr_list[ns]; + EXT(statp).nstimes[ns] = RES_MAXTIME; + EXT(statp).nssocks[ns] = -1; + } + EXT(statp).nscount = statp->nscount; + } + + /* + * Some resolvers want to even out the load on their nameservers. + * Note that RES_BLAST overrides RES_ROTATE. + */ + if ((statp->options & RES_ROTATE) != 0 && + (statp->options & RES_BLAST) == 0) { struct sockaddr_in ina; int lastns = statp->nscount - 1; ina = statp->nsaddr_list[0]; for (ns = 0; ns < lastns; ns++) statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1]; statp->nsaddr_list[lastns] = ina; } /* - * Send request, RETRY times, or until successful + * Send request, RETRY times, or until successful. */ for (try = 0; try < statp->retry; try++) { for (ns = 0; ns < statp->nscount; ns++) { struct sockaddr_in *nsap = &statp->nsaddr_list[ns]; same_ns: - if (badns & (1 << ns)) { - res_nclose(statp); - goto next_ns; - } - if (statp->qhook) { int done = 0, loops = 0; do { res_sendhookact act; act = (*statp->qhook)(&nsap, &buf, &buflen, ans, anssiz, &resplen); switch (act) { case res_goahead: done = 1; break; case res_nextns: res_nclose(statp); goto next_ns; case res_done: return (resplen); case res_modified: /* give the hook another try */ if (++loops < 42) /*doug adams*/ break; /*FALLTHROUGH*/ case res_error: /*FALLTHROUGH*/ default: return (-1); } } while (!done); } Dprint(statp->options & RES_DEBUG, (stdout, ";; Querying server (# %d) address = %s\n", ns + 1, inet_ntoa(nsap->sin_addr))); if (v_circuit) { - int truncated; - struct iovec iov[2]; - u_short len; - u_char *cp; - /* Use VC; at most one attempt per server. */ try = statp->retry; - truncated = 0; - - /* Are we still talking to whom we want to talk to? */ - if (statp->_sock >= 0 && - (statp->_flags & RES_F_VC) != 0) { - struct sockaddr_in peer; - int size = sizeof(peer); - - if (getpeername(statp->_sock, - (struct sockaddr *)&peer, - &size) < 0) { - res_nclose(statp); - statp->_flags &= ~RES_F_VC; - } else if (!cmpsock(&peer, nsap)) { - res_nclose(statp); - statp->_flags &= ~RES_F_VC; - } - } - - if (statp->_sock < 0 || - (statp->_flags & RES_F_VC) == 0) { - if (statp->_sock >= 0) - res_nclose(statp); - - statp->_sock = socket(PF_INET, - SOCK_STREAM, 0); - if (statp->_sock < 0 || - statp->_sock > highestFD) { - terrno = errno; - Perror(statp, stderr, - "socket(vc)", errno); - return (-1); - } - errno = 0; - if (connect(statp->_sock, - (struct sockaddr *)nsap, - sizeof *nsap) < 0) { - terrno = errno; - Aerror(statp, stderr, "connect/vc", - errno, *nsap); - badns |= (1 << ns); - res_nclose(statp); - goto next_ns; - } - statp->_flags |= RES_F_VC; - } - /* - * Send length & message - */ - putshort((u_short)buflen, (u_char*)&len); - iov[0].iov_base = (caddr_t)&len; - iov[0].iov_len = INT16SZ; - iov[1].iov_base = (caddr_t)buf; - iov[1].iov_len = buflen; - if (writev(statp->_sock, iov, 2) != - (INT16SZ + buflen)) { - terrno = errno; - Perror(statp, stderr, "write failed", errno); - badns |= (1 << ns); - res_nclose(statp); + n = send_vc(statp, buf, buflen, ans, anssiz, &terrno, + ns); + if (n < 0) + return (-1); + if (n == 0) goto next_ns; - } - /* - * Receive length & response - */ - read_len: - cp = ans; - len = INT16SZ; - while ((n = read(statp->_sock, - (char *)cp, (int)len)) > 0) { - cp += n; - if ((len -= n) <= 0) - break; - } - if (n <= 0) { - terrno = errno; - Perror(statp, stderr, "read failed", errno); - res_nclose(statp); - /* - * A long running process might get its TCP - * connection reset if the remote server was - * restarted. Requery the server instead of - * trying a new one. When there is only one - * server, this means that a query might work - * instead of failing. We only allow one reset - * per query to prevent looping. - */ - if (terrno == ECONNRESET && !connreset) { - connreset = 1; - res_nclose(statp); - goto same_ns; - } - res_nclose(statp); - goto next_ns; - } - resplen = ns_get16(ans); - if (resplen > anssiz) { - Dprint(statp->options & RES_DEBUG, - (stdout, ";; response truncated\n") - ); - truncated = 1; - len = anssiz; - } else - len = resplen; - if (len < HFIXEDSZ) { - /* - * Undersized message. - */ - Dprint(statp->options & RES_DEBUG, - (stdout, ";; undersized: %d\n", len)); - terrno = EMSGSIZE; - badns |= (1 << ns); - res_nclose(statp); - goto next_ns; - } - cp = ans; - while (len != 0 && - (n = read(statp->_sock, (char *)cp, (int)len)) - > 0) { - cp += n; - len -= n; - } - if (n <= 0) { - terrno = errno; - Perror(statp, stderr, "read(vc)", errno); - res_nclose(statp); - goto next_ns; - } - if (truncated) { - /* - * Flush rest of answer - * so connection stays in synch. - */ - anhp->tc = 1; - len = resplen - anssiz; - while (len != 0) { - char junk[PACKETSZ]; - - n = (len > sizeof(junk) - ? sizeof(junk) - : len); - n = read(statp->_sock, junk, n); - if (n > 0) - len -= n; - else - break; - } - } - /* - * The calling applicating has bailed out of - * a previous call and failed to arrange to have - * the circuit closed or the server has got - * itself confused. Anyway drop the packet and - * wait for the correct one. - */ - if (hp->id != anhp->id) { - DprintQ((statp->options & RES_DEBUG) || - (statp->pfcode & RES_PRF_REPLY), - (stdout, ";; old answer (unexpected):\n"), - ans, (resplen>anssiz)?anssiz:resplen); - goto read_len; - } + resplen = n; } else { - /* - * Use datagrams. - */ - struct timespec start, timeout, finish; - fd_set dsmask; - struct sockaddr_in from; - int fromlen, seconds; - - if (statp->_sock < 0 || - (statp->_flags & RES_F_VC) != 0) { - if ((statp->_flags & RES_F_VC) != 0) - res_nclose(statp); - statp->_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (statp->_sock < 0 || - statp->_sock > highestFD) { -#ifndef CAN_RECONNECT - bad_dg_sock: -#endif - terrno = errno; - Perror(statp, stderr, - "socket(dg)", errno); - return (-1); - } - statp->_flags &= ~RES_F_CONN; - } -#ifndef CANNOT_CONNECT_DGRAM - /* - * On a 4.3BSD+ machine (client and server, - * actually), sending to a nameserver datagram - * port with no nameserver will cause an - * ICMP port unreachable message to be returned. - * If our datagram socket is "connected" to the - * server, we get an ECONNREFUSED error on the next - * socket operation, and select returns if the - * error message is received. We can thus detect - * the absence of a nameserver without timing out. - * If we have sent queries to at least two servers, - * however, we don't want to remain connected, - * as we wish to receive answers from the first - * server to respond. - */ - if (statp->nscount == 1 || (try == 0 && ns == 0)) { - /* - * Connect only if we are sure we won't - * receive a response from another server. - */ - if ((statp->_flags & RES_F_CONN) == 0) { - if (connect(statp->_sock, - (struct sockaddr *)nsap, - sizeof *nsap) < 0) { - Aerror(statp, stderr, - "connect(dg)", - errno, *nsap); - badns |= (1 << ns); - res_nclose(statp); - goto next_ns; - } - statp->_flags |= RES_F_CONN; - } - if (send(statp->_sock, (char*)buf, buflen, 0) - != buflen) { - Perror(statp, stderr, "send", errno); - badns |= (1 << ns); - res_nclose(statp); - goto next_ns; - } - } else { - /* - * Disconnect if we want to listen - * for responses from more than one server. - */ - if ((statp->_flags & RES_F_CONN) != 0) { -#ifdef CAN_RECONNECT - struct sockaddr_in no_addr; - - no_addr.sin_family = AF_INET; - no_addr.sin_addr.s_addr = INADDR_ANY; - no_addr.sin_port = 0; - (void) connect(statp->_sock, - (struct sockaddr *) - &no_addr, - sizeof no_addr); -#else - struct sockaddr_in local_addr; - int len, result, s1; - - len = sizeof(local_addr); - s1 = socket(PF_INET, SOCK_DGRAM, 0); - result = getsockname(statp->_sock, - (struct sockaddr *)&local_addr, - &len); - if (s1 < 0) - goto bad_dg_sock; - (void) dup2(s1, statp->_sock); - (void) close(s1); - if (result == 0) { - /* - * Attempt to rebind to old - * port. Note connected socket - * has an sin_addr set. - */ - local_addr.sin_addr.s_addr = - htonl(0); - (void)bind(statp->_sock, - (struct sockaddr *) - &local_addr, len); - } - Dprint(statp->options & RES_DEBUG, - (stdout, ";; new DG socket\n")) -#endif /* CAN_RECONNECT */ - statp->_flags &= ~RES_F_CONN; - errno = 0; - } -#endif /* !CANNOT_CONNECT_DGRAM */ - if (sendto(statp->_sock, - (char*)buf, buflen, 0, - (struct sockaddr *)nsap, - sizeof *nsap) - != buflen) { - Aerror(statp, stderr, "sendto", errno, *nsap); - badns |= (1 << ns); - res_nclose(statp); - goto next_ns; - } -#ifndef CANNOT_CONNECT_DGRAM - } -#endif /* !CANNOT_CONNECT_DGRAM */ - - if (statp->_sock < 0 || statp->_sock > highestFD) { - Perror(statp, stderr, - "fd out-of-bounds", EMFILE); - res_nclose(statp); + /* Use datagrams. */ + n = send_dg(statp, buf, buflen, ans, anssiz, &terrno, + ns, &v_circuit, &gotsomewhere); + if (n < 0) + return (-1); + if (n == 0) goto next_ns; - } - - /* - * Wait for reply - */ - seconds = (statp->retrans << try); - if (try > 0) - seconds /= statp->nscount; - if (seconds <= 0) - seconds = 1; - start = evNowTime(); - timeout = evConsTime(seconds, 0); - finish = evAddTime(start, timeout); - wait: - FD_ZERO(&dsmask); - FD_SET(statp->_sock, &dsmask); - n = pselect(statp->_sock + 1, - &dsmask, NULL, NULL, - &timeout, NULL); - if (n == 0) { - Dprint(statp->options & RES_DEBUG, - (stdout, ";; timeout\n")); - gotsomewhere = 1; - goto next_ns; - } - if (n < 0) { - if (errno == EINTR) { - struct timespec now; - - now = evNowTime(); - if (evCmpTime(finish, now) >= 0) { - timeout = evSubTime(finish, - now); - goto wait; - } - } - Perror(statp, stderr, "select", errno); - res_nclose(statp); - goto next_ns; - } - errno = 0; - fromlen = sizeof(struct sockaddr_in); - resplen = recvfrom(statp->_sock, (char*)ans, anssiz,0, - (struct sockaddr *)&from, &fromlen); - if (resplen <= 0) { - Perror(statp, stderr, "recvfrom", errno); - res_nclose(statp); - goto next_ns; - } - gotsomewhere = 1; - if (resplen < HFIXEDSZ) { - /* - * Undersized message. - */ - Dprint(statp->options & RES_DEBUG, - (stdout, ";; undersized: %d\n", - resplen)); - terrno = EMSGSIZE; - badns |= (1 << ns); - res_nclose(statp); - goto next_ns; - } - if (hp->id != anhp->id) { - /* - * response from old query, ignore it. - * XXX - potential security hazard could - * be detected here. - */ - DprintQ((statp->options & RES_DEBUG) || - (statp->pfcode & RES_PRF_REPLY), - (stdout, ";; old answer:\n"), - ans, (resplen>anssiz)?anssiz:resplen); - goto wait; - } -#ifdef CHECK_SRVR_ADDR - if (!(statp->options & RES_INSECURE1) && - !res_ourserver_p(statp, &from)) { - /* - * response from wrong server? ignore it. - * XXX - potential security hazard could - * be detected here. - */ - DprintQ((statp->options & RES_DEBUG) || - (statp->pfcode & RES_PRF_REPLY), - (stdout, ";; not our server:\n"), - ans, (resplen>anssiz)?anssiz:resplen); - goto wait; - } -#endif - if (!(statp->options & RES_INSECURE2) && - !res_queriesmatch(buf, buf + buflen, - ans, ans + anssiz)) { - /* - * response contains wrong query? ignore it. - * XXX - potential security hazard could - * be detected here. - */ - DprintQ((statp->options & RES_DEBUG) || - (statp->pfcode & RES_PRF_REPLY), - (stdout, ";; wrong query name:\n"), - ans, (resplen>anssiz)?anssiz:resplen); - goto wait; - } - if (anhp->rcode == SERVFAIL || - anhp->rcode == NOTIMP || - anhp->rcode == REFUSED) { - DprintQ(statp->options & RES_DEBUG, - (stdout, "server rejected query:\n"), - ans, (resplen>anssiz)?anssiz:resplen); - badns |= (1 << ns); - res_nclose(statp); - /* don't retry if called from dig */ - if (!statp->pfcode) - goto next_ns; - } - if (!(statp->options & RES_IGNTC) && anhp->tc) { - /* - * get rest of answer; - * use TCP with same server. - */ - Dprint(statp->options & RES_DEBUG, - (stdout, ";; truncated answer\n")); - v_circuit = 1; - res_nclose(statp); + if (v_circuit) goto same_ns; - } - } /*if vc/dg*/ + resplen = n; + } + Dprint((statp->options & RES_DEBUG) || ((statp->pfcode & RES_PRF_REPLY) && (statp->pfcode & RES_PRF_HEAD1)), (stdout, ";; got answer:\n")); + DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ""), - ans, (resplen>anssiz)?anssiz:resplen); + ans, (resplen > anssiz) ? anssiz : resplen); + /* - * If using virtual circuits, we assume that the first server - * is preferred over the rest (i.e. it is on the local - * machine) and only keep that one open. * If we have temporarily opened a virtual circuit, * or if we haven't been asked to keep a socket open, * close the socket. */ - if ((v_circuit && (!(statp->options & RES_USEVC) || ns != 0)) || - !(statp->options & RES_STAYOPEN)) { + if (v_circuit && (statp->options & RES_USEVC) == 0 || + (statp->options & RES_STAYOPEN) == 0) { res_nclose(statp); } if (statp->rhook) { int done = 0, loops = 0; do { res_sendhookact act; act = (*statp->rhook)(nsap, buf, buflen, ans, anssiz, &resplen); switch (act) { case res_goahead: case res_done: done = 1; break; case res_nextns: res_nclose(statp); goto next_ns; case res_modified: /* give the hook another try */ if (++loops < 42) /*doug adams*/ break; /*FALLTHROUGH*/ case res_error: /*FALLTHROUGH*/ default: return (-1); } } while (!done); } return (resplen); next_ns: ; } /*foreach ns*/ } /*foreach retry*/ res_nclose(statp); if (!v_circuit) { if (!gotsomewhere) errno = ECONNREFUSED; /* no nameservers found */ else errno = ETIMEDOUT; /* no answer obtained */ } else errno = terrno; return (-1); } -/* - * This routine is for closing the socket if a virtual circuit is used and - * the program wants to close it. This provides support for endhostent() - * which expects to close the socket. - * - * This routine is not expected to be user visible. - */ -void -res_nclose(res_state statp) { - if (statp->_sock >= 0) { - (void) close(statp->_sock); - statp->_sock = -1; - statp->_flags &= ~(RES_F_VC | RES_F_CONN); +/* Private */ + +static int +send_vc(res_state statp, + const u_char *buf, int buflen, u_char *ans, int anssiz, + int *terrno, int ns) +{ + const HEADER *hp = (HEADER *) buf; + HEADER *anhp = (HEADER *) ans; + struct sockaddr_in *nsap = &statp->nsaddr_list[ns]; + int truncating, connreset, resplen, n; + struct iovec iov[2]; + u_short len; + u_char *cp; + + connreset = 0; + same_ns: + truncating = 0; + + /* Are we still talking to whom we want to talk to? */ + if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) { + struct sockaddr_in peer; + int size = sizeof peer; + + if (getpeername(statp->_vcsock, + (struct sockaddr *)&peer, &size) < 0 || + !sock_eq(&peer, nsap)) { + res_nclose(statp); + statp->_flags &= ~RES_F_VC; + } } + + if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) { + if (statp->_vcsock >= 0) + res_nclose(statp); + + statp->_vcsock = socket(PF_INET, SOCK_STREAM, 0); + if (statp->_vcsock > highestFD) { + res_nclose(statp); + errno = ENOTSOCK; + } + if (statp->_vcsock < 0) { + *terrno = errno; + Perror(statp, stderr, "socket(vc)", errno); + return (-1); + } + errno = 0; + if (connect(statp->_vcsock, (struct sockaddr *)nsap, + sizeof *nsap) < 0) { + *terrno = errno; + Aerror(statp, stderr, "connect/vc", errno, *nsap); + res_nclose(statp); + return (0); + } + statp->_flags |= RES_F_VC; + } + + /* + * Send length & message + */ + putshort((u_short)buflen, (u_char*)&len); + iov[0] = evConsIovec(&len, INT16SZ); + iov[1] = evConsIovec((void*)buf, buflen); + if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) { + *terrno = errno; + Perror(statp, stderr, "write failed", errno); + res_nclose(statp); + return (0); + } + /* + * Receive length & response + */ + read_len: + cp = ans; + len = INT16SZ; + while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) { + cp += n; + if ((len -= n) <= 0) + break; + } + if (n <= 0) { + *terrno = errno; + Perror(statp, stderr, "read failed", errno); + res_nclose(statp); + /* + * A long running process might get its TCP + * connection reset if the remote server was + * restarted. Requery the server instead of + * trying a new one. When there is only one + * server, this means that a query might work + * instead of failing. We only allow one reset + * per query to prevent looping. + */ + if (*terrno == ECONNRESET && !connreset) { + connreset = 1; + res_nclose(statp); + goto same_ns; + } + res_nclose(statp); + return (0); + } + resplen = ns_get16(ans); + if (resplen > anssiz) { + Dprint(statp->options & RES_DEBUG, + (stdout, ";; response truncated\n") + ); + truncating = 1; + len = anssiz; + } else + len = resplen; + if (len < HFIXEDSZ) { + /* + * Undersized message. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; undersized: %d\n", len)); + *terrno = EMSGSIZE; + res_nclose(statp); + return (0); + } + cp = ans; + while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){ + cp += n; + len -= n; + } + if (n <= 0) { + *terrno = errno; + Perror(statp, stderr, "read(vc)", errno); + res_nclose(statp); + return (0); + } + if (truncating) { + /* + * Flush rest of answer so connection stays in synch. + */ + anhp->tc = 1; + len = resplen - anssiz; + while (len != 0) { + char junk[PACKETSZ]; + + n = read(statp->_vcsock, junk, + (len > sizeof junk) ? sizeof junk : len); + if (n > 0) + len -= n; + else + break; + } + } + /* + * If the calling applicating has bailed out of + * a previous call and failed to arrange to have + * the circuit closed or the server has got + * itself confused, then drop the packet and + * wait for the correct one. + */ + if (hp->id != anhp->id) { + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; old answer (unexpected):\n"), + ans, (resplen > anssiz) ? anssiz: resplen); + goto read_len; + } + + /* + * All is well, or the error is fatal. Signal that the + * next nameserver ought not be tried. + */ + return (resplen); } -/* Private */ static int -cmpsock(struct sockaddr_in *a1, struct sockaddr_in *a2) { +send_dg(res_state statp, + const u_char *buf, int buflen, u_char *ans, int anssiz, + int *terrno, int ns, int *v_circuit, int *gotsomewhere) +{ + const HEADER *hp = (HEADER *) buf; + HEADER *anhp = (HEADER *) ans; + const struct sockaddr_in *nsap = &statp->nsaddr_list[ns]; + struct timespec now, timeout, finish; + fd_set dsmask; + struct sockaddr_in from; + int fromlen, resplen, seconds, n, s; + + if (EXT(statp).nssocks[ns] == -1) { + EXT(statp).nssocks[ns] = socket(PF_INET, SOCK_DGRAM, 0); + if (EXT(statp).nssocks[ns] > highestFD) { + res_nclose(statp); + errno = ENOTSOCK; + } + if (EXT(statp).nssocks[ns] < 0) { + *terrno = errno; + Perror(statp, stderr, "socket(dg)", errno); + return (-1); + } +#ifndef CANNOT_CONNECT_DGRAM + /* + * On a 4.3BSD+ machine (client and server, + * actually), sending to a nameserver datagram + * port with no nameserver will cause an + * ICMP port unreachable message to be returned. + * If our datagram socket is "connected" to the + * server, we get an ECONNREFUSED error on the next + * socket operation, and select returns if the + * error message is received. We can thus detect + * the absence of a nameserver without timing out. + */ + if (connect(EXT(statp).nssocks[ns], (struct sockaddr *)nsap, + sizeof *nsap) < 0) { + Aerror(statp, stderr, "connect(dg)", errno, *nsap); + res_nclose(statp); + return (0); + } +#endif /* !CANNOT_CONNECT_DGRAM */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; new DG socket\n")) + } + s = EXT(statp).nssocks[ns]; +#ifndef CANNOT_CONNECT_DGRAM + if (send(s, (char*)buf, buflen, 0) != buflen) { + Perror(statp, stderr, "send", errno); + res_nclose(statp); + return (0); + } +#else /* !CANNOT_CONNECT_DGRAM */ + if (sendto(s, (char*)buf, buflen, 0, + (struct sockaddr *)nsap, sizeof *nsap) != buflen) + { + Aerror(statp, stderr, "sendto", errno, *nsap); + res_nclose(statp); + return (0); + } +#endif /* !CANNOT_CONNECT_DGRAM */ + + /* + * Wait for reply. + */ + seconds = (statp->retrans << ns); + if (ns > 0) + seconds /= statp->nscount; + if (seconds <= 0) + seconds = 1; + now = evNowTime(); + timeout = evConsTime(seconds, 0); + finish = evAddTime(now, timeout); + wait: + FD_ZERO(&dsmask); + FD_SET(s, &dsmask); + n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL); + if (n == 0) { + Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); + *gotsomewhere = 1; + return (0); + } + if (n < 0) { + if (errno == EINTR) { + now = evNowTime(); + if (evCmpTime(finish, now) > 0) { + timeout = evSubTime(finish, now); + goto wait; + } + } + Perror(statp, stderr, "select", errno); + res_nclose(statp); + return (0); + } + errno = 0; + fromlen = sizeof(struct sockaddr_in); + resplen = recvfrom(s, (char*)ans, anssiz,0, + (struct sockaddr *)&from, &fromlen); + if (resplen <= 0) { + Perror(statp, stderr, "recvfrom", errno); + res_nclose(statp); + return (0); + } + *gotsomewhere = 1; + if (resplen < HFIXEDSZ) { + /* + * Undersized message. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; undersized: %d\n", + resplen)); + *terrno = EMSGSIZE; + res_nclose(statp); + return (0); + } + if (hp->id != anhp->id) { + /* + * response from old query, ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; old answer:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (!(statp->options & RES_INSECURE1) && + !res_ourserver_p(statp, &from)) { + /* + * response from wrong server? ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; not our server:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (!(statp->options & RES_INSECURE2) && + !res_queriesmatch(buf, buf + buflen, + ans, ans + anssiz)) { + /* + * response contains wrong query? ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; wrong query name:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (anhp->rcode == SERVFAIL || + anhp->rcode == NOTIMP || + anhp->rcode == REFUSED) { + DprintQ(statp->options & RES_DEBUG, + (stdout, "server rejected query:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + res_nclose(statp); + /* don't retry if called from dig */ + if (!statp->pfcode) + return (0); + } + if (!(statp->options & RES_IGNTC) && anhp->tc) { + /* + * To get the rest of answer, + * use TCP with same server. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; truncated answer\n")); + *v_circuit = 1; + res_nclose(statp); + return (1); + } + /* + * All is well, or the error is fatal. Signal that the + * next nameserver ought not be tried. + */ + return (resplen); +} + +static void +Aerror(const res_state statp, FILE *file, const char *string, int error, + struct sockaddr_in address) +{ + int save = errno; + + if ((statp->options & RES_DEBUG) != 0) { + char tmp[sizeof "255.255.255.255"]; + + fprintf(file, "res_send: %s ([%s].%u): %s\n", + string, + inet_ntop(address.sin_family, &address.sin_addr, + tmp, sizeof tmp), + ntohs(address.sin_port), + strerror(error)); + } + errno = save; +} + +static void +Perror(const res_state statp, FILE *file, const char *string, int error) { + int save = errno; + + if ((statp->options & RES_DEBUG) != 0) + fprintf(file, "res_send: %s: %s\n", + string, strerror(error)); + errno = save; +} + +static int +sock_eq(struct sockaddr_in *a1, struct sockaddr_in *a2) { return ((a1->sin_family == a2->sin_family) && (a1->sin_port == a2->sin_port) && (a1->sin_addr.s_addr == a2->sin_addr.s_addr)); } #ifdef NEED_PSELECT /* XXX needs to move to the porting library. */ static int pselect(int nfds, void *rfds, void *wfds, void *efds, - struct timespec *tsp, - const sigset_t *sigmask) + struct timespec *tsp, const sigset_t *sigmask) { struct timeval tv, *tvp; sigset_t sigs; int n; if (tsp) { tvp = &tv; tv = evTimeVal(*tsp); } else tvp = NULL; if (sigmask) sigprocmask(SIG_SETMASK, sigmask, &sigs); n = select(nfds, rfds, wfds, efds, tvp); if (sigmask) sigprocmask(SIG_SETMASK, &sigs, NULL); if (tsp) *tsp = evTimeSpec(tv); return (n); } #endif Index: head/contrib/bind/port/freebsd/include/port_before.h =================================================================== --- head/contrib/bind/port/freebsd/include/port_before.h (revision 60940) +++ head/contrib/bind/port/freebsd/include/port_before.h (revision 60941) @@ -1,14 +1,103 @@ #define WANT_IRS_NIS #define WANT_IRS_PW #define WANT_IRS_GR #define SIG_FN void +#define HAS_PTHREADS #define ts_sec tv_sec #define ts_nsec tv_nsec #if defined(HAS_PTHREADS) && defined(_REENTRANT) #define DO_PTHREADS #endif #if defined (__FreeBSD__) && __FreeBSD__>=3 #define SETPWENT_VOID #endif + +#include + +#define GROUP_R_RETURN struct group * +#define GROUP_R_SET_RETURN void +#undef GROUP_R_SET_RESULT /*empty*/ +#define GROUP_R_END_RETURN void +#define GROUP_R_END_RESULT(x) /*empty*/ +#define GROUP_R_ARGS char *buf, int buflen +#undef GROUP_R_ENT_ARGS /*empty*/ +#define GROUP_R_OK gptr +#define GROUP_R_BAD NULL + +#define HOST_R_RETURN struct hostent * +#define HOST_R_SET_RETURN void +#undef HOST_R_SET_RESULT /*empty*/ +#define HOST_R_END_RETURN void +#define HOST_R_END_RESULT(x) /*empty*/ +#define HOST_R_ARGS char *buf, int buflen, int *h_errnop +#undef HOST_R_ENT_ARGS /*empty*/ +#define HOST_R_COPY buf, buflen +#define HOST_R_COPY_ARGS char *buf, int buflen +#define HOST_R_ERRNO *h_errnop = h_errno +#define HOST_R_OK hptr +#define HOST_R_BAD NULL + +#define NET_R_RETURN struct netent * +#define NET_R_SET_RETURN void +#undef NET_R_SET_RESULT /*empty*/ +#define NET_R_END_RETURN void +#define NET_R_END_RESULT(x) /*empty*/ +#define NET_R_ARGS char *buf, int buflen +#undef NET_R_ENT_ARGS /*empty*/ +#define NET_R_COPY buf, buflen +#define NET_R_COPY_ARGS NET_R_ARGS +#define NET_R_OK nptr +#define NET_R_BAD NULL + +#define NGR_R_RETURN int +#define NGR_R_SET_RETURN void +#undef NGR_R_SET_RESULT /*empty*/ +#define NGR_R_END_RETURN void +#undef NGR_R_END_RESULT /*empty*/ +#define NGR_R_ARGS char *buf, int buflen +#undef NGR_R_ENT_ARGS /*empty*/ +#define NGR_R_COPY buf, buflen +#define NGR_R_COPY_ARGS NGR_R_ARGS +#define NGR_R_OK 1 +#define NGR_R_BAD (0) + +#define PROTO_R_RETURN struct protoent * +#define PROTO_R_SET_RETURN void +#undef PROTO_R_SET_RESULT /*empty*/ +#define PROTO_R_END_RETURN void +#define PROTO_R_END_RESULT(x) /*empty*/ +#define PROTO_R_ARGS char *buf, int buflen +#undef PROTO_R_ENT_ARGS /*empty*/ +#define PROTO_R_COPY buf, buflen +#define PROTO_R_COPY_ARGS PROTO_R_ARGS +#define PROTO_R_OK pptr +#define PROTO_R_BAD NULL + +#define PASS_R_RETURN struct passwd * +#define PASS_R_SET_RETURN void +#undef PASS_R_SET_RESULT /*empty*/ +#define PASS_R_END_RETURN void +#define PASS_R_END_RESULT(x) /*empty*/ +#define PASS_R_ARGS char *buf, int buflen +#undef PASS_R_ENT_ARGS /*empty*/ +#define PASS_R_COPY buf, buflen +#define PASS_R_COPY_ARGS PASS_R_ARGS +#define PASS_R_OK pwptr +#define PASS_R_BAD NULL + +#define SERV_R_RETURN struct servent * +#define SERV_R_SET_RETURN void +#undef SERV_R_SET_RESULT /*empty*/ +#define SERV_R_END_RETURN void +#define SERV_R_END_RESULT(x) /*empty*/ +#define SERV_R_ARGS char *buf, int buflen +#undef SERV_R_ENT_ARGS /*empty*/ +#define SERV_R_COPY buf, buflen +#define SERV_R_COPY_ARGS SERV_R_ARGS +#define SERV_R_OK sptr +#define SERV_R_BAD NULL + +#define IRS_LCL_SV_DB + Index: head/contrib/bind/port/systype =================================================================== --- head/contrib/bind/port/systype (revision 60940) +++ head/contrib/bind/port/systype (revision 60941) @@ -1,31 +1,31 @@ #!/bin/sh cachefile=${1-//} if [ -f $cachefile ]; then echo "Using $cachefile" >&2 exec cat $cachefile fi case $cachefile in /*) ;; *) cachefile=`pwd`/$cachefile ;; esac -cd `dirname $0` > /dev/null +cd `dirname $0` for systype in [a-z]*; do if [ -f $systype/probe ]; then if sh $systype/probe; then case $cachefile in //) ;; *) echo "Making $cachefile" >&2 echo $systype > $cachefile ;; esac exec echo $systype fi fi done echo unknown exit 0