pax_global_header00006660000000000000000000000064142507221320014510gustar00rootroot0000000000000052 comment=f0d8d70b7f44c4f1511dea1ab53047ec3071f5f4 yaku-ns-0.2/000077500000000000000000000000001425072213200127405ustar00rootroot00000000000000yaku-ns-0.2/.gitignore000066400000000000000000000000341425072213200147250ustar00rootroot00000000000000.depend *.o yaku-ns getzone yaku-ns-0.2/AUTHORS000066400000000000000000000011541425072213200140110ustar00rootroot00000000000000Yaku-ns is developed with little efforts (that is almost only when something is broken for my own usage) by: Salvatore Sanfilippo Thanks for bug reporting, code and/or other kind of support to: * Robert Allmeroth For reporting of a bug in the forwarding code * Aethra (http://www.aethra.com) for bugfixing in early stage of development. -------------------------------------------------------------------------------- Note: that this file is not complete since I lost the old credits file. Please demand credits if you contributed but I miss you here. yaku-ns-0.2/CHANGES000066400000000000000000000036261425072213200137420ustar00rootroot00000000000000CHANGES file Sep 16 2002 CNAME implemented Sep 14 2002 Fixed a wildcard RR response building bug. Sep 14 2002 Fixed a memory leak introduced with the new forwarding ACL control. Sep 12 2002 Forwarding fixed, now after the forward_next timeout the query is sent to the next nameserver and so on. Thanks to "Robert Allmeroth" Sep 09 2002 Now stuff that should never be < 0 are unsigned. Sep 09 2002 Major change, merged with the new hashtable library version. Sep 08 2002 Implemented wildcard Resource Records, see Docs. Sep 06 2002 Fixed a problem in name_decode(). If the name format was bad, but the pointer-to-pointer where to store the name was set to NULL (used to only know the length of the encoded name by get_min_ttl()) the function deferenced a NULL pointer, writing 0 on it. Not exploitable to break in but a bad DoS. Sep 05 2002 now chdir() before to chroot(). Sep 04 2002 call abort() in the SIG_SEGV sighandler to dump the core Sep 04 2002 use setrlimit(2) to enable core dumping Sep 04 2002 Modified Copyright notices and email Sep 04 2002 Fix for case insensitive hashtable key creation Sep 03 2002 keyword 'forwarder' changed in 'nameserver' Sep 03 2002 RRs qtype to symbol table updated Sep 03 2002 Now names are matched case insensitive Aug 30 2002 Fixed SIGCHLD handling problem. Aug 30 2002 Fixed autoptr, now generates a PTR record for all the names for the IP address, not only for the first. Aug 30 2002 New 'fwd' ACL chain used for forwarding access control Feb 8 2001 Different algorithm for cache expiration check Feb 7 2001 Now uses strlcpy and strlcat Feb 6 2001 Zone tranfer code updated Feb 5 2001 Fork from ENS: o Now uses hash table instead of lists o #ifdef away o config.c update ??? ? ???? o too much stuff, I don't remember yaku-ns-0.2/COPYING000066400000000000000000000430761425072213200140050ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. yaku-ns-0.2/Documentation/000077500000000000000000000000001425072213200155515ustar00rootroot00000000000000yaku-ns-0.2/Documentation/yaku-getzone.1000066400000000000000000000024331425072213200202570ustar00rootroot00000000000000.TH GETZONE "1" "November 2011" "yaku-getzone" "User Commands" .SH NAME yaku-getzone \- zone transfer (AXFR) tool for yaku-ns .SH SYNOPSIS yaku-getzone \fB\-z\fR \fB\-s\fR [\-p ] .PP .TP \fB\-z\fR The zone to download. .TP \fB\-s\fR The IP address of the master server. .TP \fB\-p\fR Specify a different destination port, the default is port 53. .SH DESCRIPTION This manual page documents briefly the .B yaku-getzone zone transfer tool. This is a trivial utility that performs an AXFR request over TCP to some primary DNS server and produces as output the zone in a format compatible with the Yaku-NS configuration file. You can use it to create a rudimental secondary DNS (slave). .SH EXAMPLES .TP .SH Initiate zone transfer for the test.org zone from the DNS server 1.2.3.4 yaku-getzone \-z test.org \-s 1.2.3.4 > slave-db.test.org Then you need to send a SIGHUP to Yaku-NS to force a local RRs reload. Obviously you need to add an include keyword in the yaku-ns.conf to include the zone. See the example configuration file for more information. .SH SEE ALSO .BR yaku-ns (1). .SH AUTHOR yaku-getzone was written by Salvatore Sanfilippo . .PP This manual page was written by G\[:u]rkan Myczko , for the Debian project (but may be used by others). yaku-ns-0.2/Documentation/yaku-ns.1000066400000000000000000000051211425072213200172210ustar00rootroot00000000000000.TH YAKU-NS "1" "November 2011" "yaku-ns" "User Commands" .SH NAME yaku-ns \- small and simple DNS server .SH SYNOPSIS yaku-ns [\-p ] [\-P ] [\-f ] [\-C ] [\-F ] [\-T ] [\-c ] [\-l ] [\-r ] [\-u owner] [\-b ] [\-xdhV] .SH DESCRIPTION This is a simple and small DNS server. Major features are: - Support for A, MX, NS, TXT, PTR, SOA in local resource records - Additional RRs handling - Responses cache, with optional TTL expiration - Forwarding to multiple exteral DNS servers, with simultaneous or time-driven query - Optional TCP zone transfer - A in RRs shuffling - Built-in ACL rules - Root privileges squashing - Chroot jail under unix systems - Secure DNS IDs to prevent DNS forget - Logging - Very simple config file .SH OPTIONS .TP \fB\-p\fR Use the UDP port for the DNS service. .TP \fB\-P\fR Use the TCP port for the DNS under TCP service. The only two requests accepted under TCP are IN/AXFR and IN/SOA. .TP \fB\-f\fR Query the external DNS servers to the port . .TP \fB\-C\fR Use a cache of size , 0 is no-cache. .TP \fB\-F\fR Use a forwarded request queue of size , 0 turn off the forwarding. .TP \fB\-T\fR Forwarded requets expire in seconds. .TP \fB\-c\fR Use the config file : WARNING you must specify the absolute path here, since the server chdir() to "/". Also note that this path is relative to the chroot jail. Use "-" as filename to provide the configuration from the standard input. .TP \fB\-l\fR Use the for yaku-ns logs. Remember to use an absolute path, relative to the chroot (if used). .TP \fB\-r\fR Chroot to . .TP \fB\-u\fR Run as (default is nobody, not so secure). .TP \fB\-b\fR
Bind only the interface with IP address
. .TP \fB\-x\fR Enable the TCP services. .TP \fB\-d\fR Demonize. .TP \fB\-h\fR Show some help. .TP \fB\-V\fR Verbosity level: \-V: low level, \-VV medium level, \-VVV high level of verbosity. \-VVV is raccomanded for new users. .SH SIGNALS The DNS server yaku-ns performs different action if some signal is received: SIGHUP Reload the configuration (use on record updates) SIGUSR1 Force yaku-ns to log some debugging information SIGUSR2 Switch the forwarding ON/OFF .SH SEE ALSO .BR yaku-getzone (1). .SH AUTHOR yaku-ns was written by Salvatore Sanfilippo . .PP This manual page was written by G\[:u]rkan Myczko , for the Debian project (but may be used by others). yaku-ns-0.2/Documentation/yaku-ns.conf.example000066400000000000000000000151051425072213200214430ustar00rootroot00000000000000############################################# # Example self-commented configuration file # ############################################# # NOTE: All the keywords are case insensitive # This is a comment # include # # Will include the given file. ############### ### LOGGING ### ############### # logfile # # Specify the filename for the logs # # When not demonized the default is to log to the standard output # nologtime # # Disable timestamp in log lines # # nologtime is disabled by default (i.e. the timestamp is enabled) # loglevel # # Set the verbosity level to one of the following: # # errors very low verbosity, logs only errors. # low logs more, including received queries/response. # med logs more, including info about ACL and cached responses. # high very verbose, logs loaded RRs, expirations, ... # debug logs information usually not useful. Only if DEBUG was defined. # # Example: # # loglevel med # # Default log level is: errors ########################### ### ACCESS CONTROL LISTS ## ########################### # acl ... # # Built-in Access Control Lists to deny per-IP-address # # Avaliable chains: # # dns.allow allow rules for the DNS service # dns.deny deny rules for the DNS service # fwd.allow allow rules for the DNS forwarding # fwd.deny deny rules for the DNS forwarding # axfr.allow allow rules for the zone transfer # axfr.deny deny rules for the zone transfer # # Rules: # # A rule is a truncated IP address or an IP address # with a trailer '$' character. # Example of rules and what it matches: # # 192.168.1. - will match 192.168.1.1, 192.168.1.2, 192.168.* # 192.168.1.2 - will match 192.168.1.2, 192.168.1.22, 192.168.1.2* # 192.168.1.3$ - will match only 192.168.1.3 # $ - will match ALL the ip addresses # # For example to deny all the IP addresses except # the 192.168.1.0/24 net for the DNS service you # should use: # # acl dns.allow 192.168.1. # acl dns.deny $ # # NOTE: this stuff works exactly like /etc/hosts.allow and /etc/hosts.deny ###################### ### UPTIME CONTROL ### ###################### # uptime # # Enable a built-in dynamic "uptime.yaku" CHAOS/TXT Resource Record that # contains the current uptime in a human readable format. ###################################### ### LOCAL RESOURCE RECORDS OPTIONS ### ###################################### # wildcard_lookup # # Enable the wildcard lookup, so you can define resource records # containing the '*' character, like # # A *.yakuns.org 1.2.3.4 # # The wildcard lookup algorithm works like this: # # You search for a.b.c.d, if the server can't find it than search for # *.b.c.d, if also there is no match search for *.c.d and so # on. The max number of iteration and the max reduction of the original # name are controlled using the following two options: # wildcard_lookup_deepth and wildcard_lookup_minlevel ################## ### FORWARDING ### ################## # nameserver # # Specify an external server to act like bind "forward only". # Multiple forwaders are allowed. # # If no nameservers are specified the forwarding is turned off, # a query that does not match the local Resoure Records will produce # a NXDOMAIN error. # # Leave the forwarding off if you are using Yaku-NS just as primary NS # server for a domain! # forward_max # # The maximum numbers of pending forwarded queries. # # Default is 1000 entries # forward_entry_timeout # # Maximum time to live of every entry in the forwarded requests table. # After this time the entry will be removed, if the forwarder # send the response after the expire was reached the response will # be ignored. # # Default is 50 seconds # forward_next_timeout # # Time to wait before to contact the next forwarder. # Anyway if a reply comes from an early forwarder it will # be accepted and the entry in the forwad table removed. # # Default is 3 seconds ############################ ### CACHING (FORWARDING) ### ############################ # cache_max # # Maximum number of cached DNS responses in the cache. # # Default is 5000 entries # cache_minttl # # Minimun time to live for a cached response # # Default is 0 seconds # cache_maxttl # # Maximum time to live for a cached response # # Default is 1 day # cache_noexpire # # If set the cached responses will *never* expire # Don't set it unless you know what you are doing. ####################### ### ZONE TRASFERING ### ####################### # axfr_more_rr # # For default yaku-ns replies to AXFR requests in # "compatibility-mode": during the zone transfer # every message contains just one resource record. # If the axfr_more_rr option is set yaku-ns will # behaves as specifed in the # "DNS Zone Transfer Protocol Clarifications" Internet # draft, filling every message with more resource records. # # Don't enable this option if unsure # tcp_requests_for_connection # # Yaku-ns uses DNS over TCP only to allow # AXFR requests. This option defines the number of # consecutive requests that must be accepted in the same # TCP connection. The default is 2 to allow a tipical # SOA + AXFR request for zone transfering. # # Set it to 0 to allow infinite requests in the same # TCP connection (until the 2 minutes timeout expires) # # Leave it undefined if yaku-ns works in your configuration. # Try to set it to zero if you experimented problems with secondary DNS severs. # # Default is, as stated, 2. ######################################### ### LOCAL RESOURCE RECORDS DEFINITION ### ######################################### # ttl # # Set the time to live for the next RRs defined # class # # Set the class for the next RRs defined # A # # To specify an A Resource Record, example: # # A yakuns.example.net 192.168.1.1 # MX # # Create an MX RR, example: # # MX example.net 10 mail.example.net # PTR # # Create a PTR RR, example: # # PTR 1.1.168.192.in-addr.arpa dns.example.net # TXT <...> # # Create a TXT RR, # all the characters before a dot will be in the same label. # Example: # # TXT ens ENS is a name server # NS # # Create an NS RR, example: # # NS example.net ens.example.net # SOA # # Create a SOA RR, example: # # SOA example.net ens.example.net antirez.invece.org 1234 60 60 60 0 # autoptr # # Enable the "Auto-PTR" mode. For every A RR added the logical # PTR will be automatically created. # # Disable by default # noautoptr # # Disable the "Auto-PTR" mode. # EOF yaku-ns-0.2/MD5SUM.SIGNED.asc000066400000000000000000000033621425072213200153560ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- 65b580fdaa0a067ee8dd21dc43859485 AUTHORS a388e3d2f0e60964e1d2588c8b61c795 CHANGES 8ca43cbc842c2336e835926c2166c28b COPYING 7a1915cca943dbe03f38f5115b113ebc Makefile 51625b14f685b8980e4af6468170a98b README 24946f034a1cad4df1ff0edcafab2a4b README.CVS 160bc3f6be90d6829cfb1731dda9c7b2 TODO 9e0b88fc9219e978fc7ad6323e1adc0a acl.c 122a90930a9ff32231ed5ebca43f01c9 aht.c 0168c735f307a4258af7c4299ffdfb1d aht.h ee94b63b99245961117fc8eddf41f947 arr.c c9b2ee7bde60bc6ef97498afe014bae7 assert.h b2096b43be59f3c6561c1f5f9a2b53bd autoptr.c 206c784d9002707b88bb736cc6bc454d axfr_out.c 865137e3b368d937eeec5564b55801e2 cache.c 3faa2ff027360ad5aeb08c619e8efd3d config.c 6952f57cf8fd9481cfaeeb0a4a6ef1ff core.c bb6a455cea1fc07211b25146c7f57398 dns.c 74712893ba107c3096fa505e0d6e57ce ens.conf ed54441940057dcbb8a3a5bb8308aa62 ens.h 6623e764aa2ce3f187d045ffb561ea90 forward.c d832ce0862a0c7fe694f2e0f43b7598a getzone.c 6f0e388269636ac3e08c7a17249e9a57 htkey.c 66f4b1c002dcb68fdd8a80f704657a21 local.c 6de1636601389a2f3322ad698e7357bc log.c 484fa986c2187c51e2adb24d1e89f5f7 misc.c 833b74fc9706f075c268a3a4aaa88a46 nameser.h 787e9e877d435bdb2fc55d24711dc3d9 response.c 9e2ff4405715ef1f74636e0fc17e371c signal.c c5e17f48168fbca4a54564305978eba0 strlcat.c 492029e810d205aa0a4765efe894f2e0 strlcpy.c 088ff2c2c430b02008952ab0dc77bb8e tunable.h e2f151ed7c30479c7fe6944569937098 unix.c e4a41d9303c45efb9c15be65aa024929 uptime.c -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv iQCVAwUBOts2CNwczK2pu2qVAQF0kQQAn/QWB/950Fe6o2F4f+jJt2vqFFSjyEbN F5LJH88GtfcpbUfSzANSy1FKA7C4duLq1p2bz9vukplY7QB3xoUFZiMUaVeZCT4G OPpsVeejR9TyAWxZRBgt47Oqs/hB8+fdNPBjCxc71z7Ms9VwGvKfxuuo8YZprn1L FX6fSDd5Ufs= =McKL -----END PGP SIGNATURE----- yaku-ns-0.2/Makefile000066400000000000000000000034671425072213200144120ustar00rootroot00000000000000# YAKU-NS Makefile # # Copyright (C) 2000 by Salvatore Sanfilippo # .SUFFIXES: .SUFFIXES: .c .o SHELL= /bin/sh CFLAGS= -W -Wall -O2 -g AR=/usr/bin/ar INSTALL= /usr/bin/install INSTALL_PROGRAM= $(INSTALL) INSTALL_DATA= $(INSTALL) -m 644 DESTDIR= /usr/bin/ PROGRAMS= yaku-ns yaku-getzone YAKUNS_OBJECTS= acl.o arr.o axfr_out.o autoptr.o cache.o config.o core.o \ dns.o forward.o local.o log.o htkey.o \ misc.o unix.o uptime.o aht.o strlcpy.o strlcat.o \ signal.o response.o rlimit.o GETZONE_OBJECTS= getzone.o dns.o strlcpy.o all: .depend yaku-ns yaku-getzone success .depend: @echo Making dependences @$(CC) -MM *.c > .depend .c.o: $(CC) -I. $(CFLAGS) $(DEFS) -c $< -o $@ yaku-ns: $(YAKUNS_OBJECTS) $(CC) $(LDFLAGS) $^ -o $@ yaku-getzone: $(GETZONE_OBJECTS) $(CC) $(LDFLAGS) $^ -o $@ strip: strip $(PROGRAMS) @ls -l $(PROGRAMS) install: @echo See the INSTALL file clean: rm -f *.o core .depend .nfs* */.nfs* .*.swp $(PROGRAMS) success: @echo @echo Compilation successful! @echo Now you can read the INSTALL file distclean: mostlyclean: dist: clean sign tar tar: if [ "x" = "x$(RELEASE)" ]; then \ d=`date -Iseconds | cut -d+ -f1 | tr -- :T -.`; \ v=`grep VERSION tunable.h | cut -d\" -f2`; \ b=`echo yaku-ns-$$v-$$d`; mkdir ../$$b; cp -a . ../$$b; \ cd ..; read; tar cvzf $$b.tar.gz $$b; \ else \ v=`grep VERSION tunable.h | cut -d\" -f2`; \ b=`echo yaku-ns-$$v`; mkdir ../$$b; cp -a . ../$$b; \ cd ..; read; tar cvf - $$b | gzip -9 > $$b.tar.gz; \ read; tar cvf - $$b | bzip2 -9 > $$b.tar.bz2; \ fi; \ ls -l $$b.tar.* wc: cat *.[ch] | sed -e /^$$/d | wc -l sign: rm -f SUM.SIGNED* -(md5sum * 2> /dev/null > MD5SUM.SIGNED) pgp -sta MD5SUM.SIGNED rm -f MD5SUM.SIGNED #check: # md5sum -vc MD5SUM.SIGNED.asc ifeq (.depend,$(wildcard .depend)) include .depend endif yaku-ns-0.2/README000066400000000000000000000141261425072213200136240ustar00rootroot00000000000000******************* Note: Yaku-NS is a 10 years old project. It is here for personal "historical" reasons, as this is one of the first non trivial C projects I wrote. ******************* +==================================================+ | WARNING | | Yaku-NS is in the beta stage of development | | possibly unstable, insecure, sperimental code. | | USE IT AT YOUR RISK. | +==================================================+ ----------- ADVERTISING ----------- Yaku-ns is GPLed, but you can get a BSD-like license for your own company usage (for example to develop a closed-source fork and put yaku-ns in some router, appliance, ...) for 1000 EUR. The licence can't be sold to others, and applies to the current CVS source code, that is, you can't use code that I put in the CVS _after_ you already got the license. There aren't time limits. After you got the license you can modify the code and sell it in binary form, inside a ROM, where you want, forever. For more information please write to antirez@invece.org -------- OVERVIEW -------- Yaku-NS is a DNS server that implements a subset of the DNS protocol. For a full featured DNS server check the BIND For a very secure DNS server (not complete, with a silly license) check DJBDNS For an alternative (almost complete) DNS server implementation check DENTS For a new upcoming GPL DNS implementation check MARADNS The goal is to provide an easy to configure GPLed alternative for: * A DNS server for myself. I'm unable to configure bind * A primary/secondary DNS server * Forward only DNS server * Dial-up systems * Embedded systems ---------------------------------- HOW TO SETUP YAKU IN YOUR UNIX BOX ---------------------------------- Yaku-NS runs without root privileges in a chroot jail. We provided this features for your security, please enable this features! COMMAND LINE OPTIONS ~~~~~~~~~~~~~~~~~~~~ usage: yaku-ns [-p ] [-f ] [-C ] [-F ] [-T ] [-c ] [-l ] [-r ] [-u owner] [-b ] [-xdDhV] -p Use the UDP port for the DNS service. -P Use the TCP port for the DNS under TCP service. The only two requests accepted under TCP are IN/AXFR and IN/SOA. -f Query the external DNS servers to the port . -C Use a cache of size , 0 is no-cache. -F Use a forwarded request queue of size , 0 turn off the forwarding. -T Forwarded requets expire in seconds. -c Use the config file : WARNING you must specify the absolute path here, since the server chdir() to "/". Also note that this path is relative to the chroot jail. Use "-" as filename to provide the configuration from the standard input. -l Use the for yaku-ns logs. Remember to use an absolute path, relative to the chroot (if used). -r Chroot to . -u Run as (default is nobody, not so secure). -b
Bind only the interface with IP address
. -x Enable the TCP services. -d Demonize. -h Show a little help. -V Verbosity level: -V: low level, -VV medium level, -VVV high level of verbosity. -VVV is raccomanded for new users. See how to create a configuration file using the self-commented config file at Documentation/yaku-ns.conf.example Follow this steps to install Yaku-NS in a unix-like system: COMPILING YAKU-NS ~~~~~~~~~~~~~~~~~ $ vi tunable.h (optional) $ make INSTALLING ENS ~~~~~~~~~~~~~~ Create a system user "yaku" in the "yaku" group. # mkdir /usr/local/yaku-ns # chown yaku:yaku /usr/local/yaku-ns # chmod 700 /usr/local/yaku-ns # cp yaku-ns /usr/local/yaku-ns # cp Documentation/yaku-ns.conf.example /usr/local/yaku-ns/yaku-ns.conf # chown root:root /usr/local/yaku-ns/* # chmod 755 /usr/local/yaku-ns/yaku-ns # chmod 644 /usr/local/yaku-ns/yaku-ns.conf # touch /usr/local/yaku-ns/yaku-ns.log # chown yaku:yaku /usr/local/yaku-ns/yaku-ns.log # chmod 644 /usr/local/yaku-ns/yaku-ns.log # cd /usr/local/yaku-ns # ls -l The output of the ls -l command should be like the following: total 168 -rwxr-xr-x 1 root root 153306 Dec 26 14:48 yaku-ns -rw-r--r-- 1 root root 10107 Dec 26 14:49 yaku-ns.conf -rw-r--r-- 1 yaku yaku 0 Dec 26 14:53 yaku-ns.log CONFIGURING YAKU-NS ~~~~~~~~~~~~~~~~~~~ Edit the example self-commented configuration file /usr/local/yaku-ns/yaku-ns.conf and create your configuration. RUNNING YAKU-NS ~~~~~~~~~~~~~~~ To run Yaku-NS inside a chroot jail (raccomanded!) with TCP services enabled just use: # /usr/local/yaku-ns/yaku-ns -xd -u yaku -r /usr/local/yaku-ns/ -c /yaku-ns.conf Note that the configuration file is specified using the absolute path, relative to the chroot. SIGNALS ~~~~~~~ Yaku-NS performs different action if some signal is received: SIGHUP: Reload the configuration (use it when your records was updated) SIGUSR1: Force yaku-ns to log some debugging information SIGUSR2: Switch the forwarding ON/OFF ------- GETZONE ------- Getzone is a trivial utility that performs an AXFR request over TCP to some primary DNS server and produces as output the zone in a format compatible with the Yaku-NS configuration file. You can use it to create a rudimental secondary DNS (slave) but a better solution will be on the road ASAP (It wasn't ASAP since I use only external secondary nameservers, so yaku-ns is my primary DNS server). Usage: getzone -z -s [ -p ] -z The zone to download -s The IP address of the master server -p Specify a different destination port, the default is port 53 Example: $ getzone -z test.org -s 1.2.3.4 > salve-db.test.org Than you need to send a SIGHUP to Yaku-NS to force a local RRs reload. Obviously you need to add an include keyword in the yaku-ns.conf to include the zone. See the example configuration file for more information. Have fun, antirez yaku-ns-0.2/README.CVS000066400000000000000000000012761425072213200142600ustar00rootroot00000000000000---------------------------------------------------------------- How to get the yaku-ns source code from the anonymous CVS server ---------------------------------------------------------------- $ cvs -d:pserver:anonymous@cvs.hping2.sourceforge.net:/cvsroot/hping2 login CVS will ask for the password, just press enter, no password is required than type the following to download the full source code. $ cvs -z8 -d:pserver:anonymous@cvs.hping2.sourceforge.net:/cvsroot/hping2 checkout yaku-ns ----------------------------------- How to update your source code tree ----------------------------------- change the current directory to /somewhere/yaku-ns, than just type: cvs update yaku-ns-0.2/TODO000066400000000000000000000061671425072213200134420ustar00rootroot00000000000000Urgent stuff * merge with the new version of aht * randomize the aht hash function -- upgrade aht itself, it is generally useful. * Modify the brain-dead algorithm that removes the last accessed entry with a simpler algorithm that just drops a random entry. * CNAME RR - Check better the return code of ht_add_generic and others ht_* functions expecially add assert()ions when you feel this functions can return only a range of value. - AXFR ACLs per-zone - Clear the AA bit from forwarded responses and cached ones - The control interface, unix domain sockets - name encode/decode: versions without dyn allocation - Check the effect of entry removing: removing some duplicated entry (for example with id 0) will be impossible to lookup for an entry with the same name/type/class with an id of 1,2,... - Out of memory issue - CNAME - HINFO - name compression for outgoing packets - SOA minimum issue - Query with the recursion required not set - Many things to fix about the protocol, truncation, TTLs, ..., see the RFC2181 * the responses should be generated using the same IP address in the dest address field of the query sent by the client. * RRs with the same label (name/qclass/qtype) and the same data SHOULD be suppressed, only one considered. * In an RRSet all the TTLs MUST be the same. * RRSet with different TTL should not be accepted in a response: if they comes from an authoritative source the software should use for the whole set the minimum TTL. If the source ins't authoritative the response should be discarded and the authoritative servers contacted. * Servers MUST never merge RRs from a response with RRs in the cache to form a response. * TTLs received greater than 2^31-1 should be considered as zero. --- continue with the section 10 of the RFC --- DONE: * Truncation: the TC bit should be set only when the RRSet needed for the reply don't fit the protocol limit. It should not be set when _extra_ informations can't be included. When the additional information can't be inserted the entire RR that can't fit the left space shound NOT be excluded from the response. Otherwise when TC bit is set the not complete RR of the RRSet can be left in the response. When a client receive a respose with the TC bit set should retry to query the server under the TCP DONE: DNS over TCP should also be able to reply to SOA RR requests. Usually the refresh starts asking for the SOA RR, than asking for the AXFR. -------------------------------------------------------------------------- Less urgent stuff - Clean-up the config refresh code. - Change query and query_section to question_* when appropriate - Move protocol related stuff to udp.c and tcp.c - After the fork the TCP handler should close all the unneedded filedes - The forwarding code should check for forward entry in timeout _after_ the incoming packet processing? Otherwise the response that may cause the forward entry clean-up is considered only after that the DNS server tryed to ask the same query to the next forwarder. - Use alloca() instead of malloc() when possible. yaku-ns-0.2/acl.c000066400000000000000000000067631425072213200136570ustar00rootroot00000000000000/* acl.c * Access Control List * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include /* global vars */ /* ACL chains, allow and deny lists for different targets */ struct acl *acl_dns_allow_head = NULL; struct acl *acl_dns_allow_tail = NULL; struct acl *acl_dns_deny_head = NULL; struct acl *acl_dns_deny_tail = NULL; struct acl *acl_fwd_allow_head = NULL; struct acl *acl_fwd_allow_tail = NULL; struct acl *acl_fwd_deny_head = NULL; struct acl *acl_fwd_deny_tail = NULL; struct acl *acl_axfr_allow_head = NULL; struct acl *acl_axfr_allow_tail = NULL; struct acl *acl_axfr_deny_head = NULL; struct acl *acl_axfr_deny_tail = NULL; /* not exported functions */ static int acl_check(char *ip, struct acl *allow, struct acl *deny); static void acl_free_list(struct acl **head, struct acl **tail); /* exported functions */ void acl_add_rule(char *rule, struct acl **head, struct acl **tail); void acl_free(void); int acl_check_dns(char *ip); int acl_check_fwd(char *ip); int acl_check_axfr(char *ip); /* acl_add_rule() allocate and set a rule in the * acl list identified by 'head' and 'tail' */ void acl_add_rule(char *rule, struct acl **head, struct acl **tail) { if (*head == NULL) { *head = malloc(sizeof(struct acl)); if (*head == NULL) goto out_of_memory; *tail = *head; } else { (*tail)->next = malloc(sizeof(struct acl)); if ((*tail)->next == NULL) goto out_of_memory; *tail = (*tail)->next; } strlcpy((*tail)->rule, rule, RULE_MAXSIZE); (*tail)->next = NULL; return; out_of_memory: perror("acl_dns_add_rule() malloc"); exit(1); } int acl_check_dns(char *ip) { return acl_check(ip, acl_dns_allow_head, acl_dns_deny_head); } int acl_check_fwd(char *ip) { return acl_check(ip, acl_fwd_allow_head, acl_fwd_deny_head); } int acl_check_axfr(char *ip) { return acl_check(ip, acl_axfr_allow_head, acl_axfr_deny_head); } /* acl_check() implements the hosts.allow/hosts.deny style ACL */ static int acl_check(char *ip, struct acl *allow, struct acl *deny) { struct acl *a; size_t l; a = allow; /* search in the allow list */ while(a) { l = strlen(a->rule); if (a->rule[l-1] != '$') { if (!strncmp(a->rule, ip, l)) return ACL_ALLOW; } else { if (l == 1) /* only $ */ return ACL_ALLOW; if (l == strlen(ip)+1 && !strncmp(a->rule, ip, l-1)) return ACL_ALLOW; } a = a->next; } a = deny; /* search in the deny list */ while(a) { l = strlen(a->rule); if (a->rule[l-1] != '$') { if (!strncmp(a->rule, ip, l)) return ACL_DENY; } else { if (l == 1) /* only $ */ return ACL_DENY; if (l == strlen(ip)+1 && !strncmp(a->rule, ip, l-1)) return ACL_DENY; } a = a->next; } return ACL_ALLOW; } /* Free all elements of some ACL list */ static void acl_free_list(struct acl **head, struct acl **tail) { struct acl *a, *t; a = *head; while(a) { t = a->next; free(a); a = t; }; *head = *tail = NULL; } /* Free all the ACL lists */ void acl_free(void) { /* free the ACL list */ acl_free_list(&acl_dns_allow_head, &acl_dns_allow_tail); acl_free_list(&acl_dns_deny_head, &acl_dns_deny_tail); acl_free_list(&acl_fwd_allow_head, &acl_axfr_allow_tail); acl_free_list(&acl_fwd_deny_head, &acl_axfr_deny_tail); acl_free_list(&acl_axfr_allow_head, &acl_axfr_allow_tail); acl_free_list(&acl_axfr_deny_head, &acl_axfr_deny_tail); } yaku-ns-0.2/aht.c000066400000000000000000000577301425072213200136740ustar00rootroot00000000000000/* An implementation of in-memory hash tables: * Copyright (c) 2000-2002 Salvatore Sanfilippo * * -- VERSION 2002.09.07 -- * * COPYRIGHT AND PERMISSION NOTICE * ------------------------------- * * Copyright (c) 2000 Salvatore Sanfilippo * Copyright (c) 2001 Salvatore Sanfilippo * Copyright (c) 2002 Salvatore Sanfilippo * * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons * to whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL * 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. * * Except as contained in this notice, the name of a copyright holder * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * of the copyright holder. * * OVERVIEW * -------- * * AHT is an implementation of a dictionary with support for * INSERT, DELETE and SEARCH operations. It uses the hash table * as base data structure to provide almost constant times for * the three operations. AHT also automatically care about the * size of the current key-values set increasing the hash table * as needed. * * DESIGN PRINCIPLE * ---------------- * * - AHT try to resist to attacker-induced worst-case behaviour * trought the randomization of the hash-function. This is * optional. * * - AHT takes care of the hash table expansion when needed. * The hash table load ranges from 0 to 0.5, the hash table * size is a power of two. * * - A simple implementation. The collisions resolution used * is a simple linear probing, that takes advantage of * the modern CPU caches, the low hash table max load and * the use of a strong hash function provided with this library * (ht_strong_hash), should mitigate the primary clustering * enough. Experimental results shown that double hashing * was a performance lost with common key types in modern * CPUs. * * - Moderatly method oriented, it is possible to define the hash * function, key/value destructors, key compare function, for a * given hash table, but not with a per-element base. * * - Specialized slab allocator for the hash table element structure, * useful when there are a number of INSERT/DELETE operations. * It is compiled off by default. * * === WARNING === * = Before to use this library, think about the -fact- that the * = worst case is O(N). Like for the quick sort algorithm, it may * = be a bad idea to use this library in medical software, or other * = software for wich the worst case should be taken in account * = even if not likely to happen. * = Good alternatives are red-black trees, and other trees with * = a good worst-case behavior. * =============== * * HOW TO GET UP TO DATE CODE * -------------------------- * * http://antirez.sed-consortium.com/software/aht.html * * TODO * ---- * * - Write the documentation * - ht_copy() to copy an element between hash tables * - ht_dup() to duplicate an entire hash table * - ht_merge() to add the content of one hash table to another * - disk operations, the ability to save an hashtable from the * memory to the disk and the reverse operation. * * Most of this features needs additional methods, like one * to copy an object, and should return an error if such methods * are not defined. * */ #include #include #include #include #include "aht.h" /* -------------------------- private prototypes ---------------------------- */ static int ht_expand_if_needed(struct hashtable *t); static unsigned int next_power(unsigned int size); static int ht_insert(struct hashtable *t, void *key, unsigned int *avail_index); #ifdef AHT_USE_SLAB static void slab_init(struct ht_cache *c); static void slab_destroy(struct ht_cache *c); static void *slab_get_obj(struct ht_cache *c); static void slab_free_obj(struct ht_cache *c, void *ptr); #endif /* AHT_USE_SLAB */ /* The special ht_free_element pointer is used to mark * a freed element in the hash table (note that the elements * neven used are just NULL pointers) */ static struct ht_ele *ht_free_element = (void*) -1; /* -------------------------- hash functions -------------------------------- */ /* The djb hash function, that's under public domain */ u_int32_t djb_hash(unsigned char *buf, size_t len) { u_int32_t h = 5381; while(len--) h = (h + (h << 5)) ^ *buf++; return h; } u_int32_t djb_hashR(unsigned char *buf, size_t len) { u_int32_t h = 5381; buf += len-1; while(len--) h = (h + (h << 5)) ^ *buf--; return h; } /* Another trivial hash function */ #define ROT32R(x,n) (((x)>>n)|(x<<(32-n))) u_int32_t trivial_hash(unsigned char *buf, size_t len) { u_int32_t h = 0; while(len--) { h = h + *buf++; h = ROT32R(h, 3); } return h; } u_int32_t trivial_hashR(unsigned char *buf, size_t len) { u_int32_t h = 0; buf += len-1; while(len--) { h = h + *buf--; h = ROT32R(h, 3); } return h; } /* A strong hash function that should be the default with this * hashtable implementation. Our hash tables does not support * double hashing for design: the idea is to avoid double * hashing and use a bit slower but very strong hash function like * this. This should provide quite good performances with * all the kinds of keys if you take the default max load of 50%. * * For more information see: http://burtleburtle.net/bob/hash/evahash.html */ /* The mixing step */ #define mix(a,b,c) \ { \ a=a-b; a=a-c; a=a^(c>>13); \ b=b-c; b=b-a; b=b^(a<<8); \ c=c-a; c=c-b; c=c^(b>>13); \ a=a-b; a=a-c; a=a^(c>>12); \ b=b-c; b=b-a; b=b^(a<<16); \ c=c-a; c=c-b; c=c^(b>>5); \ a=a-b; a=a-c; a=a^(c>>3); \ b=b-c; b=b-a; b=b^(a<<10); \ c=c-a; c=c-b; c=c^(b>>15); \ } /* The whole new hash function */ u_int32_t __ht_strong_hash(u_int8_t *k, u_int32_t length, u_int32_t initval) { u_int32_t a,b,c; /* the internal state */ u_int32_t len; /* how many key bytes still need mixing */ /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = initval; /* variable initialization of internal state */ /*---------------------------------------- handle most of the key */ while (len >= 12) { a=a+(k[0]+((u_int32_t)k[1]<<8)+((u_int32_t)k[2]<<16)+ ((u_int32_t)k[3]<<24)); b=b+(k[4]+((u_int32_t)k[5]<<8)+((u_int32_t)k[6]<<16)+ ((u_int32_t)k[7]<<24)); c=c+(k[8]+((u_int32_t)k[9]<<8)+((u_int32_t)k[10]<<16)+ ((u_int32_t)k[11]<<24)); mix(a,b,c); k = k+12; len = len-12; } /*------------------------------------- handle the last 11 bytes */ c = c+length; switch(len) /* all the case statements fall through */ { case 11: c=c+((u_int32_t)k[10]<<24); case 10: c=c+((u_int32_t)k[9]<<16); case 9 : c=c+((u_int32_t)k[8]<<8); /* the first byte of c is reserved for the length */ case 8 : b=b+((u_int32_t)k[7]<<24); case 7 : b=b+((u_int32_t)k[6]<<16); case 6 : b=b+((u_int32_t)k[5]<<8); case 5 : b=b+k[4]; case 4 : a=a+((u_int32_t)k[3]<<24); case 3 : a=a+((u_int32_t)k[2]<<16); case 2 : a=a+((u_int32_t)k[1]<<8); case 1 : a=a+k[0]; /* case 0: nothing left to add */ } mix(a,b,c); /*-------------------------------------------- report the result */ return c; } /* ----------------------------- API implementation ------------------------- */ /* Initialize the hash table */ int ht_init(struct hashtable *t) { t->table = NULL; t->size = 0; t->sizemask = 0; t->used = 0; t->collisions = 0; t->hashf = NULL; t->key_destructor = ht_no_destructor; t->val_destructor = ht_no_destructor; t->key_compare = ht_compare_ptr; #ifdef AHT_USE_SLAB t->cache = malloc(sizeof(struct ht_cache)); if (!t->cache) return HT_NOMEM; slab_init(t->cache); #endif return HT_OK; } /* Resize the table to the minimal size that contains all the elements */ int ht_resize(struct hashtable *t) { int minimal = (t->used * 2)+1; if (minimal < HT_INITIAL_SIZE) minimal = HT_INITIAL_SIZE; return ht_expand(t, minimal); } /* Move an element accross hash tables */ int ht_move(struct hashtable *orig, struct hashtable *dest, unsigned int index) { int ret; unsigned int new_index; /* If the element isn't in the table ht_search will store * the index of the free ht_ele in the integer pointer by *index */ ret = ht_insert(dest, orig->table[index]->key, &new_index); if (ret != HT_OK) return ret; /* Move the element */ dest->table[new_index] = orig->table[index]; orig->table[index] = ht_free_element; orig->used--; dest->used++; return HT_OK; } /* Expand or create the hashtable */ int ht_expand(struct hashtable *t, size_t size) { struct hashtable n; /* the new hashtable */ unsigned int realsize = next_power(size), i; /* the size is invalid if it is smaller than the number of * elements already inside the hashtable */ if (t->used >= size) return HT_INVALID; ht_init(&n); n.size = realsize; n.sizemask = realsize-1; n.table = malloc(realsize*sizeof(struct ht_ele*)); if (n.table == NULL) return HT_NOMEM; /* Copy methods */ n.hashf = t->hashf; n.key_destructor = t->key_destructor; n.val_destructor = t->val_destructor; n.key_compare= t->key_compare; #ifdef AHT_USE_SLAB /* We need also to migrate the object cache to the new * slab. We can just free the new and copy the old pointer */ free(n.cache); n.cache = t->cache; #endif /* AHT_USE_SLAB */ /* Initialize all the pointers to NULL */ memset(n.table, 0, realsize*sizeof(struct ht_ele*)); /* Copy all the elements from the old to the new table: * note that if the old hash table is empty t->size is zero, * so ht_expand() acts like an ht_create() */ n.used = t->used; for (i = 0; i < t->size && t->used > 0; i++) { if (t->table[i] != NULL && t->table[i] != ht_free_element) { u_int32_t h; /* Get the new element index: note that we * know that there aren't freed elements in 'n' */ h = n.hashf(t->table[i]->key) & n.sizemask; if (!n.table[h]) goto move; n.collisions++; while(1) { h = (h+1) & n.sizemask; if (!n.table[h]) break; n.collisions++; } move: /* Move the element */ n.table[h] = t->table[i]; t->used--; } } assert(t->used == 0); free(t->table); /* Remap the new hashtable in the old */ *t = n; return HT_OK; } /* Add an element, discarding the old if the key already exists */ int ht_replace(struct hashtable *t, void *key, void *data) { int ret; unsigned int index; /* Try to add the element */ ret = ht_add(t, key, data); if (ret == HT_OK || ret != HT_BUSY) return ret; /* It already exists, get the index */ ret = ht_search(t, key, &index); assert(ret == HT_FOUND); /* Remove the old */ ret = ht_free(t, index); assert(ret == HT_OK); /* And add the new */ return ht_add(t, key, data); } /* Add an element to the target hash table */ int ht_add(struct hashtable *t, void *key, void *data) { int ret; unsigned int index; /* If the element isn't in the table ht_insert() will store * the index of the free ht_ele in the integer pointer by *index */ ret = ht_insert(t, key, &index); if (ret != HT_OK) return ret; /* Allocates the memory and stores key */ #ifdef AHT_USE_SLAB if ((t->table[index] = slab_get_obj(t->cache)) == NULL) #else if ((t->table[index] = malloc(sizeof(struct ht_ele))) == NULL) #endif /* AHT_USE_SLAB */ return HT_NOMEM; /* Store the pointers */ t->table[index]->key = key; t->table[index]->data = data; t->used++; return HT_OK; } /* search and remove an element */ int ht_rm(struct hashtable *t, void *key) { int ret; unsigned int index; if ((ret = ht_search(t, key, &index)) != HT_FOUND) return ret; return ht_free(t, index); } /* Destroy an entire hash table */ int ht_destroy(struct hashtable *t) { unsigned int i; struct hashtable copy = *t; /* Free all the elements */ for (i = 0; i < t->size && t->used > 0; i++) { if (t->table[i] != NULL && t->table[i] != ht_free_element) { if (t->key_destructor) t->key_destructor(t->table[i]->key); if (t->val_destructor) t->val_destructor(t->table[i]->data); #ifndef AHT_USE_SLAB free(t->table[i]); #endif t->used--; } } #ifdef AHT_USE_SLAB slab_destroy(t->cache); #endif /* Free the table and the allocated cache structure */ free(t->table); #ifdef AHT_USE_SLAB free(t->cache); #endif /* Re-initialize the table */ ht_init(t); /* Restore methods */ t->hashf = copy.hashf; t->key_destructor = copy.key_destructor; t->val_destructor = copy.val_destructor; t->key_compare = copy.key_compare; return HT_OK; /* It can't fail ht_destroy never fails */ } /* Free an element in the hash table */ int ht_free(struct hashtable *t, unsigned int index) { if (index >= t->size) return HT_IOVERFLOW; /* Index overflow */ /* ht_free() calls against non-existent elements are ignored */ if (t->table[index] != NULL && t->table[index] != ht_free_element) { /* release the key */ if (t->key_destructor) t->key_destructor(t->table[index]->key); /* release the value */ if (t->val_destructor) t->val_destructor(t->table[index]->data); /* free the element structure */ #ifdef AHT_USE_SLAB slab_free_obj(t->cache, t->table[index]); #else free(t->table[index]); #endif /* AHT_USE_SLAB */ /* mark the element as freed */ t->table[index] = ht_free_element; t->used--; } return HT_OK; } /* Search the element with the given key */ int ht_search(struct hashtable *t, void *key, unsigned int *found_index) { int ret; u_int32_t h; /* Expand the hashtable if needed */ if (t->size == 0) { if ((ret = ht_expand_if_needed(t)) != HT_OK) return ret; } /* Try using the first hash functions */ h = t->hashf(key) & t->sizemask; /* this handles the removed elements */ if (!t->table[h]) return HT_NOTFOUND; if (t->table[h] != ht_free_element && t->key_compare(key, t->table[h]->key)) { *found_index = h; return HT_FOUND; } while(1) { h = (h+1) & t->sizemask; /* this handles the removed elements */ if (t->table[h] == ht_free_element) continue; if (!t->table[h]) return HT_NOTFOUND; if (t->key_compare(key, t->table[h]->key)) { *found_index = h; return HT_FOUND; } } } /* This function is used to run the entire hash table, * it returns: * 1 if the element with the given index is valid * 0 if the element with the given index is empty or marked free * -1 if the element if out of the range */ int ht_get_byindex(struct hashtable *t, unsigned int index) { if (index >= t->size) return -1; if (t->table[index] == NULL || t->table[index] == ht_free_element) return 0; return 1; } /* ------------------------- private functions ------------------------------ */ /* Expand the hash table if needed */ static int ht_expand_if_needed(struct hashtable *t) { /* If the hash table is empty expand it to the intial size, * if the table is half-full redobule its size. */ if (t->size == 0) return ht_expand(t, HT_INITIAL_SIZE); if (t->size <= (t->used << 1)) return ht_expand(t, t->size << 1); return HT_OK; } /* Our hash table capability is a power of two */ static unsigned int next_power(unsigned int size) { unsigned int i = 256; if (size >= 2147483648U) return 2147483648U; while(1) { if (i >= size) return i; i *= 2; } } /* the insert function to add elements out of ht expansion */ static int ht_insert(struct hashtable *t, void *key, unsigned int *avail_index) { int ret; u_int32_t h; /* Expand the hashtable if needed */ if ((ret = ht_expand_if_needed(t)) != HT_OK) return ret; /* Try using the first hash functions */ h = t->hashf(key) & t->sizemask; /* this handles the removed elements */ if (!t->table[h] || t->table[h] == ht_free_element) { *avail_index = h; return HT_OK; } t->collisions++; if (t->key_compare(key, t->table[h]->key)) return HT_BUSY; while(1) { h = (h+1) & t->sizemask; /* this handles the removed elements */ if (!t->table[h] || t->table[h] == ht_free_element) { *avail_index = h; return HT_OK; } t->collisions++; if (t->key_compare(key, t->table[h]->key)) return HT_BUSY; } } /* ------------------------- provided destructors --------------------------- */ /* destructor for heap allocated keys/values */ void ht_destructor_free(void *obj) { free(obj); } /* ------------------------- provided comparators --------------------------- */ /* default key_compare method */ int ht_compare_ptr(void *key1, void *key2) { return (key1 == key2); } /* key compare for nul-terminated strings */ int ht_compare_string(void *key1, void *key2) { return (strcmp(key1, key2) == 0) ? 1 : 0; } /* -------------------- hash functions for common data types --------------- */ /* We make this global to allow hash function randomization, * as security measure against attacker-induced worst case behaviuor. * * Note that being H_i the strong hash function with init value of i * and H_i' the same hash function with init value of i' than: * * if H_i(StringOne) is equal to H_i(CollidingStringTwo) * * it is NOT true that * * H_i'(StringOne) is equal to H_i''(CollidingStringTwo) */ static u_int32_t strong_hash_init_val = 0xF937A21; /* Set the secret initialization value. It should be set from * a secure PRNG like /dev/urandom at program initialization time */ void ht_set_strong_hash_init_val(u_int32_t secret) { strong_hash_init_val = secret; } /* __ht_strong_hash wrapper that mix a user-provided initval * with the global strong_hash_init_val. __ht_strong_hash is * even exported directly. */ u_int32_t ht_strong_hash(u_int8_t *k, u_int32_t length, u_int32_t initval) { return __ht_strong_hash(k, length, initval^strong_hash_init_val); } /* Hash function suitable for C strings and other data types using * a 0-byte as terminator */ u_int32_t ht_hash_string(void *key) { return __ht_strong_hash(key, strlen(key), strong_hash_init_val); } /* ------------------------------- memory ----------------------------------- */ #ifdef AHT_USE_SLAB #define SLAB_OBJFULSZ ((SLAB_OBJSZ)+(SLAB_PTRSZ)) /* minimum number of free elements to consider the slab not full */ #define SLAB_NOTFUL_THRE 32 /* get the slab pointer stored in the tail of the object */ #define SLAB_BY_PTR(ptr, slab) do { \ void **p = (void**)((unsigned char*)ptr + SLAB_OBJSZ); \ slab = *p; \ } while(0); /* store the slab ptr in the tail of the object */ #define SLAB_STORE_PTR(obj, slab) do { \ void **p = (void**)((unsigned char*)ptr + SLAB_OBJSZ); \ *p = slab; \ } while(0) #if 0 /* this works with unaligned data */ /* get the slab pointer stored in the tail of the object */ #define SLAB_BY_PTR(ptr, slab) do { \ memcpy(&slab, ((unsigned char*)ptr)+SLAB_OBJSZ, sizeof(void*)); \ } while(0) /* store the slab ptr in the tail of the object */ #define SLAB_STORE_PTR(ptr, slab) do { \ memcpy(((unsigned char*)ptr)+SLAB_OBJSZ, &slab, sizeof(void*)); \ } while(0) #endif u_int8_t slab_free_list_init[SLAB_ELE] = { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, }; static void slab_init(struct ht_cache *c) { c->head = NULL; c->tail = NULL; c->slabs = 0; } static void slab_destroy(struct ht_cache *c) { struct ht_slab *s = c->head, *t; while(s) { t = s->next; free(s); s = t; } } static void *slab_get_obj(struct ht_cache *c) { struct ht_slab *slab = c->head; void *ptr; /* allocation */ if (!slab || !slab->free) { slab = malloc(sizeof(struct ht_slab)); if (!slab) return NULL; /* link on head */ if (c->head) c->head->prev = slab; else c->tail = slab; slab->next = c->head; slab->prev = NULL; slab->free = SLAB_ELE; memcpy(slab->freelist, slab_free_list_init, SLAB_ELE); slab->parent = c; c->head = slab; c->slabs++; } /* get a free object */ slab->free--; ptr = slab->mem + (SLAB_OBJFULSZ * slab->freelist[slab->free]); /* if this slab is now full put it on the tail */ if (!slab->free && c->slabs > 1) { /* unlink from head */ c->head = slab->next; c->head->prev = NULL; /* link on tail */ c->tail->next = slab; slab->prev = c->tail; slab->next = NULL; c->tail = slab; } SLAB_STORE_PTR(ptr, slab); return ptr; } static void slab_free_obj(struct ht_cache *c, void *ptr) { struct ht_slab *slab; /* Obtain the slab pointer from the object */ SLAB_BY_PTR(ptr, slab); /* Update the free list and the free count */ slab->freelist[slab->free] = (ptr - (void*)slab->mem) / SLAB_OBJFULSZ; slab->free++; /* move this slab to the head if it reached the waterlevel */ if (slab->free == SLAB_NOTFUL_THRE && c->slabs > 1) { if (slab == c->head) return; /* unlink from middle or tail, we are not the head * so we can assume slab->prev != NULL */ slab->prev->next = slab->next; if (slab->next) { slab->next->prev = slab->prev; } else { c->tail = slab->prev; } /* put on the head */ slab->prev = NULL; slab->next = c->head; c->head->prev = slab; c->head = slab; return; } /* if this slab is empty: * 1) if it's already the head, free it if the next is not full. * 2) if the current head is full move it on the head * else destroy it */ if (slab->free == SLAB_ELE && c->slabs > 1) { if (slab == c->head) { if (!slab->next->free) return; /* unlink from head and free */ c->head = slab->next; c->head->prev = NULL; c->slabs--; free(slab); return; } /* unlink the slab, we can assume slab->prev != NULL */ slab->prev->next = slab->next; if (slab->next) { slab->next->prev = slab->prev; } else { c->tail = slab->prev; } /* the current head is not full? free this slab */ if (c->head->free) { c->slabs--; free(slab); return; } /* the current head is full, move this slab to the head */ slab->prev= NULL; slab->next = c->head; c->head->prev = slab; c->head = slab; return; } } #endif /* AHT_USE_SLAB */ yaku-ns-0.2/aht.h000066400000000000000000000073401425072213200136710ustar00rootroot00000000000000/* An implementation of hash tables: * Copyright(C) 2000-2002 by Salvatore Sanfilippo * * This software is under the GNU GPL license */ #include #include "ens.h" #ifndef _AHT_H #define _AHT_H /* #define AHT_USE_SLAB */ /* ------------------------------ exit codes -------------------------------- */ #define HT_OK 0 /* Success */ #define HT_FOUND 1 /* Key found */ #define HT_NOTFOUND 2 /* Key not found */ #define HT_BUSY 3 /* Key already exist */ #define HT_NOMEM 4 /* Out of memory */ #define HT_IOVERFLOW 5 /* Index overflow */ #define HT_INVALID 6 /* Invalid argument */ #define HT_INITIAL_SIZE 256 /* ------------------ structures for the object allocator ------------------- */ #ifdef AHT_USE_SLAB #define SLAB_ELE 256 /* cant be > of 256 */ #define SLAB_OBJSZ 8 /* need to be multiple of 4 */ #define SLAB_PTRSZ (sizeof(void*)) struct ht_cache; /* forward declaration */ struct ht_slab { struct ht_slab *next; struct ht_slab *prev; struct ht_cache *parent; u_int32_t free; u_int8_t freelist[SLAB_ELE]; unsigned char mem[SLAB_ELE*(SLAB_OBJSZ+SLAB_PTRSZ)]; }; struct ht_cache { struct ht_slab *head; struct ht_slab *tail; u_int32_t slabs; }; #endif /* AHT_USE_SLAB */ /* ----------------------- hash table structures -----------------------------*/ struct ht_ele { void *key; void *data; }; struct hashtable { struct ht_ele **table; unsigned int size; unsigned int sizemask; unsigned int used; unsigned int collisions; u_int32_t (*hashf)(void *key); int (*key_compare)(void *key1, void *key2); void (*key_destructor)(void *key); void (*val_destructor)(void *obj); #ifdef AHT_USE_SLAB struct ht_cache *cache; #endif }; /* ----------------------------- Prototypes ----------------------------------*/ int ht_init(struct hashtable *t); int ht_move(struct hashtable *orig, struct hashtable *dest, unsigned int index); int ht_expand(struct hashtable *t, size_t size); int ht_add(struct hashtable *t, void *key, void *data); int ht_replace(struct hashtable *t, void *key, void *data); int ht_rm(struct hashtable *t, void *key); int ht_destroy(struct hashtable *t); int ht_free(struct hashtable *t, unsigned int index); int ht_search(struct hashtable *t, void *key, unsigned int *found_index); int ht_get_byindex(struct hashtable *t, unsigned int index); int ht_resize(struct hashtable *t); /* provided destructors */ void ht_destructor_free(void *obj); #define ht_no_destructor NULL /* provided compare functions */ int ht_compare_ptr(void *key1, void *key2); int ht_compare_string(void *key1, void *key2); /* ------------------------ The hash functions ------------------------------ */ u_int32_t djb_hash(unsigned char *buf, size_t len); u_int32_t djb_hashR(unsigned char *buf, size_t len); u_int32_t trivial_hash(unsigned char *buf, size_t len); u_int32_t trivial_hashR(unsigned char *buf, size_t len); u_int32_t ht_strong_hash(u_int8_t *k, u_int32_t length, u_int32_t initval); u_int32_t __ht_strong_hash(u_int8_t *k, u_int32_t length, u_int32_t initval); /* ----------------- hash functions for common data types ------------------- */ u_int32_t ht_hash_string(void *key); /* ----------------------------- macros --------------------------------------*/ #define ht_set_hash(t,f) ((t)->hashf = (f)) #define ht_set_key_destructor(t,f) ((t)->key_destructor = (f)) #define ht_set_val_destructor(t,f) ((t)->val_destructor = (f)) #define ht_set_key_compare(t,f) ((t)->key_compare = (f)) #define ht_collisions(t) ((t)->collisions) #define ht_size(t) ((t)->size) #define ht_used(t) ((t)->used) #define ht_key(t, i) ((t)->table[(i)]->key) #define ht_value(t, i) ((t)->table[(i)]->data) #if AHT_DEBUG #define dprintf(x...) do { printf(x); fflush(stdout); } while(0) #else #define dprintf(x...) #endif #endif /* _AHT_H */ yaku-ns-0.2/arr.c000066400000000000000000000050671425072213200137000ustar00rootroot00000000000000/* arr.c * Additonal Resource Records * (check the function build_response() in dns.c for more related code) * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include /* not exported functions */ /* exported functions */ int additional_rr_needed(struct additionalrr *arr, struct RRentry *rr, int arrindex); /* Use ONLY this macro to add the next ARR, this automatically * takes the count of the max additional ARR allowed and free the * allocated memory for the name if the ARR list is full. */ #define ADD_ARR(arrtype, arrclass, arrname) \ do { \ if (arrindex+additional_count < MAX_ADDRR) {\ arr->qtype = (arrtype); \ arr->qclass = (arrclass); \ arr->name = (arrname); \ additional_count++; \ arr++; \ } else { \ free(arrname); \ } \ } while(0) /* This function is called for every Resource Record added to the response: * it compiles a list of the additional RR needed, for every requested * RR. For example if the query_processor() adds an MX IN RR, when * it calls this function the additioanl RR list will be populated with * an A IN RR. Warning: This function doesn't add the RR in the packet, * it just compile the list of the RRs that will be useful to add. The * additional RRs are added (usually) by the caller function. */ int additional_rr_needed(struct additionalrr *arr, struct RRentry *rr, int arrindex) { int additional_count = 0; /* MX needs an additional A RR with the address of the mail exchange */ if (rr->qtype == T_MX) { byte *mxname_pointer; char *mxname; int retval; /* compute the offset to find the name inside the MX record */ mxname_pointer = rr->data+sizeof(struct RR_MX); retval = name_decode(mxname_pointer, (rr->size)-sizeof(struct RR_MX), NULL, &mxname, 0); yakuns_assert(retval != YK_INVALID); if (retval == YK_NOMEM) goto out; ADD_ARR(T_A, C_ANY, mxname); } /* NS needs an additional A RR with the address of the name server */ if (rr->qtype == T_NS) { byte *nsname_pointer; char *nsname; int retval; /* compute the offset to find the name inside the NS record */ nsname_pointer = rr->data; retval = name_decode(nsname_pointer, rr->size, NULL, &nsname, 0); yakuns_assert(retval != YK_INVALID); if (retval == YK_NOMEM) goto out; ADD_ARR(T_A, C_ANY, nsname); } out: /* add the nul term */ arr->qtype = 0; arr->qclass = 0; arr->name = NULL; return additional_count; } yaku-ns-0.2/assert.h000066400000000000000000000011201425072213200144040ustar00rootroot00000000000000/* assert.h * * just the assert that uses the DNS server logging * * Copyright (C) 2000-2001 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ #ifndef ENS_ASSERT_H #define ENS_ASSERT_H #ifdef NDEBUG #define assert(expr) do { } while(0) #else /* !NDEBUG */ #define assert(x) \ do { \ if ((x) == 0) { \ ylog(VERB_FORCE, \ "assert failed: %s is false in %s at line %d\n", \ #x, __FILE__, __LINE__); \ abort(); \ } \ } while(0) #endif /* !NDEBUG */ #endif /* ENS_ASSERT_H */ yaku-ns-0.2/autoptr.c000066400000000000000000000027671425072213200146160ustar00rootroot00000000000000/* autoptr.c * autoptr code. * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ #include "ens.h" #include #include /* global */ int opt_autoptr = 0; /* The function converts the IP address x.y.z.k to * the name in-addr.arpa.k.z.y.x, but accepts in * input only valid IP addresses. * WARNING: The dest buffer MUST be at least 32 bytes long */ int inet_toinaddr(char *addr, char *dest) { char tmp[32]; /* in-addr.arpa.xxx.yyy.zzz.kkk+\0 */ char *p; size_t l = strlen(addr), i; /* xxx.yyy.zzz.kkk = 15 bytes * x.y.z.k = 7 bytes * accepted chars are only 0123456789. */ if (l > 15 || l < 4 || strspn(addr, "0123456789.") != l || strstr(addr, "..") || addr[0] == '.' || addr[l-1] == '.') return CERROR_BADIP; dest[0] = '\0'; memcpy(tmp, addr, l+1); for(i = 0; i < 3; i++) { if((p = strrchr(tmp, '.')) == NULL) return CERROR_BADIP; *p = '\0'; if (atoi(p+1) > 255 || atoi(p+1) < 0) return CERROR_BADIP; strlcat(dest, p+1, 32); strlcat(dest, ".", 32); } strlcat(dest, tmp, 32); strlcat(dest, ".in-addr.arpa", 32); return 0; } #ifdef TESTMAIN #include int main(int argc, char **argv) { char buffer[32]; if (argc != 2) { printf("usage: program
\n"); exit(1); } if (inet_toinaddr(argv[1], buffer) != 0) { printf("Forname error\n"); } else { printf("%s\n", buffer); } return 0; } #endif yaku-ns-0.2/axfr_out.c000066400000000000000000000303021425072213200147310ustar00rootroot00000000000000/* afxr_out.c * Zone transfer implementation (Output) * and DNS over TCP only for AXFR. * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ /* STATUS * * OK: behaves as desidered * TODO: behaves in some different way or the * feature is not supported. * IGNORED: behaves in some different way since it * is wanted. * * axfr-clarity draft: * o Support for zone trasfer over TCP: OK * o If the master server can't provide a zone transfer send an error: TODO * for now it closes the TCP connection. * o Master MAY send multiple answers per message, up to 65535 bytes: OK * o Masters that support multiple RRs in the same message SHOULD be * configurable to send one RR for message: OK * o If the zone transefer does not fit in 65535 bytes the master MUST send * more messages: OK * o SHOULD compress the names in the zone transfer: IGNORED * o The header of the message MUST be the following: * ID Copy from request: OK * QR 1: OK * OPCODE QUERY: OK * AA 1 (but MAY be 0 when RCODE is nonzero): OK * TC 0: OK * RD Copy from request: OK * RA Set according to availability of recursion: OK * Z 000: OK * RCODE 0 or error code: TODO, now the error isn't sent. * o No additional section processing: OK * o The question section SHOULD be the same as the request one * for the first message: OK * o Subsequent messages SHOULD NOT have a question section: OK * o The master server MUST send messages with an empty authority * section: OK. * o The first and latest RR transimtted MUST be the SOA record * for the zone: OK * o The initial and final SOA records MUST be tha same: OK * o The transfer order of all the other RRs in the zone is undefinied: OK * * RFC1034: * o If the server needs to close a dormant connection to reclaim resource * it SHOULD wait until the connection has been idle for a period on * the order of two minutes: OK * * RFC1035: * o Zone refresh and reload processing: * o If a master is sending a zone out via AXFR, and a new * version is created during the transfer, the master SHOULD * continue to send the old version. In any case, it SHOULD * never send part of one version and part of the others: OK */ /* ens.h must be included before all other includes */ #include "ens.h" #include "aht.h" #include #include #include #include #include #include #include #include #include #include #include #define AXFR_CLIENT_MAX 50 #define AXFR_CLIENT_TIMEOUT 120 /* At least 2 min, see RFC1034 */ #define TCP_REQS_DEFAULT 2 /* SOA + AXFR */ /* global vars */ int opt_axfr = 0; int opt_axfr_compatmode = 1; int opt_tcp_requests_for_connection = TCP_REQS_DEFAULT; int opt_tcp_port = DNS_PORT; /* Default is port 53, override it with -P */ int tcp_s; /* the TCP socket */ /* local vars */ static volatile int axfr_clients = 0; /* current number of clients */ /* not exported functions */ static void send_zone(HEADER *hdr, char *query, int querysize, char *zone, int clientsocket); static void send_soa(HEADER *hdr, char *query, int querysize, char *zone, int clientsocket); static int match_zone(char *name, char *zone); static struct RRentry *search_soa(char *zone); static void axfr_sigchld(int sid); /* exported functions */ int axfr_init(void); void tcp_handler(void); /* -------------------------------------------------------------------------- */ /* Initialize the AXFR capability */ int axfr_init(void) { struct sockaddr_in sa; int on = 1; tcp_s = socket(AF_INET, SOCK_STREAM, 0); if (tcp_s == -1) { perror("[axfr_init] socket"); return -1; } if (setsockopt(tcp_s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { perror("[axfr_init] warning: setsockopt(SO_REUSEADDR)"); } /* fill the address structure */ sa.sin_family = AF_INET; sa.sin_port = htons(opt_tcp_port); if (!opt_bindaddr) { sa.sin_addr.s_addr = htonl(INADDR_ANY); /* all interfaces */ } else { struct in_addr tmp; if (inet_aton(bindaddr, &tmp) == 0) { ylog(VERB_FORCE, "[axfr_init] bad IP address " "for binding\n"); exit(1); } sa.sin_addr.s_addr = tmp.s_addr; } /* bind the socket */ if (bind(tcp_s, (struct sockaddr*) &sa, sizeof(sa)) == -1) { perror("[axfr_init] bind"); close(tcp_s); return -1; } if (listen(tcp_s, 5) == -1) { perror("[axfr_init] listen"); close(tcp_s); return -1; } Signal(SIGCHLD, axfr_sigchld); signal_block(SIGCHLD); return 0; } /* Handle the SIGCHLD, taking the count of the clients connected */ static void axfr_sigchld(int sid) { pid_t pid; int stat; ARG_UNUSED(sid) while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) { axfr_clients--; } yakuns_assert(axfr_clients >= 0); return; } /* Called on timeout */ static void axfr_timeout(int sid) { ARG_UNUSED(sid); exit(0); /* kill the process */ } /* Handle a new TCP connection -- this DNS server uses the DNS over TCP * only for the zone transfer */ void tcp_handler(void) { HEADER *hdr; byte tmp[2]; char request[512]; /* even under TCP we limit to 512 bytes */ byte *data; /* points after the DNS header */ int data_size; /* data size left */ socklen_t addrlen; int new; /* new socket fd */ int query_count; /* question sections in the query */ unsigned int size; /* query size */ int retval, n_read; u_int16_t qtype, qclass; char *name = NULL; char straddr[64]; /* string converted IP address */ pid_t childpid; struct sockaddr_in newsa; /* accept the connection */ addrlen = sizeof(newsa); /* Allow SIGCHLD delivery only here, to avoid counter inc/dec races */ // XXX: signal block/unblock moved to the core() function. //signal_unblock(SIGCHLD); /* accept can't block here, since we are here by select(2) */ new = accept(tcp_s, (struct sockaddr*) &newsa, &addrlen); //signal_block(SIGCHLD); if (new == -1) { perror("[tcp_handler] accept"); goto out; } strlcpy(straddr, inet_ntoa(newsa.sin_addr), 64); /* Check the ACL */ if (acl_check_axfr(straddr) == ACL_DENY) { ylog(VERB_MED, "AXFR: access denied to client %s-%d\n", straddr, ntohs(newsa.sin_port)); goto out; } /* Max number of clients reached? */ if (axfr_clients >= AXFR_CLIENT_MAX) { ylog(VERB_MED, "AXFR: too many AXFR clients, " "access denied to %s-%d\n", straddr, ntohs(newsa.sin_port)); goto out; } /* The client's IP isn't denied, so we can fork */ axfr_clients++; if ((childpid = fork()) == -1) { axfr_clients--; perror("[tcp_handler] fork"); goto out; } if (childpid == 0) { int left_requests = opt_tcp_requests_for_connection; Signal(SIGALRM, axfr_timeout); alarm(AXFR_CLIENT_TIMEOUT); /* set the timeout */ while(left_requests-- || (opt_tcp_requests_for_connection == 0)) { n_read = recv(new, tmp, 2, 0); /* read the query size */ if (n_read != 2) goto child_out; size = (tmp[0] << 8) | tmp[1]; /* endianess conv. */ if (size > 512) goto child_out; /* read the request */ n_read = recv(new, request, size, 0); if (n_read <= 0 || (unsigned)n_read != size) goto child_out; /* SANITY CHECKS: * packet too short * accept only standard query * accept only one query section * valid name * enough space for qtype/class */ hdr = (HEADER*) request; if (size < sizeof(HEADER) || hdr->qr == 1 || hdr->opcode != 0) goto child_out; data_size = size; data = (unsigned char*)request + sizeof(HEADER); query_count = ntohs(hdr->qdcount); if (query_count != 1) goto child_out; /* parse the query */ retval = name_decode(data, data_size, (unsigned char*)request, &name, 1); if (name == NULL) goto child_out; updatep(retval); if (data_size < 4) goto child_out; qtype = (data[0] << 8) | data[1]; qclass = (data[2] << 8) | data[3]; updatep(4); /* The only two accepted requests under TCP are * IN/AXFR and IN/SOA */ if (qclass == C_IN && qtype == T_AXFR) { ylog(VERB_LOW, "AXFR requested for (%s) from " "%s-%d\n", name, straddr, ntohs(newsa.sin_port)); send_zone(hdr, request+sizeof(HEADER), retval+4, name, new); } else if (qclass == C_IN && qtype == T_SOA) { ylog(VERB_MED, "TCP IN SOA requested for (%s) from " "%s-%d\n", name, straddr, ntohs(newsa.sin_port)); send_soa(hdr, request+sizeof(HEADER), retval+4, name, new); } else { ylog(VERB_MED, "TCP unaccepted request (%s %s) " "from %s-%d\n", qclass_to_str(qclass), qtype_to_str(qtype), straddr, ntohs(newsa.sin_port)); goto child_out; } free(name); name = NULL; } child_out: free(name); exit(1); } out: if (new != -1) close(new); return; } /* Send the AXFR reply under TCP */ static void send_zone(HEADER *hdr, char *query, int querysize, char *zone, int clientsocket) { int size = 0, retval; byte *response = NULL, *tmp; struct RRentry *rr, *soa; retval = build_header(&response, hdr, 1); if (retval == YK_NOMEM) goto oom; size += retval; /* Add the question section: * Only the first message contains this section */ if ((tmp = realloc(response, size+querysize)) == NULL) goto oom; response = tmp; memcpy(response+size, query, querysize); size += querysize; /* Lookup the SOA RR */ soa = search_soa(zone); if (soa) { unsigned int index = 0; int ret; /* The first RR is the SOA */ retval = add_rr(&response, hdr, soa, size, AN_SECTION, 0); if (retval == YK_NOMEM) goto oom; size += retval; /* If compatibility mode is enabled send the first * message with the SOA RR */ if (opt_axfr_compatmode) goto sendit; while ((ret = ht_get_byindex(&local_table, index)) != -1) { index++; if (ret == 0) continue; rr = ht_value(&local_table, index); if (rr->qtype == T_SOA || !match_zone(rr->name, zone)) continue; /* Add this RR */ retval = add_rr(&response, hdr, rr, size, AN_SECTION, 0); if (retval == YK_NOMEM) goto oom; size += retval; sendit: /* Send the message if reaches 60000 bytes: * we are assuming that a single RR can't be * more than 5535 bytes. */ if (opt_axfr_compatmode || size >= 60000) { HEADER *newhdr; send_tcp(response, size, clientsocket); free(response); response = NULL; size = 0; size += build_header(&response, hdr, 1); newhdr = (HEADER*) response; newhdr->qdcount = 0; } } /* The last RR is the SOA */ retval = add_rr(&response, hdr, soa, size, AN_SECTION, 0); if (retval == YK_NOMEM) goto oom; size += retval; } send_tcp(response, size, clientsocket); free(response); return; oom: /* We can't do much better than this here */ free(response); perror("[send_zone] allocating memory"); exit(1); } /* Send the SOA reply under TCP */ static void send_soa(HEADER *hdr, char *query, int querysize, char *zone, int clientsocket) { int size = 0, retval; byte *response = NULL, *tmp; struct RRentry *soa; retval = build_header(&response, hdr, 1); if (retval == YK_NOMEM) goto oom; size += retval; /* Add the question section: * Only the first message contains this section */ if ((tmp = realloc(response, size+querysize)) == NULL) goto oom; response = tmp; memcpy(response+size, query, querysize); size += querysize; /* Lookup the SOA RR */ soa = search_soa(zone); if (soa) { retval = add_rr(&response, hdr, soa, size, AN_SECTION, 0); if (retval == YK_NOMEM) goto oom; size += retval; } send_tcp(response, size, clientsocket); free(response); return; oom: /* We can't do much better than this here */ free(response); perror("[send_soa] allocating memory"); exit(1); } /* Look for the SOA record for the given zone */ static struct RRentry *search_soa(char *zone) { return local_search(zone, T_SOA, C_IN, 0); } /* Check if a name matches a zone */ static int match_zone(char *name, char *zone) { if (strlen(name) < strlen(zone)) return 0; if (strlen(name) == strlen(zone)) return !strcmp(name, zone); if (!strcasecmp(name+strlen(name)-strlen(zone), zone) && *(name+strlen(name)-strlen(zone)-1) == '.') return 1; return 0; } yaku-ns-0.2/cache.c000066400000000000000000000171651425072213200141610ustar00rootroot00000000000000/* cache.c * ENS cache * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information * * STATUS * * OK: behaves as desidered * TODO: behaves in some different way or the * feature is not supported. * IGNORED: behaves in some different way since it * is wanted. * * RFC 1035: * o Using the cache, should NOT be cached: * o Cached RRs TTL should conceptually cont down: OK * o When several RRs of the same type are available for a * particular owner name, the resolver should either cache them * all or none at all: IGNORED, ENS caches the whole datagram. * o When a response is truncated, and a * resolver doesn't know whether it has a complete set, it should * not cache a possibly partial set of RRs: OK * o Cached data should never be used in preference to * authoritative data, so if caching would cause this to happen * the data should not be cached: OK, we first try to meet the * request using the local Resource Records. * o The results of an inverse query should not be cached: IGNORED * since inverse query aren't supported. * o Should not be cached: * The results of standard queries where the QNAME contains "*" * labels if the data might be used to construct wildcards. The * reason is that the cache does not necessarily contain existing * RRs or zone boundary information which is necessary to * restrict the application of the wildcard RRs: TODO * o RR data in responses of dubious reliability. When a resolver * receives unsolicited responses or RR data other than that * requested, it should discard it without caching it. The basic * implication is that all sanity checks on a packet should be * performed before any of it is cached: OK * o In a similar vein, when a resolver has a set of RRs for some * name in a response, and wants to cache the RRs, it should check * its cache for already existing RRs: ENS cache the whole response * packet, not single RRs, so it don't cache responses to a query * if there is already a response for the same query in the cache. */ /* ens.h must be included before all other includes */ #include "ens.h" unsigned int cache_count = 0; /* number of cached responses */ #include #include #include "aht.h" /* global vars */ struct hashtable cache_table; unsigned int cache_max = CACHE_MAX; unsigned int cache_maxttl = CACHE_MAX_TTL; unsigned int cache_minttl = CACHE_MIN_TTL; int opt_cachenoexpire = 0; /* not exported functions */ static u_int32_t cache_get_min_ttl(byte *packet, int packet_size); /* exported functions */ void cache_add_entry(struct forwardentry *p, byte *packet, int packet_size); void cache_free_oldest(void); int cache_free_expired(void); struct cacheentry *cache_search_entry(char *name, int qclass, int qtype); /* The destructor for the cache entry */ void ht_cache_destructor(void *obj) { struct cacheentry *cache = obj; free(cache->name); free(cache->answer); free(cache); } /* Add an entry in the cache hash table */ void cache_add_entry(struct forwardentry *p, byte *packet, int packet_size) { struct cacheentry *cache; char key[HT_MAX_KEYSIZE], *k; int ret; size_t klen; /* If the cache is full try to free the expired entry, * if there aren't free the oldest entry in access time */ if (cache_count >= cache_max) { if (cache_free_expired() == 0) cache_free_oldest(); } if ((cache = malloc(sizeof(struct cacheentry))) == NULL) goto oom1; /* fill the new entry */ cache->name = malloc(strlen(p->name)+1); if (cache->name == NULL) goto oom2; strlcpy(cache->name, p->name, strlen(p->name)+1); cache->qtype = p->qtype; cache->qclass = p->qclass; cache->answer_size = packet_size; cache->answer = malloc(packet_size); if (cache->answer == NULL) goto oom3; memcpy(cache->answer, packet, packet_size); cache->ttl = cache_get_min_ttl(packet, packet_size); cache->creat_timestamp = cache->ttlupdate_timestamp = cache->last_timestamp = get_sec(); cache->hits = 0; klen = rr_to_key(key, HT_MAX_KEYSIZE, cache->name, cache->qtype, cache->qclass, 0); if ((k = malloc(klen)) == NULL) goto oom4; memcpy(k, key, klen); ret = ht_add(&cache_table, k, cache); if (ret != HT_OK) goto oom4; cache_count++; return; oom4: free(cache->answer); oom3: free(cache->name); oom2: free(cache); oom1: return; } /* Free the oldest element in the cache (oldest in access time) */ void cache_free_oldest(void) { unsigned int index = 0, oldest_index = 0; int ret; struct cacheentry *oldest = NULL, *current; yakuns_assert(cache_table.used > 0); /* search in the cache table for the oldest entry, * XXX: better to remove a random element? It's at least * _much_ fater */ while((ret = ht_get_byindex(&cache_table, index)) != -1) { if (ret == 0) { index++; continue; } current = ht_value(&cache_table, index); if (oldest == NULL || current->last_timestamp < oldest->last_timestamp) { oldest = current; oldest_index = index; } index++; } if (oldest) { ht_free(&cache_table, oldest_index); cache_count--; } return; } /* Free the expired elements in the cache table */ int cache_free_expired(void) { unsigned int index = 0; int ret; struct cacheentry *current; time_t now = get_sec(); int expired = 0; if (cache_table.used == 0 || opt_cachenoexpire) return 0; /* search in the cache table for expired entries */ while((ret = ht_get_byindex(&cache_table, index)) != -1) { if (ret == 0) { index++; continue; } current = ht_value(&cache_table, index); if (current->creat_timestamp + current->ttl <= (unsigned)now) { ylog(VERB_HIG, "Expired cache entry %s %s %s\n", qtype_to_str(current->qtype), qclass_to_str(current->qclass), current->name); ht_free(&cache_table, index); cache_count--; expired++; if (cache_count == 0) break; } index++; } return expired; } struct cacheentry *cache_search_entry(char *name, int qclass, int qtype) { char key[HT_MAX_KEYSIZE]; int ret; unsigned int i; struct cacheentry *cache; time_t now = get_sec(); rr_to_key(key, HT_MAX_KEYSIZE, name, qtype, qclass, 0); ret = ht_search(&cache_table, key, &i); if (ret == HT_FOUND) { cache = ht_value(&cache_table, i); /* Expired? Free the entry and return NULL */ if (opt_cachenoexpire == 0 && cache->creat_timestamp + cache->ttl <= (unsigned)now) { ylog(VERB_HIG, "Expired cache entry %s %s %s\n", qtype_to_str(cache->qtype), qclass_to_str(cache->qclass), cache->name); ht_free(&cache_table, i); cache_count--; return NULL; } /* Adjust the access time and return the element */ cache->last_timestamp = get_sec(); return cache; } return NULL; } void cache_fix_ttl(struct cacheentry *cache) { fix_ttl(cache->answer, cache->answer_size, cache->ttlupdate_timestamp, get_sec()); cache->ttlupdate_timestamp = get_sec(); } void cache_shuffle(struct cacheentry *cache) { dns_shuffle(cache->answer, cache->answer_size); } static u_int32_t cache_get_min_ttl(byte *packet, int packet_size) { u_int32_t ttl = get_min_ttl(packet, packet_size); /* Adjust it */ if (ttl < cache_minttl) ttl = cache_minttl; else if (ttl > cache_maxttl) ttl = cache_maxttl; return ttl; } void cache_init(void) { ht_init(&cache_table); ht_set_hash(&cache_table, ht_dnskey_hash); ht_set_key_destructor(&cache_table, ht_destructor_free); ht_set_val_destructor(&cache_table, ht_cache_destructor); ht_set_key_compare(&cache_table, ht_dnskey_compare); } yaku-ns-0.2/config.c000066400000000000000000000273551425072213200143650ustar00rootroot00000000000000/* config.c * Configuration-related code * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include /* not exported functions */ static void config_error(int linenum, char *line, char *errormsg); static int op_include(int argc, char **argv); static int op_forwarder(int argc, char **argv); static int op_forward_max(int argc, char **argv); static int op_forward_entry_timeout(int argc, char **argv); static int op_forward_next_timeout(int argc, char **argv); static int op_cache_max(int argc, char **argv); static int op_cache_minttl(int argc, char **argv); static int op_cache_maxttl(int argc, char **argv); static int op_logfile(int argc, char **argv); static int op_loglevel(int argc, char **argv); static int op_ttl(int argc, char **argv); static int op_class(int argc, char **argv); static int op_acl(int argc, char **argv); static int op_cname(int argc, char **argv); static int op_mx(int argc, char **argv); static int op_ptr(int argc, char **argv); static int op_txt(int argc, char **argv); static int op_ns(int argc, char **argv); static int op_soa(int argc, char **argv); static int op_a(int argc, char **argv); static int op_tcp_requests_for_connection(int argc, char **argv); static int op_generic_enable_disable(int argc, char **argv); /* exported functions */ void config_reset(void); int config_read(char *filename); char *config_process_line(char *line); char *strcerror(int cerror); static struct config_op { char *cmd; int argc; int (*op)(int argc, char **argv); } config_table[] = { {"include", 2, op_include}, {"logfile", 2, op_logfile}, {"loglevel", 2, op_loglevel}, {"acl", -1, op_acl}, {"uptime", 1, op_generic_enable_disable}, {"nologtime", 1, op_generic_enable_disable}, {"wildcard_lookup", 1, op_generic_enable_disable}, {"nameserver", 2, op_forwarder}, {"forward_max", 2, op_forward_max}, {"forward_entry_timeout", 2, op_forward_entry_timeout}, {"forward_next_timeout", 2, op_forward_next_timeout}, {"cache_max", 2, op_cache_max}, {"cache_minttl", 2, op_cache_minttl}, {"cache_maxttl", 2, op_cache_maxttl}, {"cache_noexpire", 1, op_generic_enable_disable}, {"ttl", 2, op_ttl}, {"class", 2, op_class}, {"a", 3, op_a}, {"cname", 3, op_cname}, {"mx", 4, op_mx}, {"ptr", 3, op_ptr}, {"txt", -3, op_txt}, {"ns", 3, op_ns}, {"soa", 9, op_soa}, {"autoptr", 1, op_generic_enable_disable}, {"noautoptr", 1, op_generic_enable_disable}, {"axfr_more_rr", 1, op_generic_enable_disable}, {"tcp_requests_for_connection", 2, op_tcp_requests_for_connection}, {NULL, 0, NULL} }; char *cerror_list[] = { "Success", /* 0 */ "Wrong number of arguments", /* 1 */ "Bad IP address", /* 2 */ "Bad ACL", /* 3 */ "Bad domain name", /* 4 */ "TXT format error, label too long?", /* 5 */ "Invalid argument", /* 6 */ "Out of memory", /* 7 */ "No space left", /* 8 */ "Permission denied" /* 9 */ }; char *strcerror(int cerror) { if (cerror < 0 || cerror > CERROR_MAX_CERROR) return "Unknown error"; return cerror_list[cerror]; } char *config_process_line(char *line) { int line_argc, cerror; char *line_argv[LINEARGS_MAX+1]; struct config_op *cop = config_table; char *e = "Unknown RR type"; line_argc = line_splitter(line, line_argv, LINEARGS_MAX); if (line_argc == 0) { (void) line_splitter(NULL, line_argv, 0); /* free */ return NULL; } while(cop->cmd) { if (!strcasecmp(line_argv[0], cop->cmd)) { e = NULL; if ((cop->argc > 0 && line_argc != cop->argc) || (cop->argc < 0 && line_argc < -cop->argc)) { e = strcerror(CERROR_ARGNUM); break; } cerror = cop->op(line_argc, line_argv); if (cerror != CERROR_SUCCESS) { e = strcerror(cerror); break; } break; } cop++; } (void) line_splitter(NULL, line_argv, 0); /* free */ return e; } int config_read(char *filename) { FILE *fp; char line[1024]; int line_count = 1; if (!strcmp(filename, "-") && !opt_daemon) { fp = stdin; } else { fp = fopen(filename, "r"); if (fp == NULL) { ylog(VERB_FORCE, "Can't open the config file %s\n", filename); perror("fopen"); ylog(VERB_FORCE, "Remember that you MUST " "specify the absolute path\n"); exit(1); } } while (fgets(line, 1024, fp) != NULL) { char *e; if (line[0] == '#') { line_count++; continue; } if ((e = config_process_line(line)) != NULL) config_error(line_count, line, e); line_count++; } if (fp != stdin) fclose(fp); return 0; } static void config_error(int linenum, char *line, char *errormsg) { ylog(VERB_FORCE, "--\n`%s' at line %d\n", errormsg, linenum); ylog(VERB_FORCE, "%d: %s--\n", linenum, line); exit(1); } void config_reset(void) { opt_forward = 0; forward_server_count = 0; opt_logtime = 1; local_free(); acl_free(); } static int op_include(int argc, char **argv) { ARG_UNUSED(argc) if (securelevel >= 1) return CERROR_PERM; ylog(VERB_HIG, "> include %s\n", argv[1]); config_read(argv[1]); ylog(VERB_HIG, "< end of inclusion of %s\n", argv[1]); return CERROR_SUCCESS; } static int op_forwarder(int argc, char **argv) { ARG_UNUSED(argc) if (forward_server_count == MAX_FORWARD_SERVERS) return CERROR_NOSPACE; if (inet_aton(argv[1], &forward_server[forward_server_count]) == 0) return CERROR_BADIP; opt_forward = 1; ylog(VERB_HIG, "(forwarding) external server: %s\n", argv[1]); forward_server_count++; /* accept responses from this external server */ acl_add_rule(argv[1], &acl_dns_allow_head, &acl_dns_allow_tail); return CERROR_SUCCESS; } static int op_forward_max(int argc, char **argv) { ARG_UNUSED(argc) forward_max = atoi(argv[1]); if (forward_max <= 0) { forward_max = 0; opt_forward = 0; ylog(VERB_MED, "forwarding disabled\n"); } else { ylog(VERB_HIG, "forwarding: max queue %d\n", forward_max); } return CERROR_SUCCESS; } static int op_forward_entry_timeout(int argc, char **argv) { ARG_UNUSED(argc) forward_timeout = atoi(argv[1]); if (forward_timeout <= 0) { return CERROR_INVALID; } else { ylog(VERB_HIG, "forwarding: entry timeout %d\n", forward_timeout); } return CERROR_SUCCESS; } static int op_forward_next_timeout(int argc, char **argv) { ARG_UNUSED(argc) next_server_timeout = atoi(argv[1]); if (next_server_timeout < 0) { return CERROR_INVALID; } else { ylog(VERB_HIG, "forwarding: next server timeout %d\n", next_server_timeout); } return CERROR_SUCCESS; } static int op_cache_max(int argc, char **argv) { ARG_UNUSED(argc) cache_max = atoi(argv[1]); if (cache_max <= 0) { cache_max = 0; opt_cache = 0; ylog(VERB_MED, "cache: disabled\n"); } else { ylog(VERB_HIG, "cache: max size %d\n", cache_max); } return CERROR_SUCCESS; } static int op_cache_minttl(int argc, char **argv) { ARG_UNUSED(argc) cache_minttl = atoi(argv[1]); ylog(VERB_HIG, "cache: min TTL %d\n", cache_minttl); return CERROR_SUCCESS; } static int op_cache_maxttl(int argc, char **argv) { ARG_UNUSED(argc) cache_maxttl = atoi(argv[1]); ylog(VERB_HIG, "cache: max TTL %d\n", cache_maxttl); return CERROR_SUCCESS; } static int op_logfile(int argc, char **argv) { ARG_UNUSED(argc) if (securelevel >= 1) return CERROR_PERM; strlcpy(logfile, argv[1], 1024); opt_logfile = 1; return CERROR_SUCCESS; } static int op_loglevel(int argc, char **argv) { ARG_UNUSED(argc) if (securelevel >= 1) return CERROR_PERM; if (!strcasecmp(argv[1], "errors")) { opt_verbose = VERB_FORCE; } else if (!strcasecmp(argv[1], "low")) { opt_verbose = VERB_LOW; } else if (!strcasecmp(argv[1], "med")) { opt_verbose = VERB_MED; } else if (!strcasecmp(argv[1], "high")) { opt_verbose = VERB_HIG; } else if (!strcasecmp(argv[1], "debug")) { opt_verbose = VERB_DEBUG; } else { return CERROR_INVALID; } return CERROR_SUCCESS; } static int op_ttl(int argc, char **argv) { ARG_UNUSED(argc) local_ttl = atoi(argv[1]); ylog(VERB_HIG, "> Time To Live is %u\n", local_ttl); return CERROR_SUCCESS; } static int op_tcp_requests_for_connection(int argc, char **argv) { ARG_UNUSED(argc) opt_tcp_requests_for_connection = atoi(argv[1]); ylog(VERB_HIG, "TCP requests for connection set to %d\n", opt_tcp_requests_for_connection); return CERROR_SUCCESS; } static int op_class(int argc, char **argv) { ARG_UNUSED(argc) if (!strcasecmp(argv[1], "IN")) { local_class = C_IN; ylog(VERB_HIG, "> Class is IN\n"); } else if (!strcasecmp(argv[1], "CHAOS")) { local_class = C_CHAOS; ylog(VERB_HIG, "> Class is CHAOS\n"); } else if (!strcasecmp(argv[1], "ANY")) { local_class = C_ANY; ylog(VERB_HIG, "> Class is ANY\n"); } else { return CERROR_INVALID; } return CERROR_SUCCESS; } static int op_acl(int argc, char **argv) { char *rule_accept = "0123456789.$"; struct acl **head, **tail; int j; /* select the acl list */ if (!strcasecmp("dns.allow", argv[1])) { head = &acl_dns_allow_head; tail = &acl_dns_allow_tail; } else if (!strcasecmp("dns.deny", argv[1])) { head = &acl_dns_deny_head; tail = &acl_dns_deny_tail; } else if (!strcasecmp("fwd.allow", argv[1])) { head = &acl_fwd_allow_head; tail = &acl_fwd_allow_tail; } else if (!strcasecmp("fwd.deny", argv[1])) { head = &acl_fwd_deny_head; tail = &acl_fwd_deny_tail; } else if (!strcasecmp("axfr.allow", argv[1])) { head = &acl_axfr_allow_head; tail = &acl_axfr_allow_tail; } else if (!strcasecmp("axfr.deny", argv[1])) { head = &acl_axfr_deny_head; tail = &acl_axfr_deny_tail; } else { return CERROR_BADACL; } for (j = 2; j < argc; j++) { if ((strlen(argv[j]) >= RULE_MAXSIZE) || (strspn(argv[j], rule_accept) != strlen(argv[j])) || (strchr(argv[j], '$') && argv[j][strlen(argv[j])-1] != '$') || (strchr(argv[j], '$') != strrchr(argv[j], '$'))) { return CERROR_BADACL; } acl_add_rule(argv[j], head, tail); ylog(VERB_HIG, "acl: loaded %s %s\n", argv[1], argv[j]); } return CERROR_SUCCESS; } static int op_mx(int argc, char **argv) { ARG_UNUSED(argc) if (local_add_MX(argv[1], argv[2], argv[3]) == -1) return CERROR_BADNAME; return CERROR_SUCCESS; } static int op_ptr(int argc, char **argv) { ARG_UNUSED(argc) if (local_add_PTR(argv[1], argv[2]) == -1) return CERROR_BADNAME; return CERROR_SUCCESS; } static int op_txt(int argc, char **argv) { ARG_UNUSED(argc) if (local_add_TXT(argv) == -1) return CERROR_TXTFMTERR; return CERROR_SUCCESS; } static int op_ns(int argc, char **argv) { ARG_UNUSED(argc) if (local_add_NS(argv[1], argv[2]) == -1) return CERROR_BADNAME; return CERROR_SUCCESS; } static int op_cname(int argc, char **argv) { ARG_UNUSED(argc) if (local_add_CNAME(argv[1], argv[2]) == -1) return CERROR_BADNAME; return CERROR_SUCCESS; } static int op_soa(int argc, char **argv) { if (local_add_SOA(argc, argv) == -1) return CERROR_BADNAME; return CERROR_SUCCESS; } static int op_a(int argc, char **argv) { int retval; char tmp[32]; ARG_UNUSED(argc); retval = local_add_A(argv[1], argv[2]); if (retval == -1) return CERROR_BADNAME; if (opt_autoptr) { if ((retval = inet_toinaddr(argv[2], tmp)) != 0) return retval; strlcat(tmp, ".", 32); tmp[strlen(tmp)-1] = '\0'; if (local_add_PTR(tmp, argv[1]) == -1) return CERROR_BADNAME; } return CERROR_SUCCESS; } static int op_generic_enable_disable(int argc, char **argv) { ARG_UNUSED(argc) if (strcasecmp(argv[0], "nologtime") == 0) opt_logtime = 0; else if (strcasecmp(argv[0], "autoptr") == 0) opt_autoptr = 1; else if (strcasecmp(argv[0], "noautoptr") == 0) opt_autoptr = 0; else if (strcasecmp(argv[0], "uptime") == 0) opt_uptime = 1; else if (strcasecmp(argv[0], "cache_noexpire") == 0) opt_cachenoexpire = 1; else if (strcasecmp(argv[0], "axfr_more_rr") == 0) opt_axfr_compatmode = 0; else if (strcasecmp(argv[0], "wildcard_lookup") == 0) opt_wildcard = 1; return CERROR_SUCCESS; } yaku-ns-0.2/core.c000066400000000000000000000520541425072213200140420ustar00rootroot00000000000000/* core.c * The core of yaku-ns * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include "aht.h" #include #include #include #include "nameser.h" #include #include #include #include #include /* umask(2) */ #include #include #include #include #include /* more global stuff */ char logfile[1024]; int opt_forward = 0; int opt_cache = 1; int opt_logfile = 0; int opt_bindaddr = 0; int opt_wildcard = 0; int s; /* the UDP socket */ int opt_udp_port = DNS_PORT; char *configfile = CONFIG_FILE; char *safeuser = SAFEUSER; int next_server_timeout = NEXT_SERVER_TIMEOUT; int ens_awake_time_sec = ENS_AWAKE_TIME_SEC; int ens_awake_time_usec = ENS_AWAKE_TIME_USEC; char bindaddr[16]; int securelevel = 0; char chrootjail[1024]; int opt_chroot = 0; int opt_daemon = 0; /* statistics */ unsigned int statistic_received_packet = 0; unsigned int statistic_invalid_packet = 0; unsigned int statistic_query_count = 0; unsigned int statistic_iquery_count = 0; unsigned int statistic_response_count = 0; /* not exported functions */ static void initialize(void); static void core(void); static int scheduler(void); static void packet_processor(byte *packet, unsigned int size, struct sockaddr *from, socklen_t fromlen); static void response_processor(byte *packet, unsigned int size, struct sockaddr *from, socklen_t fromlen); static void query_processor(byte *packet, unsigned int size, struct sockaddr *from, socklen_t fromlen); #ifdef PROFILING unsigned long long get_clock(void) { unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; } #endif #ifdef TRACE_LEAKS int tl_allocated = 0; void *tl_ptr; #endif int main(int argc, char **argv) { int c; /* default is to logs to standard output */ logfp = stdout; while ((c = getopt(argc, argv, "p:P:f:C:F:T:c:u:l:r:b:xdhV")) != EOF) { switch(c) { case 'p': opt_udp_port = atoi(optarg); break; case 'P': opt_tcp_port = atoi(optarg); break; case 'C': cache_max = atoi(optarg); if (cache_max == 0) opt_cache = 0; break; case 'f': dns_forward_port = atoi(optarg); break; case 'F': forward_max = atoi(optarg); if (forward_max == 0) opt_forward = 0; break; case 'T': forward_timeout = atoi(optarg); break; case 'c': configfile = optarg; break; case 'u': safeuser = optarg; break; case 'l': strlcpy(logfile, optarg, 1024); opt_logfile = 1; break; case 'r': strlcpy(chrootjail, optarg, 1024); opt_chroot = 1; break; case 'd': opt_daemon = 1; break; case 'b': strlcpy(bindaddr, optarg, 16); opt_bindaddr = 1; break; case 'x': opt_axfr = 1; break; case 'V': opt_verbose++; break; case 'h': default: printf( "usage: yaku-ns [-p ] [-P ] [-f ] [-C ] [-F ]\n" " [-T ] [-c ]\n" " [-l ] [-r ] [-u owner]\n" " [-b ] [-xdhV]\n" ); exit(1); } } /* Initialization of random stuff and enter the main loop */ initialize(); core(); return 0; /* unreached */ } /* Create and bind the UDP socket */ #define DNS_SNDBUF 65536 #define DNS_RCVBUF 65536 int net_init(void) { struct sockaddr_in sa; int retval; int size; socklen_t optsize; /* open the main UDP socket */ s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) { perror("socket"); return -1; } /* Enlarge the input and output socket buffers: * this can help with high latency. */ optsize = sizeof(size); if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, &optsize) == -1 || size < DNS_RCVBUF) { size = DNS_RCVBUF; if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) == -1) { perror("[net_init] setsockopt"); /* not fatal */ } } optsize = sizeof(size); if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, &optsize) == -1 || size < DNS_SNDBUF) { size = DNS_SNDBUF; if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1) { perror("[net_init] setsockopt"); /* not fatal */ } } /* bind the socket */ sa.sin_family = AF_INET; sa.sin_port = htons(opt_udp_port); if (!opt_bindaddr) { sa.sin_addr.s_addr = htonl(INADDR_ANY); /* all interfaces */ } else { struct in_addr tmp; if (inet_aton(bindaddr, &tmp) == 0) { ylog(VERB_FORCE, "[main] bad IP address to bind\n"); return -1; } sa.sin_addr.s_addr = tmp.s_addr; } retval = bind(s, (struct sockaddr*) &sa, sizeof(sa)); if (retval == -1) { perror("bind"); return -1; } return 0; } /* Chroot & root dropping */ int security_init(void) { struct passwd *pw; /* drop root priviledges and chroot */ if (getuid() == 0) { pw = getpwnam(safeuser); if (!pw) { ylog(VERB_FORCE, "[main] getpwnam error, does user '%s' exist?\n", safeuser); exit(1); } /* chroot jail -- just after the getpwnam that needs passwd */ if (opt_chroot && (chdir (chrootjail) == -1 || chroot(chrootjail) == -1)) { perror("chdir/chroot"); exit(1); } /* root squashing */ if (setgroups(0, NULL) == -1 || setgid(pw->pw_gid) == -1 || setuid(pw->pw_uid) == -1) { perror("[main] setgroups/setgid/setuid"); exit(1); } ylog(VERB_MED, "switched to user '%s'\n", safeuser); } return 0; } static void initialize(void) { ylog(VERB_LOW, "DNS server started, verbosity set to %d\n", opt_verbose); /* for the uptime */ ens_start = get_sec(); /* Initialization */ set_core_size(YK_CORE_SIZE); if (opt_daemon) daemon_init(); /* If the user did not specified a log file using * the command line switch, but demonization is on, * ENS will not write the log to standard output, * This behaviour may be overrided by the configuration * file that will be read below. */ if (opt_daemon && !opt_logfile) logfp = NULL; /* It's safe to change the directory to / and * set the umask to 2 (even if actually ENS * never creates files). */ if (chdir("/") == -1) perror("WARNING: chdir to / failed"); (void) umask(2); install_signal_handler(); /* We want this signals delivered only in a safe point */ signal_block(SIGHUP); signal_block(SIGUSR1); signal_block(SIGUSR2); /* Net initialization */ if (net_init() == -1) { ylog(VERB_FORCE, "[initialize] Net initialization failed\n"); exit(1); } /* Initialize DNS over TCP for AXFR requests */ if (opt_axfr && axfr_init() == -1) { ylog(VERB_FORCE, "[initialize] failed initializing AXFR\n"); opt_axfr = 0; /* This isn't fatail */ } /* Security initialization */ if (security_init() == -1) { ylog(VERB_FORCE, "[initialize] Security initialization " "failed\n"); exit(1); } local_init(); /* Initialize the local table */ cache_init(); /* Initialize the cache table */ forward_init(); /* Initialize the forward table */ /* read the config file -- note that we dropped root privileges * and chrooted ENS before to read the config file */ config_reset(); config_read(configfile); /* open the log file */ if (opt_logfile) open_logfile(logfile); ylog(VERB_LOW, "Local resource records loaded\n"); } /* The DNS server main loop */ static void core(void) { byte packet[PACKETSZ]; /* readed UDP packet */ while(1) { int size; struct sockaddr_in from; socklen_t fromlen; fd_set rfds; struct timeval tv; int t; int maxfd = s; #ifdef PROFILING static unsigned long long saved_clock = 0, new_clock; #endif signal_unblock(SIGHUP); signal_unblock(SIGUSR1); signal_unblock(SIGUSR2); signal_unblock(SIGCHLD); /* The OS should delivery this signals here */ signal_block(SIGHUP); signal_block(SIGUSR1); signal_block(SIGUSR2); signal_block(SIGCHLD); /* scheduler() runs some CPU expansive task * at a given period */ (void) scheduler(); FD_ZERO(&rfds); FD_SET(s, &rfds); if (opt_axfr) { FD_SET(tcp_s, &rfds); if (tcp_s > maxfd) maxfd = tcp_s; } tv.tv_sec = ens_awake_time_sec; tv.tv_usec = ens_awake_time_usec; /* DNS core handler */ #ifdef PROFILING new_clock = get_clock(); printf("CLOCKS: %Lu\n", new_clock - saved_clock); #endif t = select(maxfd+1, &rfds, NULL, NULL, &tv); #ifdef PROFILING saved_clock = get_clock(); #endif if (t == -1) { if (errno != EINTR) perror("select"); continue; } /* Handle DNS over UDP requests */ if (FD_ISSET(s, &rfds)) { fromlen = sizeof(struct sockaddr_in); size = recvfrom(s, packet, PACKETSZ, 0, (struct sockaddr*)&from, &fromlen); if (size == -1) { perror("recv"); continue; } statistic_received_packet++; DEBUG(ylog(VERB_DEBUG, "Packet received\n");) packet_processor(packet, size, (struct sockaddr*)&from, fromlen); } /* Handle DNS over TCP requests (ony AXFR) */ if (opt_axfr && FD_ISSET(tcp_s, &rfds)) tcp_handler(); } } /* scheduler(): * This function ensure that if we call it many times * in one second it'll perform some operation only at * a given period. */ static int scheduler(void) { static time_t scheduler_next = 0; static time_t cache_next = 0; static time_t hash_resize_next = 0; time_t now = get_sec(); int ran = 0; /* Anyway don't run nothing more than one time in a second * (actually it may run it two times in the same second in the * worst case) */ if (now >= scheduler_next) { /* Call the handler for the signals that's unsafe * to handle asyncronously */ (void) handle_signals(); /* We must call forward_free_expired() every * second or so, the function also resend the * requests in timeout to the next forwarder */ if (opt_forward && forward_timeout) { forward_free_expired(); ran++; } if (opt_cache) { /* Free the expired entries in the cache table * Individual cached responses * are freed anyway if someone want to get * they using cache_search_entry(). * Anyway to free *all* the entries expired * is usefull if the DNS server runs under * very low traffic in some period of the day, * to free memory for other processes. * WARNING: cache_free_expired() is very * CPU expansive, don't schedule it too often */ if (now >= cache_next) { cache_free_expired(); cache_next = now + SCHEDULE_CACHE_EXPIRE; ran++; } } /* Resize the hash tables */ if (now >= hash_resize_next) { if (opt_cache) { unsigned int old_size = cache_table.size; ht_resize(&cache_table); ylog(VERB_HIG, "Cache table resize (%u -> %u)\n", old_size, cache_table.size); } if (opt_forward) { unsigned int old_size = forward_table.size; ht_resize(&forward_table); ylog(VERB_HIG, "Forward table resize " "(%u -> %u)\n", old_size, forward_table.size); } hash_resize_next = now + SCHEDULE_HASH_RESIZE; } if (opt_uptime) uptime_refresh(); /* flush the logs */ log_flush(); /* Re-get the time to strip out the time consumed */ scheduler_next = get_sec() + SCHEDULE_SCHEDULER; } return ran; } /* This function pass the control to the right function */ static void packet_processor(byte *packet, unsigned int size, struct sockaddr *from, socklen_t fromlen) { struct sockaddr_in *in = (struct sockaddr_in *) from; char straddr[64]; HEADER *hdr = (HEADER*) packet; /* Check the ACL */ strlcpy(straddr, inet_ntoa(in->sin_addr), 64); if (acl_check_dns(straddr) == ACL_DENY) { ylog(VERB_MED, "DNS Access denied to client %s-%d\n", straddr, ntohs(in->sin_port)); send_udp_error(s, (struct sockaddr*) from, fromlen, packet, size, ERR_REFUSED); return; } /* SANITYCHECK: size is < of the DNS header size */ if (size < sizeof(HEADER)) { DEBUG(ylog(VERB_DEBUG, "Packet too short\n");) return; } /* The DNS is a response? call response_processor() */ if (hdr->qr == 1) { statistic_response_count++; response_processor(packet, size, from, fromlen); return; } /* is a query, shunt the opcodes */ switch (hdr->opcode) { case QUERY: statistic_query_count++; query_processor(packet, size, from, fromlen); break; case IQUERY: /* NOT IMPLEMENTED */ statistic_iquery_count++; DEBUG(ylog(VERB_DEBUG, "Iquery received\n");) send_udp_error(s, from, fromlen, packet, size, ERR_NOTIMPLEMENTED); break; case STATUS: /* NOT IMPLEMENTED */ DEBUG(ylog(VERB_DEBUG, "Status query received\n");) send_udp_error(s, from, fromlen, packet, size, ERR_NOTIMPLEMENTED); break; case NS_NOTIFY_OP: /* NOT IMPLEMENTED */ DEBUG(ylog(VERB_DEBUG, "NS notify query received\n");) send_udp_error(s, from, fromlen, packet, size, ERR_NOTIMPLEMENTED); break; default: /* reserved opcodes */ statistic_invalid_packet++; DEBUG(ylog(VERB_DEBUG, "Invalid or unsupported opcode\n");) send_udp_error(s, from, fromlen, packet, size, ERR_FORMAT); break; } return; } /* response_processor() handles the DNS response packets: * It decodes the name and searches for a matching entry in the * forwarded requests queue. If some entry matches it sends * the response to the original requester (the client), put * this record in the cache and erase the entry in the forwarded * requests table */ static void response_processor(byte *packet, unsigned int size, struct sockaddr *from, socklen_t fromlen) { HEADER *hdr = (HEADER*) packet; int id = ntohs(hdr->id), retval; u_int16_t qtype, qclass, qdcount = ntohs(hdr->qdcount); struct forwardentry *p; char *name = NULL; byte *data = packet+sizeof(HEADER); int data_size = size-sizeof(HEADER); unsigned int index; ARG_UNUSED(fromlen); if (!opt_forward) return; DEBUG(ylog(VERB_DEBUG, "Response received ID: %d\n", id);) /* the shortest name `.' is 1 byte, + 4 for qtype/qclass = 5 bytes */ if (qdcount < 1 || data_size < 5) goto invalid; /* decode the name */ retval = name_decode(data, data_size, packet, &name, 1); if (name == NULL) { if (retval == YK_INVALID) goto invalid; /* ...else out of memory */ return; } updatep(retval); /* there is space for qtype and qclass? */ if (data_size < 4) goto invalid; qtype = (data[0] << 8) | data[1]; qclass = (data[2] << 8) | data[3]; updatep(4); ylog(VERB_LOW, "%s,%d name server replied (%s %s %s ID:%d)\n", inet_ntoa(((struct sockaddr_in*)from)->sin_addr), ntohs(((struct sockaddr_in*)from)->sin_port), qtype_to_str(qtype), qclass_to_str(qclass), name, id); /* saerch in the forward table */ p = forward_search(id, qtype, qclass, name, &index); if (p != NULL) { DEBUG(ylog(VERB_DEBUG, "Previous response matches [%s %s %s]\n", p->name, qtype_to_str(p->qtype), qclass_to_str(p->qclass));) /* cache the response */ if (opt_cache) { /* don't add already cached responses: * This can happenes for example when the * resolver asks for the same RR two times * (since the name server used a log time * to response). So the same query is repeted * in the forward entry list, and it will * be cached two (or more) times if we don't * check for this condition. */ if (!cache_search_entry(p->name, p->qclass, p->qtype)) { cache_add_entry(p, packet, size); ylog(VERB_HIG, "Previous response cached\n"); } else { DEBUG(ylog(VERB_DEBUG, "Already in cache\n");) } } /* send the response to the client */ free(name); hdr->id = htons(p->orig_id); send_udp(s, packet, size, (struct sockaddr*)&p->clientaddr, sizeof(p->clientaddr)); forward_free_by_index(index); if (forward_count > 0) forward_count--; DEBUG(ylog(VERB_DEBUG, "Response sent to client\n");) return; } DEBUG(ylog(VERB_DEBUG, "Response doesn't match\n");) free(name); return; invalid: if(name) free(name); statistic_invalid_packet++; DEBUG(ylog(VERB_DEBUG, "Response is an invalid DNS packet\n");) } /* count the number of occurrences of the char 'c' in the string 's' */ size_t strcount(char *s, int c) { size_t count = 0; while(*s) { if (*s++ == c) count++; } return count; } /* query_processor() processes the DNS query: * It decodes the query and do some sanity check, so try to find * a matching RR in the local RRs, if any it builds the response header * and call add_rr() function for any RR, and send the response to the * client. If there aren't matching RRs in the local RRs list then: * It searches in the cache, if the cache match it sends the response, * otherwise forwards the request to the external DNS server and creates * a new forwarded request entry. */ static void query_processor(byte *packet, unsigned int size, struct sockaddr *from, socklen_t fromlen) { HEADER *hdr = (HEADER*) packet; struct sockaddr_in *in = (struct sockaddr_in *) from; byte *data = packet + sizeof(HEADER); int query_count = ntohs(hdr->qdcount); char *name = NULL; int data_size = size - sizeof(HEADER); byte *response = NULL; int response_size; int query_size = 0; int retval, qclass, qtype; char straddr[64]; /* No entries in query section? */ if (query_count == 0) goto invalid; /* Log a warning if the incoming DNS packet is truncated */ DEBUG(if (hdr->tc) ylog(VERB_DEBUG, "Truncated packet\n");) /* answer only to the first query in the request */ if (query_count > 1) query_count = 1; while (query_count--) { char namecopy[MAXDNAME]; int lookups = 0; retval = name_decode(data, data_size, packet, &name, 1); if (name == NULL) { if (retval == YK_INVALID) goto invalid; /* ...else out of memory */ return; } updatep(retval); query_size = retval+4; DEBUG(ylog(VERB_DEBUG, "name: %s\n", name);) /* Enough space for qtype and qclass? */ if (data_size < 4) goto invalid; qtype = (data[0] << 8) | data[1]; qclass = (data[2] << 8) | data[3]; updatep(4); DEBUG(ylog(VERB_DEBUG, "(%s %s)\n", qtype_to_str(qtype), qclass_to_str(qclass));) ylog(VERB_LOW, "%s,%d asks for (%s %s %s)\n", inet_ntoa(((struct sockaddr_in*)from)->sin_addr), ntohs(((struct sockaddr_in*)from)->sin_port), qtype_to_str(qtype), qclass_to_str(qclass), name); /* AXFR requested under UDP */ if (qtype == T_AXFR) { send_udp_error(s, from, fromlen, packet, size, ERR_REFUSED); free(name); return; } /* build the response using the local RRs, * otherwise return NULL. * build_response() returns NULL even under out-of-memory * but store in response_size YK_NOMEM */ /* Check for wildcard local RRs if the name doesn't match */ strlcpy(namecopy, name, MAXDNAME); while(1) { int dots = strcount(namecopy, '.'); char *p, tmpname[MAXDNAME]; response = build_response(qclass, qtype, name, namecopy, packet+sizeof(HEADER), query_size, hdr, &response_size, PACKETSZ); lookups++; /* RR found? */ if (response) break; /* Out of memory building the response? */ if (response == NULL && response_size == YK_NOMEM) { free(name); return; } /* Name not found, retry with *.domain.com */ /* check the number of dots first, we don't * want to check for *.com, nor to do more * than few lookups */ if (!opt_wildcard || dots < 3 || lookups > 3) break; p = strchr(namecopy, '.'); if (!p) /* can't be true, but we make errors */ break; if (p == namecopy) break; /* If the bottom level was already a wildcard, * reduce of one level */ if (p[-1] == '*') { p = strchr(p+1, '.'); if (!p) break; /* again should be unreached */ } tmpname[0] = '*'; tmpname[1] = '\0'; strlcat(tmpname, p, MAXDNAME); strlcpy(namecopy, tmpname, MAXDNAME); } /* Sent the response */ if (response != NULL) { free(name); dns_shuffle(response, response_size); send_udp(s, response, response_size, from, fromlen); free(response); DEBUG(ylog(VERB_DEBUG, "Response sent using local RRs\n");) return; } /* Check the client against the FWD ACL lists */ strlcpy(straddr, inet_ntoa(in->sin_addr), 64); if (acl_check_fwd(straddr) == ACL_DENY) { free(name); ylog(VERB_MED, "DNS forwarding Access denied to client %s-%d\n", straddr, ntohs(in->sin_port)); send_udp_error(s, (struct sockaddr*) from, fromlen, packet, size, ERR_NAME); return; } /* If the forwarding isn't enabled send an error * back to the client */ if (opt_forward == 0) { free(name); send_udp_error(s, from, fromlen, packet, size, ERR_NAME); DEBUG(ylog(VERB_DEBUG, "No such RR\n");) return; } else { struct cacheentry *cached; /* Search in the cache */ if (opt_cache) { HEADER *answer_hdr; cached = cache_search_entry(name, qclass, qtype); if (cached != NULL) { cached->hits++; answer_hdr = (HEADER*) cached->answer; answer_hdr->id = hdr->id; cache_shuffle(cached); cache_fix_ttl(cached); send_udp(s, cached->answer, cached->answer_size, from, fromlen); DEBUG(ylog(VERB_DEBUG, "Sent from cache\n");) free(name); return; } } /* Forward the request to the first external server */ forward_request(hdr, (char*)packet, size, from, name, qtype, qclass); DEBUG(ylog(VERB_DEBUG, "Previous forwarded\n");) free(name); return; } } invalid: statistic_invalid_packet++; if (name != NULL) free(name); send_udp_error(s, from, fromlen, packet, size, ERR_FORMAT); DEBUG(ylog(VERB_DEBUG, "Invalid DNS packet\n");) } yaku-ns-0.2/dns.c000066400000000000000000000543271425072213200137030ustar00rootroot00000000000000/* dns.c * DNS protocol library * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information * * * STATUS * * OK: ENS behaves as desidered * TODO: ENS behaves in some different way or the * feature is not supported. * IGNORED: ENS behaves in some different way since it * is wanted. * * RFC 1035: * o Data trasmission order: OK * o Comparisons MUST be done in case insensitive: OK * o Case should be preserved internally: OK * o Size limits: * labels 63 octects: OK * names 255 octects: IGNORED, ens handles longer names. * TTL unsigned 32-bit: OK * UDP messages 512 bytes: OK * o Comparisons MUST be done assuming ASCII with zero * parity: TODO and to check, ens just uses strcasecmp(). * o ENS name decompression seems to follow the RFC, but * a max of 64 nested labels are allowed, to prevent DoS. * o UDP messages longer than 512 bits are truncated and * the TC bit is set: OK * o Nameserver MUST not stop to answer to UDP queries * while it waits for TCP data: OK * o Nameserver SHOULD not delay requests while it reloads * a zone from master files or while it incorporate a * newly refreshed zone into it's database: TODO or to IGNORE. * o Time: * o All timers are 32bit integers: OK * o TTLs of Zone RRs are costant: OK * o Standard query processing: * o When processing queries with QCLASS=ANY the response * SHOULD never be authoritative unless the server can * guarantee that the response covers all the classes: TODO * o When composing new respnse, RRs wich should be putted * in the additional section but are already present in the * answer or authority section may be omitted: IGNORED * o When a response is so long that truncation is required * the truncation SHOULD start at the end of the repsonse * and work forward in the datagram: TODO * o The MINIMUM value in the SOA should be used to set a * floor on the TTL of the data distributed from a zone: TODO * o This floor function SHOULD be done when the data is copied * into a response: TODO * o Inverse queries are NOT suppoted. */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include #define MAX_RR 256 /* non exported functions */ static byte *get_qsection(byte *packet, unsigned int size, int *qsize); /* exported functions */ int add_rr(byte **dest, HEADER *hdr, struct RRentry *rr, unsigned int size, int section, int maxsize); int build_header(byte **dest, HEADER *hdr, int aa); u_int32_t get_min_ttl(byte *packet, unsigned int packet_size); void dns_shuffle(byte *packet, unsigned int packet_size); int send_udp(int fd, void *packet, unsigned int size, struct sockaddr *to, int tolen); int send_tcp(byte *packet, int len, int sd); void send_udp_error(int fd, struct sockaddr *from, int fromlen, byte *packet, unsigned int size, int error_type); byte *build_error(int *retsize, byte *packet, unsigned int size, int error_type); int name_decode(byte *ptr, int data_size, byte *base, char **name, int compr); byte *name_encode(char *msg, int *size, char sep); /* -------------------------------------------------------------------------- */ /* Add a Resource Record to the DNS packet: * return codes: * >= 0 success * YK_NOMEM out of memory */ int add_rr(byte **dest, HEADER *hdr, struct RRentry *rr, unsigned int size, int section, int maxsize) { int offset = size, retval, newsize; u_int16_t tmp16; u_int32_t tmp32; byte *encoded_name, *tmp; ARG_UNUSED(hdr) #if 0 printf("Add %s %s %s (%d)\n", rr->name, qclass_to_str(rr->qclass), qtype_to_str(rr->qtype), size); #endif encoded_name = name_encode(rr->name, &retval, '.'); if (retval < 0) { /* Out of memory or invalid name */ switch(retval) { case YK_NOMEM: return YK_NOMEM; break; case YK_INVALID: default: /* just skip it */ return 0; break; } } /* The new size of the packet */ newsize = size + retval + RRFIXEDSZ + rr->size; /* Check if there is enough space */ if (newsize > maxsize) { /* For additional sections just don't add the RR */ switch(section) { case NS_SECTION: case AR_SECTION: return 0; } } /* Allocate the memmory needed */ if ((tmp = realloc(*dest, newsize)) == NULL) return YK_NOMEM; *dest = tmp; /* add the name */ memcpy((*dest)+offset, encoded_name, retval); offset += retval; free(encoded_name); /* add qtype, qclass, ttl, rdlen and the rr */ tmp16 = htons(rr->qtype); memcpy((*dest)+offset, &tmp16, 2); offset += 2; tmp16 = htons(rr->qclass); memcpy((*dest)+offset, &tmp16, 2); offset += 2; tmp32 = htonl(rr->ttl); memcpy((*dest)+offset, &tmp32, 4); offset += 4; tmp16 = htons(rr->size); memcpy((*dest)+offset, &tmp16, 2); offset += 2; memcpy((*dest)+offset, rr->data, rr->size); offset += rr->size; switch(section) { case QD_SECTION: ((HEADER*)*dest)->qdcount = htons(ntohs(((HEADER*)*dest)->qdcount)+1); break; case AN_SECTION: ((HEADER*)*dest)->ancount = htons(ntohs(((HEADER*)*dest)->ancount)+1); break; case NS_SECTION: ((HEADER*)*dest)->nscount = htons(ntohs(((HEADER*)*dest)->nscount)+1); break; case AR_SECTION: ((HEADER*)*dest)->arcount = htons(ntohs(((HEADER*)*dest)->arcount)+1); break; default: yakuns_assert(0 == 1); /* unreached */ break; } return offset - size; } /* Build an usual DNS packet header: * * return sizeof(HEADER) on success * YK_NOMEM if runs out of memory */ int build_header(byte **dest, HEADER *hdr, int aa) { HEADER *tmp; tmp = malloc(sizeof(HEADER)); if (tmp == NULL) /* out of memory */ return YK_NOMEM; memset(tmp, 0, sizeof(HEADER)); tmp->id = hdr->id; tmp->qr = 1; tmp->opcode = 0; tmp->aa = aa; tmp->tc = 0; tmp->rd = hdr->rd; tmp->ra = 1; tmp->unused = 0; tmp->rcode = ERR_SUCCESSFUL; tmp->qdcount = htons(1); tmp->ancount = 0; tmp->nscount = 0; tmp->arcount = 0; *dest = (byte*) tmp; return sizeof(HEADER); } /* This function fixes the RRs's TTL field in the given packets * accordly to the current time */ void fix_ttl(byte *packet, unsigned int packet_size, time_t last_fix, time_t now) { HEADER *hdr = (HEADER*) packet; int query_count = ntohs(hdr->qdcount); byte *data = packet + sizeof(HEADER); int data_size = packet_size - sizeof(HEADER); int retval, rdata; u_int32_t ttl, r_ttl, tmp32; u_int32_t diff = (u_int32_t) now - last_fix; /* packet_size should contain at least the header */ if (packet_size < sizeof(HEADER)) return; /* Dont accept packets with multiple queries */ if (query_count != 1) return; /* Skip the Question Section */ while(query_count--) { /* SANITY CHECK: 5 is the name '.' + qtype and qclass */ if (data_size < 5) return; retval = name_decode(data, data_size, packet, NULL, 1); if (retval < 0) /* invalid name format */ return; updatep(retval); if (data_size < 4) /* enough space for qtype and qclass? */ return; updatep(4); } /* Fix the TTLs */ while(data_size >= RRFIXEDSZ) { /* skip the name */ retval = name_decode(data, data_size, packet, NULL, 1); if (retval < 0) /* invalid name format */ return; updatep(retval); /* enough space for class, type, ttl, rrsize ? */ if (data_size < 2+2+4+2) return; /* skip the dns class and type of the RR */ updatep(4); /* finally we can read the TTL value, with any alignment */ memcpy(&tmp32, data, 4); r_ttl = ntohl(tmp32); /* Fix it */ if (r_ttl > diff) r_ttl -= diff; else r_ttl = 0; /* store the fixed TTL */ ttl = htonl(r_ttl); memcpy(data, &ttl, 4); /* skip the TTL field */ updatep(4); /* get the RR data size */ rdata = (data[0] << 8) | data[1]; updatep(2); /* skip the RR data size */ if (data_size < rdata) return; updatep(rdata); if (data_size == 0) break; } } /* function related to dns.c but used only in caching. * This function gets the MIN ttl of all the Resource Records * that the DNS packet contains. * For malformed and truncated packets the TTL is 0 */ u_int32_t get_min_ttl(byte *packet, unsigned int packet_size) { HEADER *hdr = (HEADER*) packet; int query_count = ntohs(hdr->qdcount); byte *data = packet + sizeof(HEADER); int data_size = packet_size - sizeof(HEADER); int retval, rdata; u_int32_t ttl = 0xffffffff, r_ttl, tmp32; /* packet_size should contain at least the header */ if (packet_size < sizeof(HEADER)) return 0; /* TTL for responses that contains errors are fixed */ if (hdr->rcode != ERR_SUCCESSFUL) { switch (hdr->rcode) { case ERR_FORMAT: ttl = TTL_ERR_FORMAT; break; case ERR_FAILURE: ttl = TTL_ERR_FAILURE; break; case ERR_NAME: ttl = TTL_ERR_NAME; break; case ERR_NOTIMPLEMENTED: ttl = TTL_ERR_NOTIMPLEMENTED; break; case ERR_REFUSED: ttl = TTL_ERR_REFUSED; break; default: ttl = 0; break; } return ttl; } /* Dont accept packets with multiple queries */ if (query_count != 1) return 0; /* Skip the Question Section */ while(query_count--) { /* SANITY CHECK: 5 is the name '.' + qtype and qclass */ if (data_size < 5) return 0; retval = name_decode(data, data_size, packet, NULL, 1); if (retval < 0) /* invalid name format */ return 0; updatep(retval); if (data_size < 4) /* enough space for qtype and qclass? */ return 0; updatep(4); } /* Get the minimun ttl of the RRs */ while(data_size >= RRFIXEDSZ) { /* skip the name */ retval = name_decode(data, data_size, packet, NULL, 1); if (retval < 0) /* invalid name format */ return 0; updatep(retval); /* enough space for class, type, ttl, rrsize ? */ if (data_size < 2+2+4+2) return 0; /* skip the dns class and type of the RR */ updatep(4); /* finally we can read the TTL value, with any alignment */ memcpy(&tmp32, data, 4); updatep(4); r_ttl = ntohl(tmp32); /* what matter is the minimum ttl */ if (r_ttl < ttl) ttl = r_ttl; /* get the RR data size */ rdata = (data[0] << 8) | data[1]; updatep(2); /* skip the RR data size */ if (data_size < rdata) return 0; updatep(rdata); if (data_size == 0) break; } return ttl; } #define DNS_MAX_INA 32 /* This function shifts the order of the IN/A RRs. * It isn't a true Round-Robin algorithm, since it shuffle * the records at random. We want not take information * about the latest address proposed as first address. * * WARNING: long and unclear function, but very commented */ void dns_shuffle(byte *packet, unsigned int packet_size) { HEADER *hdr = (HEADER*) packet; int query_count = ntohs(hdr->qdcount); int answer_count = ntohs(hdr->ancount); byte *data = packet + sizeof(HEADER); /* data pointer */ unsigned int data_size = packet_size - sizeof(HEADER); /* data size */ int retval; u_int16_t rdata; /* RR data size */ int n_rr = 0; /* Number of RR processed */ char *name; /* name field of the RR */ byte *ina[DNS_MAX_INA]; /* IN A RRs pointers table */ int ina_id = 0; /* Index of the next element in the table */ u_int16_t qclass, qtype; char currentname[MAXDNAME+1]; /* Current name field of the RR */ char firstname[MAXDNAME+1]; /* name of the first A IN RR found */ int i; /* just a counter */ byte tmp[4]; /* Used to save an IPv4 address */ /* packet_size should contain at least the header */ if (packet_size < sizeof(HEADER)) return; /* Shuffling not needed for DNS errors or if there is * only one RR in the answer section */ if (hdr->rcode != ERR_SUCCESSFUL || query_count != 1 || answer_count <= 1) return; /* initializations */ for (i = 0; i < DNS_MAX_INA; i++) ina[i] = NULL; firstname[0] = '\0'; /* marked as not initialized */ /* Skip the Question Section */ while(query_count--) { /* sanity check: 5 is the name '.' + qtype and qclass */ if (data_size < 5) return; retval = name_decode(data, data_size, packet, NULL, 1); if (retval < 0) /* invalid name format, name truncated, ... */ return; updatep(retval); if (data_size < 4) /* enough space for qtype and qclass? */ return; updatep(4); } /* build the IN/A pointers table */ while(data_size >= RRFIXEDSZ) { n_rr++; /* RRs processed */ /* skip the name */ retval = name_decode(data, data_size, packet, &name, 1); if (retval < 0) /* invalid name or out of memory */ return; updatep(retval); strlcpy(currentname, name, MAXDNAME+1); /* save it */ free(name); /* enough space for class, type, ttl, rrsize ? */ if (data_size < 2+2+4+2) return; /* it is a IN A RR? */ qtype = (data[0] << 8) | data[1]; qclass = (data[2] << 8) | data[3]; if (qtype == T_A && qclass == C_IN) { /* If it's the first IN A RR or if it matches * the name of the first IN A RR add it to the * IN A address pointers list */ if (firstname[0] == '\0' || !strcasecmp(firstname, currentname)) { /* enough space for the complete RR? */ if (data_size < 14) return; ina[ina_id] = data+RRFIXEDSZ; ina_id++; /* increment the index */ DEBUG(printf("SHUFFLE TABLE %s %p\n", currentname, data+10);) } /* If it was the first name save it */ if (firstname[0] == '\0') memcpy(firstname, currentname, MAXDNAME+1); } /* skip class, type, ttl */ updatep(8); /* get the RR data size and skip the two bytes size */ rdata = (data[0] << 8) | data[1]; updatep(2); /* skip the RR data size */ if (data_size < rdata) return; updatep(rdata); /* Stop here if we reached the max pointers allowed, * the end of the answer section or if there aren't no * more data */ if (n_rr == answer_count || data_size == 0 || ina_id == DNS_MAX_INA) break; } if (ina_id <= 1) /* nothing to shuffle */ return; /* shuffle the IN A RRs address */ DEBUG(printf("DO SHUFFLE\n");) for (i = 0; i < ina_id; i++) { int r = rand() % ina_id; /* swap */ memcpy(tmp, ina[i], INADDRSZ); memcpy(ina[i], ina[r], INADDRSZ); memcpy(ina[r], tmp, INADDRSZ); } } int send_udp(int fd, void *packet, unsigned int size, struct sockaddr *to, int tolen) { int retval; HEADER *hdr = (HEADER*) packet; /* UDP truncation */ if (size > PACKETSZ) { size = PACKETSZ; hdr->tc = 1; /* truncation flag ON */ } retval = sendto(fd, packet, size, 0, to, tolen); if (retval == -1) { perror("[send_udp] sendto"); } return retval; } /* Send a TCP DNS response */ int send_tcp(byte *packet, int len, int sd) { u_int16_t size; int retval; HEADER *hdr = (HEADER*) packet; /* TCP truncation */ if (len > TCPPACKETSZ) { len = TCPPACKETSZ; hdr->tc = 1; /* truncation flag ON */ } size = htons(len); if (send(sd, &size, 2, 0) == -1) { perror("[send_tcp] send"); return -1; } retval = send(sd, packet, len, 0); if (retval == -1) perror("[send_tcp] send"); return retval; } /* Get the question section */ static byte *get_qsection(byte *packet, unsigned int size, int *qsize) { char *name = NULL; char *qsection = NULL; char *encoded = NULL; int retval, encoded_size; if (size < sizeof(HEADER)+5) goto out; retval = name_decode(packet+sizeof(HEADER), size-sizeof(HEADER), packet, &name, 1); if (name == NULL) { *qsize = retval; goto out; } if (size < sizeof(HEADER)+retval+4) goto out; encoded = (char*)name_encode(name, &encoded_size, '.'); if (encoded == NULL) { *qsize = encoded_size; goto out; } qsection = malloc(encoded_size+4); if (qsection == NULL) { *qsize = YK_NOMEM; goto out; } memcpy(qsection, encoded, encoded_size); memcpy(qsection+encoded_size, packet+sizeof(HEADER)+retval, 4); free(name); free(encoded); /* the caller must free qsection */ *qsize = encoded_size+4; return (byte*) qsection; out: free(name); free(qsection); free(encoded); return NULL; } void send_udp_error(int fd, struct sockaddr *from, int fromlen, byte *packet, unsigned int size, int error_type) { byte *error; int errsize; error = build_error(&errsize, packet, size, error_type); if (error == NULL) return; send_udp(fd, error, errsize, from, fromlen); free(error); } void send_tcp_error(int sd, byte *packet, unsigned int size, int error_type) { byte *error; int errsize; error = build_error(&errsize, packet, size, error_type); if (error == NULL) return; send_tcp(error, errsize, sd); free(error); } byte *build_error(int *retsize, byte *packet, unsigned int size, int error_type) { HEADER error_header; HEADER *hdr = (HEADER*) packet; byte *question; byte *error; int question_size; int errsize = 0; memset(&error_header, 0, sizeof(HEADER)); error_header.id = hdr->id; error_header.qr = 1; error_header.opcode = hdr->opcode; error_header.aa = 0; error_header.tc = 0; error_header.rd = 0; error_header.ra = 1; error_header.unused = 0; error_header.rcode = error_type; error_header.qdcount = 0; error_header.ancount = 0; error_header.nscount = 0; error_header.arcount = 0; errsize += sizeof(HEADER); switch(error_type) { case ERR_FAILURE: case ERR_NAME: case ERR_REFUSED: if ((question = get_qsection(packet, size, &question_size)) != NULL) { error = malloc(sizeof(HEADER)+question_size); if (error == NULL) { perror("[send_error] malloc"); return NULL; } error_header.qdcount = htons(1); memcpy(error, &error_header, sizeof(HEADER)); memcpy(error+sizeof(HEADER), question, question_size); free(question); errsize += question_size; *retsize = errsize; return error; } default: error = malloc(sizeof(HEADER)); if (error == NULL) { perror("[send_error] malloc"); return NULL; } memcpy(error, &error_header, sizeof(HEADER)); *retsize = sizeof(HEADER); return error; break; } return NULL; /* unreached */ } /* Decode the name pointed by *ptr. * data_size is the size of the packet starting from ptr * *base is the pointer to the packet head * **name will contain the dynamic allocated decoded name * compr must be 0 if pointers are not allowed, otherwise 1. * * If **name is NULL the function don't allocate any memory * and can be used just to obtain the len of the name to skip * it. */ #define DNS_NAME_NESTEDPTR_MAX 64 int name_decode(byte *ptr, int data_size, byte *base, char **name, int compr) { unsigned int size = 0; /* The size of the decoded name */ int realsize = 0; /* The size of the encoded name processed */ int n_compr = 0; /* nested pointers level */ int max_compr; /* max nested pointers allowed */ char buf[MAXDNAME+1]; /* Include space for nul term */ /* set the max nested pointers allowed */ max_compr = compr ? DNS_NAME_NESTEDPTR_MAX : 1; /* data size must be at least 1 */ if (data_size < 1) goto format_error; while(*ptr) { byte label_size = 0; /* handle the DNS name pointers */ if ((*ptr & 0xc0) == 0xc0 && n_compr < max_compr) { byte pointer_b[2]; u_int16_t pointer; n_compr++; /* the label is two bytes */ if (data_size < 2) goto format_error; /* get the offset */ pointer_b[0] = *ptr & (~0xc0); pointer_b[1] = *(ptr+1); /* Fix the endianess */ pointer = (pointer_b[0] << 8) | pointer_b[1]; /* The label can't point inside the header */ if (pointer < sizeof(HEADER)) goto format_error; /* jump! */ data_size = data_size+(ptr-base)-pointer; ptr = base+pointer; /* sanity check */ if (data_size <= 0) goto format_error; /* We must add the two bytes of pointer to * the real size, only for the first * pointer */ if (n_compr == 1) realsize += 2; continue; } else if ((*ptr & 0xc0) == 0xc0 && n_compr >= compr) { /* max nested label reached, game over */ goto format_error; } /* The name has the first two bits set to 01 or 10 ? * this format is reserved, it's an error */ if ((*ptr & 0xc0) == 0x40 || (*ptr & 0xc0) == 0x80) goto format_error; /* If unsigned char is 8 bit we dont need to check * that label_size is more than 63. We take the assumption. */ label_size = *ptr & 0x3f; /* the size of this label */ /* data_size must be large enough to contain the label size, * the label (*ptr bytes) and _at least_ the nul term */ if (data_size < label_size+2) goto format_error; /* check if there is enough space for label and '.' */ if (size+label_size+1 > MAXDNAME) goto format_error; /* copy the label, that start at ptr+1 and is *ptr bytes */ memcpy(buf+size, ptr+1, label_size); /* add the '.' */ *(buf+size+label_size) = '.'; /* update the offsets */ /* Increment the realsize if we never jumpted to some pointer */ if (n_compr == 0) realsize += label_size+1; data_size -= label_size+1; size += label_size+1; ptr += label_size+1; } if (size == 0) { /* the root '.' */ *(buf) = '.'; *(buf+1) = '\0'; } else { /* the string NULL term */ *(buf+size) = '\0'; } if (name) { *name = strdup(buf); if (*name == NULL) /* Out of memory */ return YK_NOMEM; } if (n_compr > 0) return realsize; else return realsize+1; /* +1 is for the name nul term */ format_error: if (name) *name = NULL; return YK_INVALID; } /* Encode a DNS name, * the names a.b.c. and a.b.c will be encoded in the same bytes. * * *msg points to the name, rappresented as zero or more labels * separated by the character `sep'. * *size will contain the size of the encoded name in not NULL. * sep is the separator character used. * * The function returns a malloc()ated buffer of *size bytes * with the encoded name on success, otherwise it returns NULL */ byte *name_encode(char *msg, int *size, char sep) { byte buf[MAXCDNAME]; char *p = msg, *last = msg; byte *tmp; unsigned int label_len, encoded_size = 0; while(1) { p = strchr(p, sep); label_len = (p != NULL) ? (p - last) : (signed)strlen(last); if (label_len == 0) { /* end of the name */ break; } else if (label_len > MAXLABEL) { /* out of range label len */ goto invalid; } if (encoded_size+label_len+1 > MAXCDNAME) goto invalid; *(buf+encoded_size) = label_len; encoded_size++; memcpy(buf+encoded_size, last, label_len); encoded_size += label_len; if (p == NULL) /* end of the name */ break; p++; last = p; } /* Add the DNS name nul term */ if (encoded_size+1 > MAXCDNAME) goto invalid; *(buf+encoded_size) = 0; encoded_size++; if (size) *size = encoded_size; /* Out of memory */ if ((tmp = malloc(encoded_size)) == NULL) goto oom; memcpy(tmp, buf, encoded_size); return tmp; oom: /* out of memory */ if (size) *size = YK_NOMEM; return NULL; invalid: /* Invalid name */ if (size) *size = YK_INVALID; return NULL; } yaku-ns-0.2/ens.h000066400000000000000000000300561425072213200137020ustar00rootroot00000000000000#ifndef __ENS_H #define __ENS_H #include "tunable.h" #include #include #include #include #include #include #include #include #include "nameser.h" #include "utils.h" /* ---------------------------- ENS error codes ----------------------------- */ #define CERROR_SUCCESS 0 #define CERROR_ARGNUM 1 #define CERROR_BADIP 2 #define CERROR_BADACL 3 #define CERROR_BADNAME 4 #define CERROR_TXTFMTERR 5 #define CERROR_INVALID 6 #define CERROR_NOMEM 7 #define CERROR_NOSPACE 8 #define CERROR_PERM 9 #define CERROR_MAX_CERROR 9 /* ---------------------------- DNS error codes ----------------------------- */ #define ERR_SUCCESSFUL 0 #define ERR_FORMAT 1 #define ERR_FAILURE 2 #define ERR_NAME 3 #define ERR_NOTIMPLEMENTED 4 #define ERR_REFUSED 5 /* ---------------------------- defines for add_rr() ------------------------ */ #define QD_SECTION 0 #define AN_SECTION 1 #define NS_SECTION 2 #define AR_SECTION 3 /* ---------------------------- return codes for ACL ------------------------ */ #define ACL_DENY 0 #define ACL_ALLOW 1 /* ---------------------------- verbosity levels ---------------------------- */ #define VERB_FORCE 0 #define VERB_LOW 1 #define VERB_MED 2 #define VERB_HIG 3 #define VERB_DEBUG 4 /* ---------------------------- return codes -------------------------------- */ #define YK_OK 0 /* Success */ #define YK_NOMEM -1 /* Out of memory */ #define YK_INVALID -2 /* Invalid argument */ /* ---------------------------- Defines ------------------------------------- */ #define HT_MAX_KEYSIZE (MAXDNAME+10) /* ---------------------------- macros -------------------------------------- */ /* ENS's perror */ #define perror(x) ylog(VERB_FORCE, "%s: %s\n", x, strerror(errno)) /* A simple way to trace memory leaks -- sorry if it seems obfustated code :) */ #ifdef TRACE_LEAKS #ifdef 0 #define free(x) do { if (x != NULL) tl_allocated--; fprintf(logfp, "{FREE %p} (%s %d) %d\n", (x), __FILE__, __LINE__, tl_allocated); free(x); } while(0) #define malloc(x) (tl_allocated++, fprintf(logfp, "{MALLOC %d} (%s %d) %d\n", (x), __FILE__, __LINE__, tl_allocated), (x <= 0) ? exit(1) : 0, malloc(x)) #define realloc(x, y) (tl_allocated = (x != NULL || y == 0) ? tl_allocated : tl_allocated+1, fprintf(logfp, "{REALLOC %p %d} (%s %d) %d\n", (x), (y), __FILE__, __LINE__, tl_allocated), realloc(x, y)) #endif #define malloc(x) tl_malloc(__FILE__, __LINE__, x) #define realloc(x, y) tl_realloc(__FILE__, __LINE__, x, y) #define free(x) tl_free(__FILE__, __LINE__, x) void *tl_malloc(char *file, int line, size_t size); void *tl_realloc(char *file, int line, void *ptr, size_t size); void tl_free(char *file, int line, void *ptr); #endif /* Verbose debugging messages */ #if YAKU_DEBUG #define DEBUG(x) x #else #define DEBUG(x) #endif #define updatep(x) data += (x), data_size -= (x) /* ---------------------------- types and strucutres ------------------------ */ /* Now we have our clear byte type, use it every times you need a byte * for something that isn't a string. */ typedef unsigned char byte; /* A resource record */ struct RRentry { u_int32_t id; char *name; u_int16_t qtype; u_int16_t qclass; u_int32_t ttl; int size; byte *data; struct RRentry *next; }; /* A note about the query_packet field: * We must save the query in order to resend it * to another external DNS server. */ struct forwardentry { char *name; u_int16_t qtype; u_int16_t qclass; u_int16_t id; u_int16_t orig_id; struct sockaddr_in clientaddr; time_t timestamp; /* for response timeout */ int server_number; byte *query_packet; int query_size; struct forwardentry *prev; struct forwardentry *next; }; /* This struct is used to store cached elements. * as you can see ENS save the response packet * without splitting it in many Resource Records. * This is really fast, but less flexible. */ struct cacheentry { char *name; u_int16_t qtype; u_int16_t qclass; int answer_size; byte *answer; u_int32_t ttl; time_t last_timestamp; /* time at the last access */ time_t creat_timestamp; /* time at entry creation */ time_t ttlupdate_timestamp; /* time of the last TTL update */ int hits; }; /* Structure used only to store the additional RRs needed for a response */ struct additionalrr { char *name; u_int16_t qtype; u_int16_t qclass; }; /* Structure used for access control list */ struct acl { char rule[RULE_MAXSIZE]; /* xxx.yyy.zzz.kkk + $ + nulterm */ struct acl *next; }; /* Structures for supported RR types */ struct RR_A { char addr[4]; }; struct RR_MX { u_int16_t preference; /* variable size exchange name */ }; struct RR_SOA { /* variable size dns name */ /* variable size mailbox name */ u_int32_t serial; u_int32_t refresh; u_int32_t retry; u_int32_t expire; u_int32_t minimum; }; /* the RR_PTR struct is not needed since the PTR is just a name */ /* the RR_TXT struct is not needed since the TXT is just the text */ /* the RR_TXT struct is not needed since the NS is just a name */ /* main lists */ extern struct hashtable cache_table; extern struct hashtable forward_table; extern struct hashtable local_table; /* more global stuff */ /* Options */ extern int opt_cachenoexpire; extern int opt_daemon; extern int opt_axfr; extern int opt_tcp_requests_for_connection; extern int opt_axfr_compatmode; extern int opt_uptime; extern int opt_autoptr; extern int opt_logtime; extern int opt_forward; extern int opt_cache; extern int opt_logfile; extern int opt_bindaddr; extern int opt_verbose; extern int opt_wildcard; extern char *configfile; extern char *safeuser; extern char bindaddr[16]; /* Sockets & co. */ extern int s; extern int tcp_s; extern int opt_udp_port; extern int opt_tcp_port; /* Log filedes and filename */ extern FILE *logfp; extern char logfile[1024]; /* forwarding */ extern struct in_addr forward_server[MAX_FORWARD_SERVERS]; extern int forward_server_count; extern u_int16_t forward_id; extern int forward_count; extern int forward_max; extern int forward_timeout; extern int next_server_timeout; extern int dns_forward_port; /* cache */ extern unsigned int cache_count; extern unsigned int cache_max; extern unsigned int cache_maxttl; extern unsigned int cache_minttl; /* misc */ extern int ens_awake_time_sec; extern int ens_awake_time_usec; extern char *cerror_list[]; extern u_int32_t local_ttl; extern u_int16_t local_class; #ifdef TRACE_LEAKS extern int tl_allocated; #endif extern int securelevel; extern time_t ens_start; /* Chroot jail vars */ extern char chrootjail[1024]; extern int opt_chroot; /* ENS ACL */ extern struct acl *acl_dns_allow_head; extern struct acl *acl_dns_allow_tail; extern struct acl *acl_dns_deny_head; extern struct acl *acl_dns_deny_tail; extern struct acl *acl_fwd_allow_head; extern struct acl *acl_fwd_allow_tail; extern struct acl *acl_fwd_deny_head; extern struct acl *acl_fwd_deny_tail; extern struct acl *acl_axfr_allow_head; extern struct acl *acl_axfr_allow_tail; extern struct acl *acl_axfr_deny_head; extern struct acl *acl_axfr_deny_tail; /* statistics */ extern unsigned int statistic_received_packet; extern unsigned int statistic_invalid_packet; extern unsigned int statistic_query_count; extern unsigned int statistic_iquery_count; extern unsigned int statistic_response_count; /* Function prototypes */ /********* * dns.c * *********/ byte *name_encode(char *msg, int *size, char sep); int name_decode(byte *ptr, int data_size, byte *base, char **name, int compr); void send_udp_error(int fd, struct sockaddr *from, int fromlen, byte *packet, unsigned int size, int error_type); byte *build_error(int *retsize, byte *packet, unsigned int size, int error_type); int build_header(byte **dest, HEADER *hdr, int aa); int send_udp(int fd, void *packet, unsigned int size, struct sockaddr *to, int tolen); int send_tcp(byte *packet, int len, int sd); u_int32_t get_min_ttl(byte *packet, unsigned int packet_size); void dns_shuffle(byte *packet, unsigned int packet_size); void fix_ttl(byte *packet, unsigned int packet_size, time_t last_fix, time_t now); /************** * response.c * **************/ byte *build_response(u_int16_t qclass, u_int16_t qtype, char *qname, char *name, byte *query, int query_size, HEADER *hdr, int *size, int maxsize); /*********** * misc .c * ***********/ char *qtype_to_str(unsigned short qtype); char *qclass_to_str(unsigned short qclass); int line_splitter(char *buffer, char *commandargs[], int argmax); time_t get_sec(void); void dump_state(void); u_int16_t get_rand_id(void); /************ * config.c * ************/ int config_read(char *filename); void config_reset(void); char *config_process_line(char *line); char *strcerror(int cerror); /*********** * local.c * ***********/ struct RRentry *alloc_rr(char *name, u_int16_t qtype, u_int16_t qclass, unsigned int size); int add_rr(byte **dest, HEADER *hdr, struct RRentry *rr, unsigned int size, int section, int maxsize); void local_free(void); int local_add_entry(struct RRentry *rr); int local_add_A(char *name, char *addr); int local_add_MX(char *name, char *priority, char *exchange); int local_add_PTR(char *name, char *ptr); int local_add_CNAME(char *name, char *canonical); int local_add_NS(char *name, char *ns); int local_add_TXT(char *argv[]); int local_add_SOA(int argc, char **argv); void local_putontop(struct RRentry *prev, struct RRentry *rr); struct RRentry *local_search(char *name, u_int16_t qtype, u_int16_t qclass, u_int32_t seq); int local_search_all(char *name, u_int16_t qtype, u_int16_t qclass, struct RRentry **rra, unsigned int size); void local_init(void); /********** * unix.c * **********/ void daemon_init(void); /************ * signal.c * ************/ void signal_handler(int signum); void install_signal_handler(void); void (*Signal(int signo, void (*func)(int)))(int); int signal_block(int sig); int signal_unblock(int sig); int handle_signals(void); /************* * forward.c * *************/ void forward_free_entry(struct forwardentry *entry); void forward_free_expired(void); void forward_request(HEADER *hdr, char *packet, unsigned int size, struct sockaddr *from, char *name, u_int16_t qtype, u_int16_t qclass); struct forwardentry *forward_search(int id, int qtype, int qclass, char *name, unsigned int *index); void forward_free_by_index(unsigned int index); void forward_init(void); /*********** * cache.c * ***********/ void cache_add_entry(struct forwardentry *p, byte *packet, int packet_size); struct cacheentry *cache_search_entry(char *name, int qclass, int qtype); void cache_free_entry(struct cacheentry *entry, struct cacheentry *previous); void cache_free_oldest(void); int cache_free_expired(void); void cache_fix_ttl(struct cacheentry *cache); void cache_shuffle(struct cacheentry *cache); void cache_init(void); /********* * arr.c * *********/ int additional_rr_needed(struct additionalrr *arr, struct RRentry *rr, int arrindex); /********* * acl.c * *********/ void acl_add_rule(char *rule, struct acl **head, struct acl **tail); void acl_free(void); int acl_check_dns(char *ip); int acl_check_fwd(char *ip); int acl_check_dyn(char *ip); int acl_check_axfr(char *ip); /************** * axfr_out.c * **************/ int axfr_init(void); void tcp_handler(void); /********* * log.c * *********/ int ylog(int level, char *fmt, ...); int log_flush(void); void open_logfile(char *filename); /************ * uptime.c * ************/ int uptime_refresh(void); /************* * autoptr.c * *************/ int inet_toinaddr(char *addr, char *dest); /*********** * htkey.c * ***********/ size_t rr_to_key(char *dest, size_t dsize, char *name, u_int16_t type, u_int16_t class, u_int32_t seq); int ht_dnskey_compare(void *key1, void *key2); u_int32_t ht_dnskey_hash(void *key); /************************* * strlcpy.c & strlcat.c * *************************/ size_t strlcpy(char *dst, const char *src, size_t siz); size_t strlcat(char *dst, const char *src, size_t siz); /************ * rlimit.c * ************/ int set_core_size(unsigned int size); #endif /* __ENS_H */ yaku-ns-0.2/forward.c000066400000000000000000000215311425072213200145520ustar00rootroot00000000000000/* forward.c * Yaku-ns forwarding code * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information * * STATUS * * OK: ENS behaves as desidered * TODO: ENS behaves in some different way or the * feature is not supported. * IGNORED: ENS behaves in some different way since it * is wanted. * * o Processing responses (RFC speaks about resolver, but it's general) * o Check the header for rasonableness. Discard datagrams * which are queries when responses are expected: OK * o Parse the sections of the message, and insure that all * the RRs are correctly formatted: INGORED, ENS will parse * only the relevant part of the responses. It works on the * respect of its security, but don't try to act as firewall * proxy for the DNS protocol. * o As an optional step, check the TTLs of arriving data looking * for RRs with excessively long TTLs. If a RR has an accessively * long TTL, say greater than 1 week, either discard the whole * response, or limit all TTLs to 1 week: IGNORED, ENS does not * set this kind of limits, its internal design does not relay * on the TTL value to avoid floods (since it discard old data * to insert new data when there isn't more space left). * Also we cache the DNS reply using the shortest TTL from the RRs TTL, * with a max and min barrier. * o Response matching: * o Don't expect that the response come from the same IP address * than the one used sending the query to the nameserver: OK * Spoofing UDP is already trivial, so whe don't lost nothing here. * o If the resolver retrasmit a particular request to a name * server it SHOULD be able to use a response from any of the * retrasmission: OK * o A name server will occasionally not have a current copy of a * zone which it should have according to some NS RRs. The * resolver should simply remove the name server from the current * SLIST, and continue: TODO */ /* ens.h must be included before all other includes */ #include "ens.h" #include "aht.h" #include #include /* global vars */ struct in_addr forward_server[MAX_FORWARD_SERVERS]; int forward_server_count = 0; /* how many forward servers */ int forward_count = 0; /* number of pending forwarded requests */ int forward_max = FORWARD_MAX_QUEUE; int forward_timeout = FORWARD_TIMEOUT; int dns_forward_port = DNS_FORWARD_PORT; struct hashtable forward_table; /* not exported functions */ static void forward_free_oldest(void); /* exported functions */ void forward_request(HEADER *hdr, char *packet, unsigned int size, struct sockaddr *from, char *name, u_int16_t qtype, u_int16_t qclass); void forward_free_expired(void); struct forwardentry *forward_search(int id, int qtype, int qclass, char *name, unsigned int *index); /* -------------------------------------------------------------------------- */ /* The destructor for the forward entry */ void ht_forward_destructor(void *obj) { struct forwardentry *forward = obj; free(forward->name); free(forward->query_packet); free(forward); } /* Compare two keys */ int ht_forward_compare(void *key1, void *key2) { u_int16_t k1l, k2l; memcpy(&k1l, key1, 2); memcpy(&k2l, key2, 2); if (k1l != k2l) return 0; /* keys of different length can't match */ return !memcmp(key1, key2, k1l); } /* Create a new entry in the forward table */ struct forwardentry *forward_add_entry(char *name, u_int16_t qtype, u_int16_t qclass, u_int16_t id) { struct forwardentry *forward; char key[HT_MAX_KEYSIZE], *k; int ret; size_t klen; /* If the forward queue is full the oldest entry * is removed. XXX: better strategy? Think about DoS * and performance. */ if (forward_count >= forward_max) forward_free_oldest(); if ((forward = malloc(sizeof(struct forwardentry))) == NULL) goto oom1; if ((forward->name = strdup(name)) == NULL) goto oom2; forward->qtype = qtype; forward->qclass = qclass; forward->id = id; klen = rr_to_key(key, HT_MAX_KEYSIZE, name, qtype, qclass, id); if ((k = malloc(klen)) == NULL) goto oom3; memcpy(k, key, klen); ret = ht_add(&forward_table, k, forward); if (ret != HT_OK) goto oom3; return forward; oom3: free(forward->name); oom2: free(forward); oom1: return NULL; } void forward_request(HEADER *hdr, char *packet, unsigned int size, struct sockaddr *from, char *name, u_int16_t qtype, u_int16_t qclass) { struct sockaddr_in forward_addr; struct forwardentry *f; u_int16_t forward_id = get_rand_id(); /* Create the entry in the forward hash table: * if we run out of memory here filling the rest * of the structure we don't need to remove the * entry from the hash table, since it will expire * very soon (usually at max in one second), see below */ f = forward_add_entry(name, qtype, qclass, forward_id); if (f == NULL) /* Out of memory, do nothing */ return; f->server_number = 0; f->orig_id = ntohs(hdr->id); hdr->id = htons(forward_id); f->timestamp = get_sec(); f->clientaddr.sin_family = AF_INET; f->clientaddr.sin_addr.s_addr = ((struct sockaddr_in*)from)->sin_addr.s_addr; f->clientaddr.sin_port = ((struct sockaddr_in*)from)->sin_port; /* save the query if we have more than one forwarder */ if (forward_server_count > 1) { f->query_packet = malloc(size); if (f->query_packet == NULL) goto oom1; memcpy(f->query_packet, packet,size); f->query_size = size; } else { f->query_packet = NULL; f->query_size = 0; /* useless */ } forward_addr.sin_family = AF_INET; forward_addr.sin_port = htons(dns_forward_port); forward_addr.sin_addr.s_addr = forward_server[0].s_addr; send_udp(s, packet, size, (struct sockaddr*) &forward_addr, sizeof(forward_addr)); forward_count++; return; oom1: /* if we run out of memory the timestamp is set to zero will ensure * that the entry will expire ASAP, the other fileds are set to * some clear value */ f->timestamp = 0; f->query_packet = NULL; f->query_size = 0; return; } /* Free expired entry and resend timed out query to the next server */ void forward_free_expired(void) { unsigned int index = 0; int ret; struct forwardentry *f; time_t now = get_sec(); if (forward_table.used == 0) return; /* search and remove expired entries */ while ((ret = ht_get_byindex(&forward_table, index)) != -1) { if (ret == 0) { index++; continue; } f = ht_value(&forward_table, index); if (now - f->timestamp > forward_timeout) { ylog(VERB_HIG, "Expired forwarded request %s %s %s, " "ID:%d\n", qtype_to_str(f->qtype), qclass_to_str(f->qclass), f->name, f->id); ht_free(&forward_table, index); forward_count--; } else if (forward_server_count > 1 && f->server_number < (forward_server_count-1) && now - f->timestamp > (next_server_timeout * (f->server_number+1))) { struct sockaddr_in forward_addr; f->server_number++; forward_addr.sin_family = AF_INET; forward_addr.sin_port = htons(dns_forward_port); forward_addr.sin_addr.s_addr = forward_server[f->server_number].s_addr; DEBUG(ylog(VERB_FORCE, "ASK TO SERVER %d [%s %s %s]\n", f->server_number, qtype_to_str(f->qtype), qclass_to_str(f->qclass), f->name);) send_udp(s, f->query_packet, f->query_size, (struct sockaddr*) &forward_addr, sizeof(forward_addr)); } index++; } } static void forward_free_oldest(void) { unsigned int index = 0, oldest_index = 0; int ret; struct forwardentry *f, *oldest = NULL; /* XXX: better to remove a random entry? at least much faster */ while ((ret = ht_get_byindex(&forward_table, index)) != -1) { if (ret == 0) { index++; continue; } f = ht_value(&forward_table, index); if (oldest == NULL || f->timestamp < oldest->timestamp) { oldest = f; oldest_index = index; } index++; } if (oldest) { ht_free(&forward_table, oldest_index); forward_count--; } return; } /* search a matching entry in the forward table, * store the entry index in the *index (if not NULL) to make * the live easier to the caller that want to free it */ struct forwardentry *forward_search(int id, int qtype, int qclass, char *name, unsigned int *index) { char key[HT_MAX_KEYSIZE]; int ret; unsigned int i; rr_to_key(key, HT_MAX_KEYSIZE, name, qtype, qclass, id); ret = ht_search(&forward_table, key, &i); if (ret == HT_FOUND) { if (index) *index = i; return (struct forwardentry *) forward_table.table[i]->data; } return NULL; } void forward_free_by_index(unsigned int index) { ht_free(&forward_table, index); } void forward_init(void) { ht_init(&forward_table); ht_set_hash(&forward_table, ht_dnskey_hash); ht_set_key_destructor(&forward_table, ht_destructor_free); ht_set_val_destructor(&forward_table, ht_forward_destructor); ht_set_key_compare(&forward_table, ht_dnskey_compare); } yaku-ns-0.2/getzone.c000066400000000000000000000233651425072213200145700ustar00rootroot00000000000000/* Get a zone using AXFR under TCP * produce as output a yaku-ns config file. * * Copyright(C) 2001,2002 Salvatore Sanfilippo * * * This software is under the GPL license version 2 * * TODO: * - should get only the RRs concerning to the zone requested */ #include "ens.h" #ifdef perror #undef perror #endif #include #include #include #include #include #include #include char *opt_zone = NULL; char *opt_server = NULL; int opt_port = NAMESERVER_PORT; void usage(void); void get_zone(char *zone, char *server, int port); void decode_response(byte *packet, unsigned int packet_size); void output_rr(char *rrname, u_int16_t qtype, u_int16_t qclass, u_int32_t ttl, byte *base, byte *data, u_int16_t rdata, int commented); int dump_ipv4(byte *data, unsigned int rdata); int dump_name(byte *base, byte *data, unsigned int rdata); int dump_u16(byte *data, unsigned int rdata); int dump_u32(byte *data, unsigned int rdata); int dump_txt(byte *data, unsigned int rdata); void remove_trailer_dot(char *name); int main(int argc, char **argv) { int c; while ((c = getopt(argc, argv, "z:s:p:")) != EOF) { switch(c) { case 'z': opt_zone = strdup(optarg); break; case 's': opt_server = strdup(optarg); break; case 'p': opt_port = atoi(optarg); break; case '?': usage(); exit(1); } } if (!opt_zone || !opt_server) { usage(); exit(1); } get_zone(opt_zone, opt_server, opt_port); return 0; } void usage(void) { printf( "yaku-getzone\n" "usage: yaku-getzone -z -s [-p ]\n"); } int build_query_header(byte **dest, u_int16_t id, unsigned int rd) { HEADER *hdr; hdr = malloc(sizeof(HEADER)); if (hdr == NULL) return -1; memset(hdr, 0, sizeof(HEADER)); hdr->id = id; hdr->qr = 0; hdr->opcode = QUERY; hdr->aa = 0; hdr->tc = 0; hdr->rd = rd; hdr->ra = 0; hdr->qdcount = htons(1); *dest = (byte*) hdr; return sizeof(HEADER); } int add_question_section(byte **dest, int size, char *name, u_int16_t qtype, u_int16_t qclass) { byte *encname, *tmp; int encname_size; int qsection_size; u_int16_t tmp16; if ((encname = name_encode(name, &encname_size, '.')) == NULL) return -1; qsection_size = 4 + encname_size; tmp = realloc(*dest, size + qsection_size); if (tmp == NULL) return -1; *dest = tmp; tmp += size; memcpy(tmp, encname, encname_size); tmp16 = htons(qtype); memcpy(tmp+encname_size, &tmp16, 2); tmp16 = htons(qclass); memcpy(tmp+encname_size+2, &tmp16, 2); return qsection_size; } void send_zone_request(int fd, char *zone) { int size = 0; int retval; byte *query = NULL; /* Query header */ if ((retval = build_query_header(&query, 0, 0)) == -1) goto oom; size += retval; /* Query question section */ retval = add_question_section(&query, size, zone, T_AXFR, C_IN); if (retval == -1) goto oom; size += retval; /* Send the request */ if (send_tcp(query, size, fd) == -1) { perror("send_tcp"); exit(1); } return; oom: /* out of memory or name encoding problems */ fprintf(stderr, "Error\n"); exit(1); } int open_server(char *ip, int port) { int s; struct sockaddr_in sa; if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1; sa.sin_family = AF_INET; sa.sin_port = htons(port); if (inet_aton(ip, &sa.sin_addr) == 0) { close(s); return -1; } if (connect(s, (struct sockaddr*) &sa, sizeof(sa)) == -1) { close(s); return -1; } return s; } void get_zone_response(int fd) { int n_read, left; u_int16_t response_size; byte tmp[2]; byte *response; while(1) { /* Read the response */ n_read = recv(fd, tmp, 2, 0); if (n_read == 0) break; if (n_read != 2) { fprintf(stderr, "Error reading the response size\n"); exit(1); } response_size = (tmp[0] << 8) | tmp[1]; if (response_size < sizeof(HEADER)) goto invalid; response = malloc(response_size); if (!response) { fprintf(stderr, "Out of memory\n"); exit(1); } left = response_size; while(left) { n_read = recv(fd, response+response_size-left, left, 0); if (n_read <= 0) goto invalid; left -= n_read; } decode_response(response, response_size); free(response); } return; invalid: fprintf(stderr, "Invalid response\n"); exit(1); } void decode_response(byte *packet, unsigned int packet_size) { static int soa_flag = 0; HEADER *hdr = (HEADER*) packet; int query_count = ntohs(hdr->qdcount); int answer_count = ntohs(hdr->ancount); byte *data = packet + sizeof(HEADER); int data_size = packet_size - sizeof(HEADER); int retval, rdata; u_int32_t ttl, tmp32; u_int16_t qtype, qclass, tmp16; char rrname[MAXDNAME+1]; /* packet_size should contain at least the header */ if (packet_size < sizeof(HEADER)) return; /* Skip the Question Section */ while(query_count--) { /* SANITY CHECK: 5 is the name '.' + qtype and qclass */ if (data_size < 5) return; retval = name_decode(data, data_size, packet, NULL, 1); if (retval == -1) /* invalid name format */ return; updatep(retval); if (data_size < 4) /* enough space for qtype and qclass? */ return; updatep(4); } while(answer_count--) { char *name; /* skip the name */ retval = name_decode(data, data_size, packet, &name, 1); if (retval == -1) /* invalid name format */ return; strlcpy(rrname, name, MAXDNAME+1); free(name); updatep(retval); remove_trailer_dot(rrname); /* enough space for class, type, ttl, rrsize ? */ if (data_size < 2+2+4+2) return; /* skip the dns class and type of the RR */ memcpy(&tmp16, data, 2); qtype = htons(tmp16); memcpy(&tmp16, data+2, 2); qclass = htons(tmp16); updatep(4); /* finally we can read the TTL value, with any alignment */ memcpy(&tmp32, data, 4); ttl = ntohl(tmp32); /* skip the TTL field */ updatep(4); /* get the RR data size */ rdata = (data[0] << 8) | data[1]; updatep(2); /* skip the RR data size */ if (data_size < rdata) return; /* Output the RR */ /* Comment the second SOA to avoid duplication */ output_rr(rrname, qtype, qclass, ttl, packet, data, rdata, (soa_flag == 1 && qtype == T_SOA)); /* Check for SOA */ if (qtype == T_SOA) soa_flag++; if (soa_flag == 2) exit(0); updatep(rdata); if (data_size == 0) break; } } void get_zone(char *zone, char *server, int port) { int fd; /* Contact the server */ fd = open_server(server, port); if (fd == -1) { perror("Can't open the TCP connection"); exit(1); } /* Send the request */ send_zone_request(fd, zone); /* Get and decode the response */ get_zone_response(fd); } void output_rr( char *rrname, u_int16_t qtype, u_int16_t qclass, u_int32_t ttl, byte *base, byte *data, u_int16_t rdata, int commented) { static u_int32_t last_ttl = -1; static u_int16_t last_qclass = -1; int x, i; if (last_qclass != qclass) { if (commented) printf("# "); switch(qclass) { case C_IN: printf("Class IN\n"); break; case C_CHAOS: printf("Class CHAOS\n"); break; case C_ANY: printf("Class ANY\n"); break; default: return; } last_qclass = qclass; } if (last_ttl != ttl) { if (commented) printf("# "); printf("TTL %u\n", ttl); last_ttl = ttl; } if (commented) printf("# "); switch(qtype) { case T_A: x = dump_ipv4(data, rdata); printf(" %s\n", rrname); break; case T_AAAA: printf("#AAAA %s ... not supported\n", rrname); break; case T_NS: printf("NS %s ", rrname); x = dump_name(base, data, rdata); printf("\n"); break; case T_MX: printf("MX %s ", rrname); x = dump_u16(data, rdata); rdata -= x; data += x; printf(" "); x = dump_name(base, data, rdata); printf("\n"); break; case T_PTR: printf("PTR %s ", rrname); x = dump_name(base, data, rdata); printf("\n"); break; case T_TXT: printf("TXT %s ", rrname); x = dump_txt(data, rdata); printf("\n"); break; case T_CNAME: printf("#CNAME %s ", rrname); x = dump_name(base, data, rdata); printf("\n"); break; case T_SOA: printf("SOA %s ", rrname); x = dump_name(base, data, rdata); rdata -= x; data += x; printf(" "); x = dump_name(base, data, rdata); rdata -= x; data += x; for (i = 0; i < 5; i++) { printf(" "); x = dump_u32(data, rdata); rdata -= x; data += x; } printf("\n"); break; default: printf("#### unsupported RR (type: %u) %s\n", qtype, rrname); break; } return; } void format_error(void) { fprintf(stderr, "RR format error\n"); exit(1); } int dump_ipv4(byte *data, unsigned int rdata) { struct in_addr ia; if (rdata < sizeof(ia)) format_error(); memcpy(&ia, data, sizeof(ia)); printf("%s", inet_ntoa(ia)); return sizeof(ia); } int dump_name(byte *base, byte *data, unsigned int rdata) { int retval; char *name; retval = name_decode(data, rdata, base, &name, 1); if (retval < 0) format_error(); remove_trailer_dot(name); printf("%s", name); free(name); return retval; } int dump_u16(byte *data, unsigned int rdata) { u_int16_t tmp16; if (rdata < 2) format_error(); memcpy(&tmp16, data, 2); printf("%u", ntohs(tmp16)); return 2; } int dump_u32(byte *data, unsigned int rdata) { u_int32_t tmp32; if (rdata < 4) format_error(); memcpy(&tmp32, data, 4); printf("%u", ntohl(tmp32)); return 4; } int dump_txt(byte *data, unsigned int rdata) { char label[MAXLABEL+1]; unsigned int lsize; while (rdata) { if (rdata < 1) format_error(); lsize = *data; rdata--; data++; if (lsize > MAXLABEL) format_error(); if (rdata < lsize) format_error(); memcpy(label, data, lsize); label[lsize] = '\0'; printf("%s\\", label); rdata -= lsize; data += lsize; } return rdata; } void remove_trailer_dot(char *name) { int l = strlen(name); if (l > 1) { if (name[l-1] == '.') name[l-1] = '\0'; } } int ylog(int level, char *fmt, ...) { ARG_UNUSED(level); ARG_UNUSED(fmt); return 0; } yaku-ns-0.2/htkey.c000066400000000000000000000034771425072213200142430ustar00rootroot00000000000000/* htkey.c * Translate a Resource Record reference to the hash table key * * Copyright (C) 2000 Salvatore Sanfilippo * Copyright (C) 2001 Salvatore Sanfilippo * Copyright (C) 2002 Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ #include "ens.h" #include #include #include "aht.h" /* Key format: * * [klen][seq][type][class][name] * * The key is not nul terminated, fields description: * * klen: 2 bytes total key length (including itself) * seq: 4 bytes sequence number, to distinguish and access to multiple * entries with the same RR type/class/name. * type: RR type * class: RR class * name: RR name * * The format was changed the 2002-09-09. The change was needed to * support the new AHT hash-table library */ size_t rr_to_key(char *dest, size_t dsize, char *name, u_int16_t type, u_int16_t class, u_int32_t seq) { size_t l = strlen(name); u_int16_t ltwo; unsigned int i; char *p; yakuns_assert(dsize >= 33); memcpy(dest+2, &seq, 4); memcpy(dest+6, &type, 2); memcpy(dest+8, &class, 2); l = (l > dsize-10) ? (dsize-10) : l; memcpy(dest+10, name, l); p = dest+10; /* put it lowercase -- but note that we save the resource * record with the original case to avoid useless information * leak */ for (i = 0; i < l; i++) p[i] = tolower(p[i]); ltwo = l; memcpy(dest, <wo, 2); return 10 + l; } /* Compare two keys */ int ht_dnskey_compare(void *key1, void *key2) { u_int16_t k1l, k2l; memcpy(&k1l, key1, 2); memcpy(&k2l, key2, 2); if (k1l != k2l) return 0; /* keys of different length can't match */ return !memcmp(key1, key2, k1l); } /* Hash a given key */ u_int32_t ht_dnskey_hash(void *key) { u_int16_t l; memcpy(&l, key, 2); return ht_strong_hash(key, l, 0x11223344); } yaku-ns-0.2/local.c000066400000000000000000000312111425072213200141740ustar00rootroot00000000000000/* local.c * Local records manipulation * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information * * STATUS * * OK: ENS behaves as desidered * TODO: ENS behaves in some different way or the * feature is not supported. * IGNORED: ENS behaves in some different way since it * is wanted. * * o The mailbox encoding standard assumes a mailbox name of the form * "@". While the syntax allowed in each of these * sections varies substantially between the various mail internets, the * preferred syntax for the ARPA Internet is given in [RFC-822]. * The DNS encodes the as a single label, and encodes the * as a domain name. The single label from the * is prefaced to the domain name from to form the domain * name corresponding to the mailbox. Thus the mailbox HOSTMASTER@SRI- * NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA. If the * contains dots or other special characters, its * representation in a master file will require the use of backslash * quoting to ensure that the domain name is properly encoded. For * example, the mailbox Action.domains@ISI.EDU would be represented as * Action\.domains.ISI.EDU: TODO */ /* ens.h must be included before all other includes */ #include "ens.h" #include "aht.h" #include #include /* not exported functions */ /* exported functions */ struct RRentry *alloc_rr(char *name, u_int16_t qtype, u_int16_t qclass, unsigned int size); void local_free(void); int local_add_entry(struct RRentry *rr); int local_add_A(char *name, char *addr); int local_add_CNAME(char *name, char *canonical); int local_add_MX(char *name, char *priority, char *exchange); int local_add_PTR(char *name, char *ptr); int local_add_NS(char *name, char *ns); int local_add_TXT(char *argv[]); int local_add_SOA(int argc, char **argv); struct RRentry *local_search(char *name, u_int16_t qtype, u_int16_t qclass, u_int32_t seq); /* global vars */ struct hashtable local_table; u_int32_t local_ttl = TTL_LOCAL_DEFAULT; u_int16_t local_class = C_IN; /* -------------------------------------------------------------------------- */ void local_free(void) { ht_destroy(&local_table); } /* Free a struct RRentry */ void ht_local_destructor(void *obj) { struct RRentry *l = obj; free(l->data); free(l->name); free(l); } /* alloc_rr(): allocate and initialize elements int the local RRs table. * Note that the 'name' field is termined with a trailer '.' since the * function that decode the names add this trailer '.' we can search for * matching RR just with strcasecmp that is usually very optimized. */ struct RRentry *alloc_rr(char *name, u_int16_t qtype, u_int16_t qclass, unsigned int size) { static u_int32_t nextid = 0; struct RRentry *rr; int l; rr = malloc(sizeof(struct RRentry)); if (!rr) goto oom1; l = strlen(name); rr->name = malloc(l+2); if (!rr->name) goto oom2; memcpy(rr->name, name, l+1); if (!(l == 1 && name[0] == '.')) strcat(rr->name, "."); rr->qtype = qtype; rr->qclass = qclass; rr->ttl = local_ttl; rr->size = size; rr->data = malloc(rr->size); if (!rr->data) goto oom3; rr->next = NULL; rr->id = nextid++; return rr; /* We can't do so match if we run out of memory adding a local RR, * actually ens don't allocate RRs at run-time (only at start-up) * so it seems not critical to exit here */ oom3: free(rr->name); oom2: free(rr); oom1: /* return NULL; */ perror("(allocating local RR) malloc"); exit(1); } /* Add the entry in the local table: * return -1 on error, the sequence number on success */ int local_add_entry(struct RRentry *rr) { char key[HT_MAX_KEYSIZE], *k; u_int32_t j; int ret; size_t klen; /* Search the first availabe sequence number * for the given Resource Record: In the local * table we can have more records with the * same qtype/qclass/name */ j = 0; while(1) { unsigned int i; klen = rr_to_key(key, HT_MAX_KEYSIZE, rr->name, rr->qtype, rr->qclass, j); ret = ht_search(&local_table, key, &i); if (ret == HT_NOTFOUND) break; j++; continue; } if ((k = malloc(klen)) == NULL) goto oom; memcpy(k, key, klen); ret = ht_add(&local_table, k, rr); if (ret != HT_OK) { yakuns_assert(ret != HT_BUSY); /* If we run out of memory here * it's better to exit, actually this * code is called only at start-up. */ goto oom; /* return -1; */ } return j; oom: ylog(VERB_FORCE, "ht_add() failed adding a local" "Resource Record with exit code %d\n", ret); exit(1); } void local_ylog(struct RRentry *rr) { ylog(VERB_HIG, "loaded: %s %s %s\n", qtype_to_str(rr->qtype), qclass_to_str(rr->qclass), rr->name); } /* add A */ int local_add_A(char *name, char *addr) { struct RRentry *rr; int retval; rr = alloc_rr(name, T_A, local_class, sizeof(struct RR_A)); retval = inet_aton(addr, (struct in_addr*)&((struct RR_A*)rr->data)->addr); /* bad address */ if (retval == 0) { ht_local_destructor(rr); return -1; } local_add_entry(rr); local_ylog(rr); return 0; } /* add MX */ int local_add_MX(char *name, char *priority, char *exchange) { struct RRentry *rr; struct RR_MX mx; byte *enc_exchange; int enc_exchange_size; enc_exchange = name_encode(exchange, &enc_exchange_size, '.'); if (enc_exchange == NULL) return enc_exchange_size; rr = alloc_rr(name, T_MX, local_class, sizeof(struct RR_MX)+enc_exchange_size); mx.preference = htons(atoi(priority)); memcpy(rr->data, &mx, sizeof(mx)); memcpy(rr->data+2, enc_exchange, enc_exchange_size); free(enc_exchange); local_add_entry(rr); local_ylog(rr); return 0; } /* add PTR */ int local_add_PTR(char *name, char *ptr) { struct RRentry *rr; byte *enc_ptr; int enc_ptr_size; enc_ptr = name_encode(ptr, &enc_ptr_size, '.'); if (enc_ptr == NULL) return enc_ptr_size; rr = alloc_rr(name, T_PTR, local_class, enc_ptr_size); memcpy(rr->data, enc_ptr, enc_ptr_size); free(enc_ptr); local_add_entry(rr); local_ylog(rr); return 0; } /* add CNAME */ int local_add_CNAME(char *name, char *canonical) { struct RRentry *rr; byte *enc_cname; int enc_cname_size; enc_cname = name_encode(canonical, &enc_cname_size, '.'); if (enc_cname == NULL) return enc_cname_size; rr = alloc_rr(name, T_CNAME, local_class, enc_cname_size); memcpy(rr->data, enc_cname, enc_cname_size); free(enc_cname); local_add_entry(rr); local_ylog(rr); return 0; } /* add NS */ int local_add_NS(char *name, char *ns) { struct RRentry *rr; byte *enc_ns; int enc_ns_size; enc_ns = name_encode(ns, &enc_ns_size, '.'); if (enc_ns == NULL) return enc_ns_size; rr = alloc_rr(name, T_NS, local_class, enc_ns_size); memcpy(rr->data, enc_ns, enc_ns_size); free(enc_ns); local_add_entry(rr); local_ylog(rr); return 0; } /* add TXT, XXX: fix this shit */ int local_add_TXT(char *argv[]) { struct RRentry *rr; byte *enc_txt; int enc_txt_size; char text[256]; int j; text[0] = '\0'; for (j = 2; argv[j]; j++) { if (j > 2) strlcat(text, " ", 256); strlcat(text, argv[j], 256); } enc_txt = name_encode(text, &enc_txt_size, '\\'); if (enc_txt == NULL) return enc_txt_size; enc_txt_size--; rr = alloc_rr(argv[1], T_TXT, local_class, enc_txt_size); memcpy(rr->data, enc_txt, enc_txt_size); free(enc_txt); local_add_entry(rr); local_ylog(rr); return 0; } int local_add_SOA(int argc, char **argv) { struct RRentry *rr; struct RR_SOA soa; byte *enc_dns; byte *enc_mailbox; int enc_dns_size; int enc_mailbox_size; ARG_UNUSED(argc) enc_dns = name_encode(argv[2], &enc_dns_size, '.'); if (enc_dns == NULL) return enc_dns_size; enc_mailbox = name_encode(argv[3], &enc_mailbox_size, '.'); if (enc_mailbox == NULL) { free(enc_dns); return enc_mailbox_size; } rr = alloc_rr(argv[1], T_SOA, local_class, sizeof(struct RR_SOA)+enc_dns_size+enc_mailbox_size); memcpy(rr->data, enc_dns, enc_dns_size); memcpy(rr->data+enc_dns_size, enc_mailbox, enc_mailbox_size); soa.serial = htonl(atoi(argv[4])); soa.refresh = htonl(atoi(argv[5])); soa.retry = htonl(atoi(argv[6])); soa.expire = htonl(atoi(argv[7])); soa.minimum = htonl(atoi(argv[8])); memcpy(rr->data+enc_dns_size+enc_mailbox_size, &soa, sizeof(soa)); free(enc_dns); free(enc_mailbox); local_add_entry(rr); ylog(VERB_HIG, "loaded: SOA for %s\n" "\tdns : %s\n" "\tmailbox: %s\n" "\tserial : %lu\n" "\trefresh: %lu\n" "\tretry : %lu\n" "\texpire : %lu\n" "\tminimum: %lu\n", argv[1], argv[2], argv[3], (unsigned long) ntohl(soa.serial), (unsigned long) ntohl(soa.refresh), (unsigned long) ntohl(soa.retry), (unsigned long) ntohl(soa.expire), (unsigned long) ntohl(soa.minimum)); return 0; } /* Search in the local table */ struct RRentry *local_search(char *name, u_int16_t qtype, u_int16_t qclass, u_int32_t seq) { char key[HT_MAX_KEYSIZE]; int ret; unsigned int i; rr_to_key(key, HT_MAX_KEYSIZE, name, qtype, qclass, seq); ret = ht_search(&local_table, key, &i); if (ret == HT_FOUND) return (struct RRentry*) ht_value(&local_table, i); return NULL; } /* Search for a given name-class-type in the local table and put * all the matching RRs in the given array. * Four different paths for speed. */ int local_search_all(char *name, u_int16_t qtype, u_int16_t qclass, struct RRentry **rra, unsigned int size) { struct RRentry *rr; u_int32_t seq = 0; int status = 0; unsigned int index = 0; yakuns_assert(rra != NULL); if (size == 0) return 0; /* Path for specified class and type */ if (qtype != T_ANY && qclass != C_ANY) { while (size && status < 4) { switch(status) { case 0: rr = local_search(name, qtype, qclass, seq); break; case 1: rr = local_search(name, T_ANY, qclass, seq); break; case 2: rr = local_search(name, qtype, C_ANY, seq); break; case 3: rr = local_search(name, T_ANY, C_ANY, seq); break; default: yakuns_assert(1 != 1); /* unreached */ } if (rr == NULL) { seq = 0; status++; continue; } rra[index] = rr; size--; index++; seq++; } return index; } /* Path for class = * and type = * */ if (qtype == T_ANY && qclass == C_ANY) { int types[] = {T_A, T_NS, T_PTR, T_MX, T_TXT, T_SOA, T_ANY}; int classes[] = {C_IN, C_ANY, C_CHAOS}; int types_nr = sizeof(types) / sizeof(int); int classes_nr = sizeof(classes) / sizeof(int); int type, class; for (class = 0; class < classes_nr; class++) { for (type = 0; type < types_nr; type++) { for (seq = 0; ; seq++) { rr = local_search(name, types[type], classes[class], seq); if (rr == NULL) break; rra[index] = rr; size--; index++; if (size == 0) goto out1; } } } out1: return index; } /* Path for specified class and qtype = * */ if (qtype == T_ANY && qclass != C_ANY) { int types[] = {T_A, T_NS, T_PTR, T_MX, T_TXT, T_SOA, T_ANY}; int types_nr = sizeof(types) / sizeof(int); int type; for (type = 0; type < types_nr; type++) { for (seq = 0; ; seq++) { unsigned int old_index = index; rr = local_search(name, types[type], qclass, seq); if (rr != NULL) { rra[index] = rr; size--; index++; if (size == 0) goto out2; } /* Search it with the ANY class */ rr = local_search(name, types[type], C_ANY, seq); if (rr != NULL) { rra[index] = rr; size--; index++; if (size == 0) goto out2; } if (index == old_index) break; } } out2: return index; } /* Path for specified qtype and qclass = * */ if (qtype != T_ANY && qclass == C_ANY) { int classes[] = {C_IN, C_ANY, C_CHAOS}; int classes_nr = sizeof(classes) / sizeof(int); int class; for (class = 0; class < classes_nr; class++) { for (seq = 0; ; seq++) { unsigned int old_index = index; rr = local_search(name, qtype, classes[class], seq); if (rr != NULL) { rra[index] = rr; size--; index++; if (size == 0) goto out3; } rr = local_search(name, T_ANY, classes[class], seq); if (rr != NULL) { rra[index] = rr; size--; index++; if (size == 0) goto out3; } if (index == old_index) break; } } out3: return index; } yakuns_assert(1 != 1); /* unreached */ return index; } void local_init(void) { ht_init(&local_table); ht_set_hash(&local_table, ht_dnskey_hash); ht_set_key_destructor(&local_table, ht_destructor_free); ht_set_val_destructor(&local_table, ht_local_destructor); ht_set_key_compare(&local_table, ht_dnskey_compare); local_table.hashf = ht_dnskey_hash; local_table.key_destructor = ht_destructor_free; local_table.val_destructor = ht_local_destructor; local_table.key_compare = ht_dnskey_compare; } yaku-ns-0.2/log.c000066400000000000000000000026361425072213200136740ustar00rootroot00000000000000/* log.c * Functions to perform logging * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include #include /* global vars */ FILE *logfp = NULL; int opt_logtime = 1; int opt_verbose = 0; /* exported functions */ int ylog(int level, char *fmt, ...); int log_flush(void); void open_logfile(char *filename); /* Just log the printf-like message */ int ylog(int level, char *fmt, ...) { time_t t; struct tm *tmtime; char timestring[64]; int ret = 0; va_list ap; /* Return ASAP if we can't log the message */ if (opt_verbose < level || logfp == NULL) return 0; t = time(NULL); tmtime = gmtime(&t); va_start(ap, fmt); /* Log the timestamp */ if (opt_logtime && strftime(timestring, 64, "%Y/%m/%d %T] ", tmtime) != 0) fprintf(logfp, "%s", timestring); /* Log the message */ DEBUG(if(level == VERB_DEBUG) fprintf(logfp, "[DEBUG] ");) ret = vfprintf(logfp, fmt, ap); va_end(ap); return ret; } /* Flush the log buffer */ int log_flush(void) { return fflush(logfp); } /* Open the log file */ void open_logfile(char *filename) { FILE *fp; fp = fopen(filename, "a"); if (!fp) { perror("[open_logfile] Opening the log file: fopen"); return; } logfp = fp; } yaku-ns-0.2/misc.c000066400000000000000000000141621425072213200140430ustar00rootroot00000000000000/* misc.c * miscellaneous code * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include "aht.h" #ifdef __unix__ #include #include #endif /* __unix__ */ #include #include #include /* not exported functions */ /* exported functions */ u_int32_t get_rand32(void); time_t get_sec(void); char *qtype_to_str(unsigned short qtype); char *qclass_to_str(unsigned short qclass); int line_splitter(char *buffer, char *commandargs[], int argmax); void dump_state(void); u_int16_t get_rand_id(void); /* --------------------------------------------------------------------------- */ time_t get_sec(void) { return time(NULL); } char *qtype_to_str(unsigned short qtype) { int i; static struct _QTYPE { unsigned short code; char *str; } qtype_str[] = { { 1, "A" }, { 2, "NS" }, { 3, "MD" }, { 4, "MF" }, { 5, "CNAME" }, { 6, "SOA" }, { 7, "MB" }, { 8, "MG" }, { 9, "MR" }, { 10, "NULL" }, { 11, "WKS" }, { 12, "PTR" }, { 13, "HINFO" }, { 14, "MINFO" }, { 15, "MX" }, { 16, "TXT" }, { 17, "RP" }, { 18, "AFSDB" }, { 19, "X25" }, { 20, "ISDN" }, { 21, "RT" }, { 22, "NSAP" }, { 23, "NSAP_PTR"}, { 24, "SIG"}, { 25, "KEY"}, { 26, "PX"}, { 27, "GPOS"}, { 28, "AAAA"}, { 29, "LOC"}, { 30, "NXT"}, { 31, "EID"}, { 32, "NIMLOC"}, { 33, "SRV"}, { 34, "ATMA"}, { 35, "NAPTR"}, { 36, "KX"}, { 37, "CERT"}, { 38, "A6"}, { 39, "DNAME"}, { 40, "SINK"}, { 41, "OPT"}, /* non standard */ { 100, "UINFO"}, { 101, "UID"}, { 102, "GID"}, { 103, "UNSPEC"}, /* Query type values which do not appear in resource records */ { 249, "TKEY"}, { 250, "TSIG"}, { 251, "IXFR"}, { 252, "AXFR" }, { 253, "MAILB" }, { 254, "MAILA" }, { 255, "*" }, { 256, "bind-ZXFR"}, { 0, NULL} /* NUL TERM */ }; for (i = 0; qtype_str[i].code; i++) { if (qtype_str[i].code == qtype) return qtype_str[i].str; } return "UNKNOWN"; } char *qclass_to_str(unsigned short qclass) { int i; static struct _QCLASS { unsigned short code; char *str; } qclass_str[] = { { 1, "IN" }, { 2, "CS" }, { 3, "CH" }, { 4, "HS" }, { 255, "*" }, { 0, NULL} /* NUL TERM */ }; for (i = 0; qclass_str[i].code; i++) { if (qclass_str[i].code == qclass) return qclass_str[i].str; } return "UNKNOWN"; } #define skip_spacetab() while(*p == ' ' || *p == '\t') p++ int line_splitter(char *buffer, char *commandargs[], int argmax) { char *p = buffer, *d; char tmp[1024]; int size; int argindex = 0; /* if buffer is a NULL pointer free commandargs memory */ if (buffer == NULL) { for (; commandargs[argindex] != NULL; argindex++) free(commandargs[argindex]); return argindex; } /* otherwise parse the command line */ while(*p != '\0') { size = 0; d = tmp; skip_spacetab(); while(*p != ' ' && *p != '\t') { if (*p == '\0' || *p == '\n' || *p == '\r' || size >= 1023) break; *d++ = *p++; size++; } if (size != 0) { commandargs[argindex] = malloc(size+1); if (commandargs[argindex] == NULL) { perror("[line_splitter] malloc"); exit(1); } strlcpy(commandargs[argindex], tmp, size+1); } else { break; } argindex++; if (argindex >= argmax) break; } commandargs[argindex] = NULL; return argindex; } void dump_state(void) { ylog(VERB_FORCE, "dump_state() requested, dump follows\n" "\n-- GENERAL\n" "s = %d\n" "configfile = %s\n" "opt_udp_port = %d\n" "local_size = %u\n" "local_used = %u\n" "\n-- FORWARDING\n" "opt_forward = %d\n" "dns_forward_port = %d\n" "forward_server = %s\n" "forward_count = %d\n" "forward_size = %u\n" "forward_used = %u\n" "\n-- CACHE\n" "opt_cache = %d\n" "cache_count = %d\n" "cache_table_size = %u\n" "cache_table_used = %u\n" "\n-- STATS\n" "statistic_received_packet = %d\n" "statistic_invalid_packet = %d\n" "statistic_query_count = %d\n" "statistic_iquery_count = %d\n" "statistic_response_count = %d\n" "\n", s, configfile, opt_udp_port, local_table.size, local_table.used, opt_forward, dns_forward_port, inet_ntoa(forward_server[0]), forward_count, forward_table.size, forward_table.used, opt_cache, cache_count, cache_table.size, cache_table.used, statistic_received_packet, statistic_invalid_packet, statistic_query_count, statistic_iquery_count, statistic_response_count); fflush(logfp); } /* Don't expect maximun security here, anyway the id is 16 bit large */ u_int16_t get_rand_id(void) { u_int32_t id; static u_int16_t inc = 0; #ifdef __unix__ struct timeval tmptv; gettimeofday(&tmptv, NULL); id = tmptv.tv_usec ^ tmptv.tv_sec ^ getpid() ^ inc++; #else /* not __unix__ */ id = rand() ^ time(NULL) ^ clock() ^ inc++; #endif /* not __unix__ */ return (u_int16_t) (id & 0xffff); } /* WARNING: THIS MAY WORK ONLY WITH GNU MALLOC but this stuff is used only to trace memory leaks and isn't part of a normal ENS binary */ #ifdef TRACE_LEAKS #undef malloc #undef realloc #undef free void tl_current(int x, int y); void *tl_malloc(char *file, int line, size_t size) { void *ptr = malloc(size); int *l = (int*) ptr; printf("%s %d: malloc(%d) = %p, ", file, line, size, ptr); if (size == 0) { printf("malloc zero\n"); exit(1); } printf("allocated %d bytes\n", *(l-1)); tl_current(*(l-1), 1); return ptr; } void *tl_realloc(char *file, int line, void *ptr, size_t size) { int *o = (int*) ptr; int old = (o != NULL) ? *(o-1) : 0; void *newptr = realloc(ptr, size); int *l = (int*) newptr; printf("%s %d: realloc(%p, %d) = %p, ", file, line, newptr, size, ptr); printf("allocated %d bytes\n", *(l-1) - old); tl_current(*(l-1)-old, ptr ? 0 : 1); return newptr; } void tl_free(char *file, int line, void *ptr) { int *l = (int*) ptr; printf("%s %d: free(%p), ", file, line, ptr); printf("freed %d bytes\n", *(l-1)); tl_current(-(*(l-1)), -1); free(ptr); } void tl_current(int x, int y) { static int current = 0; static int chunkes = 0; current += x; chunkes += y; printf("current: %d bytes (in %d chunkes)\n", current, chunkes); } #endif yaku-ns-0.2/nameser.h000066400000000000000000000333451425072213200145530ustar00rootroot00000000000000/* * ++Copyright++ 1983, 1989, 1993 * - * 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. * - * 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. * --Copyright-- */ /* * @(#)nameser.h 8.1 (Berkeley) 6/2/93 * $Id: nameser.h,v 1.3 2002/07/12 17:11:33 antirez Exp $ */ #ifndef _ARPA_NAMESER_H #define _ARPA_NAMESER_H 1 /* #include */ #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 (__BIND > 19931104)". do not * compare for equality; rather, use it to determine whether your resolver * is new enough to contain a certain feature. */ #define __BIND 19960801 /* interface version stamp */ /* * Define constants based on rfc883 */ #define PACKETSZ 512 /* maximum UDP packet size */ #define TCPPACKETSZ 65535 /* maximum TCP packet size */ #define MAXDNAME 1025 /* maximum domain name */ #define MAXCDNAME 255 /* maximum compressed domain name */ #define MAXLABEL 63 /* maximum length of domain label */ #define HFIXEDSZ 12 /* #/bytes of fixed data in header */ #define QFIXEDSZ 4 /* #/bytes of fixed data in query */ #define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ #define INT32SZ 4 /* for systems without 32-bit ints */ #define INT16SZ 2 /* for systems without 16-bit ints */ #define INADDRSZ 4 /* IPv4 T_A */ #define IN6ADDRSZ 16 /* IPv6 T_AAAA */ /* * Internet nameserver port number */ #define NAMESERVER_PORT 53 /* * Currently defined opcodes */ #define QUERY 0x0 /* standard query */ #define IQUERY 0x1 /* inverse query */ #define STATUS 0x2 /* nameserver status query */ /*#define xxx 0x3*/ /* 0x3 reserved */ #define NS_NOTIFY_OP 0x4 /* notify secondary of SOA change */ /* * Currently defined response codes */ #define NOERROR 0 /* no error */ #define FORMERR 1 /* format error */ #define SERVFAIL 2 /* server failure */ #define NXDOMAIN 3 /* non existent domain */ #define NOTIMP 4 /* not implemented */ #define REFUSED 5 /* query refused */ /* * Type values for resources and queries */ #define T_A 1 /* host address */ #define T_NS 2 /* authoritative server */ #define T_MD 3 /* mail destination */ #define T_MF 4 /* mail forwarder */ #define T_CNAME 5 /* canonical name */ #define T_SOA 6 /* start of authority zone */ #define T_MB 7 /* mailbox domain name */ #define T_MG 8 /* mail group member */ #define T_MR 9 /* mail rename name */ #define T_NULL 10 /* null resource record */ #define T_WKS 11 /* well known service */ #define T_PTR 12 /* domain name pointer */ #define T_HINFO 13 /* host information */ #define T_MINFO 14 /* mailbox information */ #define T_MX 15 /* mail routing information */ #define T_TXT 16 /* text strings */ #define T_RP 17 /* responsible person */ #define T_AFSDB 18 /* AFS cell database */ #define T_X25 19 /* X_25 calling address */ #define T_ISDN 20 /* ISDN calling address */ #define T_RT 21 /* router */ #define T_NSAP 22 /* NSAP address */ #define T_NSAP_PTR 23 /* reverse NSAP lookup (deprecated) */ #define T_SIG 24 /* security signature */ #define T_KEY 25 /* security key */ #define T_PX 26 /* X.400 mail mapping */ #define T_GPOS 27 /* geographical position (withdrawn) */ #define T_AAAA 28 /* IP6 Address */ #define T_LOC 29 /* Location Information */ #define T_NXT 30 /* Next Valid Name in Zone */ #define T_EID 31 /* Endpoint identifier */ #define T_NIMLOC 32 /* Nimrod locator */ #define T_SRV 33 /* Server selection */ #define T_ATMA 34 /* ATM Address */ #define T_NAPTR 35 /* Naming Authority PoinTeR */ /* non standard */ #define T_UINFO 100 /* user (finger) information */ #define T_UID 101 /* user ID */ #define T_GID 102 /* group ID */ #define T_UNSPEC 103 /* Unspecified format (binary data) */ /* Query type values which do not appear in resource records */ #define T_IXFR 251 /* incremental zone transfer */ #define T_AXFR 252 /* transfer zone of authority */ #define T_MAILB 253 /* transfer mailbox records */ #define T_MAILA 254 /* transfer mail agent records */ #define T_ANY 255 /* wildcard match */ /* * Values for class field */ #define C_IN 1 /* the arpa internet */ #define C_CHAOS 3 /* for chaos net (MIT) */ #define C_HS 4 /* for Hesiod name server (MIT) (XXX) */ /* Query class values which do not appear in resource records */ #define C_ANY 255 /* wildcard match */ /* * Flags field of the KEY RR rdata */ #define KEYFLAG_TYPEMASK 0xC000 /* Mask for "type" bits */ #define KEYFLAG_TYPE_AUTH_CONF 0x0000 /* Key usable for both */ #define KEYFLAG_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */ #define KEYFLAG_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */ #define KEYFLAG_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */ /* The type bits can also be interpreted independently, as single bits: */ #define KEYFLAG_NO_AUTH 0x8000 /* Key not usable for authentication */ #define KEYFLAG_NO_CONF 0x4000 /* Key not usable for confidentiality */ #define KEYFLAG_EXPERIMENTAL 0x2000 /* Security is *mandatory* if bit=0 */ #define KEYFLAG_RESERVED3 0x1000 /* reserved - must be zero */ #define KEYFLAG_RESERVED4 0x0800 /* reserved - must be zero */ #define KEYFLAG_USERACCOUNT 0x0400 /* key is assoc. with a user acct */ #define KEYFLAG_ENTITY 0x0200 /* key is assoc. with entity eg host */ #define KEYFLAG_ZONEKEY 0x0100 /* key is zone key for the zone named */ #define KEYFLAG_IPSEC 0x0080 /* key is for IPSEC use (host or user)*/ #define KEYFLAG_EMAIL 0x0040 /* key is for email (MIME security) */ #define KEYFLAG_RESERVED10 0x0020 /* reserved - must be zero */ #define KEYFLAG_RESERVED11 0x0010 /* reserved - must be zero */ #define KEYFLAG_SIGNATORYMASK 0x000F /* key can sign DNS RR's of same name */ #define KEYFLAG_RESERVED_BITMASK ( KEYFLAG_RESERVED3 | \ KEYFLAG_RESERVED4 | \ KEYFLAG_RESERVED10| KEYFLAG_RESERVED11) /* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */ #define ALGORITHM_MD5RSA 1 /* MD5 with RSA */ #define ALGORITHM_EXPIRE_ONLY 253 /* No alg, no security */ #define ALGORITHM_PRIVATE_OID 254 /* Key begins with OID indicating alg */ /* Signatures */ /* Size of a mod or exp in bits */ #define MIN_MD5RSA_KEY_PART_BITS 512 #define MAX_MD5RSA_KEY_PART_BITS 2552 /* Total of binary mod and exp, bytes */ #define MAX_MD5RSA_KEY_BYTES ((MAX_MD5RSA_KEY_PART_BITS+7/8)*2+3) /* Max length of text sig block */ #define MAX_KEY_BASE64 (((MAX_MD5RSA_KEY_BYTES+2)/3)*4) /* * Status return codes for T_UNSPEC conversion routines */ #define CONV_SUCCESS 0 #define CONV_OVERFLOW (-1) #define CONV_BADFMT (-2) #define CONV_BADCKSUM (-3) #define CONV_BADBUFLEN (-4) /* Include endianess info in mostly portable way */ #include "yakuendian.h" __BEGIN_DECLS /* * Structure for query header. The order of the fields is machine- and * compiler-dependent, depending on the byte/bit order and the layout * of bit fields. We use bit fields only in int variables, as this * is all ANSI requires. This requires a somewhat confusing rearrangement. */ typedef struct { unsigned id :16; /* query identification number */ #if BYTE_ORDER == BIG_ENDIAN /* fields in third byte */ unsigned qr: 1; /* response flag */ unsigned opcode: 4; /* purpose of message */ unsigned aa: 1; /* authoritative answer */ unsigned tc: 1; /* truncated message */ unsigned rd: 1; /* recursion desired */ /* fields in fourth byte */ unsigned ra: 1; /* recursion available */ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ unsigned ad: 1; /* authentic data from named */ unsigned cd: 1; /* checking disabled by resolver */ unsigned rcode :4; /* response code */ #endif #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN /* fields in third byte */ unsigned rd :1; /* recursion desired */ unsigned tc :1; /* truncated message */ unsigned aa :1; /* authoritative answer */ unsigned opcode :4; /* purpose of message */ unsigned qr :1; /* response flag */ /* fields in fourth byte */ unsigned rcode :4; /* response code */ unsigned cd: 1; /* checking disabled by resolver */ unsigned ad: 1; /* authentic data from named */ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ unsigned ra :1; /* recursion available */ #endif /* remaining bytes */ unsigned qdcount :16; /* number of question entries */ unsigned ancount :16; /* number of answer entries */ unsigned nscount :16; /* number of authority entries */ unsigned arcount :16; /* number of resource entries */ } HEADER; /* * Defines for handling compressed domain names */ #define INDIR_MASK 0xc0 extern u_int16_t _getshort __P((const u_char *)); extern u_int32_t _getlong __P((const u_char *)); /* * Inline versions of get/put short/long. Pointer is advanced. * * These macros demonstrate the property of C whereby it can be * portable or it can be elegant but rarely both. */ #define GETSHORT(s, cp) { \ register u_char *t_cp = (u_char *)(cp); \ (s) = ((u_int16_t)t_cp[0] << 8) \ | ((u_int16_t)t_cp[1]) \ ; \ (cp) += INT16SZ; \ } #define GETLONG(l, cp) { \ 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) += INT32SZ; \ } #define PUTSHORT(s, cp) { \ 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) += INT16SZ; \ } #define PUTLONG(l, cp) { \ 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) += INT32SZ; \ } __END_DECLS #endif /* arpa/nameser.h */ yaku-ns-0.2/response.c000066400000000000000000000140641425072213200147470ustar00rootroot00000000000000/* response.c * Local responses building * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #define MAX_RR 256 /* exported functions */ byte *build_response(u_int16_t qclass, u_int16_t qtype, char *qname, char *name, byte *query, int query_size, HEADER *hdr, int *size, int maxsize); /* -------------------------------------------------------------------------- */ /* This function builds a complete DNS response. * The maxsize parameter doesn't specify the maximum size of the * response returned, but the maximum size of the DNS packet under * the used protocol (for example 512 for UDP). This information * is used to leave the additional information out if there isn't * space left, but the real DNS truncation is done in the * function that sends the packet. */ byte *build_response(u_int16_t qclass, u_int16_t qtype, char *qname, char *name, byte *query, int query_size, HEADER *hdr, int *size, int maxsize) { byte *response = NULL; int response_size = 0; int j; /* counter */ int ret, retsize; struct additionalrr arr[MAX_ADDRR+1] = { {NULL, 0, 0} }; int arr_index = 0; char *p, canonical[MAXDNAME]; struct RRentry *rrs[MAX_RR], *cnamerr = NULL; int cname_chainlen = 0; /* Get pointers to matching RRs */ ret = local_search_all(name, qtype, qclass, rrs, MAX_RR); /* No match? check if it is a CNAME for something. * XXX: we don't follow CNAME chains for now... it should * either implemented or not allowed in the configuration */ if (!ret) { while(cname_chainlen < CNAME_CHAIN_MAX) { if ((cnamerr = local_search(name, T_CNAME, qclass, 0))) { int retval; char *canonicalp; retval = name_decode(cnamerr->data, cnamerr->size, NULL, &canonicalp, 0); if (retval == YK_NOMEM) goto out; strlcpy(canonical, canonicalp, MAXDNAME); free(canonicalp); /* Reiterate the search using the * canonical name */ ret = local_search_all(canonical, qtype, qclass, rrs, MAX_RR); ret ++; break; } break; /* Don't follow CNAME chains for now */ } } /* Add the matching RRs in the answer section */ if (ret) { byte *tmp; /* Build the header if needed */ if (!response) { retsize = build_header(&response, hdr, 1); if (retsize == YK_NOMEM) goto out; response_size += retsize; } /* Copy the original query in the question section */ tmp = realloc(response, response_size+query_size); if (tmp == NULL) goto out; response = tmp; memcpy(response+response_size, query, query_size); response_size += query_size; /* Add the CNAME RR, if it was a CNAME match */ if (cnamerr) { ret --; retsize = add_rr(&response, hdr, cnamerr, response_size, AN_SECTION, maxsize); if (retsize < 0) goto out; response_size += retsize; /* Our RRs must refer to the canonical name */ qname = canonical; } /* Add the matching RRs found */ for (j = 0; j < ret; j++) { struct RRentry *rr = rrs[j]; char *origname; /* build the needed Additional RRs list */ arr_index += additional_rr_needed(&arr[arr_index], rrs[j], arr_index); /* add the RR. Note that even if we matched a wildcard * local RR, we add the requested name in the query */ origname = rr->name; rr->name = qname; rr->name = origname; /* ready to add the RR i the response */ retsize = add_rr(&response, hdr, rr, response_size, AN_SECTION, maxsize); if (retsize < 0) goto out; response_size += retsize; } } /* Add the authority section */ if (response) { ret = local_search_all(name, T_NS, C_ANY, rrs, MAX_RR); if (ret == 0) { int l = strlen(name); char *namecopy = alloca(l + 1); memcpy(namecopy, name, l+1); if ((p = strchr(namecopy, '.')) == NULL) goto as_out; /* skip the authority sect. code */ if (*(p+1) != '\0') p++; ret = local_search_all(p, T_NS, C_ANY, rrs, MAX_RR); } if (ret != 0) { for (j = 0; j < ret; j++) { DEBUG(log(VERB_DEBUG, "Needed ARR %s %s %s\n", qtype_to_str(rrs[j]->qtype), qclass_to_str(rrs[j]->qclass), rrs[j]->name);) arr_index += additional_rr_needed( &arr[arr_index], rrs[j], arr_index); /* add the RR */ retsize = add_rr(&response, hdr, rrs[j], response_size, NS_SECTION, maxsize); if (retsize < 0) goto out; response_size += retsize; } } } as_out: /* We can add the Additional RRs at this point, we compiled * the list of additional RRs to add inside the while() above. * XXX: add C_ANY and T_ANY checks, after reading RFCs */ for (j = 0; response && j < MAX_ADDRR && arr[j].name; j++) { int l, already = 0; DEBUG(log(VERB_DEBUG, "Adding ARR for %s %s %s\n", qtype_to_str(arr[j].qtype), qclass_to_str(arr[j].qclass), arr[j].name);) /* Avoid duplications: usually additional RRs are * few so the linear search seems the faster way */ for (l = 0; l < j; l++) { if ( arr[j].qtype == arr[l].qtype && arr[j].qclass == arr[l].qclass && strcasecmp(arr[j].name, arr[l].name) == 0) { already = 1; break; } } if (already) continue; /* Search and add the matching RRs: * of course there isn't additional RRs processing * for the additional RRs :) */ ret = local_search_all(arr[j].name, arr[j].qtype, arr[j].qclass, rrs, MAX_RR); DEBUG(log(VERB_DEBUG, "Found %d matching RRs for it\n", ret);) if (ret) { int c; for (c = 0; c < ret; c++) { /* add the RR */ retsize = add_rr(&response, hdr, rrs[c], response_size, AR_SECTION, maxsize); if (retsize < 0) goto out; response_size += retsize; } } } /* free the additional RR entry allocated */ for (j = 0; j <= MAX_ADDRR; j++) { arr[j].qtype = 0; arr[j].qclass = 0; if (arr[j].name != NULL) { free (arr[j].name); arr[j].name = NULL; } } *size = response_size; return response; out: free(response); *size = YK_NOMEM; return NULL; } yaku-ns-0.2/rlimit.c000066400000000000000000000011761425072213200144110ustar00rootroot00000000000000/* rlimit.c - process limits related stuff * * Copyright(C) 2002 Salvatore Sanfilippo * All rights reserved. * See the LICENSE file for COPYRIGHT and PERMISSION notice */ /* $Id: rlimit.c,v 1.2 2003/09/13 07:10:29 antirez Exp $ */ #include #include #include #include int set_core_size(unsigned int size) { struct rlimit rl; if (getrlimit(RLIMIT_CORE, &rl) == -1) { perror("getrlimit"); return -1; } rl.rlim_cur = (size > rl.rlim_max) ? rl.rlim_max : size; if (setrlimit(RLIMIT_CORE, &rl) == -1) { perror("setrlimit"); return -1; } return 0; } yaku-ns-0.2/signal.c000066400000000000000000000047371425072213200143740ustar00rootroot00000000000000/* signal.c * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include static volatile int signal_up = 0; static volatile int signal_usr1 = 0; static volatile int signal_usr2 = 0; /* The signals handler: * for the signals that can't be handled here * just set the flag */ void signal_handler(int signum) { switch(signum) { case SIGHUP: signal_up++; break; case SIGUSR1: signal_usr1++; break; case SIGUSR2: signal_usr2++; break; case SIGSEGV: ylog(VERB_FORCE, "SIGSEGV trapped -- INTERNAL ERROR\n"); dump_state(); abort(); break; default: ylog(VERB_FORCE, "Signal %d trapped: exit\n", signum); exit(1); } install_signal_handler(); } /* Handle the signals that can't be handled asyncronously */ int handle_signals(void) { int count = 0; if (signal_up) { signal_up--; ylog(VERB_LOW, "SIGHUP trapped: read the config file\n"); config_reset(); config_read(configfile); count++; } if (signal_usr1) { signal_usr1--; dump_state(); count++; } if (signal_usr2) { signal_usr2--; opt_forward = !opt_forward; ylog(VERB_LOW, "SIGUSR2 trapped, forwarding is %s\n", opt_forward ? "ON" : "OFF"); fflush(logfp); count++; } return count; } /* Install the handlers */ void install_signal_handler(void) { Signal(SIGHUP, signal_handler); Signal(SIGUSR1, signal_handler); Signal(SIGUSR2, signal_handler); Signal(SIGSEGV, signal_handler); } /* Portable signal() from R.Stevens, * modified to reset the handler */ void (*Signal(int signo, void (*func)(int)))(int) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* So if set SA_RESETHAND is cleared */ if (signo == SIGALRM) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD, Linux */ #endif } if (sigaction(signo, &act, &oact) == -1) return SIG_ERR; return (oact.sa_handler); } /* Block the given signal */ int signal_block(int sig) { sigset_t set; sigemptyset(&set); sigaddset(&set, sig); return sigprocmask(SIG_BLOCK, &set, NULL); } /* Unblock the given signal */ int signal_unblock(int sig) { sigset_t set; sigemptyset(&set); sigaddset(&set, sig); return sigprocmask(SIG_UNBLOCK, &set, NULL); } yaku-ns-0.2/strlcat.c000066400000000000000000000045671425072213200145740ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. * * (Old style prototype traslated) */ #include #include /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(initial dst) + strlen(src); if retval >= siz, * truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } yaku-ns-0.2/strlcpy.c000066400000000000000000000044221425072213200146060ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. * * (Old style prototype traslated) */ #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } yaku-ns-0.2/tunable.h000066400000000000000000000050661425072213200145520ustar00rootroot00000000000000#ifndef __CONFIG_H #define __CONFIG_H /* This is the ENS's config.h file, you can change defaults and enable/disable * some feature here. If you trying to obtain binary size reduction you should * start commenting some ENS_* #define in this file. */ /* ------------ The version number follows the linux kernel way ------------- */ #define ENS_VERSION "0.1.6" #if 0 #ifdef __i386__ #define PROFILING #endif #endif /* ----------------------------- defaults ------------------------------------*/ /* All the following defines are just defaults that are overridable * using the command line options. See the README for more information. */ #define DNS_PORT 53 #define DNS_FORWARD_PORT 53 #define FORWARD_MAX_QUEUE 1000 /* 0 means disable forwarding */ #define FORWARD_TIMEOUT 50 /* 0 means disable timeout */ #define NEXT_SERVER_TIMEOUT 3 #define CACHE_MAX 5000 /* 0 means disable cache */ #define SAFEUSER "nobody" #define CONFIG_FILE "/etc/yaku-ns/yaku-ns.conf" #define YK_CORE_SIZE 2000 /* max core dump size */ #define CNAME_CHAIN_MAX 4 /* max lenght of cname chain to follow in local lookup */ /* ---------------------------- fixed defines --------------------------------*/ #define MAX_FORWARD_SERVERS 10 /* ---------------------------- TTL related defines --------------------------*/ /* default time to live for local RRs */ #define TTL_LOCAL_DEFAULT 3600 /* 1h */ /* TTL_MAX/MIN is the max/min time to live in the cache for a response */ #define CACHE_MAX_TTL 86400 /* 1 day */ #define CACHE_MIN_TTL 0 /* 0 seconds */ /* time to live for errors */ #define TTL_ERR_FORMAT 0 #define TTL_ERR_FAILURE 0 #define TTL_ERR_NAME 60 #define TTL_ERR_NOTIMPLEMENTED 0 #define TTL_ERR_REFUSED 0 /* ---------------------------- Additional RRs -------------------------------*/ /* max number of additional RRs */ #define MAX_ADDRR 20 /* ---------------------------- Configuration --------------------------------*/ /* max number of tokens for line in config file and dynamic ENS protocol */ #define LINEARGS_MAX 64 /* ---------------------------- ACL ------------------------------------------*/ /* leave this untouched! 255.255.255.255$\0 is 17 bytes */ #define RULE_MAXSIZE 17 /* --------------------------- Scheduler -------------------------------------*/ #define ENS_AWAKE_TIME_SEC 1 #define ENS_AWAKE_TIME_USEC 0 #define SCHEDULE_SCHEDULER 1 #define SCHEDULE_CACHE_EXPIRE (60*15) /* 15m */ #define SCHEDULE_HASH_RESIZE (60*60) /* 1h */ #endif yaku-ns-0.2/unix.c000066400000000000000000000016701425072213200140730ustar00rootroot00000000000000/* unix.c * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license version 2 * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include #include #include #include /* from Richard Stevens's UNP */ void daemon_init(void) { int c; c = fork(); if (c == -1) { perror("[daemon_init] fork"); exit(1); } if (c) exit(0); /* parent termination */ setsid(); /* new session */ Signal(SIGHUP, SIG_IGN); c = fork(); if (c == -1) { perror("[daemon_init] fork"); exit(1); } if (c) exit(0); /* first child termination */ for (c = 0; c < 64; c++) close(c); if (open("/dev/null", O_RDWR) != 0 || open("/dev/null", O_RDWR) != 1 || open("/dev/null", O_RDWR) != 2) { perror("[daemon_init] opening /dev/null"); exit(1); } } yaku-ns-0.2/uptime.c000066400000000000000000000030431425072213200144070ustar00rootroot00000000000000/* uptime.c * Uptime over DNS * * Copyright (C) 2000,2001,2002 by Salvatore Sanfilippo * * * This code is under the GPL license * See the COPYING file for more information */ /* ens.h must be included before all other includes */ #include "ens.h" #include #include #define YAKU_UPTIME_RR_NAME "uptime.yaku" /* global vars */ int opt_uptime = 0; time_t ens_start; /* not exported functions */ /* exported functions */ int uptime_refresh(void); /* -------------------------------------------------------------------------- */ /* Usually called once every second, this function updates the * uptime.yaku TXT CHAOS RR in the local table. */ int uptime_refresh(void) { struct RRentry *uptimerr = NULL; char buffer[1024]; byte *encoded_uptime; int encoded_uptime_size; time_t uptime; uptimerr = local_search(YAKU_UPTIME_RR_NAME".", T_TXT, C_CHAOS, 0); if (uptimerr == NULL) { uptimerr = alloc_rr(YAKU_UPTIME_RR_NAME, T_TXT, C_CHAOS, 1); if (uptimerr == NULL) return -1; uptimerr->data[0] = 0; uptimerr->ttl = 0; local_add_entry(uptimerr); } uptime = get_sec() - ens_start; snprintf(buffer, 1024, "%ld days,%ld hours,%ld minutes,%ld seconds", uptime/86400, (uptime%86400)/3600, ((uptime%86400)%3600)/60, ((uptime%86400)%3600)%60); encoded_uptime = name_encode(buffer, &encoded_uptime_size, ','); if (encoded_uptime == NULL) return encoded_uptime_size; encoded_uptime_size--; free(uptimerr->data); uptimerr->data = encoded_uptime; uptimerr->size = encoded_uptime_size; return 0; } yaku-ns-0.2/utils.h000066400000000000000000000025541425072213200142570ustar00rootroot00000000000000/* utils.h - useful macro and defines * * Copyright(C) 2001-2002 Salvatore Sanfilippo * All rights reserved. * See the LICENSE file for COPYRIGHT and PERMISSION notice */ /* $Id: utils.h,v 1.1 2003/09/26 14:56:56 antirez Exp $ */ #ifndef __UTILS_H #define __UTILS_H #include /* abort() */ /* NULL may be defined as just 0, this may not the same as (void*)0 * for some arch. An example is a 64bit hard with 32bit int, and can * create problems with variadic functions. * * For variadic functions we use NULLPTR instead */ #ifndef NULLPTR /* This is hopefully not defined */ #define NULLPTR ((void*)0) #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /* It seems in gcc < 3.0 there is no way to suppress only the * warning for unused function parameters when you use -W. * This macro is better than -Wno-unused. We want all the * other unused warnings */ #define ARG_UNUSED(a) ((void) a); #ifndef MIN #define MIN(x,y) ((x)<(y)?(x):(y)) #endif #ifndef MAX #define MAX(x,y) ((x)>(y)?(x):(y)) #endif #ifdef NDEBUG #define yakuns_assert(expr) do { } while(0) #else /* !NDEBUG */ #define yakuns_assert(x) \ do { \ if ((x) == 0) { \ ylog(VERB_FORCE, \ "assert failed: %s is false in %s at line %d\n", \ #x, __FILE__, __LINE__); \ abort(); \ } \ } while(0) #endif /* !NDEBUG */ #endif /* __UTILS_H */ yaku-ns-0.2/yakuendian.h000066400000000000000000000033471425072213200152500ustar00rootroot00000000000000#ifndef __YAKUENDIAN_H #define __YAKUENDIAN_H #ifndef BYTE_ORDER #if (BSD >= 199103) # include #else #if defined(linux) || defined(__linux__) # include #else #define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */ #define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */ #define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/ #if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \ defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \ defined(__alpha__) || defined(__alpha) #define BYTE_ORDER LITTLE_ENDIAN #endif #if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \ defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \ defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\ defined(apollo) || defined(__convex__) || defined(_CRAY) || \ defined(__hppa) || defined(__hp9000) || \ defined(__hp9000s300) || defined(__hp9000s700) || \ defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc) #define BYTE_ORDER BIG_ENDIAN #endif #endif /* linux */ #endif /* BSD */ #endif /* BYTE_ORDER */ #if defined(__BYTE_ORDER) && !defined(BYTE_ORDER) #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define BYTE_ORDER LITTLE_ENDIAN #else #define BYTE_ORDER BIG_ENDIAN #endif #endif #if !defined(BYTE_ORDER) || \ (BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \ BYTE_ORDER != PDP_ENDIAN) /* you must determine what the correct bit order is for * your compiler - the next line is an intentional error * which will force your compiles to bomb until you fix * the above macros. */ #error "Undefined or invalid BYTE_ORDER" #endif #endif