From: Joerg Sonnenberger Date: Tue, 4 Jan 2005 19:58:53 +0000 (+0000) Subject: Update isc-dhcp to 3.0.2rc3 using patch infrastructure. X-Git-Tag: v2.0.1~9289^2 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/6a849ac2a542acceef89df87b6ea49a9f0ea8b7f Update isc-dhcp to 3.0.2rc3 using patch infrastructure. Also include the server and relay, but currently without rcNG script. Submitted-by: Simon 'corecode' Schubert --- 6a849ac2a542acceef89df87b6ea49a9f0ea8b7f diff --git a/contrib/dhcp-3.0/LICENSE b/contrib/dhcp-3.0/LICENSE new file mode 100644 index 0000000000..ef2536bf2b --- /dev/null +++ b/contrib/dhcp-3.0/LICENSE @@ -0,0 +1,20 @@ +# Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +# Copyright (c) 1995-2003 by Internet Software Consortium +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +# +# Internet Systems Consortium, Inc. +# 950 Charter Street +# Redwood City, CA 94063 +# +# http://www.isc.org/ diff --git a/contrib/dhcp-3.0/README b/contrib/dhcp-3.0/README new file mode 100644 index 0000000000..a487a2c6c9 --- /dev/null +++ b/contrib/dhcp-3.0/README @@ -0,0 +1,656 @@ + Internet Systems Consortium DHCP Distribution + Version 3.0.2rc3 + December 3, 2004 + + README FILE + +You should read this file carefully before trying to install or use +the ISC DHCP Distribution. + + TABLE OF CONTENTS + + 1 WHERE TO FIND DOCUMENTATION + 2 RELEASE STATUS + 3 BUILDING THE DHCP DISTRIBUTION + 3.1 UNPACKING IT + 3.2 CONFIGURING IT + 3.2.1 DYNAMIC DNS UPDATES + 3.2.2 LOCALLY DEFINED OPTIONS + 3.3 BUILDING IT + 4 INSTALLING THE DHCP DISTRIBUTION + 5 USING THE DHCP DISTRIBUTION + 5.1 FIREWALL RULES + 5.2 LINUX + 5.2.1 IF_TR.H NOT FOUND + 5.2.2 SO_ATTACH_FILTER UNDECLARED + 5.2.3 PROTOCOL NOT CONFIGURED + 5.2.4 BROADCAST + 5.2.6 IP BOOTP AGENT + 5.2.7 MULTIPLE INTERFACES + 5.3 SCO + 5.4 HP-UX + 5.5 ULTRIX + 5.6 FreeBSD + 5.7 NeXTSTEP + 5.8 SOLARIS + 6 SUPPORT + 6.1 HOW TO REPORT BUGS + + WHERE TO FIND DOCUMENTATION + +Documentation for this software includes this README file, the +RELNOTES file, and the manual pages, which are in the server, common, +client and relay subdirectories. The README file (this file) includes +late-breaking operational and system-specific information that you +should read even if you don't want to read the manual pages, and that +you should *certainly* read if you run into trouble. Internet +standards relating to the DHCP protocol are stored in the doc +subdirectory. You will have the best luck reading the manual pages if +you build this software and then install it, although you can read +them directly out of the distribution if you need to. + +DHCP server documentation is in the dhcpd man page. Information about +the DHCP server lease database is in the dhcpd.leases man page. +Server configuration documentation is in the dhcpd.conf man page as +well as the dhcp-options man page. A sample DHCP server +configuration is in the file server/dhcpd.conf. The source for the +dhcpd, dhcpd.leases and dhcpd.conf man pages is in the server/ sub- +directory in the distribution. The source for the dhcp-options.5 +man page is in the common/ subdirectory. + +DHCP Client documentation is in the dhclient man page. DHCP client +configuration documentation is in the dhclient.conf man page and the +dhcp-options man page. The DHCP client configuration script is +documented in the dhclient-script man page. The format of the DHCP +client lease database is documented in the dhclient.leases man page. +The source for all these man pages is in the client/ subdirectory in +the distribution. In addition, the dhcp-options man page should be +referred to for information about DHCP options. + +DHCP relay agent documentation is in the dhcrelay man page, the source +for which is distributed in the relay/ subdirectory. + +To read installed manual pages, use the man command. Type "man page" +where page is the name of the manual page. This will only work if +you have installed the ISC DHCP distribution using the ``make install'' +command (described later). + +If you want to read manual pages that aren't installed, you can type +``nroff -man page |more'' where page is the filename of the +unformatted manual page. The filename of an unformatted manual page +is the name of the manual page, followed by '.', followed by some +number - 5 for documentation about files, and 8 for documentation +about programs. For example, to read the dhcp-options man page, +you would type ``nroff -man common/dhcp-options.5 |more'', assuming +your current working directory is the top level directory of the ISC +DHCP Distribution. + +If you do not have the nroff command, you can type ``more catpage'' +where catpage is the filename of the catted man page. Catted man +pages names are the name of the manual page followed by ".cat" +followed by 5 or 8, as with unformatted manual pages. + +Please note that until you install the manual pages, the pathnames of +files to which they refer will not be correct for your operating +system. + + RELEASE STATUS + +This is the second release candidate of ISC DHCP 3.0.2. This is a +maintenance release which seeks only to fix bugs present in versions +3.0.1 and earlier. No new features have or will be added in subsequent +release candidates of this release. + +In this release, the server and relay agent are currently fully +functional on NetBSD, Linux systems with kernel version 2.2 or later, +FreeBSD, OpenBSD, BSD/OS, Digital Tru64 Unix and Solaris. The software +will also run on AIX and HP-UX, but only supports a single network +interface. Ports also exist for QNX, SCO, NeXTStep, and MacOS X, but +are not in wide use, with all that implies. We are not aware of an +easy way to get this software running on HP-UX. + +The DHCP client currently only knows how to configure the network on +NetBSD, FreeBSD, OpenBSD, BSD/os, Linux, Solaris and NextStep. The +client depends on a system-dependent shell script to do network +configuration - support for other operating systems is simply a matter +of porting this shell script to the new platform. + +If you are running the DHCP distribution on a machine which is a +firewall, or if there is a firewall between your DHCP server(s) and +DHCP clients, please read the section on firewalls which appears later +in this document. + +If you wish to run the DHCP Distribution on Linux, please see the +Linux-specific notes later in this document. If you wish to run on an +SCO release, please see the SCO-specific notes later in this document. +You particularly need to read these notes if you intend to support +Windows 95 clients. If you are running a version of FreeBSD prior to +2.2, please read the note on FreeBSD. If you are running HP-UX or +Ultrix, please read the notes for those operating systems below. If +you are running NeXTSTEP, please see the notes on NeXTSTEP below. + +If you start dhcpd and get a message, "no free bpf", that means you +need to configure the Berkeley Packet Filter into your operating +system kernel. On NetBSD, FreeBSD and BSD/os, type ``man bpf'' for +information. On Digital Unix, type ``man pfilt''. + + + BUILDING THE DHCP DISTRIBUTION + + UNPACKING IT + +To build the DHCP Distribution, unpack the compressed tar file using +the tar utility and the gzip command - type something like: + + zcat dhcp-3.0.2rc3.tar.gz |tar xvf - + +On BSD/OS, you have to type gzcat, not zcat, and you may run into +similar problems on other operating systems. + + CONFIGURING IT + +Now, cd to the dhcp-3.0.2rc3 subdirectory that you've just created and +configure the source tree by typing: + + ./configure + +If the configure utility can figure out what sort of system you're +running on, it will create a custom Makefile for you for that +system; otherwise, it will complain. If it can't figure out what +system you are using, that system is not supported - you are on +your own. + + DYNAMIC DNS UPDATES + +A fully-featured implementation of dynamic DNS updates is included in +this release. There are no build dependencies with any BIND version +- this version can and should just use the resolver in your C library. + +There is documentation for the DDNS support in the dhcpd.conf manual +page - see the beginning of this document for information on finding +manual pages. + + LOCALLY DEFINED OPTIONS + +In previous versions of the DHCP server there was a mechanism whereby +options that were not known by the server could be configured using +a name made up of the option code number and an identifier: +"option-nnn" This is no longer supported, because it is not future- +proof. Instead, if you want to use an option that the server doesn't +know about, you must explicitly define it using the method described +in the dhcp-options man page under the DEFINING NEW OPTIONS heading. + + BUILDING IT + +Once you've run configure, just type ``make'', and after a while +you should have a dhcp server. If you get compile errors on one +of the supported systems mentioned earlier, please let us know. +If you get warnings, it's not likely to be a problem - the DHCP +server compiles completely warning-free on as many architectures +as we can manage, but there are a few for which this is difficult. +If you get errors on a system not mentioned above, you will need +to do some programming or debugging on your own to get the DHCP +Distribution working. + + INSTALLING THE DHCP DISTRIBUTION + +Once you have successfully gotten the DHCP Distribution to build, you +can install it by typing ``make install''. If you already have an old +version of the DHCP Distribution installed, you may want to save it +before typing ``make install''. + + USING THE DHCP DISTRIBUTION + + FIREWALL RULES + +If you are running the DHCP server or client on a computer that's also +acting as a firewall, you must be sure to allow DHCP packets through +the firewall. In particular, your firewall rules _must_ allow packets +from IP address 0.0.0.0 to IP address 255.255.255.255 from UDP port 68 +to UDP port 67 through. They must also allow packets from your local +firewall's IP address and UDP port 67 through to any address your DHCP +server might serve on UDP port 68. Finally, packets from relay agents +on port 67 to the DHCP server on port 67, and vice versa, must be +permitted. + +We have noticed that on some systems where we are using a packet +filter, if you set up a firewall that blocks UDP port 67 and 68 +entirely, packets sent through the packet filter will not be blocked. +However, unicast packets will be blocked. This can result in strange +behaviour, particularly on DHCP clients, where the initial packet +exchange is broadcast, but renewals are unicast - the client will +appear to be unable to renew until it starts broadcasting its +renewals, and then suddenly it'll work. The fix is to fix the +firewall rules as described above. + + PARTIAL SERVERS + +If you have a server that is connected to two networks, and you only +want to provide DHCP service on one of those networks (e.g., you are +using a cable modem and have set up a NAT router), if you don't write +any subnet declaration for the network you aren't supporting, the DHCP +server will ignore input on that network interface if it can. If it +can't, it will refuse to run - some operating systems do not have the +capability of supporting DHCP on machines with more than one +interface, and ironically this is the case even if you don't want to +provide DHCP service on one of those interfaces. + + LINUX + +There are three big LINUX issues: the all-ones broadcast address, +Linux 2.1 ip_bootp_agent enabling, and operations with more than one +network interface. There are also two potential compilation/runtime +problems for Linux 2.1/2.2: the "SO_ATTACH_FILTER undeclared" problem +and the "protocol not configured" problem. + + LINUX: SO_ATTACH_FILTER UNDECLARED + +In addition, there is a minor issue that we will mention here because +this release is so close on the heels of the Linux 2.2 release: there +is a symlink in /usr/include that points at the linux asm headers. It +appears to be not uncommon that this link won't be updated correctly, +in which case you'll get the following error when you try to build: + + lpf.c: In function `if_register_receive': + lpf.c:152: `SO_ATTACH_FILTER' undeclared (first use this function) + lpf.c:152: (Each undeclared identifier is reported only once + lpf.c:152: for each function it appears in.) + +The line numbers may be different, of course. If you see this +header, your linux asm header link is probably bad, and you should +make sure it's pointing to correct linux source directory. + + LINUX: PROTOCOL NOT CONFIGURED + +One additional Linux 2.1/2.2 issue: if you get the following message, +it's because your kernel doesn't have the linux packetfilter or raw +packet socket configured: + + Make sure CONFIG_PACKET (Packet socket) and CONFIG_FILTER (Socket + Filtering) are enabled in your kernel configuration + +If this happens, you need to configure your Linux kernel to support +Socket Filtering and the Packet socket. You can do this by typing +``make config'', ``make menuconfig'' or ``make xconfig'', and then +enabling the Packet socket and Socket Filtering options that you'll +see displayed on the menu or in the questionnaire. You can also edit +your linux kernel .config file directly: set CONFIG_FILTER=y and +CONFIG_PACKET=y. If you do this, make sure you run ``make oldconfig'' +afterwards, so that the changes you've made are propogated to the +kernel header files. After you've reconfigured, you need to type +``make'' to build a new Linux kernel, and then install it in the +appropriate place (probably /linux). Make sure to save a copy of your +old /linux. + +If the preceding paragraph made no sense to you, ask your Linux +vendor/guru for help - please don't ask us. + +If you set CONFIG_PACKET=m or CONFIG_FILTER=m, then you must tell the +kernel module loader to load the appropriate modules. If this doesn't +make sense to you, don't use CONFIG_whatever=m - use CONFIG_whatever=y. +Don't ask for help with this on the DHCP mailing list - it's a Linux +kernel issue. This is probably not a problem with the most recent +Linux 2.2.x kernels. + + LINUX: BROADCAST + +If you are running a recent version of Linux, this won't be a problem, +but on older versions of Linux (kernel versions prior to 2.2), there +is a potential problem with the broadcast address being sent +incorrectly. + +In order for dhcpd to work correctly with picky DHCP clients (e.g., +Windows 95), it must be able to send packets with an IP destination +address of 255.255.255.255. Unfortunately, Linux changes an IP +destination of 255.255.255.255 into the local subnet broadcast address +(here, that's 192.5.5.223). + +This isn't generally a problem on Linux 2.2 and later kernels, since +we completely bypass the Linux IP stack, but on old versions of Linux +2.1 and all versions of Linux prior to 2.1, it is a problem - pickier +DHCP clients connected to the same network as the ISC DHCP server or +ISC relay agent will not see messages from the DHCP server. It *is* +possible to run into trouble with this on Linux 2.2 and later if you +are running a verson of the DHCP server that was compiled on a Linux +2.0 system, though. + +It is possible to work around this problem on some versions of Linux +by creating a host route from your network interface address to +255.255.255.255. The command you need to use to do this on Linux +varies from version to version. The easiest version is: + + route add -host 255.255.255.255 dev eth0 + +On some older Linux systems, you will get an error if you try to do +this. On those systems, try adding the following entry to your +/etc/hosts file: + +255.255.255.255 all-ones + +Then, try: + + route add -host all-ones dev eth0 + +Another route that has worked for some users is: + + route add -net 255.255.255.0 dev eth0 + +If you are not using eth0 as your network interface, you should +specify the network interface you *are* using in your route command. + + LINUX: IP BOOTP AGENT + +Some versions of the Linux 2.1 kernel apparently prevent dhcpd from +working unless you enable it by doing the following: + + echo 1 >/proc/sys/net/ipv4/ip_bootp_agent + + + LINUX: MULTIPLE INTERFACES + +Very old versions of the Linux kernel do not provide a networking API +that allows dhcpd to operate correctly if the system has more than one +broadcast network interface. However, Linux 2.0 kernels with version +numbers greater than or equal to 2.0.31 add an API feature: the +SO_BINDTODEVICE socket option. If SO_BINDTODEVICE is present, it is +possible for dhcpd to operate on Linux with more than one network +interface. In order to take advantage of this, you must be running a +2.0.31 or greater kernel, and you must have 2.0.31 or later system +headers installed *before* you build the DHCP Distribution. + +We have heard reports that you must still add routes to 255.255.255.255 +in order for the all-ones broadcast to work, even on 2.0.31 kernels. +In fact, you now need to add a route for each interface. Hopefully +the Linux kernel gurus will get this straight eventually. + +Linux 2.1 and later kernels do not use SO_BINDTODEVICE or require the +broadcast address hack, but do support multiple interfaces, using the +Linux Packet Filter. + + SCO + +SCO has the same problem as Linux (described earlier). The thing is, +SCO *really* doesn't want to let you add a host route to the all-ones +broadcast address. + +On more recent versions of SCO, you can do this: + + ifconfig net0 xxx.xxx.xxx.xxx netmask 0xNNNNNNNN broadcast 255.255.255.255 + +If this doesn't work, you can also try the following strange hack: + + ifconfig net0 alias 10.1.1.1 netmask 8.0.0.0 + +Apparently this works because of an interaction between SCO's support +for network classes and the weird netmask. The 10.* network is just a +dummy that can generally be assumed to be safe. Don't ask why this +works. Just try it. If it works for you, great. SCO has added +support for doing DHCP in a more sensible way, but I have not had the +time or cause to implement them. If you are interested in this, and +are able to hack your way out of a wet paper back without assistance, +we'd appreciate it if you'd give it a try, but don't expect too much +support from us (sorry!). + + HP-UX + +HP-UX has the same problem with the all-ones broadcast address that +SCO and Linux have. One user reported that adding the following to +/etc/rc.config.d/netconf helped (you may have to modify this to suit +your local configuration): + +INTERFACE_NAME[0]=lan0 +IP_ADDRESS[0]=1.1.1.1 +SUBNET_MASK[0]=255.255.255.0 +BROADCAST_ADDRESS[0]="255.255.255.255" +LANCONFIG_ARGS[0]="ether" +DHCP_ENABLE[0]=0 + + ULTRIX + +Now that we have Ultrix packet filter support, the DHCP Distribution +on Ultrix should be pretty trouble-free. However, one thing you do +need to be aware of is that it now requires that the pfilt device be +configured into your kernel and present in /dev. If you type ``man +packetfilter'', you will get some information on how to configure your +kernel for the packet filter (if it isn't already) and how to make an +entry for it in /dev. + + FreeBSD + +Versions of FreeBSD prior to 2.2 have a bug in BPF support in that the +ethernet driver swaps the ethertype field in the ethernet header +downstream from BPF, which corrupts the output packet. If you are +running a version of FreeBSD prior to 2.2, and you find that dhcpd +can't communicate with its clients, you should #define BROKEN_FREEBSD_BPF +in site.h and recompile. + +Modern versions of FreeBSD include the ISC DHCP 3.0 client as part of +the base system, and the full distribution (for the DHCP server and +relay agent) is available from the Ports Collection in +/usr/ports/net/isc-dhcp3, or as a package on FreeBSD installation +CDROMs. + + NeXTSTEP + +The NeXTSTEP support uses the NeXTSTEP Berkeley Packet Filter +extension, which is not included in the base NextStep system. You +must install this extension in order to get dhcpd or dhclient to work. + + SOLARIS + +One problem which has been observed and is not fixed in this +patchlevel has to do with using DLPI on Solaris machines. The symptom +of this problem is that the DHCP server never receives any requests. +This has been observed with Solaris 2.6 and Solaris 7 on Intel x86 +systems, although it may occur with other systems as well. If you +encounter this symptom, and you are running the DHCP server on a +machine with a single broadcast network interface, you may wish to +edit the includes/site.h file and uncomment the #define USE_SOCKETS +line. Then type ``make clean; make''. + +The DHCP client on Solaris will only work with DLPI. If you run it +and it just keeps saying it's sending DHCPREQUEST packets, but never +gets a response, you may be having DLPI trouble as described above. +If so, we have no solution to offer at this time. Also, because +Solaris requires you to "plumb" an interface before it can be detected +by the DHCP client, you must either specify the name(s) of the +interface(s) you want to configure on the command line, or must plumb +the interfaces prior to invoking the DHCP client. This can be done +with ``ifconfig iface plumb'', where iface is the name of the +interface (e.g., ``ifconfig hme0 plumb''). + +It should be noted that Solaris versions from 2.6 onward include a +DHCP client that you can run with ``/sbin/ifconfig iface dhcp start'' +rather than using the ISC DHCP client. The feature set of the Solaris +client is different (not necessarily better or worse) than that of the +ISC client, but in most cases it will be a lot easier for you to just +use that. Please do not ask for help in using the Solaris DHCP client +on Internet Systems Consortium mailing lists - that's why you're +paying Sun the big bucks. If you're having a problem with the +Solaris client interoperating with the ISC dhcp server, that's another +matter, but please check with Sun first. + + AIX + +The AIX support uses the BSD socket API, which cannot differentiate on +which network interface a broadcast packet was received; thus the DHCP +server and relay will work only on a single interface. (They do work +on multi-interface machines if configured to listen on only one of the +interfaces.) + +The ISC DHCP distribution does not include a dhclient-script for AIX-- +AIX comes with a DHCP client. Contribution of a working dhclient-script +for AIX would be welcome. + + SUPPORT + +The Internet Systems Consortium DHCP server is not a commercial +product, and is not supported by the ISC. However, it has attracted a +fairly sizable following on the Internet, which means that there are a +lot of knowledgable users who may be able to help you if you get +stuck. These people generally read the dhcp-server@isc.org mailing +list. + +If you are going to use dhcpd, you should probably subscribe to the +dhcp-server and dhcp-announce mailing lists. If you will be using +dhclient, you should subscribe to the dhcp-client mailing list. + +If you need help, you should ask on the dhcp-server or dhcp-client +mailing list - whichever is appropriate to your application. Support +requests for the ISC DHCP client should go to dhcp-client@isc.org. +Support requests for the DHCP server should go to dhcp-server@isc.org. +If you are having trouble with a combination of the client and server, +send the request to dhcp-server@isc.org. Please do not cross-post to +both lists under any circumstances. + +WHERE TO REPORT BUGS: If you want the act of sending in a bug report +to result in you getting help in the form of a fixed piece of +software, you are asking for help. Your bug report is helpful to us, +but fundamentally you are making a support request, so please use the +addresses described in the previous paragraphs. If you are _sure_ that +your problem is a bug, and not user error, or if your bug report +includes a patch, you can send it to dhcp-bugs@isc.org without +subscribing. This mailing list goes into a bug tracking system, so +you don't need to check periodically to see if we still remember the +bug - if you haven't been notified that the bug has been closed, we +still consider it a bug, and still have it in the system. + +PLEASE DO NOT REPORT BUGS IN OLD SOFTWARE RELEASES! Fetch the latest +release and see if the bug is still in that version of the software, +and if it's not, _then_ report it. It's okay to report bugs in the +latest patchlevel of a major version that's not the most recent major +version, though - for example, if you're running 3.0.1, you don't have +to upgrade to a 3.0.2rc (release candidate) before you can report bugs. + +PLEASE DO NOT REPORT BUGS IF YOU ARE RUNNING A VERSION OF THE ISC +DHCP DISTRIBUTION THAT YOU DIDN'T GET FROM THE ISC! Free operating +system distributions are notorious for including outdated versions of +software, and also versions of software that were not compiled on your +particular version of the operating system. These versions +frequently do not work. Getting a source distribution from the ISC +and installing it frequently *does* work. Please try this *before* +asking for help. + +PLEASE READ THIS README FILE CAREFULLY BEFORE REPORTING BUGS, +PARTICULARLY THE SECTION BELOW ON WHAT TO INCLUDE IN A BUG REPORT OR +HELP REQUEST. + +PLEASE DO NOT SEND REQUESTS FOR SUPPORT DIRECTLY TO THE ENGINEERS WHO +WORK ON THE ISC DHCP DISTRIBUTION! *PARTICULARLY*, DO NOT SEND MAIL +TO THE ENGINEERS BECAUSE YOU AREN'T SURE TO WHOM YOU SHOULD SEND MAIL +- if you aren't sure, *ask* on the dhcp-server@isc.org or +dhcp-client@isc.org mailing list. + +The number of people using the DHCP Distribution is sufficiently large +that if we take interrupts every time any one of those people runs +into trouble, we will never get any more coding done. If you send a +support request directly to any ISC or Nominum engineer, we will +forward it to the mailing list, or possibly ignore it, depending on +how much stress we are under at the time. + +Please do not Cc: us on mail you send to these lists - we read both +mailing lists, so this just means we get two copies! + +If your question can only be answered by one of the engineers, send it +to the appropriate public mailing list anyway - we will answer it +there. When we have time. + +Please do not think "Oh, I don't want to bother the whole mailing list +with this question." If you are too embarrassed to ask publically, +get a support contract. + +If you are concerned about bothering everybody on the list, that's +great, but that's what the list is there for. When you send mail to +one of the engineers, you are taking resources away from everybody on +the mailing list *anyway* - they just don't know it. + +We're not writing this because we don't respect you - we really do +want to help you, and we appreciate your bug reports and comments. +But please use the mechanisms we have in place to provide you with +help, because otherwise you are almost certainly depriving someone +else of our help. + +PLEASE DO NOT CALL US ON THE PHONE FOR HELP! Answering the phone +takes a lot more of our time and attention than answering email. If +you do call us on the phone, we will tell you to send email to the +mailing list or buy a support contract, so please don't waste your +time or ours. If you have a support contract, please use the support +channel mentioned in the support contract - otherwise you probably +won't get timely support unless you happen to ask an interesting +question and we happen to have some time to kill, because we can't +tell you're a support customer if you send mail to the public mailing +lists. + + HOW TO REPORT BUGS OR REQUEST HELP + +When you report bugs or ask for help, please provide us complete +information. A list of information we need follows. Please read it +carefully, and put all the information you can into your initial bug +report, so that we don't have to ask you any questions in order to +figure out your problem. If you need handholding support, please +consider contacting a commercial provider of the ISC DHCP +Distribution. + + 1. The specific operating system name and version of the + machine on which the DHCP server or client is running. + 2. The specific operating system name and version of the + machine on which the client is running, if you are having + trouble getting a client working with the server. + 3. If you're running Linux, the version number we care about is + the kernel version and maybe the library version, not the + distribution version - e.g., while we don't mind knowing + that you're running Redhat version mumble.foo, we must know + what kernel version you're running, and it helps if you can + tell us what version of the C library you're running, + although if you don't know that off the top of your head it + may be hard for you to figure it out, so don't go crazy + trying. + 4. The specific version of the DHCP distribution you're + running, for example "2.0b1pl19", not "2.0". + 5. Please explain the problem carefully, thinking through what + you're saying to ensure that you don't assume we know + something about your situation that we don't know. + 6. Include your dhcpd.conf and dhcpd.leases file if they're not + huge (if they are huge, we may need them anyway, but don't + send them until you're asked). Huge means more than 100 + kilobytes each. + 7. Include a log of your server or client running until it + encounters the problem - for example, if you are having + trouble getting some client to get an address, restart the + server with the -d flag and then restart the client, and + send us what the server prints. Likewise, with the client, + include the output of the client as it fails to get an + address or otherwise does the wrong thing. Do not leave + out parts of the output that you think aren't interesting. + 8. If the client or server is dumping core, please run the + debugger and get a stack trace, and include that in your + bug report. For example, if your debugger is gdb, do the + following: + + gdb dhcpd dhcpd.core + (gdb) where + [...] + (gdb) quit + + This assumes that it's the dhcp server you're debugging, and + that the core file is in dhcpd.core. + 9. If you know that the problem is an actual bug, and you can + reproduce the bug, you can skip steps 6 through 8 and instead + capture a trace file using the -tf flag (see the man page for + details). If you do this, and there is anything in your + dhcp configuration that you are not willing to make public, + please send the trace file to dhcp-bugs@isc.org and NOT to + dhcp-server@isc.org, because the tracefile contains your entire + dhcp configuration. + +PLEASE DO NOT send queries about non-isc clients to the dhcp-client +mailing list. If you're asking about them on an ISC mailing list, +it's probably because you're using the ISC DHCP server, so ask there. +If you are having problems with a client whose executable is called +dhcpcd, this is _not_ the ISC DHCP client, and we probably can't help +you with it. + +Please see http://www.isc.org/sw/dhcp/ for details on how to subscribe +to the ISC DHCP mailing lists. + + diff --git a/contrib/dhcp-3.0/README.DELETED b/contrib/dhcp-3.0/README.DELETED new file mode 100644 index 0000000000..786887d4dd --- /dev/null +++ b/contrib/dhcp-3.0/README.DELETED @@ -0,0 +1,41 @@ +Makefile +Makefile.conf +Makefile.dist +client/Makefile.dist +client/scripts/bsdos +client/scripts/linux +client/scripts/netbsd +client/scripts/nextstep +client/scripts/openbsd +client/scripts/solaris +common/Makefile.dist +configure +contrib +dhcpctl/Makefile.dist +dhcpctl/cltest.c +doc +dst/Makefile.dist +includes/cf/aix.h +includes/cf/alphaosf.h +includes/cf/bsdos.h +includes/cf/cygwin32.h +includes/cf/hpux.h +includes/cf/irix.h +includes/cf/linux.h +includes/cf/netbsd.h +includes/cf/nextstep.h +includes/cf/openbsd.h +includes/cf/qnx.h +includes/cf/rhapsody.h +includes/cf/sample.h +includes/cf/sco.h +includes/cf/sunos4.h +includes/cf/sunos5-5.h +includes/cf/ultrix.h +minires/Makefile.dist +omapip/Makefile.dist +omapip/test.c +relay/Makefile.dist +server/Makefile.dist +site.conf +tests diff --git a/contrib/dhcp-3.0/README.DRAGONFLY b/contrib/dhcp-3.0/README.DRAGONFLY new file mode 100644 index 0000000000..f02023b40e --- /dev/null +++ b/contrib/dhcp-3.0/README.DRAGONFLY @@ -0,0 +1,5 @@ +Original Source can be downloaded from: +ftp://ftp.isc.org/isc/dhcp/dhcp-3.0.2rc3.tar.gz +MD5 (dhcp-3.0.2rc3.tar.gz) = 7c5dd4587d0236275ddf026750513131 + +A list of files and directories removed is in README.DELETED. diff --git a/contrib/dhcp-3.0/RELNOTES b/contrib/dhcp-3.0/RELNOTES new file mode 100644 index 0000000000..220010218c --- /dev/null +++ b/contrib/dhcp-3.0/RELNOTES @@ -0,0 +1,1441 @@ + Internet Systems Consortium DHCP Distribution + Version 3.0.2rc3 + December 3, 2004 + + Release Notes + + NEW FEATURES + +Version 3 of the ISC DHCP Distribution includes the following features +that are new since version 2.0: + + - DHCP Failover Protocol support + - OMAPI, an API for accessing and modifying the DHCP server and + client state. + - Conditional behaviour + - Storing arbitrary information on leases + - Address pools with access control + - Client classing + - Address allocation restriction by class + - Relay agent information option support + - Dynamic DNS updates + - Many bug fixes, performance enhancements, and minor new DHCP + protocol features. + +The main bug fixed here is a bug in the subclass allocation code that +could result in a memory smash. Any users of the ISC DHCP server who +are using subclasses should seriously consider upgrading to 3.0.1. + +If you are running 3.0 beta 1 and are doing dynamic DNS updates, the +lease file is no longer forward-compatible to 3.0 final. A script +has been provided to convert 3.0b1 lease files. This is in +contrib/3.0b1-lease-convert. + +For information on how to install, configure and run this software, +as well as how to find documentation and report bugs, please consult +the README file. + +The Dynamic DNS Update support is a descendent of an implementation +done by Lans Carstensen and Brian Dols at Rose-Hulman Institute of +Technology, Jim Watt at Applied Biosystems, Irina Goble at Integrated +Measurement Systems, Igor Sharfmesser at Kazakh Telecom, and Brian +Murrell at BC Tel Advanced Communications. I'd like to express my +thanks to all of these good people here, both for working on the code +and for prodding me into improving it. + + Changes since 3.0.2rc2 + +- Two varaibles introduced in 3.0.2b1 were used without being initialized + in the case where neither the FILE nor SNAME fields were available for + overloading. This was repaired. + +- A heretofore believed to be impossible corner case of the option + overloading implementation turned out to be possible ("Unable to sort + overloaded options after 10 tries."). The implementation was reworked + to consider the case of an option so large it would require more than + three chunks to fit. + +- Many other instances of variables being used without being initialized + were repaired. + +- An uninitialized variable in omapi_io_destroy() led to the discovery + that this function may result in orphaned pointers (and hence, a memory + leak). + + Changes since 3.0.2rc1 + +- allocate_lease() was rewritten to repair a bug in which the server would + try to allocate an ABANDONED lease when FREE leases were available. + + Changes since 3.0.2b1 + +- Some dhcp-eval.5 manpage formatting was repaired. + + Changes since 3.0.1 + +- A bug was fixed in the server's 'option overloading' implementation, + where options loaded into the 'file' and 'sname' packet fields were + not aligned precisely as rfc2131 dictates. + +- The FreeBSD client script was changed to support the case where a domain + name was not provided by the server. + +- A memory leak in 'omshell' per each command line parsed was + repaired, thanks to a patch from Jarkko Torppa. + +- Log functions writing to stderr were adjusted to use the STDERR_FILENO + system definition rather than '2'. This is a no-op for 90% of platforms. + +- One call to trace_write_packet_iov() counted the number of io vectors + incorrectly, causing inconsistent tracefiles. This was fixed. + +- Some expression parse failure memory leaks were closed. + +- A host byte order problem in tracefiles was repaired. + +- Pools configured in DHCPD for failover possessing permission lists that + previously were assumed to not include dyanmic bootp clients are now + a little more pessimistic. The result is, dhcpd will nag you about just + about most pools that possess a 'allow' statement with no 'deny' that + would definitely match a dynamic bootp client. + +- The 'ddns-update-style' configuration warning bit now insists that + the configuration be globally scoped. + +- Two memory leaks in dhclient were closed thanks to a patch from Felix + Farkas. + +- Some minor but excellently pedantic documentation errors were fixed + thanks to a patch from Thomas Klausner. + +- Bugs in operator precedence in executable statements have been repaired + once again. More legal syntaxes should be parsed legally. + +- Failing to initialize a tracefile for any reason if a tracefile was + specified is now a fatal error. Thanks to a patch from Albert Herranz. + +- Corrected a bug in which the number of leases transferred as calculated + by the failover primary and sent to peers in POOLRESP responses may be + incorrect. This value is not believed to be used by other failover + implementations, excepting perhaps as logged information. + +- Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact + sending POOLREQ messages instead of POOLRESP mesasges. This message + was essentially ignored since failover secondaries effectively do not + respond to POOLREQ messages. + +- Type definitions for various bitwidths of integers in the sunos5-5 + build of ISC DHCP have been fixed. It should compile and run more + easily when built in 64-bit for this platform. + +- "allow known-clients;" is now a legal syntax, to avoid confusion. + +- If one dhcp server chooses to 'load balance' a request to its failover + peer, it first checks to see if it believes said peer has a free + lease to allocate before ignoring the DISCOVER. + +- log() was logging a work buffer, rather than the value returned by + executing the statements configured by the user. In some cases, + the work buffer and the intended results were the same. In some other + cases, they were not. This was fixed thanks to a patch from Gunnar + Fjone and directconnect.no. + +- Compiler warnings for some string type conversions was fixed, thanks + to Andreas Gustafsson. + +- The netbsd build environments were simplified to one, in which + -Wconversion is not used, thanks to Andreas Gustafsson. + +- How randomness in the backoff-cutoff dhclient configuration variable + is implemented was better documented in the manpage, and the behaviour + of dhclient in REQUEST timeout handling was changed to match that of + DISCOVER timeout handling. + +- Omapi was hardened against clients that pass in null values, thanks + to a patch from Mark Jason Dominus. + +- A bug was fixed in dhclient that kept it from doing client-side + ddns updates. Thanks to a patch from Andreas Gustafsson, which + underwent some modification after review by Jason Vas Dias. + +- Failover implementations disconnected due to the network between + them (rather than one of the two shutting down) will now try to + re-establish the failover connection every 5 seconds, rather than + to simply try once and give up until one of them is restarted. + Thanks to a patch from Ulf Ekberg from Infoblox, and field testing + by Greger V. Teigre which led to an enhancement to it. + +- A problem that kept DHCP Failover secondaries from tearing down + ddns records was repaired. Thanks to a patch from Ulf Ekberg from + Infoblox. + +- 64bit pointer sizes are detected properly on FreeBSD now. + +- A bug was repaired where the DHCP server would leave stale references + to host records on leases it once thought about offering to certain + clients. The result would be to apply host and 'known' scopes to the + wrong clients (possibly denying booting). NOTE: The 'mis-host' patch + that was being circulated as a workaround is not the way this bug was + fixed. If you were a victim of this bug in 3.0.1, you are cautioned + to proceed carefully and see if it fixes your problem. + +- A bug was repaired in the server's DHCPINFORM handling, where it + tried to divine the client's address from the source packet and + would get it wrong. Thanks to Anshuman Singh Rawat. + +- A log message was introduced to help illuminate the case where the + server was unable to find a lease to assign to any BOOTP client. + Thanks to Daniel Baker. + +- A minor dhcpd.conf.5 manpage error was fixed. + + Changes since 3.0.1rc14 + +- The global variable 'cur_time' was centralized and is now uniformly of a + type #defined in system-dependent headers. It had previously been defined + in one of many places as a 32-bit value, and this causes mayhem on 64-bit + big endian systems. It probably wasn't too healthy on little endian + systems either. + +- A printf format string error introduced in rc14 was repaired. + +- AIX system-dependent header file was altered to only define NO_SNPRINTF + if the condition used to #ifdef in vsnprintf in AIX' header files + is false. + +- The Alpha/OSF system-dependent header file was altered to define + NO_SNPRINTF on OS revisions older than 4.0G. + +- omapip/test.c had string.h added to its includes. + + Changes since 3.0.1rc13 + +! CAN-2004-0460 - CERT VU#317350: Five stack overflow exploits were closed + in logging messages with excessively long hostnames provided by the + clients. It is highly probable that these could have been used by + attackers to gain arbitrary root access on systems using ISC DHCP 3.0.1 + release candidates 12 or 13. Special thanks to Gregory Duchemin for + both finding and solving the problem. + +! CAN-2004-0461 - CERT VU#654390: Once the above was closed, an opening + in log_*() functions was evidented, on some specific platforms where + vsnprintf() was not believed to be available and calls were wrapped to + sprintf() instead. Again, credit goes to Gregory Duchemin for finding + the problem. Calls to snprintf() are now linked to a distribution-local + snprintf implementation, only in those cases where the architecture is + not known to provide one (see includes/cf/[arch].h). If you experience + linking problems with snprintf/vsnprintf or 'isc_print_' functions, this + is where to look. This vulnerability did not exist in any previously + published version of ISC DHCP. + +- Compilation on hpux 11.11 was repaired. + +- 'The cross-compile bug fix' was backed out. + + Changes since 3.0.1rc12 + +- Fixed a bug in omapi lease lookup function, to form the hardware + address for the hash lookup correctly, thanks to a patch from + Richard Hirst. + +- Fixed a bug where dhcrelay was sending relayed responses back to the + broadcast address, but with the source's unicast mac address. Should + now conform to rfc2131 section 4.1. + +- Cross-compile bug fix; use $(AR) instead of ar. Thanks to Morten Brorup. + +- Fixed a crash bug in dhclient where dhcpd servers that do not provide + renewal times results in an FPE. As a side effect, dhclient can now + properly handle 0xFFFFFFFF (-1) expiry times supplied by servers. Thanks + to a patch from Burt Silverman. + +- The 'ping timeout' debugs from rc12 were removed to -DDEBUG only, + and reformatted to correct a compilation error on solaris platforms. + +- A patch was applied which fixes a case where leases read from the + leases database do not properly over-ride previously read leases. + +- dhcpctl.3 manpage was tweaked. + + Changes since 3.0.1rc11 + +- A patch from Steve Campbell was applied with minor modifications to + permit reverse dns PTR record updates with values containing spaces. + +- A patch from Florian Lohoff was applied with some modifications to + dhcrelay. It now discards packets whose hop count exceeds 10 by default, + and a command-line option (-c) can be used to set this threshold. + +- A failover bug relating to identifying peers by name length instead of + by name was fixed. + +- Delcaring failover configs within shared-network statements should no + longer result in error. + +- The -nw command line option to dhclient now works. + +- Thanks to a patch from Michael Richardson: + - Some problems with long option processing have been fixed. + - Some fixes to minires so that updates of KEY records will work. + +- contrib/ms2isc was updated by Shu-Min Chang of the Intel Corporation. + see contrib/ms2isc/readme.txt for revision notes. + +- Dhclient no longer uses shell commands to kill another instance of + itself, it sends the signal directly. Thanks to a patch from Martin + Blapp. + +- The FreeBSD dhclient-script was changed so that a failure to write to + /etc/resolv.conf does not prematurely end the script. This keeps dhclient + from looping infinitely when this is the case. Thanks to a patch from + Martin Blapp. + +- A patch from Bill Stephens was applied which resolves a problem with lease + expiry times in failover configurations. + +- A memory leak in configuration parsing was closed thanks to a patch from + Steve G. + +- The function which discovers interfaces will now skip non-broadcast or + point-to-point interfaces, thanks to a patch from David Brownlee. + +- Options not yet known by the dhcpd or dhclient have had their names + changed such that they do not contain # symbols, in case they should ever + appear in a lease file. An option that might have been named "#144" is + now "unknown-144". + +- Another patch from Bill Stephens which allows the ping-check timeout to + be configured as 'ping-timeout'. Defaults to 1. + + Changes since 3.0.1rc10 + +- Potential buffer overflows in minires repaired. + +- A change to the linux client script to use /bin/bash, since /bin/sh may + not be bash. + +- Some missing va_end cleanups thanks to a patch from Thomas Klausner. + +- A correction of boolean parsing syntax validation - some illegal syntaxes + that worked before are now detected and produce errs, some legal syntaxes + that errored before will now work properly. + +- Some search-and-replace errors that caused some options to change their + names was repaired. + +- Shu-min Chang of the Intel corporation has contributed a perl script and + module that converts the MS NT4 DHCP configuration to a ISC DHCP3 + configuration file. + +- Applied the remainder of the dhcpctl memory leak patch provided by Bill + Squier at ReefEdge, Inc. (groo@reefedge.com). + +- Missing non-optional failover peer configurations will now result in a soft + error rather than a null dereference. + + Changes since 3.0.1rc9 + +- A format string was corrected to fix compiler warnings. + +- A number of spelling corrections were made in the man pages. + +- The dhclient.conf.5 man page was changed to refer to do-forward-updates + rather than a configuration option that doesn't exist. + +- A FreeBSD-specific bug in the interface removal handling was fixed. + +- A Linux-specific Token Ring detection problem was fixed. + +- Hashes removed from as-yet-unknown agent options, having those options + appear in reality before we know about them will no longer produce + self-corrupting lease databases. + +- dhclient will use the proper port numbers now when using the -g option. + +- A order-of-operations bug with 2 match clauses in 1 class statement is + fixed thanks to a patch from Andrew Matheson. + +- Compilation problems on Solaris were fixed. + +- Compilation problems when built with DEBUG or DEBUG_PACKET were repaired. + +- A fix to the dhcp ack process which makes certain group options will be + included in the first DHCPOFFER message was made thanks to a patch from + Ling Gou. + +- A few memory leaks were repaired thanks to patches from Bill Squier at + ReefEdge, Inc. (groo@reefedge.com). + +- A fix for shared-networks that sometimes give clients options for the + wrong subnets (in particular, 'option routers') was applied, thanks to + Ted Lemon for the patch. + +- Omshell's handling of dotted octets as values was changed such that dots + one after the other produce zero values in the integer string. + + Changes since 3.0.1rc8 + +- Fix a format string vulnerability in the server that could lead to a + remote root compromise (discovered by NGSEC Research Team, www.ngsec.com). + +- Add additional support for NetBSD/sparc64. + +- Fix a bug in the command-line parsing of the client. Also, resolve + a memory leak. + +- Add better support for shells other than bash in the Linux client + script. + +- Various build fixes for modern versions of FreeBSD and Linux. + +- Fix a bad bounds check when printing binding state names. + +- Clarify documentation about fixed-address and multiple addresses. + +- Fix a typo in the authoritative error message. + +- Make a log entry when we can't write a billing class. + +- Use conversion targets that are the right size on all architectures. + +- Increment the hop count when relaying. + +- Log a message when lease state is changed through OMAPI. + +- Don't rerun the shared_network when evaluating the pool. + +- Fix a reversed test in the parser. + +- Change the type of rbuf_max. + +- Make FTS_LAST a manifest constant to quiet warnings. + + Changes since 3.0.1rc7 + +- Fix two compiler warnings that are generated when compiling on Solaris + with gcc. These stop the build, even though they weren't actually + errors, because we prefer that our builds generate no warnings. + + Changes since 3.0.1rc6 + +- Don't allow a lease that's in the EXPIRED, RELEASED or RESET state + to be renewed. + +- Implement lease stealing for cases where the primary has fewer leases + than the secondary, as called for by the standard. + +- Add a fudge factor to the lease expiry acceptance code, (suggested + by Kevin Miller of CMU). + +- Fix a bug in permit_list_match that made it much too willing to say + that two permit lists matched. + +- Unless DEBUG_DNS_UPDATES is defined, print more user-friendly (and + also more compact) messages about DNS updates. + +- Fix a bug in generating wire-format domain names for the FQDN option. + +- Fix a bug where the FQDN option would not be returned if the client + requested it, contrary to the standard. + +- On Darwin, use the FreeBSD DHCP client script. + +- On NetBSD/sparc, don't check for casting warnings. + +- Add a flag in the DHCP client to disable updating the client's A + record when sending an FQDN option indicating that the client is + going to update its A record. + +- In the client, don't attempt a DNS update until one second after + configuring the new IP address, and if the update times out, keep + trying until a response, positive or negative, is received from the + DNS server. + +- Fix an uninitialized memory bug in the DHCP client. + +- Apply some FreeBSD-specific bug fixes suggested by Murray Stokely. + +- Fix a bug in ns_parserr(), where it was returning the wrong sort + of result code in some cases (suggested by Ben Harris of the + NetBSD project). + +- Fix a bug in is_identifier(), where it was checking against EOF + instead of the END_OF_FILE token (also suggested by Ben Harris). + +- Fix a bug where if an option universe contained no options, the + DHCP server could dump core (Walter Steiner). + +- Fix a bug in the handling of encapsulated options. + +- Fix a bug that prevented NWIP suboptions from being processed. + +- Delete the FTS_BOOTP and FTS_RESERVED states and implement them + as modifier flags to the FTS_ACTIVE state, as called for in the + failover protocol standard. + +- Fix bugs in the pool merging code that resulted in references and + dereferences of null pointers. This bug had no impact unless the + POINTER_DEBUG flag was defined. + +- In the server, added a do-forward-updates flag that can be used to + disable forward updates in all cases, so that sites that want the + clients to take sole responsibility for updating their A record can + do so. + +- Make it possible to disable optimization of PTR record updates. + + Changes since 3.0.1rc5 + +- Include some new documentation and changes provided by Karl Auer. + +- Add a workaround for some Lexmark printers that send a double-NUL- + terminated host-name option, which would break DNS updates. + +- Fix an off-by-one error in the MAC-address checking code for + DHCPRELEASE that was added in 3.0.1rc5. + +- Fix a bug where client-specific information was not being discarded + from the lease when it expired or was released, resulting in + problems if the lease was reallocated to a different client. + +- If more than one allocation pool is specified that has the same set + of constraints as another allocation pool on the same shared + network, merge the two pools. + +- Don't print an error in fallback_discard, since this just causes + confusion and does not appear to be helping to encourage anyone to + fix this bug. + + Changes since 3.0.1rc4 + +- Fix a bug that would cause the DHCP server to spin if asked to parse + a certain kind of incorrect statement. + +- Fix a related bug that would prevent an error from being reported in + the same case. + +- Additional documentation. + +- Make sure that the hardware address matches the lease when + processing a DHCPRELEASE message. + + Changes since 3.0.1rc3 + +- A minor bug fix in the arguments to a logging function call. +- Documentation update for dhcpd.conf. + + Changes since 3.0.1rc2 + +- Allow the primary to send a POOLREQ message. This isn't what the current + failover draft says to do, so we may have to back it out if I can't get the + authors to relent, but the scheme for balancing that's specified in the + current draft seems needlessly hairy, so I'm floating a trial balloon. + The rc1 code did not implement the method described in the draft either. + + Changes since 3.0.1rc1 + +- Treat NXDOMAIN and NXRRSET as success when we are trying to delete a + domain or RRSET. This allows the DHCP server to forget about a name + it added to the DNS once it's been removed, even if the DHCP server + wasn't the one that removed it. + +- Install defaults for failover maximum outstanding updates and maximum + silent time. This prevents problems that might occur if these values + were not configured. + +- Don't do DDNS deletes if ddns-update-style is none. + +- Return relay agent information options in DHCPNAK. This prevents DHCPNAK + messages from being dropped when the relay agent information option contains + routing information. + +- Fix a problem where coming up in recover wouldn't result in an update + request being sent. + +- Add some more chatty messages when we start a recovery update and when it's + done. + +- Fix a possible problem where some state might have been left around + after the peer lost contact and regained contact about how many updates + were pending. + +- Don't nix a lease update because of a lease conflict. This test has + never (as far as I know) prevented a mistake, and it appears to cause + problems with failover. + +- Add support in rc history code for keeping a selective history, rather + than a history of all references and dereferences. This code is only used + when extensive additional debugging is enabled. + + Changes since 3.0 + +- Make allocators for hash tables. As a side effect, this fixes a memory + smash in the subclass allocation code. + +- Fix a small bug in omshell where if you try to close an object when + no object is open, it dumps core. + +- Fix an obscure coredump that could occur on shutdown. + +- Fix a bug in the recording of host declaration rubouts in the lease file. + +- Fix two potential spins in the host deletion code. + +- Fix a core dump that would happen if an application tried to update + a host object attribute with a null value. + + Changes since 3.0 Release Candidate 12 + +- Fix a memory leak in the evaluation code. + +- Fix an obscure core dump. + +- Print a couple of new warnings when parsing the configuration file + when crucial information is left out. + +- Log "no free leases" as an error. + +- Documentation updates. + + Changes since 3.0 Release Candidate 11 + +- Always return a subnet selection option if one is sent. + +- Fix a warning that was being printed because an automatic data + structure wasn't zeroed. + +- Fix some failover state transitions that were being handled + incorrectly. + +- When supersede_lease is called on a lease whose end time has already + expired, but for which a state transition has not yet been done, do + a state transition. This fixes the case where if the secondary + allocated a lease to a client and the lease "expired" while the + secondary was in partner-down, no expiry event would actually + happen, so the lease would remain active until the primary was + restarted. + + Changes since 3.0 Release Candidate 10 + +- Fix a bug that was preventing released leases from changing state + in failover-enabled pools. + +- Fix a core dump in the client identifier finder code (for host + declarations). + +- Finish fixing a bug where bogus data would sometimes get logged to + the dhclient.leases file because it was opened as descriptor 2. + +- Fix the Linux dhclient-script according to suggestions made by + several people on the dhcp-client mailing list. + +- Log successful DNS updates at LOG_INFO, not LOG_ERROR. + +- Print an error message and refuse to run if a failover peer is + defined but not referenced by any pools. + +- Correct a confusing error message in failover. + + Changes since 3.0 Release Candidate 9 + +- Fix a bug in lease allocation for Dynamic BOOTP clients. + + Changes since 3.0 Release Candidate 8 Patchlevel 2 + +- Fix a bug that prevented update-static-leases from working. + +- Document failover-state OMAPI object. + +- Fix a compilation error on SunOS 4. + + Changes since 3.0 Release Candidate 8 Patchlevel 1 + +- Fix a parsing bug that broke dns updates (both interim and ad-hoc). + This was introduced in rc8pl1 as an unintended result of the memory + leakage fixes that were in pl1. + +- Fix a long-standing bug where the server would record that an update + had been done for a client with no name, even though no update had + been done, and then when the client's lease expired the deletion of + that nonexistant record would time out because the name was the null + string. + +- Clean up the omshell, dhcpctl and omapi man pages a bit. + + Changes since 3.0 Release Candidate 8 + +- Fix a bug that could cause the DHCP server to spin if + one-lease-per-client was enabled. + +- Fix a bug that was causing core dumps on BSD/os in the presence of + malformed packets. + +- In partner-down state, don't restrict lease lengths to MCLT. + +- On the failover secondary, record the MCLT received from the primary + so that if we come up without a connection to the primary we don't + wind up giving out zero-length leases. + +- Fix some compilation problems on BSD/os. + +- Fix a bunch of memory leaks. + +- Fix a couple of bugs in the option printer. + +- Fix an obscure error reporting bug in the dns update code, and also + make the message clearer when a key algorithm isn't supported. + +- Fix a bug in the tracing code that prevented trace runs that used + tcp connections from being played back. + +- Add some additional debugging capability for catching memory leaks + on exit. + +- Make the client release the lease correctly on shutdown. + +- Add some configurability to the build system. + +- Install omshell manual page in man1, not man8. + +- Craig Gwydir sent in a patch that fixes a long-standing bug in the + DHCP client that could cause core dumps, but that for some reason + hadn't been noticed until now. + + Changes since 3.0 Release Candidate 7 + +- Fix a bug in failover where we weren't sending updates after a + transition from communications-interrupted to normal. + +- Handle expired/released/reset -> free transition according to the + protocol specification (this works - the other way not only wasn't + conformant, but also didn't work). + +- Add a control object in both client and server that allows either + daemon to be shut down cleanly. + +- When writing a lease, if we run out of disk space, shut down the + output file and insist on writing a new one before proceeding. + +- In the server, if the OMAPI listener port is occupied, keep trying + to get it, rather than simply giving up and exiting. + +- Support fetching variables from leases and also updating and adding + variables to leases via OMAPI. + +- If two failover peers have wildly different clocks, refuse to start + doing failover. + +- Fix a bug in the DNS update code that could cause core dumps when + running on alpha processors. + +- Fixed a bug in ddns updates for static lease entries, thanks to a + patch from Andrey M Linkevitch. + +- Add support for Darwin/MacOS X + +- Install omshell (including new documentation). + +- Support DNS updates in the client (this is a very obscure feature + that most DHCP client users probably will not be able to use). + +- Somewhat cleaner status logging in the client. + +- Make OMAPI key naming syntax compatible with the way keys are + actually named (key names are domain names). + +- Fix a bug in the lease file writer. + +- Install DHCP ISC headers in a different place than BIND 9 ISC + headers, to avoid causing trouble in BIND 9 builds. + +- Don't send updates for attributes on an object when the attributes + haven't changed. Support deleting attributes on remote objects. + +- Fix a number of bugs in omshell, and add the unset and refresh + statements. + +- Handle disconnects in OMAPI a little bit more intelligently (so that + the caller gets ECONNRESET instead of EINVAL). + +- Fix a bunch of bugs in the handling of clients that have existing + leases when the try to renew their leases while failover is + operating. + + Changes since 3.0 Release Candidate 6 + +- Fix a core dump that could happen when processing a DHCPREQUEST from + a client that had a host declaration that contained both a + fixed-address declaration and a dhcp-client-identifier option + declaration, if the client identifier was longer than nine bytes. + +- Fix a memory leak that could happen in certain obscure cases when + using omapi to manipulate leases. + +- Fix some bugs and omissions in omshell. + + + Changes since 3.0 Release Candidate 5 + +- Fix a bug in omapi_object_dereference that prevented objects in + chains from having their reference counts decreased on dereference. + +- Fix a bug in omapi_object_dereference that would prevent object + chains from being freed upon removal of the last reference external + to the chain. + +- Fix a number of other memory leaks in the OMAPI protocol subsystem. + +- Add code in the OMAPI protocol handler to trace memory leakage. + +- Clean up the memory allocation/reference history printer. + +- Support input of dotted quads and colon-separated hex lists as + attribute values in omshell. + +- Fix a typo in the Linux interface discovery code. + +- Conditionalize a piece of trace code that wasn't conditional. + + Changes since 3.0 Release Candidate 4 + +- Fix a bug that would prevent leases from being abandoned properly on + DHCPDECLINE. + +- Fix failover peer OMAPI support. + +- In failover, correctly handle expiration of leases. Previously, + leases would never be reclaimed because they couldn't make the + transition from EXPIRED to FREE. + +- Fix some broken failover state transitions. + +- Documentation fixes. + +- Take out an unnecessary check in DHCP relay agent information option + stashing code that was preventing REBINDING clients from rebinding. + +- Prevent failover peers from allocating leases in DHCPREQUEST + processing if the lease belongs to the other server. + +- Record server version in lease file introductory comment. + +- Correctly report connection errors in OMAPI and failover. + +- Make authentication signature algorithm name comparisons in OMAPI + case-insensitive. + +- Fix compile problem on SunOS 4.x + +- If a signature algorithm is not terminated with '.', terminate it so + that comparisons between fully-qualified names will work + consistently. + +- Different SIOCGIFCONF probe code, may "fix" problem on some Linux + systems with the probe not working correctly. + +- Don't allow user to type omapi key on command line of omshell. + + Changes since 3.0 Release Candidate 3 + +- Do lease billing on startup in a way that I *think* will finally do + the billing correctly - the previous method could overbill as a + result of duplicate leases. + +- Document OMAPI server objects. + + Changes since 3.0 Release Candidate 2 Patchlevel 1 + +- Fix some problems in the DDNS update code. Thanks to Albert + Herranz for figuring out the main problem. + +- Fix some reference counting errors on host entries that were causing + core dumps. + +- Fix a byte-swap bug in the token ring code, thanks to Jochen + Friedrich. + +- Fix a bug in lease billing, thanks to Jonas Bulow. + + Changes since 3.0 Release Candidate 2 + +- Change the conditions under which a DHCPRELEASE is actually + committed to be consistent with lease binding states rather than + using the lease end time. This may fix some problems with the + billing class code. + +- Fix a bug where lease updates would fail on Digital Unix (and maybe + others) because malloc was called with a size of zero. + +- Fix a core dump that happens when the DHCP server can't create its + trace file. + + Changes since 3.0 Release Candidate 1 Patchlevel 1 + +- Fix the dhcp_failover_put_message to not attempt to allocate a + zero-length buffer. Some versions of malloc() fail if you try to + allocate a zero-length buffer, and this was causing problems on, + e.g., Digital Unix. + +- Fix a case where the failover code was printing an error message + when no error had occurred. + +- Fix a problem where when a server went down and back up again, the + peer would not see a state transition and so would stay in the + non-communicating state. + +- Be smart about going into recover_wait. + +- Fix a problem in the failover implementation where peers would fail + to come into sync if interrupted in the RECOVER state. This could + have been the cause of some problems people have reported recently. + +- Fix a problem with billing classes where they would not be unbilled + when the client lease expired. + +- If select fails, figure out which descriptor is bad, and cut it out + of the I/O loop. This prevents a potentially nasty spin. I + haven't heard any report it in a while, but it came up consistently + in testing. + +- Fix a bug in the relay agent where if you specified interfaces on + the command line, it would fail. + +- Fix a couple of small bugs in the omapi connection object (no known + user impact). + +- Add the missing 3.0 Beta 1 lease conversion script. + +- Read dhcp client script hooks if they exist, rather than only if + they're executable. + + Changes since 3.0 Release Candidate 1 + +- Fix a memory smash that happens when fixed-address leases are used. + ANY SITE AT WHICH FIXED-ADDRESS STATEMENTS ARE BEING USED SHOULD + UPGRADE IMMEDIATELY. This has been a long-standing bug - thanks to + Alvise Nobile for discovering it and helping me to find it! + +- Fix a small bug in binary-to-ascii, thanks to H. Peter Anvin of + Transmeta. + +- There is a known problem with the DHCP server doing failover on + Compaq Alpha systems. This patchlevel is not a release candidate + because of this bug. The bug should be straightforward to fix, so + a new release candidate is expected shortly. + +- There is a known problem in the DDNS update code that is probably a + bug, and is not, as far as we know, fixed in this patchlevel. + + Changes since 3.0 Beta 2 Patchlevel 24 + +- Went over problematic failover state transitions and made them all + work, so that failover should now much less fragile. + +- Add some dhcpctl and omapi documentation + +- Fix compile errors when compiling with unusual predefines. + +- Make Token Ring work on Linux 2.4 + +- Fix the Digital Unix BPF_WORDALIGN bug. + +- Fix some dhcp client documentation errors. + +- Update some parts of the README file. + +- Support GCC on SCO. + + Changes since 3.0 Beta 2 Patchlevel 23 + +- Fix a bug in the DNS update code where a status code was not being + checked. This may have been causing core dumps. + +- When parsing the lease file, if a lease declaration includes a + billing class statement, and the lease already has a billing class, + unbill the old class. + +- When processing failover transactions, where acks will be deferred, + process the state transition immediately. + +- Don't try to use the new SIOCGIFCONF buffer size detection code on + Linux 2.0, which doesn't provide this functionality. + +- Apply a patch suggested by Tuan Uong for a problem in dlpi.c. + +- Fix a problem in using the which command in the configure script. + +- Fix a parse error in the client when setting up an omapi listener. + +- Document the -n and -g flags to the client. + +- Make sure there is always a stdin and stdout on startup. This + prevents shell scripts from accidentally writing error messages into + configuration files that happen to be opened as stderr. + +- If an interface is removed, the client will now notice that it is + gone rather than spinning. This has only been tested on NetBSD. + +- The client will attempt to get an address even if it can't create a + lease file. + +- Don't overwrite tracefiles. + +- Fix some memory allocation bugs in failover. + + Changes since 3.0 Beta 2 Patchlevel 22 + +- Apply some patches suggested by Cyrille Lefevre, who is maintaining + the FreeBSD ISC DHCP Distribution port. + +- Fix a core dump in DHCPRELEASE. + + Changes since 3.0 Beta 2 Patchlevel 21 + +- This time for sure: fix the spin described in the changes for pl20. + + Changes since 3.0 Beta 2 Patchlevel 20 + +- Fix a problem with Linux detecting large numbers of interfaces (Ben) + +- Fix a memory smash in the quotify code, which was introduced in + pl19. + +- Actually fix the spin described in the changes for pl20. The + previous fix only partially fixed the problem - enough to get it + past the regression test. + + Changes since 3.0 Beta 2 Patchlevel 19 + +- Fix a bug that could cause the server to abort if compiled with + POINTER_DEBUG enabled. + +- Fix a bug that could cause the server to spin when responding to a + DHCPREQUEST. + +- Apply Joost Mulders' suggested patches for DLPI on x86. + +- Support NUL characters in quoted strings. + +- Install unformatted man pages on SunOS. + + Changes since 3.0 Beta 2 Patchlevel 18 + +- Allow the server to be placed in partner-down state using OMAPI. + (Damien Neil) + +- Implement omshell, which can be used to do arbitrary things to the + server (in theory). (Damien Neil) + +- Fix a case where if a client had two different leases the server could + actually dereference the second one when it hadn't been referenced, + leading to memory corruption and a core dump. (James Brister) + +- Fix a case where a client could request the address of another client's + lease, but find_lease wouldn't detect that the other client had it, and + would attempt to allocate it to the client, resulting in a lease conflict + message. + +- Fix a case where a client with more than one client identifier could be + given a lease where the hardware address was correct but the client + identifier was not, resulting in a lease conflict message. + +- Fix a problem where the server could write out a colon-separated + hex list as a value for a variable, which would then not parse. + The fix is to always write strings as quoted strings, with any + non-printable characters quoted as octal escape sequences. So + a file written the old way still won't work, but new files written + this way will work. + +- Fix documentation for sending non-standard options. + +- Use unparsable names for unknown options. WARNING: this will + break any configuration files that use the option-nnn convention. + If you want to continue to use this convention for some options, + please be sure to write a definition, like this: + + option option-nnn code nnn = string; + + You can use a descriptive name instead of option-nnn if you like. + +- Fix a problem where we would see a DHCPDISCOVER/DHCPOFFER/ + DHCPREQUEST/DHCPACK/DHCPREQUEST/DHCPNAK sequence. This was the + result of a deceptively silly bug in supersede_lease. + +- Fix client script exit status check, according to a fix supplied by + Hermann Lauer. + +- Fix an endianness bug in the tracefile support, regarding ICMP + messages. + +- Fix a bug in the client where the medium would not work correctly if + it contained quoted strings. + + ** there was no pl17 ** + + Changes since 3.0 Beta 2 Patchlevel 16 + +- Add support for transaction tracing. This allows the state of the + DHCP server on startup, and all the subsequent transactions, to be + recorded in a file which can then be played back to reproduce the + behaviour of the DHCP server. This can be used to quickly + reproduce bugs that cause core dumps or corruption, and also for + tracking down memory leaks. + +- Incorporate some bug fixes provided by Joost Mulders for the DLPI + package which should clear up problems people have been seeing on + Solaris. + +- Fix bugs in the handling of options stored as linked lists (agent + options, fqdn options and nwip options) that could cause memory + corruption and core dumps. + +- Fix a bug in DHCPREQUEST handling that resulted in DHCPNAK messages + not being send in some cases when they were needed. + +- Make the lease structure somewhat more compact. + +- Make initial failover startup *much* faster. This was researched + and implemented by Damien Neil. + +- Add a --version flag to all executables, which prints the program + name and version to standard output. + +- Don't rewrite the lease file every thousand leases. + +- A bug in nit.c for older SunOS machines was fixed by a patch sent in + by Takeshi Hagiwara. + +- Fix a memory corruption bug in the DHCP client. + +- Lots of documentation updates. + +- Add a feature allowing environment variables to be passed to the + DHCP client script on the DHCP client command line. + +- Fix client medium support, which had been broken for some time. + +- Fix a bug in the DHCP client initial startup backoff interval, which + would cause two DHCPDISCOVERS to be sent back-to-back on startup. + + + Changes since 3.0 Beta 2 Patchlevel 15 + +- Some documentation tweaks. + +- Maybe fix a problem in the DLPI code. + +- Fix some error code space inconsistencies in ddns update code. + +- Support relay agents that intercept unicast DHCP messages to stuff + agent options into them. + +- Fix a small memory leak in the relay agent option support code. + +- Fix a core dump that would occur if a packet was sent with no + options. + + Changes since 3.0 Beta 2 Patchlevel 14 + +- Finish fixing a long-standing bug in the agent options code. This + was causing core dumps and failing to operate correctly - in + particular, agent option stashing wasn't working. Agent option + stashing should now be working, meaning that agent options can be + used in class statements to control address allocation. + +- Fix up documentation. + +- Fix a couple of small memory leaks that would have added up + significantly in a high-demand situation. + +- Add a log-facility configuration parameter. + +- Fix a compile error on some older operating systems. + +- Add the ability in the client to execute certain statements before + transmitting packets to the server. Handy for debugging; not much + practical use otherwise. + +- Don't send faked-out giaddr when renewing or bound - again, useful + for debugging. + + Changes since 3.0 Beta 2 Patchlevel 13 + +- Fixed a problem where the fqdn decoder would sometimes try to store + an option with an (unsigned) negative length, resulting in a core + dump on some systems. + +- Work around the Win98 DHCP client, which NUL-terminates the FQDN + option. + +- Work around Win98 and Win2k clients that will claim they want to do + the update even when they don't have any way to do it. + +- Fix some log messages that can be printed when failover is operating + that were not printing enough information. + +- It was possible for a DHCPDISCOVER to get an allocation even when + the state machine said the server shouldn't be responding. + +- Don't load balance DHCPREQUESTs from clients in RENEWING and + REBINDING, since in RENEWING, if we heard it, it's for us, and in + REBINDING, the client wouldn't have got to REBINDING if its primary + were answering. + +- When we get a bogus state lease binding state transition, don't do + the transition. + + + Changes since 3.0 Beta 2 Patchlevel 12 + +- Fixed a couple of silly compile errors. + + Changes since 3.0 Beta 2 Patchlevel 11 + +- Albert Herranz tracked down and fixed a subtle bug in the base64 + decoder that would prevent any key with an 'x' in its base64 + representation from working correctly. + +- Thanks to Chris Cheney and Michael Sanders, we have a fix for the + hang that they both spotted in the DHCP server - when + one-lease-per-client was set, the code to release the "other" lease + could spin. + +- Fix a problem with alignment of the input buffer in bpf in cases + where two packets arrive in the same bpf read. + +- Fix a problem where the relay agent would crash if you specified an + interface name on the command line. + +- Add the ability to conditionalize client behaviour based on the + client state. + +- Add support for the FQDN option, and added support for a new way of + doing ddns updates (ddns update style interim) that allows more than + one DHCP server to update the DNS for the same network(s). This + was implemented by Damien Neil with some additional functionality + added by Ted Lemon. + +- Damien added a "log" statement, so that the configuration file can + be made to log debugging information and other information. + +- Fixed a bug that caused option buffers not to be terminated with an + end option. + +- Fixed a long-standing bug in the support for option spaces where the + options are stored as an ordered list rather than in a hash table, + which could theoretically result in memory pool corruption. + +- Prevent hardware declarations with no actual hardware address from + being written as something unparsable, and behave correctly in the + face of a null hardware address on input. + +- Allow key names to be FQDNs, and qualify the algorithm name if it is + specified unqualified. + +- Modify the DDNS update code so that it never prints the "resolver + failed" message, but instead says *why* the resolver failed. + +- Officially support the subnet selection option, which now has an + RFC. + +- Fix a build bug on MacOS X. + +- Allow administrator to disable ping checking. + +- Clean up dhcpd.conf documentation and add more information about how + it works. + + Changes since 3.0 Beta 2 Patchlevel 10 + +- Fix a bug introduced during debugging (!) and accidentally committed + to CVS. + + Changes since 3.0 Beta 2 Patchlevel 9 + +- Fix DHCP client handling of vendor encapsulated options. + +- Fix a bug in the handling of relay agent information options introduced + in patchlevel 9. + +- Stash agent options on client leases by default, and use the stashed + options at renewal time. + +- Add the ability to test the client's binding state in the client + configuration language. + +- Fix a core dump in the DNS update code. + +- Fix some expression evaluation bugs that were causing updates to be + done when no client hostname was received. + +- Fix expression evaluation debugging printfs. + +- Teach pretty_print_option to print options in option spaces other than + the DHCP option space. + +- Add a warning message if the RHS of a not is not boolean. + +- Never select for more than a day, because some implementations of + select will just fail if the timeout is too long (!). + +- Fix a case where a DHCPDISCOVER from an unknown network would be + silently dropped. + +- Fix a bug where if a client requested an IP address for which a different + client had the lease, the DHCP server would reallocate it anyway. + +- Fix the DNS update code so that if the client changes its name, the DNS + will be correctly updated. + + Changes since 3.0 Beta 2 Patchlevel 8 + +- Oops, there was another subtle math error in the header-length + bounds-checking. + + Changes since 3.0 Beta 2 Patchlevel 7 + +- Oops, forgot to byte-swap udp header length before bounds-checking it. + + Changes since 3.0 Beta 2 Patchlevel 6 + +- Fix a possible DoS attack where a client could cause the checksummer + to dump core. This was a read, not a write, so it shouldn't be + possible to exploit it any further than that. + +- Implement client- and server-side support for using the Client FQDN + option. + +- Support for other option spaces in the client has been added. This + means that it is now possible to define a vendor option space on the + client, request options in that space from the server (which must + define the same option space), and then use those options in the + client. This also allows NWIP and Client FQDN options to be used + meaningfully. + +- Add object initializer support. This means that objects can now be + initialized to something other than all-zeros when allocated, which + makes, e.g., the interface object support code a little more robust. + +- Fix an off-by-one bug in the host stuffer. This was causing host + deletes not the work, and may also have been causing OMAPI + connections to get dropped. Thanks to James Brister for tracking + this one down! + +- Fixed a core dump in the interface discovery code that is triggered + when there is no subnet declaration for an interface, but the server + decides to continue running. Thanks to Shane Kerr for tracking + down and fixing this problem. + + Changes since 3.0 Beta 2 Patchlevel 5 + +- Fix a bug in the recent enhancement to the interface discovery code + to support arbitrary-length interface lists. + +- Support NUL-terminated DHCP options when initializing client-script + environment. + +- Fix suffix operator. + +- Fix NetWare/IP option parsing. + +- Better error/status checking in dhcpctl initialization and omapi + connection code. + +- Fix a potential memory smash in dhcpctl code. + +- Fix SunOS4 and (maybe) Ultrix builds. + +- Fix a bug where a certain sort of incoming packet could cause a core + dump on Solaris (and probably elsewhere). + +- Add some more safety checks in error logging code. + +- Add support for ISC_R_INCOMPLETE in OMAPI protocol connection code. + +- Fix relay agent so that if an interface is specified on the command + line, the relay agent does not dump core. + +- Fix class matching so that match if can be combined with match or + spawn with. + +- Do not allow spurious leases in the lease database to introduce + potentially bogus leases into the in-memory database. + +- Fix a byte-order problem in the client hardware address type code + for OMAPI. + +- Be slightly less picky about what sort of hardware addresses OMAPI + can install in host declarations. + + Changes since 3.0 Beta 2 Patchlevel 4 + +- Incorporated Peter Marschall's proposed change to array/record + parsing, which allows things like the slp-agent option to be encoded + correctly. Thanks very much to Peter for taking the initiative to + do this, and for doing such a careful job of it (e.g., updating the + comments)! + +- Added an encoding for the slp-agent option. :') + +- Fixed SunOS 4 build. Thanks to Robert Elz for responding to my + request for help on this with patches! + +- Incorporated a change that should fix a problem reported by Philippe + Jumelle where when the network connection between two servers is + lost, they never reconnect. + +- Fix client script files other than that for NetBSD to actually use + make_resolv_conf as documented in the manual page. + +- Fix a bug in the packet handling code that could result in a core + dump. + +- Fix a bug in the bootp code where responses on the local net would + be sent to the wrong MAC address. Thanks to Jerry Schave for + catching this one. + + Changes since 3.0 Beta 2 Patchlevel 3 + +- In the DHCP client, execute client statements prior to using the values + of options, so that the client configuration can overried, e.g., the + lease renewal time. + +- Fix a reference counting error that would result in very reproducible + failures in updates, as well as occasional core dumps, if a zone was + declared without a key. + +- Fix some Linux 2.0 compilation problems. + +- Fix a bug in scope evaluation during execution of "on" statements that + caused values not to be recorded on leases. + +- If the dhcp-max-message-size option is specified in scope, and the + client didn't send this option, use the one specified in scope to + determine the maximum size of the response. + + Changes since 3.0 Beta 2 Patchlevel 2 + +- Fix a case where spawning subclasses were being allocated + incorrectly, resulting in a core dump. + +- Fix a case where the DHCP server might inappropriately NAK a + RENEWING client. + +- Fix a place dhcprequest() where static leases could leak. + +- Include memory.h in omapip_p.h so that we don't get warnings about + using memcmp(). + + Changes since 3.0 Beta 2 Patchlevel 1 + +- Notice when SIOCFIGCONF returns more data than fit in the buffer - + allocate a larger buffer, and retry. Thanks to Greg Fausak for + pointing this out. + +- In the server, if no interfaces were configured, report an error and + exit. + +- Don't ever record a state of 'startup'. + +- Don't try to evaluate the local failover binding address if none was + specified. Thanks to Joseph Breu for finding this. diff --git a/contrib/dhcp-3.0/client/clparse.c b/contrib/dhcp-3.0/client/clparse.c new file mode 100644 index 0000000000..e5079a1aaa --- /dev/null +++ b/contrib/dhcp-3.0/client/clparse.c @@ -0,0 +1,1170 @@ +/* clparse.c + + Parser for dhclient config and lease files... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: clparse.c,v 1.62.2.7 2004/11/24 17:39:14 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +static TIME parsed_time; + +struct client_config top_level_config; + +u_int32_t default_requested_options [] = { + DHO_SUBNET_MASK, + DHO_BROADCAST_ADDRESS, + DHO_TIME_OFFSET, + DHO_ROUTERS, + DHO_DOMAIN_NAME, + DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, + 0 +}; + +/* client-conf-file :== client-declarations END_OF_FILE + client-declarations :== + | client-declaration + | client-declarations client-declaration */ + +isc_result_t read_client_conf () +{ + struct client_config *config; + struct client_state *state; + struct interface_info *ip; + isc_result_t status; + + /* Set up the initial dhcp option universe. */ + initialize_common_option_spaces (); + + /* Initialize the top level client configuration. */ + memset (&top_level_config, 0, sizeof top_level_config); + + /* Set some defaults... */ + top_level_config.timeout = 60; + top_level_config.select_interval = 0; + top_level_config.reboot_timeout = 10; + top_level_config.retry_interval = 300; + top_level_config.backoff_cutoff = 15; + top_level_config.initial_interval = 3; + top_level_config.bootp_policy = P_ACCEPT; + top_level_config.script_name = path_dhclient_script; + top_level_config.requested_options = default_requested_options; + top_level_config.omapi_port = -1; + top_level_config.do_forward_update = 1; + + group_allocate (&top_level_config.on_receipt, MDL); + if (!top_level_config.on_receipt) + log_fatal ("no memory for top-level on_receipt group"); + + group_allocate (&top_level_config.on_transmission, MDL); + if (!top_level_config.on_transmission) + log_fatal ("no memory for top-level on_transmission group"); + + status = read_client_conf_file (path_dhclient_conf, + (struct interface_info *)0, + &top_level_config); + if (status != ISC_R_SUCCESS) { + ; +#ifdef LATER + /* Set up the standard name service updater routine. */ + parse = (struct parse *)0; + status = new_parse (&parse, -1, default_client_config, + (sizeof default_client_config) - 1, + "default client configuration", 0); + if (status != ISC_R_SUCCESS) + log_fatal ("can't begin default client config!"); + + do { + token = peek_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) + break; + parse_client_statement (cfile, + (struct interface_info *)0, + &top_level_config); + } while (1); + end_parse (&parse); +#endif + } + + /* Set up state and config structures for clients that don't + have per-interface configuration statements. */ + config = (struct client_config *)0; + for (ip = interfaces; ip; ip = ip -> next) { + if (!ip -> client) { + ip -> client = (struct client_state *) + dmalloc (sizeof (struct client_state), MDL); + if (!ip -> client) + log_fatal ("no memory for client state."); + memset (ip -> client, 0, sizeof *(ip -> client)); + ip -> client -> interface = ip; + } + + if (!ip -> client -> config) { + if (!config) { + config = (struct client_config *) + dmalloc (sizeof (struct client_config), + MDL); + if (!config) + log_fatal ("no memory for client config."); + memcpy (config, &top_level_config, + sizeof top_level_config); + } + ip -> client -> config = config; + } + } + return status; +} + +int read_client_conf_file (const char *name, struct interface_info *ip, + struct client_config *client) +{ + int file; + struct parse *cfile; + const char *val; + int token; + isc_result_t status; + + if ((file = open (name, O_RDONLY)) < 0) + return uerr2isc (errno); + + cfile = (struct parse *)0; + new_parse (&cfile, file, (char *)0, 0, path_dhclient_conf, 0); + + do { + token = peek_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) + break; + parse_client_statement (cfile, ip, client); + } while (1); + token = next_token (&val, (unsigned *)0, cfile); + status = (cfile -> warnings_occurred + ? ISC_R_BADPARSE + : ISC_R_SUCCESS); + close (file); + end_parse (&cfile); + return status; +} + + +/* lease-file :== client-lease-statements END_OF_FILE + client-lease-statements :== + | client-lease-statements LEASE client-lease-statement */ + +void read_client_leases () +{ + int file; + struct parse *cfile; + const char *val; + int token; + + /* Open the lease file. If we can't open it, just return - + we can safely trust the server to remember our state. */ + if ((file = open (path_dhclient_db, O_RDONLY)) < 0) + return; + cfile = (struct parse *)0; + new_parse (&cfile, file, (char *)0, 0, path_dhclient_db, 0); + + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) + break; + if (token != LEASE) { + log_error ("Corrupt lease file - possible data loss!"); + skip_to_semi (cfile); + break; + } else + parse_client_lease_statement (cfile, 0); + + } while (1); + + close (file); + end_parse (&cfile); +} + +/* client-declaration :== + SEND option-decl | + DEFAULT option-decl | + SUPERSEDE option-decl | + PREPEND option-decl | + APPEND option-decl | + hardware-declaration | + REQUEST option-list | + REQUIRE option-list | + TIMEOUT number | + RETRY number | + REBOOT number | + SELECT_TIMEOUT number | + SCRIPT string | + VENDOR_SPACE string | + interface-declaration | + LEASE client-lease-statement | + ALIAS client-lease-statement | + KEY key-definition */ + +void parse_client_statement (cfile, ip, config) + struct parse *cfile; + struct interface_info *ip; + struct client_config *config; +{ + int token; + const char *val; + struct option *option; + struct executable_statement *stmt, **p; + enum statement_op op; + int lose; + char *name; + struct data_string key_id; + enum policy policy; + int known; + int tmp, i; + isc_result_t status; + + switch (peek_token (&val, (unsigned *)0, cfile)) { + case INCLUDE: + next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "filename string expected."); + skip_to_semi (cfile); + } else { + status = read_client_conf_file (val, ip, config); + if (status != ISC_R_SUCCESS) + parse_warn (cfile, "%s: bad parse.", val); + parse_semi (cfile); + } + return; + + case KEY: + next_token (&val, (unsigned *)0, cfile); + if (ip) { + /* This may seem arbitrary, but there's a reason for + doing it: the authentication key database is not + scoped. If we allow the user to declare a key other + than in the outer scope, the user is very likely to + believe that the key will only be used in that + scope. If the user only wants the key to be used on + one interface, because it's known that the other + interface may be connected to an insecure net and + the secret key is considered sensitive, we don't + want to lull them into believing they've gotten + their way. This is a bit contrived, but people + tend not to be entirely rational about security. */ + parse_warn (cfile, "key definition not allowed here."); + skip_to_semi (cfile); + break; + } + parse_key (cfile); + return; + + /* REQUIRE can either start a policy statement or a + comma-seperated list of names of required options. */ + case REQUIRE: + next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == AUTHENTICATION) { + policy = P_REQUIRE; + goto do_policy; + } + parse_option_list (cfile, &config -> required_options); + return; + + case IGNORE: + next_token (&val, (unsigned *)0, cfile); + policy = P_IGNORE; + goto do_policy; + + case ACCEPT: + next_token (&val, (unsigned *)0, cfile); + policy = P_ACCEPT; + goto do_policy; + + case PREFER: + next_token (&val, (unsigned *)0, cfile); + policy = P_PREFER; + goto do_policy; + + case DONT: + next_token (&val, (unsigned *)0, cfile); + policy = P_DONT; + goto do_policy; + + do_policy: + token = next_token (&val, (unsigned *)0, cfile); + if (token == AUTHENTICATION) { + if (policy != P_PREFER && + policy != P_REQUIRE && + policy != P_DONT) { + parse_warn (cfile, + "invalid authentication policy."); + skip_to_semi (cfile); + return; + } + config -> auth_policy = policy; + } else if (token != TOKEN_BOOTP) { + if (policy != P_PREFER && + policy != P_IGNORE && + policy != P_ACCEPT) { + parse_warn (cfile, "invalid bootp policy."); + skip_to_semi (cfile); + return; + } + config -> bootp_policy = policy; + } else { + parse_warn (cfile, "expecting a policy type."); + skip_to_semi (cfile); + return; + } + break; + + case OPTION: + token = next_token (&val, (unsigned *)0, cfile); + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SPACE) { + if (ip) { + parse_warn (cfile, + "option space definitions %s", + " may not be scoped."); + skip_to_semi (cfile); + break; + } + parse_option_space_decl (cfile); + return; + } + + option = parse_option_name (cfile, 1, &known); + if (!option) + return; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != CODE) { + parse_warn (cfile, "expecting \"code\" keyword."); + skip_to_semi (cfile); + free_option (option, MDL); + return; + } + if (ip) { + parse_warn (cfile, + "option definitions may only appear in %s", + "the outermost scope."); + skip_to_semi (cfile); + free_option (option, MDL); + return; + } + if (!parse_option_code_definition (cfile, option)) + free_option (option, MDL); + return; + + case MEDIA: + token = next_token (&val, (unsigned *)0, cfile); + parse_string_list (cfile, &config -> media, 1); + return; + + case HARDWARE: + token = next_token (&val, (unsigned *)0, cfile); + if (ip) { + parse_hardware_param (cfile, &ip -> hw_address); + } else { + parse_warn (cfile, "hardware address parameter %s", + "not allowed here."); + skip_to_semi (cfile); + } + return; + + case REQUEST: + token = next_token (&val, (unsigned *)0, cfile); + if (config -> requested_options == default_requested_options) + config -> requested_options = (u_int32_t *)0; + parse_option_list (cfile, &config -> requested_options); + return; + + case TIMEOUT: + token = next_token (&val, (unsigned *)0, cfile); + parse_lease_time (cfile, &config -> timeout); + return; + + case RETRY: + token = next_token (&val, (unsigned *)0, cfile); + parse_lease_time (cfile, &config -> retry_interval); + return; + + case SELECT_TIMEOUT: + token = next_token (&val, (unsigned *)0, cfile); + parse_lease_time (cfile, &config -> select_interval); + return; + + case OMAPI: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != PORT) { + parse_warn (cfile, + "unexpected omapi subtype: %s", val); + skip_to_semi (cfile); + return; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "invalid port number: `%s'", val); + skip_to_semi (cfile); + return; + } + tmp = atoi (val); + if (tmp < 0 || tmp > 65535) + parse_warn (cfile, "invalid omapi port %d.", tmp); + else if (config != &top_level_config) + parse_warn (cfile, + "omapi port only works at top level."); + else + config -> omapi_port = tmp; + parse_semi (cfile); + return; + + case DO_FORWARD_UPDATE: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (!strcasecmp (val, "on") || + !strcasecmp (val, "true")) + config -> do_forward_update = 1; + else if (!strcasecmp (val, "off") || + !strcasecmp (val, "false")) + config -> do_forward_update = 0; + else { + parse_warn (cfile, "expecting boolean value."); + skip_to_semi (cfile); + return; + } + parse_semi (cfile); + return; + + case REBOOT: + token = next_token (&val, (unsigned *)0, cfile); + parse_lease_time (cfile, &config -> reboot_timeout); + return; + + case BACKOFF_CUTOFF: + token = next_token (&val, (unsigned *)0, cfile); + parse_lease_time (cfile, &config -> backoff_cutoff); + return; + + case INITIAL_INTERVAL: + token = next_token (&val, (unsigned *)0, cfile); + parse_lease_time (cfile, &config -> initial_interval); + return; + + case SCRIPT: + token = next_token (&val, (unsigned *)0, cfile); + parse_string (cfile, &config -> script_name, (unsigned *)0); + return; + + case VENDOR: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != OPTION) { + parse_warn (cfile, "expecting 'vendor option space'"); + skip_to_semi (cfile); + return; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != SPACE) { + parse_warn (cfile, "expecting 'vendor option space'"); + skip_to_semi (cfile); + return; + } + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, "expecting an identifier."); + skip_to_semi (cfile); + return; + } + config -> vendor_space_name = dmalloc (strlen (val) + 1, MDL); + if (!config -> vendor_space_name) + log_fatal ("no memory for vendor option space name."); + strcpy (config -> vendor_space_name, val); + for (i = 0; i < universe_count; i++) + if (!strcmp (universes [i] -> name, + config -> vendor_space_name)) + break; + if (i == universe_count) { + log_error ("vendor option space %s not found.", + config -> vendor_space_name); + } + parse_semi (cfile); + return; + + case INTERFACE: + token = next_token (&val, (unsigned *)0, cfile); + if (ip) + parse_warn (cfile, "nested interface declaration."); + parse_interface_declaration (cfile, config, (char *)0); + return; + + case PSEUDO: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + name = dmalloc (strlen (val) + 1, MDL); + if (!name) + log_fatal ("no memory for pseudo interface name"); + strcpy (name, val); + parse_interface_declaration (cfile, config, name); + return; + + case LEASE: + token = next_token (&val, (unsigned *)0, cfile); + parse_client_lease_statement (cfile, 1); + return; + + case ALIAS: + token = next_token (&val, (unsigned *)0, cfile); + parse_client_lease_statement (cfile, 2); + return; + + case REJECT: + token = next_token (&val, (unsigned *)0, cfile); + parse_reject_statement (cfile, config); + return; + + default: + lose = 0; + stmt = (struct executable_statement *)0; + if (!parse_executable_statement (&stmt, + cfile, &lose, context_any)) { + if (!lose) { + parse_warn (cfile, "expecting a statement."); + skip_to_semi (cfile); + } + } else { + struct executable_statement **eptr, *sptr; + if (stmt && + (stmt -> op == send_option_statement || + (stmt -> op == on_statement && + (stmt -> data.on.evtypes & ON_TRANSMISSION)))) { + eptr = &config -> on_transmission -> statements; + if (stmt -> op == on_statement) { + sptr = (struct executable_statement *)0; + executable_statement_reference + (&sptr, + stmt -> data.on.statements, MDL); + executable_statement_dereference (&stmt, + MDL); + executable_statement_reference (&stmt, + sptr, + MDL); + executable_statement_dereference (&sptr, + MDL); + } + } else + eptr = &config -> on_receipt -> statements; + + if (stmt) { + for (; *eptr; eptr = &(*eptr) -> next) + ; + executable_statement_reference (eptr, + stmt, MDL); + } + return; + } + break; + } + parse_semi (cfile); +} + +/* option-list :== option_name | + option_list COMMA option_name */ + +void parse_option_list (cfile, list) + struct parse *cfile; + u_int32_t **list; +{ + int ix; + int token; + const char *val; + pair p = (pair)0, q = (pair)0, r; + struct option *option; + + ix = 0; + do { + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SEMI) { + token = next_token (&val, (unsigned *)0, cfile); + break; + } + if (!is_identifier (token)) { + parse_warn (cfile, "%s: expected option name.", val); + token = next_token (&val, (unsigned *)0, cfile); + skip_to_semi (cfile); + return; + } + option = parse_option_name (cfile, 0, NULL); + if (!option) { + parse_warn (cfile, "%s: expected option name.", val); + return; + } + if (option -> universe != &dhcp_universe) { + parse_warn (cfile, + "%s.%s: Only global options allowed.", + option -> universe -> name, option->name ); + skip_to_semi (cfile); + return; + } + r = new_pair (MDL); + if (!r) + log_fatal ("can't allocate pair for option code."); + r -> car = (caddr_t)(long)option -> code; + r -> cdr = (pair)0; + if (p) + q -> cdr = r; + else + p = r; + q = r; + ++ix; + token = next_token (&val, (unsigned *)0, cfile); + } while (token == COMMA); + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + return; + } + /* XXX we can't free the list here, because we may have copied + XXX it from an outer config state. */ + *list = (u_int32_t *)0; + if (ix) { + *list = dmalloc ((ix + 1) * sizeof **list, MDL); + if (!*list) + log_error ("no memory for option list."); + else { + ix = 0; + for (q = p; q; q = q -> cdr) + (*list) [ix++] = (u_int32_t)(long)q -> car; + (*list) [ix] = 0; + } + while (p) { + q = p -> cdr; + free_pair (p, MDL); + p = q; + } + } +} + +/* interface-declaration :== + INTERFACE string LBRACE client-declarations RBRACE */ + +void parse_interface_declaration (cfile, outer_config, name) + struct parse *cfile; + struct client_config *outer_config; + char *name; +{ + int token; + const char *val; + struct client_state *client, **cp; + struct interface_info *ip = (struct interface_info *)0; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "expecting interface name (in quotes)."); + skip_to_semi (cfile); + return; + } + + if (!interface_or_dummy (&ip, val)) + log_fatal ("Can't allocate interface %s.", val); + + /* If we were given a name, this is a pseudo-interface. */ + if (name) { + make_client_state (&client); + client -> name = name; + client -> interface = ip; + for (cp = &ip -> client; *cp; cp = &((*cp) -> next)) + ; + *cp = client; + } else { + if (!ip -> client) { + make_client_state (&ip -> client); + ip -> client -> interface = ip; + } + client = ip -> client; + } + + if (!client -> config) + make_client_config (client, outer_config); + + ip -> flags &= ~INTERFACE_AUTOMATIC; + interfaces_requested = 1; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace."); + skip_to_semi (cfile); + return; + } + + do { + token = peek_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) { + parse_warn (cfile, + "unterminated interface declaration."); + return; + } + if (token == RBRACE) + break; + parse_client_statement (cfile, ip, client -> config); + } while (1); + token = next_token (&val, (unsigned *)0, cfile); +} + +int interface_or_dummy (struct interface_info **pi, const char *name) +{ + struct interface_info *i; + struct interface_info *ip = (struct interface_info *)0; + isc_result_t status; + + /* Find the interface (if any) that matches the name. */ + for (i = interfaces; i; i = i -> next) { + if (!strcmp (i -> name, name)) { + interface_reference (&ip, i, MDL); + break; + } + } + + /* If it's not a real interface, see if it's on the dummy list. */ + if (!ip) { + for (ip = dummy_interfaces; ip; ip = ip -> next) { + if (!strcmp (ip -> name, name)) { + interface_reference (&ip, i, MDL); + break; + } + } + } + + /* If we didn't find an interface, make a dummy interface as + a placeholder. */ + if (!ip) { + if ((status = interface_allocate (&ip, MDL)) != ISC_R_SUCCESS) + log_fatal ("Can't record interface %s: %s", + name, isc_result_totext (status)); + strcpy (ip -> name, name); + if (dummy_interfaces) { + interface_reference (&ip -> next, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + } + interface_reference (&dummy_interfaces, ip, MDL); + } + if (pi) + status = interface_reference (pi, ip, MDL); + else + status = ISC_R_FAILURE; + interface_dereference (&ip, MDL); + if (status != ISC_R_SUCCESS) + return 0; + return 1; +} + +void make_client_state (state) + struct client_state **state; +{ + *state = ((struct client_state *)dmalloc (sizeof **state, MDL)); + if (!*state) + log_fatal ("no memory for client state\n"); + memset (*state, 0, sizeof **state); +} + +void make_client_config (client, config) + struct client_state *client; + struct client_config *config; +{ + client -> config = (((struct client_config *) + dmalloc (sizeof (struct client_config), MDL))); + if (!client -> config) + log_fatal ("no memory for client config\n"); + memcpy (client -> config, config, sizeof *config); + if (!clone_group (&client -> config -> on_receipt, + config -> on_receipt, MDL) || + !clone_group (&client -> config -> on_transmission, + config -> on_transmission, MDL)) + log_fatal ("no memory for client state groups."); +} + +/* client-lease-statement :== + RBRACE client-lease-declarations LBRACE + + client-lease-declarations :== + | + client-lease-declaration | + client-lease-declarations client-lease-declaration */ + + +void parse_client_lease_statement (cfile, is_static) + struct parse *cfile; + int is_static; +{ + struct client_lease *lease, *lp, *pl, *next; + struct interface_info *ip = (struct interface_info *)0; + int token; + const char *val; + struct client_state *client = (struct client_state *)0; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace."); + skip_to_semi (cfile); + return; + } + + lease = ((struct client_lease *) + dmalloc (sizeof (struct client_lease), MDL)); + if (!lease) + log_fatal ("no memory for lease.\n"); + memset (lease, 0, sizeof *lease); + lease -> is_static = is_static; + if (!option_state_allocate (&lease -> options, MDL)) + log_fatal ("no memory for lease options.\n"); + + do { + token = peek_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) { + parse_warn (cfile, "unterminated lease declaration."); + return; + } + if (token == RBRACE) + break; + parse_client_lease_declaration (cfile, lease, &ip, &client); + } while (1); + token = next_token (&val, (unsigned *)0, cfile); + + /* If the lease declaration didn't include an interface + declaration that we recognized, it's of no use to us. */ + if (!ip) { + destroy_client_lease (lease); + return; + } + + /* Make sure there's a client state structure... */ + if (!ip -> client) { + make_client_state (&ip -> client); + ip -> client -> interface = ip; + } + if (!client) + client = ip -> client; + + /* If this is an alias lease, it doesn't need to be sorted in. */ + if (is_static == 2) { + ip -> client -> alias = lease; + return; + } + + /* The new lease may supersede a lease that's not the + active lease but is still on the lease list, so scan the + lease list looking for a lease with the same address, and + if we find it, toss it. */ + pl = (struct client_lease *)0; + for (lp = client -> leases; lp; lp = next) { + next = lp -> next; + if (lp -> address.len == lease -> address.len && + !memcmp (lp -> address.iabuf, lease -> address.iabuf, + lease -> address.len)) { + if (pl) + pl -> next = next; + else + client -> leases = next; + destroy_client_lease (lp); + break; + } else + pl = lp; + } + + /* If this is a preloaded lease, just put it on the list of recorded + leases - don't make it the active lease. */ + if (is_static) { + lease -> next = client -> leases; + client -> leases = lease; + return; + } + + /* The last lease in the lease file on a particular interface is + the active lease for that interface. Of course, we don't know + what the last lease in the file is until we've parsed the whole + file, so at this point, we assume that the lease we just parsed + is the active lease for its interface. If there's already + an active lease for the interface, and this lease is for the same + ip address, then we just toss the old active lease and replace + it with this one. If this lease is for a different address, + then if the old active lease has expired, we dump it; if not, + we put it on the list of leases for this interface which are + still valid but no longer active. */ + if (client -> active) { + if (client -> active -> expiry < cur_time) + destroy_client_lease (client -> active); + else if (client -> active -> address.len == + lease -> address.len && + !memcmp (client -> active -> address.iabuf, + lease -> address.iabuf, + lease -> address.len)) + destroy_client_lease (client -> active); + else { + client -> active -> next = client -> leases; + client -> leases = client -> active; + } + } + client -> active = lease; + + /* phew. */ +} + +/* client-lease-declaration :== + BOOTP | + INTERFACE string | + FIXED_ADDR ip_address | + FILENAME string | + SERVER_NAME string | + OPTION option-decl | + RENEW time-decl | + REBIND time-decl | + EXPIRE time-decl | + KEY id */ + +void parse_client_lease_declaration (cfile, lease, ipp, clientp) + struct parse *cfile; + struct client_lease *lease; + struct interface_info **ipp; + struct client_state **clientp; +{ + int token; + const char *val; + char *t, *n; + struct interface_info *ip; + struct option_cache *oc; + struct client_state *client = (struct client_state *)0; + struct data_string key_id; + + switch (next_token (&val, (unsigned *)0, cfile)) { + case KEY: + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING && !is_identifier (token)) { + parse_warn (cfile, "expecting key name."); + skip_to_semi (cfile); + break; + } + if (omapi_auth_key_lookup_name (&lease -> key, val) != + ISC_R_SUCCESS) + parse_warn (cfile, "unknown key %s", val); + parse_semi (cfile); + break; + case TOKEN_BOOTP: + lease -> is_bootp = 1; + break; + + case INTERFACE: + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, + "expecting interface name (in quotes)."); + skip_to_semi (cfile); + break; + } + interface_or_dummy (ipp, val); + break; + + case NAME: + token = next_token (&val, (unsigned *)0, cfile); + ip = *ipp; + if (!ip) { + parse_warn (cfile, "state name precedes interface."); + break; + } + for (client = ip -> client; client; client = client -> next) + if (client -> name && !strcmp (client -> name, val)) + break; + if (!client) + parse_warn (cfile, + "lease specified for unknown pseudo."); + *clientp = client; + break; + + case FIXED_ADDR: + if (!parse_ip_addr (cfile, &lease -> address)) + return; + break; + + case MEDIUM: + parse_string_list (cfile, &lease -> medium, 0); + return; + + case FILENAME: + parse_string (cfile, &lease -> filename, (unsigned *)0); + return; + + case SERVER_NAME: + parse_string (cfile, &lease -> server_name, (unsigned *)0); + return; + + case RENEW: + lease -> renewal = parse_date (cfile); + return; + + case REBIND: + lease -> rebind = parse_date (cfile); + return; + + case EXPIRE: + lease -> expiry = parse_date (cfile); + return; + + case OPTION: + oc = (struct option_cache *)0; + if (parse_option_decl (&oc, cfile)) { + save_option (oc -> option -> universe, + lease -> options, oc); + option_cache_dereference (&oc, MDL); + } + return; + + default: + parse_warn (cfile, "expecting lease declaration."); + skip_to_semi (cfile); + break; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + } +} + +void parse_string_list (cfile, lp, multiple) + struct parse *cfile; + struct string_list **lp; + int multiple; +{ + int token; + const char *val; + struct string_list *cur, *tmp; + + /* Find the last medium in the media list. */ + if (*lp) { + for (cur = *lp; cur -> next; cur = cur -> next) + ; + } else { + cur = (struct string_list *)0; + } + + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "Expecting media options."); + skip_to_semi (cfile); + return; + } + + tmp = ((struct string_list *) + dmalloc (strlen (val) + sizeof (struct string_list), + MDL)); + if (!tmp) + log_fatal ("no memory for string list entry."); + + strcpy (tmp -> string, val); + tmp -> next = (struct string_list *)0; + + /* Store this medium at the end of the media list. */ + if (cur) + cur -> next = tmp; + else + *lp = tmp; + cur = tmp; + + token = next_token (&val, (unsigned *)0, cfile); + } while (multiple && token == COMMA); + + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + } +} + +void parse_reject_statement (cfile, config) + struct parse *cfile; + struct client_config *config; +{ + int token; + const char *val; + struct iaddr addr; + struct iaddrlist *list; + + do { + if (!parse_ip_addr (cfile, &addr)) { + parse_warn (cfile, "expecting IP address."); + skip_to_semi (cfile); + return; + } + + list = (struct iaddrlist *)dmalloc (sizeof (struct iaddrlist), + MDL); + if (!list) + log_fatal ("no memory for reject list!"); + + list -> addr = addr; + list -> next = config -> reject_list; + config -> reject_list = list; + + token = next_token (&val, (unsigned *)0, cfile); + } while (token == COMMA); + + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + } +} + +/* allow-deny-keyword :== BOOTP + | BOOTING + | DYNAMIC_BOOTP + | UNKNOWN_CLIENTS */ + +int parse_allow_deny (oc, cfile, flag) + struct option_cache **oc; + struct parse *cfile; + int flag; +{ + enum dhcp_token token; + const char *val; + unsigned char rf = flag; + struct expression *data = (struct expression *)0; + int status; + + parse_warn (cfile, "allow/deny/ignore not permitted here."); + skip_to_semi (cfile); + return 0; +} + diff --git a/contrib/dhcp-3.0/client/dhclient-script.8 b/contrib/dhcp-3.0/client/dhclient-script.8 new file mode 100644 index 0000000000..d81cf41170 --- /dev/null +++ b/contrib/dhcp-3.0/client/dhclient-script.8 @@ -0,0 +1,221 @@ +.\" dhclient-script.8 +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 1996-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" This software has been written for Internet Systems Consortium +.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. +.\" To learn more about Internet Systems Consortium, see +.\" ``http://www.isc.org/''. To learn more about Vixie Enterprises, +.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see +.\" ``http://www.nominum.com''. +.\" +.\" $Id: dhclient-script.8,v 1.8.2.5 2004/06/10 17:59:12 dhankins Exp $ +.\" +.TH dhclient-script 8 +.SH NAME +dhclient-script - DHCP client network configuration script +.SH DESCRIPTION +The DHCP client network configuration script is invoked from time to +time by \fBdhclient(8)\fR. This script is used by the dhcp client to +set each interface's initial configuration prior to requesting an +address, to test the address once it has been offered, and to set the +interface's final configuration once a lease has been acquired. If no +lease is acquired, the script is used to test predefined leases, if +any, and also called once if no valid lease can be identified. +.PP +This script is not meant to be customized by the end user. If local +customizations are needed, they should be possible using the enter and +exit hooks provided (see HOOKS for details). These hooks will allow the +user to override the default behaviour of the client in creating a +.B /etc/resolv.conf +file. +.PP +No standard client script exists for some operating systems, even though +the actual client may work, so a pioneering user may well need to create +a new script or modify an existing one. In general, customizations specific +to a particular computer should be done in the +.B ETCDIR/dhclient.conf +file. If you find that you can't make such a customization without +customizing +.B ETCDIR/dhclient.conf +or using the enter and exit hooks, please submit a bug report. +.SH HOOKS +When it starts, the client script first defines a shell function, +.B make_resolv_conf , +which is later used to create the +.B /etc/resolv.conf +file. To override the default behaviour, redefine this function in +the enter hook script. +.PP +On after defining the make_resolv_conf function, the client script checks +for the presence of an executable +.B ETCDIR/dhclient-enter-hooks +script, and if present, it invokes the script inline, using the Bourne +shell '.' command. The entire environment documented under OPERATION +is available to this script, which may modify the environment if needed +to change the behaviour of the script. If an error occurs during the +execution of the script, it can set the exit_status variable to a nonzero +value, and +.B CLIENTBINDIR/dhclient-script +will exit with that error code immediately after the client script exits. +.PP +After all processing has completed, +.B CLIENTBINDIR/dhclient-script +checks for the presence of an executable +.B ETCDIR/dhclient-exit-hooks +script, which if present is invoked using the '.' command. The exit +status of dhclient-script will be passed to dhclient-exit-hooks in the +exit_status shell variable, and will always be zero if the script +succeeded at the task for which it was invoked. The rest of the +environment as described previously for dhclient-enter-hooks is also +present. The +.B ETCDIR/dhclient-exit-hooks +script can modify the valid of exit_status to change the exit status +of dhclient-script. +.SH OPERATION +When dhclient needs to invoke the client configuration script, it +defines a set of variables in the environment, and then invokes +.B CLIENTBINDIR/dhclient-script. +In all cases, $reason is set to the name of the reason why the script +has been invoked. The following reasons are currently defined: +MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT, EXPIRE, FAIL and TIMEOUT. +.PP +.SH MEDIUM +The DHCP client is requesting that an interface's media type +be set. The interface name is passed in $interface, and the media +type is passed in $medium. +.SH PREINIT +The DHCP client is requesting that an interface be configured as +required in order to send packets prior to receiving an actual +address. For clients which use the BSD socket library, this means +configuring the interface with an IP address of 0.0.0.0 and a +broadcast address of 255.255.255.255. For other clients, it may be +possible to simply configure the interface up without actually giving +it an IP address at all. The interface name is passed in $interface, +and the media type in $medium. +.PP +If an IP alias has been declared in dhclient.conf, its address will be +passed in $alias_ip_address, and that ip alias should be deleted from +the interface, along with any routes to it. +.SH BOUND +The DHCP client has done an initial binding to a new address. The +new ip address is passed in $new_ip_address, and the interface name is +passed in $interface. The media type is passed in $medium. Any +options acquired from the server are passed using the option name +described in \fBdhcp-options\fR, except that dashes ('-') are replaced +by underscores ('_') in order to make valid shell variables, and the +variable names start with new_. So for example, the new subnet mask +would be passed in $new_subnet_mask. +.PP +Before actually configuring the address, dhclient-script should +somehow ARP for it and exit with a nonzero status if it receives a +reply. In this case, the client will send a DHCPDECLINE message to +the server and acquire a different address. This may also be done in +the RENEW, REBIND, or REBOOT states, but is not required, and indeed +may not be desirable. +.PP +When a binding has been completed, a lot of network parameters are +likely to need to be set up. A new /etc/resolv.conf needs to be +created, using the values of $new_domain_name and +$new_domain_name_servers (which may list more than one server, +separated by spaces). A default route should be set using +$new_routers, and static routes may need to be set up using +$new_static_routes. +.PP +If an IP alias has been declared, it must be set up here. The alias +IP address will be written as $alias_ip_address, and other DHCP +options that are set for the alias (e.g., subnet mask) will be passed +in variables named as described previously except starting with +$alias_ instead of $new_. Care should be taken that the alias IP +address not be used if it is identical to the bound IP address +($new_ip_address), since the other alias parameters may be incorrect +in this case. +.SH RENEW +When a binding has been renewed, the script is called as in BOUND, +except that in addition to all the variables starting with $new_, +there is another set of variables starting with $old_. Persistent +settings that may have changed need to be deleted - for example, if a +local route to the bound address is being configured, the old local +route should be deleted. If the default route has changed, the old default +route should be deleted. If the static routes have changed, the old +ones should be deleted. Otherwise, processing can be done as with +BOUND. +.SH REBIND +The DHCP client has rebound to a new DHCP server. This can be handled +as with RENEW, except that if the IP address has changed, the ARP +table should be cleared. +.SH REBOOT +The DHCP client has successfully reacquired its old address after a +reboot. This can be processed as with BOUND. +.SH EXPIRE +The DHCP client has failed to renew its lease or acquire a new one, +and the lease has expired. The IP address must be relinquished, and +all related parameters should be deleted, as in RENEW and REBIND. +.SH FAIL +The DHCP client has been unable to contact any DHCP servers, and any +leases that have been tested have not proved to be valid. The +parameters from the last lease tested should be deconfigured. This +can be handled in the same way as EXPIRE. +.SH TIMEOUT +The DHCP client has been unable to contact any DHCP servers. +However, an old lease has been identified, and its parameters have +been passed in as with BOUND. The client configuration script should +test these parameters and, if it has reason to believe they are valid, +should exit with a value of zero. If not, it should exit with a +nonzero value. +.PP +The usual way to test a lease is to set up the network as with REBIND +(since this may be called to test more than one lease) and then ping +the first router defined in $routers. If a response is received, the +lease must be valid for the network to which the interface is +currently connected. It would be more complete to try to ping all of +the routers listed in $new_routers, as well as those listed in +$new_static_routes, but current scripts do not do this. +.SH FILES +Each operating system should generally have its own script file, +although the script files for similar operating systems may be similar +or even identical. The script files included in Internet +Systems Consortium DHCP distribution appear in the distribution tree +under client/scripts, and bear the names of the operating systems on +which they are intended to work. +.SH BUGS +If more than one interface is being used, there's no obvious way to +avoid clashes between server-supplied configuration parameters - for +example, the stock dhclient-script rewrites /etc/resolv.conf. If +more than one interface is being configured, /etc/resolv.conf will be +repeatedly initialized to the values provided by one server, and then +the other. Assuming the information provided by both servers is +valid, this shouldn't cause any real problems, but it could be +confusing. +.SH SEE ALSO +dhclient(8), dhcpd(8), dhcrelay(8), dhclient.conf(5) and +dhclient.leases(5). +.SH AUTHOR +.B dhclient-script(8) +has been written for Internet Systems Consortium +by Ted Lemon in cooperation with Vixie +Enterprises. To learn more about Internet Systems Consortium, +see +.B http://www.isc.org. +To learn more about Vixie +Enterprises, see +.B http://www.vix.com. diff --git a/contrib/dhcp-3.0/client/dhclient.8 b/contrib/dhcp-3.0/client/dhclient.8 new file mode 100644 index 0000000000..01d1368dcc --- /dev/null +++ b/contrib/dhcp-3.0/client/dhclient.8 @@ -0,0 +1,319 @@ +.\" dhclient.8 +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 1996-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" $Id: dhclient.8,v 1.12.2.9 2004/09/29 23:01:46 dhankins Exp $ +.\" +.TH dhclient 8 +.SH NAME +dhclient - Dynamic Host Configuration Protocol Client +.SH SYNOPSIS +.B dhclient +[ +.B -p +.I port +] +[ +.B -d +] +[ +.B -q +] +[ +.B -1 +] +[ +.B -r +] +[ +.B -lf +.I lease-file +] +[ +.B -pf +.I pid-file +] +[ +.B -cf +.I config-file +] +[ +.B -sf +.I script-file +] +[ +.B -s +server +] +[ +.B -g +relay +] +[ +.B -n +] +[ +.B -nw +] +[ +.B -w +] +[ +.I if0 +[ +.I ...ifN +] +] +.SH DESCRIPTION +The Internet Systems Consortium DHCP Client, dhclient, provides a +means for configuring one or more network interfaces using the Dynamic +Host Configuration Protocol, BOOTP protocol, or if these protocols +fail, by statically assigning an address. +.SH OPERATION +.PP +The DHCP protocol allows a host to contact a central server which +maintains a list of IP addresses which may be assigned on one or more +subnets. A DHCP client may request an address from this pool, and +then use it on a temporary basis for communication on network. The +DHCP protocol also provides a mechanism whereby a client can learn +important details about the network to which it is attached, such as +the location of a default router, the location of a name server, and +so on. +.PP +On startup, dhclient reads the +.IR dhclient.conf +for configuration instructions. It then gets a list of all the +network interfaces that are configured in the current system. For +each interface, it attempts to configure the interface using the DHCP +protocol. +.PP +In order to keep track of leases across system reboots and server +restarts, dhclient keeps a list of leases it has been assigned in the +dhclient.leases(5) file. On startup, after reading the dhclient.conf +file, dhclient reads the dhclient.leases file to refresh its memory +about what leases it has been assigned. +.PP +When a new lease is acquired, it is appended to the end of the +dhclient.leases file. In order to prevent the file from becoming +arbitrarily large, from time to time dhclient creates a new +dhclient.leases file from its in-core lease database. The old version +of the dhclient.leases file is retained under the name +.IR dhclient.leases~ +until the next time dhclient rewrites the database. +.PP +Old leases are kept around in case the DHCP server is unavailable when +dhclient is first invoked (generally during the initial system boot +process). In that event, old leases from the dhclient.leases file +which have not yet expired are tested, and if they are determined to +be valid, they are used until either they expire or the DHCP server +becomes available. +.PP +A mobile host which may sometimes need to access a network on which no +DHCP server exists may be preloaded with a lease for a fixed +address on that network. When all attempts to contact a DHCP server +have failed, dhclient will try to validate the static lease, and if it +succeeds, will use that lease until it is restarted. +.PP +A mobile host may also travel to some networks on which DHCP is not +available but BOOTP is. In that case, it may be advantageous to +arrange with the network administrator for an entry on the BOOTP +database, so that the host can boot quickly on that network rather +than cycling through the list of old leases. +.SH COMMAND LINE +.PP +The names of the network interfaces that dhclient should attempt to +configure may be specified on the command line. If no interface names +are specified on the command line dhclient will normally identify all +network interfaces, eliminating non-broadcast interfaces if +possible, and attempt to configure each interface. +.PP +It is also possible to specify interfaces by name in the +.B dhclient.conf(5) +file. If interfaces are specified in this way, then the client will +only configure interfaces that are either specified in the +configuration file or on the command line, and will ignore all other +interfaces. +.PP +If the DHCP client should listen and transmit on a port other than the +standard (port 68), the +.B -p +flag may used. It should be followed by the udp port number that +dhclient should use. This is mostly useful for debugging purposes. +If a different port is specified for the client to listen on and +transmit on, the client will also use a different destination port - +one greater than the specified destination port. +.PP +The DHCP client normally transmits any protocol messages it sends +before acquiring an IP address to, 255.255.255.255, the IP limited +broadcast address. For debugging purposes, it may be useful to have +the server transmit these messages to some other address. This can +be specified with the +.B -s +flag, followed by the IP address or domain name of the destination. +.PP +For testing purposes, the giaddr field of all packets that the client +sends can be set using the +.B -g +flag, followed by the IP address to send. This is only useful for testing, +and should not be expected to work in any consistent or useful way. +.PP +The DHCP client will normally run in the foreground until it has +configured an interface, and then will revert to running in the +background. To run force dhclient to always run as a foreground +process, the +.B -d +flag should be specified. This is useful when running the client +under a debugger, or when running it out of inittab on System V +systems. +.PP +The client normally prints a startup message and displays the +protocol sequence to the standard error descriptor until it has +acquired an address, and then only logs messages using the +.B syslog (3) +facility. The +.B -q +flag prevents any messages other than errors from being printed to the +standard error descriptor. +.PP +The client normally doesn't release the current lease as it is not +required by the DHCP protocol. Some cable ISPs require their clients +to notify the server if they wish to release an assigned IP address. +The +.B -r +flag explicitly releases the current lease, and once the lease has been +released, the client exits. +.PP +The +.B -1 +flag cause dhclient to try once to get a lease. If it fails, dhclient exits +with exit code two. +.PP +The DHCP client normally gets its configuration information from +.B ETCDIR/dhclient.conf, +its lease database from +.B DBDIR/dhclient.leases, +stores its process ID in a file called +.B RUNDIR/dhclient.pid, +and configures the network interface using +.B CLIENTBINDIR/dhclient-script +To specify different names and/or locations for these files, use the +.B -cf, +.B -lf, +.B -pf +and +.B -sf +flags, respectively, followed by the name of the file. This can be +particularly useful if, for example, +.B DBDIR +or +.B RUNDIR +has not yet been mounted when the DHCP client is started. +.PP +The DHCP client normally exits if it isn't able to identify any +network interfaces to configure. On laptop computers and other +computers with hot-swappable I/O buses, it is possible that a +broadcast interface may be added after system startup. The +.B -w +flag can be used to cause the client not to exit when it doesn't find +any such interfaces. The +.B omshell (8) +program can then be used to notify the client when a network interface +has been added or removed, so that the client can attempt to configure an IP +address on that interface. +.PP +The DHCP client can be directed not to attempt to configure any interfaces +using the +.B -n +flag. This is most likely to be useful in combination with the +.B -w +flag. +.PP +The client can also be instructed to become a daemon immediately, rather +than waiting until it has acquired an IP address. This can be done by +supplying the +.B -nw +flag. +.SH CONFIGURATION +The syntax of the dhclient.conf(5) file is discussed separately. +.SH OMAPI +The DHCP client provides some ability to control it while it is +running, without stopping it. This capability is provided using OMAPI, +an API for manipulating remote objects. OMAPI clients connect to the +client using TCP/IP, authenticate, and can then examine the client's +current status and make changes to it. +.PP +Rather than implementing the underlying OMAPI protocol directly, user +programs should use the dhcpctl API or OMAPI itself. Dhcpctl is a +wrapper that handles some of the housekeeping chores that OMAPI does +not do automatically. Dhcpctl and OMAPI are documented in \fBdhcpctl(3)\fR +and \fBomapi(3)\fR. Most things you'd want to do with the client can +be done directly using the \fBomshell(1)\fR command, rather than +having to write a special program. +.SH THE CONTROL OBJECT +The control object allows you to shut the client down, releasing all +leases that it holds and deleting any DNS records it may have added. +It also allows you to pause the client - this unconfigures any +interfaces the client is using. You can then restart it, which +causes it to reconfigure those interfaces. You would normally pause +the client prior to going into hibernation or sleep on a laptop +computer. You would then resume it after the power comes back. +This allows PC cards to be shut down while the computer is hibernating +or sleeping, and then reinitialized to their previous state once the +computer comes out of hibernation or sleep. +.PP +The control object has one attribute - the state attribute. To shut +the client down, set its state attribute to 2. It will automatically +do a DHCPRELEASE. To pause it, set its state attribute to 3. To +resume it, set its state attribute to 4. +.PP +.SH FILES +.B CLIENTBINDIR/dhclient-script, +.B ETCDIR/dhclient.conf, DBDIR/dhclient.leases, RUNDIR/dhclient.pid, +.B DBDIR/dhclient.leases~. +.SH SEE ALSO +dhcpd(8), dhcrelay(8), dhclient-script(8), dhclient.conf(5), +dhclient.leases(5). +.SH AUTHOR +.B dhclient(8) +has been written for Internet Systems Consortium +by Ted Lemon in cooperation with Vixie +Enterprises. To learn more about Internet Systems Consortium, +see +.B http://www.isc.org +To learn more about Vixie +Enterprises, see +.B http://www.vix.com. +.PP +This client was substantially modified and enhanced by Elliot Poger +for use on Linux while he was working on the MosquitoNet project at +Stanford. +.PP +The current version owes much to Elliot's Linux enhancements, but +was substantially reorganized and partially rewritten by Ted Lemon +so as to use the same networking framework that the Internet Systems +Consortium DHCP server uses. Much system-specific configuration code +was moved into a shell script so that as support for more operating +systems is added, it will not be necessary to port and maintain +system-specific configuration code to these operating systems - instead, +the shell script can invoke the native tools to accomplish the same +purpose. +.PP diff --git a/contrib/dhcp-3.0/client/dhclient.c b/contrib/dhcp-3.0/client/dhclient.c new file mode 100644 index 0000000000..709c507c60 --- /dev/null +++ b/contrib/dhcp-3.0/client/dhclient.c @@ -0,0 +1,3162 @@ +/* dhclient.c + + DHCP Client. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This code is based on the original client state machine that was + * written by Elliot Poger. The code has been extensively hacked on + * by Ted Lemon since then, so any mistakes you find are probably his + * fault and not Elliot's. + */ + +#ifndef lint +static char ocopyright[] = +"$Id: dhclient.c,v 1.129.2.23 2004/11/24 17:39:14 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include "version.h" + +TIME default_lease_time = 43200; /* 12 hours... */ +TIME max_lease_time = 86400; /* 24 hours... */ + +const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; +const char *path_dhclient_db = _PATH_DHCLIENT_DB; +const char *path_dhclient_pid = _PATH_DHCLIENT_PID; +static char path_dhclient_script_array [] = _PATH_DHCLIENT_SCRIPT; +char *path_dhclient_script = path_dhclient_script_array; + +int dhcp_max_agent_option_packet_length = 0; + +int interfaces_requested = 0; + +struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; +struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } }; +struct in_addr inaddr_any; +struct sockaddr_in sockaddr_broadcast; +struct in_addr giaddr; + +/* ASSERT_STATE() does nothing now; it used to be + assert (state_is == state_shouldbe). */ +#define ASSERT_STATE(state_is, state_shouldbe) {} + +static char copyright[] = "Copyright 2004 Internet Systems Consortium."; +static char arr [] = "All rights reserved."; +static char message [] = "Internet Systems Consortium DHCP Client"; +static char url [] = "For info, please visit http://www.isc.org/products/DHCP"; + +u_int16_t local_port=0; +u_int16_t remote_port=0; +int no_daemon=0; +struct string_list *client_env=NULL; +int client_env_count=0; +int onetry=0; +int quiet=0; +int nowait=0; + +static void usage PROTO ((void)); + +void do_release(struct client_state *); + +int main (argc, argv, envp) + int argc; + char **argv, **envp; +{ + int i; + struct servent *ent; + struct interface_info *ip; + struct client_state *client; + unsigned seed; + char *server = (char *)0; + char *relay = (char *)0; + isc_result_t status; + int release_mode = 0; + omapi_object_t *listener; + isc_result_t result; + int persist = 0; + int omapi_port; + int no_dhclient_conf = 0; + int no_dhclient_db = 0; + int no_dhclient_pid = 0; + int no_dhclient_script = 0; + char *s; + + /* Make sure we have stdin, stdout and stderr. */ + i = open ("/dev/null", O_RDWR); + if (i == 0) + i = open ("/dev/null", O_RDWR); + if (i == 1) { + i = open ("/dev/null", O_RDWR); + log_perror = 0; /* No sense logging to /dev/null. */ + } else if (i != -1) + close (i); + +#ifdef SYSLOG_4_2 + openlog ("dhclient", LOG_NDELAY); + log_priority = LOG_DAEMON; +#else + openlog ("dhclient", LOG_NDELAY, LOG_DAEMON); +#endif + +#if !(defined (DEBUG) || defined (SYSLOG_4_2) || defined (__CYGWIN32__)) + setlogmask (LOG_UPTO (LOG_INFO)); +#endif + + /* Set up the OMAPI. */ + status = omapi_init (); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't initialize OMAPI: %s", + isc_result_totext (status)); + + /* Set up the OMAPI wrappers for various server database internal + objects. */ + dhcp_common_objects_setup (); + + dhcp_interface_discovery_hook = dhclient_interface_discovery_hook; + dhcp_interface_shutdown_hook = dhclient_interface_shutdown_hook; + dhcp_interface_startup_hook = dhclient_interface_startup_hook; + + for (i = 1; i < argc; i++) { + if (!strcmp (argv [i], "-r")) { + release_mode = 1; + no_daemon = 1; + } else if (!strcmp (argv [i], "-p")) { + if (++i == argc) + usage (); + local_port = htons (atoi (argv [i])); + log_debug ("binding to user-specified port %d", + ntohs (local_port)); + } else if (!strcmp (argv [i], "-d")) { + no_daemon = 1; + } else if (!strcmp (argv [i], "-pf")) { + if (++i == argc) + usage (); + path_dhclient_pid = argv [i]; + no_dhclient_pid = 1; + } else if (!strcmp (argv [i], "-cf")) { + if (++i == argc) + usage (); + path_dhclient_conf = argv [i]; + no_dhclient_conf = 1; + } else if (!strcmp (argv [i], "-lf")) { + if (++i == argc) + usage (); + path_dhclient_db = argv [i]; + no_dhclient_db = 1; + } else if (!strcmp (argv [i], "-sf")) { + if (++i == argc) + usage (); + path_dhclient_script = argv [i]; + no_dhclient_script = 1; + } else if (!strcmp (argv [i], "-1")) { + onetry = 1; + } else if (!strcmp (argv [i], "-q")) { + quiet = 1; + quiet_interface_discovery = 1; + } else if (!strcmp (argv [i], "-s")) { + if (++i == argc) + usage (); + server = argv [i]; + } else if (!strcmp (argv [i], "-g")) { + if (++i == argc) + usage (); + relay = argv [i]; + } else if (!strcmp (argv [i], "-nw")) { + nowait = 1; + } else if (!strcmp (argv [i], "-n")) { + /* do not start up any interfaces */ + interfaces_requested = 1; + } else if (!strcmp (argv [i], "-w")) { + /* do not exit if there are no broadcast interfaces. */ + persist = 1; + } else if (!strcmp (argv [i], "-e")) { + struct string_list *tmp; + if (++i == argc) + usage (); + tmp = dmalloc (strlen (argv [i]) + sizeof *tmp, MDL); + if (!tmp) + log_fatal ("No memory for %s", argv [i]); + strcpy (tmp -> string, argv [i]); + tmp -> next = client_env; + client_env = tmp; + client_env_count++; + } else if (!strcmp (argv [i], "--version")) { + log_info ("isc-dhclient-%s", DHCP_VERSION); + exit (0); + } else if (argv [i][0] == '-') { + usage (); + } else { + struct interface_info *tmp = (struct interface_info *)0; + status = interface_allocate (&tmp, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't record interface %s:%s", + argv [i], isc_result_totext (status)); + if (strlen (argv [i]) > sizeof tmp -> name) + log_fatal ("%s: interface name too long (max %ld)", + argv [i], (long)strlen (argv [i])); + strcpy (tmp -> name, argv [i]); + if (interfaces) { + interface_reference (&tmp -> next, + interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, tmp, MDL); + tmp -> flags = INTERFACE_REQUESTED; + interfaces_requested = 1; + } + } + + if (!no_dhclient_conf && (s = getenv ("PATH_DHCLIENT_CONF"))) { + path_dhclient_conf = s; + } + if (!no_dhclient_db && (s = getenv ("PATH_DHCLIENT_DB"))) { + path_dhclient_db = s; + } + if (!no_dhclient_pid && (s = getenv ("PATH_DHCLIENT_PID"))) { + path_dhclient_pid = s; + } + if (!no_dhclient_script && (s = getenv ("PATH_DHCLIENT_SCRIPT"))) { + path_dhclient_script = s; + } + + /* first kill of any currently running client */ + if (release_mode) { + FILE *pidfd; + pid_t oldpid; + long temp; + int e; + + oldpid = 0; + if ((pidfd = fopen(path_dhclient_pid, "r")) != NULL) { + e = fscanf(pidfd, "%ld\n", &temp); + oldpid = (pid_t)temp; + + if (e != 0 && e != EOF) { + if (oldpid) { + if (kill(oldpid, SIGTERM) == 0) + unlink(path_dhclient_pid); + } + } + fclose(pidfd); + } + } + + if (!quiet) { + log_info ("%s %s", message, DHCP_VERSION); + log_info (copyright); + log_info (arr); + log_info (url); + log_info ("%s", ""); + } else + log_perror = 0; + + /* If we're given a relay agent address to insert, for testing + purposes, figure out what it is. */ + if (relay) { + if (!inet_aton (relay, &giaddr)) { + struct hostent *he; + he = gethostbyname (relay); + if (he) { + memcpy (&giaddr, he -> h_addr_list [0], + sizeof giaddr); + } else { + log_fatal ("%s: no such host", relay); + } + } + } + + /* Default to the DHCP/BOOTP port. */ + if (!local_port) { + /* If we're faking a relay agent, and we're not using loopback, + use the server port, not the client port. */ + if (relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) { + local_port = htons(67); + } else { + ent = getservbyname ("dhcpc", "udp"); + if (!ent) + local_port = htons (68); + else + local_port = ent -> s_port; +#ifndef __CYGWIN32__ + endservent (); +#endif + } + } + + /* If we're faking a relay agent, and we're not using loopback, + we're using the server port, not the client port. */ + if (relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) { + remote_port = local_port; + } else + remote_port = htons (ntohs (local_port) - 1); /* XXX */ + + /* Get the current time... */ + GET_TIME (&cur_time); + + sockaddr_broadcast.sin_family = AF_INET; + sockaddr_broadcast.sin_port = remote_port; + if (server) { + if (!inet_aton (server, &sockaddr_broadcast.sin_addr)) { + struct hostent *he; + he = gethostbyname (server); + if (he) { + memcpy (&sockaddr_broadcast.sin_addr, + he -> h_addr_list [0], + sizeof sockaddr_broadcast.sin_addr); + } else + sockaddr_broadcast.sin_addr.s_addr = + INADDR_BROADCAST; + } + } else { + sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; + } + + inaddr_any.s_addr = INADDR_ANY; + + /* Discover all the network interfaces. */ + discover_interfaces (DISCOVER_UNCONFIGURED); + + /* Parse the dhclient.conf file. */ + read_client_conf (); + + /* Parse the lease database. */ + read_client_leases (); + + /* Rewrite the lease database... */ + rewrite_client_leases (); + + /* XXX */ +/* config_counter(&snd_counter, &rcv_counter); */ + + /* If no broadcast interfaces were discovered, call the script + and tell it so. */ + if (!interfaces) { + /* Call dhclient-script with the NBI flag, in case somebody + cares. */ + script_init ((struct client_state *)0, "NBI", + (struct string_list *)0); + script_go ((struct client_state *)0); + + /* If we haven't been asked to persist, waiting for new + interfaces, then just exit. */ + if (!persist) { + /* Nothing more to do. */ + log_info ("No broadcast interfaces found - exiting."); + exit (0); + } + } else if (!release_mode) { + /* Call the script with the list of interfaces. */ + for (ip = interfaces; ip; ip = ip -> next) { + /* If interfaces were specified, don't configure + interfaces that weren't specified! */ + if (interfaces_requested && + ((ip -> flags & (INTERFACE_REQUESTED | + INTERFACE_AUTOMATIC)) != + INTERFACE_REQUESTED)) + continue; + script_init (ip -> client, + "PREINIT", (struct string_list *)0); + if (ip -> client -> alias) + script_write_params (ip -> client, "alias_", + ip -> client -> alias); + script_go (ip -> client); + } + } + + /* At this point, all the interfaces that the script thinks + are relevant should be running, so now we once again call + discover_interfaces(), and this time ask it to actually set + up the interfaces. */ + discover_interfaces (interfaces_requested + ? DISCOVER_REQUESTED + : DISCOVER_RUNNING); + + /* Make up a seed for the random number generator from current + time plus the sum of the last four bytes of each + interface's hardware address interpreted as an integer. + Not much entropy, but we're booting, so we're not likely to + find anything better. */ + seed = 0; + for (ip = interfaces; ip; ip = ip -> next) { + int junk; + memcpy (&junk, + &ip -> hw_address.hbuf [ip -> hw_address.hlen - + sizeof seed], sizeof seed); + seed += junk; + } + srandom (seed + cur_time); + + /* Start a configuration state machine for each interface. */ + for (ip = interfaces; ip; ip = ip -> next) { + ip -> flags |= INTERFACE_RUNNING; + for (client = ip -> client; client; client = client -> next) { + if (release_mode) + do_release (client); + else { + client -> state = S_INIT; + /* Set up a timeout to start the initialization + process. */ + add_timeout (cur_time + random () % 5, + state_reboot, client, 0, 0); + } + } + } + + if (release_mode) + return 0; + + /* Start up a listener for the object management API protocol. */ + if (top_level_config.omapi_port != -1) { + listener = (omapi_object_t *)0; + result = omapi_generic_new (&listener, MDL); + if (result != ISC_R_SUCCESS) + log_fatal ("Can't allocate new generic object: %s\n", + isc_result_totext (result)); + result = omapi_protocol_listen (listener, + (unsigned) + top_level_config.omapi_port, + 1); + if (result != ISC_R_SUCCESS) + log_fatal ("Can't start OMAPI protocol: %s", + isc_result_totext (result)); + } + + /* Set up the bootp packet handler... */ + bootp_packet_handler = do_packet; + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + dmalloc_cutoff_generation = dmalloc_generation; + dmalloc_longterm = dmalloc_outstanding; + dmalloc_outstanding = 0; +#endif + + /* If we're not supposed to wait before getting the address, + don't. */ + if (nowait) + go_daemon (); + + /* If we're not going to daemonize, write the pid file + now. */ + if (no_daemon || nowait) + write_client_pid_file (); + + /* Start dispatching packets and timeouts... */ + dispatch (); + + /*NOTREACHED*/ + return 0; +} + +static void usage () +{ + log_info ("%s %s", message, DHCP_VERSION); + log_info (copyright); + log_info (arr); + log_info (url); + + log_error ("Usage: dhclient [-1dqr] [-nw] [-p ] %s", + "[-s server]"); + log_error (" [-cf config-file] [-lf lease-file]%s", + "[-pf pid-file] [-e VAR=val]"); + log_fatal (" [-sf script-file] [interface]"); +} + +isc_result_t find_class (struct class **c, + const char *s, const char *file, int line) +{ + return 0; +} + +int check_collection (packet, lease, collection) + struct packet *packet; + struct lease *lease; + struct collection *collection; +{ + return 0; +} + +void classify (packet, class) + struct packet *packet; + struct class *class; +{ +} + +int unbill_class (lease, class) + struct lease *lease; + struct class *class; +{ + return 0; +} + +int find_subnet (struct subnet **sp, + struct iaddr addr, const char *file, int line) +{ + return 0; +} + +/* Individual States: + * + * Each routine is called from the dhclient_state_machine() in one of + * these conditions: + * -> entering INIT state + * -> recvpacket_flag == 0: timeout in this state + * -> otherwise: received a packet in this state + * + * Return conditions as handled by dhclient_state_machine(): + * Returns 1, sendpacket_flag = 1: send packet, reset timer. + * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). + * Returns 0: finish the nap which was interrupted for no good reason. + * + * Several per-interface variables are used to keep track of the process: + * active_lease: the lease that is being used on the interface + * (null pointer if not configured yet). + * offered_leases: leases corresponding to DHCPOFFER messages that have + * been sent to us by DHCP servers. + * acked_leases: leases corresponding to DHCPACK messages that have been + * sent to us by DHCP servers. + * sendpacket: DHCP packet we're trying to send. + * destination: IP address to send sendpacket to + * In addition, there are several relevant per-lease variables. + * T1_expiry, T2_expiry, lease_expiry: lease milestones + * In the active lease, these control the process of renewing the lease; + * In leases on the acked_leases list, this simply determines when we + * can no longer legitimately use the lease. + */ + +void state_reboot (cpp) + void *cpp; +{ + struct client_state *client = cpp; + + /* If we don't remember an active lease, go straight to INIT. */ + if (!client -> active || + client -> active -> is_bootp || + client -> active -> expiry <= cur_time) { + state_init (client); + return; + } + + /* We are in the rebooting state. */ + client -> state = S_REBOOTING; + + /* make_request doesn't initialize xid because it normally comes + from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, + so pick an xid now. */ + client -> xid = random (); + + /* Make a DHCPREQUEST packet, and set appropriate per-interface + flags. */ + make_request (client, client -> active); + client -> destination = iaddr_broadcast; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + + /* Zap the medium list... */ + client -> medium = (struct string_list *)0; + + /* Send out the first DHCPREQUEST packet. */ + send_request (client); +} + +/* Called when a lease has completely expired and we've been unable to + renew it. */ + +void state_init (cpp) + void *cpp; +{ + struct client_state *client = cpp; + + ASSERT_STATE(state, S_INIT); + + /* Make a DHCPDISCOVER packet, and set appropriate per-interface + flags. */ + make_discover (client, client -> active); + client -> xid = client -> packet.xid; + client -> destination = iaddr_broadcast; + client -> state = S_SELECTING; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + + /* Add an immediate timeout to cause the first DHCPDISCOVER packet + to go out. */ + send_discover (client); +} + +/* state_selecting is called when one or more DHCPOFFER packets have been + received and a configurable period of time has passed. */ + +void state_selecting (cpp) + void *cpp; +{ + struct client_state *client = cpp; + struct client_lease *lp, *next, *picked; + + + ASSERT_STATE(state, S_SELECTING); + + /* Cancel state_selecting and send_discover timeouts, since either + one could have got us here. */ + cancel_timeout (state_selecting, client); + cancel_timeout (send_discover, client); + + /* We have received one or more DHCPOFFER packets. Currently, + the only criterion by which we judge leases is whether or + not we get a response when we arp for them. */ + picked = (struct client_lease *)0; + for (lp = client -> offered_leases; lp; lp = next) { + next = lp -> next; + + /* Check to see if we got an ARPREPLY for the address + in this particular lease. */ + if (!picked) { + picked = lp; + picked -> next = (struct client_lease *)0; + } else { + freeit: + destroy_client_lease (lp); + } + } + client -> offered_leases = (struct client_lease *)0; + + /* If we just tossed all the leases we were offered, go back + to square one. */ + if (!picked) { + client -> state = S_INIT; + state_init (client); + return; + } + + /* If it was a BOOTREPLY, we can just take the address right now. */ + if (picked -> is_bootp) { + client -> new = picked; + + /* Make up some lease expiry times + XXX these should be configurable. */ + client -> new -> expiry = cur_time + 12000; + client -> new -> renewal += cur_time + 8000; + client -> new -> rebind += cur_time + 10000; + + client -> state = S_REQUESTING; + + /* Bind to the address we received. */ + bind_lease (client); + return; + } + + /* Go to the REQUESTING state. */ + client -> destination = iaddr_broadcast; + client -> state = S_REQUESTING; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + + /* Make a DHCPREQUEST packet from the lease we picked. */ + make_request (client, picked); + client -> xid = client -> packet.xid; + + /* Toss the lease we picked - we'll get it back in a DHCPACK. */ + destroy_client_lease (picked); + + /* Add an immediate timeout to send the first DHCPREQUEST packet. */ + send_request (client); +} + +/* state_requesting is called when we receive a DHCPACK message after + having sent out one or more DHCPREQUEST packets. */ + +void dhcpack (packet) + struct packet *packet; +{ + struct interface_info *ip = packet -> interface; + struct client_state *client; + struct client_lease *lease; + struct option_cache *oc; + struct data_string ds; + int i; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + for (client = ip -> client; client; client = client -> next) { + if (client -> xid == packet -> raw -> xid) + break; + } + if (!client || + (packet -> interface -> hw_address.hlen - 1 != + packet -> raw -> hlen) || + (memcmp (&packet -> interface -> hw_address.hbuf [1], + packet -> raw -> chaddr, packet -> raw -> hlen))) { +#if defined (DEBUG) + log_debug ("DHCPACK in wrong transaction."); +#endif + return; + } + + if (client -> state != S_REBOOTING && + client -> state != S_REQUESTING && + client -> state != S_RENEWING && + client -> state != S_REBINDING) { +#if defined (DEBUG) + log_debug ("DHCPACK in wrong state."); +#endif + return; + } + + log_info ("DHCPACK from %s", piaddr (packet -> client_addr)); + + lease = packet_to_lease (packet, client); + if (!lease) { + log_info ("packet_to_lease failed."); + return; + } + + client -> new = lease; + + /* Stop resending DHCPREQUEST. */ + cancel_timeout (send_request, client); + + /* Figure out the lease time. */ + oc = lookup_option (&dhcp_universe, client -> new -> options, + DHO_DHCP_LEASE_TIME); + memset (&ds, 0, sizeof ds); + if (oc && + evaluate_option_cache (&ds, packet, (struct lease *)0, client, + packet -> options, client -> new -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) + client -> new -> expiry = getULong (ds.data); + else + client -> new -> expiry = 0; + data_string_forget (&ds, MDL); + } else + client -> new -> expiry = 0; + + if (!client -> new -> expiry) { + log_error ("no expiry time on offered lease."); + /* XXX this is going to be bad - if this _does_ + XXX happen, we should probably dynamically + XXX disqualify the DHCP server that gave us the + XXX bad packet from future selections and + XXX then go back into the init state. */ + state_init (client); + return; + } + + /* A number that looks negative here is really just very large, + because the lease expiry offset is unsigned. */ + if (client -> new -> expiry < 0) + client -> new -> expiry = TIME_MAX; + /* Take the server-provided renewal time if there is one. */ + oc = lookup_option (&dhcp_universe, client -> new -> options, + DHO_DHCP_RENEWAL_TIME); + if (oc && + evaluate_option_cache (&ds, packet, (struct lease *)0, client, + packet -> options, client -> new -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) + client -> new -> renewal = getULong (ds.data); + else + client -> new -> renewal = 0; + data_string_forget (&ds, MDL); + } else + client -> new -> renewal = 0; + + /* If it wasn't specified by the server, calculate it. */ + if (!client -> new -> renewal) + client -> new -> renewal = client -> new -> expiry / 2 + 1; + + if (client -> new -> renewal <= 0) + client -> new -> renewal = TIME_MAX; + + /* Now introduce some randomness to the renewal time: */ + if (client -> new -> renewal <= TIME_MAX / 3 - 3) + client -> new -> renewal = + (((client -> new -> renewal + 3) * 3 / 4) + + (random () % /* XXX NUMS */ + ((client -> new -> renewal + 3) / 4))); + + /* Same deal with the rebind time. */ + oc = lookup_option (&dhcp_universe, client -> new -> options, + DHO_DHCP_REBINDING_TIME); + if (oc && + evaluate_option_cache (&ds, packet, (struct lease *)0, client, + packet -> options, client -> new -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) + client -> new -> rebind = getULong (ds.data); + else + client -> new -> rebind = 0; + data_string_forget (&ds, MDL); + } else + client -> new -> rebind = 0; + + if (client -> new -> rebind <= 0) { + if (client -> new -> expiry <= TIME_MAX / 7) + client -> new -> rebind = + client -> new -> expiry * 7 / 8; + else + client -> new -> rebind = + client -> new -> expiry / 8 * 7; + } + + /* Make sure our randomness didn't run the renewal time past the + rebind time. */ + if (client -> new -> renewal > client -> new -> rebind) { + if (client -> new -> rebind <= TIME_MAX / 3) + client -> new -> renewal = + client -> new -> rebind * 3 / 4; + else + client -> new -> renewal = + client -> new -> rebind / 4 * 3; + } + + client -> new -> expiry += cur_time; + /* Lease lengths can never be negative. */ + if (client -> new -> expiry < cur_time) + client -> new -> expiry = TIME_MAX; + client -> new -> renewal += cur_time; + if (client -> new -> renewal < cur_time) + client -> new -> renewal = TIME_MAX; + client -> new -> rebind += cur_time; + if (client -> new -> rebind < cur_time) + client -> new -> rebind = TIME_MAX; + + bind_lease (client); +} + +void bind_lease (client) + struct client_state *client; +{ + struct interface_info *ip = client -> interface; + + /* Remember the medium. */ + client -> new -> medium = client -> medium; + + /* Run the client script with the new parameters. */ + script_init (client, (client -> state == S_REQUESTING + ? "BOUND" + : (client -> state == S_RENEWING + ? "RENEW" + : (client -> state == S_REBOOTING + ? "REBOOT" : "REBIND"))), + client -> new -> medium); + if (client -> active && client -> state != S_REBOOTING) + script_write_params (client, "old_", client -> active); + script_write_params (client, "new_", client -> new); + if (client -> alias) + script_write_params (client, "alias_", client -> alias); + + /* If the BOUND/RENEW code detects another machine using the + offered address, it exits nonzero. We need to send a + DHCPDECLINE and toss the lease. */ + if (script_go (client)) { + make_decline (client, client -> new); + send_decline (client); + destroy_client_lease (client -> new); + client -> new = (struct client_lease *)0; + state_init (client); + return; + } + + /* Write out the new lease. */ + write_client_lease (client, client -> new, 0, 0); + + /* Replace the old active lease with the new one. */ + if (client -> active) + destroy_client_lease (client -> active); + client -> active = client -> new; + client -> new = (struct client_lease *)0; + + /* Set up a timeout to start the renewal process. */ + add_timeout (client -> active -> renewal, + state_bound, client, 0, 0); + + log_info ("bound to %s -- renewal in %ld seconds.", + piaddr (client -> active -> address), + (long)(client -> active -> renewal - cur_time)); + client -> state = S_BOUND; + reinitialize_interfaces (); + go_daemon (); + if (client -> config -> do_forward_update) { + client -> dns_update_timeout = 1; + add_timeout (cur_time + 1, client_dns_update_timeout, + client, 0, 0); + } +} + +/* state_bound is called when we've successfully bound to a particular + lease, but the renewal time on that lease has expired. We are + expected to unicast a DHCPREQUEST to the server that gave us our + original lease. */ + +void state_bound (cpp) + void *cpp; +{ + struct client_state *client = cpp; + int i; + struct option_cache *oc; + struct data_string ds; + + ASSERT_STATE(state, S_BOUND); + + /* T1 has expired. */ + make_request (client, client -> active); + client -> xid = client -> packet.xid; + + memset (&ds, 0, sizeof ds); + oc = lookup_option (&dhcp_universe, client -> active -> options, + DHO_DHCP_SERVER_IDENTIFIER); + if (oc && + evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0, + client, (struct option_state *)0, + client -> active -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) { + memcpy (client -> destination.iabuf, ds.data, 4); + client -> destination.len = 4; + } else + client -> destination = iaddr_broadcast; + + data_string_forget (&ds, MDL); + } else + client -> destination = iaddr_broadcast; + + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + client -> state = S_RENEWING; + + /* Send the first packet immediately. */ + send_request (client); +} + +/* state_stop is called when we've been told to shut down. We unconfigure + the interfaces, and then stop operating until told otherwise. */ + +void state_stop (cpp) + void *cpp; +{ + struct client_state *client = cpp; + int i; + + /* Cancel all timeouts. */ + cancel_timeout (state_selecting, client); + cancel_timeout (send_discover, client); + cancel_timeout (send_request, client); + cancel_timeout (state_bound, client); + + /* If we have an address, unconfigure it. */ + if (client -> active) { + script_init (client, "STOP", client -> active -> medium); + script_write_params (client, "old_", client -> active); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_go (client); + } +} + +int commit_leases () +{ + return 0; +} + +int write_lease (lease) + struct lease *lease; +{ + return 0; +} + +int write_host (host) + struct host_decl *host; +{ + return 0; +} + +void db_startup (testp) + int testp; +{ +} + +void bootp (packet) + struct packet *packet; +{ + struct iaddrlist *ap; + + if (packet -> raw -> op != BOOTREPLY) + return; + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet -> interface -> client -> config -> reject_list; + ap; ap = ap -> next) { + if (addr_eq (packet -> client_addr, ap -> addr)) { + log_info ("BOOTREPLY from %s rejected.", + piaddr (ap -> addr)); + return; + } + } + + dhcpoffer (packet); + +} + +void dhcp (packet) + struct packet *packet; +{ + struct iaddrlist *ap; + void (*handler) PROTO ((struct packet *)); + const char *type; + + switch (packet -> packet_type) { + case DHCPOFFER: + handler = dhcpoffer; + type = "DHCPOFFER"; + break; + + case DHCPNAK: + handler = dhcpnak; + type = "DHCPNACK"; + break; + + case DHCPACK: + handler = dhcpack; + type = "DHCPACK"; + break; + + default: + return; + } + + /* If there's a reject list, make sure this packet's sender isn't + on it. */ + for (ap = packet -> interface -> client -> config -> reject_list; + ap; ap = ap -> next) { + if (addr_eq (packet -> client_addr, ap -> addr)) { + log_info ("%s from %s rejected.", + type, piaddr (ap -> addr)); + return; + } + } + (*handler) (packet); +} + +void dhcpoffer (packet) + struct packet *packet; +{ + struct interface_info *ip = packet -> interface; + struct client_state *client; + struct client_lease *lease, *lp; + int i; + int stop_selecting; + const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY"; + struct iaddrlist *ap; + struct option_cache *oc; + char obuf [1024]; + +#ifdef DEBUG_PACKET + dump_packet (packet); +#endif + + /* Find a client state that matches the xid... */ + for (client = ip -> client; client; client = client -> next) + if (client -> xid == packet -> raw -> xid) + break; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (!client || + client -> state != S_SELECTING || + (packet -> interface -> hw_address.hlen - 1 != + packet -> raw -> hlen) || + (memcmp (&packet -> interface -> hw_address.hbuf [1], + packet -> raw -> chaddr, packet -> raw -> hlen))) { +#if defined (DEBUG) + log_debug ("%s in wrong transaction.", name); +#endif + return; + } + + sprintf (obuf, "%s from %s", name, piaddr (packet -> client_addr)); + + + /* If this lease doesn't supply the minimum required parameters, + blow it off. */ + if (client -> config -> required_options) { + for (i = 0; client -> config -> required_options [i]; i++) { + if (!lookup_option + (&dhcp_universe, packet -> options, + client -> config -> required_options [i])) { + log_info ("%s: no %s option.", + obuf, (dhcp_universe.options + [client -> config -> required_options [i]] + -> name)); + return; + } + } + } + + /* If we've already seen this lease, don't record it again. */ + for (lease = client -> offered_leases; lease; lease = lease -> next) { + if (lease -> address.len == sizeof packet -> raw -> yiaddr && + !memcmp (lease -> address.iabuf, + &packet -> raw -> yiaddr, lease -> address.len)) { + log_debug ("%s: already seen.", obuf); + return; + } + } + + lease = packet_to_lease (packet, client); + if (!lease) { + log_info ("%s: packet_to_lease failed.", obuf); + return; + } + + /* If this lease was acquired through a BOOTREPLY, record that + fact. */ + if (!packet -> options_valid || !packet -> packet_type) + lease -> is_bootp = 1; + + /* Record the medium under which this lease was offered. */ + lease -> medium = client -> medium; + + /* Figure out when we're supposed to stop selecting. */ + stop_selecting = (client -> first_sending + + client -> config -> select_interval); + + /* If this is the lease we asked for, put it at the head of the + list, and don't mess with the arp request timeout. */ + if (lease -> address.len == client -> requested_address.len && + !memcmp (lease -> address.iabuf, + client -> requested_address.iabuf, + client -> requested_address.len)) { + lease -> next = client -> offered_leases; + client -> offered_leases = lease; + } else { + /* Put the lease at the end of the list. */ + lease -> next = (struct client_lease *)0; + if (!client -> offered_leases) + client -> offered_leases = lease; + else { + for (lp = client -> offered_leases; lp -> next; + lp = lp -> next) + ; + lp -> next = lease; + } + } + + /* If the selecting interval has expired, go immediately to + state_selecting(). Otherwise, time out into + state_selecting at the select interval. */ + if (stop_selecting <= 0) + state_selecting (client); + else { + add_timeout (stop_selecting, state_selecting, client, 0, 0); + cancel_timeout (send_discover, client); + } + log_info ("%s", obuf); +} + +/* Allocate a client_lease structure and initialize it from the parameters + in the specified packet. */ + +struct client_lease *packet_to_lease (packet, client) + struct packet *packet; + struct client_state *client; +{ + struct client_lease *lease; + unsigned i; + struct option_cache *oc; + struct data_string data; + + lease = (struct client_lease *)new_client_lease (MDL); + + if (!lease) { + log_error ("packet_to_lease: no memory to record lease.\n"); + return (struct client_lease *)0; + } + + memset (lease, 0, sizeof *lease); + + /* Copy the lease options. */ + option_state_reference (&lease -> options, packet -> options, MDL); + + lease -> address.len = sizeof (packet -> raw -> yiaddr); + memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr, + lease -> address.len); + + memset (&data, 0, sizeof data); + + if (client -> config -> vendor_space_name) { + i = DHO_VENDOR_ENCAPSULATED_OPTIONS; + + /* See if there was a vendor encapsulation option. */ + oc = lookup_option (&dhcp_universe, lease -> options, i); + if (oc && + client -> config -> vendor_space_name && + evaluate_option_cache (&data, packet, + (struct lease *)0, client, + packet -> options, lease -> options, + &global_scope, oc, MDL)) { + if (data.len) { + parse_encapsulated_suboptions + (packet -> options, &dhcp_options [i], + data.data, data.len, &dhcp_universe, + client -> config -> vendor_space_name + ); + } + data_string_forget (&data, MDL); + } + } else + i = 0; + + /* Figure out the overload flag. */ + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_OPTION_OVERLOAD); + if (oc && + evaluate_option_cache (&data, packet, (struct lease *)0, client, + packet -> options, lease -> options, + &global_scope, oc, MDL)) { + if (data.len > 0) + i = data.data [0]; + else + i = 0; + data_string_forget (&data, MDL); + } else + i = 0; + + /* If the server name was filled out, copy it. */ + if (!(i & 2) && packet -> raw -> sname [0]) { + unsigned len; + /* Don't count on the NUL terminator. */ + for (len = 0; len < 64; len++) + if (!packet -> raw -> sname [len]) + break; + lease -> server_name = dmalloc (len + 1, MDL); + if (!lease -> server_name) { + log_error ("dhcpoffer: no memory for filename.\n"); + destroy_client_lease (lease); + return (struct client_lease *)0; + } else { + memcpy (lease -> server_name, + packet -> raw -> sname, len); + lease -> server_name [len] = 0; + } + } + + /* Ditto for the filename. */ + if (!(i & 1) && packet -> raw -> file [0]) { + unsigned len; + /* Don't count on the NUL terminator. */ + for (len = 0; len < 64; len++) + if (!packet -> raw -> file [len]) + break; + lease -> filename = dmalloc (len + 1, MDL); + if (!lease -> filename) { + log_error ("dhcpoffer: no memory for filename.\n"); + destroy_client_lease (lease); + return (struct client_lease *)0; + } else { + memcpy (lease -> filename, + packet -> raw -> file, len); + lease -> filename [len] = 0; + } + } + + execute_statements_in_scope ((struct binding_value **)0, + (struct packet *)packet, + (struct lease *)0, client, + lease -> options, lease -> options, + &global_scope, + client -> config -> on_receipt, + (struct group *)0); + + return lease; +} + +void dhcpnak (packet) + struct packet *packet; +{ + struct interface_info *ip = packet -> interface; + struct client_state *client; + + /* Find a client state that matches the xid... */ + for (client = ip -> client; client; client = client -> next) + if (client -> xid == packet -> raw -> xid) + break; + + /* If we're not receptive to an offer right now, or if the offer + has an unrecognizable transaction id, then just drop it. */ + if (!client || + (packet -> interface -> hw_address.hlen - 1 != + packet -> raw -> hlen) || + (memcmp (&packet -> interface -> hw_address.hbuf [1], + packet -> raw -> chaddr, packet -> raw -> hlen))) { +#if defined (DEBUG) + log_debug ("DHCPNAK in wrong transaction."); +#endif + return; + } + + if (client -> state != S_REBOOTING && + client -> state != S_REQUESTING && + client -> state != S_RENEWING && + client -> state != S_REBINDING) { +#if defined (DEBUG) + log_debug ("DHCPNAK in wrong state."); +#endif + return; + } + + log_info ("DHCPNAK from %s", piaddr (packet -> client_addr)); + + if (!client -> active) { +#if defined (DEBUG) + log_info ("DHCPNAK with no active lease.\n"); +#endif + return; + } + + destroy_client_lease (client -> active); + client -> active = (struct client_lease *)0; + + /* Stop sending DHCPREQUEST packets... */ + cancel_timeout (send_request, client); + + client -> state = S_INIT; + state_init (client); +} + +/* Send out a DHCPDISCOVER packet, and set a timeout to send out another + one after the right interval has expired. If we don't get an offer by + the time we reach the panic interval, call the panic function. */ + +void send_discover (cpp) + void *cpp; +{ + struct client_state *client = cpp; + + int result; + int interval; + int increase = 1; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - client -> first_sending; + + /* If we're past the panic timeout, call the script and tell it + we haven't found anything for this interface yet. */ + if (interval > client -> config -> timeout) { + state_panic (client); + return; + } + + /* If we're selecting media, try the whole list before doing + the exponential backoff, but if we've already received an + offer, stop looping, because we obviously have it right. */ + if (!client -> offered_leases && + client -> config -> media) { + int fail = 0; + again: + if (client -> medium) { + client -> medium = client -> medium -> next; + increase = 0; + } + if (!client -> medium) { + if (fail) + log_fatal ("No valid media types for %s!", + client -> interface -> name); + client -> medium = + client -> config -> media; + increase = 1; + } + + log_info ("Trying medium \"%s\" %d", + client -> medium -> string, increase); + script_init (client, "MEDIUM", client -> medium); + if (script_go (client)) { + fail = 1; + goto again; + } + } + + /* If we're supposed to increase the interval, do so. If it's + currently zero (i.e., we haven't sent any packets yet), set + it to initial_interval; otherwise, add to it a random number + between zero and two times itself. On average, this means + that it will double with every transmission. */ + if (increase) { + if (!client -> interval) + client -> interval = + client -> config -> initial_interval; + else + client -> interval += ((random () >> 2) % + (2 * client -> interval)); + + /* Don't backoff past cutoff. */ + if (client -> interval > + client -> config -> backoff_cutoff) + client -> interval = + ((client -> config -> backoff_cutoff / 2) + + ((random () >> 2) % + client -> config -> backoff_cutoff)); + } else if (!client -> interval) + client -> interval = client -> config -> initial_interval; + + /* If the backoff would take us to the panic timeout, just use that + as the interval. */ + if (cur_time + client -> interval > + client -> first_sending + client -> config -> timeout) + client -> interval = + (client -> first_sending + + client -> config -> timeout) - cur_time + 1; + + /* Record the number of seconds since we started sending. */ + if (interval < 65536) + client -> packet.secs = htons (interval); + else + client -> packet.secs = htons (65535); + client -> secs = client -> packet.secs; + + log_info ("DHCPDISCOVER on %s to %s port %d interval %ld", + client -> name ? client -> name : client -> interface -> name, + inet_ntoa (sockaddr_broadcast.sin_addr), + ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval)); + + /* Send out a packet. */ + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, + inaddr_any, &sockaddr_broadcast, + (struct hardware *)0); + + add_timeout (cur_time + client -> interval, + send_discover, client, 0, 0); +} + +/* state_panic gets called if we haven't received any offers in a preset + amount of time. When this happens, we try to use existing leases that + haven't yet expired, and failing that, we call the client script and + hope it can do something. */ + +void state_panic (cpp) + void *cpp; +{ + struct client_state *client = cpp; + struct client_lease *loop; + struct client_lease *lp; + + loop = lp = client -> active; + + log_info ("No DHCPOFFERS received."); + + /* We may not have an active lease, but we may have some + predefined leases that we can try. */ + if (!client -> active && client -> leases) + goto activate_next; + + /* Run through the list of leases and see if one can be used. */ + while (client -> active) { + if (client -> active -> expiry > cur_time) { + log_info ("Trying recorded lease %s", + piaddr (client -> active -> address)); + /* Run the client script with the existing + parameters. */ + script_init (client, "TIMEOUT", + client -> active -> medium); + script_write_params (client, "new_", client -> active); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + + /* If the old lease is still good and doesn't + yet need renewal, go into BOUND state and + timeout at the renewal time. */ + if (!script_go (client)) { + if (cur_time < client -> active -> renewal) { + client -> state = S_BOUND; + log_info ("bound: renewal in %ld %s.", + (long)(client -> active -> renewal - + cur_time), "seconds"); + add_timeout (client -> active -> renewal, + state_bound, client, 0, 0); + } else { + client -> state = S_BOUND; + log_info ("bound: immediate renewal."); + state_bound (client); + } + reinitialize_interfaces (); + go_daemon (); + return; + } + } + + /* If there are no other leases, give up. */ + if (!client -> leases) { + client -> leases = client -> active; + client -> active = (struct client_lease *)0; + break; + } + + activate_next: + /* Otherwise, put the active lease at the end of the + lease list, and try another lease.. */ + for (lp = client -> leases; lp -> next; lp = lp -> next) + ; + lp -> next = client -> active; + if (lp -> next) { + lp -> next -> next = (struct client_lease *)0; + } + client -> active = client -> leases; + client -> leases = client -> leases -> next; + + /* If we already tried this lease, we've exhausted the + set of leases, so we might as well give up for + now. */ + if (client -> active == loop) + break; + else if (!loop) + loop = client -> active; + } + + /* No leases were available, or what was available didn't work, so + tell the shell script that we failed to allocate an address, + and try again later. */ + if (onetry) { + if (!quiet) + log_info ("Unable to obtain a lease on first try.%s", + " Exiting."); + exit (2); + } + + log_info ("No working leases in persistent database - sleeping."); + script_init (client, "FAIL", (struct string_list *)0); + if (client -> alias) + script_write_params (client, "alias_", client -> alias); + script_go (client); + client -> state = S_INIT; + add_timeout (cur_time + + ((client -> config -> retry_interval + 1) / 2 + + (random () % client -> config -> retry_interval)), + state_init, client, 0, 0); + go_daemon (); +} + +void send_request (cpp) + void *cpp; +{ + struct client_state *client = cpp; + + int result; + int interval; + struct sockaddr_in destination; + struct in_addr from; + + /* Figure out how long it's been since we started transmitting. */ + interval = cur_time - client -> first_sending; + + /* If we're in the INIT-REBOOT or REQUESTING state and we're + past the reboot timeout, go to INIT and see if we can + DISCOVER an address... */ + /* XXX In the INIT-REBOOT state, if we don't get an ACK, it + means either that we're on a network with no DHCP server, + or that our server is down. In the latter case, assuming + that there is a backup DHCP server, DHCPDISCOVER will get + us a new address, but we could also have successfully + reused our old address. In the former case, we're hosed + anyway. This is not a win-prone situation. */ + if ((client -> state == S_REBOOTING || + client -> state == S_REQUESTING) && + interval > client -> config -> reboot_timeout) { + cancel: + client -> state = S_INIT; + cancel_timeout (send_request, client); + state_init (client); + return; + } + + /* If we're in the reboot state, make sure the media is set up + correctly. */ + if (client -> state == S_REBOOTING && + !client -> medium && + client -> active -> medium ) { + script_init (client, "MEDIUM", client -> active -> medium); + + /* If the medium we chose won't fly, go to INIT state. */ + if (script_go (client)) + goto cancel; + + /* Record the medium. */ + client -> medium = client -> active -> medium; + } + + /* If the lease has expired, relinquish the address and go back + to the INIT state. */ + if (client -> state != S_REQUESTING && + cur_time > client -> active -> expiry) { + /* Run the client script with the new parameters. */ + script_init (client, "EXPIRE", (struct string_list *)0); + script_write_params (client, "old_", client -> active); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_go (client); + + /* Now do a preinit on the interface so that we can + discover a new address. */ + script_init (client, "PREINIT", (struct string_list *)0); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_go (client); + + client -> state = S_INIT; + state_init (client); + return; + } + + /* Do the exponential backoff... */ + if (!client -> interval) + client -> interval = client -> config -> initial_interval; + else { + client -> interval += ((random () >> 2) % + (2 * client -> interval)); + } + + /* Don't backoff past cutoff. */ + if (client -> interval > + client -> config -> backoff_cutoff) + client -> interval = + ((client -> config -> backoff_cutoff / 2) + + ((random () >> 2) % + client -> config -> backoff_cutoff)); + + /* If the backoff would take us to the expiry time, just set the + timeout to the expiry time. */ + if (client -> state != S_REQUESTING && + cur_time + client -> interval > client -> active -> expiry) + client -> interval = + client -> active -> expiry - cur_time + 1; + + /* If the lease T2 time has elapsed, or if we're not yet bound, + broadcast the DHCPREQUEST rather than unicasting. */ + if (client -> state == S_REQUESTING || + client -> state == S_REBOOTING || + cur_time > client -> active -> rebind) + destination.sin_addr = sockaddr_broadcast.sin_addr; + else + memcpy (&destination.sin_addr.s_addr, + client -> destination.iabuf, + sizeof destination.sin_addr.s_addr); + destination.sin_port = remote_port; + destination.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + destination.sin_len = sizeof destination; +#endif + + if (client -> state == S_RENEWING || + client -> state == S_REBINDING) + memcpy (&from, client -> active -> address.iabuf, + sizeof from); + else + from.s_addr = INADDR_ANY; + + /* Record the number of seconds since we started sending. */ + if (client -> state == S_REQUESTING) + client -> packet.secs = client -> secs; + else { + if (interval < 65536) + client -> packet.secs = htons (interval); + else + client -> packet.secs = htons (65535); + } + + log_info ("DHCPREQUEST on %s to %s port %d", + client -> name ? client -> name : client -> interface -> name, + inet_ntoa (destination.sin_addr), + ntohs (destination.sin_port)); + + if (destination.sin_addr.s_addr != INADDR_BROADCAST && + fallback_interface) + result = send_packet (fallback_interface, + (struct packet *)0, + &client -> packet, + client -> packet_length, + from, &destination, + (struct hardware *)0); + else + /* Send out a packet. */ + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, + from, &destination, + (struct hardware *)0); + + add_timeout (cur_time + client -> interval, + send_request, client, 0, 0); +} + +void send_decline (cpp) + void *cpp; +{ + struct client_state *client = cpp; + + int result; + + log_info ("DHCPDECLINE on %s to %s port %d", + client -> name ? client -> name : client -> interface -> name, + inet_ntoa (sockaddr_broadcast.sin_addr), + ntohs (sockaddr_broadcast.sin_port)); + + /* Send out a packet. */ + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, + inaddr_any, &sockaddr_broadcast, + (struct hardware *)0); +} + +void send_release (cpp) + void *cpp; +{ + struct client_state *client = cpp; + + int result; + struct sockaddr_in destination; + struct in_addr from; + + memcpy (&from, client -> active -> address.iabuf, + sizeof from); + memcpy (&destination.sin_addr.s_addr, + client -> destination.iabuf, + sizeof destination.sin_addr.s_addr); + destination.sin_port = remote_port; + destination.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + destination.sin_len = sizeof destination; +#endif + + /* Set the lease to end now, so that we don't accidentally + reuse it if we restart before the old expiry time. */ + client -> active -> expiry = + client -> active -> renewal = + client -> active -> rebind = cur_time; + if (!write_client_lease (client, client -> active, 1, 1)) { + log_error ("Can't release lease: lease write failed."); + return; + } + + log_info ("DHCPRELEASE on %s to %s port %d", + client -> name ? client -> name : client -> interface -> name, + inet_ntoa (destination.sin_addr), + ntohs (destination.sin_port)); + + if (fallback_interface) + result = send_packet (fallback_interface, + (struct packet *)0, + &client -> packet, + client -> packet_length, + from, &destination, + (struct hardware *)0); + else + /* Send out a packet. */ + result = send_packet (client -> interface, (struct packet *)0, + &client -> packet, + client -> packet_length, + from, &destination, + (struct hardware *)0); +} + +void make_client_options (client, lease, type, sid, rip, prl, op) + struct client_state *client; + struct client_lease *lease; + u_int8_t *type; + struct option_cache *sid; + struct iaddr *rip; + u_int32_t *prl; + struct option_state **op; +{ + unsigned i; + struct option_cache *oc; + struct buffer *bp = (struct buffer *)0; + + /* If there are any leftover options, get rid of them. */ + if (*op) + option_state_dereference (op, MDL); + + /* Allocate space for options. */ + option_state_allocate (op, MDL); + + /* Send the server identifier if provided. */ + if (sid) + save_option (&dhcp_universe, *op, sid); + + oc = (struct option_cache *)0; + + /* Send the requested address if provided. */ + if (rip) { + client -> requested_address = *rip; + if (!(make_const_option_cache + (&oc, (struct buffer **)0, rip -> iabuf, rip -> len, + &dhcp_options [DHO_DHCP_REQUESTED_ADDRESS], MDL))) + log_error ("can't make requested address cache."); + else { + save_option (&dhcp_universe, *op, oc); + option_cache_dereference (&oc, MDL); + } + } else { + client -> requested_address.len = 0; + } + + if (!(make_const_option_cache + (&oc, (struct buffer **)0, + type, 1, &dhcp_options [DHO_DHCP_MESSAGE_TYPE], MDL))) + log_error ("can't make message type."); + else { + save_option (&dhcp_universe, *op, oc); + option_cache_dereference (&oc, MDL); + } + + if (prl) { + /* Figure out how many parameters were requested. */ + for (i = 0; prl [i]; i++) + ; + if (!buffer_allocate (&bp, i, MDL)) + log_error ("can't make parameter list buffer."); + else { + for (i = 0; prl [i]; i++) + bp -> data [i] = prl [i]; + if (!(make_const_option_cache + (&oc, &bp, (u_int8_t *)0, i, + &dhcp_options [DHO_DHCP_PARAMETER_REQUEST_LIST], + MDL))) + log_error ("can't make option cache"); + else { + save_option (&dhcp_universe, *op, oc); + option_cache_dereference (&oc, MDL); + } + } + } + + /* Run statements that need to be run on transmission. */ + if (client -> config -> on_transmission) + execute_statements_in_scope + ((struct binding_value **)0, + (struct packet *)0, (struct lease *)0, client, + (lease ? lease -> options : (struct option_state *)0), + *op, &global_scope, + client -> config -> on_transmission, + (struct group *)0); +} + +void make_discover (client, lease) + struct client_state *client; + struct client_lease *lease; +{ + unsigned char discover = DHCPDISCOVER; + int i; + struct option_state *options = (struct option_state *)0; + + memset (&client -> packet, 0, sizeof (client -> packet)); + + make_client_options (client, + lease, &discover, (struct option_cache *)0, + lease ? &lease -> address : (struct iaddr *)0, + client -> config -> requested_options, + &options); + + /* Set up the option buffer... */ + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, + /* maximum packet size */1500, + (struct option_state *)0, + options, + /* scope */ &global_scope, + /* overload */ 0, + /* terminate */0, + /* bootpp */0, + (struct data_string *)0, + client -> config -> vendor_space_name); + + option_state_dereference (&options, MDL); + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; + + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = random (); + client -> packet.secs = 0; /* filled in by send_discover. */ + + if (can_receive_unicast_unconfigured (client -> interface)) + client -> packet.flags = 0; + else + client -> packet.flags = htons (BOOTP_BROADCAST); + + memset (&(client -> packet.ciaddr), + 0, sizeof client -> packet.ciaddr); + memset (&(client -> packet.yiaddr), + 0, sizeof client -> packet.yiaddr); + memset (&(client -> packet.siaddr), + 0, sizeof client -> packet.siaddr); + client -> packet.giaddr = giaddr; + if (client -> interface -> hw_address.hlen > 0) + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + (unsigned)(client -> interface -> hw_address.hlen - 1)); + +#ifdef DEBUG_PACKET + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); +#endif +} + + +void make_request (client, lease) + struct client_state *client; + struct client_lease *lease; +{ + unsigned char request = DHCPREQUEST; + int i, j; + unsigned char *tmp, *digest; + unsigned char *old_digest_loc; + struct option_cache *oc; + + memset (&client -> packet, 0, sizeof (client -> packet)); + + if (client -> state == S_REQUESTING) + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_SERVER_IDENTIFIER); + else + oc = (struct option_cache *)0; + + if (client -> sent_options) + option_state_dereference (&client -> sent_options, MDL); + + make_client_options (client, lease, &request, oc, + ((client -> state == S_REQUESTING || + client -> state == S_REBOOTING) + ? &lease -> address + : (struct iaddr *)0), + client -> config -> requested_options, + &client -> sent_options); + + /* Set up the option buffer... */ + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, + /* maximum packet size */1500, + (struct option_state *)0, + client -> sent_options, + /* scope */ &global_scope, + /* overload */ 0, + /* terminate */0, + /* bootpp */0, + (struct data_string *)0, + client -> config -> vendor_space_name); + + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; + + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = client -> xid; + client -> packet.secs = 0; /* Filled in by send_request. */ + + /* If we own the address we're requesting, put it in ciaddr; + otherwise set ciaddr to zero. */ + if (client -> state == S_BOUND || + client -> state == S_RENEWING || + client -> state == S_REBINDING) { + memcpy (&client -> packet.ciaddr, + lease -> address.iabuf, lease -> address.len); + client -> packet.flags = 0; + } else { + memset (&client -> packet.ciaddr, 0, + sizeof client -> packet.ciaddr); + if (can_receive_unicast_unconfigured (client -> interface)) + client -> packet.flags = 0; + else + client -> packet.flags = htons (BOOTP_BROADCAST); + } + + memset (&client -> packet.yiaddr, 0, + sizeof client -> packet.yiaddr); + memset (&client -> packet.siaddr, 0, + sizeof client -> packet.siaddr); + if (client -> state != S_BOUND && + client -> state != S_RENEWING) + client -> packet.giaddr = giaddr; + else + memset (&client -> packet.giaddr, 0, + sizeof client -> packet.giaddr); + if (client -> interface -> hw_address.hlen > 0) + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + (unsigned)(client -> interface -> hw_address.hlen - 1)); + +#ifdef DEBUG_PACKET + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); +#endif +} + +void make_decline (client, lease) + struct client_state *client; + struct client_lease *lease; +{ + unsigned char decline = DHCPDECLINE; + int i; + struct option_cache *oc; + + struct option_state *options = (struct option_state *)0; + + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_SERVER_IDENTIFIER); + make_client_options (client, lease, &decline, oc, + &lease -> address, (u_int32_t *)0, &options); + + /* Set up the option buffer... */ + memset (&client -> packet, 0, sizeof (client -> packet)); + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, 0, + (struct option_state *)0, options, + &global_scope, 0, 0, 0, (struct data_string *)0, + client -> config -> vendor_space_name); + option_state_dereference (&options, MDL); + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; + option_state_dereference (&options, MDL); + + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = client -> xid; + client -> packet.secs = 0; /* Filled in by send_request. */ + if (can_receive_unicast_unconfigured (client -> interface)) + client -> packet.flags = 0; + else + client -> packet.flags = htons (BOOTP_BROADCAST); + + /* ciaddr must always be zero. */ + memset (&client -> packet.ciaddr, 0, + sizeof client -> packet.ciaddr); + memset (&client -> packet.yiaddr, 0, + sizeof client -> packet.yiaddr); + memset (&client -> packet.siaddr, 0, + sizeof client -> packet.siaddr); + client -> packet.giaddr = giaddr; + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + client -> interface -> hw_address.hlen); + +#ifdef DEBUG_PACKET + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); +#endif +} + +void make_release (client, lease) + struct client_state *client; + struct client_lease *lease; +{ + unsigned char request = DHCPRELEASE; + int i; + struct option_cache *oc; + + struct option_state *options = (struct option_state *)0; + + memset (&client -> packet, 0, sizeof (client -> packet)); + + oc = lookup_option (&dhcp_universe, lease -> options, + DHO_DHCP_SERVER_IDENTIFIER); + make_client_options (client, lease, &request, oc, + (struct iaddr *)0, (u_int32_t *)0, + &options); + + /* Set up the option buffer... */ + client -> packet_length = + cons_options ((struct packet *)0, &client -> packet, + (struct lease *)0, client, + /* maximum packet size */1500, + (struct option_state *)0, + options, + /* scope */ &global_scope, + /* overload */ 0, + /* terminate */0, + /* bootpp */0, + (struct data_string *)0, + client -> config -> vendor_space_name); + + if (client -> packet_length < BOOTP_MIN_LEN) + client -> packet_length = BOOTP_MIN_LEN; + option_state_dereference (&options, MDL); + + client -> packet.op = BOOTREQUEST; + client -> packet.htype = client -> interface -> hw_address.hbuf [0]; + client -> packet.hlen = client -> interface -> hw_address.hlen - 1; + client -> packet.hops = 0; + client -> packet.xid = random (); + client -> packet.secs = 0; + client -> packet.flags = 0; + memcpy (&client -> packet.ciaddr, + lease -> address.iabuf, lease -> address.len); + memset (&client -> packet.yiaddr, 0, + sizeof client -> packet.yiaddr); + memset (&client -> packet.siaddr, 0, + sizeof client -> packet.siaddr); + client -> packet.giaddr = giaddr; + memcpy (client -> packet.chaddr, + &client -> interface -> hw_address.hbuf [1], + client -> interface -> hw_address.hlen); + +#ifdef DEBUG_PACKET + dump_raw ((unsigned char *)&client -> packet, client -> packet_length); +#endif +} + +void destroy_client_lease (lease) + struct client_lease *lease; +{ + int i; + + if (lease -> server_name) + dfree (lease -> server_name, MDL); + if (lease -> filename) + dfree (lease -> filename, MDL); + option_state_dereference (&lease -> options, MDL); + free_client_lease (lease, MDL); +} + +FILE *leaseFile; + +void rewrite_client_leases () +{ + struct interface_info *ip; + struct client_state *client; + struct client_lease *lp; + + if (leaseFile) + fclose (leaseFile); + leaseFile = fopen (path_dhclient_db, "w"); + if (!leaseFile) { + log_error ("can't create %s: %m", path_dhclient_db); + return; + } + + /* Write out all the leases attached to configured interfaces that + we know about. */ + for (ip = interfaces; ip; ip = ip -> next) { + for (client = ip -> client; client; client = client -> next) { + for (lp = client -> leases; lp; lp = lp -> next) { + write_client_lease (client, lp, 1, 0); + } + if (client -> active) + write_client_lease (client, + client -> active, 1, 0); + } + } + + /* Write out any leases that are attached to interfaces that aren't + currently configured. */ + for (ip = dummy_interfaces; ip; ip = ip -> next) { + for (client = ip -> client; client; client = client -> next) { + for (lp = client -> leases; lp; lp = lp -> next) { + write_client_lease (client, lp, 1, 0); + } + if (client -> active) + write_client_lease (client, + client -> active, 1, 0); + } + } + fflush (leaseFile); +} + +void write_lease_option (struct option_cache *oc, + struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff) +{ + const char *name, *dot; + struct data_string ds; + int status; + struct client_state *client; + + memset (&ds, 0, sizeof ds); + + if (u != &dhcp_universe) { + name = u -> name; + dot = "."; + } else { + name = ""; + dot = ""; + } + if (evaluate_option_cache (&ds, packet, lease, client_state, + in_options, cfg_options, scope, oc, MDL)) { + fprintf (leaseFile, + " option %s%s%s %s;\n", + name, dot, oc -> option -> name, + pretty_print_option (oc -> option, + ds.data, ds.len, 1, 1)); + data_string_forget (&ds, MDL); + } +} + +int write_client_lease (client, lease, rewrite, makesure) + struct client_state *client; + struct client_lease *lease; + int rewrite; + int makesure; +{ + int i; + struct tm *t; + static int leases_written; + struct option_cache *oc; + struct data_string ds; + pair *hash; + int errors = 0; + char *s; + + if (!rewrite) { + if (leases_written++ > 20) { + rewrite_client_leases (); + leases_written = 0; + } + } + + /* If the lease came from the config file, we don't need to stash + a copy in the lease database. */ + if (lease -> is_static) + return 1; + + if (!leaseFile) { /* XXX */ + leaseFile = fopen (path_dhclient_db, "w"); + if (!leaseFile) { + log_error ("can't create %s: %m", path_dhclient_db); + return 0; + } + } + + errno = 0; + fprintf (leaseFile, "lease {\n"); + if (lease -> is_bootp) { + fprintf (leaseFile, " bootp;\n"); + if (errno) { + ++errors; + errno = 0; + } + } + fprintf (leaseFile, " interface \"%s\";\n", + client -> interface -> name); + if (errno) { + ++errors; + errno = 0; + } + if (client -> name) { + fprintf (leaseFile, " name \"%s\";\n", client -> name); + if (errno) { + ++errors; + errno = 0; + } + } + fprintf (leaseFile, " fixed-address %s;\n", + piaddr (lease -> address)); + if (errno) { + ++errors; + errno = 0; + } + if (lease -> filename) { + s = quotify_string (lease -> filename, MDL); + if (s) { + fprintf (leaseFile, " filename \"%s\";\n", s); + if (errno) { + ++errors; + errno = 0; + } + dfree (s, MDL); + } else + errors++; + + } + if (lease -> server_name) { + s = quotify_string (lease -> filename, MDL); + if (s) { + fprintf (leaseFile, " server-name \"%s\";\n", s); + if (errno) { + ++errors; + errno = 0; + } + dfree (s, MDL); + } else + ++errors; + } + if (lease -> medium) { + s = quotify_string (lease -> medium -> string, MDL); + if (s) { + fprintf (leaseFile, " medium \"%s\";\n", s); + if (errno) { + ++errors; + errno = 0; + } + dfree (s, MDL); + } else + errors++; + } + if (errno != 0) { + errors++; + errno = 0; + } + + memset (&ds, 0, sizeof ds); + + for (i = 0; i < lease -> options -> universe_count; i++) { + option_space_foreach ((struct packet *)0, (struct lease *)0, + client, (struct option_state *)0, + lease -> options, &global_scope, + universes [i], + client, write_lease_option); + } + + /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until + somebody invents a time machine, I think we can safely disregard + it. */ + t = gmtime (&lease -> renewal); + fprintf (leaseFile, + " renew %d %d/%d/%d %02d:%02d:%02d;\n", + t -> tm_wday, t -> tm_year + 1900, + t -> tm_mon + 1, t -> tm_mday, + t -> tm_hour, t -> tm_min, t -> tm_sec); + if (errno != 0) { + errors++; + errno = 0; + } + t = gmtime (&lease -> rebind); + fprintf (leaseFile, + " rebind %d %d/%d/%d %02d:%02d:%02d;\n", + t -> tm_wday, t -> tm_year + 1900, + t -> tm_mon + 1, t -> tm_mday, + t -> tm_hour, t -> tm_min, t -> tm_sec); + if (errno != 0) { + errors++; + errno = 0; + } + t = gmtime (&lease -> expiry); + fprintf (leaseFile, + " expire %d %d/%d/%d %02d:%02d:%02d;\n", + t -> tm_wday, t -> tm_year + 1900, + t -> tm_mon + 1, t -> tm_mday, + t -> tm_hour, t -> tm_min, t -> tm_sec); + if (errno != 0) { + errors++; + errno = 0; + } + fprintf (leaseFile, "}\n"); + if (errno != 0) { + errors++; + errno = 0; + } + fflush (leaseFile); + if (errno != 0) { + errors++; + errno = 0; + } + if (!errors && makesure) { + if (fsync (fileno (leaseFile)) < 0) { + log_info ("write_client_lease: %m"); + return 0; + } + } + return errors ? 0 : 1; +} + +/* Variables holding name of script and file pointer for writing to + script. Needless to say, this is not reentrant - only one script + can be invoked at a time. */ +char scriptName [256]; +FILE *scriptFile; + +void script_init (client, reason, medium) + struct client_state *client; + const char *reason; + struct string_list *medium; +{ + struct string_list *sl, *next; + + if (client) { + for (sl = client -> env; sl; sl = next) { + next = sl -> next; + dfree (sl, MDL); + } + client -> env = (struct string_list *)0; + client -> envc = 0; + + if (client -> interface) { + client_envadd (client, "", "interface", "%s", + client -> interface -> name); + } + if (client -> name) + client_envadd (client, + "", "client", "%s", client -> name); + if (medium) + client_envadd (client, + "", "medium", "%s", medium -> string); + + client_envadd (client, "", "reason", "%s", reason); + client_envadd (client, "", "pid", "%ld", (long int)getpid ()); + } +} + +struct envadd_state { + struct client_state *client; + const char *prefix; +}; + +void client_option_envadd (struct option_cache *oc, + struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff) +{ + struct envadd_state *es = stuff; + struct data_string data; + memset (&data, 0, sizeof data); + + if (evaluate_option_cache (&data, packet, lease, client_state, + in_options, cfg_options, scope, oc, MDL)) { + if (data.len) { + char name [256]; + if (dhcp_option_ev_name (name, sizeof name, + oc -> option)) { + client_envadd (es -> client, es -> prefix, + name, "%s", + (pretty_print_option + (oc -> option, + data.data, data.len, + 0, 0))); + data_string_forget (&data, MDL); + } + } + } +} + +void script_write_params (client, prefix, lease) + struct client_state *client; + const char *prefix; + struct client_lease *lease; +{ + int i; + struct data_string data; + struct option_cache *oc; + pair *hash; + char *s, *t; + struct envadd_state es; + + es.client = client; + es.prefix = prefix; + + client_envadd (client, + prefix, "ip_address", "%s", piaddr (lease -> address)); + + /* For the benefit of Linux (and operating systems which may + have similar needs), compute the network address based on + the supplied ip address and netmask, if provided. Also + compute the broadcast address (the host address all ones + broadcast address, not the host address all zeroes + broadcast address). */ + + memset (&data, 0, sizeof data); + oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK); + if (oc && evaluate_option_cache (&data, (struct packet *)0, + (struct lease *)0, client, + (struct option_state *)0, + lease -> options, + &global_scope, oc, MDL)) { + if (data.len > 3) { + struct iaddr netmask, subnet, broadcast; + + memcpy (netmask.iabuf, data.data, data.len); + netmask.len = data.len; + data_string_forget (&data, MDL); + + subnet = subnet_number (lease -> address, netmask); + if (subnet.len) { + client_envadd (client, prefix, "network_number", + "%s", piaddr (subnet)); + + oc = lookup_option (&dhcp_universe, + lease -> options, + DHO_BROADCAST_ADDRESS); + if (!oc || + !(evaluate_option_cache + (&data, (struct packet *)0, + (struct lease *)0, client, + (struct option_state *)0, + lease -> options, + &global_scope, oc, MDL))) { + broadcast = broadcast_addr (subnet, netmask); + if (broadcast.len) { + client_envadd (client, + prefix, "broadcast_address", + "%s", piaddr (broadcast)); + } + } + } + } + data_string_forget (&data, MDL); + } + + if (lease -> filename) + client_envadd (client, + prefix, "filename", "%s", lease -> filename); + if (lease -> server_name) + client_envadd (client, prefix, "server_name", + "%s", lease -> server_name); + + for (i = 0; i < lease -> options -> universe_count; i++) { + option_space_foreach ((struct packet *)0, (struct lease *)0, + client, (struct option_state *)0, + lease -> options, &global_scope, + universes [i], + &es, client_option_envadd); + } + client_envadd (client, prefix, "expiry", "%d", (int)(lease -> expiry)); +} + +int script_go (client) + struct client_state *client; +{ + int rval; + char *scriptName; + char *argv [2]; + char **envp; + char *epp [3]; + char reason [] = "REASON=NBI"; + static char client_path [] = CLIENT_PATH; + int i; + struct string_list *sp, *next; + int pid, wpid, wstatus; + + if (client) + scriptName = client -> config -> script_name; + else + scriptName = top_level_config.script_name; + + envp = dmalloc (((client ? client -> envc : 2) + + client_env_count + 2) * sizeof (char *), MDL); + if (!envp) { + log_error ("No memory for client script environment."); + return 0; + } + i = 0; + /* Copy out the environment specified on the command line, + if any. */ + for (sp = client_env; sp; sp = sp -> next) { + envp [i++] = sp -> string; + } + /* Copy out the environment specified by dhclient. */ + if (client) { + for (sp = client -> env; sp; sp = sp -> next) { + envp [i++] = sp -> string; + } + } else { + envp [i++] = reason; + } + /* Set $PATH. */ + envp [i++] = client_path; + envp [i] = (char *)0; + + argv [0] = scriptName; + argv [1] = (char *)0; + + pid = fork (); + if (pid < 0) { + log_error ("fork: %m"); + wstatus = 0; + } else if (pid) { + do { + wpid = wait (&wstatus); + } while (wpid != pid && wpid > 0); + if (wpid < 0) { + log_error ("wait: %m"); + wstatus = 0; + } + } else { + execve (scriptName, argv, envp); + log_error ("execve (%s, ...): %m", scriptName); + exit (0); + } + + if (client) { + for (sp = client -> env; sp; sp = next) { + next = sp -> next; + dfree (sp, MDL); + } + client -> env = (struct string_list *)0; + client -> envc = 0; + } + dfree (envp, MDL); + GET_TIME (&cur_time); + return (WIFEXITED (wstatus) ? + WEXITSTATUS (wstatus) : -WTERMSIG (wstatus)); +} + +void client_envadd (struct client_state *client, + const char *prefix, const char *name, const char *fmt, ...) +{ + char spbuf [1024]; + char *s; + unsigned len, i; + struct string_list *val; + va_list list; + + va_start (list, fmt); + len = vsnprintf (spbuf, sizeof spbuf, fmt, list); + va_end (list); + + val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ + + len + sizeof *val, MDL); + if (!val) + return; + s = val -> string; + strcpy (s, prefix); + strcat (s, name); + s += strlen (s); + *s++ = '='; + if (len >= sizeof spbuf) { + va_start (list, fmt); + vsnprintf (s, len + 1, fmt, list); + va_end (list); + } else + strcpy (s, spbuf); + val -> next = client -> env; + client -> env = val; + client -> envc++; +} + +int dhcp_option_ev_name (buf, buflen, option) + char *buf; + size_t buflen; + struct option *option; +{ + int i, j; + const char *s; + + j = 0; + if (option -> universe != &dhcp_universe) { + s = option -> universe -> name; + i = 0; + } else { + s = option -> name; + i = 1; + } + + do { + while (*s) { + if (j + 1 == buflen) + return 0; + if (*s == '-') + buf [j++] = '_'; + else + buf [j++] = *s; + ++s; + } + if (!i) { + s = option -> name; + if (j + 1 == buflen) + return 0; + buf [j++] = '_'; + } + ++i; + } while (i != 2); + + buf [j] = 0; + return 1; +} + +void go_daemon () +{ + static int state = 0; + int pid; + int i; + + /* Don't become a daemon if the user requested otherwise. */ + if (no_daemon) { + write_client_pid_file (); + return; + } + + /* Only do it once. */ + if (state) + return; + state = 1; + + /* Stop logging to stderr... */ + log_perror = 0; + + /* Become a daemon... */ + if ((pid = fork ()) < 0) + log_fatal ("Can't fork daemon: %m"); + else if (pid) + exit (0); + /* Become session leader and get pid... */ + pid = setsid (); + + /* Close standard I/O descriptors. */ + close(0); + close(1); + close(2); + + /* Reopen them on /dev/null. */ + i = open ("/dev/null", O_RDWR); + if (i == 0) + i = open ("/dev/null", O_RDWR); + if (i == 1) { + i = open ("/dev/null", O_RDWR); + log_perror = 0; /* No sense logging to /dev/null. */ + } else if (i != -1) + close (i); + + write_client_pid_file (); +} + +void write_client_pid_file () +{ + FILE *pf; + int pfdesc; + + pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644); + + if (pfdesc < 0) { + log_error ("Can't create %s: %m", path_dhclient_pid); + return; + } + + pf = fdopen (pfdesc, "w"); + if (!pf) + log_error ("Can't fdopen %s: %m", path_dhclient_pid); + else { + fprintf (pf, "%ld\n", (long)getpid ()); + fclose (pf); + } +} + +void client_location_changed () +{ + struct interface_info *ip; + struct client_state *client; + + for (ip = interfaces; ip; ip = ip -> next) { + for (client = ip -> client; client; client = client -> next) { + switch (client -> state) { + case S_SELECTING: + cancel_timeout (send_discover, client); + break; + + case S_BOUND: + cancel_timeout (state_bound, client); + break; + + case S_REBOOTING: + case S_REQUESTING: + case S_RENEWING: + cancel_timeout (send_request, client); + break; + + case S_INIT: + case S_REBINDING: + case S_STOPPED: + break; + } + client -> state = S_INIT; + state_reboot (client); + } + } +} + +void do_release(client) + struct client_state *client; +{ + struct data_string ds; + struct option_cache *oc; + + /* Pick a random xid. */ + client -> xid = random (); + + /* is there even a lease to release? */ + if (client -> active) { + /* Make a DHCPRELEASE packet, and set appropriate per-interface + flags. */ + make_release (client, client -> active); + + memset (&ds, 0, sizeof ds); + oc = lookup_option (&dhcp_universe, + client -> active -> options, + DHO_DHCP_SERVER_IDENTIFIER); + if (oc && + evaluate_option_cache (&ds, (struct packet *)0, + (struct lease *)0, client, + (struct option_state *)0, + client -> active -> options, + &global_scope, oc, MDL)) { + if (ds.len > 3) { + memcpy (client -> destination.iabuf, + ds.data, 4); + client -> destination.len = 4; + } else + client -> destination = iaddr_broadcast; + + data_string_forget (&ds, MDL); + } else + client -> destination = iaddr_broadcast; + client -> first_sending = cur_time; + client -> interval = client -> config -> initial_interval; + + /* Zap the medium list... */ + client -> medium = (struct string_list *)0; + + /* Send out the first and only DHCPRELEASE packet. */ + send_release (client); + + /* Do the client script RELEASE operation. */ + script_init (client, + "RELEASE", (struct string_list *)0); + if (client -> alias) + script_write_params (client, "alias_", + client -> alias); + script_write_params (client, "old_", client -> active); + script_go (client); + } + + /* Cancel any timeouts. */ + cancel_timeout (state_bound, client); + cancel_timeout (send_discover, client); + cancel_timeout (state_init, client); + cancel_timeout (send_request, client); + cancel_timeout (state_reboot, client); + client -> state = S_STOPPED; +} + +int dhclient_interface_shutdown_hook (struct interface_info *interface) +{ + do_release (interface -> client); + + return 1; +} + +int dhclient_interface_discovery_hook (struct interface_info *tmp) +{ + struct interface_info *last, *ip; + /* See if we can find the client from dummy_interfaces */ + last = 0; + for (ip = dummy_interfaces; ip; ip = ip -> next) { + if (!strcmp (ip -> name, tmp -> name)) { + /* Remove from dummy_interfaces */ + if (last) { + ip = (struct interface_info *)0; + interface_reference (&ip, last -> next, MDL); + interface_dereference (&last -> next, MDL); + if (ip -> next) { + interface_reference (&last -> next, + ip -> next, MDL); + interface_dereference (&ip -> next, + MDL); + } + } else { + ip = (struct interface_info *)0; + interface_reference (&ip, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + if (ip -> next) { + interface_reference (&dummy_interfaces, + ip -> next, MDL); + interface_dereference (&ip -> next, + MDL); + } + } + /* Copy "client" to tmp */ + if (ip -> client) { + tmp -> client = ip -> client; + tmp -> client -> interface = tmp; + } + interface_dereference (&ip, MDL); + break; + } + last = ip; + } + return 1; +} + +isc_result_t dhclient_interface_startup_hook (struct interface_info *interface) +{ + struct interface_info *ip; + struct client_state *client; + + /* This code needs some rethinking. It doesn't test against + a signal name, and it just kind of bulls into doing something + that may or may not be appropriate. */ + + if (interfaces) { + interface_reference (&interface -> next, interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, interface, MDL); + + discover_interfaces (DISCOVER_UNCONFIGURED); + + for (ip = interfaces; ip; ip = ip -> next) { + /* If interfaces were specified, don't configure + interfaces that weren't specified! */ + if (ip -> flags & INTERFACE_RUNNING || + (ip -> flags & (INTERFACE_REQUESTED | + INTERFACE_AUTOMATIC)) != + INTERFACE_REQUESTED) + continue; + script_init (ip -> client, + "PREINIT", (struct string_list *)0); + if (ip -> client -> alias) + script_write_params (ip -> client, "alias_", + ip -> client -> alias); + script_go (ip -> client); + } + + discover_interfaces (interfaces_requested + ? DISCOVER_REQUESTED + : DISCOVER_RUNNING); + + for (ip = interfaces; ip; ip = ip -> next) { + if (ip -> flags & INTERFACE_RUNNING) + continue; + ip -> flags |= INTERFACE_RUNNING; + for (client = ip -> client; client; client = client -> next) { + client -> state = S_INIT; + /* Set up a timeout to start the initialization + process. */ + add_timeout (cur_time + random () % 5, + state_reboot, client, 0, 0); + } + } + return ISC_R_SUCCESS; +} + +/* The client should never receive a relay agent information option, + so if it does, log it and discard it. */ + +int parse_agent_information_option (packet, len, data) + struct packet *packet; + int len; + u_int8_t *data; +{ + return 1; +} + +/* The client never sends relay agent information options. */ + +unsigned cons_agent_information_options (cfg_options, outpacket, + agentix, length) + struct option_state *cfg_options; + struct dhcp_packet *outpacket; + unsigned agentix; + unsigned length; +{ + return length; +} + +static void shutdown_exit (void *foo) +{ + exit (0); +} + +isc_result_t dhcp_set_control_state (control_object_state_t oldstate, + control_object_state_t newstate) +{ + struct interface_info *ip; + struct client_state *client; + + /* Do the right thing for each interface. */ + for (ip = interfaces; ip; ip = ip -> next) { + for (client = ip -> client; client; client = client -> next) { + switch (newstate) { + case server_startup: + return ISC_R_SUCCESS; + + case server_running: + return ISC_R_SUCCESS; + + case server_shutdown: + if (client -> active && + client -> active -> expiry > cur_time) { + if (client -> config -> do_forward_update) + client_dns_update (client, 0, 0); + do_release (client); + } + break; + + case server_hibernate: + state_stop (client); + break; + + case server_awaken: + state_reboot (client); + break; + } + } + } + if (newstate == server_shutdown) + add_timeout (cur_time + 1, shutdown_exit, 0, 0, 0); + return ISC_R_SUCCESS; +} + +/* Called after a timeout if the DNS update failed on the previous try. + Retries the update, and if it times out, schedules a retry after + ten times as long of a wait. */ + +void client_dns_update_timeout (void *cp) +{ + struct client_state *client = cp; + isc_result_t status; + + if (client -> active) { + status = client_dns_update (client, 1, + (client -> active -> renewal - + cur_time)); + if (status == ISC_R_TIMEDOUT) { + client -> dns_update_timeout *= 10; + add_timeout (cur_time + client -> dns_update_timeout, + client_dns_update_timeout, client, 0, 0); + } + } +} + +/* See if we should do a DNS update, and if so, do it. */ + +isc_result_t client_dns_update (struct client_state *client, int addp, int ttl) +{ + struct data_string ddns_fqdn, ddns_fwd_name, + ddns_dhcid, client_identifier; + struct option_cache *oc; + int ignorep; + int result; + isc_result_t rcode; + + /* If we didn't send an FQDN option, we certainly aren't going to + be doing an update. */ + if (!client -> sent_options) + return ISC_R_SUCCESS; + + /* If we don't have a lease, we can't do an update. */ + if (!client -> active) + return ISC_R_SUCCESS; + + /* If we set the no client update flag, don't do the update. */ + if ((oc = lookup_option (&fqdn_universe, client -> sent_options, + FQDN_NO_CLIENT_UPDATE)) && + evaluate_boolean_option_cache (&ignorep, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) + return ISC_R_SUCCESS; + + /* If we set the "server, please update" flag, or didn't set it + to false, don't do the update. */ + if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, + FQDN_SERVER_UPDATE)) || + evaluate_boolean_option_cache (&ignorep, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) + return ISC_R_SUCCESS; + + /* If no FQDN option was supplied, don't do the update. */ + memset (&ddns_fwd_name, 0, sizeof ddns_fwd_name); + if (!(oc = lookup_option (&fqdn_universe, client -> sent_options, + FQDN_FQDN)) || + !evaluate_option_cache (&ddns_fwd_name, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) + return ISC_R_SUCCESS; + + /* Make a dhcid string out of either the client identifier, + if we are sending one, or the interface's MAC address, + otherwise. */ + memset (&ddns_dhcid, 0, sizeof ddns_dhcid); + + memset (&client_identifier, 0, sizeof client_identifier); + if ((oc = lookup_option (&dhcp_universe, client -> sent_options, + DHO_DHCP_CLIENT_IDENTIFIER)) && + evaluate_option_cache (&client_identifier, (struct packet *)0, + (struct lease *)0, client, + client -> sent_options, + (struct option_state *)0, + &global_scope, oc, MDL)) { + result = get_dhcid (&ddns_dhcid, + DHO_DHCP_CLIENT_IDENTIFIER, + client_identifier.data, + client_identifier.len); + data_string_forget (&client_identifier, MDL); + } else + result = get_dhcid (&ddns_dhcid, 0, + client -> interface -> hw_address.hbuf, + client -> interface -> hw_address.hlen); + if (!result) { + data_string_forget (&ddns_fwd_name, MDL); + return ISC_R_SUCCESS; + } + + /* Start the resolver, if necessary. */ + if (!resolver_inited) { + minires_ninit (&resolver_state); + resolver_inited = 1; + resolver_state.retrans = 1; + resolver_state.retry = 1; + } + + /* + * Perform updates. + */ + if (ddns_fwd_name.len && ddns_dhcid.len) { + if (addp) + rcode = ddns_update_a (&ddns_fwd_name, + client -> active -> address, + &ddns_dhcid, ttl, + 1); + else + rcode = ddns_remove_a (&ddns_fwd_name, + client -> active -> address, + &ddns_dhcid); + } else + rcode = ISC_R_FAILURE; + + data_string_forget (&ddns_fwd_name, MDL); + data_string_forget (&ddns_dhcid, MDL); + return rcode; +} diff --git a/contrib/dhcp-3.0/client/dhclient.conf b/contrib/dhcp-3.0/client/dhclient.conf new file mode 100644 index 0000000000..147e0045a5 --- /dev/null +++ b/contrib/dhcp-3.0/client/dhclient.conf @@ -0,0 +1,36 @@ +send host-name "andare.fugue.com"; +send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; +send dhcp-lease-time 3600; +supersede domain-name "fugue.com home.vix.com"; +prepend domain-name-servers 127.0.0.1; +request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, host-name; +require subnet-mask, domain-name-servers; +timeout 60; +retry 60; +reboot 10; +select-timeout 5; +initial-interval 2; +script "/etc/dhclient-script"; +media "-link0 -link1 -link2", "link0 link1"; +reject 192.33.137.209; + +alias { + interface "ep0"; + fixed-address 192.5.5.213; + option subnet-mask 255.255.255.255; +} + +lease { + interface "ep0"; + fixed-address 192.33.137.200; + medium "link0 link1"; + option host-name "andare.swiftmedia.com"; + option subnet-mask 255.255.255.0; + option broadcast-address 192.33.137.255; + option routers 192.33.137.250; + option domain-name-servers 127.0.0.1; + renew 2 2000/1/12 00:00:01; + rebind 2 2000/1/12 00:00:01; + expire 2 2000/1/12 00:00:01; +} diff --git a/contrib/dhcp-3.0/client/dhclient.conf.5 b/contrib/dhcp-3.0/client/dhclient.conf.5 new file mode 100644 index 0000000000..cc63f855c2 --- /dev/null +++ b/contrib/dhcp-3.0/client/dhclient.conf.5 @@ -0,0 +1,623 @@ +.\" $Id: dhclient.conf.5,v 1.12.2.11 2004/09/10 21:02:30 dhankins Exp $ +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 1996-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" This software has been written for Internet Software Consortium +.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. +.\" To learn more about Internet Software Consortium, see +.\" ``http://www.isc.org/''. To learn more about Vixie Enterprises, +.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see +.\" ``http://www.nominum.com''. +.TH dhclient.conf 5 +.SH NAME +dhclient.conf - DHCP client configuration file +.SH DESCRIPTION +The dhclient.conf file contains configuration information for +.IR dhclient, +the Internet Systems Consortium DHCP Client. +.PP +The dhclient.conf file is a free-form ASCII text file. It is parsed by +the recursive-descent parser built into dhclient. The file may contain +extra tabs and newlines for formatting purposes. Keywords in the file +are case-insensitive. Comments may be placed anywhere within the +file (except within quotes). Comments begin with the # character and +end at the end of the line. +.PP +The dhclient.conf file can be used to configure the behaviour of the +client in a wide variety of ways: protocol timing, information +requested from the server, information required of the server, +defaults to use if the server does not provide certain information, +values with which to override information provided by the server, or +values to prepend or append to information provided by the server. +The configuration file can also be preinitialized with addresses to +use on networks that don't have DHCP servers. +.SH PROTOCOL TIMING +The timing behaviour of the client need not be configured by the user. +If no timing configuration is provided by the user, a fairly +reasonable timing behaviour will be used by default - one which +results in fairly timely updates without placing an inordinate load on +the server. +.PP +The following statements can be used to adjust the timing behaviour of +the DHCP client if required, however: +.PP +.I The +.B timeout +.I statement +.PP +.B timeout +.I time +.B ; +.PP +The +.I timeout +statement determines the amount of time that must pass between the +time that the client begins to try to determine its address and the +time that it decides that it's not going to be able to contact a +server. By default, this timeout is sixty seconds. After the +timeout has passed, if there are any static leases defined in the +configuration file, or any leases remaining in the lease database that +have not yet expired, the client will loop through these leases +attempting to validate them, and if it finds one that appears to be +valid, it will use that lease's address. If there are no valid +static leases or unexpired leases in the lease database, the client +will restart the protocol after the defined retry interval. +.PP +.I The +.B retry +.I statement +.PP + \fBretry \fItime\fR\fB;\fR +.PP +The +.I retry +statement determines the time that must pass after the client has +determined that there is no DHCP server present before it tries again +to contact a DHCP server. By default, this is five minutes. +.PP +.I The +.B select-timeout +.I statement +.PP + \fBselect-timeout \fItime\fR\fB;\fR +.PP +It is possible (some might say desirable) for there to be more than +one DHCP server serving any given network. In this case, it is +possible that a client may be sent more than one offer in response to +its initial lease discovery message. It may be that one of these +offers is preferable to the other (e.g., one offer may have the +address the client previously used, and the other may not). +.PP +The +.I select-timeout +is the time after the client sends its first lease discovery request +at which it stops waiting for offers from servers, assuming that it +has received at least one such offer. If no offers have been +received by the time the +.I select-timeout +has expired, the client will accept the first offer that arrives. +.PP +By default, the select-timeout is zero seconds - that is, the client +will take the first offer it sees. +.PP +.I The +.B reboot +.I statement +.PP + \fBreboot \fItime\fR\fB;\fR +.PP +When the client is restarted, it first tries to reacquire the last +address it had. This is called the INIT-REBOOT state. If it is +still attached to the same network it was attached to when it last +ran, this is the quickest way to get started. The +.I reboot +statement sets the time that must elapse after the client first tries +to reacquire its old address before it gives up and tries to discover +a new address. By default, the reboot timeout is ten seconds. +.PP +.I The +.B backoff-cutoff +.I statement +.PP + \fBbackoff-cutoff \fItime\fR\fB;\fR +.PP +The client uses an exponential backoff algorithm with some randomness, +so that if many clients try to configure themselves at the same time, +they will not make their requests in lockstep. The +.I backoff-cutoff +statement determines the maximum amount of time that the client is +allowed to back off, the actual value will be evaluated randomly between +1/2 to 1 1/2 times the \fItime\fR specified. It defaults to two minutes. +.PP +.I The +.B initial-interval +.I statement +.PP + \fBinitial-interval \fItime\fR\fB;\fR +.PP +The +.I initial-interval +statement sets the amount of time between the first attempt to reach a +server and the second attempt to reach a server. Each time a message +is sent, the interval between messages is incremented by twice the +current interval multiplied by a random number between zero and one. +If it is greater than the backoff-cutoff amount, it is set to that +amount. It defaults to ten seconds. +.SH LEASE REQUIREMENTS AND REQUESTS +The DHCP protocol allows the client to request that the server send it +specific information, and not send it other information that it is not +prepared to accept. The protocol also allows the client to reject +offers from servers if they don't contain information the client +needs, or if the information provided is not satisfactory. +.PP +There is a variety of data contained in offers that DHCP servers send +to DHCP clients. The data that can be specifically requested is what +are called \fIDHCP Options\fR. DHCP Options are defined in + \fBdhcp-options(5)\fR. +.PP +.I The +.B request +.I statement +.PP + \fBrequest [ \fIoption\fR ] [\fB,\fI ... \fIoption\fR ]\fB;\fR +.PP +The request statement causes the client to request that any server +responding to the client send the client its values for the specified +options. Only the option names should be specified in the request +statement - not option parameters. By default, the DHCP server +requests the subnet-mask, broadcast-address, time-offset, routers, +domain-name, domain-name-servers and host-name options. +.PP +In some cases, it may be desirable to send no parameter request list +at all. To do this, simply write the request statement but specify +no parameters: +.PP +.nf + request; +.fi +.PP +.I The +.B require +.I statement +.PP + \fBrequire [ \fIoption\fR ] [\fB,\fI ... \fIoption ]\fB;\fR +.PP +The require statement lists options that must be sent in order for an +offer to be accepted. Offers that do not contain all the listed +options will be ignored. +.PP +.I The +.B send +.I statement +.PP + \fBsend { [ \fIoption declaration\fR ] +[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR +.PP +The send statement causes the client to send the specified options to +the server with the specified values. These are full option +declarations as described in \fBdhcp-options(5)\fR. Options that are +always sent in the DHCP protocol should not be specified here, except +that the client can specify a \fBrequested-lease-time\fR option other +than the default requested lease time, which is two hours. The other +obvious use for this statement is to send information to the server +that will allow it to differentiate between this client and other +clients or kinds of clients. +.SH DYNAMIC DNS +The client now has some very limited support for doing DNS updates +when a lease is acquired. This is prototypical, and probably doesn't +do what you want. It also only works if you happen to have control +over your DNS server, which isn't very likely. +.PP +To make it work, you have to declare a key and zone as in the DHCP +server (see \fBdhcpd.conf\fR(5) for details). You also need to +configure the fqdn option on the client, as follows: +.PP +.nf + send fqdn.fqdn "grosse.fugue.com."; + send fqdn.encoded on; + send fqdn.server-update off; +.fi +.PP +The \fIfqdn.fqdn\fR option \fBMUST\fR be a fully-qualified domain +name. You \fBMUST\fR define a zone statement for the zone to be +updated. The \fIfqdn.encoded\fR option may need to be set to +\fIon\fR or \fIoff\fR, depending on the DHCP server you are using. +.PP +.I The +.B do-forward-updates +.I statement +.PP + \fBdo-forward-updates [ \fIflag\fR ] \fB;\fR +.PP +If you want to do DNS updates in the DHCP client +script (see \fBdhclient-script(8)\fR) rather than having the +DHCP client do the update directly (for example, if you want to +use SIG(0) authentication, which is not supported directly by the +DHCP client, you can instruct the client not to do the update using +the \fBdo-forward-updates\fR statement. \fIFlag\fR should be \fBtrue\fR +if you want the DHCP client to do the update, and \fBfalse\fR if +you don't want the DHCP client to do the update. By default, the DHCP +client will do the DNS update. +.SH OPTION MODIFIERS +In some cases, a client may receive option data from the server which +is not really appropriate for that client, or may not receive +information that it needs, and for which a useful default value +exists. It may also receive information which is useful, but which +needs to be supplemented with local information. To handle these +needs, several option modifiers are available. +.PP +.I The +.B default +.I statement +.PP + \fBdefault [ \fIoption declaration\fR ] \fB;\fR +.PP +If for some option the client should use the value supplied by +the server, but needs to use some default value if no value was supplied +by the server, these values can be defined in the +.B default +statement. +.PP +.I The +.B supersede +.I statement +.PP + \fBsupersede [ \fIoption declaration\fR ] \fB;\fR +.PP +If for some option the client should always use a locally-configured +value or values rather than whatever is supplied by the server, these +values can be defined in the +.B supersede +statement. +.PP +.I The +.B prepend +.I statement +.PP + \fBprepend [ \fIoption declaration\fR ] \fB;\fR +.PP +If for some set of options the client should use a value you +supply, and then use the values supplied by +the server, if any, these values can be defined in the +.B prepend +statement. The +.B prepend +statement can only be used for options which +allow more than one value to be given. This restriction is not +enforced - if you ignore it, the behaviour will be unpredictable. +.PP +.I The +.B append +.I statement +.PP + \fBappend [ \fIoption declaration\fR ] \fB;\fR +.PP +If for some set of options the client should first use the values +supplied by the server, if any, and then use values you supply, these +values can be defined in the +.B append +statement. The +.B append +statement can only be used for options which +allow more than one value to be given. This restriction is not +enforced - if you ignore it, the behaviour will be unpredictable. +.SH LEASE DECLARATIONS +.PP +.I The +.B lease +.I declaration +.PP + \fBlease {\fR \fIlease-declaration\fR [ ... \fIlease-declaration ] \fB}\fR +.PP +The DHCP client may decide after some period of time (see \fBPROTOCOL +TIMING\fR) that it is not going to succeed in contacting a +server. At that time, it consults its own database of old leases and +tests each one that has not yet timed out by pinging the listed router +for that lease to see if that lease could work. It is possible to +define one or more \fIfixed\fR leases in the client configuration file +for networks where there is no DHCP or BOOTP service, so that the +client can still automatically configure its address. This is done +with the +.B lease +statement. +.PP +NOTE: the lease statement is also used in the dhclient.leases file in +order to record leases that have been received from DHCP servers. +Some of the syntax for leases as described below is only needed in the +dhclient.leases file. Such syntax is documented here for +completeness. +.PP +A lease statement consists of the lease keyword, followed by a left +curly brace, followed by one or more lease declaration statements, +followed by a right curly brace. The following lease declarations +are possible: +.PP + \fBbootp;\fR +.PP +The +.B bootp +statement is used to indicate that the lease was acquired using the +BOOTP protocol rather than the DHCP protocol. It is never necessary +to specify this in the client configuration file. The client uses +this syntax in its lease database file. +.PP + \fBinterface\fR \fB"\fR\fIstring\fR\fB";\fR +.PP +The +.B interface +lease statement is used to indicate the interface on which the lease +is valid. If set, this lease will only be tried on a particular +interface. When the client receives a lease from a server, it always +records the interface number on which it received that lease. +If predefined leases are specified in the dhclient.conf file, the +interface should also be specified, although this is not required. +.PP + \fBfixed-address\fR \fIip-address\fR\fB;\fR +.PP +The +.B fixed-address +statement is used to set the ip address of a particular lease. This +is required for all lease statements. The IP address must be +specified as a dotted quad (e.g., 12.34.56.78). +.PP + \fBfilename "\fR\fIstring\fR\fB";\fR +.PP +The +.B filename +statement specifies the name of the boot filename to use. This is +not used by the standard client configuration script, but is included +for completeness. +.PP + \fBserver-name "\fR\fIstring\fR\fB";\fR +.PP +The +.B server-name +statement specifies the name of the boot server name to use. This is +also not used by the standard client configuration script. +.PP + \fBoption\fR \fIoption-declaration\fR\fB;\fR +.PP +The +.B option +statement is used to specify the value of an option supplied by the +server, or, in the case of predefined leases declared in +dhclient.conf, the value that the user wishes the client configuration +script to use if the predefined lease is used. +.PP + \fBscript "\fIscript-name\fB";\fR +.PP +The +.B script +statement is used to specify the pathname of the dhcp client +configuration script. This script is used by the dhcp client to set +each interface's initial configuration prior to requesting an address, +to test the address once it has been offered, and to set the +interface's final configuration once a lease has been acquired. If +no lease is acquired, the script is used to test predefined leases, if +any, and also called once if no valid lease can be identified. For +more information, see +.B dhclient-script(8). +.PP + \fBvendor option space "\fIname\fB";\fR +.PP +The +.B vendor option space +statement is used to specify which option space should be used for +decoding the vendor-encapsulate-options option if one is received. +The \fIdhcp-vendor-identifier\fR can be used to request a specific +class of vendor options from the server. See +.B dhcp-options(5) +for details. +.PP + \fBmedium "\fImedia setup\fB";\fR +.PP +The +.B medium +statement can be used on systems where network interfaces cannot +automatically determine the type of network to which they are +connected. The media setup string is a system-dependent parameter +which is passed to the dhcp client configuration script when +initializing the interface. On Unix and Unix-like systems, the +argument is passed on the ifconfig command line when configuring the +interface. +.PP +The dhcp client automatically declares this parameter if it uses a +media type (see the +.B media +statement) when configuring the interface in order to obtain a lease. +This statement should be used in predefined leases only if the network +interface requires media type configuration. +.PP + \fBrenew\fR \fIdate\fB;\fR +.PP + \fBrebind\fR \fIdate\fB;\fR +.PP + \fBexpire\fR \fIdate\fB;\fR +.PP +The \fBrenew\fR statement defines the time at which the dhcp client +should begin trying to contact its server to renew a lease that it is +using. The \fBrebind\fR statement defines the time at which the dhcp +client should begin to try to contact \fIany\fR dhcp server in order +to renew its lease. The \fBexpire\fR statement defines the time at +which the dhcp client must stop using a lease if it has not been able +to contact a server in order to renew it. +.PP +These declarations are automatically set in leases acquired by the +DHCP client, but must also be configured in predefined leases - a +predefined lease whose expiry time has passed will not be used by the +DHCP client. +.PP +Dates are specified as follows: +.PP + \fI \fB/\fI\fB/\fI +\fB:\fI\fB:\fI\fR +.PP +The weekday is present to make it easy for a human to tell when a +lease expires - it's specified as a number from zero to six, with zero +being Sunday. When declaring a predefined lease, it can always be +specified as zero. The year is specified with the century, so it +should generally be four digits except for really long leases. The +month is specified as a number starting with 1 for January. The day +of the month is likewise specified starting with 1. The hour is a +number between 0 and 23, the minute a number between 0 and 59, and the +second also a number between 0 and 59. +.SH ALIAS DECLARATIONS + \fBalias { \fI declarations ... \fB}\fR +.PP +Some DHCP clients running TCP/IP roaming protocols may require that in +addition to the lease they may acquire via DHCP, their interface also +be configured with a predefined IP alias so that they can have a +permanent IP address even while roaming. The Internet Systems +Consortium DHCP client doesn't support roaming with fixed addresses +directly, but in order to facilitate such experimentation, the dhcp +client can be set up to configure an IP alias using the +.B alias +declaration. +.PP +The alias declaration resembles a lease declaration, except that +options other than the subnet-mask option are ignored by the standard +client configuration script, and expiry times are ignored. A typical +alias declaration includes an interface declaration, a fixed-address +declaration for the IP alias address, and a subnet-mask option +declaration. A medium statement should never be included in an alias +declaration. +.SH OTHER DECLARATIONS + \fBreject \fIip-address\fB;\fR +.PP +The +.B reject +statement causes the DHCP client to reject offers from +servers who use the specified address as a server identifier. This +can be used to avoid being configured by rogue or misconfigured dhcp +servers, although it should be a last resort - better to track down +the bad DHCP server and fix it. +.PP + \fBinterface "\fIname\fB" { \fIdeclarations ... \fB } +.PP +A client with more than one network interface may require different +behaviour depending on which interface is being configured. All +timing parameters and declarations other than lease and alias +declarations can be enclosed in an interface declaration, and those +parameters will then be used only for the interface that matches the +specified name. Interfaces for which there is no interface +declaration will use the parameters declared outside of any interface +declaration, or the default settings. +.PP + \fBpseudo "\fIname\fR" "\fIreal-name\fB" { \fIdeclarations ... \fB } +.PP +Under some circumstances it can be useful to declare a pseudo-interface +and have the DHCP client acquire a configuration for that interface. +Each interface that the DHCP client is supporting normally has a DHCP +client state machine running on it to acquire and maintain its lease. +A pseudo-interface is just another state machine running on the +interface named \fIreal-name\fR, with its own lease and its own +state. If you use this feature, you must provide a client identifier +for both the pseudo-interface and the actual interface, and the two +identifiers must be different. You must also provide a separate +client script for the pseudo-interface to do what you want with the IP +address. For example: +.PP +.nf + interface "ep0" { + send dhcp-client-identifier "my-client-ep0"; + } + pseudo "secondary" "ep0" { + send dhcp-client-identifier "my-client-ep0-secondary"; + script "/etc/dhclient-secondary"; + } +.fi +.PP +The client script for the pseudo-interface should not configure the +interface up or down - essentially, all it needs to handle are the +states where a lease has been acquired or renewed, and the states +where a lease has expired. See \fBdhclient-script(8)\fR for more +information. +.PP + \fBmedia "\fImedia setup\fB"\fI [ \fB, "\fImedia setup\fB", \fI... ]\fB;\fR +.PP +The +.B media +statement defines one or more media configuration parameters which may +be tried while attempting to acquire an IP address. The dhcp client +will cycle through each media setup string on the list, configuring +the interface using that setup and attempting to boot, and then trying +the next one. This can be used for network interfaces which aren't +capable of sensing the media type unaided - whichever media type +succeeds in getting a request to the server and hearing the reply is +probably right (no guarantees). +.PP +The media setup is only used for the initial phase of address +acquisition (the DHCPDISCOVER and DHCPOFFER packets). Once an +address has been acquired, the dhcp client will record it in its lease +database and will record the media type used to acquire the address. +Whenever the client tries to renew the lease, it will use that same +media type. The lease must expire before the client will go back to +cycling through media types. +.SH SAMPLE +The following configuration file is used on a laptop running NetBSD +1.3. The laptop has an IP alias of 192.5.5.213, and has one +interface, ep0 (a 3com 3C589C). Booting intervals have been +shortened somewhat from the default, because the client is known to +spend most of its time on networks with little DHCP activity. The +laptop does roam to multiple networks. + +.nf + +timeout 60; +retry 60; +reboot 10; +select-timeout 5; +initial-interval 2; +reject 192.33.137.209; + +interface "ep0" { + send host-name "andare.fugue.com"; + send dhcp-client-identifier 1:0:a0:24:ab:fb:9c; + send dhcp-lease-time 3600; + supersede domain-name "fugue.com rc.vix.com home.vix.com"; + prepend domain-name-servers 127.0.0.1; + request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, host-name; + require subnet-mask, domain-name-servers; + script "CLIENTBINDIR/dhclient-script"; + media "media 10baseT/UTP", "media 10base2/BNC"; +} + +alias { + interface "ep0"; + fixed-address 192.5.5.213; + option subnet-mask 255.255.255.255; +} +.fi +This is a very complicated dhclient.conf file - in general, yours +should be much simpler. In many cases, it's sufficient to just +create an empty dhclient.conf file - the defaults are usually fine. +.SH SEE ALSO +dhcp-options(5), dhclient.leases(5), dhcpd(8), dhcpd.conf(5), RFC2132, +RFC2131. +.SH AUTHOR +.B dhclient(8) +was written by Ted Lemon +under a contract with Vixie Labs. Funding +for this project was provided by Internet Systems Consortium. +Information about Internet Systems Consortium can be found at +.B http://www.isc.org. diff --git a/contrib/dhcp-3.0/client/dhclient.leases.5 b/contrib/dhcp-3.0/client/dhclient.leases.5 new file mode 100644 index 0000000000..204a800534 --- /dev/null +++ b/contrib/dhcp-3.0/client/dhclient.leases.5 @@ -0,0 +1,53 @@ +.\" $Id: dhclient.leases.5,v 1.2.4.4 2004/06/10 17:59:13 dhankins Exp $ +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 1997-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" This software has been written for Internet Systems Consortium +.\" by Ted Lemon in cooperation with Vixie +.\" Enterprises. To learn more about Internet Systems Consortium, +.\" see ``http://www.isc.org/isc''. To learn more about Vixie +.\" Enterprises, see ``http://www.vix.com''. +.TH dhclient.leases 5 +.SH NAME +dhclient.leases - DHCP client lease database +.SH DESCRIPTION +The Internet Systems Consortium DHCP client keeps a persistent +database of leases that it has acquired that are still valid. The +database is a free-form ASCII file containing one valid declaration +per lease. If more than one declaration appears for a given lease, +the last one in the file is used. The file is written as a log, so +this is not an unusual occurrance. +.PP +The format of the lease declarations is described in +.B dhclient.conf(5). +.SH FILES +.B DBDIR/dhclient.leases +.SH SEE ALSO +dhclient(8), dhcp-options(5), dhclient.conf(5), dhcpd(8), +dhcpd.conf(5), RFC2132, RFC2131. +.SH AUTHOR +.B dhclient(8) +was written by Ted Lemon +under a contract with Vixie Labs. Funding +for this project was provided by Internet Systems Consortium. +Information about Internet Systems Consortium can be found at +.B http://www.isc.org. diff --git a/contrib/dhcp-3.0/client/scripts/freebsd b/contrib/dhcp-3.0/client/scripts/freebsd new file mode 100644 index 0000000000..2058c13bc7 --- /dev/null +++ b/contrib/dhcp-3.0/client/scripts/freebsd @@ -0,0 +1,242 @@ +#!/bin/sh +# +# $Id: freebsd,v 1.13.2.7 2004/09/30 23:22:48 dhankins Exp $ +# +# $FreeBSD$ + +if [ -x /usr/bin/logger ]; then + LOGGER="/usr/bin/logger -s -p user.notice -t dhclient" +else + LOGGER=echo +fi + +make_resolv_conf() { + if [ x"$new_domain_name_servers" != x ]; then + if [ "x$new_domain_name" != x ]; then + ( echo search $new_domain_name >/etc/resolv.conf ) + exit_status=$? + else + if [ -e /etc/resolv.conf ] ; then + ( rm /etc/resolv.conf ) + exit_status=$? + else + ( touch /etc/resolv.conf ) + exit_status=$? + fi + fi + if [ $exit_status -ne 0 ]; then + $LOGGER "WARNING: Unable to update resolv.conf: Error $exit_status" + else + for nameserver in $new_domain_name_servers; do + ( echo nameserver $nameserver >>/etc/resolv.conf ) + done + fi + fi +} + +# Must be used on exit. Invokes the local dhcp client exit hooks, if any. +exit_with_hooks() { + exit_status=$1 + if [ -f /etc/dhclient-exit-hooks ]; then + . /etc/dhclient-exit-hooks + fi +# probably should do something with exit status of the local script + exit $exit_status +} + +# Invoke the local dhcp client enter hooks, if they exist. +if [ -f /etc/dhclient-enter-hooks ]; then + exit_status=0 + . /etc/dhclient-enter-hooks + # allow the local script to abort processing of this state + # local script must set exit_status variable to nonzero. + if [ $exit_status -ne 0 ]; then + exit $exit_status + fi +fi + +if [ x$new_network_number != x ]; then + $LOGGER New Network Number: $new_network_number +fi + +if [ x$new_broadcast_address != x ]; then + $LOGGER New Broadcast Address: $new_broadcast_address + new_broadcast_arg="broadcast $new_broadcast_address" +fi +if [ x$old_broadcast_address != x ]; then + old_broadcast_arg="broadcast $old_broadcast_address" +fi +if [ x$new_subnet_mask != x ]; then + new_netmask_arg="netmask $new_subnet_mask" +fi +if [ x$old_subnet_mask != x ]; then + old_netmask_arg="netmask $old_subnet_mask" +fi +if [ x$alias_subnet_mask != x ]; then + alias_subnet_arg="netmask $alias_subnet_mask" +fi + +if [ x$reason = xMEDIUM ]; then + eval "ifconfig $interface $medium" + eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1 + sleep 1 + exit_with_hooks 0 +fi + +if [ x$reason = xPREINIT ]; then + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \ + broadcast 255.255.255.255 up + exit_with_hooks 0 +fi + +if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then + exit_with_hooks 0; +fi + +if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \ + [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then + current_hostname=`/bin/hostname` + if [ x$current_hostname = x ] || \ + [ x$current_hostname = x$old_host_name ]; then + if [ x$current_hostname = x ] || \ + [ x$new_host_name != x$old_host_name ]; then + $LOGGER "New Hostname: $new_host_name" + hostname $new_host_name + fi + fi + if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \ + [ x$alias_ip_address != x$old_ip_address ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ] + then + eval "ifconfig $interface inet -alias $old_ip_address $medium" + route delete $old_ip_address 127.1 >/dev/null 2>&1 + for router in $old_routers; do + route delete default $router >/dev/null 2>&1 + done + if [ -n "$old_static_routes" ]; then + set -- $old_static_routes + while [ $# -gt 1 ]; do + route delete $1 $2 + shift; shift + done + fi + arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' |sh + fi + if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \ + [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then + eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ + $new_broadcast_arg $medium" + $LOGGER "New IP Address ($interface): $new_ip_address" + $LOGGER "New Subnet Mask ($interface): $new_subnet_mask" + $LOGGER "New Broadcast Address ($interface): $new_broadcast_address" + if [ -n "$new_routers" ]; then + $LOGGER "New Routers: $new_routers" + fi + route add $new_ip_address 127.1 >/dev/null 2>&1 + for router in $new_routers; do + route add default $router >/dev/null 2>&1 + done + if [ -n "$new_static_routes" ]; then + $LOGGER "New Static Routes: $new_static_routes" + set -- $new_static_routes + while [ $# -gt 1 ]; do + route add $1 $2 + shift; shift + done + fi + fi + if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ]; + then + ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg + route add $alias_ip_address 127.0.0.1 + fi + make_resolv_conf + exit_with_hooks 0 +fi + +if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \ + || [ x$reason = xSTOP ]; then + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + if [ x$old_ip_address != x ]; then + eval "ifconfig $interface inet -alias $old_ip_address $medium" + route delete $old_ip_address 127.1 >/dev/null 2>&1 + for router in $old_routers; do + route delete default $router >/dev/null 2>&1 + done + if [ -n "$old_static_routes" ]; then + set -- $old_static_routes + while [ $# -gt 1 ]; do + route delete $1 $2 + shift; shift + done + fi + arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' \ + |sh >/dev/null 2>&1 + fi + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg + route add $alias_ip_address 127.0.0.1 + fi + exit_with_hooks 0 +fi + +if [ x$reason = xTIMEOUT ]; then + if [ x$alias_ip_address != x ]; then + ifconfig $interface inet -alias $alias_ip_address > /dev/null 2>&1 + route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1 + fi + eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \ + $new_broadcast_arg $medium" + $LOGGER "New IP Address ($interface): $new_ip_address" + $LOGGER "New Subnet Mask ($interface): $new_subnet_mask" + $LOGGER "New Broadcast Address ($interface): $new_broadcast_address" + sleep 1 + if [ -n "$new_routers" ]; then + $LOGGER "New Routers: $new_routers" + set -- $new_routers + if ping -q -c 1 $1; then + if [ x$new_ip_address != x$alias_ip_address ] && \ + [ x$alias_ip_address != x ]; then + ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg + route add $alias_ip_address 127.0.0.1 + fi + route add $new_ip_address 127.1 >/dev/null 2>&1 + for router in $new_routers; do + route add default $router >/dev/null 2>&1 + done + set -- $new_static_routes + while [ $# -gt 1 ]; do + route add $1 $2 + shift; shift + done + make_resolv_conf + exit_with_hooks 0 + fi + fi + eval "ifconfig $interface inet -alias $new_ip_address $medium" + for router in $old_routers; do + route delete default $router >/dev/null 2>&1 + done + if [ -n "$old_static_routes" ]; then + set -- $old_static_routes + while [ $# -gt 1 ]; do + route delete $1 $2 + shift; shift + done + fi + arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' \ + |sh >/dev/null 2>&1 + exit_with_hooks 1 +fi + +exit_with_hooks 0 diff --git a/contrib/dhcp-3.0/common/alloc.c b/contrib/dhcp-3.0/common/alloc.c new file mode 100644 index 0000000000..39edd810ce --- /dev/null +++ b/contrib/dhcp-3.0/common/alloc.c @@ -0,0 +1,1317 @@ +/* alloc.c + + Memory allocation... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: alloc.c,v 1.53.2.10 2004/06/10 17:59:14 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include + +struct dhcp_packet *dhcp_free_list; +struct packet *packet_free_list; + +int option_chain_head_allocate (ptr, file, line) + struct option_chain_head **ptr; + const char *file; + int line; +{ + int size; + struct option_chain_head *h; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct option_chain_head *)0; +#endif + } + + h = dmalloc (sizeof *h, file, line); + if (h) { + memset (h, 0, sizeof *h); + return option_chain_head_reference (ptr, h, file, line); + } + return 0; +} + +int option_chain_head_reference (ptr, bp, file, line) + struct option_chain_head **ptr; + struct option_chain_head *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct option_chain_head *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int option_chain_head_dereference (ptr, file, line) + struct option_chain_head **ptr; + const char *file; + int line; +{ + int i; + struct option_chain_head *option_chain_head; + pair car, cdr; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + option_chain_head = *ptr; + *ptr = (struct option_chain_head *)0; + --option_chain_head -> refcnt; + rc_register (file, line, ptr, option_chain_head, + option_chain_head -> refcnt, 1, RC_MISC); + if (option_chain_head -> refcnt > 0) + return 1; + + if (option_chain_head -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (option_chain_head); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + /* If there are any options on this head, free them. */ + for (car = option_chain_head -> first; car; car = cdr) { + cdr = car -> cdr; + if (car -> car) + option_cache_dereference ((struct option_cache **) + (&car -> car), MDL); + dfree (car, MDL); + car = cdr; + } + + dfree (option_chain_head, file, line); + return 1; +} + +int group_allocate (ptr, file, line) + struct group **ptr; + const char *file; + int line; +{ + int size; + struct group *g; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct group *)0; +#endif + } + + g = dmalloc (sizeof *g, file, line); + if (g) { + memset (g, 0, sizeof *g); + return group_reference (ptr, g, file, line); + } + return 0; +} + +int group_reference (ptr, bp, file, line) + struct group **ptr; + struct group *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct group *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int group_dereference (ptr, file, line) + struct group **ptr; + const char *file; + int line; +{ + int i; + struct group *group; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + group = *ptr; + *ptr = (struct group *)0; + --group -> refcnt; + rc_register (file, line, ptr, group, group -> refcnt, 1, RC_MISC); + if (group -> refcnt > 0) + return 1; + + if (group -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (group); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (group -> object) + group_object_dereference (&group -> object, file, line); + if (group -> subnet) + subnet_dereference (&group -> subnet, file, line); + if (group -> shared_network) + shared_network_dereference (&group -> shared_network, + file, line); + if (group -> statements) + executable_statement_dereference (&group -> statements, + file, line); + if (group -> next) + group_dereference (&group -> next, file, line); + dfree (group, file, line); + return 1; +} + +struct dhcp_packet *new_dhcp_packet (file, line) + const char *file; + int line; +{ + struct dhcp_packet *rval; + rval = (struct dhcp_packet *)dmalloc (sizeof (struct dhcp_packet), + file, line); + return rval; +} + +struct protocol *new_protocol (file, line) + const char *file; + int line; +{ + struct protocol *rval = dmalloc (sizeof (struct protocol), file, line); + return rval; +} + +struct domain_search_list *new_domain_search_list (file, line) + const char *file; + int line; +{ + struct domain_search_list *rval = + dmalloc (sizeof (struct domain_search_list), file, line); + return rval; +} + +struct name_server *new_name_server (file, line) + const char *file; + int line; +{ + struct name_server *rval = + dmalloc (sizeof (struct name_server), file, line); + return rval; +} + +void free_name_server (ptr, file, line) + struct name_server *ptr; + const char *file; + int line; +{ + dfree ((VOIDPTR)ptr, file, line); +} + +struct option *new_option (file, line) + const char *file; + int line; +{ + struct option *rval = + dmalloc (sizeof (struct option), file, line); + if (rval) + memset (rval, 0, sizeof *rval); + return rval; +} + +void free_option (ptr, file, line) + struct option *ptr; + const char *file; + int line; +{ +/* XXX have to put all options on heap before this is possible. */ +#if 0 + if (ptr -> name) + dfree ((VOIDPTR)option -> name, file, line); + dfree ((VOIDPTR)ptr, file, line); +#endif +} + +struct universe *new_universe (file, line) + const char *file; + int line; +{ + struct universe *rval = + dmalloc (sizeof (struct universe), file, line); + return rval; +} + +void free_universe (ptr, file, line) + struct universe *ptr; + const char *file; + int line; +{ + dfree ((VOIDPTR)ptr, file, line); +} + +void free_domain_search_list (ptr, file, line) + struct domain_search_list *ptr; + const char *file; + int line; +{ + dfree ((VOIDPTR)ptr, file, line); +} + +void free_protocol (ptr, file, line) + struct protocol *ptr; + const char *file; + int line; +{ + dfree ((VOIDPTR)ptr, file, line); +} + +void free_dhcp_packet (ptr, file, line) + struct dhcp_packet *ptr; + const char *file; + int line; +{ + dfree ((VOIDPTR)ptr, file, line); +} + +struct client_lease *new_client_lease (file, line) + const char *file; + int line; +{ + return (struct client_lease *)dmalloc (sizeof (struct client_lease), + file, line); +} + +void free_client_lease (lease, file, line) + struct client_lease *lease; + const char *file; + int line; +{ + dfree (lease, file, line); +} + +pair free_pairs; + +pair new_pair (file, line) + const char *file; + int line; +{ + pair foo; + + if (free_pairs) { + foo = free_pairs; + free_pairs = foo -> cdr; + memset (foo, 0, sizeof *foo); + dmalloc_reuse (foo, file, line, 0); + return foo; + } + + foo = dmalloc (sizeof *foo, file, line); + if (!foo) + return foo; + memset (foo, 0, sizeof *foo); + return foo; +} + +void free_pair (foo, file, line) + pair foo; + const char *file; + int line; +{ + foo -> cdr = free_pairs; + free_pairs = foo; + dmalloc_reuse (free_pairs, (char *)0, 0, 0); +} + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_pairs () +{ + pair pf, pc; + + for (pf = free_pairs; pf; pf = pc) { + pc = pf -> cdr; + dfree (pf, MDL); + } + free_pairs = (pair)0; +} +#endif + +struct expression *free_expressions; + +int expression_allocate (cptr, file, line) + struct expression **cptr; + const char *file; + int line; +{ + struct expression *rval; + + if (free_expressions) { + rval = free_expressions; + free_expressions = rval -> data.not; + dmalloc_reuse (rval, file, line, 1); + } else { + rval = dmalloc (sizeof (struct expression), file, line); + if (!rval) + return 0; + } + memset (rval, 0, sizeof *rval); + return expression_reference (cptr, rval, file, line); +} + +int expression_reference (ptr, src, file, line) + struct expression **ptr; + struct expression *src; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct expression *)0; +#endif + } + *ptr = src; + src -> refcnt++; + rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); + return 1; +} + +void free_expression (expr, file, line) + struct expression *expr; + const char *file; + int line; +{ + expr -> data.not = free_expressions; + free_expressions = expr; + dmalloc_reuse (free_expressions, (char *)0, 0, 0); +} + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_expressions () +{ + struct expression *e, *n; + + for (e = free_expressions; e; e = n) { + n = e -> data.not; + dfree (e, MDL); + } + free_expressions = (struct expression *)0; +} +#endif + +struct binding_value *free_binding_values; + +int binding_value_allocate (cptr, file, line) + struct binding_value **cptr; + const char *file; + int line; +{ + struct binding_value *rval; + + if (free_binding_values) { + rval = free_binding_values; + free_binding_values = rval -> value.bv; + dmalloc_reuse (rval, file, line, 1); + } else { + rval = dmalloc (sizeof (struct binding_value), file, line); + if (!rval) + return 0; + } + memset (rval, 0, sizeof *rval); + return binding_value_reference (cptr, rval, file, line); +} + +int binding_value_reference (ptr, src, file, line) + struct binding_value **ptr; + struct binding_value *src; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct binding_value *)0; +#endif + } + *ptr = src; + src -> refcnt++; + rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); + return 1; +} + +void free_binding_value (bv, file, line) + struct binding_value *bv; + const char *file; + int line; +{ + bv -> value.bv = free_binding_values; + free_binding_values = bv; + dmalloc_reuse (free_binding_values, (char *)0, 0, 0); +} + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_binding_values () +{ + struct binding_value *b, *n; + + for (b = free_binding_values; b; b = n) { + n = b -> value.bv; + dfree (b, MDL); + } + free_binding_values = (struct binding_value *)0; +} +#endif + +int fundef_allocate (cptr, file, line) + struct fundef **cptr; + const char *file; + int line; +{ + struct fundef *rval; + + rval = dmalloc (sizeof (struct fundef), file, line); + if (!rval) + return 0; + memset (rval, 0, sizeof *rval); + return fundef_reference (cptr, rval, file, line); +} + +int fundef_reference (ptr, src, file, line) + struct fundef **ptr; + struct fundef *src; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct fundef *)0; +#endif + } + *ptr = src; + src -> refcnt++; + rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); + return 1; +} + +struct option_cache *free_option_caches; + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_option_caches () +{ + struct option_cache *o, *n; + + for (o = free_option_caches; o; o = n) { + n = (struct option_cache *)(o -> expression); + dfree (o, MDL); + } + free_option_caches = (struct option_cache *)0; +} +#endif + +int option_cache_allocate (cptr, file, line) + struct option_cache **cptr; + const char *file; + int line; +{ + struct option_cache *rval; + + if (free_option_caches) { + rval = free_option_caches; + free_option_caches = + (struct option_cache *)(rval -> expression); + dmalloc_reuse (rval, file, line, 0); + } else { + rval = dmalloc (sizeof (struct option_cache), file, line); + if (!rval) + return 0; + } + memset (rval, 0, sizeof *rval); + return option_cache_reference (cptr, rval, file, line); +} + +int option_cache_reference (ptr, src, file, line) + struct option_cache **ptr; + struct option_cache *src; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct option_cache *)0; +#endif + } + *ptr = src; + src -> refcnt++; + rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC); + return 1; +} + +int buffer_allocate (ptr, len, file, line) + struct buffer **ptr; + unsigned len; + const char *file; + int line; +{ + struct buffer *bp; + + bp = dmalloc (len + sizeof *bp, file, line); + if (!bp) + return 0; + memset (bp, 0, sizeof *bp); + bp -> refcnt = 0; + return buffer_reference (ptr, bp, file, line); +} + +int buffer_reference (ptr, bp, file, line) + struct buffer **ptr; + struct buffer *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct buffer *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int buffer_dereference (ptr, file, line) + struct buffer **ptr; + const char *file; + int line; +{ + struct buffer *bp; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (!*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + (*ptr) -> refcnt--; + rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); + if (!(*ptr) -> refcnt) { + dfree ((*ptr), file, line); + } else if ((*ptr) -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*ptr); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + *ptr = (struct buffer *)0; + return 1; +} + +int dns_host_entry_allocate (ptr, hostname, file, line) + struct dns_host_entry **ptr; + const char *hostname; + const char *file; + int line; +{ + struct dns_host_entry *bp; + + bp = dmalloc (strlen (hostname) + sizeof *bp, file, line); + if (!bp) + return 0; + memset (bp, 0, sizeof *bp); + bp -> refcnt = 0; + strcpy (bp -> hostname, hostname); + return dns_host_entry_reference (ptr, bp, file, line); +} + +int dns_host_entry_reference (ptr, bp, file, line) + struct dns_host_entry **ptr; + struct dns_host_entry *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct dns_host_entry *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int dns_host_entry_dereference (ptr, file, line) + struct dns_host_entry **ptr; + const char *file; + int line; +{ + struct dns_host_entry *bp; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + (*ptr) -> refcnt--; + rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); + if (!(*ptr) -> refcnt) + dfree ((*ptr), file, line); + if ((*ptr) -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*ptr); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + *ptr = (struct dns_host_entry *)0; + return 1; +} + +int option_state_allocate (ptr, file, line) + struct option_state **ptr; + const char *file; + int line; +{ + unsigned size; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct option_state *)0; +#endif + } + + size = sizeof **ptr + (universe_count - 1) * sizeof (VOIDPTR); + *ptr = dmalloc (size, file, line); + if (*ptr) { + memset (*ptr, 0, size); + (*ptr) -> universe_count = universe_count; + (*ptr) -> refcnt = 1; + rc_register (file, line, + ptr, *ptr, (*ptr) -> refcnt, 0, RC_MISC); + return 1; + } + return 0; +} + +int option_state_reference (ptr, bp, file, line) + struct option_state **ptr; + struct option_state *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct option_state *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int option_state_dereference (ptr, file, line) + struct option_state **ptr; + const char *file; + int line; +{ + int i; + struct option_state *options; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + options = *ptr; + *ptr = (struct option_state *)0; + --options -> refcnt; + rc_register (file, line, ptr, options, options -> refcnt, 1, RC_MISC); + if (options -> refcnt > 0) + return 1; + + if (options -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (options); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + /* Loop through the per-universe state. */ + for (i = 0; i < options -> universe_count; i++) + if (options -> universes [i] && + universes [i] -> option_state_dereference) + ((*(universes [i] -> option_state_dereference)) + (universes [i], options, file, line)); + dfree (options, file, line); + return 1; +} + +int executable_statement_allocate (ptr, file, line) + struct executable_statement **ptr; + const char *file; + int line; +{ + struct executable_statement *bp; + + bp = dmalloc (sizeof *bp, file, line); + if (!bp) + return 0; + memset (bp, 0, sizeof *bp); + return executable_statement_reference (ptr, bp, file, line); +} + +int executable_statement_reference (ptr, bp, file, line) + struct executable_statement **ptr; + struct executable_statement *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct executable_statement *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +static struct packet *free_packets; + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_packets () +{ + struct packet *p, *n; + for (p = free_packets; p; p = n) { + n = (struct packet *)(p -> raw); + dfree (p, MDL); + } + free_packets = (struct packet *)0; +} +#endif + +int packet_allocate (ptr, file, line) + struct packet **ptr; + const char *file; + int line; +{ + int size; + struct packet *p; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct packet *)0; +#endif + } + + if (free_packets) { + p = free_packets; + free_packets = (struct packet *)(p -> raw); + dmalloc_reuse (p, file, line, 1); + } else { + p = dmalloc (sizeof *p, file, line); + } + if (p) { + memset (p, 0, sizeof *p); + return packet_reference (ptr, p, file, line); + } + return 0; +} + +int packet_reference (ptr, bp, file, line) + struct packet **ptr; + struct packet *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct packet *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int packet_dereference (ptr, file, line) + struct packet **ptr; + const char *file; + int line; +{ + int i; + struct packet *packet; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + packet = *ptr; + *ptr = (struct packet *)0; + --packet -> refcnt; + rc_register (file, line, ptr, packet, packet -> refcnt, 1, RC_MISC); + if (packet -> refcnt > 0) + return 1; + + if (packet -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (packet); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (packet -> options) + option_state_dereference (&packet -> options, file, line); + if (packet -> interface) + interface_dereference (&packet -> interface, MDL); + if (packet -> shared_network) + shared_network_dereference (&packet -> shared_network, MDL); + for (i = 0; i < packet -> class_count && i < PACKET_MAX_CLASSES; i++) { + if (packet -> classes [i]) + omapi_object_dereference ((omapi_object_t **) + &packet -> classes [i], MDL); + } + packet -> raw = (struct dhcp_packet *)free_packets; + free_packets = packet; + dmalloc_reuse (free_packets, (char *)0, 0, 0); + return 1; +} + +int dns_zone_allocate (ptr, file, line) + struct dns_zone **ptr; + const char *file; + int line; +{ + int size; + struct dns_zone *d; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct dns_zone *)0; +#endif + } + + d = dmalloc (sizeof *d, file, line); + if (d) { + memset (d, 0, sizeof *d); + return dns_zone_reference (ptr, d, file, line); + } + return 0; +} + +int dns_zone_reference (ptr, bp, file, line) + struct dns_zone **ptr; + struct dns_zone *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct dns_zone *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +int binding_scope_allocate (ptr, file, line) + struct binding_scope **ptr; + const char *file; + int line; +{ + struct binding_scope *bp; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + bp = dmalloc (sizeof *bp, file, line); + if (!bp) + return 0; + memset (bp, 0, sizeof *bp); + binding_scope_reference (ptr, bp, file, line); + return 1; +} + +int binding_scope_reference (ptr, bp, file, line) + struct binding_scope **ptr; + struct binding_scope *bp; + const char *file; + int line; +{ + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (*ptr) { + log_error ("%s(%d): non-null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct binding_scope *)0; +#endif + } + *ptr = bp; + bp -> refcnt++; + rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC); + return 1; +} + +/* Make a copy of the data in data_string, upping the buffer reference + count if there's a buffer. */ + +void data_string_copy (dest, src, file, line) + struct data_string *dest; + struct data_string *src; + const char *file; + int line; +{ + if (src -> buffer) + buffer_reference (&dest -> buffer, src -> buffer, file, line); + dest -> data = src -> data; + dest -> terminated = src -> terminated; + dest -> len = src -> len; +} + +/* Release the reference count to a data string's buffer (if any) and + zero out the other information, yielding the null data string. */ + +void data_string_forget (data, file, line) + struct data_string *data; + const char *file; + int line; +{ + if (data -> buffer) + buffer_dereference (&data -> buffer, file, line); + memset (data, 0, sizeof *data); +} + +/* Make a copy of the data in data_string, upping the buffer reference + count if there's a buffer. */ + +void data_string_truncate (dp, len) + struct data_string *dp; + int len; +{ + if (len < dp -> len) { + dp -> terminated = 0; + dp -> len = len; + } +} diff --git a/contrib/dhcp-3.0/common/bpf.c b/contrib/dhcp-3.0/common/bpf.c new file mode 100644 index 0000000000..2066268ba9 --- /dev/null +++ b/contrib/dhcp-3.0/common/bpf.c @@ -0,0 +1,547 @@ +/* bpf.c + + BPF socket interface code, originally contributed by Archie Cobbs. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software was contributed to Internet Systems Consortium + * by Archie Cobbs. + * + * Patches for FDDI support on Digital Unix were written by Bill + * Stapleton, and maintained for a while by Mike Meredith before he + * managed to get me to integrate them. + */ + +#ifndef lint +static char copyright[] = +"$Id: bpf.c,v 1.48.2.7 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) \ + || defined (USE_LPF_RECEIVE) +# if defined (USE_LPF_RECEIVE) +# include +# include +# define bpf_insn sock_filter /* Linux: dare to be gratuitously different. */ +# else +# include +# include +# include +# if defined (NEED_OSF_PFILT_HACKS) +# include +# endif +# endif + +#include +#include "includes/netinet/ip.h" +#include "includes/netinet/udp.h" +#include "includes/netinet/if_ether.h" +#endif + +/* Reinitializes the specified interface after an address change. This + is not required for packet-filter APIs. */ + +#ifdef USE_BPF_SEND +void if_reinitialize_send (info) + struct interface_info *info; +{ +} +#endif + +#ifdef USE_BPF_RECEIVE +void if_reinitialize_receive (info) + struct interface_info *info; +{ +} +#endif + +/* Called by get_interface_list for each interface that's discovered. + Opens a packet filter for each interface and adds it to the select + mask. */ + +#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) +int if_register_bpf (info) + struct interface_info *info; +{ + int sock; + char filename[50]; + int b; + + /* Open a BPF device */ + for (b = 0; 1; b++) { + /* %Audit% 31 bytes max. %2004.06.17,Safe% */ + sprintf(filename, BPF_FORMAT, b); + sock = open (filename, O_RDWR, 0); + if (sock < 0) { + if (errno == EBUSY) { + continue; + } else { + if (!b) + log_fatal ("No bpf devices.%s%s%s", + " Please read the README", + " section for your operating", + " system."); + log_fatal ("Can't find free bpf: %m"); + } + } else { + break; + } + } + + /* Set the BPF device to point at this interface. */ + if (ioctl (sock, BIOCSETIF, info -> ifp) < 0) + log_fatal ("Can't attach interface %s to bpf device %s: %m", + info -> name, filename); + + return sock; +} +#endif /* USE_BPF_SEND || USE_BPF_RECEIVE */ + +#ifdef USE_BPF_SEND +void if_register_send (info) + struct interface_info *info; +{ + /* If we're using the bpf API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_BPF_RECEIVE + info -> wfdesc = if_register_bpf (info, interface); +#else + info -> wfdesc = info -> rfdesc; +#endif + if (!quiet_interface_discovery) + log_info ("Sending on BPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_send (info) + struct interface_info *info; +{ + /* If we're using the bpf API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_BPF_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling output on BPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_BPF_SEND */ + +#if defined (USE_BPF_RECEIVE) || defined (USE_LPF_RECEIVE) +/* Packet filter program... + XXX Changes to the filter program may require changes to the constant + offsets used in if_register_send to patch the BPF program! XXX */ + +struct bpf_insn dhcp_bpf_filter [] = { + /* Make sure this is an IP packet... */ + BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), + + /* Make sure it's a UDP packet... */ + BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), + + /* Make sure this isn't a fragment... */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), + + /* Get the IP header length... */ + BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14), + + /* Make sure it's to the right port... */ + BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ + + /* If we passed all the tests, ask for the whole packet. */ + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + + /* Otherwise, drop it. */ + BPF_STMT(BPF_RET+BPF_K, 0), +}; + +#if defined (DEC_FDDI) +struct bpf_insn *bpf_fddi_filter; +#endif + +int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn); +#if defined (HAVE_TR_SUPPORT) +struct bpf_insn dhcp_bpf_tr_filter [] = { + /* accept all token ring packets due to variable length header */ + /* if we want to get clever, insert the program here */ + + /* If we passed all the tests, ask for the whole packet. */ + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + + /* Otherwise, drop it. */ + BPF_STMT(BPF_RET+BPF_K, 0), +}; + +int dhcp_bpf_tr_filter_len = (sizeof dhcp_bpf_tr_filter / + sizeof (struct bpf_insn)); +#endif /* HAVE_TR_SUPPORT */ +#endif /* USE_LPF_RECEIVE || USE_BPF_RECEIVE */ + +#if defined (USE_BPF_RECEIVE) +void if_register_receive (info) + struct interface_info *info; +{ + int flag = 1; + struct bpf_version v; + u_int32_t addr; + struct bpf_program p; + u_int32_t bits; +#ifdef DEC_FDDI + int link_layer; +#endif /* DEC_FDDI */ + + /* Open a BPF device and hang it on this interface... */ + info -> rfdesc = if_register_bpf (info); + + /* Make sure the BPF version is in range... */ + if (ioctl (info -> rfdesc, BIOCVERSION, &v) < 0) + log_fatal ("Can't get BPF version: %m"); + + if (v.bv_major != BPF_MAJOR_VERSION || + v.bv_minor < BPF_MINOR_VERSION) + log_fatal ("BPF version mismatch - recompile DHCP!"); + + /* Set immediate mode so that reads return as soon as a packet + comes in, rather than waiting for the input buffer to fill with + packets. */ + if (ioctl (info -> rfdesc, BIOCIMMEDIATE, &flag) < 0) + log_fatal ("Can't set immediate mode on bpf device: %m"); + +#ifdef NEED_OSF_PFILT_HACKS + /* Allow the copyall flag to be set... */ + if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0) + log_fatal ("Can't set ALLOWCOPYALL: %m"); + + /* Clear all the packet filter mode bits first... */ + bits = 0; + if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) + log_fatal ("Can't clear pfilt bits: %m"); + + /* Set the ENBATCH, ENCOPYALL, ENBPFHDR bits... */ + bits = ENBATCH | ENCOPYALL | ENBPFHDR; + if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) + log_fatal ("Can't set ENBATCH|ENCOPYALL|ENBPFHDR: %m"); +#endif + /* Get the required BPF buffer length from the kernel. */ + if (ioctl (info -> rfdesc, BIOCGBLEN, &info -> rbuf_max) < 0) + log_fatal ("Can't get bpf buffer length: %m"); + info -> rbuf = dmalloc (info -> rbuf_max, MDL); + if (!info -> rbuf) + log_fatal ("Can't allocate %ld bytes for bpf input buffer.", + (long)(info -> rbuf_max)); + info -> rbuf_offset = 0; + info -> rbuf_len = 0; + + /* Set up the bpf filter program structure. */ + p.bf_len = dhcp_bpf_filter_len; + +#ifdef DEC_FDDI + /* See if this is an FDDI interface, flag it for later. */ + if (ioctl(info -> rfdesc, BIOCGDLT, &link_layer) >= 0 && + link_layer == DLT_FDDI) { + if (!bpf_fddi_filter) { + bpf_fddi_filter = dmalloc (sizeof bpf_fddi_filter, + MDL); + if (!bpf_fddi_filter) + log_fatal ("No memory for FDDI filter."); + memcpy (bpf_fddi_filter, + dhcp_bpf_filter, sizeof dhcp_bpf_filter); + /* Patch the BPF program to account for the difference + in length between ethernet headers (14), FDDI and + 802.2 headers (16 +8=24, +10). + XXX changes to filter program may require changes to + XXX the insn number(s) used below! */ + bpf_fddi_filter[0].k += 10; + bpf_fddi_filter[2].k += 10; + bpf_fddi_filter[4].k += 10; + bpf_fddi_filter[6].k += 10; + bpf_fddi_filter[7].k += 10; + } + p.bf_insns = bpf_fddi_filter; + } else +#endif /* DEC_FDDI */ + p.bf_insns = dhcp_bpf_filter; + + /* Patch the server port into the BPF program... + XXX changes to filter program may require changes + to the insn number(s) used below! XXX */ + dhcp_bpf_filter [8].k = ntohs (local_port); + + if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0) + log_fatal ("Can't install packet filter program: %m"); + if (!quiet_interface_discovery) + log_info ("Listening on BPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + close (info -> rfdesc); + info -> rfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling input on BPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_BPF_RECEIVE */ + +#ifdef USE_BPF_SEND +ssize_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + unsigned hbufp = 0, ibufp = 0; + double hw [4]; + double ip [32]; + struct iovec iov [3]; + int result; + int fudge; + + if (!strcmp (interface -> name, "fallback")) + return send_fallback (interface, packet, raw, + len, from, to, hto); + + /* Assemble the headers... */ + assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto); + assemble_udp_ip_header (interface, + (unsigned char *)ip, &ibufp, from.s_addr, + to -> sin_addr.s_addr, to -> sin_port, + (unsigned char *)raw, len); + + /* Fire it off */ + iov [0].iov_base = ((char *)hw); + iov [0].iov_len = hbufp; + iov [1].iov_base = ((char *)ip); + iov [1].iov_len = ibufp; + iov [2].iov_base = (char *)raw; + iov [2].iov_len = len; + + result = writev(interface -> wfdesc, iov, 3); + if (result < 0) + log_error ("send_packet: %m"); + return result; +} +#endif /* USE_BPF_SEND */ + +#ifdef USE_BPF_RECEIVE +ssize_t receive_packet (interface, buf, len, from, hfrom) + struct interface_info *interface; + unsigned char *buf; + size_t len; + struct sockaddr_in *from; + struct hardware *hfrom; +{ + int length = 0; + int offset = 0; + struct bpf_hdr hdr; + + /* All this complexity is because BPF doesn't guarantee + that only one packet will be returned at a time. We're + getting what we deserve, though - this is a terrible abuse + of the BPF interface. Sigh. */ + + /* Process packets until we get one we can return or until we've + done a read and gotten nothing we can return... */ + + do { + /* If the buffer is empty, fill it. */ + if (interface -> rbuf_offset == interface -> rbuf_len) { + length = read (interface -> rfdesc, + interface -> rbuf, + (size_t)interface -> rbuf_max); + if (length <= 0) { +#ifdef __FreeBSD__ + if (errno == ENXIO) { +#else + if (errno == EIO) { +#endif + dhcp_interface_remove + ((omapi_object_t *)interface, + (omapi_object_t *)0); + } + return length; + } + interface -> rbuf_offset = 0; + interface -> rbuf_len = BPF_WORDALIGN (length); + } + + /* If there isn't room for a whole bpf header, something went + wrong, but we'll ignore it and hope it goes away... XXX */ + if (interface -> rbuf_len - + interface -> rbuf_offset < sizeof hdr) { + interface -> rbuf_offset = interface -> rbuf_len; + continue; + } + + /* Copy out a bpf header... */ + memcpy (&hdr, &interface -> rbuf [interface -> rbuf_offset], + sizeof hdr); + + /* If the bpf header plus data doesn't fit in what's left + of the buffer, stick head in sand yet again... */ + if (interface -> rbuf_offset + + hdr.bh_hdrlen + hdr.bh_caplen > interface -> rbuf_len) { + interface -> rbuf_offset = interface -> rbuf_len; + continue; + } + + /* If the captured data wasn't the whole packet, or if + the packet won't fit in the input buffer, all we + can do is drop it. */ + if (hdr.bh_caplen != hdr.bh_datalen) { + interface -> rbuf_offset = + BPF_WORDALIGN (interface -> rbuf_offset + + hdr.bh_hdrlen + hdr.bh_caplen); + continue; + } + + /* Skip over the BPF header... */ + interface -> rbuf_offset += hdr.bh_hdrlen; + + /* Decode the physical header... */ + offset = decode_hw_header (interface, + interface -> rbuf, + interface -> rbuf_offset, + hfrom); + + /* If a physical layer checksum failed (dunno of any + physical layer that supports this, but WTH), skip this + packet. */ + if (offset < 0) { + interface -> rbuf_offset = + BPF_WORDALIGN (interface -> rbuf_offset + + hdr.bh_caplen); + continue; + } + interface -> rbuf_offset += offset; + hdr.bh_caplen -= offset; + + /* Decode the IP and UDP headers... */ + offset = decode_udp_ip_header (interface, + interface -> rbuf, + interface -> rbuf_offset, + from, + hdr.bh_caplen); + + /* If the IP or UDP checksum was bad, skip the packet... */ + if (offset < 0) { + interface -> rbuf_offset = + BPF_WORDALIGN (interface -> rbuf_offset + + hdr.bh_caplen); + continue; + } + interface -> rbuf_offset = interface -> rbuf_offset + offset; + hdr.bh_caplen -= offset; + + /* If there's not enough room to stash the packet data, + we have to skip it (this shouldn't happen in real + life, though). */ + if (hdr.bh_caplen > len) { + interface -> rbuf_offset = + BPF_WORDALIGN (interface -> rbuf_offset + + hdr.bh_caplen); + continue; + } + + /* Copy out the data in the packet... */ + memcpy (buf, interface -> rbuf + interface -> rbuf_offset, + hdr.bh_caplen); + interface -> rbuf_offset = + BPF_WORDALIGN (interface -> rbuf_offset + + hdr.bh_caplen); + return hdr.bh_caplen; + } while (!length); + return 0; +} + +int can_unicast_without_arp (ip) + struct interface_info *ip; +{ + return 1; +} + +int can_receive_unicast_unconfigured (ip) + struct interface_info *ip; +{ + return 1; +} + +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ + return 1; +} + +void maybe_setup_fallback () +{ + isc_result_t status; + struct interface_info *fbi = (struct interface_info *)0; + if (setup_fallback (&fbi, MDL)) { + if_register_fallback (fbi); + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); + } +} +#endif diff --git a/contrib/dhcp-3.0/common/comapi.c b/contrib/dhcp-3.0/common/comapi.c new file mode 100644 index 0000000000..9f0b965d88 --- /dev/null +++ b/contrib/dhcp-3.0/common/comapi.c @@ -0,0 +1,949 @@ +/* omapi.c + + OMAPI object interfaces for the DHCP server. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +/* Many, many thanks to Brian Murrell and BCtel for this code - BCtel + provided the funding that resulted in this code and the entire + OMAPI support library being written, and Brian helped brainstorm + and refine the requirements. To the extent that this code is + useful, you have Brian and BCtel to thank. Any limitations in the + code are a result of mistakes on my part. -- Ted Lemon */ + +#ifndef lint +static char copyright[] = +"$Id: comapi.c,v 1.9.2.7 2004/06/10 17:59:14 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include + +OMAPI_OBJECT_ALLOC (subnet, struct subnet, dhcp_type_subnet) +OMAPI_OBJECT_ALLOC (shared_network, struct shared_network, + dhcp_type_shared_network) +OMAPI_OBJECT_ALLOC (group_object, struct group_object, dhcp_type_group) +OMAPI_OBJECT_ALLOC (dhcp_control, dhcp_control_object_t, dhcp_type_control) + +omapi_object_type_t *dhcp_type_interface; +omapi_object_type_t *dhcp_type_group; +omapi_object_type_t *dhcp_type_shared_network; +omapi_object_type_t *dhcp_type_subnet; +omapi_object_type_t *dhcp_type_control; +dhcp_control_object_t *dhcp_control_object; + +void dhcp_common_objects_setup () +{ + isc_result_t status; + + status = omapi_object_type_register (&dhcp_type_control, + "control", + dhcp_control_set_value, + dhcp_control_get_value, + dhcp_control_destroy, + dhcp_control_signal_handler, + dhcp_control_stuff_values, + dhcp_control_lookup, + dhcp_control_create, + dhcp_control_remove, 0, 0, 0, + sizeof (dhcp_control_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register control object type: %s", + isc_result_totext (status)); + status = dhcp_control_allocate (&dhcp_control_object, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't make initial control object: %s", + isc_result_totext (status)); + dhcp_control_object -> state = server_startup; + + status = omapi_object_type_register (&dhcp_type_group, + "group", + dhcp_group_set_value, + dhcp_group_get_value, + dhcp_group_destroy, + dhcp_group_signal_handler, + dhcp_group_stuff_values, + dhcp_group_lookup, + dhcp_group_create, + dhcp_group_remove, 0, 0, 0, + sizeof (struct group_object), 0, + RC_MISC); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register group object type: %s", + isc_result_totext (status)); + + status = omapi_object_type_register (&dhcp_type_subnet, + "subnet", + dhcp_subnet_set_value, + dhcp_subnet_get_value, + dhcp_subnet_destroy, + dhcp_subnet_signal_handler, + dhcp_subnet_stuff_values, + dhcp_subnet_lookup, + dhcp_subnet_create, + dhcp_subnet_remove, 0, 0, 0, + sizeof (struct subnet), 0, + RC_MISC); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register subnet object type: %s", + isc_result_totext (status)); + + status = omapi_object_type_register + (&dhcp_type_shared_network, + "shared-network", + dhcp_shared_network_set_value, + dhcp_shared_network_get_value, + dhcp_shared_network_destroy, + dhcp_shared_network_signal_handler, + dhcp_shared_network_stuff_values, + dhcp_shared_network_lookup, + dhcp_shared_network_create, + dhcp_shared_network_remove, 0, 0, 0, + sizeof (struct shared_network), 0, RC_MISC); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register shared network object type: %s", + isc_result_totext (status)); + + interface_setup (); +} + +isc_result_t dhcp_group_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + struct group_object *group; + isc_result_t status; + int foo; + + if (h -> type != dhcp_type_group) + return ISC_R_INVALIDARG; + group = (struct group_object *)h; + + /* XXX For now, we can only set these values on new group objects. + XXX Soon, we need to be able to update group objects. */ + if (!omapi_ds_strcmp (name, "name")) { + if (group -> name) + return ISC_R_EXISTS; + if (value -> type == omapi_datatype_data || + value -> type == omapi_datatype_string) { + group -> name = dmalloc (value -> u.buffer.len + 1, + MDL); + if (!group -> name) + return ISC_R_NOMEMORY; + memcpy (group -> name, + value -> u.buffer.value, + value -> u.buffer.len); + group -> name [value -> u.buffer.len] = 0; + } else + return ISC_R_INVALIDARG; + return ISC_R_SUCCESS; + } + + if (!omapi_ds_strcmp (name, "statements")) { + if (group -> group && group -> group -> statements) + return ISC_R_EXISTS; + if (!group -> group) { + if (!clone_group (&group -> group, root_group, MDL)) + return ISC_R_NOMEMORY; + } + if (value -> type == omapi_datatype_data || + value -> type == omapi_datatype_string) { + struct parse *parse; + int lose = 0; + parse = (struct parse *)0; + status = new_parse (&parse, -1, + (char *)value -> u.buffer.value, + value -> u.buffer.len, + "network client", 0); + if (status != ISC_R_SUCCESS) + return status; + if (!(parse_executable_statements + (&group -> group -> statements, parse, &lose, + context_any))) { + end_parse (&parse); + return ISC_R_BADPARSE; + } + end_parse (&parse); + return ISC_R_SUCCESS; + } else + return ISC_R_INVALIDARG; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) + return status; + } + + return ISC_R_NOTFOUND; +} + + +isc_result_t dhcp_group_get_value (omapi_object_t *h, omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + struct group_object *group; + isc_result_t status; + struct data_string ip_addrs; + + if (h -> type != dhcp_type_group) + return ISC_R_INVALIDARG; + group = (struct group_object *)h; + + if (!omapi_ds_strcmp (name, "name")) + return omapi_make_string_value (value, + name, group -> name, MDL); + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_group_destroy (omapi_object_t *h, const char *file, int line) +{ + struct group_object *group, *t; + isc_result_t status; + + if (h -> type != dhcp_type_group) + return ISC_R_INVALIDARG; + group = (struct group_object *)h; + + if (group -> name) { + if (group_name_hash) { + t = (struct group_object *)0; + if (group_hash_lookup (&t, group_name_hash, + group -> name, + strlen (group -> name), MDL)) { + group_hash_delete (group_name_hash, + group -> name, + strlen (group -> name), + MDL); + group_object_dereference (&t, MDL); + } + } + dfree (group -> name, file, line); + group -> name = (char *)0; + } + if (group -> group) + group_dereference (&group -> group, MDL); + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_group_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + struct group_object *group, *t; + isc_result_t status; + int updatep = 0; + + if (h -> type != dhcp_type_group) + return ISC_R_INVALIDARG; + group = (struct group_object *)h; + + if (!strcmp (name, "updated")) { + /* A group object isn't valid if a subgroup hasn't yet been + associated with it. */ + if (!group -> group) + return ISC_R_INVALIDARG; + + /* Group objects always have to have names. */ + if (!group -> name) { + char hnbuf [64]; + sprintf (hnbuf, "ng%08lx%08lx", + (unsigned long)cur_time, + (unsigned long)group); + group -> name = dmalloc (strlen (hnbuf) + 1, MDL); + if (!group -> name) + return ISC_R_NOMEMORY; + strcpy (group -> name, hnbuf); + } + + supersede_group (group, 1); + updatep = 1; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> signal_handler)) + (h -> inner, name, ap)); + if (status == ISC_R_SUCCESS) + return status; + } + if (updatep) + return ISC_R_SUCCESS; + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_group_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + struct group_object *group; + isc_result_t status; + + if (h -> type != dhcp_type_group) + return ISC_R_INVALIDARG; + group = (struct group_object *)h; + + /* Write out all the values. */ + if (group -> name) { + status = omapi_connection_put_name (c, "name"); + if (status != ISC_R_SUCCESS) + return status; + status = omapi_connection_put_string (c, group -> name); + if (status != ISC_R_SUCCESS) + return status; + } + + /* Write out the inner object, if any. */ + if (h -> inner && h -> inner -> type -> stuff_values) { + status = ((*(h -> inner -> type -> stuff_values)) + (c, id, h -> inner)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_group_lookup (omapi_object_t **lp, + omapi_object_t *id, omapi_object_t *ref) +{ + omapi_value_t *tv = (omapi_value_t *)0; + isc_result_t status; + struct group_object *group; + + if (!ref) + return ISC_R_NOKEYS; + + /* First see if we were sent a handle. */ + status = omapi_get_value_str (ref, id, "handle", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_handle_td_lookup (lp, tv -> value); + + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) + return status; + + /* Don't return the object if the type is wrong. */ + if ((*lp) -> type != dhcp_type_group) { + omapi_object_dereference (lp, MDL); + return ISC_R_INVALIDARG; + } + } + + /* Now look for a name. */ + status = omapi_get_value_str (ref, id, "name", &tv); + if (status == ISC_R_SUCCESS) { + group = (struct group_object *)0; + if (group_name_hash && + group_hash_lookup (&group, group_name_hash, + (const char *) + tv -> value -> u.buffer.value, + tv -> value -> u.buffer.len, MDL)) { + omapi_value_dereference (&tv, MDL); + + if (*lp && *lp != (omapi_object_t *)group) { + group_object_dereference (&group, MDL); + omapi_object_dereference (lp, MDL); + return ISC_R_KEYCONFLICT; + } else if (!*lp) { + /* XXX fix so that hash lookup itself creates + XXX the reference. */ + omapi_object_reference (lp, + (omapi_object_t *)group, + MDL); + group_object_dereference (&group, MDL); + } + } else if (!*lp) + return ISC_R_NOTFOUND; + } + + /* If we get to here without finding a group, no valid key was + specified. */ + if (!*lp) + return ISC_R_NOKEYS; + + if (((struct group_object *)(*lp)) -> flags & GROUP_OBJECT_DELETED) { + omapi_object_dereference (lp, MDL); + return ISC_R_NOTFOUND; + } + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_group_create (omapi_object_t **lp, + omapi_object_t *id) +{ + struct group_object *group; + isc_result_t status; + group = (struct group_object *)0; + + status = group_object_allocate (&group, MDL); + if (status != ISC_R_SUCCESS) + return status; + group -> flags = GROUP_OBJECT_DYNAMIC; + status = omapi_object_reference (lp, (omapi_object_t *)group, MDL); + group_object_dereference (&group, MDL); + return status; +} + +isc_result_t dhcp_group_remove (omapi_object_t *lp, + omapi_object_t *id) +{ + struct group_object *group; + isc_result_t status; + if (lp -> type != dhcp_type_group) + return ISC_R_INVALIDARG; + group = (struct group_object *)lp; + + group -> flags |= GROUP_OBJECT_DELETED; + if (group_write_hook) { + if (!(*group_write_hook) (group)) + return ISC_R_IOERROR; + } + + status = dhcp_group_destroy ((omapi_object_t *)group, MDL); + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_control_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + dhcp_control_object_t *control; + isc_result_t status; + int foo; + unsigned long newstate; + + if (h -> type != dhcp_type_control) + return ISC_R_INVALIDARG; + control = (dhcp_control_object_t *)h; + + if (!omapi_ds_strcmp (name, "state")) { + status = omapi_get_int_value (&newstate, value); + if (status != ISC_R_SUCCESS) + return status; + status = dhcp_set_control_state (control -> state, newstate); + if (status == ISC_R_SUCCESS) + control -> state = value -> u.integer; + return status; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) + return status; + } + + return ISC_R_NOTFOUND; +} + + +isc_result_t dhcp_control_get_value (omapi_object_t *h, omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + dhcp_control_object_t *control; + isc_result_t status; + struct data_string ip_addrs; + + if (h -> type != dhcp_type_control) + return ISC_R_INVALIDARG; + control = (dhcp_control_object_t *)h; + + if (!omapi_ds_strcmp (name, "state")) + return omapi_make_int_value (value, + name, (int)control -> state, MDL); + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_control_destroy (omapi_object_t *h, + const char *file, int line) +{ + dhcp_control_object_t *control, *t; + isc_result_t status; + + if (h -> type != dhcp_type_control) + return ISC_R_INVALIDARG; + + /* Can't destroy the control object. */ + return ISC_R_NOPERM; +} + +isc_result_t dhcp_control_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + dhcp_control_object_t *control, *t; + isc_result_t status; + int updatep = 0; + + if (h -> type != dhcp_type_control) + return ISC_R_INVALIDARG; + control = (dhcp_control_object_t *)h; + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> signal_handler)) + (h -> inner, name, ap)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_control_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + dhcp_control_object_t *control; + isc_result_t status; + + if (h -> type != dhcp_type_control) + return ISC_R_INVALIDARG; + control = (dhcp_control_object_t *)h; + + /* Write out all the values. */ + status = omapi_connection_put_name (c, "state"); + if (status != ISC_R_SUCCESS) + return status; + status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); + if (status != ISC_R_SUCCESS) + return status; + status = omapi_connection_put_uint32 (c, control -> state); + if (status != ISC_R_SUCCESS) + return status; + + /* Write out the inner object, if any. */ + if (h -> inner && h -> inner -> type -> stuff_values) { + status = ((*(h -> inner -> type -> stuff_values)) + (c, id, h -> inner)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_control_lookup (omapi_object_t **lp, + omapi_object_t *id, omapi_object_t *ref) +{ + omapi_value_t *tv = (omapi_value_t *)0; + isc_result_t status; + dhcp_control_object_t *control; + + /* First see if we were sent a handle. */ + if (ref) { + status = omapi_get_value_str (ref, id, "handle", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_handle_td_lookup (lp, tv -> value); + + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) + return status; + + /* Don't return the object if the type is wrong. */ + if ((*lp) -> type != dhcp_type_control) { + omapi_object_dereference (lp, MDL); + return ISC_R_INVALIDARG; + } + } + } + + /* Otherwise, stop playing coy - there's only one control object, + so we can just return it. */ + dhcp_control_reference ((dhcp_control_object_t **)lp, + dhcp_control_object, MDL); + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_control_create (omapi_object_t **lp, + omapi_object_t *id) +{ + /* Can't create a control object - there can be only one. */ + return ISC_R_NOPERM; +} + +isc_result_t dhcp_control_remove (omapi_object_t *lp, + omapi_object_t *id) +{ + /* Form is emptiness; emptiness form. The control object + cannot go out of existance. */ + return ISC_R_NOPERM; +} + +isc_result_t dhcp_subnet_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + struct subnet *subnet; + isc_result_t status; + int foo; + + if (h -> type != dhcp_type_subnet) + return ISC_R_INVALIDARG; + subnet = (struct subnet *)h; + + /* No values to set yet. */ + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) + return status; + } + + return ISC_R_NOTFOUND; +} + + +isc_result_t dhcp_subnet_get_value (omapi_object_t *h, omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + struct subnet *subnet; + isc_result_t status; + + if (h -> type != dhcp_type_subnet) + return ISC_R_INVALIDARG; + subnet = (struct subnet *)h; + + /* No values to get yet. */ + + /* Try to find some inner object that can provide the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_subnet_destroy (omapi_object_t *h, const char *file, int line) +{ + struct subnet *subnet; + isc_result_t status; + + if (h -> type != dhcp_type_subnet) + return ISC_R_INVALIDARG; + subnet = (struct subnet *)h; + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + if (subnet -> next_subnet) + subnet_dereference (&subnet -> next_subnet, file, line); + if (subnet -> next_sibling) + subnet_dereference (&subnet -> next_sibling, file, line); + if (subnet -> shared_network) + shared_network_dereference (&subnet -> shared_network, + file, line); + if (subnet -> interface) + interface_dereference (&subnet -> interface, file, line); + if (subnet -> group) + group_dereference (&subnet -> group, file, line); +#endif + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_subnet_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + struct subnet *subnet; + isc_result_t status; + int updatep = 0; + + if (h -> type != dhcp_type_subnet) + return ISC_R_INVALIDARG; + subnet = (struct subnet *)h; + + /* Can't write subnets yet. */ + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> signal_handler)) + (h -> inner, name, ap)); + if (status == ISC_R_SUCCESS) + return status; + } + if (updatep) + return ISC_R_SUCCESS; + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_subnet_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + struct subnet *subnet; + isc_result_t status; + + if (h -> type != dhcp_type_subnet) + return ISC_R_INVALIDARG; + subnet = (struct subnet *)h; + + /* Can't stuff subnet values yet. */ + + /* Write out the inner object, if any. */ + if (h -> inner && h -> inner -> type -> stuff_values) { + status = ((*(h -> inner -> type -> stuff_values)) + (c, id, h -> inner)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_subnet_lookup (omapi_object_t **lp, + omapi_object_t *id, + omapi_object_t *ref) +{ + omapi_value_t *tv = (omapi_value_t *)0; + isc_result_t status; + struct subnet *subnet; + + /* Can't look up subnets yet. */ + + /* If we get to here without finding a subnet, no valid key was + specified. */ + if (!*lp) + return ISC_R_NOKEYS; + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_subnet_create (omapi_object_t **lp, + omapi_object_t *id) +{ + return ISC_R_NOTIMPLEMENTED; +} + +isc_result_t dhcp_subnet_remove (omapi_object_t *lp, + omapi_object_t *id) +{ + return ISC_R_NOTIMPLEMENTED; +} + +isc_result_t dhcp_shared_network_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + struct shared_network *shared_network; + isc_result_t status; + int foo; + + if (h -> type != dhcp_type_shared_network) + return ISC_R_INVALIDARG; + shared_network = (struct shared_network *)h; + + /* No values to set yet. */ + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) + return status; + } + + return ISC_R_NOTFOUND; +} + + +isc_result_t dhcp_shared_network_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + struct shared_network *shared_network; + isc_result_t status; + + if (h -> type != dhcp_type_shared_network) + return ISC_R_INVALIDARG; + shared_network = (struct shared_network *)h; + + /* No values to get yet. */ + + /* Try to find some inner object that can provide the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_shared_network_destroy (omapi_object_t *h, + const char *file, int line) +{ + struct shared_network *shared_network; + isc_result_t status; + + if (h -> type != dhcp_type_shared_network) + return ISC_R_INVALIDARG; + shared_network = (struct shared_network *)h; + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + if (shared_network -> next) + shared_network_dereference (&shared_network -> next, + file, line); + if (shared_network -> name) { + dfree (shared_network -> name, file, line); + shared_network -> name = 0; + } + if (shared_network -> subnets) + subnet_dereference (&shared_network -> subnets, file, line); + if (shared_network -> interface) + interface_dereference (&shared_network -> interface, + file, line); + if (shared_network -> pools) + omapi_object_dereference ((omapi_object_t **) + &shared_network -> pools, file, line); + if (shared_network -> group) + group_dereference (&shared_network -> group, file, line); +#if defined (FAILOVER_PROTOCOL) + if (shared_network -> failover_peer) + omapi_object_dereference ((omapi_object_t **) + &shared_network -> failover_peer, + file, line); +#endif +#endif /* DEBUG_MEMORY_LEAKAGE */ + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *h, + const char *name, + va_list ap) +{ + struct shared_network *shared_network; + isc_result_t status; + int updatep = 0; + + if (h -> type != dhcp_type_shared_network) + return ISC_R_INVALIDARG; + shared_network = (struct shared_network *)h; + + /* Can't write shared_networks yet. */ + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> signal_handler)) + (h -> inner, name, ap)); + if (status == ISC_R_SUCCESS) + return status; + } + if (updatep) + return ISC_R_SUCCESS; + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + struct shared_network *shared_network; + isc_result_t status; + + if (h -> type != dhcp_type_shared_network) + return ISC_R_INVALIDARG; + shared_network = (struct shared_network *)h; + + /* Can't stuff shared_network values yet. */ + + /* Write out the inner object, if any. */ + if (h -> inner && h -> inner -> type -> stuff_values) { + status = ((*(h -> inner -> type -> stuff_values)) + (c, id, h -> inner)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_shared_network_lookup (omapi_object_t **lp, + omapi_object_t *id, + omapi_object_t *ref) +{ + omapi_value_t *tv = (omapi_value_t *)0; + isc_result_t status; + struct shared_network *shared_network; + + /* Can't look up shared_networks yet. */ + + /* If we get to here without finding a shared_network, no valid key was + specified. */ + if (!*lp) + return ISC_R_NOKEYS; + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_shared_network_create (omapi_object_t **lp, + omapi_object_t *id) +{ + return ISC_R_NOTIMPLEMENTED; +} + +isc_result_t dhcp_shared_network_remove (omapi_object_t *lp, + omapi_object_t *id) +{ + return ISC_R_NOTIMPLEMENTED; +} + diff --git a/contrib/dhcp-3.0/common/conflex.c b/contrib/dhcp-3.0/common/conflex.c new file mode 100644 index 0000000000..7c809f9437 --- /dev/null +++ b/contrib/dhcp-3.0/common/conflex.c @@ -0,0 +1,1078 @@ +/* conflex.c + + Lexical scanner for dhcpd config file... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: conflex.c,v 1.92.2.9 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include + +static int get_char PROTO ((struct parse *)); +static enum dhcp_token get_token PROTO ((struct parse *)); +static void skip_to_eol PROTO ((struct parse *)); +static enum dhcp_token read_string PROTO ((struct parse *)); +static enum dhcp_token read_number PROTO ((int, struct parse *)); +static enum dhcp_token read_num_or_name PROTO ((int, struct parse *)); +static enum dhcp_token intern PROTO ((char *, enum dhcp_token)); + +isc_result_t new_parse (cfile, file, inbuf, buflen, name, eolp) + struct parse **cfile; + int file; + char *inbuf; + unsigned buflen; + const char *name; + int eolp; +{ + struct parse *tmp; + + tmp = dmalloc (sizeof (struct parse), MDL); + if (!tmp) + return ISC_R_NOMEMORY; + memset (tmp, 0, sizeof *tmp); + + tmp -> token = 0; + tmp -> tlname = name; + tmp -> lpos = tmp -> line = 1; + tmp -> cur_line = tmp -> line1; + tmp -> prev_line = tmp -> line2; + tmp -> token_line = tmp -> cur_line; + tmp -> cur_line [0] = tmp -> prev_line [0] = 0; + tmp -> warnings_occurred = 0; + tmp -> file = file; + tmp -> eol_token = eolp; + + tmp -> bufix = 0; + tmp -> buflen = buflen; + if (inbuf) { + tmp -> bufsiz = 0; + tmp -> inbuf = inbuf; + } else { + tmp -> inbuf = dmalloc (8192, MDL); + if (!tmp -> inbuf) { + dfree (tmp, MDL); + return ISC_R_NOMEMORY; + } + tmp -> bufsiz = 8192; + } + + *cfile = tmp; + return ISC_R_SUCCESS; +} + +isc_result_t end_parse (cfile) + struct parse **cfile; +{ + if ((*cfile) -> bufsiz) + dfree ((*cfile) -> inbuf, MDL); + dfree (*cfile, MDL); + *cfile = (struct parse *)0; + return ISC_R_SUCCESS; +} + +static int get_char (cfile) + struct parse *cfile; +{ + /* My kingdom for WITH... */ + int c; + + if (cfile -> bufix == cfile -> buflen) { + if (cfile -> file != -1) { + cfile -> buflen = + read (cfile -> file, + cfile -> inbuf, cfile -> bufsiz); + if (cfile -> buflen == 0) { + c = EOF; + cfile -> bufix = 0; + } else if (cfile -> buflen < 0) { + c = EOF; + cfile -> bufix = cfile -> buflen = 0; + } else { + c = cfile -> inbuf [0]; + cfile -> bufix = 1; + } + } else + c = EOF; + } else { + c = cfile -> inbuf [cfile -> bufix]; + cfile -> bufix++; + } + + if (!cfile -> ugflag) { + if (c == EOL) { + if (cfile -> cur_line == cfile -> line1) { + cfile -> cur_line = cfile -> line2; + cfile -> prev_line = cfile -> line1; + } else { + cfile -> cur_line = cfile -> line1; + cfile -> prev_line = cfile -> line2; + } + cfile -> line++; + cfile -> lpos = 1; + cfile -> cur_line [0] = 0; + } else if (c != EOF) { + if (cfile -> lpos <= 80) { + cfile -> cur_line [cfile -> lpos - 1] = c; + cfile -> cur_line [cfile -> lpos] = 0; + } + cfile -> lpos++; + } + } else + cfile -> ugflag = 0; + return c; +} + +static enum dhcp_token get_token (cfile) + struct parse *cfile; +{ + int c; + enum dhcp_token ttok; + static char tb [2]; + int l, p, u; + + do { + l = cfile -> line; + p = cfile -> lpos; + u = cfile -> ugflag; + + c = get_char (cfile); +#ifdef OLD_LEXER + if (c == '\n' && p == 1 && !u + && cfile -> comment_index < sizeof cfile -> comments) + cfile -> comments [cfile -> comment_index++] = '\n'; +#endif + + if (!(c == '\n' && cfile -> eol_token) + && isascii (c) && isspace (c)) + continue; + if (c == '#') { +#ifdef OLD_LEXER + if (cfile -> comment_index < sizeof cfile -> comments) + cfile -> comments [cfile -> comment_index++] = '#'; +#endif + skip_to_eol (cfile); + continue; + } + if (c == '"') { + cfile -> lexline = l; + cfile -> lexchar = p; + ttok = read_string (cfile); + break; + } + if ((isascii (c) && isdigit (c)) || c == '-') { + cfile -> lexline = l; + cfile -> lexchar = p; + ttok = read_number (c, cfile); + break; + } else if (isascii (c) && isalpha (c)) { + cfile -> lexline = l; + cfile -> lexchar = p; + ttok = read_num_or_name (c, cfile); + break; + } else if (c == EOF) { + ttok = END_OF_FILE; + cfile -> tlen = 0; + break; + } else { + cfile -> lexline = l; + cfile -> lexchar = p; + tb [0] = c; + tb [1] = 0; + cfile -> tval = tb; + cfile -> tlen = 1; + ttok = c; + break; + } + } while (1); + return ttok; +} + +enum dhcp_token next_token (rval, rlen, cfile) + const char **rval; + unsigned *rlen; + struct parse *cfile; +{ + int rv; + + if (cfile -> token) { + if (cfile -> lexline != cfile -> tline) + cfile -> token_line = cfile -> cur_line; + cfile -> lexchar = cfile -> tlpos; + cfile -> lexline = cfile -> tline; + rv = cfile -> token; + cfile -> token = 0; + } else { + rv = get_token (cfile); + cfile -> token_line = cfile -> cur_line; + } + if (rval) + *rval = cfile -> tval; + if (rlen) + *rlen = cfile -> tlen; +#ifdef DEBUG_TOKENS + fprintf (stderr, "%s:%d ", cfile -> tval, rv); +#endif + return rv; +} + +enum dhcp_token peek_token (rval, rlen, cfile) + const char **rval; + unsigned int *rlen; + struct parse *cfile; +{ + int x; + + if (!cfile -> token) { + cfile -> tlpos = cfile -> lexchar; + cfile -> tline = cfile -> lexline; + cfile -> token = get_token (cfile); + if (cfile -> lexline != cfile -> tline) + cfile -> token_line = cfile -> prev_line; + + x = cfile -> lexchar; + cfile -> lexchar = cfile -> tlpos; + cfile -> tlpos = x; + + x = cfile -> lexline; + cfile -> lexline = cfile -> tline; + cfile -> tline = x; + } + if (rval) + *rval = cfile -> tval; + if (rlen) + *rlen = cfile -> tlen; +#ifdef DEBUG_TOKENS + fprintf (stderr, "(%s:%d) ", cfile -> tval, cfile -> token); +#endif + return cfile -> token; +} + +static void skip_to_eol (cfile) + struct parse *cfile; +{ + int c; + do { + c = get_char (cfile); + if (c == EOF) + return; +#ifdef OLD_LEXER + if (cfile -> comment_index < sizeof (cfile -> comments)) + comments [cfile -> comment_index++] = c; +#endif + if (c == EOL) { + return; + } + } while (1); +} + +static enum dhcp_token read_string (cfile) + struct parse *cfile; +{ + int i; + int bs = 0; + int c; + int value = 0; + int hex = 0; + + for (i = 0; i < sizeof cfile -> tokbuf; i++) { + again: + c = get_char (cfile); + if (c == EOF) { + parse_warn (cfile, "eof in string constant"); + break; + } + if (bs == 1) { + switch (c) { + case 't': + cfile -> tokbuf [i] = '\t'; + break; + case 'r': + cfile -> tokbuf [i] = '\r'; + break; + case 'n': + cfile -> tokbuf [i] = '\n'; + break; + case 'b': + cfile -> tokbuf [i] = '\b'; + break; + case '0': + case '1': + case '2': + case '3': + hex = 0; + value = c - '0'; + ++bs; + goto again; + case 'x': + hex = 1; + value = 0; + ++bs; + goto again; + default: + cfile -> tokbuf [i] = c; + bs = 0; + break; + } + bs = 0; + } else if (bs > 1) { + if (hex) { + if (c >= '0' && c <= '9') { + value = value * 16 + (c - '0'); + } else if (c >= 'a' && c <= 'f') { + value = value * 16 + (c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + value = value * 16 + (c - 'A' + 10); + } else { + parse_warn (cfile, + "invalid hex digit: %x", + c); + bs = 0; + continue; + } + if (++bs == 4) { + cfile -> tokbuf [i] = value; + bs = 0; + } else + goto again; + } else { + if (c >= '0' && c <= '9') { + value = value * 8 + (c - '0'); + } else { + if (value != 0) { + parse_warn (cfile, + "invalid octal digit %x", + c); + continue; + } else + cfile -> tokbuf [i] = 0; + bs = 0; + } + if (++bs == 4) { + cfile -> tokbuf [i] = value; + bs = 0; + } else + goto again; + } + } else if (c == '\\') { + bs = 1; + goto again; + } else if (c == '"') + break; + else + cfile -> tokbuf [i] = c; + } + /* Normally, I'd feel guilty about this, but we're talking about + strings that'll fit in a DHCP packet here... */ + if (i == sizeof cfile -> tokbuf) { + parse_warn (cfile, + "string constant larger than internal buffer"); + --i; + } + cfile -> tokbuf [i] = 0; + cfile -> tlen = i; + cfile -> tval = cfile -> tokbuf; + return STRING; +} + +static enum dhcp_token read_number (c, cfile) + int c; + struct parse *cfile; +{ + int seenx = 0; + int i = 0; + int token = NUMBER; + + cfile -> tokbuf [i++] = c; + for (; i < sizeof cfile -> tokbuf; i++) { + c = get_char (cfile); + if (!seenx && c == 'x') { + seenx = 1; +#ifndef OLD_LEXER + } else if (isascii (c) && !isxdigit (c) && + (c == '-' || c == '_' || isalpha (c))) { + token = NAME; + } else if (isascii (c) && !isdigit (c) && isxdigit (c)) { + token = NUMBER_OR_NAME; +#endif + } else if (!isascii (c) || !isxdigit (c)) { + if (c != EOF) { + cfile -> bufix--; + cfile -> ugflag = 1; + } + break; + } + cfile -> tokbuf [i] = c; + } + if (i == sizeof cfile -> tokbuf) { + parse_warn (cfile, + "numeric token larger than internal buffer"); + --i; + } + cfile -> tokbuf [i] = 0; + cfile -> tlen = i; + cfile -> tval = cfile -> tokbuf; + return token; +} + +static enum dhcp_token read_num_or_name (c, cfile) + int c; + struct parse *cfile; +{ + int i = 0; + enum dhcp_token rv = NUMBER_OR_NAME; + cfile -> tokbuf [i++] = c; + for (; i < sizeof cfile -> tokbuf; i++) { + c = get_char (cfile); + if (!isascii (c) || + (c != '-' && c != '_' && !isalnum (c))) { + if (c != EOF) { + cfile -> bufix--; + cfile -> ugflag = 1; + } + break; + } + if (!isxdigit (c)) + rv = NAME; + cfile -> tokbuf [i] = c; + } + if (i == sizeof cfile -> tokbuf) { + parse_warn (cfile, "token larger than internal buffer"); + --i; + } + cfile -> tokbuf [i] = 0; + cfile -> tlen = i; + cfile -> tval = cfile -> tokbuf; + return intern (cfile -> tval, rv); +} + +static enum dhcp_token intern (atom, dfv) + char *atom; + enum dhcp_token dfv; +{ + if (!isascii (atom [0])) + return dfv; + + switch (tolower (atom [0])) { + case '-': + if (atom [1] == 0) + return MINUS; + break; + + case 'a': + if (!strncasecmp (atom + 1, "uth", 3)) { + if (!strncasecmp (atom + 3, "uthenticat", 10)) { + if (!strcasecmp (atom + 13, "ed")) + return AUTHENTICATED; + if (!strcasecmp (atom + 13, "ion")) + return AUTHENTICATION; + break; + } + if (!strcasecmp (atom + 1, "uthoritative")) + return AUTHORITATIVE; + break; + } + if (!strcasecmp (atom + 1, "nd")) + return AND; + if (!strcasecmp (atom + 1, "ppend")) + return APPEND; + if (!strcasecmp (atom + 1, "llow")) + return ALLOW; + if (!strcasecmp (atom + 1, "lias")) + return ALIAS; + if (!strcasecmp (atom + 1, "lgorithm")) + return ALGORITHM; + if (!strcasecmp (atom + 1, "bandoned")) + return TOKEN_ABANDONED; + if (!strcasecmp (atom + 1, "dd")) + return TOKEN_ADD; + if (!strcasecmp (atom + 1, "ll")) + return ALL; + if (!strcasecmp (atom + 1, "t")) + return AT; + if (!strcasecmp (atom + 1, "rray")) + return ARRAY; + if (!strcasecmp (atom + 1, "ddress")) + return ADDRESS; + if (!strcasecmp (atom + 1, "ctive")) + return TOKEN_ACTIVE; + break; + case 'b': + if (!strcasecmp (atom + 1, "ackup")) + return TOKEN_BACKUP; + if (!strcasecmp (atom + 1, "ootp")) + return TOKEN_BOOTP; + if (!strcasecmp (atom + 1, "inding")) + return BINDING; + if (!strcasecmp (atom + 1, "inary-to-ascii")) + return BINARY_TO_ASCII; + if (!strcasecmp (atom + 1, "ackoff-cutoff")) + return BACKOFF_CUTOFF; + if (!strcasecmp (atom + 1, "ooting")) + return BOOTING; + if (!strcasecmp (atom + 1, "oot-unknown-clients")) + return BOOT_UNKNOWN_CLIENTS; + if (!strcasecmp (atom + 1, "reak")) + return BREAK; + if (!strcasecmp (atom + 1, "illing")) + return BILLING; + if (!strcasecmp (atom + 1, "oolean")) + return BOOLEAN; + if (!strcasecmp (atom + 1, "alance")) + return BALANCE; + if (!strcasecmp (atom + 1, "ound")) + return BOUND; + break; + case 'c': + if (!strcasecmp (atom + 1, "ase")) + return CASE; + if (!strcasecmp (atom + 1, "ommit")) + return COMMIT; + if (!strcasecmp (atom + 1, "ode")) + return CODE; + if (!strcasecmp (atom + 1, "onfig-option")) + return CONFIG_OPTION; + if (!strcasecmp (atom + 1, "heck")) + return CHECK; + if (!strcasecmp (atom + 1, "lass")) + return CLASS; + if (!strcasecmp (atom + 1, "lose")) + return TOKEN_CLOSE; + if (!strcasecmp (atom + 1, "reate")) + return TOKEN_CREATE; + if (!strcasecmp (atom + 1, "iaddr")) + return CIADDR; + if (!strncasecmp (atom + 1, "lient", 5)) { + if (!strcasecmp (atom + 6, "-identifier")) + return CLIENT_IDENTIFIER; + if (!strcasecmp (atom + 6, "-hostname")) + return CLIENT_HOSTNAME; + if (!strcasecmp (atom + 6, "-state")) + return CLIENT_STATE; + if (!strcasecmp (atom + 6, "-updates")) + return CLIENT_UPDATES; + if (!strcasecmp (atom + 6, "s")) + return CLIENTS; + } + if (!strcasecmp (atom + 1, "oncat")) + return CONCAT; + if (!strcasecmp (atom + 1, "onnect")) + return CONNECT; + if (!strcasecmp (atom + 1, "ommunications-interrupted")) + return COMMUNICATIONS_INTERRUPTED; + if (!strcasecmp (atom + 1, "ltt")) + return CLTT; + break; + case 'd': + if (!strcasecmp (atom + 1, "ns-update")) + return DNS_UPDATE; + if (!strcasecmp (atom + 1, "ns-delete")) + return DNS_DELETE; + if (!strcasecmp (atom + 1, "omain")) + return DOMAIN; + if (!strcasecmp (atom + 1, "omain-name")) + return DOMAIN_NAME; + if (!strcasecmp (atom + 1, "o-forward-update")) + return DO_FORWARD_UPDATE; + if (!strcasecmp (atom + 1, "ebug")) + return TOKEN_DEBUG; + if (!strcasecmp (atom + 1, "eny")) + return DENY; + if (!strcasecmp (atom + 1, "eleted")) + return TOKEN_DELETED; + if (!strcasecmp (atom + 1, "elete")) + return TOKEN_DELETE; + if (!strncasecmp (atom + 1, "efault", 6)) { + if (!atom [7]) + return DEFAULT; + if (!strcasecmp (atom + 7, "-lease-time")) + return DEFAULT_LEASE_TIME; + break; + } + if (!strncasecmp (atom + 1, "ynamic", 6)) { + if (!atom [7]) + return DYNAMIC; + if (!strncasecmp (atom + 7, "-bootp", 6)) { + if (!atom [13]) + return DYNAMIC_BOOTP; + if (!strcasecmp (atom + 13, "-lease-cutoff")) + return DYNAMIC_BOOTP_LEASE_CUTOFF; + if (!strcasecmp (atom + 13, "-lease-length")) + return DYNAMIC_BOOTP_LEASE_LENGTH; + break; + } + } + if (!strcasecmp (atom + 1, "uplicates")) + return DUPLICATES; + if (!strcasecmp (atom + 1, "eclines")) + return DECLINES; + if (!strncasecmp (atom + 1, "efine", 5)) { + if (!strcasecmp (atom + 6, "d")) + return DEFINED; + if (!atom [6]) + return DEFINE; + } + break; + case 'e': + if (isascii (atom [1]) && tolower (atom [1]) == 'x') { + if (!strcasecmp (atom + 2, "tract-int")) + return EXTRACT_INT; + if (!strcasecmp (atom + 2, "ists")) + return EXISTS; + if (!strcasecmp (atom + 2, "piry")) + return EXPIRY; + if (!strcasecmp (atom + 2, "pire")) + return EXPIRE; + if (!strcasecmp (atom + 2, "pired")) + return TOKEN_EXPIRED; + } + if (!strcasecmp (atom + 1, "ncode-int")) + return ENCODE_INT; + if (!strcasecmp (atom + 1, "thernet")) + return ETHERNET; + if (!strcasecmp (atom + 1, "nds")) + return ENDS; + if (!strncasecmp (atom + 1, "ls", 2)) { + if (!strcasecmp (atom + 3, "e")) + return ELSE; + if (!strcasecmp (atom + 3, "if")) + return ELSIF; + break; + } + if (!strcasecmp (atom + 1, "rror")) + return ERROR; + if (!strcasecmp (atom + 1, "val")) + return EVAL; + if (!strcasecmp (atom + 1, "ncapsulate")) + return ENCAPSULATE; + break; + case 'f': + if (!strcasecmp (atom + 1, "atal")) + return FATAL; + if (!strcasecmp (atom + 1, "ilename")) + return FILENAME; + if (!strcasecmp (atom + 1, "ixed-address")) + return FIXED_ADDR; + if (!strcasecmp (atom + 1, "ddi")) + return FDDI; + if (!strcasecmp (atom + 1, "ormerr")) + return NS_FORMERR; + if (!strcasecmp (atom + 1, "unction")) + return FUNCTION; + if (!strcasecmp (atom + 1, "ailover")) + return FAILOVER; + if (!strcasecmp (atom + 1, "ree")) + return TOKEN_FREE; + break; + case 'g': + if (!strcasecmp (atom + 1, "iaddr")) + return GIADDR; + if (!strcasecmp (atom + 1, "roup")) + return GROUP; + if (!strcasecmp (atom + 1, "et-lease-hostnames")) + return GET_LEASE_HOSTNAMES; + break; + case 'h': + if (!strcasecmp (atom + 1, "ba")) + return HBA; + if (!strcasecmp (atom + 1, "ost")) + return HOST; + if (!strcasecmp (atom + 1, "ost-decl-name")) + return HOST_DECL_NAME; + if (!strcasecmp (atom + 1, "ardware")) + return HARDWARE; + if (!strcasecmp (atom + 1, "ostname")) + return HOSTNAME; + if (!strcasecmp (atom + 1, "elp")) + return TOKEN_HELP; + break; + case 'i': + if (!strcasecmp (atom + 1, "nclude")) + return INCLUDE; + if (!strcasecmp (atom + 1, "nteger")) + return INTEGER; + if (!strcasecmp (atom + 1, "nfinite")) + return INFINITE; + if (!strcasecmp (atom + 1, "nfo")) + return INFO; + if (!strcasecmp (atom + 1, "p-address")) + return IP_ADDRESS; + if (!strcasecmp (atom + 1, "nitial-interval")) + return INITIAL_INTERVAL; + if (!strcasecmp (atom + 1, "nterface")) + return INTERFACE; + if (!strcasecmp (atom + 1, "dentifier")) + return IDENTIFIER; + if (!strcasecmp (atom + 1, "f")) + return IF; + if (!strcasecmp (atom + 1, "s")) + return IS; + if (!strcasecmp (atom + 1, "gnore")) + return IGNORE; + break; + case 'k': + if (!strncasecmp (atom + 1, "nown", 4)) { + if (!strcasecmp (atom + 5, "-clients")) + return KNOWN_CLIENTS; + if (!atom[5]) + return KNOWN; + break; + } + if (!strcasecmp (atom + 1, "ey")) + return KEY; + break; + case 'l': + if (!strcasecmp (atom + 1, "ease")) + return LEASE; + if (!strcasecmp (atom + 1, "eased-address")) + return LEASED_ADDRESS; + if (!strcasecmp (atom + 1, "ease-time")) + return LEASE_TIME; + if (!strcasecmp (atom + 1, "imit")) + return LIMIT; + if (!strcasecmp (atom + 1, "et")) + return LET; + if (!strcasecmp (atom + 1, "oad")) + return LOAD; + if (!strcasecmp (atom + 1, "og")) + return LOG; + break; + case 'm': + if (!strncasecmp (atom + 1, "ax", 2)) { + if (!atom [3]) + return TOKEN_MAX; + if (!strcasecmp (atom + 3, "-lease-time")) + return MAX_LEASE_TIME; + if (!strcasecmp (atom + 3, "-transmit-idle")) + return MAX_TRANSMIT_IDLE; + if (!strcasecmp (atom + 3, "-response-delay")) + return MAX_RESPONSE_DELAY; + if (!strcasecmp (atom + 3, "-unacked-updates")) + return MAX_UNACKED_UPDATES; + } + if (!strncasecmp (atom + 1, "in-", 3)) { + if (!strcasecmp (atom + 4, "lease-time")) + return MIN_LEASE_TIME; + if (!strcasecmp (atom + 4, "secs")) + return MIN_SECS; + break; + } + if (!strncasecmp (atom + 1, "edi", 3)) { + if (!strcasecmp (atom + 4, "a")) + return MEDIA; + if (!strcasecmp (atom + 4, "um")) + return MEDIUM; + break; + } + if (!strcasecmp (atom + 1, "atch")) + return MATCH; + if (!strcasecmp (atom + 1, "embers")) + return MEMBERS; + if (!strcasecmp (atom + 1, "y")) + return MY; + if (!strcasecmp (atom + 1, "clt")) + return MCLT; + break; + case 'n': + if (!strcasecmp (atom + 1, "ormal")) + return NORMAL; + if (!strcasecmp (atom + 1, "ameserver")) + return NAMESERVER; + if (!strcasecmp (atom + 1, "etmask")) + return NETMASK; + if (!strcasecmp (atom + 1, "ever")) + return NEVER; + if (!strcasecmp (atom + 1, "ext-server")) + return NEXT_SERVER; + if (!strcasecmp (atom + 1, "ot")) + return TOKEN_NOT; + if (!strcasecmp (atom + 1, "o")) + return NO; + if (!strcasecmp (atom + 1, "s-update")) + return NS_UPDATE; + if (!strcasecmp (atom + 1, "oerror")) + return NS_NOERROR; + if (!strcasecmp (atom + 1, "otauth")) + return NS_NOTAUTH; + if (!strcasecmp (atom + 1, "otimp")) + return NS_NOTIMP; + if (!strcasecmp (atom + 1, "otzone")) + return NS_NOTZONE; + if (!strcasecmp (atom + 1, "xdomain")) + return NS_NXDOMAIN; + if (!strcasecmp (atom + 1, "xrrset")) + return NS_NXRRSET; + if (!strcasecmp (atom + 1, "ull")) + return TOKEN_NULL; + if (!strcasecmp (atom + 1, "ext")) + return TOKEN_NEXT; + if (!strcasecmp (atom + 1, "ew")) + return TOKEN_NEW; + break; + case 'o': + if (!strcasecmp (atom + 1, "mapi")) + return OMAPI; + if (!strcasecmp (atom + 1, "r")) + return OR; + if (!strcasecmp (atom + 1, "n")) + return ON; + if (!strcasecmp (atom + 1, "pen")) + return TOKEN_OPEN; + if (!strcasecmp (atom + 1, "ption")) + return OPTION; + if (!strcasecmp (atom + 1, "ne-lease-per-client")) + return ONE_LEASE_PER_CLIENT; + if (!strcasecmp (atom + 1, "f")) + return OF; + if (!strcasecmp (atom + 1, "wner")) + return OWNER; + break; + case 'p': + if (!strcasecmp (atom + 1, "repend")) + return PREPEND; + if (!strcasecmp (atom + 1, "acket")) + return PACKET; + if (!strcasecmp (atom + 1, "ool")) + return POOL; + if (!strcasecmp (atom + 1, "seudo")) + return PSEUDO; + if (!strcasecmp (atom + 1, "eer")) + return PEER; + if (!strcasecmp (atom + 1, "rimary")) + return PRIMARY; + if (!strncasecmp (atom + 1, "artner", 6)) { + if (!atom [7]) + return PARTNER; + if (!strcasecmp (atom + 7, "-down")) + return PARTNER_DOWN; + } + if (!strcasecmp (atom + 1, "ort")) + return PORT; + if (!strcasecmp (atom + 1, "otential-conflict")) + return POTENTIAL_CONFLICT; + if (!strcasecmp (atom + 1, "ick-first-value") || + !strcasecmp (atom + 1, "ick")) + return PICK; + if (!strcasecmp (atom + 1, "aused")) + return PAUSED; + break; + case 'r': + if (!strcasecmp (atom + 1, "esolution-interrupted")) + return RESOLUTION_INTERRUPTED; + if (!strcasecmp (atom + 1, "ange")) + return RANGE; + if (!strcasecmp (atom + 1, "ecover")) + return RECOVER; + if (!strcasecmp (atom + 1, "ecover-done")) + return RECOVER_DONE; + if (!strcasecmp (atom + 1, "ecover-wait")) + return RECOVER_WAIT; + if (!strcasecmp (atom + 1, "econtact-interval")) + return RECONTACT_INTERVAL; + if (!strcasecmp (atom + 1, "equest")) + return REQUEST; + if (!strcasecmp (atom + 1, "equire")) + return REQUIRE; + if (!strcasecmp (atom + 1, "equire")) + return REQUIRE; + if (!strcasecmp (atom + 1, "etry")) + return RETRY; + if (!strcasecmp (atom + 1, "eturn")) + return RETURN; + if (!strcasecmp (atom + 1, "enew")) + return RENEW; + if (!strcasecmp (atom + 1, "ebind")) + return REBIND; + if (!strcasecmp (atom + 1, "eboot")) + return REBOOT; + if (!strcasecmp (atom + 1, "eject")) + return REJECT; + if (!strcasecmp (atom + 1, "everse")) + return REVERSE; + if (!strcasecmp (atom + 1, "elease")) + return RELEASE; + if (!strcasecmp (atom + 1, "efused")) + return NS_REFUSED; + if (!strcasecmp (atom + 1, "eleased")) + return TOKEN_RELEASED; + if (!strcasecmp (atom + 1, "eset")) + return TOKEN_RESET; + if (!strcasecmp (atom + 1, "eserved")) + return TOKEN_RESERVED; + if (!strcasecmp (atom + 1, "emove")) + return REMOVE; + if (!strcasecmp (atom + 1, "efresh")) + return REFRESH; + break; + case 's': + if (!strcasecmp (atom + 1, "tate")) + return STATE; + if (!strcasecmp (atom + 1, "ecret")) + return SECRET; + if (!strcasecmp (atom + 1, "ervfail")) + return NS_SERVFAIL; + if (!strcasecmp (atom + 1, "witch")) + return SWITCH; + if (!strcasecmp (atom + 1, "igned")) + return SIGNED; + if (!strcasecmp (atom + 1, "tring")) + return STRING_TOKEN; + if (!strcasecmp (atom + 1, "uffix")) + return SUFFIX; + if (!strcasecmp (atom + 1, "earch")) + return SEARCH; + if (!strcasecmp (atom + 1, "tarts")) + return STARTS; + if (!strcasecmp (atom + 1, "iaddr")) + return SIADDR; + if (!strcasecmp (atom + 1, "hared-network")) + return SHARED_NETWORK; + if (!strcasecmp (atom + 1, "econdary")) + return SECONDARY; + if (!strcasecmp (atom + 1, "erver-name")) + return SERVER_NAME; + if (!strcasecmp (atom + 1, "erver-identifier")) + return SERVER_IDENTIFIER; + if (!strcasecmp (atom + 1, "erver")) + return SERVER; + if (!strcasecmp (atom + 1, "elect-timeout")) + return SELECT_TIMEOUT; + if (!strcasecmp (atom + 1, "elect")) + return SELECT; + if (!strcasecmp (atom + 1, "end")) + return SEND; + if (!strcasecmp (atom + 1, "cript")) + return SCRIPT; + if (!strcasecmp (atom + 1, "upersede")) + return SUPERSEDE; + if (!strncasecmp (atom + 1, "ub", 2)) { + if (!strcasecmp (atom + 3, "string")) + return SUBSTRING; + if (!strcasecmp (atom + 3, "net")) + return SUBNET; + if (!strcasecmp (atom + 3, "class")) + return SUBCLASS; + break; + } + if (!strcasecmp (atom + 1, "pawn")) + return SPAWN; + if (!strcasecmp (atom + 1, "pace")) + return SPACE; + if (!strcasecmp (atom + 1, "tatic")) + return STATIC; + if (!strcasecmp (atom + 1, "plit")) + return SPLIT; + if (!strcasecmp (atom + 1, "et")) + return TOKEN_SET; + if (!strcasecmp (atom + 1, "econds")) + return SECONDS; + if (!strcasecmp (atom + 1, "hutdown")) + return SHUTDOWN; + if (!strcasecmp (atom + 1, "tartup")) + return STARTUP; + break; + case 't': + if (!strcasecmp (atom + 1, "imestamp")) + return TIMESTAMP; + if (!strcasecmp (atom + 1, "imeout")) + return TIMEOUT; + if (!strcasecmp (atom + 1, "oken-ring")) + return TOKEN_RING; + if (!strcasecmp (atom + 1, "ext")) + return TEXT; + if (!strcasecmp (atom + 1, "stp")) + return TSTP; + if (!strcasecmp (atom + 1, "sfp")) + return TSFP; + if (!strcasecmp (atom + 1, "ransmission")) + return TRANSMISSION; + break; + case 'u': + if (!strcasecmp (atom + 1, "nset")) + return UNSET; + if (!strcasecmp (atom + 1, "nsigned")) + return UNSIGNED; + if (!strcasecmp (atom + 1, "id")) + return UID; + if (!strncasecmp (atom + 1, "se", 2)) { + if (!strcasecmp (atom + 3, "r-class")) + return USER_CLASS; + if (!strcasecmp (atom + 3, "-host-decl-names")) + return USE_HOST_DECL_NAMES; + if (!strcasecmp (atom + 3, + "-lease-addr-for-default-route")) + return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE; + break; + } + if (!strncasecmp (atom + 1, "nknown", 6)) { + if (!strcasecmp (atom + 7, "-clients")) + return UNKNOWN_CLIENTS; + if (!strcasecmp (atom + 7, "-state")) + return UNKNOWN_STATE; + if (!atom [7]) + return UNKNOWN; + break; + } + if (!strcasecmp (atom + 1, "nauthenticated")) + return AUTHENTICATED; + if (!strcasecmp (atom + 1, "pdated-dns-rr")) + return UPDATED_DNS_RR; + if (!strcasecmp (atom + 1, "pdate")) + return UPDATE; + break; + case 'v': + if (!strcasecmp (atom + 1, "endor-class")) + return VENDOR_CLASS; + if (!strcasecmp (atom + 1, "endor")) + return VENDOR; + break; + case 'w': + if (!strcasecmp (atom + 1, "ith")) + return WITH; + break; + case 'y': + if (!strcasecmp (atom + 1, "iaddr")) + return YIADDR; + if (!strcasecmp (atom + 1, "xdomain")) + return NS_YXDOMAIN; + if (!strcasecmp (atom + 1, "xrrset")) + return NS_YXRRSET; + break; + case 'z': + if (!strcasecmp (atom + 1, "one")) + return ZONE; + break; + } + return dfv; +} diff --git a/contrib/dhcp-3.0/common/ctrace.c b/contrib/dhcp-3.0/common/ctrace.c new file mode 100644 index 0000000000..1149faaff8 --- /dev/null +++ b/contrib/dhcp-3.0/common/ctrace.c @@ -0,0 +1,295 @@ +/* trace.c + + Subroutines that support dhcp tracing... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon, as part of a project for Nominum, Inc. To learn more + * about Internet Systems Consortium, see http://www.isc.org/. To + * learn more about Nominum, Inc., see ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: ctrace.c,v 1.3.2.3 2004/09/30 20:23:06 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (TRACING) +void trace_interface_register (trace_type_t *ttype, struct interface_info *ip) +{ + trace_interface_packet_t tipkt; + + if (trace_record ()) { + memset (&tipkt, 0, sizeof tipkt); + memcpy (&tipkt.hw_address, + &ip -> hw_address, sizeof ip -> hw_address); + memcpy (&tipkt.primary_address, + &ip -> primary_address, sizeof ip -> primary_address); + memcpy (tipkt.name, ip -> name, sizeof ip -> name); + tipkt.index = htonl (ip -> index); + + trace_write_packet (ttype, sizeof tipkt, (char *)&tipkt, MDL); + } +} + +void trace_interface_input (trace_type_t *ttype, unsigned len, char *buf) +{ + trace_interface_packet_t *tipkt; + struct interface_info *ip; + struct sockaddr_in *sin; + struct iaddr addr; + isc_result_t status; + + if (len != sizeof *tipkt) { + log_error ("trace interface packet size mismatch: %ld != %d", + (long)(sizeof *tipkt), len); + return; + } + tipkt = (trace_interface_packet_t *)buf; + + ip = (struct interface_info *)0; + status = interface_allocate (&ip, MDL); + if (status != ISC_R_SUCCESS) { + foo: + log_error ("trace_interface_input: %s.", + isc_result_totext (status)); + return; + } + ip -> ifp = dmalloc (sizeof *(ip -> ifp), MDL); + if (!ip -> ifp) { + interface_dereference (&ip, MDL); + status = ISC_R_NOMEMORY; + goto foo; + } + + memcpy (&ip -> hw_address, &tipkt -> hw_address, + sizeof ip -> hw_address); + memcpy (&ip -> primary_address, &tipkt -> primary_address, + sizeof ip -> primary_address); + memcpy (ip -> name, tipkt -> name, sizeof ip -> name); + ip -> index = ntohl (tipkt -> index); + + interface_snorf (ip, 0); + if (dhcp_interface_discovery_hook) + (*dhcp_interface_discovery_hook) (ip); + + /* Fake up an ifp. */ + memcpy (ip -> ifp -> ifr_name, ip -> name, sizeof ip -> name); +#ifdef HAVE_SA_LEN + ip -> ifp -> ifr_addr.sa_len = sizeof (struct sockaddr_in); +#endif + sin = (struct sockaddr_in *)&ip -> ifp -> ifr_addr; + sin -> sin_addr = ip -> primary_address; + + addr.len = 4; + memcpy (addr.iabuf, &sin -> sin_addr.s_addr, addr.len); + if (dhcp_interface_setup_hook) + (*dhcp_interface_setup_hook) (ip, &addr); + interface_stash (ip); + + if (!quiet_interface_discovery) { + log_info ("Listening on Trace/%s/%s%s%s", + ip -> name, + print_hw_addr (ip -> hw_address.hbuf [0], + ip -> hw_address.hlen - 1, + &ip -> hw_address.hbuf [1]), + (ip -> shared_network ? "/" : ""), + (ip -> shared_network ? + ip -> shared_network -> name : "")); + if (strcmp (ip -> name, "fallback")) { + log_info ("Sending on Trace/%s/%s%s%s", + ip -> name, + print_hw_addr (ip -> hw_address.hbuf [0], + ip -> hw_address.hlen - 1, + &ip -> hw_address.hbuf [1]), + (ip -> shared_network ? "/" : ""), + (ip -> shared_network ? + ip -> shared_network -> name : "")); + } + } + interface_dereference (&ip, MDL); +} + +void trace_interface_stop (trace_type_t *ttype) { + /* XXX */ +} + +void trace_inpacket_stash (struct interface_info *interface, + struct dhcp_packet *packet, + unsigned len, + unsigned int from_port, + struct iaddr from, + struct hardware *hfrom) +{ + trace_inpacket_t tip; + trace_iov_t iov [2]; + + if (!trace_record ()) + return; + tip.from_port = from_port; + tip.from = from; + tip.from.len = htonl (tip.from.len); + if (hfrom) { + tip.hfrom = *hfrom; + tip.havehfrom = 1; + } else { + memset (&tip.hfrom, 0, sizeof tip.hfrom); + tip.havehfrom = 0; + } + tip.index = htonl (interface -> index); + + iov [0].buf = (char *)&tip; + iov [0].len = sizeof tip; + iov [1].buf = (char *)packet; + iov [1].len = len; + trace_write_packet_iov (inpacket_trace, 2, iov, MDL); +} + +void trace_inpacket_input (trace_type_t *ttype, unsigned len, char *buf) +{ + trace_inpacket_t *tip; + int index; + + if (len < sizeof *tip) { + log_error ("trace_input_packet: too short - %d", len); + return; + } + tip = (trace_inpacket_t *)buf; + index = ntohl (tip -> index); + tip -> from.len = ntohl (tip -> from.len); + + if (index > interface_count || + index < 0 || + !interface_vector [index]) { + log_error ("trace_input_packet: unknown interface index %d", + index); + return; + } + + if (!bootp_packet_handler) { + log_error ("trace_input_packet: no bootp packet handler."); + return; + } + + (*bootp_packet_handler) (interface_vector [index], + (struct dhcp_packet *)(tip + 1), + len - sizeof *tip, + tip -> from_port, + tip -> from, + (tip -> havehfrom ? + &tip -> hfrom + : (struct hardware *)0)); +} + +void trace_inpacket_stop (trace_type_t *ttype) { } + +ssize_t trace_packet_send (struct interface_info *interface, + struct packet *packet, + struct dhcp_packet *raw, + size_t len, + struct in_addr from, + struct sockaddr_in *to, + struct hardware *hto) +{ + trace_outpacket_t tip; + trace_iov_t iov [2]; + + if (trace_record ()) { + if (hto) { + tip.hto = *hto; + tip.havehto = 1; + } else { + memset (&tip.hto, 0, sizeof tip.hto); + tip.havehto = 0; + } + tip.from.len = 4; + memcpy (tip.from.iabuf, &from, 4); + tip.to.len = 4; + memcpy (tip.to.iabuf, &to -> sin_addr, 4); + tip.to_port = to -> sin_port; + tip.index = htonl (interface -> index); + + iov [0].buf = (char *)&tip; + iov [0].len = sizeof tip; + iov [1].buf = (char *)raw; + iov [1].len = len; + trace_write_packet_iov (outpacket_trace, 2, iov, MDL); + } + if (!trace_playback ()) { + return send_packet (interface, packet, raw, len, + from, to, hto); + } + return len; +} + +void trace_outpacket_input (trace_type_t *ttype, unsigned len, char *buf) +{ + trace_outpacket_t *tip; + int index; + + if (len < sizeof *tip) { + log_error ("trace_input_packet: too short - %d", len); + return; + } + tip = (trace_outpacket_t *)buf; + index = ntohl (tip -> index); + + if (index > interface_count || + index < 0 || + !interface_vector [index]) { + log_error ("trace_input_packet: unknown interface index %d", + index); + return; + } + + /* XXX would be nice to somehow take notice of these. */ +} + +void trace_outpacket_stop (trace_type_t *ttype) { } + +void trace_seed_stash (trace_type_t *ttype, unsigned seed) +{ + u_int32_t outseed; + if (!trace_record ()) + return; + outseed = htonl (seed); + trace_write_packet (ttype, sizeof outseed, (char *)&outseed, MDL); + return; +} + +void trace_seed_input (trace_type_t *ttype, unsigned length, char *buf) +{ + u_int32_t *seed; + + if (length != sizeof seed) { + log_error ("trace_seed_input: wrong size (%d)", length); + } + seed = (u_int32_t *)buf; + srandom (ntohl (*seed)); +} + +void trace_seed_stop (trace_type_t *ttype) { } +#endif /* TRACING */ diff --git a/contrib/dhcp-3.0/common/dhcp-eval.5 b/contrib/dhcp-3.0/common/dhcp-eval.5 new file mode 100644 index 0000000000..b2a44f7c44 --- /dev/null +++ b/contrib/dhcp-3.0/common/dhcp-eval.5 @@ -0,0 +1,478 @@ +.\" $Id: dhcp-eval.5,v 1.17.2.8 2004/10/12 18:45:10 dhankins Exp $ +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 1996-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" This software has been written for Internet Systems Consortium +.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. +.\" To learn more about Internet Systems Consortium, see +.\" ``http://www.isc.org/''. To learn more about Vixie Enterprises, +.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see +.\" ``http://www.nominum.com''. +.TH dhcp-eval 5 +.SH NAME +dhcp-eval - ISC DHCP conditional evaluation +.SH DESCRIPTION +The Internet Systems Consortium DHCP client and server both provide +the ability to perform conditional behavior depending on the contents +of packets they receive. The syntax for specifying this conditional +behaviour is documented here. +.SH REFERENCE: CONDITIONAL BEHAVIOUR +Conditional behaviour is specified using the if statement and the else +or elsif statements. A conditional statement can appear anywhere +that a regular statement (e.g., an option statement) can appear, and +can enclose one or more such statements. A typical conditional +statement in a server might be: +.PP +.nf +if option dhcp-user-class = "accounting" { + max-lease-time 17600; + option domain-name "accounting.example.org"; + option domain-name-servers ns1.accounting.example.org, + ns2.accounting.example.org; +} elsif option dhcp-user-class = "sales" { + max-lease-time 17600; + option domain-name "sales.example.org"; + option domain-name-servers ns1.sales.example.org, + ns2.sales.example.org; +} elsif option dhcp-user-class = "engineering" { + max-lease-time 17600; + option domain-name "engineering.example.org"; + option domain-name-servers ns1.engineering.example.org, + ns2.engineering.example.org; +} else { + max-lease-time 600; + option domain-name "misc.example.org"; + option domain-name-servers ns1.misc.example.org, + ns2.misc.example.org; +} +.fi +.PP +On the client side, an example of conditional evaluation might be: +.PP +.nf +# example.org filters DNS at its firewall, so we have to use their DNS +# servers when we connect to their network. If we are not at +# example.org, prefer our own DNS server. +if not option domain-name = "example.org" { + prepend domain-name-servers 127.0.0.1; +} +.fi +.PP +The +.B if +statement and the +.B elsif +continuation statement both take boolean expressions as their +arguments. That is, they take expressions that, when evaluated, +produce a boolean result. If the expression evaluates to true, then +the statements enclosed in braces following the +.B if +statement are executed, and all subsequent +.B elsif +and +.B else +clauses are skipped. Otherwise, each subsequent +.B elsif +clause's expression is checked, until an elsif clause is encountered +whose test evaluates to true. If such a clause is found, the +statements in braces following it are executed, and then any +subsequent +.B elsif +and +.B else +clauses are skipped. If all the +.B if +and +.B elsif +clauses are checked but none +of their expressions evaluate true, then if there is an +.B else +clause, the statements enclosed in braces following the +.B else +are evaluated. Boolean expressions that evaluate to null are +treated as false in conditionals. +.SH BOOLEAN EXPRESSIONS +The following is the current list of boolean expressions that are +supported by the DHCP distribution. +.PP +.I data-expression-1 \fB=\fI data-expression-2\fR +.RS 0.25i +.PP +The \fB=\fR operator compares the values of two data expressions, +returning true if they are the same, false if they are not. If +either the left-hand side or the right-hand side are null, the +result is also null. +.RE +.PP +.I boolean-expression-1 \fBand\fI boolean-expression-2\fR +.PP +.RS 0.25i +The \fBand\fR operator evaluates to true if the boolean expression on +the left-hand side and the boolean expression on the right-hand side +both evaluate to true. Otherwise, it evaluates to false. If either +the expression on the left-hand side or the expression on the +right-hand side are null, the result is null. +.RE +.PP +.I boolean-expression-1 \fBor\fI boolean-expression-2\fR +.PP +.RS 0.25i +The \fBor\fR operator evaluates to true if either the boolean +expression on the left-hand side or the boolean expression on the +right-hand side evaluate to true. Otherwise, it evaluates to false. +If either the expression on the left-hand side or the expression on +the right-hand side are null, the result is null. +.RE +.PP +.B not \fIboolean-expression +.PP +.RS 0.25i +The \fBnot\fR operator evaluates to true if \fIboolean-expression\fR +evaluates to false, and returns false if \fIboolean-expression\fR evaluates +to true. If \fIboolean-expression\fR evaluates to null, the result +is also null. +.RE +.PP +.B exists \fIoption-name\fR +.PP +.RS 0.25i +The \fBexists\fR expression returns true if the specified option +exists in the incoming DHCP packet being processed. +.RE +.B known +.PP +.RS 0.25i +The \fBknown\fR expression returns true if the client whose request is +currently being processed is known - that is, if there's a host +declaration for it. +.RE +.B static +.PP +.RS 0.25i +The \fBstatic\fR expression returns true if the lease assigned to the +client whose request is currently being processed is derived from a static +address assignment. +.RE +.SH DATA EXPRESSIONS +Several of the boolean expressions above depend on the results of +evaluating data expressions. A list of these expressions is provided +here. +.PP +.B substring (\fIdata-expr\fB, \fIoffset\fB, \fIlength\fB)\fR +.PP +.RS 0.25i +The \fBsubstring\fR operator evaluates the data expression and returns +the substring of the result of that evaluation that starts +\fIoffset\fR bytes from the beginning, continuing for \fIlength\fR +bytes. \fIOffset\fR and \fIlength\fR are both numeric expressions. +If \fIdata-expr\fR, \fIoffset\fR or \fIlength\fR evaluate to null, +then the result is also null. If \fIoffset\fR is greater than or +equal to the length of the evaluated data, then a zero-length data +string is returned. If \fIlength\fI is greater then the remaining +length of the evaluated data after \fIoffset\fR, then a data string +containing all data from \fIoffset\fR to the end of the evaluated data +is returned. +.RE +.PP +.B suffix (\fIdata-expr\fB, \fIlength\fB)\fR +.PP +.RS 0.25i +The \fBsuffix\fR operator evaluates \fIdata-expr\fR and returns the +last \fIlength\fR bytes of the result of that evaluation. \fILength\fR +is a numeric expression. If \fIdata-expr\fR or \fIlength\fR evaluate +to null, then the result is also null. If \fIsuffix\fR evaluates to a +number greater than the length of the evaluated data, then the +evaluated data is returned. +.RE +.PP +.B option \fIoption-name\fR +.PP +.RS 0.25i +The \fBoption\fR operator returns the contents of the specified option in +the packet to which the server is responding. +.RE +.PP +.B config-option \fIoption-name\fR +.PP +.RS 0.25i +The \fBconfig-option\fR operator returns the value for the specified option +that the DHCP client or server has been configured to send. +.RE +.PP +.B hardware +.PP +.RS 0.25i +The \fBhardware\fR operator returns a data string whose first element +is the type of network interface indicated in packet being considered, +and whose subsequent elements are client's link-layer address. If +there is no packet, or if the RFC2131 \fIhlen\fR field is invalid, +then the result is null. Hardware types include ethernet (1), +token-ring (6), and fddi (8). Hardware types are specified by the +IETF, and details on how the type numbers are defined can be found in +RFC2131 (in the ISC DHCP distribution, this is included in the doc/ +subdirectory). +.RE +.PP +.B packet (\fIoffset\fB, \fIlength\fB)\fR +.PP +.RS 0.25i +The \fBpacket\fR operator returns the specified portion of the packet +being considered, or null in contexts where no packet is being +considered. \fIOffset\fR and \fIlength\fR are applied to the +contents packet as in the \fBsubstring\fR operator. +.RE +.PP +.I string +.PP +.RS 0.25i +A string, enclosed in quotes, may be specified as a data expression, +and returns the text between the quotes, encoded in ASCII. The +backslash ('\\') character is treated specially, as in C programming: '\\t' +means TAB, '\\r' means carriage return, '\\n' means newline, and '\\b' means +bell. Any octal value can be specified with '\\nnn', where nnn is any +positive octal number less than 0400. Any hexadecimal value can be +specified with '\\xnn', where nn is any positive hexadecimal number less +than 0xff. +.RE +.PP +.I colon-separated hexadecimal list +.PP +.RS 0.25i +A list of hexadecimal octet values, separated by colons, may be +specified as a data expression. +.RE +.PP +.B concat (\fIdata-expr1\fB, ..., \fIdata-exprN\fB)\fR +.RS 0.25i +The expressions are evaluated, and the results of each evaluation are +concatenated in the sequence that the subexpressions are listed. If +any subexpression evaluates to null, the result of the concatenation +is null. +.RE +.PP +.B reverse (\fInumeric-expr1\fB, \fIdata-expr2\fB)\fR +.RS 0.25i +The two expressions are evaluated, and then the result of evaluating +the data expression is reversed in place, using hunks of the size +specified in the numeric expression. For example, if the numeric +expression evaluates to four, and the data expression evaluates to +twelve bytes of data, then the reverse expression will evaluate to +twelve bytes of data, consisting of the last four bytes of the the +input data, followed by the middle four bytes, followed by the first +four bytes. +.RE +.PP +.B leased-address +.RS 0.25i +In any context where the client whose request is being processed has +been assigned an IP address, this data expression returns that IP +address. +.RE +.PP +.B binary-to-ascii (\fInumeric-expr1\fB, \fInumeric-expr2\fB, +.B \fIdata-expr1\fB,\fR \fIdata-expr2\fB)\fR +.RS 0.25i +Converts the result of evaluating data-expr2 into a text string +containing one number for each element of the result of evaluating +data-expr2. Each number is separated from the other by the result of +evaluating data-expr1. The result of evaluating numeric-expr1 +specifies the base (2 through 16) into which the numbers should be +converted. The result of evaluating numeric-expr2 specifies the +width in bits of each number, which may be either 8, 16 or 32. +.PP +As an example of the preceding three types of expressions, to produce +the name of a PTR record for the IP address being assigned to a +client, one could write the following expression: +.RE +.PP +.nf + concat (binary-to-ascii (10, 8, ".", + reverse (1, leased-address)), + ".in-addr.arpa."); + +.fi +.PP +.B encode-int (\fInumeric-expr\fB, \fIwidth\fB)\fR +.RS 0.25i +Numeric-expr is evaluated and encoded as a data string of the +specified width, in network byte order (most significant byte first). +If the numeric expression evaluates to the null value, the result is +also null. +.RE +.PP +.B pick-first-value (\fIdata-expr1\fR [ ... \fIexpr\fRn ] \fB)\fR +.RS 0.25i +The pick-first-value function takes any number of data expressions as +its arguments. Each expression is evaluated, starting with the first +in the list, until an expression is found that does not evaluate to a +null value. That expression is returned, and none of the subsequent +expressions are evaluated. If all expressions evaluate to a null +value, the null value is returned. +.RE +.PP +.B host-decl-name +.RS 0.25i +The host-decl-name function returns the name of the host declaration +that matched the client whose request is currently being processed, if +any. If no host declaration matched, the result is the null value. +.RE +.SH NUMERIC EXPRESSIONS +Numeric expressions are expressions that evaluate to an integer. In +general, the maximum size of such an integer should not be assumed to +be representable in fewer than 32 bits, but the precision of such +integers may be more than 32 bits. +.PP +.B extract-int (\fIdata-expr\fB, \fIwidth\fB)\fR +.PP +.RS 0.25i +The \fBextract-int\fR operator extracts an integer value in network +byte order from the result of evaluating the specified data +expression. Width is the width in bits of the integer to extract. +Currently, the only supported widths are 8, 16 and 32. If the +evaluation of the data expression doesn't provide sufficient bits to +extract an integer of the specified size, the null value is returned. +.RE +.PP +.B lease-time +.PP +.RS 0.25i +The duration of the current lease - that is, the difference between +the current time and the time that the lease expires. +.RE +.PP +.I number +.PP +.RS 0.25i +Any number between zero and the maximum representable size may be +specified as a numeric expression. +.RE +.PP +.B client-state +.PP +.RS 0.25i +The current state of the client instance being processed. This is +only useful in DHCP client configuration files. Possible values are: +.TP 2 +.I \(bu +Booting - DHCP client is in the INIT state, and does not yet have an +IP address. The next message transmitted will be a DHCPDISCOVER, +which will be broadcast. +.TP +.I \(bu +Reboot - DHCP client is in the INIT-REBOOT state. It has an IP +address, but is not yet using it. The next message to be transmitted +will be a DHCPREQUEST, which will be broadcast. If no response is +heard, the client will bind to its address and move to the BOUND state. +.TP +.I \(bu +Select - DHCP client is in the SELECTING state - it has received at +least one DHCPOFFER message, but is waiting to see if it may receive +other DHCPOFFER messages from other servers. No messages are sent in +the SELECTING state. +.TP +.I \(bu +Request - DHCP client is in the REQUESTING state - it has received at +least one DHCPOFFER message, and has chosen which one it will +request. The next message to be sent will be a DHCPREQUEST message, +which will be broadcast. +.TP +.I \(bu +Bound - DHCP client is in the BOUND state - it has an IP address. No +messages are transmitted in this state. +.TP +.I \(bu +Renew - DHCP client is in the RENEWING state - it has an IP address, +and is trying to contact the server to renew it. The next message to +be sent will be a DHCPREQUEST message, which will be unicast directly +to the server. +.TP +.I \(bu +Rebind - DHCP client is in the REBINDING state - it has an IP address, +and is trying to contact any server to renew it. The next message to +be sent will be a DHCPREQUEST, which will be broadcast. +.RE +.SH REFERENCE: LOGGING +Logging statements may be used to send information to the standard logging +channels. A logging statement includes an optional priority (\fBfatal\fR, +\fBerror\fR, \fBinfo\fR, or \fBdebug\fR), and a data expression. +.PP +.B log (\fIpriority\fB, \fIdata-expr\fB)\fR +.PP +Logging statements take only a single data expression argument, so if you +want to output multiple data values, you will need to use the \fBconcat\fR +operator to concatenate them. +.RE +.SH REFERENCE: DYNAMIC DNS UPDATES +.PP +The DHCP client and server have the ability to dynamically update the +Domain Name System. Within the configuration files, you can define +how you want the Domain Name System to be updated. These updates are +RFC 2136 compliant so any DNS server supporting RFC 2136 should be +able to accept updates from the DHCP server. +.SH SECURITY +Support for TSIG and DNSSEC is not yet available. When you set your +DNS server up to allow updates from the DHCP server or client, you may +be exposing it to unauthorized updates. To avoid this, the best you +can do right now is to use IP address-based packet filtering to +prevent unauthorized hosts from submitting update requests. +Obviously, there is currently no way to provide security for client +updates - this will require TSIG or DNSSEC, neither of which is yet +available in the DHCP distribution. +.PP +Dynamic DNS (DDNS) updates are performed by using the \fBdns-update\fR +expression. The \fBdns-update\fR expression is a boolean expression +that takes four parameters. If the update succeeds, the result is +true. If it fails, the result is false. The four parameters that the +are the resource record type (RR), the left hand side of the RR, the +right hand side of the RR and the ttl that should be applied to the +record. The simplest example of the use of the function can be found +in the reference section of the dhcpd.conf file, where events are +described. In this example several statements are being used to make +the arguments to the \fBdns-update\f\R. +.PP +In the example, the first argument to the first \f\Bdns-update\fR +expression is a data expression that evaluates to the A RR type. The +second argument is constructed by concatenating the DHCP host-name +option with a text string containing the local domain, in this case +"ssd.example.net". The third argument is constructed by converting +the address the client has been assigned from a 32-bit number into an +ascii string with each byte separated by a ".". The fourth argument, +the TTL, specifies the amount of time remaining in the lease (note +that this isn't really correct, since the DNS server will pass this +TTL out whenever a request comes in, even if that is only a few +seconds before the lease expires). +.PP +If the first \fBdns-update\fR statement succeeds, it is followed up +with a second update to install a PTR RR. The installation of a PTR +record is similar to installing an A RR except that the left hand side +of the record is the leased address, reversed, with ".in-addr.arpa" +concatenated. The right hand side is the fully qualified domain name +of the client to which the address is being leased. +.SH SEE ALSO +dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8), +dhclient(8), RFC2132, RFC2131. +.SH AUTHOR +The Internet Systems Consortium DHCP Distribution was written by Ted +Lemon under a contract with Vixie Labs. Funding for +this project was provided through Internet Systems Consortium. +Information about Internet Systems Consortium can be found at +.B http://www.isc.org. diff --git a/contrib/dhcp-3.0/common/dhcp-options.5 b/contrib/dhcp-3.0/common/dhcp-options.5 new file mode 100644 index 0000000000..dac94a82fc --- /dev/null +++ b/contrib/dhcp-3.0/common/dhcp-options.5 @@ -0,0 +1,1515 @@ +.\" $Id: dhcp-options.5,v 1.19.2.14 2004/09/21 22:43:09 dhankins Exp $ +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 1996-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" This software has been written for Internet Systems Consortium +.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. +.\" To learn more about Internet Systems Consortium, see +.\" ``http://www.isc.org/''. To learn more about Vixie Enterprises, +.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see +.\" ``http://www.nominum.com''. +.TH dhcpd-options 5 +.SH NAME +dhcp-options - Dynamic Host Configuration Protocol options +.SH DESCRIPTION +The Dynamic Host Configuration protocol allows the client to receive +.B options +from the DHCP server describing the network configuration and various +services that are available on the network. When configuring +.B dhcpd(8) +or +.B dhclient(8) , +options must often be declared. The syntax for declaring options, +and the names and formats of the options that can be declared, are +documented here. +.SH REFERENCE: OPTION STATEMENTS +.PP +DHCP \fIoption\fR statements always start with the \fIoption\fR +keyword, followed by an option name, followed by option data. The +option names and data formats are described below. It is not +necessary to exhaustively specify all DHCP options - only those +options which are needed by clients must be specified. +.PP +Option data comes in a variety of formats, as defined below: +.PP +The +.B ip-address +data type can be entered either as an explicit IP +address (e.g., 239.254.197.10) or as a domain name (e.g., +haagen.isc.org). When entering a domain name, be sure that that +domain name resolves to a single IP address. +.PP +The +.B int32 +data type specifies a signed 32-bit integer. The +.B uint32 +data type specifies an unsigned 32-bit integer. The +.B int16 +and +.B uint16 +data types specify signed and unsigned 16-bit integers. The +.B int8 +and +.B uint8 +data types specify signed and unsigned 8-bit integers. +Unsigned 8-bit integers are also sometimes referred to as octets. +.PP +The +.B text +data type specifies an NVT ASCII string, which must be +enclosed in double quotes - for example, to specify a root-path +option, the syntax would be +.nf +.sp 1 +option root-path "10.0.1.4:/var/tmp/rootfs"; +.fi +.PP +The +.B domain-name +data type specifies a domain name, which must not +enclosed in double quotes. This data type is not used for any +existing DHCP options. The domain name is stored just as if it were +a text option. +.PP +The +.B flag +data type specifies a boolean value. Booleans can be either true or +false (or on or off, if that makes more sense to you). +.PP +The +.B string +data type specifies either an NVT ASCII string +enclosed in double quotes, or a series of octets specified in +hexadecimal, separated by colons. For example: +.nf +.sp 1 + option dhcp-client-identifier "CLIENT-FOO"; +or + option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f; +.fi +.SH SETTING OPTION VALUES USING EXPRESSIONS +Sometimes it's helpful to be able to set the value of a DHCP option +based on some value that the client has sent. To do this, you can +use expression evaluation. The +.B dhcp-eval(5) +manual page describes how to write expressions. To assign the result +of an evaluation to an option, define the option as follows: +.nf +.sp 1 + \fBoption \fImy-option \fB= \fIexpression \fB;\fR +.fi +.PP +For example: +.nf +.sp 1 + option hostname = binary-to-ascii (16, 8, "-", + substring (hardware, 1, 6)); +.fi +.SH STANDARD DHCP OPTIONS +The documentation for the various options mentioned below is taken +from the latest IETF draft document on DHCP options. Options not +listed below may not yet be implemented, but it is possible to use +such options by defining them in the configuration file. Please see +the DEFINING NEW OPTIONS heading later in this document for more +information. +.PP +Some of the options documented here are automatically generated by +the DHCP server or by clients, and cannot be configured by the user. +The value of such an option can be used in the configuration file of +the receiving DHCP protocol agent (server or client), for example in +conditional expressions. However, the value of the option cannot be +used in the configuration file of the sending agent, because the value +is determined only \fIafter\fR the configuration file has been +processed. In the following documentation, such options will be shown +as "not user configurable" +.PP +The standard options are: +.PP +.B option \fBall-subnets-local\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client may assume that all +subnets of the IP network to which the client is connected use the +same MTU as the subnet of that network to which the client is +directly connected. A value of true indicates that all subnets share +the same MTU. A value of false means that the client should assume that +some subnets of the directly connected network may have smaller MTUs. +.RE +.PP +.B option \fBarp-cache-timeout\fR \fIuint32\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the timeout in seconds for ARP cache entries. +.RE +.PP +.B option \fBbootfile-name\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option is used to identify a bootstrap file. If supported by the +client, it should have the same effect as the \fBfilename\fR +declaration. BOOTP clients are unlikely to support this option. Some +DHCP clients will support it, and others actually require it. +.RE +.PP +.B option \fBboot-size\fR \fIuint16\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the length in 512-octet blocks of the default +boot image for the client. +.RE +.PP +.B option \fBbroadcast-address\fR \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the broadcast address in use on the client's +subnet. Legal values for broadcast addresses are specified in +section 3.2.1.3 of STD 3 (RFC1122). +.RE +.PP +.B option \fBcookie-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The cookie server option specifies a list of RFC 865 cookie +servers available to the client. Servers should be listed in order +of preference. +.RE +.PP +.B option \fBdefault-ip-ttl\fR \fIuint8;\fR +.RS 0.25i +.PP +This option specifies the default time-to-live that the client should +use on outgoing datagrams. +.RE +.PP +.B option \fBdefault-tcp-ttl\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the default TTL that the client should use when +sending TCP segments. The minimum value is 1. +.RE +.PP +.B option \fBdhcp-client-identifier\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +This option can be used to specify a DHCP client identifier in a +host declaration, so that dhcpd can find the host record by matching +against the client identifier. +.PP +Please be aware that some DHCP clients, when configured with client +identifiers that are ASCII text, will prepend a zero to the ASCII +text. So you may need to write: +.nf + + option dhcp-client-identifier "\\0foo"; + +rather than: + + option dhcp-client-identifier "foo"; +.fi +.RE +.PP +.B option \fBdhcp-lease-time\fR \fIuint32\fR\fB;\fR +.RS 0.25i +.PP +This option is used in a client request (DHCPDISCOVER or DHCPREQUEST) +to allow the client to request a lease time for the IP address. In a +server reply (DHCPOFFER), a DHCP server uses this option to specify +the lease time it is willing to offer. +.PP +This option is not directly user configurable in the server; refer to the +\fImax-lease-time\fR and \fIdefault-lease-time\fR server options in +.B dhcpd.conf(5). +.RE +.PP +.B option \fBdhcp-max-message-size\fR \fIuint16\fR\fB;\fR +.RS 0.25i +.PP +This option, when sent by the client, specifies the maximum size of +any response that the server sends to the client. When specified on +the server, if the client did not send a dhcp-max-message-size option, +the size specified on the server is used. This works for BOOTP as +well as DHCP responses. +.RE +.PP +.B option \fBdhcp-message\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option is used by a DHCP server to provide an error message to a +DHCP client in a DHCPNAK message in the event of a failure. A client +may use this option in a DHCPDECLINE message to indicate why the +client declined the offered parameters. +.PP +This option is not user configurable. +.RE +.PP +.B option \fBdhcp-message-type\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +This option, sent by both client and server, specifies the type of DHCP +message contained in the DHCP packet. Possible values (taken directly from +RFC2132) are: +.PP +.nf + 1 DHCPDISCOVER + 2 DHCPOFFER + 3 DHCPREQUEST + 4 DHCPDECLINE + 5 DHCPACK + 6 DHCPNAK + 7 DHCPRELEASE + 8 DHCPINFORM +.fi +.PP +This option is not user configurable. +.PP +.RE +.B option \fBdhcp-option-overload\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +This option is used to indicate that the DHCP 'sname' or 'file' +fields are being overloaded by using them to carry DHCP options. A +DHCP server inserts this option if the returned parameters will +exceed the usual space allotted for options. +.PP +If this option is present, the client interprets the specified +additional fields after it concludes interpretation of the standard +option fields. +.PP +Legal values for this option are: +.PP +.nf + 1 the 'file' field is used to hold options + 2 the 'sname' field is used to hold options + 3 both fields are used to hold options +.fi +.PP +This option is not user configurable. +.PP +.RE +.PP +.B option \fBdhcp-parameter-request-list\fR \fIuint16\fR\fB;\fR +.RS 0.25i +.PP +This option, when sent by the client, specifies which options the +client wishes the server to return. Normally, in the ISC DHCP +client, this is done using the \fIrequest\fR statement. If this +option is not specified by the client, the DHCP server will normally +return every option that is valid in scope and that fits into the +reply. When this option is specified on the server, the server +returns the specified options. This can be used to force a client to +take options that it hasn't requested, and it can also be used to +tailor the response of the DHCP server for clients that may need a +more limited set of options than those the server would normally +return. +.RE +.PP +.B option \fBdhcp-rebinding-time\fR \fIuint32\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the number of seconds from the time a client gets +an address until the client transitions to the REBINDING state. +.PP +This option is not user configurable. +.PP +.RE +.PP +.B option \fBdhcp-renewal-time\fR \fIuint32\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the number of seconds from the time a client gets +an address until the client transitions to the RENEWING state. +.PP +This option is not user configurable. +.PP +.RE +.PP +.B option \fBdhcp-requested-address\fR \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +This option is used by the client in a DHCPDISCOVER to +request that a particular IP address be assigned. +.PP +This option is not user configurable. +.PP +.RE +.PP +.B option \fBdhcp-server-identifier\fR \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +This option is used in DHCPOFFER and DHCPREQUEST messages, and may +optionally be included in the DHCPACK and DHCPNAK messages. DHCP +servers include this option in the DHCPOFFER in order to allow the +client to distinguish between lease offers. DHCP clients use the +contents of the 'server identifier' field as the destination address +for any DHCP messages unicast to the DHCP server. DHCP clients also +indicate which of several lease offers is being accepted by including +this option in a DHCPREQUEST message. +.PP +The value of this option is the IP address of the server. +.PP +This option is not directly user configurable. See the +\fIserver-identifier\fR server option in +.B \fIdhcpd.conf(5). +.PP +.RE +.PP +.B option \fBdomain-name\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the domain name that client should use when +resolving hostnames via the Domain Name System. +.RE +.PP +.B option \fBdomain-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The domain-name-servers option specifies a list of Domain Name System +(STD 13, RFC 1035) name servers available to the client. Servers +should be listed in order of preference. +.RE +.PP +.B option \fBextensions-path\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the name of a file containing additional options +to be interpreted according to the DHCP option format as specified in +RFC2132. +.RE +.PP +.B option \fBfinger-server\fR \fIip-address\fR [\fB,\fR +\fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The Finger server option specifies a list of Finger servers available +to the client. Servers should be listed in order of preference. +.RE +.PP +.B option \fBfont-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +This option specifies a list of X Window System Font servers available +to the client. Servers should be listed in order of preference. +.RE +.PP +.B option \fBhost-name\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the name of the client. The name may or may +not be qualified with the local domain name (it is preferable to use +the domain-name option to specify the domain name). See RFC 1035 for +character set restrictions. This option is only honored by +.B dhclient-script(8) +if the hostname for the client machine is not set. +.RE +.PP +.B option \fBieee802-3-encapsulation\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client should use Ethernet +Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the +interface is an Ethernet. A value of false indicates that the client +should use RFC 894 encapsulation. A value of true means that the client +should use RFC 1042 encapsulation. +.RE +.PP +.B option \fBien116-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]; +.RS 0.25i +.PP +The ien116-name-servers option specifies a list of IEN 116 name servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option \fBimpress-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The impress-server option specifies a list of Imagen Impress servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option \fBinterface-mtu\fR \fIuint16\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the MTU to use on this interface. The minimum +legal value for the MTU is 68. +.RE +.PP +.B option \fBip-forwarding\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether the client should configure its IP +layer for packet forwarding. A value of false means disable IP +forwarding, and a value of true means enable IP forwarding. +.RE +.PP +.B option \fBirc-server\fR \fIip-address\fR [\fB,\fR +\fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The IRC server option specifies a list of IRC servers available +to the client. Servers should be listed in order of preference. +.RE +.PP +.B option \fBlog-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The log-server option specifies a list of MIT-LCS UDP log servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The LPR server option specifies a list of RFC 1179 line printer +servers available to the client. Servers should be listed in order +of preference. +.RE +.PP +.B option \fBmask-supplier\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client should respond to +subnet mask requests using ICMP. A value of false indicates that the +client should not respond. A value of true means that the client should +respond. +.RE +.PP +.B option \fBmax-dgram-reassembly\fR \fIuint16\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the maximum size datagram that the client +should be prepared to reassemble. The minimum legal value is +576. +.RE +.PP +.B option \fBmerit-dump\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the path-name of a file to which the client's +core image should be dumped in the event the client crashes. The +path is formatted as a character string consisting of characters from +the NVT ASCII character set. +.RE +.PP +.B option \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +This option specifies a list of IP addresses indicating mobile IP +home agents available to the client. Agents should be listed in +order of preference, although normally there will be only one such +agent. +.RE +.PP +.B option \fBnds-context\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The nds-context option specifies the name of the initial Netware +Directory Service for an NDS client. +.RE +.PP +.B option \fBnds-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The nds-servers option specifies a list of IP addresses of NDS servers. +.RE +.PP +.B option \fBnds-tree-name\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The nds-tree-name option specifies NDS tree name that the NDS client +should use. +.RE +.PP +.B option \fBnetbios-dd-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The NetBIOS datagram distribution server (NBDD) option specifies a +list of RFC 1001/1002 NBDD servers listed in order of preference. +.RE +.PP +.B option \fBnetbios-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR +.RS 0.25i +.PP +The NetBIOS name server (NBNS) option specifies a list of RFC +1001/1002 NBNS name servers listed in order of preference. NetBIOS +Name Service is currently more commonly referred to as WINS. WINS +servers can be specified using the netbios-name-servers option. +.RE +.PP +.B option \fBnetbios-node-type\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +The NetBIOS node type option allows NetBIOS over TCP/IP clients which +are configurable to be configured as described in RFC 1001/1002. The +value is specified as a single octet which identifies the client type. +.PP +Possible node types are: +.PP +.TP 5 +.I 1 +B-node: Broadcast - no WINS +.TP +.I 2 +P-node: Peer - WINS only +.TP +.I 4 +M-node: Mixed - broadcast, then WINS +.TP +.I 8 +H-node: Hybrid - WINS, then broadcast +.RE +.PP +.B option \fBnetbios-scope\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The NetBIOS scope option specifies the NetBIOS over TCP/IP scope +parameter for the client as specified in RFC 1001/1002. See RFC1001, +RFC1002, and RFC1035 for character-set restrictions. +.RE +.PP +.B option \fBnis-domain\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the name of the client's NIS (Sun Network +Information Services) domain. The domain is formatted as a character +string consisting of characters from the NVT ASCII character set. +.RE +.PP +.B option \fBnis-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +This option specifies a list of IP addresses indicating NIS servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option \fBnisplus-domain\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the name of the client's NIS+ domain. The +domain is formatted as a character string consisting of characters +from the NVT ASCII character set. +.RE +.PP +.B option \fBnisplus-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +This option specifies a list of IP addresses indicating NIS+ servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option \fBnntp-server\fR \fIip-address\fR [\fB,\fR +\fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The NNTP server option specifies a list of NNTP servesr available +to the client. Servers should be listed in order of preference. +.RE +.PP +.B option \fBnon-local-source-routing\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether the client should configure its IP +layer to allow forwarding of datagrams with non-local source routes +(see Section 3.3.5 of [4] for a discussion of this topic). A value +of false means disallow forwarding of such datagrams, and a value of true +means allow forwarding. +.RE +.PP +.B option \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +This option specifies a list of IP addresses indicating NTP (RFC 1035) +servers available to the client. Servers should be listed in order +of preference. +.RE +.PP +.B option \fBnwip-domain\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The name of the NetWare/IP domain that a NetWare/IP client should +use. +.RE +.PP +.B option \fBnwip-suboptions\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +A sequence of suboptions for NetWare/IP clients - see RFC2242 for +details. Normally this option is set by specifying specific +NetWare/IP suboptions - see the NETWARE/IP SUBOPTIONS section for more +information. +.RE +.PP +.B option \fBpath-mtu-aging-timeout\fR \fIuint32\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the timeout (in seconds) to use when aging Path +MTU values discovered by the mechanism defined in RFC 1191. +.RE +.PP +.B option \fBpath-mtu-plateau-table\fR \fIuint16\fR [\fB,\fR \fIuint16\fR... +]\fB;\fR +.RS 0.25i +.PP +This option specifies a table of MTU sizes to use when performing +Path MTU Discovery as defined in RFC 1191. The table is formatted as +a list of 16-bit unsigned integers, ordered from smallest to largest. +The minimum MTU value cannot be smaller than 68. +.RE +.PP +.B option \fBperform-mask-discovery\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client should perform subnet +mask discovery using ICMP. A value of false indicates that the client +should not perform mask discovery. A value of true means that the +client should perform mask discovery. +.RE +.PP +.nf +.B option \fBpolicy-filter\fR \fIip-address ip-address\fR + [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR +.RE +.fi +.RS 0.25i +.PP +This option specifies policy filters for non-local source routing. +The filters consist of a list of IP addresses and masks which specify +destination/mask pairs with which to filter incoming source routes. +.PP +Any source routed datagram whose next-hop address does not match one +of the filters should be discarded by the client. +.PP +See STD 3 (RFC1122) for further information. +.RE +.PP +.B option \fBpop-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The POP3 server option specifies a list of POP3 servers available +to the client. Servers should be listed in order of preference. +.RE +.PP +.B option \fBresource-location-servers\fR \fIip-address\fR + [\fB, \fR\fIip-address\fR...]\fB;\fR +.fi +.RS 0.25i +.PP +This option specifies a list of RFC 887 Resource Location +servers available to the client. Servers should be listed in order +of preference. +.RE +.PP +.B option \fBroot-path\fR \fItext\fB;\fR\fR +.RS 0.25i +.PP +This option specifies the path-name that contains the client's root +disk. The path is formatted as a character string consisting of +characters from the NVT ASCII character set. +.RE +.PP +.B option \fBrouter-discovery\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client should solicit +routers using the Router Discovery mechanism defined in RFC 1256. +A value of false indicates that the client should not perform +router discovery. A value of true means that the client should perform +router discovery. +.RE +.PP +.B option \fBrouter-solicitation-address\fR \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the address to which the client should transmit +router solicitation requests. +.RE +.PP +.B option routers \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The routers option specifies a list of IP addresses for routers on the +client's subnet. Routers should be listed in order of preference. +.RE +.PP +.B option slp-directory-agent \fIboolean ip-address +[\fB,\fR \fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +This option specifies two things: the IP addresses of one or more +Service Location Protocol Directory Agents, and whether the use of +these addresses is mandatory. If the initial boolean value is true, +the SLP agent should just use the IP addresses given. If the value +is false, the SLP agent may additionally do active or passive +multicast discovery of SLP agents (see RFC2165 for details). +.PP +Please note that in this option and the slp-service-scope option, the +term "SLP Agent" is being used to refer to a Service Location Protocol +agent running on a machine that is being configured using the DHCP +protocol. +.PP +Also, please be aware that some companies may refer to SLP as NDS. +If you have an NDS directory agent whose address you need to +configure, the slp-directory-agent option should work. +.RE +.PP +.B option slp-service-scope \fIboolean text\fR\fB;\fR +.RS 0.25i +.PP +The Service Location Protocol Service Scope Option specifies two +things: a list of service scopes for SLP, and whether the use of this +list is mandatory. If the initial boolean value is true, the SLP +agent should only use the list of scopes provided in this option; +otherwise, it may use its own static configuration in preference to +the list provided in this option. +.PP +The text string should be a comma-separated list of scopes that the +SLP agent should use. It may be omitted, in which case the SLP Agent +will use the aggregated list of scopes of all directory agents known +to the SLP agent. +.RE +.PP +.B option \fBsmtp-server\fR \fIip-address\fR [\fB,\fR +\fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The SMTP server option specifies a list of SMTP servers available to +the client. Servers should be listed in order of preference. +.RE +.PP +.nf +.B option \fBstatic-routes\fR \fIip-address ip-address\fR + [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR +.fi +.RS 0.25i +.PP +This option specifies a list of static routes that the client should +install in its routing cache. If multiple routes to the same +destination are specified, they are listed in descending order of +priority. +.PP +The routes consist of a list of IP address pairs. The first address +is the destination address, and the second address is the router for +the destination. +.PP +The default route (0.0.0.0) is an illegal destination for a static +route. To specify the default route, use the +.B routers +option. Also, please note that this option is not intended for +classless IP routing - it does not include a subnet mask. Since +classless IP routing is now the most widely deployed routing standard, +this option is virtually useless, and is not implemented by any of the +popular DHCP clients, for example the Microsoft DHCP client. +.RE +.PP +.nf +.B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR + [\fB,\fR \fIip-address\fR...]\fB;\fR +.fi +.RS 0.25i +.PP +The StreetTalk Directory Assistance (STDA) server option specifies a +list of STDA servers available to the client. Servers should be +listed in order of preference. +.RE +.PP +.B option \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The StreetTalk server option specifies a list of StreetTalk servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option subnet-mask \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +The subnet mask option specifies the client's subnet mask as per RFC +950. If no subnet mask option is provided anywhere in scope, as a +last resort dhcpd will use the subnet mask from the subnet declaration +for the network on which an address is being assigned. However, +.I any +subnet-mask option declaration that is in scope for the address being +assigned will override the subnet mask specified in the subnet +declaration. +.RE +.PP +.B option \fBsubnet-selection\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +Sent by the client if an address is required in a subnet other than the one +that would normally be selected (based on the relaying address of the +connected subnet the request is obtained from). See RFC3011. Note that the +option number used by this server is 118; this has not always been the +defined number, and some clients may use a different value. Use of this +option should be regarded as slightly experimental! +.RE +.PP +This option is not user configurable in the server. +.PP +.PP +.B option \fBswap-server\fR \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +This specifies the IP address of the client's swap server. +.RE +.PP +.B option \fBtcp-keepalive-garbage\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client should send TCP +keepalive messages with an octet of garbage for compatibility with +older implementations. A value of false indicates that a garbage octet +should not be sent. A value of true indicates that a garbage octet +should be sent. +.RE +.PP +.B option \fBtcp-keepalive-interval\fR \fIuint32\fR\fB;\fR +.RS 0.25i +.PP +This option specifies the interval (in seconds) that the client TCP +should wait before sending a keepalive message on a TCP connection. +The time is specified as a 32-bit unsigned integer. A value of zero +indicates that the client should not generate keepalive messages on +connections unless specifically requested by an application. +.RE +.PP +.B option \fBtftp-server-name\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option is used to identify a TFTP server and, if supported by the +client, should have the same effect as the \fBserver-name\fR +declaration. BOOTP clients are unlikely to support this option. +Some DHCP clients will support it, and others actually require it. +.RE +.PP +.B option time-offset \fIint32\fR\fB;\fR +.RS 0.25i +.PP +The time-offset option specifies the offset of the client's subnet in +seconds from Coordinated Universal Time (UTC). +.RE +.PP +.B option time-servers \fIip-address\fR [, \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +The time-server option specifies a list of RFC 868 time servers +available to the client. Servers should be listed in order of +preference. +.RE +.PP +.B option \fBtrailer-encapsulation\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +This option specifies whether or not the client should negotiate the +use of trailers (RFC 893 [14]) when using the ARP protocol. A value +of false indicates that the client should not attempt to use trailers. A +value of true means that the client should attempt to use trailers. +.RE +.PP +.B option \fBuap-servers\fR \fItext\fR\fB;\fR +.RS 0.25i +.PP +This option specifies a list of URLs, each pointing to a user +authentication service that is capable of processing authentication +requests encapsulated in the User Authentication Protocol (UAP). UAP +servers can accept either HTTP 1.1 or SSLv3 connections. If the list +includes a URL that does not contain a port component, the normal +default port is assumed (i.e., port 80 for http and port 443 for +https). If the list includes a URL that does not contain a path +component, the path /uap is assumed. If more than one URL is +specified in this list, the URLs are separated by spaces. +.RE +.PP +.B option \fBuser-class\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +This option is used by some DHCP clients as a way for users to +specify identifying information to the client. This can be used in a +similar way to the vendor-class-identifier option, but the value of +the option is specified by the user, not the vendor. Most recent +DHCP clients have a way in the user interface to specify the value for +this identifier, usually as a text string. +.PP +.B option \fBvendor-class-identifier\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +This option is used by some DHCP clients to identify the vendor +type and possibly the configuration of a DHCP client. The information +is a string of bytes whose contents are specific to the vendor and are +not specified in a standard. To see what vendor class identifier +clients are sending, you can write the following in your DHCP server +configuration file: +.nf +.PP +set vendor-string = option vendor-class-identifier; +.fi +.PP +This will result in all entries in the DHCP server lease database file +for clients that sent vendor-class-identifier options having a set +statement that looks something like this: +.nf +.PP +set vendor-string = "SUNW.Ultra-5_10"; +.fi +.PP +The vendor-class-identifier option is normally used by the DHCP server +to determine the options that are returned in the +.B vendor-encapsulated-options +option. Please see the VENDOR ENCAPSULATED OPTIONS section later in this +manual page for further information. +.RE +.PP +.B option \fBvendor-encapsulated-options\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The \fBvendor-encapsulated-options\fR option can contain either a +single vendor-specific value or one or more vendor-specific +suboptions. This option is not normally specified in the DHCP server +configuration file - instead, a vendor class is defined for each +vendor, vendor class suboptions are defined, values for those +suboptions are defined, and the DHCP server makes up a response on +that basis. +.PP +Some default behaviours for well-known DHCP client vendors (currently, +the Microsoft Windows 2000 DHCP client) are configured automatically, +but otherwise this must be configured manually - see the VENDOR +ENCAPSULATED OPTIONS section later in this manual page for details. +.RE +.PP +.B option \fBwww-server\fR \fIip-address\fR [\fB,\fR +\fIip-address\fR... ]\fB;\fR +.RS 0.25i +.PP +The WWW server option specifies a list of WWW servers available +to the client. Servers should be listed in order of preference. +.RE +.PP +.B option \fBx-display-manager\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... +]\fB;\fR +.RS 0.25i +.PP +This option specifies a list of systems that are running the X Window +System Display Manager and are available to the client. Addresses +should be listed in order of preference. +.RE +.SH RELAY AGENT INFORMATION OPTION +An IETF draft, draft-ietf-dhc-agent-options-11.txt, defines a series +of encapsulated options that a relay agent can add to a DHCP packet +when relaying it to the DHCP server. The server can then make +address allocation decisions (or whatever other decisions it wants) +based on these options. The server also returns these options in any +replies it sends through the relay agent, so that the relay agent can +use the information in these options for delivery or accounting +purposes. +.PP +The current draft defines two options. To reference +these options in the dhcp server, specify the option space name, +"agent", followed by a period, followed by the option name. It is +not normally useful to define values for these options in the server, +although it is permissible. These options are not supported in the +client. +.PP +.B option \fBagent.circuit-id\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The circuit-id suboption encodes an agent-local identifier of the +circuit from which a DHCP client-to-server packet was received. It is +intended for use by agents in relaying DHCP responses back to the +proper circuit. The format of this option is currently defined to be +vendor-dependent, and will probably remain that way, although the +current draft allows for for the possibility of standardizing the +format in the future. +.RE +.PP +.B option \fBagent.remote-id\fR \fIstring\fR\fB;\fR +.RS 0.25i +.PP +The remote-id suboption encodes information about the remote host end +of a circuit. Examples of what it might contain include caller ID +information, username information, remote ATM address, cable modem ID, +and similar things. In principal, the meaning is not well-specified, +and it should generally be assumed to be an opaque object that is +administratively guaranteed to be unique to a particular remote end of +a circuit. +.RE +.SH THE CLIENT FQDN SUBOPTIONS +The Client FQDN option, currently defined in the Internet Draft +draft-ietf-dhc-fqdn-option-00.txt is not a standard yet, but is in +sufficiently wide use already that we have implemented it. Due to +the complexity of the option format, we have implemented it as a +suboption space rather than a single option. In general this +option should not be configured by the user - instead it should be +used as part of an automatic DNS update system. +.PP +.B option fqdn.no-client-update \fIflag\fB; +.RS 0.25i +.PP +When the client sends this, if it is true, it means the client will not +attempt to update its A record. When sent by the server to the client, +it means that the client \fIshould not\fR update its own A record. +.RE +.PP +.B option fqdn.server-update \fIflag\fB; +.RS 0.25i +.PP +When the client sends this to the server, it is requesting that the server +update its A record. When sent by the server, it means that the server +has updated (or is about to update) the client's A record. +.RE +.PP +.B option fqdn.encoded \fIflag\fB; +.RS 0.25i +.PP +If true, this indicates that the domain name included in the option is +encoded in DNS wire format, rather than as plain ASCII text. The client +normally sets this to false if it doesn't support DNS wire format in the +FQDN option. The server should always send back the same value that the +client sent. When this value is set on the configuration side, it controls +the format in which the \fIfqdn.fqdn\fR suboption is encoded. +.RE +.PP +.B option fqdn.rcode1 \fIflag\fB; +.PP +.B option fqdn.rcode2 \fIflag\fB; +.RS 0.25i +.PP +These options specify the result of the updates of the A and PTR records, +respectively, and are only sent by the DHCP server to the DHCP client. +The values of these fields are those defined in the DNS protocol specification. +.RE +.PP +.B option fqdn.fqdn \fItext\fB; +.RS 0.25i +.PP +Specifies the domain name that the client wishes to use. This can be a +fully-qualified domain name, or a single label. If there is no trailing +'.' character in the name, it is not fully-qualified, and the server will +generally update that name in some locally-defined domain. +.RE +.PP +.B option fqdn.hostname \fI--never set--\fB; +.RS 0.25i +.PP +This option should never be set, but it can be read back using the \fBoption\fR +and \fBconfig-option\fR operators in an expression, in which case it returns +the first label in the \fBfqdn.fqdn\fR suboption - for example, if +the value of \fBfqdn.fqdn\fR is "foo.example.com.", then \fBfqdn.hostname\fR +will be "foo". +.RE +.PP +.B option fqdn.domainname \fI--never set--\fB; +.RS 0.25i +.PP +This option should never be set, but it can be read back using the \fBoption\fR +and \fBconfig-option\fR operators in an expression, in which case it returns +all labels after the first label in the \fBfqdn.fqdn\fR suboption - for +example, if the value of \fBfqdn.fqdn\fR is "foo.example.com.", +then \fBfqdn.hostname\fR will be "example.com.". If this suboption value +is not set, it means that an unqualified name was sent in the fqdn option, +or that no fqdn option was sent at all. +.RE +.PP +If you wish to use any of these suboptions, we strongly recommend that you +refer to the Client FQDN option draft (or standard, when it becomes a +standard) - the documentation here is sketchy and incomplete in comparison, +and is just intended for reference by people who already understand the +Client FQDN option specification. +.SH THE NETWARE/IP SUBOPTIONS +RFC2242 defines a set of encapsulated options for Novell NetWare/IP +clients. To use these options in the dhcp server, specify the option +space name, "nwip", followed by a period, followed by the option name. +The following options can be specified: +.PP +.B option \fBnwip.nsq-broadcast\fR \fIflag\fR\fB;\fR +.RS 0.25i +.PP +If true, the client should use the NetWare Nearest Server Query to +locate a NetWare/IP server. The behaviour of the Novell client if +this suboption is false, or is not present, is not specified. +.PP +.RE +.B option \fBnwip.preferred-dss\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fR\fB;\fR +.RS 0.25i +.PP +This suboption specifies a list of up to five IP addresses, each of +which should be the IP address of a NetWare Domain SAP/RIP server +(DSS). +.RE +.PP +.B option \fBnwip.nearest-nwip-server\fR \fI\fIip-address\fR + [\fB,\fR \fIip-address\fR...]\fR\fB;\fR +.RS 0.25i +.PP +This suboption specifies a list of up to five IP addresses, each of +which should be the IP address of a Nearest NetWare IP server. +.RE +.PP +.B option \fBnwip.autoretries\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +Specifies the number of times that a NetWare/IP client should attempt +to communicate with a given DSS server at startup. +.RE +.PP +.B option \fBnwip.autoretry-secs\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +Specifies the number of seconds that a Netware/IP client should wait +between retries when attempting to establish communications with a DSS +server at startup. +.RE +.PP +.B option \fBnwip.nwip-1-1\fR \fIuint8\fR\fB;\fR +.RS 0.25i +.PP +If true, the NetWare/IP client should support NetWare/IP version 1.1 +compatibility. This is only needed if the client will be contacting +Netware/IP version 1.1 servers. +.RE +.PP +.B option \fBnwip.primary-dss\fR \fIip-address\fR\fB;\fR +.RS 0.25i +.PP +Specifies the IP address of the Primary Domain SAP/RIP Service server +(DSS) for this NetWare/IP domain. The NetWare/IP administration +utility uses this value as Primary DSS server when configuring a +secondary DSS server. +.RE +.SH DEFINING NEW OPTIONS +The Internet Systems Consortium DHCP client and server provide the +capability to define new options. Each DHCP option has a name, a +code, and a structure. The name is used by you to refer to the +option. The code is a number, used by the DHCP server and client to +refer to an option. The structure describes what the contents of an +option looks like. +.PP +To define a new option, you need to choose a name for it that is not +in use for some other option - for example, you can't use "host-name" +because the DHCP protocol already defines a host-name option, which is +documented earlier in this manual page. If an option name doesn't +appear in this manual page, you can use it, but it's probably a good +idea to put some kind of unique string at the beginning so you can be +sure that future options don't take your name. For example, you +might define an option, "local-host-name", feeling some confidence +that no official DHCP option name will ever start with "local". +.PP +Once you have chosen a name, you must choose a code. For site-local +options, all codes between 128 and 254 are reserved for DHCP options, +so you can pick any one of these. In practice, some vendors have +interpreted the protocol rather loosely and have used option code +values greater than 128 themselves. There's no real way to avoid +this problem, but it's not likely to cause too much trouble in +practice. +.PP +The structure of an option is simply the format in which the option +data appears. The ISC DHCP server currently supports a few simple +types, like integers, booleans, strings and IP addresses, and it also +supports the ability to define arrays of single types or arrays of +fixed sequences of types. +.PP +New options are declared as follows: +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.I definition +.B ; +.PP +The values of +.I new-name +and +.I new-code +should be the name you have chosen for the new option and the code you +have chosen. The +.I definition +should be the definition of the structure of the option. +.PP +The following simple option type definitions are supported: +.PP +.B BOOLEAN +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.B boolean +.B ; +.PP +An option of type boolean is a flag with a value of either on or off +(or true or false). So an example use of the boolean type would be: +.nf + +option use-zephyr code 180 = boolean; +option use-zephyr on; + +.fi +.B INTEGER +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.I sign +.B integer +.I width +.B ; +.PP +The \fIsign\fR token should either be blank, \fIunsigned\fR +or \fIsigned\fR. The width can be either 8, 16 or 32, and refers to +the number of bits in the integer. So for example, the following two +lines show a definition of the sql-connection-max option and its use: +.nf + +option sql-connection-max code 192 = unsigned integer 16; +option sql-connection-max 1536; + +.fi +.B IP-ADDRESS +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.B ip-address +.B ; +.PP +An option whose structure is an IP address can be expressed either as +a domain name or as a dotted quad. So the following is an example use +of the ip-address type: +.nf + +option sql-server-address code 193 = ip-address; +option sql-server-address sql.example.com; + +.fi +.PP +.B TEXT +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.B text +.B ; +.PP +An option whose type is text will encode an ASCII text string. For +example: +.nf + +option sql-default-connection-name code 194 = text; +option sql-default-connection-name "PRODZA"; + +.fi +.PP +.B DATA STRING +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.B string +.B ; +.PP +An option whose type is a data string is essentially just a collection +of bytes, and can be specified either as quoted text, like the text +type, or as a list of hexadecimal contents separated by colons whose +values must be between 0 and FF. For example: +.nf + +option sql-identification-token code 195 = string; +option sql-identification-token 17:23:19:a6:42:ea:99:7c:22; + +.fi +.PP +.B ENCAPSULATION +.PP +.B option +.I new-name +.B code +.I new-code +.B = +.B encapsulate +.I identifier +.B ; +.PP +An option whose type is \fBencapsulate\fR will encapsulate the +contents of the option space specified in \fIidentifier\fR. Examples +of encapsulated options in the DHCP protocol as it currently exists +include the vendor-encapsulated-options option, the netware-suboptions +option and the relay-agent-information option. +.nf + +option space local; +option local.demo code 1 = text; +option local-encapsulation code 197 = encapsulate local; +option local.demo "demo"; + +.fi +.PP +.B ARRAYS +.PP +Options can contain arrays of any of the above types except for the +text and data string types, which aren't currently supported in +arrays. An example of an array definition is as follows: +.nf + +option kerberos-servers code 200 = array of ip-address; +option kerberos-servers 10.20.10.1, 10.20.11.1; + +.fi +.B RECORDS +.PP +Options can also contain data structures consisting of a sequence of +data types, which is sometimes called a record type. For example: +.nf + +option contrived-001 code 201 = { boolean, integer 32, text }; +option contrived-001 on 1772 "contrivance"; + +.fi +It's also possible to have options that are arrays of records, for +example: +.nf + +option new-static-routes code 201 = array of { + ip-address, ip-address, ip-address, integer 8 }; +option static-routes + 10.0.0.0 255.255.255.0 net-0-rtr.example.com 1, + 10.0.1.0 255.255.255.0 net-1-rtr.example.com 1, + 10.2.0.0 255.255.224.0 net-2-0-rtr.example.com 3; + +.fi +.SH VENDOR ENCAPSULATED OPTIONS +The DHCP protocol defines the \fB vendor-encapsulated-options\fR +option, which allows vendors to define their own options that will be +sent encapsulated in a standard DHCP option. The format of the +.B vendor-encapsulated-options +option is either a series of bytes whose format is not specified, or +a sequence of options, each of which consists of a single-byte +vendor-specific option code, followed by a single-byte length, +followed by as many bytes of data as are specified in the length (the +length does not include itself or the option code). +.PP +The value of this option can be set in one of two ways. The first +way is to simply specify the data directly, using a text string or a +colon-separated list of hexadecimal values. For example: +.PP +.nf +option vendor-encapsulated-options + 2:4:AC:11:41:1: + 3:12:73:75:6e:64:68:63:70:2d:73:65:72:76:65:72:31:37:2d:31: + 4:12:2f:65:78:70:6f:72:74:2f:72:6f:6f:74:2f:69:38:36:70:63; +.fi +.PP +The second way of setting the value of this option is to have the DHCP +server generate a vendor-specific option buffer. To do this, you +must do four things: define an option space, define some options in +that option space, provide values for them, and specify that that +option space should be used to generate the +.B vendor-encapsulated-options +option. +.PP +To define a new option space in which vendor options can be stored, +use the \fRoption space\fP statement: +.PP +.B option +.B space +.I name +.B ; +.PP +The name can then be used in option definitions, as described earlier in +this document. For example: +.nf + +option space SUNW; +option SUNW.server-address code 2 = ip-address; +option SUNW.server-name code 3 = text; +option SUNW.root-path code 4 = text; + +.fi +Once you have defined an option space and the format of some options, +you can set up scopes that define values for those options, and you +can say when to use them. For example, suppose you want to handle +two different classes of clients. Using the option space definition +shown in the previous example, you can send different option values to +different clients based on the vendor-class-identifier option that the +clients send, as follows: +.PP +.nf +class "vendor-classes" { + match option vendor-class-identifier; +} + +option SUNW.server-address 172.17.65.1; +option SUNW.server-name "sundhcp-server17-1"; + +subclass "vendor-classes" "SUNW.Ultra-5_10" { + vendor-option-space SUNW; + option SUNW.root-path "/export/root/sparc"; +} + +subclass "vendor-classes" "SUNW.i86pc" { + vendor-option-space SUNW; + option SUNW.root-path "/export/root/i86pc"; +} +.fi +.PP +As you can see in the preceding example, regular scoping rules apply, +so you can define values that are global in the global scope, and only +define values that are specific to a particular class in the local +scope. The \fBvendor-option-space\fR declaration tells the DHCP +server to use options in the SUNW option space to construct the +.B vendor-encapsulated-options +option. +.SH SEE ALSO +dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8), +dhclient(8), RFC2132, RFC2131, draft-ietf-dhc-agent-options-??.txt. +.SH AUTHOR +The Internet Systems Consortium DHCP Distribution was written by Ted +Lemon under a contract with Vixie Labs. Funding for +this project was provided through Internet Systems Consortium. +Information about Internet Systems Consortium can be found at +.B http://www.isc.org. diff --git a/contrib/dhcp-3.0/common/discover.c b/contrib/dhcp-3.0/common/discover.c new file mode 100644 index 0000000000..c2f9d47daa --- /dev/null +++ b/contrib/dhcp-3.0/common/discover.c @@ -0,0 +1,1138 @@ +/* dispatch.c + + Network input dispatcher... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: discover.c,v 1.42.2.15 2004/06/10 17:59:16 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include + +struct interface_info *interfaces, *dummy_interfaces, *fallback_interface; +int interfaces_invalidated; +int quiet_interface_discovery; +u_int16_t local_port; +u_int16_t remote_port; +int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); +int (*dhcp_interface_discovery_hook) (struct interface_info *); +isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); +int (*dhcp_interface_shutdown_hook) (struct interface_info *); + +struct in_addr limited_broadcast; +struct in_addr local_address; + +void (*bootp_packet_handler) PROTO ((struct interface_info *, + struct dhcp_packet *, unsigned, + unsigned int, + struct iaddr, struct hardware *)); + +omapi_object_type_t *dhcp_type_interface; +#if defined (TRACING) +trace_type_t *interface_trace; +trace_type_t *inpacket_trace; +trace_type_t *outpacket_trace; +#endif +struct interface_info **interface_vector; +int interface_count; +int interface_max; + +OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface) + +isc_result_t interface_setup () +{ + isc_result_t status; + status = omapi_object_type_register (&dhcp_type_interface, + "interface", + dhcp_interface_set_value, + dhcp_interface_get_value, + dhcp_interface_destroy, + dhcp_interface_signal_handler, + dhcp_interface_stuff_values, + dhcp_interface_lookup, + dhcp_interface_create, + dhcp_interface_remove, + 0, 0, 0, + sizeof (struct interface_info), + interface_initialize, RC_MISC); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register interface object type: %s", + isc_result_totext (status)); + + return status; +} + +#if defined (TRACING) +void interface_trace_setup () +{ + interface_trace = trace_type_register ("interface", (void *)0, + trace_interface_input, + trace_interface_stop, MDL); + inpacket_trace = trace_type_register ("inpacket", (void *)0, + trace_inpacket_input, + trace_inpacket_stop, MDL); + outpacket_trace = trace_type_register ("outpacket", (void *)0, + trace_outpacket_input, + trace_outpacket_stop, MDL); +} +#endif + +isc_result_t interface_initialize (omapi_object_t *ipo, + const char *file, int line) +{ + struct interface_info *ip = (struct interface_info *)ipo; + ip -> rfdesc = ip -> wfdesc = -1; + return ISC_R_SUCCESS; +} + +/* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces. + For each interface that's of type INET and not the loopback interface, + register that interface with the network I/O software, figure out what + subnet it's on, and add it to the list of interfaces. */ + +void discover_interfaces (state) + int state; +{ + struct interface_info *tmp, *ip; + struct interface_info *last, *next; + char buf [2048]; + struct ifconf ic; + struct ifreq ifr; + int i; + int sock; + int address_count = 0; + struct subnet *subnet; + struct shared_network *share; + struct sockaddr_in foo; + int ir; + struct ifreq *tif; +#ifdef ALIAS_NAMES_PERMUTED + char *s; +#endif + isc_result_t status; + static int setup_fallback = 0; + int wifcount = 0; + + /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */ + if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + log_fatal ("Can't create addrlist socket"); + + /* Get the interface configuration information... */ + +#ifdef SIOCGIFCONF_ZERO_PROBE + /* linux will only tell us how long a buffer it wants if we give it + * a null buffer first. So, do a dry run to figure out the length. + * + * XXX this code is duplicated from below because trying to fold + * the logic into the if statement and goto resulted in excesssive + * obfuscation. The intent is that unless you run Linux you shouldn't + * have to deal with this. */ + + ic.ifc_len = 0; + ic.ifc_ifcu.ifcu_buf = (caddr_t)NULL; +#else + /* otherwise, we just feed it a starting size, and it'll tell us if + * it needs more */ + + ic.ifc_len = sizeof buf; + ic.ifc_ifcu.ifcu_buf = (caddr_t)buf; +#endif + + gifconf_again: + i = ioctl(sock, SIOCGIFCONF, &ic); + + if (i < 0) + log_fatal ("ioctl: SIOCGIFCONF: %m"); + +#ifdef SIOCGIFCONF_ZERO_PROBE + /* Workaround for SIOCGIFCONF bug on some Linux versions. */ + if (ic.ifc_ifcu.ifcu_buf == 0 && ic.ifc_len == 0) { + ic.ifc_len = sizeof buf; + ic.ifc_ifcu.ifcu_buf = (caddr_t)buf; + goto gifconf_again; + } +#endif + + /* If the SIOCGIFCONF resulted in more data than would fit in + a buffer, allocate a bigger buffer. */ + if ((ic.ifc_ifcu.ifcu_buf == buf +#ifdef SIOCGIFCONF_ZERO_PROBE + || ic.ifc_ifcu.ifcu_buf == 0 +#endif + ) && ic.ifc_len > sizeof buf) { + ic.ifc_ifcu.ifcu_buf = dmalloc ((size_t)ic.ifc_len, MDL); + if (!ic.ifc_ifcu.ifcu_buf) + log_fatal ("Can't allocate SIOCGIFCONF buffer."); + goto gifconf_again; +#ifdef SIOCGIFCONF_ZERO_PROBE + } else if (ic.ifc_ifcu.ifcu_buf == 0) { + ic.ifc_ifcu.ifcu_buf = (caddr_t)buf; + ic.ifc_len = sizeof buf; + goto gifconf_again; +#endif + } + + + /* If we already have a list of interfaces, and we're running as + a DHCP server, the interfaces were requested. */ + if (interfaces && (state == DISCOVER_SERVER || + state == DISCOVER_RELAY || + state == DISCOVER_REQUESTED)) + ir = 0; + else if (state == DISCOVER_UNCONFIGURED) + ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC; + else + ir = INTERFACE_REQUESTED; + + /* Cycle through the list of interfaces looking for IP addresses. */ + for (i = 0; i < ic.ifc_len;) { + struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i); +#ifdef HAVE_SA_LEN + if (ifp -> ifr_addr.sa_len > sizeof (struct sockaddr)) + i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len; + else +#endif + i += sizeof *ifp; + +#ifdef ALIAS_NAMES_PERMUTED + if ((s = strrchr (ifp -> ifr_name, ':'))) { + *s = 0; + } +#endif + +#ifdef SKIP_DUMMY_INTERFACES + if (!strncmp (ifp -> ifr_name, "dummy", 5)) + continue; +#endif + + + /* See if this is the sort of interface we want to + deal with. */ + strcpy (ifr.ifr_name, ifp -> ifr_name); + if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) + log_fatal ("Can't get interface flags for %s: %m", + ifr.ifr_name); + + /* See if we've seen an interface that matches this one. */ + for (tmp = interfaces; tmp; tmp = tmp -> next) + if (!strcmp (tmp -> name, ifp -> ifr_name)) + break; + + /* Skip non broadcast interfaces (plus loopback and + point-to-point in case an OS incorrectly marks them + as broadcast). Also skip down interfaces unless we're + trying to get a list of configurable interfaces. */ + if (((!(ifr.ifr_flags & IFF_BROADCAST) || + ifr.ifr_flags & IFF_LOOPBACK || + ifr.ifr_flags & IFF_POINTOPOINT) && !tmp) || + (!(ifr.ifr_flags & IFF_UP) && + state != DISCOVER_UNCONFIGURED)) + continue; + + /* If there isn't already an interface by this name, + allocate one. */ + if (!tmp) { + tmp = (struct interface_info *)0; + status = interface_allocate (&tmp, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Error allocating interface %s: %s", + ifp -> ifr_name, + isc_result_totext (status)); + strcpy (tmp -> name, ifp -> ifr_name); + interface_snorf (tmp, ir); + interface_dereference (&tmp, MDL); + tmp = interfaces; /* XXX */ + } + + if (dhcp_interface_discovery_hook) + (*dhcp_interface_discovery_hook) (tmp); + + /* If we have the capability, extract link information + and record it in a linked list. */ +#ifdef HAVE_AF_LINK + if (ifp -> ifr_addr.sa_family == AF_LINK) { + struct sockaddr_dl *foo = ((struct sockaddr_dl *) + (&ifp -> ifr_addr)); +#if defined (HAVE_SIN_LEN) + tmp -> hw_address.hlen = foo -> sdl_alen; +#else + tmp -> hw_address.hlen = 6; /* XXX!!! */ +#endif + tmp -> hw_address.hbuf [0] = HTYPE_ETHER; /* XXX */ + memcpy (&tmp -> hw_address.hbuf [1], + LLADDR (foo), tmp -> hw_address.hlen); + tmp -> hw_address.hlen++; /* for type. */ + } else +#endif /* AF_LINK */ + + if (ifp -> ifr_addr.sa_family == AF_INET) { + struct iaddr addr; + + /* Get a pointer to the address... */ + memcpy (&foo, &ifp -> ifr_addr, + sizeof ifp -> ifr_addr); + + /* We don't want the loopback interface. */ + if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK) && + ((tmp -> flags & INTERFACE_AUTOMATIC) && + state == DISCOVER_SERVER)) + continue; + + + /* If this is the first real IP address we've + found, keep a pointer to ifreq structure in + which we found it. */ + if (!tmp -> ifp) { +#ifdef HAVE_SA_LEN + unsigned len = ((sizeof ifp -> ifr_name) + + ifp -> ifr_addr.sa_len); +#else + unsigned len = sizeof *ifp; +#endif + tif = (struct ifreq *)dmalloc (len, MDL); + if (!tif) + log_fatal ("no space for ifp."); + memcpy (tif, ifp, len); + tmp -> ifp = tif; + tmp -> primary_address = foo.sin_addr; + } + + /* Grab the address... */ + addr.len = 4; + memcpy (addr.iabuf, &foo.sin_addr.s_addr, + addr.len); + if (dhcp_interface_setup_hook) + (*dhcp_interface_setup_hook) (tmp, &addr); + } + } + + /* If we allocated a buffer, free it. */ + if (ic.ifc_ifcu.ifcu_buf != buf) + dfree (ic.ifc_ifcu.ifcu_buf, MDL); + +#if defined (LINUX_SLASHPROC_DISCOVERY) + /* On Linux, interfaces that don't have IP addresses don't + show up in the SIOCGIFCONF syscall. This only matters for + the DHCP client, of course - the relay agent and server + should only care about interfaces that are configured with + IP addresses anyway. + + The PROCDEV_DEVICE (/proc/net/dev) is a kernel-supplied file + that, when read, prints a human readable network status. We + extract the names of the network devices by skipping the first + two lines (which are header) and then parsing off everything + up to the colon in each subsequent line - these lines start + with the interface name, then a colon, then a bunch of + statistics. */ + + if (state == DISCOVER_UNCONFIGURED) { + FILE *proc_dev; + char buffer [256]; + int skip = 2; + + proc_dev = fopen (PROCDEV_DEVICE, "r"); + if (!proc_dev) + log_fatal ("%s: %m", PROCDEV_DEVICE); + + while (fgets (buffer, sizeof buffer, proc_dev)) { + char *name = buffer; + char *sep; + + /* Skip the first two blocks, which are header + lines. */ + if (skip) { + --skip; + continue; + } + + sep = strrchr (buffer, ':'); + if (sep) + *sep = '\0'; + while (*name == ' ') + name++; + + /* See if we've seen an interface that matches + this one. */ + for (tmp = interfaces; tmp; tmp = tmp -> next) + if (!strcmp (tmp -> name, name)) + break; + + /* If we found one, nothing more to do.. */ + if (tmp) + continue; + + /* Otherwise, allocate one. */ + tmp = (struct interface_info *)0; + status = interface_allocate (&tmp, MDL); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't allocate interface %s: %s", + name, isc_result_totext (status)); + tmp -> flags = ir; + strncpy (tmp -> name, name, IFNAMSIZ); + if (interfaces) { + interface_reference (&tmp -> next, + interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, tmp, MDL); + interface_dereference (&tmp, MDL); + tmp = interfaces; + + if (dhcp_interface_discovery_hook) + (*dhcp_interface_discovery_hook) (tmp); + + } + fclose (proc_dev); + } +#endif + + /* Now cycle through all the interfaces we found, looking for + hardware addresses. */ +#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) + for (tmp = interfaces; tmp; tmp = tmp -> next) { + struct ifreq ifr; + struct sockaddr sa; + int b, sk; + + if (!tmp -> ifp) { + /* Make up an ifreq structure. */ + tif = (struct ifreq *)dmalloc (sizeof (struct ifreq), + MDL); + if (!tif) + log_fatal ("no space to remember ifp."); + memset (tif, 0, sizeof (struct ifreq)); + strcpy (tif -> ifr_name, tmp -> name); + tmp -> ifp = tif; + } + + /* Read the hardware address from this interface. */ + ifr = *tmp -> ifp; + if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) + continue; + + sa = *(struct sockaddr *)&ifr.ifr_hwaddr; + + switch (sa.sa_family) { +#ifdef HAVE_ARPHRD_TUNNEL + case ARPHRD_TUNNEL: + /* ignore tunnel interfaces. */ +#endif +#ifdef HAVE_ARPHRD_ROSE + case ARPHRD_ROSE: +#endif +#ifdef HAVE_ARPHRD_LOOPBACK + case ARPHRD_LOOPBACK: + /* ignore loopback interface */ + break; +#endif + + case ARPHRD_ETHER: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_ETHER; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; + +#ifndef HAVE_ARPHRD_IEEE802 +# define ARPHRD_IEEE802 HTYPE_IEEE802 +#endif +#if defined (HAVE_ARPHRD_IEEE802_TR) + case ARPHRD_IEEE802_TR: +#endif + case ARPHRD_IEEE802: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_IEEE802; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; + +#ifndef HAVE_ARPHRD_FDDI +# define ARPHRD_FDDI HTYPE_FDDI +#endif + case ARPHRD_FDDI: + tmp -> hw_address.hlen = 17; + tmp -> hw_address.hbuf [0] = HTYPE_FDDI; /* XXX */ + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 16); + break; + +#ifdef HAVE_ARPHRD_METRICOM + case ARPHRD_METRICOM: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_METRICOM; + memcpy (&tmp -> hw_address.hbuf [0], sa.sa_data, 6); + break; +#endif + +#ifdef HAVE_ARPHRD_AX25 + case ARPHRD_AX25: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_AX25; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; +#endif + +#ifdef HAVE_ARPHRD_NETROM + case ARPHRD_NETROM: + tmp -> hw_address.hlen = 7; + tmp -> hw_address.hbuf [0] = ARPHRD_NETROM; + memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6); + break; +#endif + + default: + log_error ("%s: unknown hardware address type %d", + ifr.ifr_name, sa.sa_family); + break; + } + } +#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */ + + /* If we're just trying to get a list of interfaces that we might + be able to configure, we can quit now. */ + if (state == DISCOVER_UNCONFIGURED) { + close (sock); + return; + } + + /* Weed out the interfaces that did not have IP addresses. */ + tmp = last = next = (struct interface_info *)0; + if (interfaces) + interface_reference (&tmp, interfaces, MDL); + while (tmp) { + if (next) + interface_dereference (&next, MDL); + if (tmp -> next) + interface_reference (&next, tmp -> next, MDL); + /* skip interfaces that are running already */ + if (tmp -> flags & INTERFACE_RUNNING) { + interface_dereference(&tmp, MDL); + if(next) + interface_reference(&tmp, next, MDL); + continue; + } + if ((tmp -> flags & INTERFACE_AUTOMATIC) && + state == DISCOVER_REQUESTED) + tmp -> flags &= ~(INTERFACE_AUTOMATIC | + INTERFACE_REQUESTED); + if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) { + if ((tmp -> flags & INTERFACE_REQUESTED) != ir) + log_fatal ("%s: not found", tmp -> name); + if (!last) { + if (interfaces) + interface_dereference (&interfaces, + MDL); + if (next) + interface_reference (&interfaces, next, MDL); + } else { + interface_dereference (&last -> next, MDL); + if (next) + interface_reference (&last -> next, + next, MDL); + } + if (tmp -> next) + interface_dereference (&tmp -> next, MDL); + + /* Remember the interface in case we need to know + about it later. */ + if (dummy_interfaces) { + interface_reference (&tmp -> next, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + } + interface_reference (&dummy_interfaces, tmp, MDL); + interface_dereference (&tmp, MDL); + if (next) + interface_reference (&tmp, next, MDL); + continue; + } + last = tmp; + + memcpy (&foo, &tmp -> ifp -> ifr_addr, + sizeof tmp -> ifp -> ifr_addr); + + /* We must have a subnet declaration for each interface. */ + if (!tmp -> shared_network && (state == DISCOVER_SERVER)) { + log_error ("%s", ""); + log_error ("No subnet declaration for %s (%s).", + tmp -> name, inet_ntoa (foo.sin_addr)); + if (supports_multiple_interfaces (tmp)) { + log_error ("** Ignoring requests on %s. %s", + tmp -> name, "If this is not what"); + log_error (" you want, please write %s", + "a subnet declaration"); + log_error (" in your dhcpd.conf file %s", + "for the network segment"); + log_error (" to %s %s %s", + "which interface", + tmp -> name, "is attached. **"); + log_error ("%s", ""); + goto next; + } else { + log_error ("You must write a subnet %s", + " declaration for this"); + log_error ("subnet. You cannot prevent %s", + "the DHCP server"); + log_error ("from listening on this subnet %s", + "because your"); + log_fatal ("operating system does not %s.", + "support this capability"); + } + } + + /* Find subnets that don't have valid interface + addresses... */ + for (subnet = (tmp -> shared_network + ? tmp -> shared_network -> subnets + : (struct subnet *)0); + subnet; subnet = subnet -> next_sibling) { + if (!subnet -> interface_address.len) { + /* Set the interface address for this subnet + to the first address we found. */ + subnet -> interface_address.len = 4; + memcpy (subnet -> interface_address.iabuf, + &foo.sin_addr.s_addr, 4); + } + } + + /* Flag the index as not having been set, so that the + interface registerer can set it or not as it chooses. */ + tmp -> index = -1; + + /* Register the interface... */ + if_register_receive (tmp); + if_register_send (tmp); + + interface_stash (tmp); + wifcount++; +#if defined (HAVE_SETFD) + if (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on %s: %m", + tmp -> name); + if (tmp -> rfdesc != tmp -> wfdesc) { + if (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on %s: %m", + tmp -> name); + } +#endif + next: + interface_dereference (&tmp, MDL); + if (next) + interface_reference (&tmp, next, MDL); + } + + /* Now register all the remaining interfaces as protocols. */ + for (tmp = interfaces; tmp; tmp = tmp -> next) { + /* not if it's been registered before */ + if (tmp -> flags & INTERFACE_RUNNING) + continue; + if (tmp -> rfdesc == -1) + continue; + status = omapi_register_io_object ((omapi_object_t *)tmp, + if_readsocket, 0, + got_one, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + tmp -> name, isc_result_totext (status)); + } + + close (sock); + + if (state == DISCOVER_SERVER && wifcount == 0) { + log_info ("%s", ""); + log_fatal ("Not configured to listen on any interfaces!"); + } + + if (!setup_fallback) { + setup_fallback = 1; + maybe_setup_fallback (); + } + +#if defined (HAVE_SETFD) + if (fallback_interface) { + if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on fallback: %m"); + if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) { + if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on fallback: %m"); + } + } +#endif +} + +int if_readsocket (h) + omapi_object_t *h; +{ + struct interface_info *ip; + + if (h -> type != dhcp_type_interface) + return -1; + ip = (struct interface_info *)h; + return ip -> rfdesc; +} + +int setup_fallback (struct interface_info **fp, const char *file, int line) +{ + isc_result_t status; + + status = interface_allocate (&fallback_interface, file, line); + if (status != ISC_R_SUCCESS) + log_fatal ("Error allocating fallback interface: %s", + isc_result_totext (status)); + strcpy (fallback_interface -> name, "fallback"); + if (dhcp_interface_setup_hook) + (*dhcp_interface_setup_hook) (fallback_interface, + (struct iaddr *)0); + status = interface_reference (fp, fallback_interface, file, line); + + fallback_interface -> index = -1; + interface_stash (fallback_interface); + return status == ISC_R_SUCCESS; +} + +void reinitialize_interfaces () +{ + struct interface_info *ip; + + for (ip = interfaces; ip; ip = ip -> next) { + if_reinitialize_receive (ip); + if_reinitialize_send (ip); + } + + if (fallback_interface) + if_reinitialize_send (fallback_interface); + + interfaces_invalidated = 1; +} + +isc_result_t got_one (h) + omapi_object_t *h; +{ + struct sockaddr_in from; + struct hardware hfrom; + struct iaddr ifrom; + int result; + union { + unsigned char packbuf [4095]; /* Packet input buffer. + Must be as large as largest + possible MTU. */ + struct dhcp_packet packet; + } u; + struct interface_info *ip; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + ip = (struct interface_info *)h; + + again: + if ((result = + receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) { + log_error ("receive_packet failed on %s: %m", ip -> name); + return ISC_R_UNEXPECTED; + } + if (result == 0) + return ISC_R_UNEXPECTED; + + /* If we didn't at least get the fixed portion of the BOOTP + packet, drop the packet. We're allowing packets with no + sname or filename, because we're aware of at least one + client that sends such packets, but this definitely falls + into the category of being forgiving. */ + if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN) + return ISC_R_UNEXPECTED; + + if (bootp_packet_handler) { + ifrom.len = 4; + memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); + + (*bootp_packet_handler) (ip, &u.packet, (unsigned)result, + from.sin_port, ifrom, &hfrom); + } + + /* If there is buffered data, read again. This is for, e.g., + bpf, which may return two packets at once. */ + if (ip -> rbuf_offset != ip -> rbuf_len) + goto again; + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_interface_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + struct interface_info *interface; + isc_result_t status; + int foo; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + if (!omapi_ds_strcmp (name, "name")) { + if ((value -> type == omapi_datatype_data || + value -> type == omapi_datatype_string) && + value -> u.buffer.len < sizeof interface -> name) { + memcpy (interface -> name, + value -> u.buffer.value, + value -> u.buffer.len); + interface -> name [value -> u.buffer.len] = 0; + } else + return ISC_R_INVALIDARG; + return ISC_R_SUCCESS; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) + return status; + } + + return ISC_R_NOTFOUND; +} + + +isc_result_t dhcp_interface_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + return ISC_R_NOTIMPLEMENTED; +} + +isc_result_t dhcp_interface_destroy (omapi_object_t *h, + const char *file, int line) +{ + struct interface_info *interface; + isc_result_t status; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + if (interface -> ifp) { + dfree (interface -> ifp, file, line); + interface -> ifp = 0; + } + if (interface -> next) + interface_dereference (&interface -> next, file, line); + if (interface -> rbuf) { + dfree (interface -> rbuf, file, line); + interface -> rbuf = (unsigned char *)0; + } + if (interface -> client) + interface -> client = (struct client_state *)0; + + if (interface -> shared_network) + omapi_object_dereference ((omapi_object_t **) + &interface -> shared_network, MDL); + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_interface_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + struct interface_info *ip, *interface; + struct client_config *config; + struct client_state *client; + isc_result_t status; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + /* If it's an update signal, see if the interface is dead right + now, or isn't known at all, and if that's the case, revive it. */ + if (!strcmp (name, "update")) { + for (ip = dummy_interfaces; ip; ip = ip -> next) + if (ip == interface) + break; + if (ip && dhcp_interface_startup_hook) + return (*dhcp_interface_startup_hook) (ip); + + for (ip = interfaces; ip; ip = ip -> next) + if (ip == interface) + break; + if (!ip && dhcp_interface_startup_hook) + return (*dhcp_interface_startup_hook) (ip); + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> get_value) { + status = ((*(h -> inner -> type -> signal_handler)) + (h -> inner, name, ap)); + if (status == ISC_R_SUCCESS) + return status; + } + return ISC_R_NOTFOUND; +} + +isc_result_t dhcp_interface_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + struct interface_info *interface; + isc_result_t status; + + if (h -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)h; + + /* Write out all the values. */ + + status = omapi_connection_put_name (c, "state"); + if (status != ISC_R_SUCCESS) + return status; + if (interface -> flags && INTERFACE_REQUESTED) + status = omapi_connection_put_string (c, "up"); + else + status = omapi_connection_put_string (c, "down"); + if (status != ISC_R_SUCCESS) + return status; + + /* Write out the inner object, if any. */ + if (h -> inner && h -> inner -> type -> stuff_values) { + status = ((*(h -> inner -> type -> stuff_values)) + (c, id, h -> inner)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t dhcp_interface_lookup (omapi_object_t **ip, + omapi_object_t *id, + omapi_object_t *ref) +{ + omapi_value_t *tv = (omapi_value_t *)0; + isc_result_t status; + struct interface_info *interface; + + if (!ref) + return ISC_R_NOKEYS; + + /* First see if we were sent a handle. */ + status = omapi_get_value_str (ref, id, "handle", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_handle_td_lookup (ip, tv -> value); + + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) + return status; + + /* Don't return the object if the type is wrong. */ + if ((*ip) -> type != dhcp_type_interface) { + omapi_object_dereference (ip, MDL); + return ISC_R_INVALIDARG; + } + } + + /* Now look for an interface name. */ + status = omapi_get_value_str (ref, id, "name", &tv); + if (status == ISC_R_SUCCESS) { + char *s; + unsigned len; + for (interface = interfaces; interface; + interface = interface -> next) { + s = memchr (interface -> name, 0, IFNAMSIZ); + if (s) + len = s - &interface -> name [0]; + else + len = IFNAMSIZ; + if ((tv -> value -> u.buffer.len == len && + !memcmp (interface -> name, + (char *)tv -> value -> u.buffer.value, + len))) + break; + } + if (!interface) { + for (interface = dummy_interfaces; + interface; interface = interface -> next) { + s = memchr (interface -> name, 0, IFNAMSIZ); + if (s) + len = s - &interface -> name [0]; + else + len = IFNAMSIZ; + if ((tv -> value -> u.buffer.len == len && + !memcmp (interface -> name, + (char *) + tv -> value -> u.buffer.value, + len))) + break; + } + } + + omapi_value_dereference (&tv, MDL); + if (*ip && *ip != (omapi_object_t *)interface) { + omapi_object_dereference (ip, MDL); + return ISC_R_KEYCONFLICT; + } else if (!interface) { + if (*ip) + omapi_object_dereference (ip, MDL); + return ISC_R_NOTFOUND; + } else if (!*ip) + omapi_object_reference (ip, + (omapi_object_t *)interface, + MDL); + } + + /* If we get to here without finding an interface, no valid key was + specified. */ + if (!*ip) + return ISC_R_NOKEYS; + return ISC_R_SUCCESS; +} + +/* actually just go discover the interface */ +isc_result_t dhcp_interface_create (omapi_object_t **lp, + omapi_object_t *id) +{ + struct interface_info *hp; + isc_result_t status; + + hp = (struct interface_info *)0; + status = interface_allocate (&hp, MDL); + if (status != ISC_R_SUCCESS) + return status; + hp -> flags = INTERFACE_REQUESTED; + status = interface_reference ((struct interface_info **)lp, hp, MDL); + interface_dereference (&hp, MDL); + return status; +} + +isc_result_t dhcp_interface_remove (omapi_object_t *lp, + omapi_object_t *id) +{ + struct interface_info *interface, *ip, *last; + + interface = (struct interface_info *)lp; + + /* remove from interfaces */ + last = 0; + for (ip = interfaces; ip; ip = ip -> next) { + if (ip == interface) { + if (last) { + interface_dereference (&last -> next, MDL); + if (ip -> next) + interface_reference (&last -> next, + ip -> next, MDL); + } else { + interface_dereference (&interfaces, MDL); + if (ip -> next) + interface_reference (&interfaces, + ip -> next, MDL); + } + if (ip -> next) + interface_dereference (&ip -> next, MDL); + break; + } + last = ip; + } + if (!ip) + return ISC_R_NOTFOUND; + + /* add the interface to the dummy_interface list */ + if (dummy_interfaces) { + interface_reference (&interface -> next, + dummy_interfaces, MDL); + interface_dereference (&dummy_interfaces, MDL); + } + interface_reference (&dummy_interfaces, interface, MDL); + + /* do a DHCPRELEASE */ + if (dhcp_interface_shutdown_hook) + (*dhcp_interface_shutdown_hook) (interface); + + /* remove the io object */ + omapi_unregister_io_object ((omapi_object_t *)interface); + + if_deregister_send (interface); + if_deregister_receive (interface); + + return ISC_R_SUCCESS; +} + +void interface_stash (struct interface_info *tptr) +{ + struct interface_info **vec; + int delta; + + /* If the registerer didn't assign an index, assign one now. */ + if (tptr -> index == -1) { + tptr -> index = interface_count++; + while (tptr -> index < interface_max && + interface_vector [tptr -> index]) + tptr -> index = interface_count++; + } + + if (interface_max <= tptr -> index) { + delta = tptr -> index - interface_max + 10; + vec = dmalloc ((interface_max + delta) * + sizeof (struct interface_info *), MDL); + if (!vec) + return; + memset (&vec [interface_max], 0, + (sizeof (struct interface_info *)) * delta); + interface_max += delta; + if (interface_vector) { + memcpy (vec, interface_vector, + (interface_count * + sizeof (struct interface_info *))); + dfree (interface_vector, MDL); + } + interface_vector = vec; + } + interface_reference (&interface_vector [tptr -> index], tptr, MDL); + if (tptr -> index >= interface_count) + interface_count = tptr -> index + 1; +#if defined (TRACING) + trace_interface_register (interface_trace, tptr); +#endif +} + +void interface_snorf (struct interface_info *tmp, int ir) +{ + tmp -> circuit_id = (u_int8_t *)tmp -> name; + tmp -> circuit_id_len = strlen (tmp -> name); + tmp -> remote_id = 0; + tmp -> remote_id_len = 0; + tmp -> flags = ir; + if (interfaces) { + interface_reference (&tmp -> next, + interfaces, MDL); + interface_dereference (&interfaces, MDL); + } + interface_reference (&interfaces, tmp, MDL); +} diff --git a/contrib/dhcp-3.0/common/dispatch.c b/contrib/dhcp-3.0/common/dispatch.c new file mode 100644 index 0000000000..01865ddadc --- /dev/null +++ b/contrib/dhcp-3.0/common/dispatch.c @@ -0,0 +1,219 @@ +/* dispatch.c + + Network input dispatcher... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: dispatch.c,v 1.63.2.4 2004/06/10 17:59:16 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +struct timeout *timeouts; +static struct timeout *free_timeouts; + +void set_time (u_int32_t t) +{ + /* Do any outstanding timeouts. */ + if (cur_time != t) { + cur_time = t; + process_outstanding_timeouts ((struct timeval *)0); + } +} + +struct timeval *process_outstanding_timeouts (struct timeval *tvp) +{ + /* Call any expired timeouts, and then if there's + still a timeout registered, time out the select + call then. */ + another: + if (timeouts) { + struct timeout *t; + if (timeouts -> when <= cur_time) { + t = timeouts; + timeouts = timeouts -> next; + (*(t -> func)) (t -> what); + if (t -> unref) + (*t -> unref) (&t -> what, MDL); + t -> next = free_timeouts; + free_timeouts = t; + goto another; + } + if (tvp) { + tvp -> tv_sec = timeouts -> when; + tvp -> tv_usec = 0; + } + return tvp; + } else + return (struct timeval *)0; +} + +/* Wait for packets to come in using select(). When one does, call + receive_packet to receive the packet and possibly strip hardware + addressing information from it, and then call through the + bootp_packet_handler hook to try to do something with it. */ + +void dispatch () +{ + struct timeval tv, *tvp; + isc_result_t status; + + /* Wait for a packet or a timeout... XXX */ + do { + tvp = process_outstanding_timeouts (&tv); + status = omapi_one_dispatch (0, tvp); + } while (status == ISC_R_TIMEDOUT || status == ISC_R_SUCCESS); + log_fatal ("omapi_one_dispatch failed: %s -- exiting.", + isc_result_totext (status)); +} + +void add_timeout (when, where, what, ref, unref) + TIME when; + void (*where) PROTO ((void *)); + void *what; + tvref_t ref; + tvunref_t unref; +{ + struct timeout *t, *q; + + /* See if this timeout supersedes an existing timeout. */ + t = (struct timeout *)0; + for (q = timeouts; q; q = q -> next) { + if ((where == NULL || q -> func == where) && + q -> what == what) { + if (t) + t -> next = q -> next; + else + timeouts = q -> next; + break; + } + t = q; + } + + /* If we didn't supersede a timeout, allocate a timeout + structure now. */ + if (!q) { + if (free_timeouts) { + q = free_timeouts; + free_timeouts = q -> next; + } else { + q = ((struct timeout *) + dmalloc (sizeof (struct timeout), MDL)); + if (!q) + log_fatal ("add_timeout: no memory!"); + } + memset (q, 0, sizeof *q); + q -> func = where; + q -> ref = ref; + q -> unref = unref; + if (q -> ref) + (*q -> ref)(&q -> what, what, MDL); + else + q -> what = what; + } + + q -> when = when; + + /* Now sort this timeout into the timeout list. */ + + /* Beginning of list? */ + if (!timeouts || timeouts -> when > q -> when) { + q -> next = timeouts; + timeouts = q; + return; + } + + /* Middle of list? */ + for (t = timeouts; t -> next; t = t -> next) { + if (t -> next -> when > q -> when) { + q -> next = t -> next; + t -> next = q; + return; + } + } + + /* End of list. */ + t -> next = q; + q -> next = (struct timeout *)0; +} + +void cancel_timeout (where, what) + void (*where) PROTO ((void *)); + void *what; +{ + struct timeout *t, *q; + + /* Look for this timeout on the list, and unlink it if we find it. */ + t = (struct timeout *)0; + for (q = timeouts; q; q = q -> next) { + if (q -> func == where && q -> what == what) { + if (t) + t -> next = q -> next; + else + timeouts = q -> next; + break; + } + t = q; + } + + /* If we found the timeout, put it on the free list. */ + if (q) { + if (q -> unref) + (*q -> unref) (&q -> what, MDL); + q -> next = free_timeouts; + free_timeouts = q; + } +} + +#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void cancel_all_timeouts () +{ + struct timeout *t, *n; + for (t = timeouts; t; t = n) { + n = t -> next; + if (t -> unref && t -> what) + (*t -> unref) (&t -> what, MDL); + t -> next = free_timeouts; + free_timeouts = t; + } +} + +void relinquish_timeouts () +{ + struct timeout *t, *n; + for (t = free_timeouts; t; t = n) { + n = t -> next; + dfree (t, MDL); + } +} +#endif diff --git a/contrib/dhcp-3.0/common/dlpi.c b/contrib/dhcp-3.0/common/dlpi.c new file mode 100644 index 0000000000..83fc40689e --- /dev/null +++ b/contrib/dhcp-3.0/common/dlpi.c @@ -0,0 +1,1336 @@ +/* dlpi.c + + Data Link Provider Interface (DLPI) network interface code. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software was written for Internet Systems Consortium + * by Eric James Negaard, . To learn more about + * Internet Systems Consortium, see ``http://www.isc.org''. + * + * Joost Mulders has also done considerable work in debugging the DLPI API + * support on Solaris and getting this code to work properly on a variety + * of different Solaris platforms. + */ + +/* + * Based largely in part to the existing NIT code in nit.c. + * + * This code has been developed and tested on sparc-based machines running + * SunOS 5.5.1, with le and hme network interfaces. It should be pretty + * generic, though. + */ + +/* + * Implementation notes: + * + * I first tried to write this code to the "vanilla" DLPI 2.0 API. + * It worked on a Sun Ultra-1 with a hme interface, but didn't work + * on Sun SparcStation 5's with "le" interfaces (the packets sent out + * via dlpiunitdatareq contained an Ethernet type of 0x0000 instead + * of the expected 0x0800). + * + * Therefore I added the "DLPI_RAW" code which is a Sun extension to + * the DLPI standard. This code works on both of the above machines. + * This is configurable in the OS-dependent include file by defining + * USE_DLPI_RAW. + * + * It quickly became apparant that I should also use the "pfmod" + * STREAMS module to cut down on the amount of user level packet + * processing. I don't know how widely available "pfmod" is, so it's + * use is conditionally included. This is configurable in the + * OS-dependent include file by defining USE_DLPI_PFMOD. + * + * A major quirk on the Sun's at least, is that no packets seem to get + * sent out the interface until six seconds after the interface is + * first "attached" to [per system reboot] (it's actually from when + * the interface is attached, not when it is plumbed, so putting a + * sleep into the dhclient-script at PREINIT time doesn't help). I + * HAVE tried, without success to poll the fd to see when it is ready + * for writing. This doesn't help at all. If the sleeps are not done, + * the initial DHCPREQUEST or DHCPDISCOVER never gets sent out, so + * I've put them here, when register_send and register_receive are + * called (split up into two three-second sleeps between the notices, + * so that it doesn't seem like so long when you're watching :-). The + * amount of time to sleep is configurable in the OS-dependent include + * file by defining DLPI_FIRST_SEND_WAIT to be the number of seconds + * to sleep. + */ + +#ifndef lint +static char copyright[] = +"$Id: dlpi.c,v 1.28.2.3 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) + +# include +# include +# include +# include +# ifdef USE_DLPI_PFMOD +# include +# endif +# ifdef USE_POLL +# include +# endif + +# include +# include "includes/netinet/ip.h" +# include "includes/netinet/udp.h" +# include "includes/netinet/if_ether.h" + +# ifdef USE_DLPI_PFMOD +# ifdef USE_DLPI_RAW +# define DLPI_MODNAME "DLPI+RAW+PFMOD" +# else +# define DLPI_MODNAME "DLPI+PFMOD" +# endif +# else +# ifdef USE_DLPI_RAW +# define DLPI_MODNAME "DLPI+RAW" +# else +# define DLPI_MODNAME "DLPI" +# endif +# endif + +# ifndef ABS +# define ABS(x) ((x) >= 0 ? (x) : 0-(x)) +# endif + +static int strioctl PROTO ((int fd, int cmd, int timeout, int len, char *dp)); + +#define DLPI_MAXDLBUF 8192 /* Buffer size */ +#define DLPI_MAXDLADDR 1024 /* Max address size */ +#define DLPI_DEVDIR "/dev/" /* Device directory */ + +static int dlpiopen PROTO ((char *ifname)); +static int dlpiunit PROTO ((char *ifname)); +static int dlpiinforeq PROTO ((int fd)); +static int dlpiphysaddrreq PROTO ((int fd, unsigned long addrtype)); +static int dlpiattachreq PROTO ((int fd, unsigned long ppa)); +static int dlpibindreq PROTO ((int fd, unsigned long sap, unsigned long max_conind, + unsigned long service_mode, unsigned long conn_mgmt, + unsigned long xidtest)); +static int dlpidetachreq PROTO ((int fd)); +static int dlpiunbindreq PROTO ((int fd)); +static int dlpiokack PROTO ((int fd, char *bufp)); +static int dlpiinfoack PROTO ((int fd, char *bufp)); +static int dlpiphysaddrack PROTO ((int fd, char *bufp)); +static int dlpibindack PROTO ((int fd, char *bufp)); +static int dlpiunitdatareq PROTO ((int fd, unsigned char *addr, + int addrlen, unsigned long minpri, + unsigned long maxpri, unsigned char *data, + int datalen)); +static int dlpiunitdataind PROTO ((int fd, + unsigned char *dstaddr, + unsigned long *dstaddrlen, + unsigned char *srcaddr, + unsigned long *srcaddrlen, + unsigned long *grpaddr, + unsigned char *data, + int datalen)); + +# ifndef USE_POLL +static void sigalrm PROTO ((int sig)); +# endif +static int expected PROTO ((unsigned long prim, union DL_primitives *dlp, + int msgflags)); +static int strgetmsg PROTO ((int fd, struct strbuf *ctlp, + struct strbuf *datap, int *flagsp, + char *caller)); + +/* Reinitializes the specified interface after an address change. This + is not required for packet-filter APIs. */ + +#ifdef USE_DLPI_SEND +void if_reinitialize_send (info) + struct interface_info *info; +{ +} +#endif + +#ifdef USE_DLPI_RECEIVE +void if_reinitialize_receive (info) + struct interface_info *info; +{ +} +#endif + +/* Called by get_interface_list for each interface that's discovered. + Opens a packet filter for each interface and adds it to the select + mask. */ + +int if_register_dlpi (info) + struct interface_info *info; +{ + int sock; + int unit; + long buf [DLPI_MAXDLBUF]; + union DL_primitives *dlp; + + dlp = (union DL_primitives *)buf; + + /* Open a DLPI device */ + if ((sock = dlpiopen (info -> name)) < 0) { + log_fatal ("Can't open DLPI device for %s: %m", info -> name); + } + + + /* + * Submit a DL_INFO_REQ request, to find the dl_mac_type and + * dl_provider_style + */ + if (dlpiinforeq(sock) < 0 || dlpiinfoack(sock, (char *)buf) < 0) { + log_fatal ("Can't get DLPI MAC type for %s: %m", info -> name); + } else { + switch (dlp -> info_ack.dl_mac_type) { + case DL_CSMACD: /* IEEE 802.3 */ + case DL_ETHER: + info -> hw_address.hbuf [0] = HTYPE_ETHER; + break; + /* adding token ring 5/1999 - mayer@ping.at */ + case DL_TPR: + info -> hw_address.hbuf [0] = HTYPE_IEEE802; + break; + case DL_FDDI: + info -> hw_address.hbuf [0] = HTYPE_FDDI; + break; + default: + log_fatal ("%s: unsupported DLPI MAC type %ld", + info -> name, dlp -> info_ack.dl_mac_type); + break; + } + /* + * copy the sap length and broadcast address of this interface + * to interface_info. This fixes nothing but seemed nicer than to + * assume -2 and ffffff. + */ + info -> dlpi_sap_length = dlp -> info_ack.dl_sap_length; + info -> dlpi_broadcast_addr.hlen = + dlp -> info_ack.dl_brdcst_addr_length; + memcpy (info -> dlpi_broadcast_addr.hbuf, + (char *)dlp + dlp -> info_ack.dl_brdcst_addr_offset, + dlp -> info_ack.dl_brdcst_addr_length); + } + + if (dlp -> info_ack.dl_provider_style == DL_STYLE2) { + /* + * Attach to the device. If this fails, the device + * does not exist. + */ + unit = dlpiunit (info -> name); + + if (dlpiattachreq (sock, unit) < 0 + || dlpiokack (sock, (char *)buf) < 0) { + log_fatal ("Can't attach DLPI device for %s: %m", info -> name); + } + } + + /* + * Bind to the IP service access point (SAP), connectionless (CLDLS). + */ + if (dlpibindreq (sock, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0) < 0 + || dlpibindack (sock, (char *)buf) < 0) { + log_fatal ("Can't bind DLPI device for %s: %m", info -> name); + } + + /* + * Submit a DL_PHYS_ADDR_REQ request, to find + * the hardware address + */ + if (dlpiphysaddrreq (sock, DL_CURR_PHYS_ADDR) < 0 + || dlpiphysaddrack (sock, (char *)buf) < 0) { + log_fatal ("Can't get DLPI hardware address for %s: %m", + info -> name); + } + + info -> hw_address.hlen = dlp -> physaddr_ack.dl_addr_length + 1; + memcpy (&info -> hw_address.hbuf [1], + (char *)buf + dlp -> physaddr_ack.dl_addr_offset, + dlp -> physaddr_ack.dl_addr_length); + +#ifdef USE_DLPI_RAW + if (strioctl (sock, DLIOCRAW, INFTIM, 0, 0) < 0) { + log_fatal ("Can't set DLPI RAW mode for %s: %m", + info -> name); + } +#endif + +#ifdef USE_DLPI_PFMOD + if (ioctl (sock, I_PUSH, "pfmod") < 0) { + log_fatal ("Can't push packet filter onto DLPI for %s: %m", + info -> name); + } +#endif + + return sock; +} + +static int +strioctl (fd, cmd, timeout, len, dp) +int fd; +int cmd; +int timeout; +int len; +char *dp; +{ + struct strioctl sio; + int rslt; + + sio.ic_cmd = cmd; + sio.ic_timout = timeout; + sio.ic_len = len; + sio.ic_dp = dp; + + if ((rslt = ioctl (fd, I_STR, &sio)) < 0) { + return rslt; + } else { + return sio.ic_len; + } +} + +#ifdef USE_DLPI_SEND +void if_register_send (info) + struct interface_info *info; +{ + /* If we're using the DLPI API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_DLPI_RECEIVE +# ifdef USE_DLPI_PFMOD + struct packetfilt pf; +# endif + + info -> wfdesc = if_register_dlpi (info); + +# ifdef USE_DLPI_PFMOD + /* Set up an PFMOD filter that rejects everything... */ + pf.Pf_Priority = 0; + pf.Pf_FilterLen = 1; + pf.Pf_Filter [0] = ENF_PUSHZERO; + + /* Install the filter */ + if (strioctl (info -> wfdesc, PFIOCSETF, INFTIM, + sizeof (pf), (char *)&pf) < 0) { + log_fatal ("Can't set PFMOD send filter on %s: %m", info -> name); + } + +# endif /* USE_DLPI_PFMOD */ +#else /* !defined (USE_DLPI_RECEIVE) */ + /* + * If using DLPI for both send and receive, simply re-use + * the read file descriptor that was set up earlier. + */ + info -> wfdesc = info -> rfdesc; +#endif + + if (!quiet_interface_discovery) + log_info ("Sending on DLPI/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); + +#ifdef DLPI_FIRST_SEND_WAIT +/* See the implementation notes at the beginning of this file */ +# ifdef USE_DLPI_RECEIVE + sleep (DLPI_FIRST_SEND_WAIT - (DLPI_FIRST_SEND_WAIT / 2)); +# else + sleep (DLPI_FIRST_SEND_WAIT); +# endif +#endif +} + +void if_deregister_send (info) + struct interface_info *info; +{ + /* If we're using the DLPI API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_DLPI_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling output on DLPI/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_DLPI_SEND */ + +#ifdef USE_DLPI_RECEIVE +/* Packet filter program... + XXX Changes to the filter program may require changes to the constant + offsets used in if_register_send to patch the NIT program! XXX */ + +void if_register_receive (info) + struct interface_info *info; +{ +#ifdef USE_DLPI_PFMOD + struct packetfilt pf; + struct ip iphdr; + u_int16_t offset; +#endif + + /* Open a DLPI device and hang it on this interface... */ + info -> rfdesc = if_register_dlpi (info); + +#ifdef USE_DLPI_PFMOD + /* Set up the PFMOD filter program. */ + /* XXX Unlike the BPF filter program, this one won't work if the + XXX IP packet is fragmented or if there are options on the IP + XXX header. */ + pf.Pf_Priority = 0; + pf.Pf_FilterLen = 0; + +#if defined (USE_DLPI_RAW) +# define ETHER_H_PREFIX (14) /* sizeof (ethernet_header) */ + /* + * ethertype == ETHERTYPE_IP + */ + offset = 12; + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; + pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP); +# else +# define ETHER_H_PREFIX (0) +# endif /* USE_DLPI_RAW */ + /* + * The packets that will be received on this file descriptor + * will be IP packets (due to the SAP that was specified in + * the dlbind call). There will be no ethernet header. + * Therefore, setup the packet filter to check the protocol + * field for UDP, and the destination port number equal + * to the local port. All offsets are relative to the start + * of an IP packet. + */ + + /* + * BOOTPS destination port + */ + offset = ETHER_H_PREFIX + sizeof (iphdr) + sizeof (u_int16_t); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; + pf.Pf_Filter [pf.Pf_FilterLen++] = local_port; + + /* + * protocol should be udp. this is a byte compare, test for + * endianess. + */ + offset = ETHER_H_PREFIX + ((u_int8_t *)&(iphdr.ip_p) - (u_int8_t *)&iphdr); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_AND; + pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0x00FF); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; + pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP); + + /* Install the filter... */ + if (strioctl (info -> rfdesc, PFIOCSETF, INFTIM, + sizeof (pf), (char *)&pf) < 0) { + log_fatal ("Can't set PFMOD receive filter on %s: %m", info -> name); + } +#endif /* USE_DLPI_PFMOD */ + + if (!quiet_interface_discovery) + log_info ("Listening on DLPI/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); + +#ifdef DLPI_FIRST_SEND_WAIT +/* See the implementation notes at the beginning of this file */ +# ifdef USE_DLPI_SEND + sleep (DLPI_FIRST_SEND_WAIT / 2); +# else + sleep (DLPI_FIRST_SEND_WAIT); +# endif +#endif +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + /* If we're using the DLPI API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_DLPI_SEND + close (info -> rfdesc); +#endif + info -> rfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling input on DLPI/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_DLPI_RECEIVE */ + +#ifdef USE_DLPI_SEND +ssize_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + unsigned hbufp = 0; + double hh [32]; + double ih [1536 / sizeof (double)]; + unsigned char *dbuf = (unsigned char *)ih; + unsigned dbuflen; + unsigned char dstaddr [DLPI_MAXDLADDR]; + unsigned addrlen; + int result; + int fudge; + + if (!strcmp (interface -> name, "fallback")) + return send_fallback (interface, packet, raw, + len, from, to, hto); + + dbuflen = 0; + + /* Assemble the headers... */ +#ifdef USE_DLPI_RAW + assemble_hw_header (interface, (unsigned char *)hh, &dbuflen, hto); + if (dbuflen > sizeof hh) + log_fatal ("send_packet: hh buffer too small.\n"); + fudge = dbuflen % 4; /* IP header must be word-aligned. */ + memcpy (dbuf + fudge, (unsigned char *)hh, dbuflen); + dbuflen += fudge; +#else + fudge = 0; +#endif + assemble_udp_ip_header (interface, dbuf, &dbuflen, from.s_addr, + to -> sin_addr.s_addr, to -> sin_port, + (unsigned char *)raw, len); + + /* Copy the data into the buffer (yuk). */ + memcpy (dbuf + dbuflen, raw, len); + dbuflen += len; + +#ifdef USE_DLPI_RAW + result = write (interface -> wfdesc, dbuf + fudge, dbuflen - fudge); +#else + + /* + * Setup the destination address (DLSAP) in dstaddr + * + * If sap_length < 0 we must deliver the DLSAP as phys+sap. + * If sap_length > 0 we must deliver the DLSAP as sap+phys. + * + * sap = Service Access Point == ETHERTYPE_IP + * sap + datalink address is called DLSAP in dlpi speak. + */ + { /* ENCODE DLSAP */ + unsigned char phys [DLPI_MAXDLADDR]; + unsigned char sap [4]; + int sap_len = interface -> dlpi_sap_length; + int phys_len = interface -> hw_address.hlen - 1; + + /* sap = htons (ETHERTYPE_IP) kludge */ + memset (sap, 0, sizeof (sap)); +# if (BYTE_ORDER == LITTLE_ENDIAN) + sap [0] = 0x00; + sap [1] = 0x08; +# else + sap [0] = 0x08; + sap [1] = 0x00; +# endif + + if (hto && hto -> hlen == interface -> hw_address.hlen) + memcpy ( phys, (char *) &hto -> hbuf [1], phys_len); + else + memcpy ( phys, interface -> dlpi_broadcast_addr.hbuf, + interface -> dlpi_broadcast_addr.hlen); + + if (sap_len < 0) { + memcpy ( dstaddr, phys, phys_len); + memcpy ( (char *) &dstaddr [phys_len], sap, ABS (sap_len)); + } + else { + memcpy ( dstaddr, (void *) sap, sap_len); + memcpy ( (char *) &dstaddr [sap_len], phys, phys_len); + } + addrlen = phys_len + ABS (sap_len); + } /* ENCODE DLSAP */ + + result = dlpiunitdatareq (interface -> wfdesc, dstaddr, addrlen, + 0, 0, dbuf, dbuflen); +#endif /* USE_DLPI_RAW */ + if (result < 0) + log_error ("send_packet: %m"); + return result; +} +#endif /* USE_DLPI_SEND */ + +#ifdef USE_DLPI_RECEIVE +ssize_t receive_packet (interface, buf, len, from, hfrom) + struct interface_info *interface; + unsigned char *buf; + size_t len; + struct sockaddr_in *from; + struct hardware *hfrom; +{ + unsigned char dbuf [1536]; + unsigned char srcaddr [DLPI_MAXDLADDR]; + unsigned long srcaddrlen; + int flags = 0; + int length = 0; + int offset = 0; + int rslt; + int bufix = 0; + +#ifdef USE_DLPI_RAW + length = read (interface -> rfdesc, dbuf, sizeof (dbuf)); +#else + length = dlpiunitdataind (interface -> rfdesc, (unsigned char *)NULL, + (unsigned long *)NULL, srcaddr, &srcaddrlen, + (unsigned long *)NULL, dbuf, sizeof (dbuf)); +#endif + + if (length <= 0) { + return length; + } + +# if !defined (USE_DLPI_RAW) + /* + * Copy the sender's hw address into hfrom + * If sap_len < 0 the DLSAP is as phys+sap. + * If sap_len > 0 the DLSAP is as sap+phys. + * + * sap is discarded here. + */ + { /* DECODE DLSAP */ + int sap_len = interface -> dlpi_sap_length; + int phys_len = interface -> hw_address.hlen - 1; + + if (hfrom && (srcaddrlen == ABS (sap_len) + phys_len )) { + hfrom -> hbuf [0] = interface -> hw_address.hbuf [0]; + hfrom -> hlen = interface -> hw_address.hlen; + + if (sap_len < 0) { + memcpy ((char *) &hfrom -> hbuf [1], srcaddr, phys_len); + } + else { + memcpy ((char *) &hfrom -> hbuf [1], (char *) &srcaddr [phys_len], + phys_len); + } + } + else if (hfrom) { + memset (hfrom, '\0', sizeof *hfrom); + } + } /* DECODE_DLSAP */ + +# endif /* !defined (USE_DLPI_RAW) */ + + /* Decode the IP and UDP headers... */ + bufix = 0; +#ifdef USE_DLPI_RAW + /* Decode the physical header... */ + offset = decode_hw_header (interface, dbuf, bufix, hfrom); + + /* If a physical layer checksum failed (dunno of any + physical layer that supports this, but WTH), skip this + packet. */ + if (offset < 0) { + return 0; + } + bufix += offset; + length -= offset; +#endif + offset = decode_udp_ip_header (interface, dbuf, bufix, + from, length); + + /* If the IP or UDP checksum was bad, skip the packet... */ + if (offset < 0) { + return 0; + } + + bufix += offset; + length -= offset; + + /* Copy out the data in the packet... */ + memcpy (buf, &dbuf [bufix], length); + return length; +} +#endif + +/* Common DLPI routines ... + * + * Written by Eric James Negaard, + * + * Based largely in part to the example code contained in the document + * "How to Use the STREAMS Data Link Provider Interface (DLPI)", written + * by Neal Nuckolls of SunSoft Internet Engineering. + * + * This code has been developed and tested on sparc-based machines running + * SunOS 5.5.1, with le and hme network interfaces. It should be pretty + * generic, though. + * + * The usual disclaimers apply. This code works for me. Don't blame me + * if it makes your machine or network go down in flames. That taken + * into consideration, use this code as you wish. If you make usefull + * modifications I'd appreciate hearing about it. + */ + +#define DLPI_MAXWAIT 15 /* Max timeout */ + + +/* + * Parse an interface name and extract the unit number + */ + +static int dlpiunit (ifname) + char *ifname; +{ + int fd; + char *cp, *dp, *ep; + int unit; + + if (!ifname) { + return 0; + } + + /* Advance to the end of the name */ + cp = ifname; + while (*cp) cp++; + /* Back up to the start of the first digit */ + while ((*(cp-1) >= '0' && *(cp-1) <= '9') || *(cp - 1) == ':') cp--; + + /* Convert the unit number */ + unit = 0; + while (*cp >= '0' && *cp <= '9') { + unit *= 10; + unit += (*cp++ - '0'); + } + + return unit; +} + +/* + * dlpiopen - open the DLPI device for a given interface name + */ +static int dlpiopen (ifname) + char *ifname; +{ + char devname [50]; + char *cp, *dp, *ep; + + if (!ifname) { + return -1; + } + + /* Open a DLPI device */ + if (*ifname == '/') { + dp = devname; + } else { + /* Prepend the device directory */ + memcpy (devname, DLPI_DEVDIR, strlen (DLPI_DEVDIR)); + dp = &devname [strlen (DLPI_DEVDIR)]; + } + + /* Find the end of the interface name */ + ep = cp = ifname; + while (*ep) + ep++; + /* And back up to the first digit (unit number) */ + while ((*(ep - 1) >= '0' && *(ep - 1) <= '9') || *(ep - 1) == ':') + ep--; + + /* Copy everything up to the unit number */ + while (cp < ep) { + *dp++ = *cp++; + } + *dp = '\0'; + + return open (devname, O_RDWR, 0); +} + +/* + * dlpiinforeq - request information about the data link provider. + */ + +static int dlpiinforeq (fd) + int fd; +{ + dl_info_req_t info_req; + struct strbuf ctl; + int flags; + + info_req.dl_primitive = DL_INFO_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (info_req); + ctl.buf = (char *)&info_req; + + flags = RS_HIPRI; + + return putmsg (fd, &ctl, (struct strbuf *)NULL, flags); +} + +/* + * dlpiphysaddrreq - request the current physical address. + */ +static int dlpiphysaddrreq (fd, addrtype) + int fd; + unsigned long addrtype; +{ + dl_phys_addr_req_t physaddr_req; + struct strbuf ctl; + int flags; + + physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ; + physaddr_req.dl_addr_type = addrtype; + + ctl.maxlen = 0; + ctl.len = sizeof (physaddr_req); + ctl.buf = (char *)&physaddr_req; + + flags = RS_HIPRI; + + return putmsg (fd, &ctl, (struct strbuf *)NULL, flags); +} + +/* + * dlpiattachreq - send a request to attach to a specific unit. + */ +static int dlpiattachreq (fd, ppa) + unsigned long ppa; + int fd; +{ + dl_attach_req_t attach_req; + struct strbuf ctl; + int flags; + + attach_req.dl_primitive = DL_ATTACH_REQ; + attach_req.dl_ppa = ppa; + + ctl.maxlen = 0; + ctl.len = sizeof (attach_req); + ctl.buf = (char *)&attach_req; + + flags = 0; + + return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); +} + +/* + * dlpibindreq - send a request to bind to a specific SAP address. + */ +static int dlpibindreq (fd, sap, max_conind, service_mode, conn_mgmt, xidtest) + unsigned long sap; + unsigned long max_conind; + unsigned long service_mode; + unsigned long conn_mgmt; + unsigned long xidtest; + int fd; +{ + dl_bind_req_t bind_req; + struct strbuf ctl; + int flags; + + bind_req.dl_primitive = DL_BIND_REQ; + bind_req.dl_sap = sap; + bind_req.dl_max_conind = max_conind; + bind_req.dl_service_mode = service_mode; + bind_req.dl_conn_mgmt = conn_mgmt; + bind_req.dl_xidtest_flg = xidtest; + + ctl.maxlen = 0; + ctl.len = sizeof (bind_req); + ctl.buf = (char *)&bind_req; + + flags = 0; + + return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); +} + +/* + * dlpiunbindreq - send a request to unbind. + */ +static int dlpiunbindreq (fd) + int fd; +{ + dl_unbind_req_t unbind_req; + struct strbuf ctl; + int flags; + + unbind_req.dl_primitive = DL_UNBIND_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (unbind_req); + ctl.buf = (char *)&unbind_req; + + flags = 0; + + return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); +} + + +/* + * dlpidetachreq - send a request to detach. + */ +static int dlpidetachreq (fd) + int fd; +{ + dl_detach_req_t detach_req; + struct strbuf ctl; + int flags; + + detach_req.dl_primitive = DL_DETACH_REQ; + + ctl.maxlen = 0; + ctl.len = sizeof (detach_req); + ctl.buf = (char *)&detach_req; + + flags = 0; + + return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); +} + + +/* + * dlpibindack - receive an ack to a dlbindreq. + */ +static int dlpibindack (fd, bufp) + char *bufp; + int fd; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = DLPI_MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + if (strgetmsg (fd, &ctl, + (struct strbuf*)NULL, &flags, "dlpibindack") < 0) { + return -1; + } + + dlp = (union DL_primitives *)ctl.buf; + + if (!expected (DL_BIND_ACK, dlp, flags) < 0) { + return -1; + } + + if (ctl.len < sizeof (dl_bind_ack_t)) { + /* Returned structure is too short */ + return -1; + } + + return 0; +} + +/* + * dlpiokack - general acknowledgement reception. + */ +static int dlpiokack (fd, bufp) + char *bufp; + int fd; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = DLPI_MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + if (strgetmsg (fd, &ctl, + (struct strbuf*)NULL, &flags, "dlpiokack") < 0) { + return -1; + } + + dlp = (union DL_primitives *)ctl.buf; + + if (!expected (DL_OK_ACK, dlp, flags) < 0) { + return -1; + } + + if (ctl.len < sizeof (dl_ok_ack_t)) { + /* Returned structure is too short */ + return -1; + } + + return 0; +} + +/* + * dlpiinfoack - receive an ack to a dlinforeq. + */ +static int dlpiinfoack (fd, bufp) + char *bufp; + int fd; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = DLPI_MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags, + "dlpiinfoack") < 0) { + return -1; + } + + dlp = (union DL_primitives *) ctl.buf; + + if (!expected (DL_INFO_ACK, dlp, flags) < 0) { + return -1; + } + + if (ctl.len < sizeof (dl_info_ack_t)) { + /* Returned structure is too short */ + return -1; + } + + return 0; +} + +/* + * dlpiphysaddrack - receive an ack to a dlpiphysaddrreq. + */ +int dlpiphysaddrack (fd, bufp) + char *bufp; + int fd; +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = DLPI_MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags, + "dlpiphysaddrack") < 0) { + return -1; + } + + dlp = (union DL_primitives *)ctl.buf; + + if (!expected (DL_PHYS_ADDR_ACK, dlp, flags) < 0) { + return -1; + } + + if (ctl.len < sizeof (dl_phys_addr_ack_t)) { + /* Returned structure is too short */ + return -1; + } + + return 0; +} + +int dlpiunitdatareq (fd, addr, addrlen, minpri, maxpri, dbuf, dbuflen) + int fd; + unsigned char *addr; + int addrlen; + unsigned long minpri; + unsigned long maxpri; + unsigned char *dbuf; + int dbuflen; +{ + long buf [DLPI_MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf ctl, data; + + /* Set up the control information... */ + dlp = (union DL_primitives *)buf; + dlp -> unitdata_req.dl_primitive = DL_UNITDATA_REQ; + dlp -> unitdata_req.dl_dest_addr_length = addrlen; + dlp -> unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); + dlp -> unitdata_req.dl_priority.dl_min = minpri; + dlp -> unitdata_req.dl_priority.dl_max = maxpri; + + /* Append the destination address */ + memcpy ((char *)buf + dlp -> unitdata_req.dl_dest_addr_offset, + addr, addrlen); + + ctl.maxlen = 0; + ctl.len = dlp -> unitdata_req.dl_dest_addr_offset + addrlen; + ctl.buf = (char *)buf; + + data.maxlen = 0; + data.buf = (char *)dbuf; + data.len = dbuflen; + + /* Send the packet down the wire... */ + return putmsg (fd, &ctl, &data, 0); +} + +static int dlpiunitdataind (fd, daddr, daddrlen, + saddr, saddrlen, grpaddr, dbuf, dlen) + int fd; + unsigned char *daddr; + unsigned long *daddrlen; + unsigned char *saddr; + unsigned long *saddrlen; + unsigned long *grpaddr; + unsigned char *dbuf; + int dlen; +{ + long buf [DLPI_MAXDLBUF]; + union DL_primitives *dlp; + struct strbuf ctl, data; + int flags = 0; + int result; + + /* Set up the msg_buf structure... */ + dlp = (union DL_primitives *)buf; + dlp -> unitdata_ind.dl_primitive = DL_UNITDATA_IND; + + ctl.maxlen = DLPI_MAXDLBUF; + ctl.len = 0; + ctl.buf = (char *)buf; + + data.maxlen = dlen; + data.len = 0; + data.buf = (char *)dbuf; + + result = getmsg (fd, &ctl, &data, &flags); + + if (result != 0) { + return -1; + } + + if (ctl.len < sizeof (dl_unitdata_ind_t) || + dlp -> unitdata_ind.dl_primitive != DL_UNITDATA_IND) { + return -1; + } + + if (data.len <= 0) { + return data.len; + } + + /* Copy sender info */ + if (saddr) { + memcpy (saddr, + (char *)buf + dlp -> unitdata_ind.dl_src_addr_offset, + dlp -> unitdata_ind.dl_src_addr_length); + } + if (saddrlen) { + *saddrlen = dlp -> unitdata_ind.dl_src_addr_length; + } + + /* Copy destination info */ + if (daddr) { + memcpy (daddr, + (char *)buf + dlp -> unitdata_ind.dl_dest_addr_offset, + dlp -> unitdata_ind.dl_dest_addr_length); + } + if (daddrlen) { + *daddrlen = dlp -> unitdata_ind.dl_dest_addr_length; + } + + if (grpaddr) { + *grpaddr = dlp -> unitdata_ind.dl_group_address; + } + + return data.len; +} + +/* + * expected - see if we got what we wanted. + */ +static int expected (prim, dlp, msgflags) + unsigned long prim; + union DL_primitives *dlp; + int msgflags; +{ + if (msgflags != RS_HIPRI) { + /* Message was not M_PCPROTO */ + return 0; + } + + if (dlp -> dl_primitive != prim) { + /* Incorrect/unexpected return message */ + return 0; + } + + return 1; +} + +/* + * strgetmsg - get a message from a stream, with timeout. + */ +static int strgetmsg (fd, ctlp, datap, flagsp, caller) + struct strbuf *ctlp, *datap; + char *caller; + int *flagsp; + int fd; +{ + int result; +#ifdef USE_POLL + struct pollfd pfd; + int count; + time_t now; + time_t starttime; + int to_msec; +#endif + +#ifdef USE_POLL + pfd.fd = fd; + pfd.events = POLLPRI; /* We're only interested in knowing + * when we can receive the next high + * priority message. + */ + pfd.revents = 0; + + now = time (&starttime); + while (now <= starttime + DLPI_MAXWAIT) { + to_msec = ((starttime + DLPI_MAXWAIT) - now) * 1000; + count = poll (&pfd, 1, to_msec); + + if (count == 0) { + /* log_fatal ("strgetmsg: timeout"); */ + return -1; + } else if (count < 0) { + if (errno == EAGAIN || errno == EINTR) { + time (&now); + continue; + } else { + /* log_fatal ("poll: %m"); */ + return -1; + } + } else { + break; + } + } +#else /* defined (USE_POLL) */ + /* + * Start timer. Can't use select, since it might return true if there + * were non High-Priority data available on the stream. + */ + (void) sigset (SIGALRM, sigalrm); + + if (alarm (DLPI_MAXWAIT) < 0) { + /* log_fatal ("alarm: %m"); */ + return -1; + } +#endif /* !defined (USE_POLL) */ + + /* + * Set flags argument and issue getmsg (). + */ + *flagsp = 0; + if ((result = getmsg (fd, ctlp, datap, flagsp)) < 0) { + return result; + } + +#ifndef USE_POLL + /* + * Stop timer. + */ + if (alarm (0) < 0) { + /* log_fatal ("alarm: %m"); */ + return -1; + } +#endif + + /* + * Check for MOREDATA and/or MORECTL. + */ + if (result & (MORECTL|MOREDATA)) { + return -1; + } + + /* + * Check for at least sizeof (long) control data portion. + */ + if (ctlp -> len < sizeof (long)) { + return -1; + } + + return 0; +} + +#ifndef USE_POLL +/* + * sigalrm - handle alarms. + */ +static void sigalrm (sig) + int sig; +{ + fprintf (stderr, "strgetmsg: timeout"); + exit (1); +} +#endif /* !defined (USE_POLL) */ + +int can_unicast_without_arp (ip) + struct interface_info *ip; +{ + return 1; +} + +int can_receive_unicast_unconfigured (ip) + struct interface_info *ip; +{ + return 1; +} + +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ + return 1; +} + +void maybe_setup_fallback () +{ + isc_result_t status; + struct interface_info *fbi = (struct interface_info *)0; + if (setup_fallback (&fbi, MDL)) { + if_register_fallback (fbi); + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); + } +} +#endif /* USE_DLPI */ diff --git a/contrib/dhcp-3.0/common/dns.c b/contrib/dhcp-3.0/common/dns.c new file mode 100644 index 0000000000..e80df9ab7c --- /dev/null +++ b/contrib/dhcp-3.0/common/dns.c @@ -0,0 +1,953 @@ +/* dns.c + + Domain Name Service subroutines. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: dns.c,v 1.35.2.16 2004/06/17 20:54:38 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include "arpa/nameser.h" +#include "dst/md5.h" + +/* This file is kind of a crutch for the BIND 8 nsupdate code, which has + * itself been cruelly hacked from its original state. What this code + * does is twofold: first, it maintains a database of zone cuts that can + * be used to figure out which server should be contacted to update any + * given domain name. Secondly, it maintains a set of named TSIG keys, + * and associates those keys with zones. When an update is requested for + * a particular zone, the key associated with that zone is used for the + * update. + * + * The way this works is that you define the domain name to which an + * SOA corresponds, and the addresses of some primaries for that domain name: + * + * zone FOO.COM { + * primary 10.0.17.1; + * secondary 10.0.22.1, 10.0.23.1; + * key "FOO.COM Key"; + * } + * + * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name + * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM", + * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*, + * looks for "FOO.COM", finds it. So it + * attempts the update to the primary for FOO.COM. If that times out, it + * tries the secondaries. You can list multiple primaries if you have some + * kind of magic name server that supports that. You shouldn't list + * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't + * support update forwarding, AFAIK). If no TSIG key is listed, the update + * is attempted without TSIG. + * + * The DHCP server tries to find an existing zone for any given name by + * trying to look up a local zone structure for each domain containing + * that name, all the way up to '.'. If it finds one cached, it tries + * to use that one to do the update. That's why it tries to update + * "FOO.COM" above, even though theoretically it should try GAZANGA... + * and TOPANGA... first. + * + * If the update fails with a predefined or cached zone (we'll get to + * those in a second), then it tries to find a more specific zone. This + * is done by looking first for an SOA for GAZANGA.TOPANGA.FOO.COM. Then + * an SOA for TOPANGA.FOO.COM is sought. If during this search a predefined + * or cached zone is found, the update fails - there's something wrong + * somewhere. + * + * If a more specific zone _is_ found, that zone is cached for the length of + * its TTL in the same database as that described above. TSIG updates are + * never done for cached zones - if you want TSIG updates you _must_ + * write a zone definition linking the key to the zone. In cases where you + * know for sure what the key is but do not want to hardcode the IP addresses + * of the primary or secondaries, a zone declaration can be made that doesn't + * include any primary or secondary declarations. When the DHCP server + * encounters this while hunting up a matching zone for a name, it looks up + * the SOA, fills in the IP addresses, and uses that record for the update. + * If the SOA lookup returns NXRRSET, a warning is printed and the zone is + * discarded, TSIG key and all. The search for the zone then continues as if + * the zone record hadn't been found. Zones without IP addresses don't + * match when initially hunting for a predefined or cached zone to update. + * + * When an update is attempted and no predefined or cached zone is found + * that matches any enclosing domain of the domain being updated, the DHCP + * server goes through the same process that is done when the update to a + * predefined or cached zone fails - starting with the most specific domain + * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root), + * it tries to look up an SOA record. When it finds one, it creates a cached + * zone and attempts an update, and gives up if the update fails. + * + * TSIG keys are defined like this: + * + * key "FOO.COM Key" { + * algorithm HMAC-MD5.SIG-ALG.REG.INT; + * secret ; + * } + * + * is a number expressed in base64 that represents the key. + * It's also permissible to use a quoted string here - this will be + * translated as the ASCII bytes making up the string, and will not + * include any NUL termination. The key name can be any text string, + * and the key type must be one of the key types defined in the draft + * or by the IANA. Currently only the HMAC-MD5... key type is + * supported. + */ + +dns_zone_hash_t *dns_zone_hash; + +#if defined (NSUPDATE) +isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname, + struct dns_zone *zone) +{ + isc_result_t status; + ns_tsig_key *tkey; + + if (!zone) + return ISC_R_NOTFOUND; + + if (!zone -> key) { + return ISC_R_KEY_UNKNOWN; + } + + if ((!zone -> key -> name || + strlen (zone -> key -> name) > NS_MAXDNAME) || + (!zone -> key -> algorithm || + strlen (zone -> key -> algorithm) > NS_MAXDNAME) || + (!zone -> key) || + (!zone -> key -> key) || + (zone -> key -> key -> len == 0)) { + return ISC_R_INVALIDKEY; + } + tkey = dmalloc (sizeof *tkey, MDL); + if (!tkey) { + nomem: + return ISC_R_NOMEMORY; + } + memset (tkey, 0, sizeof *tkey); + tkey -> data = dmalloc (zone -> key -> key -> len, MDL); + if (!tkey -> data) { + dfree (tkey, MDL); + goto nomem; + } + strcpy (tkey -> name, zone -> key -> name); + strcpy (tkey -> alg, zone -> key -> algorithm); + memcpy (tkey -> data, + zone -> key -> key -> value, zone -> key -> key -> len); + tkey -> len = zone -> key -> key -> len; + *key = tkey; + return ISC_R_SUCCESS; +} + +void tkey_free (ns_tsig_key **key) +{ + if ((*key) -> data) + dfree ((*key) -> data, MDL); + dfree ((*key), MDL); + *key = (ns_tsig_key *)0; +} +#endif + +isc_result_t enter_dns_zone (struct dns_zone *zone) +{ + struct dns_zone *tz = (struct dns_zone *)0; + + if (dns_zone_hash) { + dns_zone_hash_lookup (&tz, + dns_zone_hash, zone -> name, 0, MDL); + if (tz == zone) { + dns_zone_dereference (&tz, MDL); + return ISC_R_SUCCESS; + } + if (tz) { + dns_zone_hash_delete (dns_zone_hash, + zone -> name, 0, MDL); + dns_zone_dereference (&tz, MDL); + } + } else { + if (!dns_zone_new_hash (&dns_zone_hash, 1, MDL)) + return ISC_R_NOMEMORY; + } + dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL); + return ISC_R_SUCCESS; +} + +isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name) +{ + struct dns_zone *tz = (struct dns_zone *)0; + int len; + char *tname = (char *)0; + isc_result_t status; + + if (!dns_zone_hash) + return ISC_R_NOTFOUND; + + len = strlen (name); + if (name [len - 1] != '.') { + tname = dmalloc ((unsigned)len + 2, MDL); + if (!tname) + return ISC_R_NOMEMORY;; + strcpy (tname, name); + tname [len] = '.'; + tname [len + 1] = 0; + name = tname; + } + if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL)) + status = ISC_R_NOTFOUND; + else + status = ISC_R_SUCCESS; + + if (tname) + dfree (tname, MDL); + return status; +} + +int dns_zone_dereference (ptr, file, line) + struct dns_zone **ptr; + const char *file; + int line; +{ + int i; + struct dns_zone *dns_zone; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + dns_zone = *ptr; + *ptr = (struct dns_zone *)0; + --dns_zone -> refcnt; + rc_register (file, line, ptr, dns_zone, dns_zone -> refcnt, 1, RC_MISC); + if (dns_zone -> refcnt > 0) + return 1; + + if (dns_zone -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (dns_zone); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (dns_zone -> name) + dfree (dns_zone -> name, file, line); + if (dns_zone -> key) + omapi_auth_key_dereference (&dns_zone -> key, file, line); + if (dns_zone -> primary) + option_cache_dereference (&dns_zone -> primary, file, line); + if (dns_zone -> secondary) + option_cache_dereference (&dns_zone -> secondary, file, line); + dfree (dns_zone, file, line); + return 1; +} + +#if defined (NSUPDATE) +isc_result_t find_cached_zone (const char *dname, ns_class class, + char *zname, size_t zsize, + struct in_addr *addrs, + int naddrs, int *naddrout, + struct dns_zone **zcookie) +{ + isc_result_t status = ISC_R_NOTFOUND; + const char *np; + struct dns_zone *zone = (struct dns_zone *)0; + struct data_string nsaddrs; + int ix; + + /* The absence of the zcookie pointer indicates that we + succeeded previously, but the update itself failed, meaning + that we shouldn't use the cached zone. */ + if (!zcookie) + return ISC_R_NOTFOUND; + + /* We can't look up a null zone. */ + if (!dname || !*dname) + return ISC_R_INVALIDARG; + + /* For each subzone, try to find a cached zone. */ + for (np = dname; np; np = strchr (np, '.')) { + np++; + status = dns_zone_lookup (&zone, np); + if (status == ISC_R_SUCCESS) + break; + } + + if (status != ISC_R_SUCCESS) + return status; + + /* Make sure the zone is valid. */ + if (zone -> timeout && zone -> timeout < cur_time) { + dns_zone_dereference (&zone, MDL); + return ISC_R_CANCELED; + } + + /* Make sure the zone name will fit. */ + if (strlen (zone -> name) > zsize) { + dns_zone_dereference (&zone, MDL); + return ISC_R_NOSPACE; + } + strcpy (zname, zone -> name); + + memset (&nsaddrs, 0, sizeof nsaddrs); + ix = 0; + + if (zone -> primary) { + if (evaluate_option_cache (&nsaddrs, (struct packet *)0, + (struct lease *)0, + (struct client_state *)0, + (struct option_state *)0, + (struct option_state *)0, + &global_scope, + zone -> primary, MDL)) { + int ip = 0; + while (ix < naddrs) { + if (ip + 4 > nsaddrs.len) + break; + memcpy (&addrs [ix], &nsaddrs.data [ip], 4); + ip += 4; + ix++; + } + data_string_forget (&nsaddrs, MDL); + } + } + if (zone -> secondary) { + if (evaluate_option_cache (&nsaddrs, (struct packet *)0, + (struct lease *)0, + (struct client_state *)0, + (struct option_state *)0, + (struct option_state *)0, + &global_scope, + zone -> secondary, MDL)) { + int ip = 0; + while (ix < naddrs) { + if (ip + 4 > nsaddrs.len) + break; + memcpy (&addrs [ix], &nsaddrs.data [ip], 4); + ip += 4; + ix++; + } + data_string_forget (&nsaddrs, MDL); + } + } + + /* It's not an error for zcookie to have a value here - actually, + it's quite likely, because res_nupdate cycles through all the + names in the update looking for their zones. */ + if (!*zcookie) + dns_zone_reference (zcookie, zone, MDL); + dns_zone_dereference (&zone, MDL); + if (naddrout) + *naddrout = ix; + return ISC_R_SUCCESS; +} + +void forget_zone (struct dns_zone **zone) +{ + dns_zone_dereference (zone, MDL); +} + +void repudiate_zone (struct dns_zone **zone) +{ + /* XXX Currently we're not differentiating between a cached + XXX zone and a zone that's been repudiated, which means + XXX that if we reap cached zones, we blow away repudiated + XXX zones. This isn't a big problem since we're not yet + XXX caching zones... :'} */ + + (*zone) -> timeout = cur_time - 1; + dns_zone_dereference (zone, MDL); +} + +void cache_found_zone (ns_class class, + char *zname, struct in_addr *addrs, int naddrs) +{ + isc_result_t status = ISC_R_NOTFOUND; + struct dns_zone *zone = (struct dns_zone *)0; + struct data_string nsaddrs; + int ix = strlen (zname); + + if (zname [ix - 1] == '.') + ix = 0; + + /* See if there's already such a zone. */ + if (dns_zone_lookup (&zone, zname) == ISC_R_SUCCESS) { + /* If it's not a dynamic zone, leave it alone. */ + if (!zone -> timeout) + return; + /* Address may have changed, so just blow it away. */ + if (zone -> primary) + option_cache_dereference (&zone -> primary, MDL); + if (zone -> secondary) + option_cache_dereference (&zone -> secondary, MDL); + } else if (!dns_zone_allocate (&zone, MDL)) + return; + + if (!zone -> name) { + zone -> name = + dmalloc (strlen (zname) + 1 + (ix != 0), MDL); + if (!zone -> name) { + dns_zone_dereference (&zone, MDL); + return; + } + strcpy (zone -> name, zname); + /* Add a trailing '.' if it was missing. */ + if (ix) { + zone -> name [ix] = '.'; + zone -> name [ix + 1] = 0; + } + } + + /* XXX Need to get the lower-level code to push the actual zone + XXX TTL up to us. */ + zone -> timeout = cur_time + 1800; + + if (!option_cache_allocate (&zone -> primary, MDL)) { + dns_zone_dereference (&zone, MDL); + return; + } + if (!buffer_allocate (&zone -> primary -> data.buffer, + naddrs * sizeof (struct in_addr), MDL)) { + dns_zone_dereference (&zone, MDL); + return; + } + memcpy (zone -> primary -> data.buffer -> data, + addrs, naddrs * sizeof *addrs); + zone -> primary -> data.data = + &zone -> primary -> data.buffer -> data [0]; + zone -> primary -> data.len = naddrs * sizeof *addrs; + + enter_dns_zone (zone); +} + +/* Have to use TXT records for now. */ +#define T_DHCID T_TXT + +int get_dhcid (struct data_string *id, + int type, const u_int8_t *data, unsigned len) +{ + unsigned char buf[MD5_DIGEST_LENGTH]; + MD5_CTX md5; + int i; + + /* Types can only be 0..(2^16)-1. */ + if (type < 0 || type > 65535) + return 0; + + /* Hexadecimal MD5 digest plus two byte type and NUL. */ + if (!buffer_allocate (&id -> buffer, + (MD5_DIGEST_LENGTH * 2) + 3, MDL)) + return 0; + id -> data = id -> buffer -> data; + + /* + * DHCP clients and servers should use the following forms of client + * identification, starting with the most preferable, and finishing + * with the least preferable. If the client does not send any of these + * forms of identification, the DHCP/DDNS interaction is not defined by + * this specification. The most preferable form of identification is + * the Globally Unique Identifier Option [TBD]. Next is the DHCP + * Client Identifier option. Last is the client's link-layer address, + * as conveyed in its DHCPREQUEST message. Implementors should note + * that the link-layer address cannot be used if there are no + * significant bytes in the chaddr field of the DHCP client's request, + * because this does not constitute a unique identifier. + * -- "Interaction between DHCP and DNS" + * + * M. Stapp, Y. Rekhter + */ + + /* Put the type in the first two bytes. */ + id -> buffer -> data [0] = "0123456789abcdef" [type >> 4]; + id -> buffer -> data [1] = "0123456789abcdef" [type % 15]; + + /* Mash together an MD5 hash of the identifier. */ + MD5_Init (&md5); + MD5_Update (&md5, data, len); + MD5_Final (buf, &md5); + + /* Convert into ASCII. */ + for (i = 0; i < MD5_DIGEST_LENGTH; i++) { + id -> buffer -> data [i * 2 + 2] = + "0123456789abcdef" [(buf [i] >> 4) & 0xf]; + id -> buffer -> data [i * 2 + 3] = + "0123456789abcdef" [buf [i] & 0xf]; + } + id -> len = MD5_DIGEST_LENGTH * 2 + 2; + id -> buffer -> data [id -> len] = 0; + id -> terminated = 1; + + return 1; +} + +/* Now for the DDNS update code that is shared between client and + server... */ + +isc_result_t ddns_update_a (struct data_string *ddns_fwd_name, + struct iaddr ddns_addr, + struct data_string *ddns_dhcid, + unsigned long ttl, int rrsetp) +{ + ns_updque updqueue; + ns_updrec *updrec; + isc_result_t result; + char ddns_address [16]; + + if (ddns_addr.len != 4) + return ISC_R_INVALIDARG; + + /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% */ + sprintf (ddns_address, "%u.%u.%u.%u", + ddns_addr.iabuf[0], ddns_addr.iabuf[1], + ddns_addr.iabuf[2], ddns_addr.iabuf[3]); + + /* + * When a DHCP client or server intends to update an A RR, it first + * prepares a DNS UPDATE query which includes as a prerequisite the + * assertion that the name does not exist. The update section of the + * query attempts to add the new name and its IP address mapping (an A + * RR), and the DHCID RR with its unique client-identity. + * -- "Interaction between DHCP and DNS" + */ + + ISC_LIST_INIT (updqueue); + + /* + * A RR does not exist. + */ + updrec = minires_mkupdrec (S_PREREQ, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)0; + updrec -> r_size = 0; + updrec -> r_opcode = rrsetp ? NXRRSET : NXDOMAIN; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Add A RR. + */ + updrec = minires_mkupdrec (S_UPDATE, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, ttl); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)ddns_address; + updrec -> r_size = strlen (ddns_address); + updrec -> r_opcode = ADD; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Add DHCID RR. + */ + updrec = minires_mkupdrec (S_UPDATE, + (const char *)ddns_fwd_name -> data, + C_IN, T_DHCID, ttl); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = ddns_dhcid -> data; + updrec -> r_size = ddns_dhcid -> len; + updrec -> r_opcode = ADD; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Attempt to perform the update. + */ + result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue)); + +#ifdef DEBUG_DNS_UPDATES + print_dns_status ((int)result, &updqueue); +#endif + + /* + * If this update operation succeeds, the updater can conclude that it + * has added a new name whose only RRs are the A and DHCID RR records. + * The A RR update is now complete (and a client updater is finished, + * while a server might proceed to perform a PTR RR update). + * -- "Interaction between DHCP and DNS" + */ + + if (result == ISC_R_SUCCESS) { + log_info ("Added new forward map from %.*s to %s", + (int)ddns_fwd_name -> len, + (const char *)ddns_fwd_name -> data, ddns_address); + goto error; + } + + + /* + * If the first update operation fails with YXDOMAIN, the updater can + * conclude that the intended name is in use. The updater then + * attempts to confirm that the DNS name is not being used by some + * other host. The updater prepares a second UPDATE query in which the + * prerequisite is that the desired name has attached to it a DHCID RR + * whose contents match the client identity. The update section of + * this query deletes the existing A records on the name, and adds the + * A record that matches the DHCP binding and the DHCID RR with the + * client identity. + * -- "Interaction between DHCP and DNS" + */ + + if (result != (rrsetp ? ISC_R_YXRRSET : ISC_R_YXDOMAIN)) { + log_error ("Unable to add forward map from %.*s to %s: %s", + (int)ddns_fwd_name -> len, + (const char *)ddns_fwd_name -> data, ddns_address, + isc_result_totext (result)); + goto error; + } + + while (!ISC_LIST_EMPTY (updqueue)) { + updrec = ISC_LIST_HEAD (updqueue); + ISC_LIST_UNLINK (updqueue, updrec, r_link); + minires_freeupdrec (updrec); + } + + /* + * DHCID RR exists, and matches client identity. + */ + updrec = minires_mkupdrec (S_PREREQ, + (const char *)ddns_fwd_name -> data, + C_IN, T_DHCID, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = ddns_dhcid -> data; + updrec -> r_size = ddns_dhcid -> len; + updrec -> r_opcode = YXRRSET; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Delete A RRset. + */ + updrec = minires_mkupdrec (S_UPDATE, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)0; + updrec -> r_size = 0; + updrec -> r_opcode = DELETE; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Add A RR. + */ + updrec = minires_mkupdrec (S_UPDATE, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, ttl); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)ddns_address; + updrec -> r_size = strlen (ddns_address); + updrec -> r_opcode = ADD; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Attempt to perform the update. + */ + result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue)); + + if (result != ISC_R_SUCCESS) { + if (result == YXRRSET || result == YXDOMAIN || + result == NXRRSET || result == NXDOMAIN) + log_error ("Forward map from %.*s to %s already in use", + (int)ddns_fwd_name -> len, + (const char *)ddns_fwd_name -> data, + ddns_address); + else + log_error ("Can't update forward map %.*s to %s: %s", + (int)ddns_fwd_name -> len, + (const char *)ddns_fwd_name -> data, + ddns_address, isc_result_totext (result)); + + } else { + log_info ("Added new forward map from %.*s to %s", + (int)ddns_fwd_name -> len, + (const char *)ddns_fwd_name -> data, ddns_address); + } +#if defined (DEBUG_DNS_UPDATES) + print_dns_status ((int)result, &updqueue); +#endif + + /* + * If this query succeeds, the updater can conclude that the current + * client was the last client associated with the domain name, and that + * the name now contains the updated A RR. The A RR update is now + * complete (and a client updater is finished, while a server would + * then proceed to perform a PTR RR update). + * -- "Interaction between DHCP and DNS" + */ + + /* + * If the second query fails with NXRRSET, the updater must conclude + * that the client's desired name is in use by another host. At this + * juncture, the updater can decide (based on some administrative + * configuration outside of the scope of this document) whether to let + * the existing owner of the name keep that name, and to (possibly) + * perform some name disambiguation operation on behalf of the current + * client, or to replace the RRs on the name with RRs that represent + * the current client. If the configured policy allows replacement of + * existing records, the updater submits a query that deletes the + * existing A RR and the existing DHCID RR, adding A and DHCID RRs that + * represent the IP address and client-identity of the new client. + * -- "Interaction between DHCP and DNS" + */ + + error: + while (!ISC_LIST_EMPTY (updqueue)) { + updrec = ISC_LIST_HEAD (updqueue); + ISC_LIST_UNLINK (updqueue, updrec, r_link); + minires_freeupdrec (updrec); + } + + return result; +} + +isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name, + struct iaddr ddns_addr, + struct data_string *ddns_dhcid) +{ + ns_updque updqueue; + ns_updrec *updrec; + isc_result_t result = SERVFAIL; + char ddns_address [16]; + + if (ddns_addr.len != 4) + return ISC_R_INVALIDARG; + + /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe% */ + sprintf (ddns_address, "%u.%u.%u.%u", + ddns_addr.iabuf[0], ddns_addr.iabuf[1], + ddns_addr.iabuf[2], ddns_addr.iabuf[3]); + + /* + * The entity chosen to handle the A record for this client (either the + * client or the server) SHOULD delete the A record that was added when + * the lease was made to the client. + * + * In order to perform this delete, the updater prepares an UPDATE + * query which contains two prerequisites. The first prerequisite + * asserts that the DHCID RR exists whose data is the client identity + * described in Section 4.3. The second prerequisite asserts that the + * data in the A RR contains the IP address of the lease that has + * expired or been released. + * -- "Interaction between DHCP and DNS" + */ + + ISC_LIST_INIT (updqueue); + + /* + * DHCID RR exists, and matches client identity. + */ + updrec = minires_mkupdrec (S_PREREQ, + (const char *)ddns_fwd_name -> data, + C_IN, T_DHCID,0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = ddns_dhcid -> data; + updrec -> r_size = ddns_dhcid -> len; + updrec -> r_opcode = YXRRSET; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * A RR matches the expiring lease. + */ + updrec = minires_mkupdrec (S_PREREQ, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)ddns_address; + updrec -> r_size = strlen (ddns_address); + updrec -> r_opcode = YXRRSET; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + + /* + * Delete appropriate A RR. + */ + updrec = minires_mkupdrec (S_UPDATE, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)ddns_address; + updrec -> r_size = strlen (ddns_address); + updrec -> r_opcode = DELETE; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + /* + * Attempt to perform the update. + */ + result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue)); + print_dns_status ((int)result, &updqueue); + + /* + * If the query fails, the updater MUST NOT delete the DNS name. It + * may be that the host whose lease on the server has expired has moved + * to another network and obtained a lease from a different server, + * which has caused the client's A RR to be replaced. It may also be + * that some other client has been configured with a name that matches + * the name of the DHCP client, and the policy was that the last client + * to specify the name would get the name. In this case, the DHCID RR + * will no longer match the updater's notion of the client-identity of + * the host pointed to by the DNS name. + * -- "Interaction between DHCP and DNS" + */ + + if (result != ISC_R_SUCCESS) { + /* If the rrset isn't there, we didn't need to do the + delete, which is success. */ + if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN) + result = ISC_R_SUCCESS; + goto error; + } + + while (!ISC_LIST_EMPTY (updqueue)) { + updrec = ISC_LIST_HEAD (updqueue); + ISC_LIST_UNLINK (updqueue, updrec, r_link); + minires_freeupdrec (updrec); + } + + /* If the deletion of the A succeeded, and there are no A records + left for this domain, then we can blow away the DHCID record + as well. We can't blow away the DHCID record above because + it's possible that more than one A has been added to this + domain name. */ + ISC_LIST_INIT (updqueue); + + /* + * A RR does not exist. + */ + updrec = minires_mkupdrec (S_PREREQ, + (const char *)ddns_fwd_name -> data, + C_IN, T_A, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = (unsigned char *)0; + updrec -> r_size = 0; + updrec -> r_opcode = NXRRSET; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + /* + * Delete appropriate DHCID RR. + */ + updrec = minires_mkupdrec (S_UPDATE, + (const char *)ddns_fwd_name -> data, + C_IN, T_DHCID, 0); + if (!updrec) { + result = ISC_R_NOMEMORY; + goto error; + } + + updrec -> r_data = ddns_dhcid -> data; + updrec -> r_size = ddns_dhcid -> len; + updrec -> r_opcode = DELETE; + + ISC_LIST_APPEND (updqueue, updrec, r_link); + + /* + * Attempt to perform the update. + */ + result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue)); + print_dns_status ((int)result, &updqueue); + + /* Fall through. */ + error: + + while (!ISC_LIST_EMPTY (updqueue)) { + updrec = ISC_LIST_HEAD (updqueue); + ISC_LIST_UNLINK (updqueue, updrec, r_link); + minires_freeupdrec (updrec); + } + + return result; +} + + +#endif /* NSUPDATE */ + +HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t, + dns_zone_reference, dns_zone_dereference) diff --git a/contrib/dhcp-3.0/common/ethernet.c b/contrib/dhcp-3.0/common/ethernet.c new file mode 100644 index 0000000000..dd7af2b987 --- /dev/null +++ b/contrib/dhcp-3.0/common/ethernet.c @@ -0,0 +1,98 @@ +/* ethernet.c + + Packet assembly code, originally contributed by Archie Cobbs. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: ethernet.c,v 1.6.2.3 2004/06/10 17:59:17 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) +#include "includes/netinet/if_ether.h" +#endif /* PACKET_ASSEMBLY || PACKET_DECODING */ + +#if defined (PACKET_ASSEMBLY) +/* Assemble an hardware header... */ + +void assemble_ethernet_header (interface, buf, bufix, to) + struct interface_info *interface; + unsigned char *buf; + unsigned *bufix; + struct hardware *to; +{ + struct isc_ether_header eh; + + if (to && to -> hlen == 7) /* XXX */ + memcpy (eh.ether_dhost, &to -> hbuf [1], + sizeof eh.ether_dhost); + else + memset (eh.ether_dhost, 0xff, sizeof (eh.ether_dhost)); + if (interface -> hw_address.hlen - 1 == sizeof (eh.ether_shost)) + memcpy (eh.ether_shost, &interface -> hw_address.hbuf [1], + sizeof (eh.ether_shost)); + else + memset (eh.ether_shost, 0x00, sizeof (eh.ether_shost)); + + eh.ether_type = htons (ETHERTYPE_IP); + + memcpy (&buf [*bufix], &eh, ETHER_HEADER_SIZE); + *bufix += ETHER_HEADER_SIZE; +} +#endif /* PACKET_ASSEMBLY */ + +#ifdef PACKET_DECODING +/* Decode a hardware header... */ + +ssize_t decode_ethernet_header (interface, buf, bufix, from) + struct interface_info *interface; + unsigned char *buf; + unsigned bufix; + struct hardware *from; +{ + struct isc_ether_header eh; + + memcpy (&eh, buf + bufix, ETHER_HEADER_SIZE); + +#ifdef USERLAND_FILTER + if (ntohs (eh.ether_type) != ETHERTYPE_IP) + return -1; +#endif + memcpy (&from -> hbuf [1], eh.ether_shost, sizeof (eh.ether_shost)); + from -> hbuf [0] = ARPHRD_ETHER; + from -> hlen = (sizeof eh.ether_shost) + 1; + + return ETHER_HEADER_SIZE; +} +#endif /* PACKET_DECODING */ diff --git a/contrib/dhcp-3.0/common/execute.c b/contrib/dhcp-3.0/common/execute.c new file mode 100644 index 0000000000..39653309e9 --- /dev/null +++ b/contrib/dhcp-3.0/common/execute.c @@ -0,0 +1,1054 @@ +/* execute.c + + Support for executable statements. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: execute.c,v 1.44.2.12 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include + +int execute_statements (result, packet, lease, client_state, + in_options, out_options, scope, statements) + struct binding_value **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *out_options; + struct binding_scope **scope; + struct executable_statement *statements; +{ + struct executable_statement *r, *e, *next; + int rc; + int status; + unsigned long num; + struct binding_scope *outer; + struct binding *binding; + struct data_string ds; + struct binding_scope *ns; + + if (!statements) + return 1; + + r = (struct executable_statement *)0; + next = (struct executable_statement *)0; + e = (struct executable_statement *)0; + executable_statement_reference (&r, statements, MDL); + while (r && !(result && *result)) { + if (r -> next) + executable_statement_reference (&next, r -> next, MDL); + switch (r -> op) { + case statements_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: statements"); +#endif + status = execute_statements (result, packet, lease, + client_state, in_options, + out_options, scope, + r -> data.statements); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: statements returns %d", status); +#endif + if (!status) + return 0; + break; + + case on_statement: + if (lease) { + if (r -> data.on.evtypes & ON_EXPIRY) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: on expiry"); +#endif + if (lease -> on_expiry) + executable_statement_dereference + (&lease -> on_expiry, MDL); + if (r -> data.on.statements) + executable_statement_reference + (&lease -> on_expiry, + r -> data.on.statements, MDL); + } + if (r -> data.on.evtypes & ON_RELEASE) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: on release"); +#endif + if (lease -> on_release) + executable_statement_dereference + (&lease -> on_release, MDL); + if (r -> data.on.statements) + executable_statement_reference + (&lease -> on_release, + r -> data.on.statements, MDL); + } + if (r -> data.on.evtypes & ON_COMMIT) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: on commit"); +#endif + if (lease -> on_commit) + executable_statement_dereference + (&lease -> on_commit, MDL); + if (r -> data.on.statements) + executable_statement_reference + (&lease -> on_commit, + r -> data.on.statements, MDL); + } + } + break; + + case switch_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: switch"); +#endif + status = (find_matching_case + (&e, packet, lease, client_state, + in_options, out_options, scope, + r -> data.s_switch.expr, + r -> data.s_switch.statements)); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: switch: case %lx", (unsigned long)e); +#endif + if (status) { + if (!(execute_statements + (result, packet, lease, client_state, + in_options, out_options, scope, e))) { + executable_statement_dereference + (&e, MDL); + return 0; + } + executable_statement_dereference (&e, MDL); + } + break; + + /* These have no effect when executed. */ + case case_statement: + case default_statement: + break; + + case if_statement: + status = (evaluate_boolean_expression + (&rc, packet, + lease, client_state, in_options, + out_options, scope, r -> data.ie.expr)); + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: if %s", (status + ? (rc ? "true" : "false") + : "NULL")); +#endif + /* XXX Treat NULL as false */ + if (!status) + rc = 0; + if (!execute_statements + (result, packet, lease, client_state, + in_options, out_options, scope, + rc ? r -> data.ie.tc : r -> data.ie.fc)) + return 0; + break; + + case eval_statement: + status = evaluate_expression + ((struct binding_value **)0, + packet, lease, client_state, in_options, + out_options, scope, r -> data.eval, MDL); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: evaluate: %s", + (status ? "succeeded" : "failed")); +#endif + break; + + case return_statement: + status = evaluate_expression + (result, packet, + lease, client_state, in_options, + out_options, scope, r -> data.retval, MDL); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: return: %s", + (status ? "succeeded" : "failed")); +#endif + break; + + case add_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: add %s", (r -> data.add -> name + ? r -> data.add -> name + : "")); +#endif + classify (packet, r -> data.add); + break; + + case break_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: break"); +#endif + return 1; + + case supersede_option_statement: + case send_option_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: %s option %s.%s", + (r -> op == supersede_option_statement + ? "supersede" : "send"), + r -> data.option -> option -> universe -> name, + r -> data.option -> option -> name); + goto option_statement; +#endif + case default_option_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: default option %s.%s", + r -> data.option -> option -> universe -> name, + r -> data.option -> option -> name); + goto option_statement; +#endif + case append_option_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: append option %s.%s", + r -> data.option -> option -> universe -> name, + r -> data.option -> option -> name); + goto option_statement; +#endif + case prepend_option_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: prepend option %s.%s", + r -> data.option -> option -> universe -> name, + r -> data.option -> option -> name); + option_statement: +#endif + set_option (r -> data.option -> option -> universe, + out_options, r -> data.option, r -> op); + break; + + case set_statement: + case define_statement: + if (!scope) { + log_error ("set %s: no scope", + r -> data.set.name); + status = 0; + break; + } + if (!*scope) { + if (!binding_scope_allocate (scope, MDL)) { + log_error ("set %s: can't allocate scope", + r -> data.set.name); + status = 0; + break; + } + } + binding = find_binding (*scope, r -> data.set.name); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: set %s", r -> data.set.name); +#endif + if (!binding) { + binding = dmalloc (sizeof *binding, MDL); + if (binding) { + memset (binding, 0, sizeof *binding); + binding -> name = + dmalloc (strlen + (r -> data.set.name) + 1, + MDL); + if (binding -> name) { + strcpy (binding -> name, + r -> data.set.name); + binding -> next = (*scope) -> bindings; + (*scope) -> bindings = binding; + } else { + badalloc: + dfree (binding, MDL); + binding = (struct binding *)0; + } + } + } + if (binding) { + if (binding -> value) + binding_value_dereference + (&binding -> value, MDL); + if (r -> op == set_statement) { + status = (evaluate_expression + (&binding -> value, packet, + lease, client_state, + in_options, out_options, + scope, r -> data.set.expr, + MDL)); + } else { + if (!(binding_value_allocate + (&binding -> value, MDL))) { + dfree (binding, MDL); + binding = (struct binding *)0; + } + if (binding -> value) { + binding -> value -> type = + binding_function; + (fundef_reference + (&binding -> value -> value.fundef, + r -> data.set.expr -> data.func, + MDL)); + } + } + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: set %s%s", r -> data.set.name, + (binding && status ? "" : " (failed)")); +#endif + break; + + case unset_statement: + if (!scope || !*scope) { + status = 0; + break; + } + binding = find_binding (*scope, r -> data.unset); + if (binding) { + if (binding -> value) + binding_value_dereference + (&binding -> value, MDL); + status = 1; + } else + status = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: unset %s: %s", r -> data.unset, + (status ? "found" : "not found")); +#endif + break; + + case let_statement: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: let %s", r -> data.let.name); +#endif + ns = (struct binding_scope *)0; + binding_scope_allocate (&ns, MDL); + e = r; + + next_let: + if (ns) { + binding = dmalloc (sizeof *binding, MDL); + memset (binding, 0, sizeof *binding); + if (!binding) { + blb: + binding_scope_dereference (&ns, MDL); + } else { + binding -> name = + dmalloc (strlen + (e -> data.let.name + 1), + MDL); + if (binding -> name) + strcpy (binding -> name, + e -> data.let.name); + else { + dfree (binding, MDL); + binding = (struct binding *)0; + goto blb; + } + } + } else + binding = NULL; + + if (ns && binding) { + status = (evaluate_expression + (&binding -> value, packet, lease, + client_state, + in_options, out_options, + scope, e -> data.set.expr, MDL)); + binding -> next = ns -> bindings; + ns -> bindings = binding; + } + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: let %s%s", e -> data.let.name, + (binding && status ? "" : "failed")); +#endif + if (!e -> data.let.statements) { + } else if (e -> data.let.statements -> op == + let_statement) { + e = e -> data.let.statements; + goto next_let; + } else if (ns) { + if (scope && *scope) + binding_scope_reference (&ns -> outer, + *scope, MDL); + execute_statements + (result, packet, lease, + client_state, + in_options, out_options, + &ns, e -> data.let.statements); + } + if (ns) + binding_scope_dereference (&ns, MDL); + break; + + case log_statement: + memset (&ds, 0, sizeof ds); + status = (evaluate_data_expression + (&ds, packet, + lease, client_state, in_options, + out_options, scope, r -> data.log.expr, + MDL)); + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("exec: log"); +#endif + + if (status) { + switch (r -> data.log.priority) { + case log_priority_fatal: + log_fatal ("%.*s", (int)ds.len, + ds.data); + break; + case log_priority_error: + log_error ("%.*s", (int)ds.len, + ds.data); + break; + case log_priority_debug: + log_debug ("%.*s", (int)ds.len, + ds.data); + break; + case log_priority_info: + log_info ("%.*s", (int)ds.len, + ds.data); + break; + } + data_string_forget (&ds, MDL); + } + + break; + + default: + log_error ("bogus statement type %d", r -> op); + break; + } + executable_statement_dereference (&r, MDL); + if (next) { + executable_statement_reference (&r, next, MDL); + executable_statement_dereference (&next, MDL); + } + } + + return 1; +} + +/* Execute all the statements in a particular scope, and all statements in + scopes outer from that scope, but if a particular limiting scope is + reached, do not execute statements in that scope or in scopes outer + from it. More specific scopes need to take precedence over less + specific scopes, so we recursively traverse the scope list, executing + the most outer scope first. */ + +void execute_statements_in_scope (result, packet, + lease, client_state, in_options, out_options, + scope, group, limiting_group) + struct binding_value **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *out_options; + struct binding_scope **scope; + struct group *group; + struct group *limiting_group; +{ + struct group *limit; + + /* If we've recursed as far as we can, return. */ + if (!group) + return; + + /* As soon as we get to a scope that is outer than the limiting + scope, we are done. This is so that if somebody does something + like this, it does the expected thing: + + domain-name "fugue.com"; + shared-network FOO { + host bar { + domain-name "othello.fugue.com"; + fixed-address 10.20.30.40; + } + subnet 10.20.30.0 netmask 255.255.255.0 { + domain-name "manhattan.fugue.com"; + } + } + + The problem with the above arrangement is that the host's + group nesting will be host -> shared-network -> top-level, + and the limiting scope when we evaluate the host's scope + will be the subnet -> shared-network -> top-level, so we need + to know when we evaluate the host's scope to stop before we + evaluate the shared-networks scope, because it's outer than + the limiting scope, which means we've already evaluated it. */ + + for (limit = limiting_group; limit; limit = limit -> next) { + if (group == limit) + return; + } + + if (group -> next) + execute_statements_in_scope (result, packet, + lease, client_state, + in_options, out_options, scope, + group -> next, limiting_group); + execute_statements (result, packet, lease, client_state, in_options, + out_options, scope, group -> statements); +} + +/* Dereference or free any subexpressions of a statement being freed. */ + +int executable_statement_dereference (ptr, file, line) + struct executable_statement **ptr; + const char *file; + int line; +{ + struct executable_statement *bp; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + (*ptr) -> refcnt--; + rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); + if ((*ptr) -> refcnt > 0) { + *ptr = (struct executable_statement *)0; + return 1; + } + + if ((*ptr) -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*ptr); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if ((*ptr) -> next) + executable_statement_dereference (&(*ptr) -> next, file, line); + + switch ((*ptr) -> op) { + case statements_statement: + if ((*ptr) -> data.statements) + executable_statement_dereference + (&(*ptr) -> data.statements, file, line); + break; + + case on_statement: + if ((*ptr) -> data.on.statements) + executable_statement_dereference + (&(*ptr) -> data.on.statements, file, line); + break; + + case switch_statement: + if ((*ptr) -> data.s_switch.statements) + executable_statement_dereference + (&(*ptr) -> data.on.statements, file, line); + if ((*ptr) -> data.s_switch.expr) + expression_dereference (&(*ptr) -> data.s_switch.expr, + file, line); + break; + + case case_statement: + if ((*ptr) -> data.s_switch.expr) + expression_dereference (&(*ptr) -> data.c_case, + file, line); + break; + + case if_statement: + if ((*ptr) -> data.ie.expr) + expression_dereference (&(*ptr) -> data.ie.expr, + file, line); + if ((*ptr) -> data.ie.tc) + executable_statement_dereference + (&(*ptr) -> data.ie.tc, file, line); + if ((*ptr) -> data.ie.fc) + executable_statement_dereference + (&(*ptr) -> data.ie.fc, file, line); + break; + + case eval_statement: + if ((*ptr) -> data.eval) + expression_dereference (&(*ptr) -> data.eval, + file, line); + break; + + case return_statement: + if ((*ptr) -> data.eval) + expression_dereference (&(*ptr) -> data.eval, + file, line); + break; + + case set_statement: + if ((*ptr)->data.set.name) + dfree ((*ptr)->data.set.name, file, line); + if ((*ptr)->data.set.expr) + expression_dereference (&(*ptr) -> data.set.expr, + file, line); + break; + + case unset_statement: + if ((*ptr)->data.unset) + dfree ((*ptr)->data.unset, file, line); + break; + + case supersede_option_statement: + case send_option_statement: + case default_option_statement: + case append_option_statement: + case prepend_option_statement: + if ((*ptr) -> data.option) + option_cache_dereference (&(*ptr) -> data.option, + file, line); + break; + + default: + /* Nothing to do. */ + break; + } + + dfree ((*ptr), file, line); + *ptr = (struct executable_statement *)0; + return 1; +} + +void write_statements (file, statements, indent) + FILE *file; + struct executable_statement *statements; + int indent; +{ + struct executable_statement *r, *x; + int result; + int status; + const char *s, *t, *dot; + int col; + + if (!statements) + return; + + for (r = statements; r; r = r -> next) { + switch (r -> op) { + case statements_statement: + write_statements (file, r -> data.statements, indent); + break; + + case on_statement: + indent_spaces (file, indent); + fprintf (file, "on "); + s = ""; + if (r -> data.on.evtypes & ON_EXPIRY) { + fprintf (file, "%sexpiry", s); + s = " or "; + } + if (r -> data.on.evtypes & ON_COMMIT) { + fprintf (file, "%scommit", s); + s = "or"; + } + if (r -> data.on.evtypes & ON_RELEASE) { + fprintf (file, "%srelease", s); + s = "or"; + } + if (r -> data.on.statements) { + fprintf (file, " {"); + write_statements (file, + r -> data.on.statements, + indent + 2); + indent_spaces (file, indent); + fprintf (file, "}"); + } else { + fprintf (file, ";"); + } + break; + + case switch_statement: + indent_spaces (file, indent); + fprintf (file, "switch ("); + col = write_expression (file, + r -> data.s_switch.expr, + indent + 7, indent + 7, 1); + col = token_print_indent (file, col, indent + 7, + "", "", ")"); + token_print_indent (file, + col, indent, " ", "", "{"); + write_statements (file, r -> data.s_switch.statements, + indent + 2); + indent_spaces (file, indent); + fprintf (file, "}"); + break; + + case case_statement: + indent_spaces (file, indent - 1); + fprintf (file, "case "); + col = write_expression (file, + r -> data.s_switch.expr, + indent + 5, indent + 5, 1); + token_print_indent (file, col, indent + 5, + "", "", ":"); + break; + + case default_statement: + indent_spaces (file, indent - 1); + fprintf (file, "default: "); + break; + + case if_statement: + indent_spaces (file, indent); + fprintf (file, "if "); + x = r; + col = write_expression (file, + x -> data.ie.expr, + indent + 3, indent + 3, 1); + else_if: + token_print_indent (file, col, indent, " ", "", "{"); + write_statements (file, x -> data.ie.tc, indent + 2); + if (x -> data.ie.fc && + x -> data.ie.fc -> op == if_statement && + !x -> data.ie.fc -> next) { + indent_spaces (file, indent); + fprintf (file, "} elsif "); + x = x -> data.ie.fc; + col = write_expression (file, + x -> data.ie.expr, + indent + 6, + indent + 6, 1); + goto else_if; + } + if (x -> data.ie.fc) { + indent_spaces (file, indent); + fprintf (file, "} else {"); + write_statements (file, x -> data.ie.fc, + indent + 2); + } + indent_spaces (file, indent); + fprintf (file, "}"); + break; + + case eval_statement: + indent_spaces (file, indent); + fprintf (file, "eval "); + col = write_expression (file, r -> data.eval, + indent + 5, indent + 5, 1); + fprintf (file, ";"); + break; + + case return_statement: + indent_spaces (file, indent); + fprintf (file, "return;"); + break; + + case add_statement: + indent_spaces (file, indent); + fprintf (file, "add \"%s\"", r -> data.add -> name); + break; + + case break_statement: + indent_spaces (file, indent); + fprintf (file, "break;"); + break; + + case supersede_option_statement: + case send_option_statement: + s = "supersede"; + goto option_statement; + + case default_option_statement: + s = "default"; + goto option_statement; + + case append_option_statement: + s = "append"; + goto option_statement; + + case prepend_option_statement: + s = "prepend"; + option_statement: + /* Note: the reason we don't try to pretty print + the option here is that the format of the option + may change in dhcpd.conf, and then when this + statement was read back, it would cause a syntax + error. */ + if (r -> data.option -> option -> universe == + &dhcp_universe) { + t = ""; + dot = ""; + } else { + t = (r -> data.option -> option -> + universe -> name); + dot = "."; + } + indent_spaces (file, indent); + fprintf (file, "%s %s%s%s = ", s, t, dot, + r -> data.option -> option -> name); + col = (indent + strlen (s) + strlen (t) + + strlen (dot) + strlen (r -> data.option -> + option -> name) + 4); + if (r -> data.option -> expression) + write_expression + (file, + r -> data.option -> expression, + col, indent + 8, 1); + else + token_indent_data_string + (file, col, indent + 8, "", "", + &r -> data.option -> data); + + fprintf (file, ";"); /* XXX */ + break; + + case set_statement: + indent_spaces (file, indent); + fprintf (file, "set "); + col = token_print_indent (file, indent + 4, indent + 4, + "", "", r -> data.set.name); + col = token_print_indent (file, col, indent + 4, + " ", " ", "="); + col = write_expression (file, r -> data.set.expr, + indent + 3, indent + 3, 0); + col = token_print_indent (file, col, indent + 4, + " ", "", ";"); + break; + + case unset_statement: + indent_spaces (file, indent); + fprintf (file, "unset "); + col = token_print_indent (file, indent + 6, indent + 6, + "", "", r -> data.set.name); + col = token_print_indent (file, col, indent + 6, + " ", "", ";"); + break; + + case log_statement: + indent_spaces (file, indent); + fprintf (file, "log "); + col = token_print_indent (file, indent + 4, indent + 4, + "", "", "("); + switch (r -> data.log.priority) { + case log_priority_fatal: + col = token_print_indent + (file, col, indent + 4, "", + " ", "fatal,"); + break; + case log_priority_error: + col = token_print_indent + (file, col, indent + 4, "", + " ", "error,"); + break; + case log_priority_debug: + col = token_print_indent + (file, col, indent + 4, "", + " ", "debug,"); + break; + case log_priority_info: + col = token_print_indent + (file, col, indent + 4, "", + " ", "info,"); + break; + } + col = write_expression (file, r -> data.log.expr, + indent + 4, indent + 4, 0); + col = token_print_indent (file, col, indent + 4, + "", "", ");"); + + break; + + default: + log_fatal ("bogus statement type %d\n", r -> op); + } + } +} + +/* Find a case statement in the sequence of executable statements that + matches the expression, and if found, return the following statement. + If no case statement matches, try to find a default statement and + return that (the default statement can precede all the case statements). + Otherwise, return the null statement. */ + +int find_matching_case (struct executable_statement **ep, + struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *out_options, + struct binding_scope **scope, + struct expression *expr, + struct executable_statement *stmt) +{ + int status, sub; + struct executable_statement *s; + unsigned long foo; + + if (is_data_expression (expr)) { + struct executable_statement *e; + struct data_string cd, ds; + memset (&ds, 0, sizeof ds); + memset (&cd, 0, sizeof cd); + + status = (evaluate_data_expression (&ds, packet, lease, + client_state, in_options, + out_options, scope, expr, + MDL)); + if (status) { + for (s = stmt; s; s = s -> next) { + if (s -> op == case_statement) { + sub = (evaluate_data_expression + (&cd, packet, lease, client_state, + in_options, out_options, + scope, s -> data.c_case, MDL)); + if (sub && cd.len == ds.len && + !memcmp (cd.data, ds.data, cd.len)) + { + data_string_forget (&cd, MDL); + data_string_forget (&ds, MDL); + executable_statement_reference + (ep, s -> next, MDL); + return 1; + } + data_string_forget (&cd, MDL); + } + } + data_string_forget (&ds, MDL); + } + } else { + unsigned long n, c; + status = evaluate_numeric_expression (&n, packet, lease, + client_state, + in_options, out_options, + scope, expr); + + if (status) { + for (s = stmt; s; s = s -> next) { + if (s -> op == case_statement) { + sub = (evaluate_numeric_expression + (&c, packet, lease, client_state, + in_options, out_options, + scope, s -> data.c_case)); + if (sub && n == c) { + executable_statement_reference + (ep, s -> next, MDL); + return 1; + } + } + } + } + } + + /* If we didn't find a matching case statement, look for a default + statement and return the statement following it. */ + for (s = stmt; s; s = s -> next) + if (s -> op == default_statement) + break; + if (s) { + executable_statement_reference (ep, s -> next, MDL); + return 1; + } + return 0; +} + +int executable_statement_foreach (struct executable_statement *stmt, + int (*callback) (struct + executable_statement *, + void *, int), + void *vp, int condp) +{ + struct executable_statement *foo; + int ok = 0; + int result; + + for (foo = stmt; foo; foo = foo -> next) { + if ((*callback) (foo, vp, condp) != 0) + ok = 1; + switch (foo -> op) { + case null_statement: + break; + case if_statement: + if (executable_statement_foreach (foo -> data.ie.tc, + callback, vp, 1)) + ok = 1; + if (executable_statement_foreach (foo -> data.ie.fc, + callback, vp, 1)) + ok = 1; + break; + case add_statement: + break; + case eval_statement: + break; + case break_statement: + break; + case default_option_statement: + break; + case supersede_option_statement: + break; + case append_option_statement: + break; + case prepend_option_statement: + break; + case send_option_statement: + break; + case statements_statement: + if ((executable_statement_foreach + (foo -> data.statements, callback, vp, condp))) + ok = 1; + break; + case on_statement: + if ((executable_statement_foreach + (foo -> data.on.statements, callback, vp, 1))) + ok = 1; + break; + case switch_statement: + if ((executable_statement_foreach + (foo -> data.s_switch.statements, callback, vp, 1))) + ok = 1; + break; + case case_statement: + break; + case default_statement: + break; + case set_statement: + break; + case unset_statement: + break; + case let_statement: + if ((executable_statement_foreach + (foo -> data.let.statements, callback, vp, 0))) + ok = 1; + break; + case define_statement: + break; + case log_statement: + case return_statement: + break; + } + } + return ok; +} diff --git a/contrib/dhcp-3.0/common/fddi.c b/contrib/dhcp-3.0/common/fddi.c new file mode 100644 index 0000000000..9fdbb94d38 --- /dev/null +++ b/contrib/dhcp-3.0/common/fddi.c @@ -0,0 +1,97 @@ +/* fddi.c + + Packet assembly code, originally contributed by Archie Cobbs. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: fddi.c,v 1.3.2.2 2004/06/10 17:59:18 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (DEC_FDDI) +#include +#include + +#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) +#include "includes/netinet/if_ether.h" +#endif /* PACKET_ASSEMBLY || PACKET_DECODING */ + +#if defined (PACKET_ASSEMBLY) +/* Assemble an hardware header... */ + +void assemble_fddi_header (interface, buf, bufix, to) + struct interface_info *interface; + unsigned char *buf; + unsigned *bufix; + struct hardware *to; +{ + struct fddi_header fh; + struct llc lh; + + if (to && to -> hlen == 7) + memcpy (fh.fddi_dhost, &to -> hbuf [1], + sizeof (fh.fddi_dhost)); + memcpy (fh.fddi_shost, + &interface -> hw_address.hbuf [1], sizeof (fh.fddi_shost)); + fh.fddi_fc = FDDIFC_LLC_ASYNC; + memcpy (&buf [*bufix], &fh, sizeof fh); + *bufix += sizeof fh; + + lh.llc_dsap = LLC_SNAP_LSAP; + lh.llc_ssap = LLC_SNAP_LSAP; + lh.llc_un.type_snap.control = LLC_UI; + lh.llc_un.type_snap.ether_type = htons (ETHERTYPE_IP); + memcpy (&buf [*bufix], &lh, LLC_SNAP_LEN); + *bufix += LLC_SNAP_LEN; +} +#endif /* PACKET_ASSEMBLY */ + +#ifdef PACKET_DECODING +/* Decode a hardware header... */ + +ssize_t decode_fddi_header (interface, buf, bufix, from) + struct interface_info *interface; + unsigned char *buf; + unsigned bufix; + struct hardware *from; +{ + struct fddi_header fh; + struct llc lh; + + from -> hbuf [0] = HTYPE_FDDI; + memcpy (&from -> hbuf [1], fh.fddi_shost, sizeof fh.fddi_shost); + return FDDI_HEADER_SIZE + LLC_SNAP_LEN; +} +#endif /* PACKET_DECODING */ +#endif /* DEC_FDDI */ diff --git a/contrib/dhcp-3.0/common/icmp.c b/contrib/dhcp-3.0/common/icmp.c new file mode 100644 index 0000000000..8ef22838a8 --- /dev/null +++ b/contrib/dhcp-3.0/common/icmp.c @@ -0,0 +1,317 @@ +/* dhcp.c + + ICMP Protocol engine - for sending out pings and receiving + responses. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: icmp.c,v 1.30.2.6 2004/06/10 17:59:18 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include "netinet/ip.h" +#include "netinet/ip_icmp.h" + +struct icmp_state *icmp_state; +static omapi_object_type_t *dhcp_type_icmp; +static int no_icmp; + +OMAPI_OBJECT_ALLOC (icmp_state, struct icmp_state, dhcp_type_icmp) + +#if defined (TRACING) +trace_type_t *trace_icmp_input; +trace_type_t *trace_icmp_output; +#endif + +/* Initialize the ICMP protocol. */ + +void icmp_startup (routep, handler) + int routep; + void (*handler) PROTO ((struct iaddr, u_int8_t *, int)); +{ + struct protoent *proto; + int protocol = 1; + struct sockaddr_in from; + int fd; + int state; + struct icmp_state *new; + omapi_object_t *h; + isc_result_t result; + + /* Only initialize icmp once. */ + if (dhcp_type_icmp) + log_fatal ("attempted to reinitialize icmp protocol"); + + result = omapi_object_type_register (&dhcp_type_icmp, "icmp", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + sizeof (struct icmp_state), + 0, RC_MISC); + + if (result != ISC_R_SUCCESS) + log_fatal ("Can't register icmp object type: %s", + isc_result_totext (result)); + + icmp_state_allocate (&icmp_state, MDL); + icmp_state -> icmp_handler = handler; + +#if defined (TRACING) + trace_icmp_input = trace_type_register ("icmp-input", (void *)0, + trace_icmp_input_input, + trace_icmp_input_stop, MDL); + trace_icmp_output = trace_type_register ("icmp-output", (void *)0, + trace_icmp_output_input, + trace_icmp_output_stop, MDL); + + /* If we're playing back a trace file, don't create the socket + or set up the callback. */ + if (!trace_playback ()) { +#endif + /* Get the protocol number (should be 1). */ + proto = getprotobyname ("icmp"); + if (proto) + protocol = proto -> p_proto; + + /* Get a raw socket for the ICMP protocol. */ + icmp_state -> socket = socket (AF_INET, SOCK_RAW, protocol); + if (icmp_state -> socket < 0) { + no_icmp = 1; + log_error ("unable to create icmp socket: %m"); + return; + } + +#if defined (HAVE_SETFD) + if (fcntl (icmp_state -> socket, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on icmp: %m"); +#endif + + /* Make sure it does routing... */ + state = 0; + if (setsockopt (icmp_state -> socket, SOL_SOCKET, SO_DONTROUTE, + (char *)&state, sizeof state) < 0) + log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m"); + + result = (omapi_register_io_object + ((omapi_object_t *)icmp_state, + icmp_readsocket, 0, icmp_echoreply, 0, 0)); + if (result != ISC_R_SUCCESS) + log_fatal ("Can't register icmp handle: %s", + isc_result_totext (result)); +#if defined (TRACING) + } +#endif +} + +int icmp_readsocket (h) + omapi_object_t *h; +{ + struct icmp_state *state; + + state = (struct icmp_state *)h; + return state -> socket; +} + +int icmp_echorequest (addr) + struct iaddr *addr; +{ + struct sockaddr_in to; + struct icmp icmp; + int status; +#if defined (TRACING) + trace_iov_t iov [2]; +#endif + + if (no_icmp) + return 1; + if (!icmp_state) + log_fatal ("ICMP protocol used before initialization."); + + memset (&to, 0, sizeof(to)); +#ifdef HAVE_SA_LEN + to.sin_len = sizeof to; +#endif + to.sin_family = AF_INET; + to.sin_port = 0; /* unused. */ + memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */ + + icmp.icmp_type = ICMP_ECHO; + icmp.icmp_code = 0; + icmp.icmp_cksum = 0; + icmp.icmp_seq = 0; +#ifdef PTRSIZE_64BIT + icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^ + (u_int32_t)(((u_int64_t)addr) >> 32)); +#else + icmp.icmp_id = (u_int32_t)addr; +#endif + memset (&icmp.icmp_dun, 0, sizeof icmp.icmp_dun); + + icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp, + sizeof icmp, 0)); + +#if defined (TRACING) + if (trace_playback ()) { + char *buf = (char *)0; + unsigned buflen = 0; + + /* Consume the ICMP event. */ + status = trace_get_packet (&trace_icmp_output, &buflen, &buf); + if (status != ISC_R_SUCCESS) + log_error ("icmp_echorequest: %s", + isc_result_totext (status)); + if (buf) + dfree (buf, MDL); + } else { + if (trace_record ()) { + iov [0].buf = (char *)addr; + iov [0].len = sizeof *addr; + iov [1].buf = (char *)&icmp; + iov [1].len = sizeof icmp; + trace_write_packet_iov (trace_icmp_output, + 2, iov, MDL); + } +#endif + /* Send the ICMP packet... */ + status = sendto (icmp_state -> socket, + (char *)&icmp, sizeof icmp, 0, + (struct sockaddr *)&to, sizeof to); + if (status < 0) + log_error ("icmp_echorequest %s: %m", + inet_ntoa(to.sin_addr)); + + if (status != sizeof icmp) + return 0; +#if defined (TRACING) + } +#endif + return 1; +} + +isc_result_t icmp_echoreply (h) + omapi_object_t *h; +{ + struct icmp *icfrom; + struct ip *ip; + struct sockaddr_in from; + u_int8_t icbuf [1500]; + int status; + SOCKLEN_T sl; + int hlen, len; + struct iaddr ia; + struct icmp_state *state; +#if defined (TRACING) + trace_iov_t iov [2]; +#endif + + state = (struct icmp_state *)h; + + sl = sizeof from; + status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0, + (struct sockaddr *)&from, &sl); + if (status < 0) { + log_error ("icmp_echoreply: %m"); + return ISC_R_UNEXPECTED; + } + + /* Find the IP header length... */ + ip = (struct ip *)icbuf; + hlen = IP_HL (ip); + + /* Short packet? */ + if (status < hlen + (sizeof *icfrom)) { + return ISC_R_SUCCESS; + } + + len = status - hlen; + icfrom = (struct icmp *)(icbuf + hlen); + + /* Silently discard ICMP packets that aren't echoreplies. */ + if (icfrom -> icmp_type != ICMP_ECHOREPLY) { + return ISC_R_SUCCESS; + } + + /* If we were given a second-stage handler, call it. */ + if (state -> icmp_handler) { + memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr); + ia.len = sizeof from.sin_addr; + +#if defined (TRACING) + if (trace_record ()) { + ia.len = htonl(ia.len); + iov [0].buf = (char *)&ia; + iov [0].len = sizeof ia; + iov [1].buf = (char *)icbuf; + iov [1].len = len; + trace_write_packet_iov (trace_icmp_input, 2, iov, MDL); + ia.len = ntohl(ia.len); + } +#endif + (*state -> icmp_handler) (ia, icbuf, len); + } + return ISC_R_SUCCESS; +} + +#if defined (TRACING) +void trace_icmp_input_input (trace_type_t *ttype, unsigned length, char *buf) +{ + struct iaddr *ia; + unsigned len; + u_int8_t *icbuf; + ia = (struct iaddr *)buf; + ia->len = ntohl(ia->len); + icbuf = (u_int8_t *)(ia + 1); + if (icmp_state -> icmp_handler) + (*icmp_state -> icmp_handler) (*ia, icbuf, + (int)(length - sizeof ia)); +} + +void trace_icmp_input_stop (trace_type_t *ttype) { } + +void trace_icmp_output_input (trace_type_t *ttype, unsigned length, char *buf) +{ + struct icmp *icmp; + struct iaddr ia; + + if (length != (sizeof (*icmp) + (sizeof ia))) { + log_error ("trace_icmp_output_input: data size mismatch %d:%d", + length, (int)((sizeof (*icmp)) + (sizeof ia))); + return; + } + ia.len = 4; + memcpy (ia.iabuf, buf, 4); + icmp = (struct icmp *)(buf + 1); + + log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia)); +} + +void trace_icmp_output_stop (trace_type_t *ttype) { } +#endif /* TRACING */ diff --git a/contrib/dhcp-3.0/common/inet.c b/contrib/dhcp-3.0/common/inet.c new file mode 100644 index 0000000000..53f5869ff4 --- /dev/null +++ b/contrib/dhcp-3.0/common/inet.c @@ -0,0 +1,233 @@ +/* inet.c + + Subroutines to manipulate internet addresses in a safely portable + way... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: inet.c,v 1.8.2.5 2004/06/10 17:59:18 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +/* Return just the network number of an internet address... */ + +struct iaddr subnet_number (addr, mask) + struct iaddr addr; + struct iaddr mask; +{ + int i; + struct iaddr rv; + + rv.len = 0; + + /* Both addresses must have the same length... */ + if (addr.len != mask.len) + return rv; + + rv.len = addr.len; + for (i = 0; i < rv.len; i++) + rv.iabuf [i] = addr.iabuf [i] & mask.iabuf [i]; + return rv; +} + +/* Combine a network number and a integer to produce an internet address. + This won't work for subnets with more than 32 bits of host address, but + maybe this isn't a problem. */ + +struct iaddr ip_addr (subnet, mask, host_address) + struct iaddr subnet; + struct iaddr mask; + u_int32_t host_address; +{ + int i, j, k; + u_int32_t swaddr; + struct iaddr rv; + unsigned char habuf [sizeof swaddr]; + + swaddr = htonl (host_address); + memcpy (habuf, &swaddr, sizeof swaddr); + + /* Combine the subnet address and the host address. If + the host address is bigger than can fit in the subnet, + return a zero-length iaddr structure. */ + rv = subnet; + j = rv.len - sizeof habuf; + for (i = sizeof habuf - 1; i >= 0; i--) { + if (mask.iabuf [i + j]) { + if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) { + rv.len = 0; + return rv; + } + for (k = i - 1; k >= 0; k--) { + if (habuf [k]) { + rv.len = 0; + return rv; + } + } + rv.iabuf [i + j] |= habuf [i]; + break; + } else + rv.iabuf [i + j] = habuf [i]; + } + + return rv; +} + +/* Given a subnet number and netmask, return the address on that subnet + for which the host portion of the address is all ones (the standard + broadcast address). */ + +struct iaddr broadcast_addr (subnet, mask) + struct iaddr subnet; + struct iaddr mask; +{ + int i, j, k; + struct iaddr rv; + + if (subnet.len != mask.len) { + rv.len = 0; + return rv; + } + + for (i = 0; i < subnet.len; i++) { + rv.iabuf [i] = subnet.iabuf [i] | (~mask.iabuf [i] & 255); + } + rv.len = subnet.len; + + return rv; +} + +u_int32_t host_addr (addr, mask) + struct iaddr addr; + struct iaddr mask; +{ + int i; + u_int32_t swaddr; + struct iaddr rv; + + rv.len = 0; + + /* Mask out the network bits... */ + rv.len = addr.len; + for (i = 0; i < rv.len; i++) + rv.iabuf [i] = addr.iabuf [i] & ~mask.iabuf [i]; + + /* Copy out up to 32 bits... */ + memcpy (&swaddr, &rv.iabuf [rv.len - sizeof swaddr], sizeof swaddr); + + /* Swap it and return it. */ + return ntohl (swaddr); +} + +int addr_eq (addr1, addr2) + struct iaddr addr1, addr2; +{ + if (addr1.len != addr2.len) + return 0; + return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0; +} + +char *piaddr (addr) + struct iaddr addr; +{ + static char pbuf [4 * 16]; + char *s = pbuf; + int i; + + if (addr.len == 0) { + strcpy (s, ""); + } + for (i = 0; i < addr.len; i++) { + sprintf (s, "%s%d", i ? "." : "", addr.iabuf [i]); + s += strlen (s); + } + return pbuf; +} + +char *piaddr1 (addr) + struct iaddr addr; +{ + static char pbuf [4 * 16]; + char *s = pbuf; + int i; + + if (addr.len == 0) { + strcpy (s, ""); + } + for (i = 0; i < addr.len; i++) { + sprintf (s, "%s%d", i ? "." : "", addr.iabuf [i]); + s += strlen (s); + } + return pbuf; +} + +char *piaddrmask (struct iaddr addr, struct iaddr mask, + const char *file, int line) +{ + char *s, *t; + int i, mw; + unsigned len; + + for (i = 0; i < 32; i++) { + if (!mask.iabuf [3 - i / 8]) + i += 7; + else if (mask.iabuf [3 - i / 8] & (1 << (i % 8))) + break; + } + mw = 32 - i; + len = mw > 9 ? 2 : 1; + len += 4; /* three dots and a slash. */ + for (i = 0; i < (mw / 8) + 1; i++) { + if (addr.iabuf [i] > 99) + len += 3; + else if (addr.iabuf [i] > 9) + len += 2; + else + len++; + } + s = dmalloc (len + 1, file, line); + if (!s) + return s; + t = s; + sprintf (t, "%d", addr.iabuf [0]); + t += strlen (t); + for (i = 1; i < (mw / 8) + 1; i++) { + sprintf (t, ".%d", addr.iabuf [i]); + t += strlen (t); + } + *t++ = '/'; + sprintf (t, "%d", mw); + return s; +} + diff --git a/contrib/dhcp-3.0/common/lpf.c b/contrib/dhcp-3.0/common/lpf.c new file mode 100644 index 0000000000..54c1138833 --- /dev/null +++ b/contrib/dhcp-3.0/common/lpf.c @@ -0,0 +1,410 @@ +/* lpf.c + + Linux packet filter code, contributed by Brian Murrel at Interlinx + Support Services in Vancouver, B.C. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static char copyright[] = +"$Id: lpf.c,v 1.29.2.3 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE) +#include +#include + +#include +#include +#include +#include +#include "includes/netinet/ip.h" +#include "includes/netinet/udp.h" +#include "includes/netinet/if_ether.h" + +/* Reinitializes the specified interface after an address change. This + is not required for packet-filter APIs. */ + +#ifdef USE_LPF_SEND +void if_reinitialize_send (info) + struct interface_info *info; +{ +} +#endif + +#ifdef USE_LPF_RECEIVE +void if_reinitialize_receive (info) + struct interface_info *info; +{ +} +#endif + +/* Called by get_interface_list for each interface that's discovered. + Opens a packet filter for each interface and adds it to the select + mask. */ + +int if_register_lpf (info) + struct interface_info *info; +{ + int sock; + char filename[50]; + int b; + struct sockaddr sa; + + /* Make an LPF socket. */ + if ((sock = socket(PF_PACKET, SOCK_PACKET, + htons((short)ETH_P_ALL))) < 0) { + if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || + errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || + errno == EAFNOSUPPORT || errno == EINVAL) { + log_error ("socket: %m - make sure"); + log_error ("CONFIG_PACKET (Packet socket) %s", + "and CONFIG_FILTER"); + log_error ("(Socket Filtering) are enabled %s", + "in your kernel"); + log_fatal ("configuration!"); + } + log_fatal ("Open a socket for LPF: %m"); + } + + /* Bind to the interface name */ + memset (&sa, 0, sizeof sa); + sa.sa_family = AF_PACKET; + strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data); + if (bind (sock, &sa, sizeof sa)) { + if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || + errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || + errno == EAFNOSUPPORT || errno == EINVAL) { + log_error ("socket: %m - make sure"); + log_error ("CONFIG_PACKET (Packet socket) %s", + "and CONFIG_FILTER"); + log_error ("(Socket Filtering) are enabled %s", + "in your kernel"); + log_fatal ("configuration!"); + } + log_fatal ("Bind socket to interface: %m"); + } + + return sock; +} +#endif /* USE_LPF_SEND || USE_LPF_RECEIVE */ + +#ifdef USE_LPF_SEND +void if_register_send (info) + struct interface_info *info; +{ + /* If we're using the lpf API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_LPF_RECEIVE + info -> wfdesc = if_register_lpf (info); +#else + info -> wfdesc = info -> rfdesc; +#endif + if (!quiet_interface_discovery) + log_info ("Sending on LPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_send (info) + struct interface_info *info; +{ + /* don't need to close twice if we are using lpf for sending and + receiving */ +#ifndef USE_LPF_RECEIVE + /* for LPF this is simple, packet filters are removed when sockets + are closed */ + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + if (!quiet_interface_discovery) + log_info ("Disabling output on LPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_LPF_SEND */ + +#ifdef USE_LPF_RECEIVE +/* Defined in bpf.c. We can't extern these in dhcpd.h without pulling + in bpf includes... */ +extern struct sock_filter dhcp_bpf_filter []; +extern int dhcp_bpf_filter_len; + +#if defined (HAVE_TR_SUPPORT) +extern struct sock_filter dhcp_bpf_tr_filter []; +extern int dhcp_bpf_tr_filter_len; +static void lpf_tr_filter_setup (struct interface_info *); +#endif + +static void lpf_gen_filter_setup (struct interface_info *); + +void if_register_receive (info) + struct interface_info *info; +{ + /* Open a LPF device and hang it on this interface... */ + info -> rfdesc = if_register_lpf (info); + +#if defined (HAVE_TR_SUPPORT) + if (info -> hw_address.hbuf [0] == HTYPE_IEEE802) + lpf_tr_filter_setup (info); + else +#endif + lpf_gen_filter_setup (info); + + if (!quiet_interface_discovery) + log_info ("Listening on LPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + /* for LPF this is simple, packet filters are removed when sockets + are closed */ + close (info -> rfdesc); + info -> rfdesc = -1; + if (!quiet_interface_discovery) + log_info ("Disabling input on LPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +static void lpf_gen_filter_setup (info) + struct interface_info *info; +{ + struct sock_fprog p; + + /* Set up the bpf filter program structure. This is defined in + bpf.c */ + p.len = dhcp_bpf_filter_len; + p.filter = dhcp_bpf_filter; + + /* Patch the server port into the LPF program... + XXX changes to filter program may require changes + to the insn number(s) used below! XXX */ + dhcp_bpf_filter [8].k = ntohs ((short)local_port); + + if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, + sizeof p) < 0) { + if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || + errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || + errno == EAFNOSUPPORT) { + log_error ("socket: %m - make sure"); + log_error ("CONFIG_PACKET (Packet socket) %s", + "and CONFIG_FILTER"); + log_error ("(Socket Filtering) are enabled %s", + "in your kernel"); + log_fatal ("configuration!"); + } + log_fatal ("Can't install packet filter program: %m"); + } +} + +#if defined (HAVE_TR_SUPPORT) +static void lpf_tr_filter_setup (info) + struct interface_info *info; +{ + struct sock_fprog p; + + /* Set up the bpf filter program structure. This is defined in + bpf.c */ + p.len = dhcp_bpf_tr_filter_len; + p.filter = dhcp_bpf_tr_filter; + + /* Patch the server port into the LPF program... + XXX changes to filter program may require changes + XXX to the insn number(s) used below! + XXX Token ring filter is null - when/if we have a filter + XXX that's not, we'll need this code. + XXX dhcp_bpf_filter [?].k = ntohs (local_port); */ + + if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, + sizeof p) < 0) { + if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || + errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT || + errno == EAFNOSUPPORT) { + log_error ("socket: %m - make sure"); + log_error ("CONFIG_PACKET (Packet socket) %s", + "and CONFIG_FILTER"); + log_error ("(Socket Filtering) are enabled %s", + "in your kernel"); + log_fatal ("configuration!"); + } + log_fatal ("Can't install packet filter program: %m"); + } +} +#endif /* HAVE_TR_SUPPORT */ +#endif /* USE_LPF_RECEIVE */ + +#ifdef USE_LPF_SEND +ssize_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + unsigned hbufp = 0, ibufp = 0; + double hh [16]; + double ih [1536 / sizeof (double)]; + unsigned char *buf = (unsigned char *)ih; + struct sockaddr sa; + int result; + int fudge; + + if (!strcmp (interface -> name, "fallback")) + return send_fallback (interface, packet, raw, + len, from, to, hto); + + /* Assemble the headers... */ + assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto); + fudge = hbufp % 4; /* IP header must be word-aligned. */ + memcpy (buf + fudge, (unsigned char *)hh, hbufp); + ibufp = hbufp + fudge; + assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr, + to -> sin_addr.s_addr, to -> sin_port, + (unsigned char *)raw, len); + memcpy (buf + ibufp, raw, len); + + /* For some reason, SOCK_PACKET sockets can't be connected, + so we have to do a sentdo every time. */ + memset (&sa, 0, sizeof sa); + sa.sa_family = AF_PACKET; + strncpy (sa.sa_data, + (const char *)interface -> ifp, sizeof sa.sa_data); + + result = sendto (interface -> wfdesc, + buf + fudge, ibufp + len - fudge, 0, &sa, sizeof sa); + if (result < 0) + log_error ("send_packet: %m"); + return result; +} +#endif /* USE_LPF_SEND */ + +#ifdef USE_LPF_RECEIVE +ssize_t receive_packet (interface, buf, len, from, hfrom) + struct interface_info *interface; + unsigned char *buf; + size_t len; + struct sockaddr_in *from; + struct hardware *hfrom; +{ + int nread; + int length = 0; + int offset = 0; + unsigned char ibuf [1536]; + unsigned bufix = 0; + + length = read (interface -> rfdesc, ibuf, sizeof ibuf); + if (length <= 0) + return length; + + bufix = 0; + /* Decode the physical header... */ + offset = decode_hw_header (interface, ibuf, bufix, hfrom); + + /* If a physical layer checksum failed (dunno of any + physical layer that supports this, but WTH), skip this + packet. */ + if (offset < 0) { + return 0; + } + + bufix += offset; + length -= offset; + + /* Decode the IP and UDP headers... */ + offset = decode_udp_ip_header (interface, ibuf, bufix, from, + (unsigned)length); + + /* If the IP or UDP checksum was bad, skip the packet... */ + if (offset < 0) + return 0; + + bufix += offset; + length -= offset; + + /* Copy out the data in the packet... */ + memcpy (buf, &ibuf [bufix], length); + return length; +} + +int can_unicast_without_arp (ip) + struct interface_info *ip; +{ + return 1; +} + +int can_receive_unicast_unconfigured (ip) + struct interface_info *ip; +{ + return 1; +} + +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ + return 1; +} + +void maybe_setup_fallback () +{ + isc_result_t status; + struct interface_info *fbi = (struct interface_info *)0; + if (setup_fallback (&fbi, MDL)) { + if_register_fallback (fbi); + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); + } +} +#endif diff --git a/contrib/dhcp-3.0/common/memory.c b/contrib/dhcp-3.0/common/memory.c new file mode 100644 index 0000000000..4a444e2531 --- /dev/null +++ b/contrib/dhcp-3.0/common/memory.c @@ -0,0 +1,161 @@ +/* memory.c + + Memory-resident database... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: memory.c,v 1.66.2.5 2004/06/10 17:59:19 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +struct group *root_group; +group_hash_t *group_name_hash; +int (*group_write_hook) (struct group_object *); + +isc_result_t delete_group (struct group_object *group, int writep) +{ + struct group_object *d; + + /* The group should exist and be hashed - if not, it's invalid. */ + if (group_name_hash) { + d = (struct group_object *)0; + group_hash_lookup (&d, group_name_hash, group -> name, + strlen (group -> name), MDL); + } else + return ISC_R_INVALIDARG; + if (!d) + return ISC_R_INVALIDARG; + + /* Also not okay to delete a group that's not the one in + the hash table. */ + if (d != group) + return ISC_R_INVALIDARG; + + /* If it's dynamic, and we're deleting it, we can just blow away the + hash table entry. */ + if ((group -> flags & GROUP_OBJECT_DYNAMIC) && + !(group -> flags & GROUP_OBJECT_STATIC)) { + group_hash_delete (group_name_hash, + group -> name, strlen (group -> name), MDL); + } else { + group -> flags |= GROUP_OBJECT_DELETED; + if (group -> group) + group_dereference (&group -> group, MDL); + } + + /* Store the group declaration in the lease file. */ + if (writep && group_write_hook) { + if (!(*group_write_hook) (group)) + return ISC_R_IOERROR; + } + return ISC_R_SUCCESS; +} + +isc_result_t supersede_group (struct group_object *group, int writep) +{ + struct group_object *t, *u; + isc_result_t status; + + /* Register the group in the group name hash table, + so we can look it up later. */ + if (group_name_hash) { + t = (struct group_object *)0; + group_hash_lookup (&t, group_name_hash, + group -> name, + strlen (group -> name), MDL); + if (t && t != group) { + /* If this isn't a dynamic entry, then we need to flag + the replacement as not dynamic either - otherwise, + if the dynamic entry is deleted later, the static + entry will come back next time the server is stopped + and restarted. */ + if (!(t -> flags & GROUP_OBJECT_DYNAMIC)) + group -> flags |= GROUP_OBJECT_STATIC; + + /* Delete the old object if it hasn't already been + deleted. If it has already been deleted, get rid of + the hash table entry. This is a legitimate + situation - a deleted static object needs to be kept + around so we remember it's deleted. */ + if (!(t -> flags & GROUP_OBJECT_DELETED)) + delete_group (t, 0); + else { + group_hash_delete (group_name_hash, + group -> name, + strlen (group -> name), + MDL); + group_object_dereference (&t, MDL); + } + } + } else { + group_new_hash (&group_name_hash, 0, MDL); + t = (struct group_object *)0; + } + + /* Add the group to the group name hash if it's not + already there, and also thread it into the list of + dynamic groups if appropriate. */ + if (!t) { + group_hash_add (group_name_hash, group -> name, + strlen (group -> name), group, MDL); + } + + /* Store the group declaration in the lease file. */ + if (writep && group_write_hook) { + if (!(*group_write_hook) (group)) + return ISC_R_IOERROR; + } + return ISC_R_SUCCESS; +} + +int clone_group (struct group **gp, struct group *group, + const char *file, int line) +{ + isc_result_t status; + struct group *g = (struct group *)0; + + /* Normally gp should contain the null pointer, but for convenience + it's permissible to clone a group into itself. */ + if (*gp && *gp != group) + return 0; + if (!group_allocate (&g, file, line)) + return 0; + if (group == *gp) + *gp = (struct group *)0; + group_reference (gp, g, file, line); + g -> authoritative = group -> authoritative; + group_reference (&g -> next, group, file, line); + group_dereference (&g, file, line); + return 1; +} diff --git a/contrib/dhcp-3.0/common/nit.c b/contrib/dhcp-3.0/common/nit.c new file mode 100644 index 0000000000..bd8472fec9 --- /dev/null +++ b/contrib/dhcp-3.0/common/nit.c @@ -0,0 +1,420 @@ +/* nit.c + + Network Interface Tap (NIT) network interface code, by Ted Lemon + with one crucial tidbit of help from Stu Grossmen. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: nit.c,v 1.34.2.3 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE) +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "includes/netinet/ip.h" +#include "includes/netinet/udp.h" +#include "includes/netinet/if_ether.h" + +/* Reinitializes the specified interface after an address change. This + is not required for packet-filter APIs. */ + +#ifdef USE_NIT_SEND +void if_reinitialize_send (info) + struct interface_info *info; +{ +} +#endif + +#ifdef USE_NIT_RECEIVE +void if_reinitialize_receive (info) + struct interface_info *info; +{ +} +#endif + +/* Called by get_interface_list for each interface that's discovered. + Opens a packet filter for each interface and adds it to the select + mask. */ + +int if_register_nit (info) + struct interface_info *info; +{ + int sock; + char filename[50]; + struct ifreq ifr; + struct strioctl sio; + + /* Open a NIT device */ + sock = open ("/dev/nit", O_RDWR); + if (sock < 0) + log_fatal ("Can't open NIT device for %s: %m", info -> name); + + /* Set the NIT device to point at this interface. */ + sio.ic_cmd = NIOCBIND; + sio.ic_len = sizeof *(info -> ifp); + sio.ic_dp = (char *)(info -> ifp); + sio.ic_timout = INFTIM; + if (ioctl (sock, I_STR, &sio) < 0) + log_fatal ("Can't attach interface %s to nit device: %m", + info -> name); + + /* Get the low-level address... */ + sio.ic_cmd = SIOCGIFADDR; + sio.ic_len = sizeof ifr; + sio.ic_dp = (char *)𝔦 + sio.ic_timout = INFTIM; + if (ioctl (sock, I_STR, &sio) < 0) + log_fatal ("Can't get physical layer address for %s: %m", + info -> name); + + /* XXX code below assumes ethernet interface! */ + info -> hw_address.hlen = 7; + info -> hw_address.hbuf [0] = ARPHRD_ETHER; + memcpy (&info -> hw_address.hbuf [1], + ifr.ifr_ifru.ifru_addr.sa_data, 6); + + if (ioctl (sock, I_PUSH, "pf") < 0) + log_fatal ("Can't push packet filter onto NIT for %s: %m", + info -> name); + + return sock; +} +#endif /* USE_NIT_SEND || USE_NIT_RECEIVE */ + +#ifdef USE_NIT_SEND +void if_register_send (info) + struct interface_info *info; +{ + /* If we're using the nit API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_NIT_RECEIVE + struct packetfilt pf; + struct strioctl sio; + + info -> wfdesc = if_register_nit (info); + + pf.Pf_Priority = 0; + pf.Pf_FilterLen = 1; + pf.Pf_Filter [0] = ENF_PUSHZERO; + + /* Set up an NIT filter that rejects everything... */ + sio.ic_cmd = NIOCSETF; + sio.ic_len = sizeof pf; + sio.ic_dp = (char *)&pf; + sio.ic_timout = INFTIM; + if (ioctl (info -> wfdesc, I_STR, &sio) < 0) + log_fatal ("Can't set NIT filter: %m"); +#else + info -> wfdesc = info -> rfdesc; +#endif + if (!quiet_interface_discovery) + log_info ("Sending on NIT/%s%s%s", + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_send (info) + struct interface_info *info; +{ + /* If we're using the nit API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_NIT_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + if (!quiet_interface_discovery) + log_info ("Disabling output on NIT/%s%s%s", + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_NIT_SEND */ + +#ifdef USE_NIT_RECEIVE +/* Packet filter program... + XXX Changes to the filter program may require changes to the constant + offsets used in if_register_send to patch the NIT program! XXX */ + +void if_register_receive (info) + struct interface_info *info; +{ + int flag = 1; + u_int32_t x; + struct packetfilt pf; + struct strioctl sio; + u_int16_t addr [2]; + struct timeval t; + + /* Open a NIT device and hang it on this interface... */ + info -> rfdesc = if_register_nit (info); + + /* Set the snap length to 0, which means always take the whole + packet. */ + x = 0; + if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0) + log_fatal ("Can't set NIT snap length on %s: %m", info -> name); + + /* Set the stream to byte stream mode */ + if (ioctl (info -> rfdesc, I_SRDOPT, RMSGN) != 0) + log_info ("I_SRDOPT failed on %s: %m", info -> name); + +#if 0 + /* Push on the chunker... */ + if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0) + log_fatal ("Can't push chunker onto NIT STREAM: %m"); + + /* Set the timeout to zero. */ + t.tv_sec = 0; + t.tv_usec = 0; + if (ioctl (info -> rfdesc, NIOCSTIME, &t) < 0) + log_fatal ("Can't set chunk timeout: %m"); +#endif + + /* Ask for no header... */ + x = 0; + if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0) + log_fatal ("Can't set NIT flags on %s: %m", info -> name); + + /* Set up the NIT filter program. */ + /* XXX Unlike the BPF filter program, this one won't work if the + XXX IP packet is fragmented or if there are options on the IP + XXX header. */ + pf.Pf_Priority = 0; + pf.Pf_FilterLen = 0; + + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6; + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; + pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT; + pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11; + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_AND; + pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0xFF); + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND; + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18; + pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; + pf.Pf_Filter [pf.Pf_FilterLen++] = local_port; + + /* Install the filter... */ + sio.ic_cmd = NIOCSETF; + sio.ic_len = sizeof pf; + sio.ic_dp = (char *)&pf; + sio.ic_timout = INFTIM; + if (ioctl (info -> rfdesc, I_STR, &sio) < 0) + log_fatal ("Can't set NIT filter on %s: %m", info -> name); + + if (!quiet_interface_discovery) + log_info ("Listening on NIT/%s%s%s", + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + /* If we're using the nit API for sending and receiving, + we don't need to register this interface twice. */ + close (info -> rfdesc); + info -> rfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling input on NIT/%s%s%s", + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_NIT_RECEIVE */ + +#ifdef USE_NIT_SEND +ssize_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + unsigned hbufp, ibufp; + double hh [16]; + double ih [1536 / sizeof (double)]; + unsigned char *buf = (unsigned char *)ih; + struct sockaddr *junk; + struct strbuf ctl, data; + struct sockaddr_in foo; + int result; + + if (!strcmp (interface -> name, "fallback")) + return send_fallback (interface, packet, raw, + len, from, to, hto); + + /* Start with the sockaddr struct... */ + junk = (struct sockaddr *)&hh [0]; + hbufp = (((unsigned char *)&junk -> sa_data [0]) - + (unsigned char *)&hh[0]); + ibufp = 0; + + /* Assemble the headers... */ + assemble_hw_header (interface, (unsigned char *)junk, &hbufp, hto); + assemble_udp_ip_header (interface, buf, &ibufp, + from.s_addr, to -> sin_addr.s_addr, + to -> sin_port, (unsigned char *)raw, len); + + /* Copy the data into the buffer (yuk). */ + memcpy (buf + ibufp, raw, len); + + /* Set up the sockaddr structure... */ +#if USE_SIN_LEN + junk -> sa_len = hbufp - 2; /* XXX */ +#endif + junk -> sa_family = AF_UNSPEC; + + /* Set up the msg_buf structure... */ + ctl.buf = (char *)&hh [0]; + ctl.maxlen = ctl.len = hbufp; + data.buf = (char *)&ih [0]; + data.maxlen = data.len = ibufp + len; + + result = putmsg (interface -> wfdesc, &ctl, &data, 0); + if (result < 0) + log_error ("send_packet: %m"); + return result; +} +#endif /* USE_NIT_SEND */ + +#ifdef USE_NIT_RECEIVE +ssize_t receive_packet (interface, buf, len, from, hfrom) + struct interface_info *interface; + unsigned char *buf; + size_t len; + struct sockaddr_in *from; + struct hardware *hfrom; +{ + int nread; + int length = 0; + int offset = 0; + unsigned char ibuf [1536]; + int bufix = 0; + + length = read (interface -> rfdesc, ibuf, sizeof ibuf); + if (length <= 0) + return length; + + /* Decode the physical header... */ + offset = decode_hw_header (interface, ibuf, bufix, hfrom); + + /* If a physical layer checksum failed (dunno of any + physical layer that supports this, but WTH), skip this + packet. */ + if (offset < 0) { + return 0; + } + + bufix += offset; + length -= offset; + + /* Decode the IP and UDP headers... */ + offset = decode_udp_ip_header (interface, ibuf, bufix, + from, length); + + /* If the IP or UDP checksum was bad, skip the packet... */ + if (offset < 0) + return 0; + + bufix += offset; + length -= offset; + + /* Copy out the data in the packet... */ + memcpy (buf, &ibuf [bufix], length); + return length; +} + +int can_unicast_without_arp (ip) + struct interface_info *ip; +{ + return 1; +} + +int can_receive_unicast_unconfigured (ip) + struct interface_info *ip; +{ + return 1; +} + +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ + return 1; +} + +void maybe_setup_fallback () +{ + isc_result_t status; + struct interface_info *fbi = (struct interface_info *)0; + if (setup_fallback (&fbi, MDL)) { + if_register_fallback (fbi); + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); + } +} +#endif diff --git a/contrib/dhcp-3.0/common/options.c b/contrib/dhcp-3.0/common/options.c new file mode 100644 index 0000000000..d0d5caccca --- /dev/null +++ b/contrib/dhcp-3.0/common/options.c @@ -0,0 +1,2332 @@ +/* options.c + + DHCP options parsing and reassembly. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: options.c,v 1.85.2.27 2004/12/04 00:03:18 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#define DHCP_OPTION_DATA +#include "dhcpd.h" +#include + +struct option *vendor_cfg_option; + +static void do_option_set PROTO ((pair *, + struct option_cache *, + enum statement_op)); + +/* Parse all available options out of the specified packet. */ + +int parse_options (packet) + struct packet *packet; +{ + int i; + struct option_cache *op = (struct option_cache *)0; + + /* Allocate a new option state. */ + if (!option_state_allocate (&packet -> options, MDL)) { + packet -> options_valid = 0; + return 0; + } + + /* If we don't see the magic cookie, there's nothing to parse. */ + if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) { + packet -> options_valid = 0; + return 1; + } + + /* Go through the options field, up to the end of the packet + or the End field. */ + if (!parse_option_buffer (packet -> options, + &packet -> raw -> options [4], + (packet -> packet_length - + DHCP_FIXED_NON_UDP - 4), + &dhcp_universe)) + return 0; + + /* If we parsed a DHCP Option Overload option, parse more + options out of the buffer(s) containing them. */ + if (packet -> options_valid && + (op = lookup_option (&dhcp_universe, packet -> options, + DHO_DHCP_OPTION_OVERLOAD))) { + if (op -> data.data [0] & 1) { + if (!parse_option_buffer + (packet -> options, + (unsigned char *)packet -> raw -> file, + sizeof packet -> raw -> file, + &dhcp_universe)) + return 0; + } + if (op -> data.data [0] & 2) { + if (!parse_option_buffer + (packet -> options, + (unsigned char *)packet -> raw -> sname, + sizeof packet -> raw -> sname, + &dhcp_universe)) + return 0; + } + } + packet -> options_valid = 1; + return 1; +} + +/* Parse options out of the specified buffer, storing addresses of option + values in packet -> options and setting packet -> options_valid if no + errors are encountered. */ + +int parse_option_buffer (options, buffer, length, universe) + struct option_state *options; + const unsigned char *buffer; + unsigned length; + struct universe *universe; +{ + unsigned char *t; + const unsigned char *end = buffer + length; + unsigned len, offset; + int code; + struct option_cache *op = (struct option_cache *)0; + struct buffer *bp = (struct buffer *)0; + + if (!buffer_allocate (&bp, length, MDL)) { + log_error ("no memory for option buffer."); + return 0; + } + memcpy (bp -> data, buffer, length); + + for (offset = 0; buffer [offset] != DHO_END && offset < length; ) { + code = buffer [offset]; + /* Pad options don't have a length - just skip them. */ + if (code == DHO_PAD) { + ++offset; + continue; + } + + /* Don't look for length if the buffer isn't that big. */ + if (offset + 2 > length) { + len = 65536; + goto bogus; + } + + /* All other fields (except end, see above) have a + one-byte length. */ + len = buffer [offset + 1]; + + /* If the length is outrageous, the options are bad. */ + if (offset + len + 2 > length) { + bogus: + log_error ("parse_option_buffer: option %s (%d) %s.", + dhcp_options [code].name, len, + "larger than buffer"); + buffer_dereference (&bp, MDL); + return 0; + } + + /* If the option contains an encapsulation, parse it. If + the parse fails, or the option isn't an encapsulation (by + far the most common case), or the option isn't entirely + an encapsulation, keep the raw data as well. */ + if (universe -> options [code] && + !((universe -> options [code] -> format [0] == 'e' || + universe -> options [code] -> format [0] == 'E') && + (parse_encapsulated_suboptions + (options, universe -> options [code], + buffer + offset + 2, len, + universe, (const char *)0)))) { + op = lookup_option (universe, options, code); + if (op) { + struct data_string new; + memset (&new, 0, sizeof new); + if (!buffer_allocate (&new.buffer, op -> data.len + len, + MDL)) { + log_error ("parse_option_buffer: No memory."); + return 0; + } + memcpy (new.buffer -> data, op -> data.data, + op -> data.len); + memcpy (&new.buffer -> data [op -> data.len], + &bp -> data [offset + 2], len); + new.len = op -> data.len + len; + new.data = new.buffer -> data; + data_string_forget (&op -> data, MDL); + data_string_copy (&op -> data, &new, MDL); + data_string_forget (&new, MDL); + } else { + save_option_buffer (universe, options, bp, + &bp -> data [offset + 2], len, + universe -> options [code], 1); + } + } + offset += len + 2; + } + buffer_dereference (&bp, MDL); + return 1; +} + +/* If an option in an option buffer turns out to be an encapsulation, + figure out what to do. If we don't know how to de-encapsulate it, + or it's not well-formed, return zero; otherwise, return 1, indicating + that we succeeded in de-encapsulating it. */ + +struct universe *find_option_universe (struct option *eopt, const char *uname) +{ + int i; + char *s, *t; + struct universe *universe = (struct universe *)0; + + /* Look for the E option in the option format. */ + s = strchr (eopt -> format, 'E'); + if (!s) { + log_error ("internal encapsulation format error 1."); + return 0; + } + /* Look for the universe name in the option format. */ + t = strchr (++s, '.'); + /* If there was no trailing '.', or there's something after the + trailing '.', the option is bogus and we can't use it. */ + if (!t || t [1]) { + log_error ("internal encapsulation format error 2."); + return 0; + } + if (t == s && uname) { + for (i = 0; i < universe_count; i++) { + if (!strcmp (universes [i] -> name, uname)) { + universe = universes [i]; + break; + } + } + } else if (t != s) { + for (i = 0; i < universe_count; i++) { + if (strlen (universes [i] -> name) == t - s && + !memcmp (universes [i] -> name, + s, (unsigned)(t - s))) { + universe = universes [i]; + break; + } + } + } + return universe; +} + +/* If an option in an option buffer turns out to be an encapsulation, + figure out what to do. If we don't know how to de-encapsulate it, + or it's not well-formed, return zero; otherwise, return 1, indicating + that we succeeded in de-encapsulating it. */ + +int parse_encapsulated_suboptions (struct option_state *options, + struct option *eopt, + const unsigned char *buffer, + unsigned len, struct universe *eu, + const char *uname) +{ + int i; + struct universe *universe = find_option_universe (eopt, uname); + + /* If we didn't find the universe, we can't do anything with it + right now (e.g., we can't decode vendor options until we've + decoded the packet and executed the scopes that it matches). */ + if (!universe) + return 0; + + /* If we don't have a decoding function for it, we can't decode + it. */ + if (!universe -> decode) + return 0; + + i = (*universe -> decode) (options, buffer, len, universe); + + /* If there is stuff before the suboptions, we have to keep it. */ + if (eopt -> format [0] != 'E') + return 0; + /* Otherwise, return the status of the decode function. */ + return i; +} + +int fqdn_universe_decode (struct option_state *options, + const unsigned char *buffer, + unsigned length, struct universe *u) +{ + char *name; + struct buffer *bp = (struct buffer *)0; + + /* FQDN options have to be at least four bytes long. */ + if (length < 3) + return 0; + + /* Save the contents of the option in a buffer. */ + if (!buffer_allocate (&bp, length + 4, MDL)) { + log_error ("no memory for option buffer."); + return 0; + } + memcpy (&bp -> data [3], buffer + 1, length - 1); + + if (buffer [0] & 4) /* encoded */ + bp -> data [0] = 1; + else + bp -> data [0] = 0; + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [0], 1, + &fqdn_options [FQDN_ENCODED], 0)) { + bad: + buffer_dereference (&bp, MDL); + return 0; + } + + if (buffer [0] & 1) /* server-update */ + bp -> data [2] = 1; + else + bp -> data [2] = 0; + if (buffer [0] & 2) /* no-client-update */ + bp -> data [1] = 1; + else + bp -> data [1] = 0; + + /* XXX Ideally we should store the name in DNS format, so if the + XXX label isn't in DNS format, we convert it to DNS format, + XXX rather than converting labels specified in DNS format to + XXX the plain ASCII representation. But that's hard, so + XXX not now. */ + + /* Not encoded using DNS format? */ + if (!bp -> data [0]) { + unsigned i; + + /* Some broken clients NUL-terminate this option. */ + if (buffer [length - 1] == 0) { + --length; + bp -> data [1] = 1; + } + + /* Determine the length of the hostname component of the + name. If the name contains no '.' character, it + represents a non-qualified label. */ + for (i = 3; i < length && buffer [i] != '.'; i++); + i -= 3; + + /* Note: If the client sends a FQDN, the first '.' will + be used as a NUL terminator for the hostname. */ + if (i) + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data[5], i, + &fqdn_options [FQDN_HOSTNAME], + 0)) + goto bad; + /* Note: If the client sends a single label, the + FQDN_DOMAINNAME option won't be set. */ + if (length > 4 + i && + !save_option_buffer (&fqdn_universe, options, bp, + &bp -> data[6 + i], length - 4 - i, + &fqdn_options [FQDN_DOMAINNAME], 1)) + goto bad; + /* Also save the whole name. */ + if (length > 3) + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [5], length - 3, + &fqdn_options [FQDN_FQDN], 1)) + goto bad; + } else { + unsigned len; + unsigned total_len = 0; + unsigned first_len = 0; + int terminated = 0; + unsigned char *s; + + s = &bp -> data[5]; + + while (s < &bp -> data[0] + length + 2) { + len = *s; + if (len > 63) { + log_info ("fancy bits in fqdn option"); + return 0; + } + if (len == 0) { + terminated = 1; + break; + } + if (s + len > &bp -> data [0] + length + 3) { + log_info ("fqdn tag longer than buffer"); + return 0; + } + + if (first_len == 0) { + first_len = len; + } + + *s = '.'; + s += len + 1; + total_len += len + 1; + } + + /* We wind up with a length that's one too many because + we shouldn't increment for the last label, but there's + no way to tell we're at the last label until we exit + the loop. :'*/ + if (total_len > 0) + total_len--; + + if (!terminated) { + first_len = total_len; + } + + if (first_len > 0 && + !save_option_buffer (&fqdn_universe, options, bp, + &bp -> data[6], first_len, + &fqdn_options [FQDN_HOSTNAME], 0)) + goto bad; + if (total_len > 0 && first_len != total_len) { + if (!save_option_buffer + (&fqdn_universe, options, bp, + &bp -> data[6 + first_len], total_len - first_len, + &fqdn_options [FQDN_DOMAINNAME], 1)) + goto bad; + } + if (total_len > 0) + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [6], total_len, + &fqdn_options [FQDN_FQDN], 1)) + goto bad; + } + + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [1], 1, + &fqdn_options [FQDN_NO_CLIENT_UPDATE], 0)) + goto bad; + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [2], 1, + &fqdn_options [FQDN_SERVER_UPDATE], 0)) + goto bad; + + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [3], 1, + &fqdn_options [FQDN_RCODE1], 0)) + goto bad; + if (!save_option_buffer (&fqdn_universe, options, bp, + &bp -> data [4], 1, + &fqdn_options [FQDN_RCODE2], 0)) + goto bad; + + buffer_dereference (&bp, MDL); + return 1; +} + +/* cons options into a big buffer, and then split them out into the + three seperate buffers if needed. This allows us to cons up a set + of vendor options using the same routine. */ + +int cons_options (inpacket, outpacket, lease, client_state, + mms, in_options, cfg_options, + scope, overload, terminate, bootpp, prl, vuname) + struct packet *inpacket; + struct dhcp_packet *outpacket; + struct lease *lease; + struct client_state *client_state; + int mms; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + int overload; /* Overload flags that may be set. */ + int terminate; + int bootpp; + struct data_string *prl; + const char *vuname; +{ +#define PRIORITY_COUNT 300 + unsigned priority_list [PRIORITY_COUNT]; + int priority_len; + unsigned char buffer [4096]; /* Really big buffer... */ + unsigned main_buffer_size; + unsigned mainbufix, bufix, agentix; + int fileix; + int snameix; + unsigned option_size; + unsigned length; + int i; + struct option_cache *op; + struct data_string ds; + pair pp, *hash; + int need_endopt = 0; + int have_sso = 0; + int ocount = 0; + int ofbuf1=0, ofbuf2=0; + + memset (&ds, 0, sizeof ds); + + /* If there's a Maximum Message Size option in the incoming packet + and no alternate maximum message size has been specified, take the + one in the packet. */ + + if (inpacket && + (op = lookup_option (&dhcp_universe, inpacket -> options, + DHO_DHCP_MAX_MESSAGE_SIZE))) { + evaluate_option_cache (&ds, inpacket, + lease, client_state, in_options, + cfg_options, scope, op, MDL); + if (ds.len >= sizeof (u_int16_t)) { + i = getUShort (ds.data); + + if(!mms || (i < mms)) + mms = i; + } + data_string_forget (&ds, MDL); + } + + /* If the client has provided a maximum DHCP message size, + use that; otherwise, if it's BOOTP, only 64 bytes; otherwise + use up to the minimum IP MTU size (576 bytes). */ + /* XXX if a BOOTP client specifies a max message size, we will + honor it. */ + + if (mms) { + main_buffer_size = mms - DHCP_FIXED_LEN; + + /* Enforce a minimum packet size... */ + if (main_buffer_size < (576 - DHCP_FIXED_LEN)) + main_buffer_size = 576 - DHCP_FIXED_LEN; + } else if (bootpp) { + if (inpacket) { + main_buffer_size = + inpacket -> packet_length - DHCP_FIXED_LEN; + if (main_buffer_size < 64) + main_buffer_size = 64; + } else + main_buffer_size = 64; + } else + main_buffer_size = 576 - DHCP_FIXED_LEN; + + /* Set a hard limit at the size of the output buffer. */ + if (main_buffer_size > sizeof buffer) + main_buffer_size = sizeof buffer; + + /* Preload the option priority list with mandatory options. */ + priority_len = 0; + priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE; + priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER; + priority_list [priority_len++] = DHO_DHCP_LEASE_TIME; + priority_list [priority_len++] = DHO_DHCP_MESSAGE; + priority_list [priority_len++] = DHO_DHCP_REQUESTED_ADDRESS; + priority_list [priority_len++] = DHO_FQDN; + + if (prl && prl -> len > 0) { + if ((op = lookup_option (&dhcp_universe, cfg_options, + DHO_SUBNET_SELECTION))) { + if (priority_len < PRIORITY_COUNT) + priority_list [priority_len++] = + DHO_SUBNET_SELECTION; + } + + data_string_truncate (prl, (PRIORITY_COUNT - priority_len)); + + for (i = 0; i < prl -> len; i++) { + /* Prevent client from changing order of delivery + of relay agent information option. */ + if (prl -> data [i] != DHO_DHCP_AGENT_OPTIONS) + priority_list [priority_len++] = + prl -> data [i]; + } + } else { + /* First, hardcode some more options that ought to be + sent first... */ + priority_list [priority_len++] = DHO_SUBNET_MASK; + priority_list [priority_len++] = DHO_ROUTERS; + priority_list [priority_len++] = DHO_DOMAIN_NAME_SERVERS; + priority_list [priority_len++] = DHO_HOST_NAME; + + /* Append a list of the standard DHCP options from the + standard DHCP option space. Actually, if a site + option space hasn't been specified, we wind up + treating the dhcp option space as the site option + space, and the first for loop is skipped, because + it's slightly more general to do it this way, + taking the 1Q99 DHCP futures work into account. */ + if (cfg_options -> site_code_min) { + for (i = 0; i < OPTION_HASH_SIZE; i++) { + hash = cfg_options -> universes [dhcp_universe.index]; + if (hash) { + for (pp = hash [i]; pp; pp = pp -> cdr) { + op = (struct option_cache *)(pp -> car); + if (op -> option -> code < + cfg_options -> site_code_min && + priority_len < PRIORITY_COUNT && + (op -> option -> code != + DHO_DHCP_AGENT_OPTIONS)) + priority_list [priority_len++] = + op -> option -> code; + } + } + } + } + + /* Now cycle through the site option space, or if there + is no site option space, we'll be cycling through the + dhcp option space. */ + for (i = 0; i < OPTION_HASH_SIZE; i++) { + hash = (cfg_options -> universes + [cfg_options -> site_universe]); + if (hash) + for (pp = hash [i]; pp; pp = pp -> cdr) { + op = (struct option_cache *)(pp -> car); + if (op -> option -> code >= + cfg_options -> site_code_min && + priority_len < PRIORITY_COUNT && + (op -> option -> code != + DHO_DHCP_AGENT_OPTIONS)) + priority_list [priority_len++] = + op -> option -> code; + } + } + + /* Now go through all the universes for which options + were set and see if there are encapsulations for + them; if there are, put the encapsulation options + on the priority list as well. */ + for (i = 0; i < cfg_options -> universe_count; i++) { + if (cfg_options -> universes [i] && + universes [i] -> enc_opt && + priority_len < PRIORITY_COUNT && + universes [i] -> enc_opt -> universe == &dhcp_universe) + { + if (universes [i] -> enc_opt -> code != + DHO_DHCP_AGENT_OPTIONS) + priority_list [priority_len++] = + universes [i] -> enc_opt -> code; + } + } + + /* The vendor option space can't stand on its own, so always + add it to the list. */ + if (priority_len < PRIORITY_COUNT) + priority_list [priority_len++] = + DHO_VENDOR_ENCAPSULATED_OPTIONS; + } + + /* Figure out the overload buffer offset(s). */ + if (overload) { + ofbuf1 = main_buffer_size - 4; + if (overload == 3) + ofbuf2 = main_buffer_size - 4 + DHCP_FILE_LEN; + } + + /* Copy the options into the big buffer... */ + option_size = store_options (&ocount, buffer, + (main_buffer_size - 4 + + ((overload & 1) ? DHCP_FILE_LEN : 0) + + ((overload & 2) ? DHCP_SNAME_LEN : 0)), + inpacket, lease, client_state, + in_options, cfg_options, scope, + priority_list, priority_len, + ofbuf1, ofbuf2, terminate, vuname); + /* If store_options failed. */ + if (option_size == 0) + return 0; + if (overload) { + if (ocount == 1 && (overload & 1)) + overload = 1; + else if (ocount == 1 && (overload & 2)) + overload = 2; + else if (ocount == 3) + overload = 3; + else + overload = 0; + } + + /* Put the cookie up front... */ + memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4); + mainbufix = 4; + + /* If we're going to have to overload, store the overload + option at the beginning. If we can, though, just store the + whole thing in the packet's option buffer and leave it at + that. */ + memcpy (&outpacket -> options [mainbufix], + buffer, option_size); + mainbufix += option_size; + if (overload) { + outpacket -> options [mainbufix++] = DHO_DHCP_OPTION_OVERLOAD; + outpacket -> options [mainbufix++] = 1; + outpacket -> options [mainbufix++] = overload; + + if (overload & 1) { + memcpy (outpacket -> file, + &buffer [ofbuf1], DHCP_FILE_LEN); + } + if (overload & 2) { + if (ofbuf2) { + memcpy (outpacket -> sname, &buffer [ofbuf2], + DHCP_SNAME_LEN); + } else { + memcpy (outpacket -> sname, &buffer [ofbuf1], + DHCP_SNAME_LEN); + } + } + } + agentix = mainbufix; + if (mainbufix < main_buffer_size) + need_endopt = 1; + length = DHCP_FIXED_NON_UDP + mainbufix; + + /* Now hack in the agent options if there are any. */ + priority_list [0] = DHO_DHCP_AGENT_OPTIONS; + priority_len = 1; + agentix += + store_options (0, &outpacket -> options [agentix], + 1500 - DHCP_FIXED_LEN - agentix, + inpacket, lease, client_state, + in_options, cfg_options, scope, + priority_list, priority_len, + 0, 0, 0, (char *)0); + + /* Tack a DHO_END option onto the packet if we need to. */ + if (agentix < 1500 - DHCP_FIXED_LEN && need_endopt) + outpacket -> options [agentix++] = DHO_END; + + /* Figure out the length. */ + length = DHCP_FIXED_NON_UDP + agentix; + return length; +} + +/* Store all the requested options into the requested buffer. */ + +int store_options (ocount, buffer, buflen, packet, lease, client_state, + in_options, cfg_options, scope, priority_list, priority_len, + first_cutoff, second_cutoff, terminate, vuname) + int *ocount; + unsigned char *buffer; + unsigned buflen; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + unsigned *priority_list; + int priority_len; + unsigned first_cutoff, second_cutoff; + int terminate; + const char *vuname; +{ + int bufix = 0, six = 0, tix = 0; + int i; + int ix; + int tto; + int bufend, sbufend; + struct data_string od; + struct option_cache *oc; + unsigned code; + + if (first_cutoff) { + if (first_cutoff >= buflen) + log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL); + + bufend = first_cutoff; + } else + bufend = buflen; + + if (second_cutoff) { + if (second_cutoff >= buflen) + log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL); + + sbufend = second_cutoff; + } else + sbufend = buflen; + + memset (&od, 0, sizeof od); + + /* Eliminate duplicate options in the parameter request list. + There's got to be some clever knuthian way to do this: + Eliminate all but the first occurance of a value in an array + of values without otherwise disturbing the order of the array. */ + for (i = 0; i < priority_len - 1; i++) { + tto = 0; + for (ix = i + 1; ix < priority_len + tto; ix++) { + if (tto) + priority_list [ix - tto] = + priority_list [ix]; + if (priority_list [i] == priority_list [ix]) { + tto++; + priority_len--; + } + } + } + + /* Copy out the options in the order that they appear in the + priority list... */ + for (i = 0; i < priority_len; i++) { + /* Number of bytes left to store (some may already + have been stored by a previous pass). */ + unsigned length; + int optstart, soptstart, toptstart; + struct universe *u; + int have_encapsulation = 0; + struct data_string encapsulation; + int splitup; + + memset (&encapsulation, 0, sizeof encapsulation); + + /* Code for next option to try to store. */ + code = priority_list [i]; + + /* Look up the option in the site option space if the code + is above the cutoff, otherwise in the DHCP option space. */ + if (code >= cfg_options -> site_code_min) + u = universes [cfg_options -> site_universe]; + else + u = &dhcp_universe; + + oc = lookup_option (u, cfg_options, code); + + /* It's an encapsulation, try to find the universe + to be encapsulated first, except that if it's a straight + encapsulation and the user has provided a value for the + encapsulation option, use the user-provided value. */ + if (u -> options [code] && + ((u -> options [code] -> format [0] == 'E' && !oc) || + u -> options [code] -> format [0] == 'e')) { + int uix; + static char *s, *t; + struct option_cache *tmp; + struct data_string name; + + s = strchr (u -> options [code] -> format, 'E'); + if (s) + t = strchr (++s, '.'); + if (s && t) { + memset (&name, 0, sizeof name); + + /* A zero-length universe name means the vendor + option space, if one is defined. */ + if (t == s) { + if (vendor_cfg_option) { + tmp = lookup_option (vendor_cfg_option -> universe, + cfg_options, + vendor_cfg_option -> code); + if (tmp) + evaluate_option_cache (&name, packet, lease, + client_state, + in_options, + cfg_options, + scope, tmp, MDL); + } else if (vuname) { + name.data = (unsigned char *)s; + name.len = strlen (s); + } + } else { + name.data = (unsigned char *)s; + name.len = t - s; + } + + /* If we found a universe, and there are options configured + for that universe, try to encapsulate it. */ + if (name.len) { + have_encapsulation = + (option_space_encapsulate + (&encapsulation, packet, lease, client_state, + in_options, cfg_options, scope, &name)); + data_string_forget (&name, MDL); + } + } + } + + /* In order to avoid memory leaks, we have to get to here + with any option cache that we allocated in tmp not being + referenced by tmp, and whatever option cache is referenced + by oc being an actual reference. lookup_option doesn't + generate a reference (this needs to be fixed), so the + preceding goop ensures that if we *didn't* generate a new + option cache, oc still winds up holding an actual reference. */ + + /* If no data is available for this option, skip it. */ + if (!oc && !have_encapsulation) { + continue; + } + + /* Find the value of the option... */ + if (oc) { + evaluate_option_cache (&od, packet, + lease, client_state, in_options, + cfg_options, scope, oc, MDL); + if (!od.len) { + data_string_forget (&encapsulation, MDL); + data_string_forget (&od, MDL); + have_encapsulation = 0; + continue; + } + } + + /* We should now have a constant length for the option. */ + length = od.len; + if (have_encapsulation) { + length += encapsulation.len; + if (!od.len) { + data_string_copy (&od, &encapsulation, MDL); + data_string_forget (&encapsulation, MDL); + } else { + struct buffer *bp = (struct buffer *)0; + if (!buffer_allocate (&bp, length, MDL)) { + option_cache_dereference (&oc, MDL); + data_string_forget (&od, MDL); + data_string_forget (&encapsulation, MDL); + continue; + } + memcpy (&bp -> data [0], od.data, od.len); + memcpy (&bp -> data [od.len], encapsulation.data, + encapsulation.len); + data_string_forget (&od, MDL); + data_string_forget (&encapsulation, MDL); + od.data = &bp -> data [0]; + buffer_reference (&od.buffer, bp, MDL); + buffer_dereference (&bp, MDL); + od.len = length; + od.terminated = 0; + } + } + + /* Do we add a NUL? */ + if (terminate && dhcp_options [code].format [0] == 't') { + length++; + tto = 1; + } else { + tto = 0; + } + + /* Try to store the option. */ + + /* If the option's length is more than 255, we must store it + in multiple hunks. Store 255-byte hunks first. However, + in any case, if the option data will cross a buffer + boundary, split it across that boundary. */ + + + if (length > 255) + splitup = 1; + else + splitup = 0; + + ix = 0; + optstart = bufix; + soptstart = six; + toptstart = tix; + while (length) { + unsigned incr = length; + int consumed = 0; + int *pix; + char *base; + + /* Try to fit it in the options buffer. */ + if (!splitup && + ((!six && !tix && (i == priority_len - 1) && + (bufix + 2 + length < bufend)) || + (bufix + 5 + length < bufend))) { + base = buffer; + pix = &bufix; + /* Try to fit it in the second buffer. */ + } else if (!splitup && first_cutoff && + (first_cutoff + six + 3 + length < sbufend)) { + base = &buffer[first_cutoff]; + pix = &six; + /* Try to fit it in the third buffer. */ + } else if (!splitup && second_cutoff && + (second_cutoff + tix + 3 + length < buflen)) { + base = &buffer[second_cutoff]; + pix = &tix; + /* Split the option up into the remaining space. */ + } else { + splitup = 1; + + /* Use any remaining options space. */ + if (bufix + 6 < bufend) { + incr = bufend - bufix - 5; + base = buffer; + pix = &bufix; + /* Use any remaining first_cutoff space. */ + } else if (first_cutoff && + (first_cutoff + six + 4 < sbufend)) { + incr = sbufend - (first_cutoff + six) - 3; + base = &buffer[first_cutoff]; + pix = &six; + /* Use any remaining second_cutoff space. */ + } else if (second_cutoff && + (second_cutoff + tix + 4 < buflen)) { + incr = buflen - (second_cutoff + tix) - 3; + base = &buffer[second_cutoff]; + pix = &tix; + /* Give up, roll back this option. */ + } else { + bufix = optstart; + six = soptstart; + tix = toptstart; + break; + } + } + + if (incr > length) + incr = length; + if (incr > 255) + incr = 255; + + /* Everything looks good - copy it in! */ + base [*pix] = code; + base [*pix + 1] = (unsigned char)incr; + if (tto && incr == length) { + if (incr > 1) + memcpy (base + *pix + 2, + od.data + ix, (unsigned)(incr - 1)); + base [*pix + 2 + incr - 1] = 0; + } else { + memcpy (base + *pix + 2, + od.data + ix, (unsigned)incr); + } + length -= incr; + ix += incr; + *pix += 2 + incr; + } + data_string_forget (&od, MDL); + } + + /* If we can overload, and we have, then PAD and END those spaces. */ + if (first_cutoff && six) { + if ((first_cutoff + six + 1) < sbufend) + memset (&buffer[first_cutoff + six + 1], DHO_PAD, + sbufend - (first_cutoff + six + 1)); + else if (first_cutoff + six >= sbufend) + log_fatal("Second buffer overflow in overloaded options."); + + buffer[first_cutoff + six] = DHO_END; + *ocount |= 1; /* So that caller knows there's data there. */ + } + + if (second_cutoff && tix) { + if (second_cutoff + tix + 1 < buflen) { + memset (&buffer[second_cutoff + tix + 1], DHO_PAD, + buflen - (second_cutoff + tix + 1)); + } else if (second_cutoff + tix >= buflen) + log_fatal("Third buffer overflow in overloaded options."); + + buffer[second_cutoff + tix] = DHO_END; + *ocount |= 2; /* So that caller knows there's data there. */ + } + + if ((six || tix) && (bufix + 3 > bufend)) + log_fatal("Not enough space for option overload option."); + + return bufix; +} + +/* Format the specified option so that a human can easily read it. */ + +const char *pretty_print_option (option, data, len, emit_commas, emit_quotes) + struct option *option; + const unsigned char *data; + unsigned len; + int emit_commas; + int emit_quotes; +{ + static char optbuf [32768]; /* XXX */ + int hunksize = 0; + int opthunk = 0; + int hunkinc = 0; + int numhunk = -1; + int numelem = 0; + char fmtbuf [32]; + struct enumeration *enumbuf [32]; + int i, j, k, l; + char *op = optbuf; + const unsigned char *dp = data; + struct in_addr foo; + char comma; + unsigned long tval; + + if (emit_commas) + comma = ','; + else + comma = ' '; + + memset (enumbuf, 0, sizeof enumbuf); + + /* Figure out the size of the data. */ + for (l = i = 0; option -> format [i]; i++, l++) { + if (!numhunk) { + log_error ("%s: Extra codes in format string: %s", + option -> name, + &(option -> format [i])); + break; + } + numelem++; + fmtbuf [l] = option -> format [i]; + switch (option -> format [i]) { + case 'a': + --numelem; + fmtbuf [l] = 0; + numhunk = 0; + break; + case 'A': + --numelem; + fmtbuf [l] = 0; + numhunk = 0; + break; + case 'E': + /* Skip the universe name. */ + while (option -> format [i] && + option -> format [i] != '.') + i++; + case 'X': + for (k = 0; k < len; k++) { + if (!isascii (data [k]) || + !isprint (data [k])) + break; + } + /* If we found no bogus characters, or the bogus + character we found is a trailing NUL, it's + okay to print this option as text. */ + if (k == len || (k + 1 == len && data [k] == 0)) { + fmtbuf [l] = 't'; + numhunk = -2; + } else { + fmtbuf [l] = 'x'; + hunksize++; + comma = ':'; + numhunk = 0; + } + fmtbuf [l + 1] = 0; + break; + case 'd': + case 't': + fmtbuf [l] = 't'; + fmtbuf [l + 1] = 0; + numhunk = -2; + break; + case 'N': + k = i; + while (option -> format [i] && + option -> format [i] != '.') + i++; + enumbuf [l] = + find_enumeration (&option -> format [k] + 1, + i - k - 1); + hunksize += 1; + hunkinc = 1; + break; + case 'I': + case 'l': + case 'L': + case 'T': + hunksize += 4; + hunkinc = 4; + break; + case 's': + case 'S': + hunksize += 2; + hunkinc = 2; + break; + case 'b': + case 'B': + case 'f': + hunksize++; + hunkinc = 1; + break; + case 'e': + break; + case 'o': + opthunk += hunkinc; + break; + default: + log_error ("%s: garbage in format string: %s", + option -> name, + &(option -> format [i])); + break; + } + } + + /* Check for too few bytes... */ + if (hunksize - opthunk > len) { + log_error ("%s: expecting at least %d bytes; got %d", + option -> name, + hunksize, len); + return ""; + } + /* Check for too many bytes... */ + if (numhunk == -1 && hunksize < len) + log_error ("%s: %d extra bytes", + option -> name, + len - hunksize); + + /* If this is an array, compute its size. */ + if (!numhunk) + numhunk = len / hunksize; + /* See if we got an exact number of hunks. */ + if (numhunk > 0 && numhunk * hunksize < len) + log_error ("%s: %d extra bytes at end of array\n", + option -> name, + len - numhunk * hunksize); + + /* A one-hunk array prints the same as a single hunk. */ + if (numhunk < 0) + numhunk = 1; + + /* Cycle through the array (or hunk) printing the data. */ + for (i = 0; i < numhunk; i++) { + for (j = 0; j < numelem; j++) { + switch (fmtbuf [j]) { + case 't': + if (emit_quotes) + *op++ = '"'; + for (; dp < data + len; dp++) { + if (!isascii (*dp) || + !isprint (*dp)) { + /* Skip trailing NUL. */ + if (dp + 1 != data + len || + *dp != 0) { + sprintf (op, "\\%03o", + *dp); + op += 4; + } + } else if (*dp == '"' || + *dp == '\'' || + *dp == '$' || + *dp == '`' || + *dp == '\\') { + *op++ = '\\'; + *op++ = *dp; + } else + *op++ = *dp; + } + if (emit_quotes) + *op++ = '"'; + *op = 0; + break; + /* pretty-printing an array of enums is + going to get ugly. */ + case 'N': + if (!enumbuf [j]) + goto enum_as_num; + for (i = 0; ;i++) { + if (!enumbuf [j] -> values [i].name) + goto enum_as_num; + if (enumbuf [j] -> values [i].value == + *dp) + break; + } + strcpy (op, enumbuf [j] -> values [i].name); + op += strlen (op); + break; + case 'I': + foo.s_addr = htonl (getULong (dp)); + strcpy (op, inet_ntoa (foo)); + dp += 4; + break; + case 'l': + sprintf (op, "%ld", (long)getLong (dp)); + dp += 4; + break; + case 'T': + tval = getULong (dp); + if (tval == -1) + sprintf (op, "%s", "infinite"); + else + sprintf (op, "%ld", tval); + break; + case 'L': + sprintf (op, "%ld", + (unsigned long)getULong (dp)); + dp += 4; + break; + case 's': + sprintf (op, "%d", (int)getShort (dp)); + dp += 2; + break; + case 'S': + sprintf (op, "%d", (unsigned)getUShort (dp)); + dp += 2; + break; + case 'b': + sprintf (op, "%d", *(const char *)dp++); + break; + case 'B': + enum_as_num: + sprintf (op, "%d", *dp++); + break; + case 'x': + sprintf (op, "%x", *dp++); + break; + case 'f': + strcpy (op, *dp++ ? "true" : "false"); + break; + default: + log_error ("Unexpected format code %c", + fmtbuf [j]); + } + op += strlen (op); + if (dp == data + len) + break; + if (j + 1 < numelem && comma != ':') + *op++ = ' '; + } + if (i + 1 < numhunk) { + *op++ = comma; + } + if (dp == data + len) + break; + } + return optbuf; +} + +int get_option (result, universe, packet, lease, client_state, + in_options, cfg_options, options, scope, code, file, line) + struct data_string *result; + struct universe *universe; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct option_state *options; + struct binding_scope **scope; + unsigned code; + const char *file; + int line; +{ + struct option_cache *oc; + + if (!universe -> lookup_func) + return 0; + oc = ((*universe -> lookup_func) (universe, options, code)); + if (!oc) + return 0; + if (!evaluate_option_cache (result, packet, lease, client_state, + in_options, cfg_options, scope, oc, + file, line)) + return 0; + return 1; +} + +void set_option (universe, options, option, op) + struct universe *universe; + struct option_state *options; + struct option_cache *option; + enum statement_op op; +{ + struct option_cache *oc, *noc; + + switch (op) { + case if_statement: + case add_statement: + case eval_statement: + case break_statement: + default: + log_error ("bogus statement type in do_option_set."); + break; + + case default_option_statement: + oc = lookup_option (universe, options, + option -> option -> code); + if (oc) + break; + save_option (universe, options, option); + break; + + case supersede_option_statement: + case send_option_statement: + /* Install the option, replacing any existing version. */ + save_option (universe, options, option); + break; + + case append_option_statement: + case prepend_option_statement: + oc = lookup_option (universe, options, + option -> option -> code); + if (!oc) { + save_option (universe, options, option); + break; + } + /* If it's not an expression, make it into one. */ + if (!oc -> expression && oc -> data.len) { + if (!expression_allocate (&oc -> expression, MDL)) { + log_error ("Can't allocate const expression."); + break; + } + oc -> expression -> op = expr_const_data; + data_string_copy + (&oc -> expression -> data.const_data, + &oc -> data, MDL); + data_string_forget (&oc -> data, MDL); + } + noc = (struct option_cache *)0; + if (!option_cache_allocate (&noc, MDL)) + break; + if (op == append_option_statement) { + if (!make_concat (&noc -> expression, + oc -> expression, + option -> expression)) { + option_cache_dereference (&noc, MDL); + break; + } + } else { + if (!make_concat (&noc -> expression, + option -> expression, + oc -> expression)) { + option_cache_dereference (&noc, MDL); + break; + } + } + noc -> option = oc -> option; + save_option (universe, options, noc); + option_cache_dereference (&noc, MDL); + break; + } +} + +struct option_cache *lookup_option (universe, options, code) + struct universe *universe; + struct option_state *options; + unsigned code; +{ + if (!options) + return (struct option_cache *)0; + if (universe -> lookup_func) + return (*universe -> lookup_func) (universe, options, code); + else + log_error ("can't look up options in %s space.", + universe -> name); + return (struct option_cache *)0; +} + +struct option_cache *lookup_hashed_option (universe, options, code) + struct universe *universe; + struct option_state *options; + unsigned code; +{ + int hashix; + pair bptr; + pair *hash; + + /* Make sure there's a hash table. */ + if (universe -> index >= options -> universe_count || + !(options -> universes [universe -> index])) + return (struct option_cache *)0; + + hash = options -> universes [universe -> index]; + + hashix = compute_option_hash (code); + for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { + if (((struct option_cache *)(bptr -> car)) -> option -> code == + code) + return (struct option_cache *)(bptr -> car); + } + return (struct option_cache *)0; +} + +int save_option_buffer (struct universe *universe, + struct option_state *options, + struct buffer *bp, + unsigned char *buffer, unsigned length, + struct option *option, int tp) +{ + struct buffer *lbp = (struct buffer *)0; + struct option_cache *op = (struct option_cache *)0; + + if (!option_cache_allocate (&op, MDL)) { + log_error ("No memory for option %s.%s.", + universe -> name, + option -> name); + return 0; + } + + /* If we weren't passed a buffer in which the data are saved and + refcounted, allocate one now. */ + if (!bp) { + if (!buffer_allocate (&lbp, length, MDL)) { + log_error ("no memory for option buffer."); + + option_cache_dereference (&op, MDL); + return 0; + } + memcpy (lbp -> data, buffer, length + tp); + bp = lbp; + buffer = &bp -> data [0]; /* Refer to saved buffer. */ + } + + /* Reference buffer copy to option cache. */ + op -> data.buffer = (struct buffer *)0; + buffer_reference (&op -> data.buffer, bp, MDL); + + /* Point option cache into buffer. */ + op -> data.data = buffer; + op -> data.len = length; + + if (tp) { + /* NUL terminate (we can get away with this because we (or + the caller!) allocated one more than the buffer size, and + because the byte following the end of an option is always + the code of the next option, which the caller is getting + out of the *original* buffer. */ + buffer [length] = 0; + op -> data.terminated = 1; + } else + op -> data.terminated = 0; + + op -> option = option; + + /* Now store the option. */ + save_option (universe, options, op); + + /* And let go of our reference. */ + option_cache_dereference (&op, MDL); + + return 1; +} + +void save_option (struct universe *universe, + struct option_state *options, struct option_cache *oc) +{ + if (universe -> save_func) + (*universe -> save_func) (universe, options, oc); + else + log_error ("can't store options in %s space.", + universe -> name); +} + +void save_hashed_option (universe, options, oc) + struct universe *universe; + struct option_state *options; + struct option_cache *oc; +{ + int hashix; + pair bptr; + pair *hash = options -> universes [universe -> index]; + + if (oc -> refcnt == 0) + abort (); + + /* Compute the hash. */ + hashix = compute_option_hash (oc -> option -> code); + + /* If there's no hash table, make one. */ + if (!hash) { + hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL); + if (!hash) { + log_error ("no memory to store %s.%s", + universe -> name, oc -> option -> name); + return; + } + memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash); + options -> universes [universe -> index] = (VOIDPTR)hash; + } else { + /* Try to find an existing option matching the new one. */ + for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { + if (((struct option_cache *) + (bptr -> car)) -> option -> code == + oc -> option -> code) + break; + } + + /* If we find one, dereference it and put the new one + in its place. */ + if (bptr) { + option_cache_dereference + ((struct option_cache **)&bptr -> car, MDL); + option_cache_reference + ((struct option_cache **)&bptr -> car, + oc, MDL); + return; + } + } + + /* Otherwise, just put the new one at the head of the list. */ + bptr = new_pair (MDL); + if (!bptr) { + log_error ("No memory for option_cache reference."); + return; + } + bptr -> cdr = hash [hashix]; + bptr -> car = 0; + option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL); + hash [hashix] = bptr; +} + +void delete_option (universe, options, code) + struct universe *universe; + struct option_state *options; + int code; +{ + if (universe -> delete_func) + (*universe -> delete_func) (universe, options, code); + else + log_error ("can't delete options from %s space.", + universe -> name); +} + +void delete_hashed_option (universe, options, code) + struct universe *universe; + struct option_state *options; + int code; +{ + int hashix; + pair bptr, prev = (pair)0; + pair *hash = options -> universes [universe -> index]; + + /* There may not be any options in this space. */ + if (!hash) + return; + + /* Try to find an existing option matching the new one. */ + hashix = compute_option_hash (code); + for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { + if (((struct option_cache *)(bptr -> car)) -> option -> code + == code) + break; + prev = bptr; + } + /* If we found one, wipe it out... */ + if (bptr) { + if (prev) + prev -> cdr = bptr -> cdr; + else + hash [hashix] = bptr -> cdr; + option_cache_dereference + ((struct option_cache **)(&bptr -> car), MDL); + free_pair (bptr, MDL); + } +} + +extern struct option_cache *free_option_caches; /* XXX */ + +int option_cache_dereference (ptr, file, line) + struct option_cache **ptr; + const char *file; + int line; +{ + if (!ptr || !*ptr) { + log_error ("Null pointer in option_cache_dereference: %s(%d)", + file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + (*ptr) -> refcnt--; + rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); + if (!(*ptr) -> refcnt) { + if ((*ptr) -> data.buffer) + data_string_forget (&(*ptr) -> data, file, line); + if ((*ptr) -> expression) + expression_dereference (&(*ptr) -> expression, + file, line); + if ((*ptr) -> next) + option_cache_dereference (&((*ptr) -> next), + file, line); + /* Put it back on the free list... */ + (*ptr) -> expression = (struct expression *)free_option_caches; + free_option_caches = *ptr; + dmalloc_reuse (free_option_caches, (char *)0, 0, 0); + } + if ((*ptr) -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*ptr); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + *ptr = (struct option_cache *)0; + return 0; +#endif + } + *ptr = (struct option_cache *)0; + return 1; + +} + +int hashed_option_state_dereference (universe, state, file, line) + struct universe *universe; + struct option_state *state; + const char *file; + int line; +{ + pair *heads; + pair cp, next; + int i; + + /* Get the pointer to the array of hash table bucket heads. */ + heads = (pair *)(state -> universes [universe -> index]); + if (!heads) + return 0; + + /* For each non-null head, loop through all the buckets dereferencing + the attached option cache structures and freeing the buckets. */ + for (i = 0; i < OPTION_HASH_SIZE; i++) { + for (cp = heads [i]; cp; cp = next) { + next = cp -> cdr; + option_cache_dereference + ((struct option_cache **)&cp -> car, + file, line); + free_pair (cp, file, line); + } + } + + dfree (heads, file, line); + state -> universes [universe -> index] = (void *)0; + return 1; +} + +int store_option (result, universe, packet, lease, client_state, + in_options, cfg_options, scope, oc) + struct data_string *result; + struct universe *universe; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct option_cache *oc; +{ + struct data_string d1, d2; + + memset (&d1, 0, sizeof d1); + memset (&d2, 0, sizeof d2); + + if (evaluate_option_cache (&d2, packet, lease, client_state, + in_options, cfg_options, scope, oc, MDL)) { + if (!buffer_allocate (&d1.buffer, + (result -> len + + universe -> length_size + + universe -> tag_size + d2.len), MDL)) { + data_string_forget (result, MDL); + data_string_forget (&d2, MDL); + return 0; + } + d1.data = &d1.buffer -> data [0]; + if (result -> len) + memcpy (d1.buffer -> data, + result -> data, result -> len); + d1.len = result -> len; + (*universe -> store_tag) (&d1.buffer -> data [d1.len], + oc -> option -> code); + d1.len += universe -> tag_size; + (*universe -> store_length) (&d1.buffer -> data [d1.len], + d2.len); + d1.len += universe -> length_size; + memcpy (&d1.buffer -> data [d1.len], d2.data, d2.len); + d1.len += d2.len; + data_string_forget (&d2, MDL); + data_string_forget (result, MDL); + data_string_copy (result, &d1, MDL); + data_string_forget (&d1, MDL); + return 1; + } + return 0; +} + +int option_space_encapsulate (result, packet, lease, client_state, + in_options, cfg_options, scope, name) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct data_string *name; +{ + struct universe *u; + + u = (struct universe *)0; + universe_hash_lookup (&u, universe_hash, + (const char *)name -> data, name -> len, MDL); + if (!u) + return 0; + + if (u -> encapsulate) + return (*u -> encapsulate) (result, packet, lease, + client_state, + in_options, cfg_options, scope, u); + log_error ("encapsulation requested for %s with no support.", + name -> data); + return 0; +} + +int hashed_option_space_encapsulate (result, packet, lease, client_state, + in_options, cfg_options, scope, universe) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct universe *universe; +{ + pair p, *hash; + int status; + int i; + + if (universe -> index >= cfg_options -> universe_count) + return 0; + + hash = cfg_options -> universes [universe -> index]; + if (!hash) + return 0; + + status = 0; + for (i = 0; i < OPTION_HASH_SIZE; i++) { + for (p = hash [i]; p; p = p -> cdr) { + if (store_option (result, universe, packet, + lease, client_state, in_options, + cfg_options, scope, + (struct option_cache *)p -> car)) + status = 1; + } + } + + return status; +} + +int nwip_option_space_encapsulate (result, packet, lease, client_state, + in_options, cfg_options, scope, universe) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct universe *universe; +{ + pair ocp; + int status; + int i; + static struct option_cache *no_nwip; + struct data_string ds; + struct option_chain_head *head; + + if (universe -> index >= cfg_options -> universe_count) + return 0; + head = ((struct option_chain_head *) + cfg_options -> universes [fqdn_universe.index]); + if (!head) + return 0; + + status = 0; + for (ocp = head -> first; ocp; ocp = ocp -> cdr) { + struct option_cache *oc = (struct option_cache *)(ocp -> car); + if (store_option (result, universe, packet, + lease, client_state, in_options, + cfg_options, scope, + (struct option_cache *)ocp -> car)) + status = 1; + } + + /* If there's no data, the nwip suboption is supposed to contain + a suboption saying there's no data. */ + if (!status) { + if (!no_nwip) { + static unsigned char nni [] = { 1, 0 }; + memset (&ds, 0, sizeof ds); + ds.data = nni; + ds.len = 2; + if (option_cache_allocate (&no_nwip, MDL)) + data_string_copy (&no_nwip -> data, &ds, MDL); + no_nwip -> option = nwip_universe.options [1]; + } + if (no_nwip) { + if (store_option (result, universe, packet, lease, + client_state, in_options, + cfg_options, scope, no_nwip)) + status = 1; + } + } else { + memset (&ds, 0, sizeof ds); + + /* If we have nwip options, the first one has to be the + nwip-exists-in-option-area option. */ + if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) { + data_string_forget (result, MDL); + return 0; + } + ds.data = &ds.buffer -> data [0]; + ds.buffer -> data [0] = 2; + ds.buffer -> data [1] = 0; + memcpy (&ds.buffer -> data [2], result -> data, result -> len); + data_string_forget (result, MDL); + data_string_copy (result, &ds, MDL); + data_string_forget (&ds, MDL); + } + + return status; +} + +int fqdn_option_space_encapsulate (result, packet, lease, client_state, + in_options, cfg_options, scope, universe) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct universe *universe; +{ + pair ocp; + struct data_string results [FQDN_SUBOPTION_COUNT + 1]; + unsigned i; + unsigned len; + struct buffer *bp = (struct buffer *)0; + struct option_chain_head *head; + + /* If there's no FQDN universe, don't encapsulate. */ + if (fqdn_universe.index >= cfg_options -> universe_count) + return 0; + head = ((struct option_chain_head *) + cfg_options -> universes [fqdn_universe.index]); + if (!head) + return 0; + + /* Figure out the values of all the suboptions. */ + memset (results, 0, sizeof results); + for (ocp = head -> first; ocp; ocp = ocp -> cdr) { + struct option_cache *oc = (struct option_cache *)(ocp -> car); + if (oc -> option -> code > FQDN_SUBOPTION_COUNT) + continue; + evaluate_option_cache (&results [oc -> option -> code], + packet, lease, client_state, in_options, + cfg_options, scope, oc, MDL); + } + len = 4 + results [FQDN_FQDN].len; + /* Save the contents of the option in a buffer. */ + if (!buffer_allocate (&bp, len, MDL)) { + log_error ("no memory for option buffer."); + return 0; + } + buffer_reference (&result -> buffer, bp, MDL); + result -> len = 3; + result -> data = &bp -> data [0]; + + memset (&bp -> data [0], 0, len); + if (results [FQDN_NO_CLIENT_UPDATE].len && + results [FQDN_NO_CLIENT_UPDATE].data [0]) + bp -> data [0] |= 2; + if (results [FQDN_SERVER_UPDATE].len && + results [FQDN_SERVER_UPDATE].data [0]) + bp -> data [0] |= 1; + if (results [FQDN_RCODE1].len) + bp -> data [1] = results [FQDN_RCODE1].data [0]; + if (results [FQDN_RCODE2].len) + bp -> data [2] = results [FQDN_RCODE2].data [0]; + + if (results [FQDN_ENCODED].len && + results [FQDN_ENCODED].data [0]) { + unsigned char *out; + int i; + bp -> data [0] |= 4; + out = &bp -> data [3]; + if (results [FQDN_FQDN].len) { + i = 0; + while (i < results [FQDN_FQDN].len) { + int j; + for (j = i; ('.' != + results [FQDN_FQDN].data [j]) && + j < results [FQDN_FQDN].len; j++) + ; + *out++ = j - i; + memcpy (out, &results [FQDN_FQDN].data [i], + (unsigned)(j - i)); + out += j - i; + i = j; + if (results [FQDN_FQDN].data [j] == '.') + i++; + } + if ((results [FQDN_FQDN].data + [results [FQDN_FQDN].len - 1] == '.')) + *out++ = 0; + result -> len = out - result -> data; + result -> terminated = 0; + } + } else { + if (results [FQDN_FQDN].len) { + memcpy (&bp -> data [3], results [FQDN_FQDN].data, + results [FQDN_FQDN].len); + result -> len += results [FQDN_FQDN].len; + result -> terminated = 0; + } + } + for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) { + if (results [i].len) + data_string_forget (&results [i], MDL); + } + buffer_dereference (&bp, MDL); + return 1; +} + +void option_space_foreach (struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff, + void (*func) (struct option_cache *, + struct packet *, + struct lease *, struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)) +{ + if (u -> foreach) + (*u -> foreach) (packet, lease, client_state, in_options, + cfg_options, scope, u, stuff, func); +} + +void suboption_foreach (struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff, + void (*func) (struct option_cache *, + struct packet *, + struct lease *, struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *), + struct option_cache *oc, + const char *vsname) +{ + struct universe *universe = find_option_universe (oc -> option, + vsname); + int i; + + if (universe -> foreach) + (*universe -> foreach) (packet, lease, client_state, + in_options, cfg_options, + scope, universe, stuff, func); +} + +void hashed_option_space_foreach (struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff, + void (*func) (struct option_cache *, + struct packet *, + struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)) +{ + pair *hash; + int i; + struct option_cache *oc; + + if (cfg_options -> universe_count <= u -> index) + return; + + hash = cfg_options -> universes [u -> index]; + if (!hash) + return; + for (i = 0; i < OPTION_HASH_SIZE; i++) { + pair p; + /* XXX save _all_ options! XXX */ + for (p = hash [i]; p; p = p -> cdr) { + oc = (struct option_cache *)p -> car; + (*func) (oc, packet, lease, client_state, + in_options, cfg_options, scope, u, stuff); + } + } +} + +void save_linked_option (universe, options, oc) + struct universe *universe; + struct option_state *options; + struct option_cache *oc; +{ + pair *tail; + pair np = (pair )0; + struct option_chain_head *head; + + if (universe -> index >= options -> universe_count) + return; + head = ((struct option_chain_head *) + options -> universes [universe -> index]); + if (!head) { + if (!option_chain_head_allocate (((struct option_chain_head **) + &options -> universes + [universe -> index]), MDL)) + return; + head = ((struct option_chain_head *) + options -> universes [universe -> index]); + } + + /* Find the tail of the list. */ + for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) { + if (oc -> option == + ((struct option_cache *)((*tail) -> car)) -> option) { + option_cache_dereference ((struct option_cache **) + (&(*tail) -> car), MDL); + option_cache_reference ((struct option_cache **) + (&(*tail) -> car), oc, MDL); + return; + } + } + + *tail = cons (0, 0); + if (*tail) { + option_cache_reference ((struct option_cache **) + (&(*tail) -> car), oc, MDL); + } +} + +int linked_option_space_encapsulate (result, packet, lease, client_state, + in_options, cfg_options, scope, universe) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct universe *universe; +{ + int status; + pair oc; + struct option_chain_head *head; + + if (universe -> index >= cfg_options -> universe_count) + return 0; + head = ((struct option_chain_head *) + cfg_options -> universes [universe -> index]); + if (!head) + return 0; + + status = 0; + for (oc = head -> first; oc; oc = oc -> cdr) { + if (store_option (result, universe, packet, + lease, client_state, in_options, cfg_options, + scope, (struct option_cache *)(oc -> car))) + status = 1; + } + + return status; +} + +void delete_linked_option (universe, options, code) + struct universe *universe; + struct option_state *options; + int code; +{ + pair *tail, tmp = (pair)0; + struct option_chain_head *head; + + if (universe -> index >= options -> universe_count) + return; + head = ((struct option_chain_head *) + options -> universes [universe -> index]); + if (!head) + return; + + for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) { + if (code == + ((struct option_cache *)(*tail) -> car) -> option -> code) + { + tmp = (*tail) -> cdr; + option_cache_dereference ((struct option_cache **) + (&(*tail) -> car), MDL); + dfree (*tail, MDL); + (*tail) = tmp; + break; + } + } +} + +struct option_cache *lookup_linked_option (universe, options, code) + struct universe *universe; + struct option_state *options; + unsigned code; +{ + pair oc; + struct option_chain_head *head; + + if (universe -> index >= options -> universe_count) + return 0; + head = ((struct option_chain_head *) + options -> universes [universe -> index]); + if (!head) + return 0; + + for (oc = head -> first; oc; oc = oc -> cdr) { + if (code == + ((struct option_cache *)(oc -> car)) -> option -> code) { + return (struct option_cache *)(oc -> car); + } + } + + return (struct option_cache *)0; +} + +int linked_option_state_dereference (universe, state, file, line) + struct universe *universe; + struct option_state *state; + const char *file; + int line; +{ + return (option_chain_head_dereference + ((struct option_chain_head **) + (&state -> universes [universe -> index]), MDL)); +} + +void linked_option_space_foreach (struct packet *packet, struct lease *lease, + struct client_state *client_state, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *stuff, + void (*func) (struct option_cache *, + struct packet *, + struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)) +{ + pair car; + struct option_chain_head *head; + + if (u -> index >= cfg_options -> universe_count) + return; + head = ((struct option_chain_head *) + cfg_options -> universes [u -> index]); + if (!head) + return; + for (car = head -> first; car; car = car -> cdr) { + (*func) ((struct option_cache *)(car -> car), + packet, lease, client_state, + in_options, cfg_options, scope, u, stuff); + } +} + +void do_packet (interface, packet, len, from_port, from, hfrom) + struct interface_info *interface; + struct dhcp_packet *packet; + unsigned len; + unsigned int from_port; + struct iaddr from; + struct hardware *hfrom; +{ + int i; + struct option_cache *op; + struct packet *decoded_packet; +#if defined (DEBUG_MEMORY_LEAKAGE) + unsigned long previous_outstanding = dmalloc_outstanding; +#endif + +#if defined (TRACING) + trace_inpacket_stash (interface, packet, len, from_port, from, hfrom); +#endif + + decoded_packet = (struct packet *)0; + if (!packet_allocate (&decoded_packet, MDL)) { + log_error ("do_packet: no memory for incoming packet!"); + return; + } + decoded_packet -> raw = packet; + decoded_packet -> packet_length = len; + decoded_packet -> client_port = from_port; + decoded_packet -> client_addr = from; + interface_reference (&decoded_packet -> interface, interface, MDL); + decoded_packet -> haddr = hfrom; + + if (packet -> hlen > sizeof packet -> chaddr) { + packet_dereference (&decoded_packet, MDL); + log_info ("Discarding packet with bogus hlen."); + return; + } + + /* If there's an option buffer, try to parse it. */ + if (decoded_packet -> packet_length >= DHCP_FIXED_NON_UDP + 4) { + if (!parse_options (decoded_packet)) { + if (decoded_packet -> options) + option_state_dereference + (&decoded_packet -> options, MDL); + packet_dereference (&decoded_packet, MDL); + return; + } + + if (decoded_packet -> options_valid && + (op = lookup_option (&dhcp_universe, + decoded_packet -> options, + DHO_DHCP_MESSAGE_TYPE))) { + struct data_string dp; + memset (&dp, 0, sizeof dp); + evaluate_option_cache (&dp, decoded_packet, + (struct lease *)0, + (struct client_state *)0, + decoded_packet -> options, + (struct option_state *)0, + (struct binding_scope **)0, + op, MDL); + if (dp.len > 0) + decoded_packet -> packet_type = dp.data [0]; + else + decoded_packet -> packet_type = 0; + data_string_forget (&dp, MDL); + } + } + + if (decoded_packet -> packet_type) + dhcp (decoded_packet); + else + bootp (decoded_packet); + + /* If the caller kept the packet, they'll have upped the refcnt. */ + packet_dereference (&decoded_packet, MDL); + +#if defined (DEBUG_MEMORY_LEAKAGE) + log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term", + dmalloc_generation, + dmalloc_outstanding - previous_outstanding, + dmalloc_outstanding, dmalloc_longterm); +#endif +#if defined (DEBUG_MEMORY_LEAKAGE) + dmalloc_dump_outstanding (); +#endif +#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) + dump_rc_history (0); +#endif +} + diff --git a/contrib/dhcp-3.0/common/packet.c b/contrib/dhcp-3.0/common/packet.c new file mode 100644 index 0000000000..936703ee9c --- /dev/null +++ b/contrib/dhcp-3.0/common/packet.c @@ -0,0 +1,337 @@ +/* packet.c + + Packet assembly code, originally contributed by Archie Cobbs. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This code was originally contributed by Archie Cobbs, and is still + * very similar to that contribution, although the packet checksum code + * has been hacked significantly with the help of quite a few ISC DHCP + * users, without whose gracious and thorough help the checksum code would + * still be disabled. + */ + +#ifndef lint +static char copyright[] = +"$Id: packet.c,v 1.40.2.4 2004/11/24 17:39:16 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING) +#include "includes/netinet/ip.h" +#include "includes/netinet/udp.h" +#include "includes/netinet/if_ether.h" +#endif /* PACKET_ASSEMBLY || PACKET_DECODING */ + +/* Compute the easy part of the checksum on a range of bytes. */ + +u_int32_t checksum (buf, nbytes, sum) + unsigned char *buf; + unsigned nbytes; + u_int32_t sum; +{ + unsigned i; + +#ifdef DEBUG_CHECKSUM + log_debug ("checksum (%x %d %x)", buf, nbytes, sum); +#endif + + /* Checksum all the pairs of bytes first... */ + for (i = 0; i < (nbytes & ~1U); i += 2) { +#ifdef DEBUG_CHECKSUM_VERBOSE + log_debug ("sum = %x", sum); +#endif + sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i))); + /* Add carry. */ + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + + /* If there's a single byte left over, checksum it, too. Network + byte order is big-endian, so the remaining byte is the high byte. */ + if (i < nbytes) { +#ifdef DEBUG_CHECKSUM_VERBOSE + log_debug ("sum = %x", sum); +#endif + sum += buf [i] << 8; + /* Add carry. */ + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + + return sum; +} + +/* Finish computing the checksum, and then put it into network byte order. */ + +u_int32_t wrapsum (sum) + u_int32_t sum; +{ +#ifdef DEBUG_CHECKSUM + log_debug ("wrapsum (%x)", sum); +#endif + + sum = ~sum & 0xFFFF; +#ifdef DEBUG_CHECKSUM_VERBOSE + log_debug ("sum = %x", sum); +#endif + +#ifdef DEBUG_CHECKSUM + log_debug ("wrapsum returns %x", htons (sum)); +#endif + return htons(sum); +} + +#ifdef PACKET_ASSEMBLY +void assemble_hw_header (interface, buf, bufix, to) + struct interface_info *interface; + unsigned char *buf; + unsigned *bufix; + struct hardware *to; +{ +#if defined (HAVE_TR_SUPPORT) + if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802) + assemble_tr_header (interface, buf, bufix, to); + else +#endif +#if defined (DEC_FDDI) + if (interface -> hw_address.hbuf [0] == HTYPE_FDDI) + assemble_fddi_header (interface, buf, bufix, to); + else +#endif + assemble_ethernet_header (interface, buf, bufix, to); + +} + +/* UDP header and IP header assembled together for convenience. */ + +void assemble_udp_ip_header (interface, buf, bufix, + from, to, port, data, len) + struct interface_info *interface; + unsigned char *buf; + unsigned *bufix; + u_int32_t from; + u_int32_t to; + u_int32_t port; + unsigned char *data; + unsigned len; +{ + struct ip ip; + struct udphdr udp; + + /* Fill out the IP header */ + IP_V_SET (&ip, 4); + IP_HL_SET (&ip, 20); + ip.ip_tos = IPTOS_LOWDELAY; + ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len); + ip.ip_id = 0; + ip.ip_off = 0; + ip.ip_ttl = 16; + ip.ip_p = IPPROTO_UDP; + ip.ip_sum = 0; + ip.ip_src.s_addr = from; + ip.ip_dst.s_addr = to; + + /* Checksum the IP header... */ + ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0)); + + /* Copy the ip header into the buffer... */ + memcpy (&buf [*bufix], &ip, sizeof ip); + *bufix += sizeof ip; + + /* Fill out the UDP header */ + udp.uh_sport = local_port; /* XXX */ + udp.uh_dport = port; /* XXX */ + udp.uh_ulen = htons(sizeof(udp) + len); + memset (&udp.uh_sum, 0, sizeof udp.uh_sum); + + /* Compute UDP checksums, including the ``pseudo-header'', the UDP + header and the data. */ + + udp.uh_sum = + wrapsum (checksum ((unsigned char *)&udp, sizeof udp, + checksum (data, len, + checksum ((unsigned char *) + &ip.ip_src, + 2 * sizeof ip.ip_src, + IPPROTO_UDP + + (u_int32_t) + ntohs (udp.uh_ulen))))); + + /* Copy the udp header into the buffer... */ + memcpy (&buf [*bufix], &udp, sizeof udp); + *bufix += sizeof udp; +} +#endif /* PACKET_ASSEMBLY */ + +#ifdef PACKET_DECODING +/* Decode a hardware header... */ +/* XXX currently only supports ethernet; doesn't check for other types. */ + +ssize_t decode_hw_header (interface, buf, bufix, from) + struct interface_info *interface; + unsigned char *buf; + unsigned bufix; + struct hardware *from; +{ +#if defined (HAVE_TR_SUPPORT) + if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802) + return decode_tr_header (interface, buf, bufix, from); + else +#endif +#if defined (DEC_FDDI) + if (interface -> hw_address.hbuf [0] == HTYPE_FDDI) + return decode_fddi_header (interface, buf, bufix, from); + else +#endif + return decode_ethernet_header (interface, buf, bufix, from); +} + +/* UDP header and IP header decoded together for convenience. */ + +ssize_t decode_udp_ip_header (interface, buf, bufix, from, buflen) + struct interface_info *interface; + unsigned char *buf; + unsigned bufix; + struct sockaddr_in *from; + unsigned buflen; +{ + unsigned char *data; + struct ip *ip; + struct udphdr *udp; + u_int32_t ip_len = (buf [bufix] & 0xf) << 2; + u_int32_t sum, usum; + static int ip_packets_seen; + static int ip_packets_bad_checksum; + static int udp_packets_seen; + static int udp_packets_bad_checksum; + static int udp_packets_length_checked; + static int udp_packets_length_overflow; + unsigned len; + unsigned ulen; + int ignore = 0; + + ip = (struct ip *)(buf + bufix); + udp = (struct udphdr *)(buf + bufix + ip_len); + +#ifdef USERLAND_FILTER + /* Is it a UDP packet? */ + if (ip -> ip_p != IPPROTO_UDP) + return -1; + + /* Is it to the port we're serving? */ + if (udp -> uh_dport != local_port) + return -1; +#endif /* USERLAND_FILTER */ + + ulen = ntohs (udp -> uh_ulen); + if (ulen < sizeof *udp || + ((unsigned char *)udp) + ulen > buf + bufix + buflen) { + log_info ("bogus UDP packet length: %d", ulen); + return -1; + } + + /* Check the IP header checksum - it should be zero. */ + ++ip_packets_seen; + if (wrapsum (checksum (buf + bufix, ip_len, 0))) { + ++ip_packets_bad_checksum; + if (ip_packets_seen > 4 && + (ip_packets_seen / ip_packets_bad_checksum) < 2) { + log_info ("%d bad IP checksums seen in %d packets", + ip_packets_bad_checksum, ip_packets_seen); + ip_packets_seen = ip_packets_bad_checksum = 0; + } + return -1; + } + + /* Check the IP packet length. */ + if (ntohs (ip -> ip_len) != buflen) { + if ((ntohs (ip -> ip_len + 2) & ~1) == buflen) + ignore = 1; + else + log_debug ("ip length %d disagrees with bytes received %d.", + ntohs (ip -> ip_len), buflen); + } + + /* Copy out the IP source address... */ + memcpy (&from -> sin_addr, &ip -> ip_src, 4); + + /* Compute UDP checksums, including the ``pseudo-header'', the UDP + header and the data. If the UDP checksum field is zero, we're + not supposed to do a checksum. */ + + data = buf + bufix + ip_len + sizeof *udp; + len = ulen - sizeof *udp; + ++udp_packets_length_checked; + if (len + data > buf + bufix + buflen) { + ++udp_packets_length_overflow; + if (udp_packets_length_checked > 4 && + (udp_packets_length_checked / + udp_packets_length_overflow) < 2) { + log_info ("%d udp packets in %d too long - dropped", + udp_packets_length_overflow, + udp_packets_length_checked); + udp_packets_length_overflow = + udp_packets_length_checked = 0; + } + return -1; + } + if (len + data < buf + bufix + buflen && + len + data != buf + bufix + buflen && !ignore) + log_debug ("accepting packet with data after udp payload."); + if (len + data > buf + bufix + buflen) { + log_debug ("dropping packet with bogus uh_ulen %ld", + (long)(len + sizeof *udp)); + return -1; + } + + usum = udp -> uh_sum; + udp -> uh_sum = 0; + + sum = wrapsum (checksum ((unsigned char *)udp, sizeof *udp, + checksum (data, len, + checksum ((unsigned char *) + &ip -> ip_src, + 2 * sizeof ip -> ip_src, + IPPROTO_UDP + + (u_int32_t)ulen)))); + + udp_packets_seen++; + if (usum && usum != sum) { + udp_packets_bad_checksum++; + if (udp_packets_seen > 4 && + (udp_packets_seen / udp_packets_bad_checksum) < 2) { + log_info ("%d bad udp checksums in %d packets", + udp_packets_bad_checksum, udp_packets_seen); + udp_packets_seen = udp_packets_bad_checksum = 0; + } + return -1; + } + + /* Copy out the port... */ + memcpy (&from -> sin_port, &udp -> uh_sport, sizeof udp -> uh_sport); + + return ip_len + sizeof *udp; +} +#endif /* PACKET_DECODING */ diff --git a/contrib/dhcp-3.0/common/parse.c b/contrib/dhcp-3.0/common/parse.c new file mode 100644 index 0000000000..e29fdcff03 --- /dev/null +++ b/contrib/dhcp-3.0/common/parse.c @@ -0,0 +1,4853 @@ +/* parse.c + + Common parser code for dhcpd and dhclient. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: parse.c,v 1.104.2.20 2004/09/30 20:38:31 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +/* Enumerations can be specified in option formats, and are used for + parsing, so we define the routines that manage them here. */ + +struct enumeration *enumerations; + +void add_enumeration (struct enumeration *enumeration) +{ + enumeration -> next = enumerations; + enumerations = enumeration; +} + +struct enumeration *find_enumeration (const char *name, int length) +{ + struct enumeration *e; + + for (e = enumerations; e; e = e -> next) + if (strlen (e -> name) == length && + !memcmp (e -> name, name, (unsigned)length)) + return e; + return (struct enumeration *)0; +} + +struct enumeration_value *find_enumeration_value (const char *name, + int length, + const char *value) +{ + struct enumeration *e; + int i; + + e = find_enumeration (name, length); + if (e) { + for (i = 0; e -> values [i].name; i++) { + if (!strcmp (value, e -> values [i].name)) + return &e -> values [i]; + } + } + return (struct enumeration_value *)0; +} + +/* Skip to the semicolon ending the current statement. If we encounter + braces, the matching closing brace terminates the statement. If we + encounter a right brace but haven't encountered a left brace, return + leaving the brace in the token buffer for the caller. If we see a + semicolon and haven't seen a left brace, return. This lets us skip + over: + + statement; + statement foo bar { } + statement foo bar { statement { } } + statement} + + ...et cetera. */ + +void skip_to_semi (cfile) + struct parse *cfile; +{ + skip_to_rbrace (cfile, 0); +} + +void skip_to_rbrace (cfile, brace_count) + struct parse *cfile; + int brace_count; +{ + enum dhcp_token token; + const char *val; + +#if defined (DEBUG_TOKEN) + log_error ("skip_to_rbrace: %d\n", brace_count); +#endif + do { + token = peek_token (&val, (unsigned *)0, cfile); + if (token == RBRACE) { + token = next_token (&val, (unsigned *)0, cfile); + if (brace_count) { + if (!--brace_count) + return; + } else + return; + } else if (token == LBRACE) { + brace_count++; + } else if (token == SEMI && !brace_count) { + token = next_token (&val, (unsigned *)0, cfile); + return; + } else if (token == EOL) { + /* EOL only happens when parsing /etc/resolv.conf, + and we treat it like a semicolon because the + resolv.conf file is line-oriented. */ + token = next_token (&val, (unsigned *)0, cfile); + return; + } + token = next_token (&val, (unsigned *)0, cfile); + } while (token != END_OF_FILE); +} + +int parse_semi (cfile) + struct parse *cfile; +{ + enum dhcp_token token; + const char *val; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != SEMI) { + parse_warn (cfile, "semicolon expected."); + skip_to_semi (cfile); + return 0; + } + return 1; +} + +/* string-parameter :== STRING SEMI */ + +int parse_string (cfile, sptr, lptr) + struct parse *cfile; + char **sptr; + unsigned *lptr; +{ + const char *val; + enum dhcp_token token; + char *s; + unsigned len; + + token = next_token (&val, &len, cfile); + if (token != STRING) { + parse_warn (cfile, "expecting a string"); + skip_to_semi (cfile); + return 0; + } + s = (char *)dmalloc (len + 1, MDL); + if (!s) + log_fatal ("no memory for string %s.", val); + memcpy (s, val, len + 1); + + if (!parse_semi (cfile)) { + dfree (s, MDL); + return 0; + } + if (sptr) + *sptr = s; + else + dfree (s, MDL); + if (lptr) + *lptr = len; + return 1; +} + +/* + * hostname :== IDENTIFIER + * | IDENTIFIER DOT + * | hostname DOT IDENTIFIER + */ + +char *parse_host_name (cfile) + struct parse *cfile; +{ + const char *val; + enum dhcp_token token; + unsigned len = 0; + char *s; + char *t; + pair c = (pair)0; + int ltid = 0; + + /* Read a dotted hostname... */ + do { + /* Read a token, which should be an identifier. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token) && token != NUMBER) + break; + token = next_token (&val, (unsigned *)0, cfile); + + /* Store this identifier... */ + if (!(s = (char *)dmalloc (strlen (val) + 1, MDL))) + log_fatal ("can't allocate temp space for hostname."); + strcpy (s, val); + c = cons ((caddr_t)s, c); + len += strlen (s) + 1; + /* Look for a dot; if it's there, keep going, otherwise + we're done. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token == DOT) { + token = next_token (&val, (unsigned *)0, cfile); + ltid = 1; + } else + ltid = 0; + } while (token == DOT); + + /* Should be at least one token. */ + if (!len) + return (char *)0; + + /* Assemble the hostname together into a string. */ + if (!(s = (char *)dmalloc (len + ltid, MDL))) + log_fatal ("can't allocate space for hostname."); + t = s + len + ltid; + *--t = 0; + if (ltid) + *--t = '.'; + while (c) { + pair cdr = c -> cdr; + unsigned l = strlen ((char *)(c -> car)); + t -= l; + memcpy (t, (char *)(c -> car), l); + /* Free up temp space. */ + dfree (c -> car, MDL); + dfree (c, MDL); + c = cdr; + if (t != s) + *--t = '.'; + } + return s; +} + +/* ip-addr-or-hostname :== ip-address | hostname + ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER + + Parse an ip address or a hostname. If uniform is zero, put in + an expr_substring node to limit hostnames that evaluate to more + than one IP address. */ + +int parse_ip_addr_or_hostname (expr, cfile, uniform) + struct expression **expr; + struct parse *cfile; + int uniform; +{ + const char *val; + enum dhcp_token token; + unsigned char addr [4]; + unsigned len = sizeof addr; + char *name; + struct expression *x = (struct expression *)0; + + token = peek_token (&val, (unsigned *)0, cfile); + if (is_identifier (token)) { + name = parse_host_name (cfile); + if (!name) + return 0; + if (!make_host_lookup (expr, name)) { + dfree(name, MDL); + return 0; + } + dfree(name, MDL); + if (!uniform) { + if (!make_limit (&x, *expr, 4)) + return 0; + expression_dereference (expr, MDL); + *expr = x; + } + } else if (token == NUMBER) { + if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) + return 0; + return make_const_data (expr, addr, len, 0, 1, MDL); + } else { + if (token != RBRACE && token != LBRACE) + token = next_token (&val, (unsigned *)0, cfile); + parse_warn (cfile, "%s (%d): expecting IP address or hostname", + val, token); + if (token != SEMI) + skip_to_semi (cfile); + return 0; + } + + return 1; +} + +/* + * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER + */ + +int parse_ip_addr (cfile, addr) + struct parse *cfile; + struct iaddr *addr; +{ + const char *val; + enum dhcp_token token; + + addr -> len = 4; + if (parse_numeric_aggregate (cfile, addr -> iabuf, + &addr -> len, DOT, 10, 8)) + return 1; + return 0; +} + +/* + * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI + * hardware-type :== ETHERNET | TOKEN_RING | FDDI + */ + +void parse_hardware_param (cfile, hardware) + struct parse *cfile; + struct hardware *hardware; +{ + const char *val; + enum dhcp_token token; + unsigned hlen; + unsigned char *t; + + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + case ETHERNET: + hardware -> hbuf [0] = HTYPE_ETHER; + break; + case TOKEN_RING: + hardware -> hbuf [0] = HTYPE_IEEE802; + break; + case FDDI: + hardware -> hbuf [0] = HTYPE_FDDI; + break; + default: + if (!strncmp (val, "unknown-", 8)) { + hardware -> hbuf [0] = atoi (&val [8]); + } else { + parse_warn (cfile, + "expecting a network hardware type"); + skip_to_semi (cfile); + + return; + } + } + + /* Parse the hardware address information. Technically, + it would make a lot of sense to restrict the length of the + data we'll accept here to the length of a particular hardware + address type. Unfortunately, there are some broken clients + out there that put bogus data in the chaddr buffer, and we accept + that data in the lease file rather than simply failing on such + clients. Yuck. */ + hlen = 0; + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SEMI) { + hardware -> hlen = 1; + goto out; + } + t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen, + COLON, 16, 8); + if (!t) { + hardware -> hlen = 1; + return; + } + if (hlen + 1 > sizeof hardware -> hbuf) { + dfree (t, MDL); + parse_warn (cfile, "hardware address too long"); + } else { + hardware -> hlen = hlen + 1; + memcpy ((unsigned char *)&hardware -> hbuf [1], t, hlen); + if (hlen + 1 < sizeof hardware -> hbuf) + memset (&hardware -> hbuf [hlen + 1], 0, + (sizeof hardware -> hbuf) - hlen - 1); + dfree (t, MDL); + } + + out: + token = next_token (&val, (unsigned *)0, cfile); + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + } +} + +/* lease-time :== NUMBER SEMI */ + +void parse_lease_time (cfile, timep) + struct parse *cfile; + TIME *timep; +{ + const char *val; + enum dhcp_token token; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "Expecting numeric lease time"); + skip_to_semi (cfile); + return; + } + convert_num (cfile, (unsigned char *)timep, val, 10, 32); + /* Unswap the number - convert_num returns stuff in NBO. */ + *timep = ntohl (*timep); /* XXX */ + + parse_semi (cfile); +} + +/* No BNF for numeric aggregates - that's defined by the caller. What + this function does is to parse a sequence of numbers seperated by + the token specified in seperator. If max is zero, any number of + numbers will be parsed; otherwise, exactly max numbers are + expected. Base and size tell us how to internalize the numbers + once they've been tokenized. */ + +unsigned char *parse_numeric_aggregate (cfile, buf, + max, seperator, base, size) + struct parse *cfile; + unsigned char *buf; + unsigned *max; + int seperator; + int base; + unsigned size; +{ + const char *val; + enum dhcp_token token; + unsigned char *bufp = buf, *s, *t; + unsigned count = 0; + pair c = (pair)0; + + if (!bufp && *max) { + bufp = (unsigned char *)dmalloc (*max * size / 8, MDL); + if (!bufp) + log_fatal ("no space for numeric aggregate"); + s = 0; + } else + s = bufp; + + do { + if (count) { + token = peek_token (&val, (unsigned *)0, cfile); + if (token != seperator) { + if (!*max) + break; + if (token != RBRACE && token != LBRACE) + token = next_token (&val, + (unsigned *)0, + cfile); + parse_warn (cfile, "too few numbers."); + if (token != SEMI) + skip_to_semi (cfile); + return (unsigned char *)0; + } + token = next_token (&val, (unsigned *)0, cfile); + } + token = next_token (&val, (unsigned *)0, cfile); + + if (token == END_OF_FILE) { + parse_warn (cfile, "unexpected end of file"); + break; + } + + /* Allow NUMBER_OR_NAME if base is 16. */ + if (token != NUMBER && + (base != 16 || token != NUMBER_OR_NAME)) { + parse_warn (cfile, "expecting numeric value."); + skip_to_semi (cfile); + return (unsigned char *)0; + } + /* If we can, convert the number now; otherwise, build + a linked list of all the numbers. */ + if (s) { + convert_num (cfile, s, val, base, size); + s += size / 8; + } else { + t = (unsigned char *)dmalloc (strlen (val) + 1, MDL); + if (!t) + log_fatal ("no temp space for number."); + strcpy ((char *)t, val); + c = cons ((caddr_t)t, c); + } + } while (++count != *max); + + /* If we had to cons up a list, convert it now. */ + if (c) { + bufp = (unsigned char *)dmalloc (count * size / 8, MDL); + if (!bufp) + log_fatal ("no space for numeric aggregate."); + s = bufp + count - size / 8; + *max = count; + } + while (c) { + pair cdr = c -> cdr; + convert_num (cfile, s, (char *)(c -> car), base, size); + s -= size / 8; + /* Free up temp space. */ + dfree (c -> car, MDL); + dfree (c, MDL); + c = cdr; + } + return bufp; +} + +void convert_num (cfile, buf, str, base, size) + struct parse *cfile; + unsigned char *buf; + const char *str; + int base; + unsigned size; +{ + const char *ptr = str; + int negative = 0; + u_int32_t val = 0; + int tval; + int max; + + if (*ptr == '-') { + negative = 1; + ++ptr; + } + + /* If base wasn't specified, figure it out from the data. */ + if (!base) { + if (ptr [0] == '0') { + if (ptr [1] == 'x') { + base = 16; + ptr += 2; + } else if (isascii (ptr [1]) && isdigit (ptr [1])) { + base = 8; + ptr += 1; + } else { + base = 10; + } + } else { + base = 10; + } + } + + do { + tval = *ptr++; + /* XXX assumes ASCII... */ + if (tval >= 'a') + tval = tval - 'a' + 10; + else if (tval >= 'A') + tval = tval - 'A' + 10; + else if (tval >= '0') + tval -= '0'; + else { + parse_warn (cfile, "Bogus number: %s.", str); + break; + } + if (tval >= base) { + parse_warn (cfile, + "Bogus number %s: digit %d not in base %d", + str, tval, base); + break; + } + val = val * base + tval; + } while (*ptr); + + if (negative) + max = (1 << (size - 1)); + else + max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); + if (val > max) { + switch (base) { + case 8: + parse_warn (cfile, + "%s%lo exceeds max (%d) for precision.", + negative ? "-" : "", + (unsigned long)val, max); + break; + case 16: + parse_warn (cfile, + "%s%lx exceeds max (%d) for precision.", + negative ? "-" : "", + (unsigned long)val, max); + break; + default: + parse_warn (cfile, + "%s%lu exceeds max (%d) for precision.", + negative ? "-" : "", + (unsigned long)val, max); + break; + } + } + + if (negative) { + switch (size) { + case 8: + *buf = -(unsigned long)val; + break; + case 16: + putShort (buf, -(long)val); + break; + case 32: + putLong (buf, -(long)val); + break; + default: + parse_warn (cfile, + "Unexpected integer size: %d\n", size); + break; + } + } else { + switch (size) { + case 8: + *buf = (u_int8_t)val; + break; + case 16: + putUShort (buf, (u_int16_t)val); + break; + case 32: + putULong (buf, val); + break; + default: + parse_warn (cfile, + "Unexpected integer size: %d\n", size); + break; + } + } +} + +/* + * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER + * NUMBER COLON NUMBER COLON NUMBER SEMI | + * NUMBER NUMBER SLASH NUMBER SLASH NUMBER + * NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI | + * NEVER + * + * Dates are stored in GMT or with a timezone offset; first number is day + * of week; next is year/month/day; next is hours:minutes:seconds on a + * 24-hour clock, followed by the timezone offset in seconds, which is + * optional. + */ + +TIME parse_date (cfile) + struct parse *cfile; +{ + struct tm tm; + int guess; + int tzoff, wday, year, mon, mday, hour, min, sec; + const char *val; + enum dhcp_token token; + static int months [11] = { 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334 }; + + /* Day of week, or "never"... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token == NEVER) { + if (!parse_semi (cfile)) + return 0; + return MAX_TIME; + } + + if (token != NUMBER) { + parse_warn (cfile, "numeric day of week expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + wday = atoi (val); + + /* Year... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "numeric year expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + + /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until + somebody invents a time machine, I think we can safely disregard + it. This actually works around a stupid Y2K bug that was present + in a very early beta release of dhcpd. */ + year = atoi (val); + if (year > 1900) + year -= 1900; + + /* Slash seperating year from month... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != SLASH) { + parse_warn (cfile, + "expected slash seperating year from month."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + + /* Month... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "numeric month expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + mon = atoi (val) - 1; + + /* Slash seperating month from day... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != SLASH) { + parse_warn (cfile, + "expected slash seperating month from day."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + + /* Month... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "numeric day of month expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + mday = atoi (val); + + /* Hour... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "numeric hour expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + hour = atoi (val); + + /* Colon seperating hour from minute... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != COLON) { + parse_warn (cfile, + "expected colon seperating hour from minute."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + + /* Minute... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "numeric minute expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + min = atoi (val); + + /* Colon seperating minute from second... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != COLON) { + parse_warn (cfile, + "expected colon seperating hour from minute."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + + /* Minute... */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "numeric minute expected."); + if (token != SEMI) + skip_to_semi (cfile); + return (TIME)0; + } + sec = atoi (val); + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == NUMBER) { + token = next_token (&val, (unsigned *)0, cfile); + tzoff = atoi (val); + } else + tzoff = 0; + + /* Make sure the date ends in a semicolon... */ + if (!parse_semi (cfile)) + return 0; + + /* Guess the time value... */ + guess = ((((((365 * (year - 70) + /* Days in years since '70 */ + (year - 69) / 4 + /* Leap days since '70 */ + (mon /* Days in months this year */ + ? months [mon - 1] + : 0) + + (mon > 1 && /* Leap day this year */ + !((year - 72) & 3)) + + mday - 1) * 24) + /* Day of month */ + hour) * 60) + + min) * 60) + sec + tzoff; + + /* This guess could be wrong because of leap seconds or other + weirdness we don't know about that the system does. For + now, we're just going to accept the guess, but at some point + it might be nice to do a successive approximation here to + get an exact value. Even if the error is small, if the + server is restarted frequently (and thus the lease database + is reread), the error could accumulate into something + significant. */ + + return guess; +} + +/* + * option-name :== IDENTIFIER | + IDENTIFIER . IDENTIFIER + */ + +struct option *parse_option_name (cfile, allocate, known) + struct parse *cfile; + int allocate; + int *known; +{ + const char *val; + enum dhcp_token token; + char *uname; + struct universe *universe; + struct option *option; + + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "expecting identifier after option keyword."); + if (token != SEMI) + skip_to_semi (cfile); + return (struct option *)0; + } + uname = dmalloc (strlen (val) + 1, MDL); + if (!uname) + log_fatal ("no memory for uname information."); + strcpy (uname, val); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == DOT) { + /* Go ahead and take the DOT token... */ + token = next_token (&val, (unsigned *)0, cfile); + + /* The next token should be an identifier... */ + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, "expecting identifier after '.'"); + if (token != SEMI) + skip_to_semi (cfile); + return (struct option *)0; + } + + /* Look up the option name hash table for the specified + uname. */ + universe = (struct universe *)0; + if (!universe_hash_lookup (&universe, universe_hash, + uname, 0, MDL)) { + parse_warn (cfile, "no option space named %s.", uname); + skip_to_semi (cfile); + return (struct option *)0; + } + } else { + /* Use the default hash table, which contains all the + standard dhcp option names. */ + val = uname; + universe = &dhcp_universe; + } + + /* Look up the actual option info... */ + option = (struct option *)0; + option_hash_lookup (&option, universe -> hash, val, 0, MDL); + + /* If we didn't get an option structure, it's an undefined option. */ + if (option) { + if (known) + *known = 1; + } else { + /* If we've been told to allocate, that means that this + (might) be an option code definition, so we'll create + an option structure just in case. */ + if (allocate) { + option = new_option (MDL); + if (val == uname) + option -> name = val; + else { + char *s; + dfree (uname, MDL); + s = dmalloc (strlen (val) + 1, MDL); + if (!s) + log_fatal ("no memory for option %s.%s", + universe -> name, val); + strcpy (s, val); + option -> name = s; + } + option -> universe = universe; + option -> code = 0; + return option; + } + if (val == uname) + parse_warn (cfile, "no option named %s", val); + else + parse_warn (cfile, "no option named %s in space %s", + val, uname); + skip_to_semi (cfile); + return (struct option *)0; + } + + /* Free the initial identifier token. */ + dfree (uname, MDL); + return option; +} + +/* IDENTIFIER SEMI */ + +void parse_option_space_decl (cfile) + struct parse *cfile; +{ + int token; + const char *val; + struct universe **ua, *nu; + char *s; + + next_token (&val, (unsigned *)0, cfile); /* Discard the SPACE token, + which was checked by the + caller. */ + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, "expecting identifier."); + skip_to_semi (cfile); + return; + } + nu = new_universe (MDL); + if (!nu) + log_fatal ("No memory for new option space."); + + /* Set up the server option universe... */ + s = dmalloc (strlen (val) + 1, MDL); + if (!s) + log_fatal ("No memory for new option space name."); + strcpy (s, val); + nu -> name = s; + nu -> lookup_func = lookup_hashed_option; + nu -> option_state_dereference = + hashed_option_state_dereference; + nu -> foreach = hashed_option_space_foreach; + nu -> save_func = save_hashed_option; + nu -> delete_func = delete_hashed_option; + nu -> encapsulate = hashed_option_space_encapsulate; + nu -> decode = parse_option_buffer; + nu -> length_size = 1; + nu -> tag_size = 1; + nu -> store_tag = putUChar; + nu -> store_length = putUChar; + nu -> index = universe_count++; + if (nu -> index >= universe_max) { + ua = dmalloc (universe_max * 2 * sizeof *ua, MDL); + if (!ua) + log_fatal ("No memory to expand option space array."); + memcpy (ua, universes, universe_max * sizeof *ua); + universe_max *= 2; + dfree (universes, MDL); + universes = ua; + } + universes [nu -> index] = nu; + option_new_hash (&nu -> hash, 1, MDL); + if (!nu -> hash) + log_fatal ("Can't allocate %s option hash table.", nu -> name); + universe_hash_add (universe_hash, nu -> name, 0, nu, MDL); + parse_semi (cfile); +} + +/* This is faked up to look good right now. Ideally, this should do a + recursive parse and allow arbitrary data structure definitions, but for + now it just allows you to specify a single type, an array of single types, + a sequence of types, or an array of sequences of types. + + ocd :== NUMBER EQUALS ocsd SEMI + + ocsd :== ocsd_type | + ocsd_type_sequence | + ARRAY OF ocsd_simple_type_sequence + + ocsd_type_sequence :== LBRACE ocsd_types RBRACE + + ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE + + ocsd_types :== ocsd_type | + ocsd_types ocsd_type + + ocsd_type :== ocsd_simple_type | + ARRAY OF ocsd_simple_type + + ocsd_simple_types :== ocsd_simple_type | + ocsd_simple_types ocsd_simple_type + + ocsd_simple_type :== BOOLEAN | + INTEGER NUMBER | + SIGNED INTEGER NUMBER | + UNSIGNED INTEGER NUMBER | + IP-ADDRESS | + TEXT | + STRING | + ENCAPSULATE identifier */ + +int parse_option_code_definition (cfile, option) + struct parse *cfile; + struct option *option; +{ + const char *val; + enum dhcp_token token; + unsigned arrayp = 0; + int recordp = 0; + int no_more_in_record = 0; + char tokbuf [128]; + unsigned tokix = 0; + char type; + int code; + int is_signed; + char *s; + int has_encapsulation = 0; + + /* Parse the option code. */ + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "expecting option code number."); + skip_to_semi (cfile); + return 0; + } + option -> code = atoi (val); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != EQUAL) { + parse_warn (cfile, "expecting \"=\""); + skip_to_semi (cfile); + return 0; + } + + /* See if this is an array. */ + token = next_token (&val, (unsigned *)0, cfile); + if (token == ARRAY) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != OF) { + parse_warn (cfile, "expecting \"of\"."); + skip_to_semi (cfile); + return 0; + } + arrayp = 1; + token = next_token (&val, (unsigned *)0, cfile); + } + + if (token == LBRACE) { + recordp = 1; + token = next_token (&val, (unsigned *)0, cfile); + } + + /* At this point we're expecting a data type. */ + next_type: + if (has_encapsulation) { + parse_warn (cfile, + "encapsulate must always be the last item."); + skip_to_semi (cfile); + return 0; + } + + switch (token) { + case ARRAY: + if (arrayp) { + parse_warn (cfile, "no nested arrays."); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != OF) { + parse_warn (cfile, "expecting \"of\"."); + skip_to_semi (cfile); + return 0; + } + arrayp = recordp + 1; + token = next_token (&val, (unsigned *)0, cfile); + if ((recordp) && (token == LBRACE)) { + parse_warn (cfile, + "only uniform array inside record."); + skip_to_rbrace (cfile, recordp + 1); + skip_to_semi (cfile); + return 0; + } + goto next_type; + case BOOLEAN: + type = 'f'; + break; + case INTEGER: + is_signed = 1; + parse_integer: + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "expecting number."); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + switch (atoi (val)) { + case 8: + type = is_signed ? 'b' : 'B'; + break; + case 16: + type = is_signed ? 's' : 'S'; + break; + case 32: + type = is_signed ? 'l' : 'L'; + break; + default: + parse_warn (cfile, + "%s bit precision is not supported.", val); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + break; + case SIGNED: + is_signed = 1; + parse_signed: + token = next_token (&val, (unsigned *)0, cfile); + if (token != INTEGER) { + parse_warn (cfile, "expecting \"integer\" keyword."); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + goto parse_integer; + case UNSIGNED: + is_signed = 0; + goto parse_signed; + + case IP_ADDRESS: + type = 'I'; + break; + case DOMAIN_NAME: + type = 'd'; + goto no_arrays; + case TEXT: + type = 't'; + no_arrays: + if (arrayp) { + parse_warn (cfile, "arrays of text strings not %s", + "yet supported."); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + no_more_in_record = 1; + break; + case STRING_TOKEN: + type = 'X'; + goto no_arrays; + + case ENCAPSULATE: + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "expecting option space identifier"); + skip_to_semi (cfile); + return 0; + } + if (strlen (val) + tokix + 2 > sizeof (tokbuf)) + goto toobig; + tokbuf [tokix++] = 'E'; + strcpy (&tokbuf [tokix], val); + tokix += strlen (val); + type = '.'; + has_encapsulation = 1; + break; + + default: + parse_warn (cfile, "unknown data type %s", val); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + + if (tokix == sizeof tokbuf) { + toobig: + parse_warn (cfile, "too many types in record."); + skip_to_rbrace (cfile, recordp); + if (recordp) + skip_to_semi (cfile); + return 0; + } + tokbuf [tokix++] = type; + + if (recordp) { + token = next_token (&val, (unsigned *)0, cfile); + if (arrayp > recordp) { + if (tokix == sizeof tokbuf) { + parse_warn (cfile, + "too many types in record."); + skip_to_rbrace (cfile, 1); + skip_to_semi (cfile); + return 0; + } + arrayp = 0; + tokbuf[tokix++] = 'a'; + } + if (token == COMMA) { + if (no_more_in_record) { + parse_warn (cfile, + "%s must be at end of record.", + type == 't' ? "text" : "string"); + skip_to_rbrace (cfile, 1); + if (recordp) + skip_to_semi (cfile); + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + goto next_type; + } + if (token != RBRACE) { + parse_warn (cfile, "expecting right brace."); + skip_to_rbrace (cfile, 1); + if (recordp) + skip_to_semi (cfile); + return 0; + } + } + if (!parse_semi (cfile)) { + parse_warn (cfile, "semicolon expected."); + skip_to_semi (cfile); + if (recordp) + skip_to_semi (cfile); + return 0; + } + if (has_encapsulation && arrayp) { + parse_warn (cfile, + "Arrays of encapsulations don't make sense."); + return 0; + } + if (has_encapsulation && tokbuf [0] == 'E') + has_encapsulation = 0; + s = dmalloc (tokix + + (arrayp ? 1 : 0) + + (has_encapsulation ? 1 : 0) + 1, MDL); + if (!s) + log_fatal ("no memory for option format."); + if (has_encapsulation) + s [0] = 'e'; + memcpy (s + has_encapsulation, tokbuf, tokix); + tokix += has_encapsulation; + if (arrayp) + s [tokix++] = (arrayp > recordp) ? 'a' : 'A'; + s [tokix] = 0; + option -> format = s; + if (option -> universe -> options [option -> code]) { + /* XXX Free the option, but we can't do that now because they + XXX may start out static. */ + } + option -> universe -> options [option -> code] = option; + option_hash_add (option -> universe -> hash, + (const char *)option -> name, + 0, option, MDL); + return 1; +} + +/* + * base64 :== NUMBER_OR_STRING + */ + +int parse_base64 (data, cfile) + struct data_string *data; + struct parse *cfile; +{ + enum dhcp_token token; + const char *val; + int i, j, k; + unsigned acc = 0; + static unsigned char + from64 [] = {64, 64, 64, 64, 64, 64, 64, 64, /* \"#$%&' */ + 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */ + 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */ + 60, 61, 64, 64, 64, 64, 64, 64, /* 89:;<=>? */ + 64, 0, 1, 2, 3, 4, 5, 6, /* @ABCDEFG */ + 7, 8, 9, 10, 11, 12, 13, 14, /* HIJKLMNO */ + 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */ + 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */ + 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */ + 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */ + 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */ + 49, 50, 51, 64, 64, 64, 64, 64}; /* xyz{|}~ */ + struct string_list *bufs = (struct string_list *)0, + *last = (struct string_list *)0, + *t; + int cc = 0; + int terminated = 0; + + /* It's possible for a + or a / to cause a base64 quantity to be + tokenized into more than one token, so we have to parse them all + in before decoding. */ + do { + unsigned l; + + token = next_token (&val, &l, cfile); + t = dmalloc (l + sizeof *t, MDL); + if (!t) + log_fatal ("no memory for base64 buffer."); + memset (t, 0, (sizeof *t) - 1); + memcpy (t -> string, val, l + 1); + cc += l; + if (last) + last -> next = t; + else + bufs = t; + last = t; + token = peek_token (&val, (unsigned *)0, cfile); + } while (token == NUMBER_OR_NAME || token == NAME || token == EQUAL || + token == NUMBER || token == PLUS || token == SLASH || + token == STRING); + + data -> len = cc; + data -> len = (data -> len * 3) / 4; + if (!buffer_allocate (&data -> buffer, data -> len, MDL)) { + parse_warn (cfile, "can't allocate buffer for base64 data."); + data -> len = 0; + data -> data = (unsigned char *)0; + return 0; + } + + j = k = 0; + for (t = bufs; t; t = t -> next) { + for (i = 0; t -> string [i]; i++) { + unsigned foo = t -> string [i]; + if (terminated && foo != '=') { + parse_warn (cfile, + "stuff after base64 '=' terminator: %s.", + &t -> string [i]); + goto bad; + } + if (foo < ' ' || foo > 'z') { + bad64: + parse_warn (cfile, + "invalid base64 character %d.", + t -> string [i]); + bad: + data_string_forget (data, MDL); + goto out; + } + if (foo == '=') + terminated = 1; + else { + foo = from64 [foo - ' ']; + if (foo == 64) + goto bad64; + acc = (acc << 6) + foo; + switch (k % 4) { + case 0: + break; + case 1: + data -> buffer -> data [j++] = (acc >> 4); + acc = acc & 0x0f; + break; + + case 2: + data -> buffer -> data [j++] = (acc >> 2); + acc = acc & 0x03; + break; + case 3: + data -> buffer -> data [j++] = acc; + acc = 0; + break; + } + } + k++; + } + } + if (k % 4) { + if (acc) { + parse_warn (cfile, + "partial base64 value left over: %d.", + acc); + } + } + data -> len = j; + data -> data = data -> buffer -> data; + out: + for (t = bufs; t; t = last) { + last = t -> next; + dfree (t, MDL); + } + if (data -> len) + return 1; + else + return 0; +} + + +/* + * colon-seperated-hex-list :== NUMBER | + * NUMBER COLON colon-seperated-hex-list + */ + +int parse_cshl (data, cfile) + struct data_string *data; + struct parse *cfile; +{ + u_int8_t ibuf [128]; + unsigned ilen = 0; + unsigned tlen = 0; + struct option_tag *sl = (struct option_tag *)0; + struct option_tag *next, **last = &sl; + enum dhcp_token token; + const char *val; + unsigned char *rvp; + + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER && token != NUMBER_OR_NAME) { + parse_warn (cfile, "expecting hexadecimal number."); + skip_to_semi (cfile); + for (; sl; sl = next) { + next = sl -> next; + dfree (sl, MDL); + } + return 0; + } + if (ilen == sizeof ibuf) { + next = (struct option_tag *) + dmalloc (ilen - 1 + + sizeof (struct option_tag), MDL); + if (!next) + log_fatal ("no memory for string list."); + memcpy (next -> data, ibuf, ilen); + *last = next; + last = &next -> next; + tlen += ilen; + ilen = 0; + } + convert_num (cfile, &ibuf [ilen++], val, 16, 8); + + token = peek_token (&val, (unsigned *)0, cfile); + if (token != COLON) + break; + token = next_token (&val, (unsigned *)0, cfile); + } while (1); + + if (!buffer_allocate (&data -> buffer, tlen + ilen, MDL)) + log_fatal ("no memory to store octet data."); + data -> data = &data -> buffer -> data [0]; + data -> len = tlen + ilen; + data -> terminated = 0; + + rvp = &data -> buffer -> data [0]; + while (sl) { + next = sl -> next; + memcpy (rvp, sl -> data, sizeof ibuf); + rvp += sizeof ibuf; + dfree (sl, MDL); + sl = next; + } + + memcpy (rvp, ibuf, ilen); + return 1; +} + +/* + * executable-statements :== executable-statement executable-statements | + * executable-statement + * + * executable-statement :== + * IF if-statement | + * ADD class-name SEMI | + * BREAK SEMI | + * OPTION option-parameter SEMI | + * SUPERSEDE option-parameter SEMI | + * PREPEND option-parameter SEMI | + * APPEND option-parameter SEMI + */ + +int parse_executable_statements (statements, cfile, lose, case_context) + struct executable_statement **statements; + struct parse *cfile; + int *lose; + enum expression_context case_context; +{ + struct executable_statement **next; + + next = statements; + while (parse_executable_statement (next, cfile, lose, case_context)) + next = &((*next) -> next); + if (!*lose) + return 1; + return 0; +} + +int parse_executable_statement (result, cfile, lose, case_context) + struct executable_statement **result; + struct parse *cfile; + int *lose; + enum expression_context case_context; +{ + enum dhcp_token token; + const char *val; + struct executable_statement base; + struct class *cta; + struct option *option; + struct option_cache *cache; + int known; + int flag; + int i; + struct dns_zone *zone; + isc_result_t status; + char *s; + + token = peek_token (&val, (unsigned *)0, cfile); + switch (token) { + case IF: + next_token (&val, (unsigned *)0, cfile); + return parse_if_statement (result, cfile, lose); + + case TOKEN_ADD: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "expecting class name."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + cta = (struct class *)0; + status = find_class (&cta, val, MDL); + if (status != ISC_R_SUCCESS) { + parse_warn (cfile, "class %s: %s", + val, isc_result_totext (status)); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + return 0; + } + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = add_statement; + (*result) -> data.add = cta; + break; + + case BREAK: + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_semi (cfile)) { + *lose = 1; + return 0; + } + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = break_statement; + break; + + case SEND: + token = next_token (&val, (unsigned *)0, cfile); + known = 0; + option = parse_option_name (cfile, 0, &known); + if (!option) { + *lose = 1; + return 0; + } + return parse_option_statement (result, cfile, 1, option, + send_option_statement); + + case SUPERSEDE: + case OPTION: + token = next_token (&val, (unsigned *)0, cfile); + known = 0; + option = parse_option_name (cfile, 0, &known); + if (!option) { + *lose = 1; + return 0; + } + return parse_option_statement (result, cfile, 1, option, + supersede_option_statement); + + case ALLOW: + flag = 1; + goto pad; + case DENY: + flag = 0; + goto pad; + case IGNORE: + flag = 2; + pad: + token = next_token (&val, (unsigned *)0, cfile); + cache = (struct option_cache *)0; + if (!parse_allow_deny (&cache, cfile, flag)) + return 0; + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = supersede_option_statement; + (*result) -> data.option = cache; + break; + + case DEFAULT: + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == COLON) + goto switch_default; + known = 0; + option = parse_option_name (cfile, 0, &known); + if (!option) { + *lose = 1; + return 0; + } + return parse_option_statement (result, cfile, 1, option, + default_option_statement); + + case PREPEND: + token = next_token (&val, (unsigned *)0, cfile); + known = 0; + option = parse_option_name (cfile, 0, &known); + if (!option) { + *lose = 1; + return 0; + } + return parse_option_statement (result, cfile, 1, option, + prepend_option_statement); + + case APPEND: + token = next_token (&val, (unsigned *)0, cfile); + known = 0; + option = parse_option_name (cfile, 0, &known); + if (!option) { + *lose = 1; + return 0; + } + return parse_option_statement (result, cfile, 1, option, + append_option_statement); + + case ON: + token = next_token (&val, (unsigned *)0, cfile); + return parse_on_statement (result, cfile, lose); + + case SWITCH: + token = next_token (&val, (unsigned *)0, cfile); + return parse_switch_statement (result, cfile, lose); + + case CASE: + token = next_token (&val, (unsigned *)0, cfile); + if (case_context == context_any) { + parse_warn (cfile, + "case statement in inappropriate scope."); + *lose = 1; + skip_to_semi (cfile); + return 0; + } + return parse_case_statement (result, + cfile, lose, case_context); + + switch_default: + token = next_token (&val, (unsigned *)0, cfile); + if (case_context == context_any) { + parse_warn (cfile, "switch default statement in %s", + "inappropriate scope."); + + *lose = 1; + return 0; + } else { + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for default statement."); + (*result) -> op = default_statement; + return 1; + } + + case DEFINE: + case TOKEN_SET: + token = next_token (&val, (unsigned *)0, cfile); + if (token == DEFINE) + flag = 1; + else + flag = 0; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NAME && token != NUMBER_OR_NAME) { + parse_warn (cfile, + "%s can't be a variable name", val); + badset: + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for set statement."); + (*result) -> op = flag ? define_statement : set_statement; + (*result) -> data.set.name = dmalloc (strlen (val) + 1, MDL); + if (!(*result)->data.set.name) + log_fatal ("can't allocate variable name"); + strcpy ((*result) -> data.set.name, val); + token = next_token (&val, (unsigned *)0, cfile); + + if (token == LPAREN) { + struct string_list *head, *cur, *new; + struct expression *expr; + head = cur = (struct string_list *)0; + do { + token = next_token (&val, + (unsigned *)0, cfile); + if (token == RPAREN) + break; + if (token != NAME && token != NUMBER_OR_NAME) { + parse_warn (cfile, + "expecting argument name"); + skip_to_rbrace (cfile, 0); + *lose = 1; + executable_statement_dereference + (result, MDL); + return 0; + } + new = ((struct string_list *) + dmalloc (sizeof (struct string_list) + + strlen (val), MDL)); + if (!new) + log_fatal ("can't allocate string."); + memset (new, 0, sizeof *new); + strcpy (new -> string, val); + if (cur) { + cur -> next = new; + cur = new; + } else { + head = cur = new; + } + token = next_token (&val, + (unsigned *)0, cfile); + } while (token == COMMA); + + if (token != RPAREN) { + parse_warn (cfile, "expecting right paren."); + badx: + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace."); + goto badx; + } + + expr = (struct expression *)0; + if (!(expression_allocate (&expr, MDL))) + log_fatal ("can't allocate expression."); + expr -> op = expr_function; + if (!fundef_allocate (&expr -> data.func, MDL)) + log_fatal ("can't allocate fundef."); + expr -> data.func -> args = head; + (*result) -> data.set.expr = expr; + + if (!(parse_executable_statements + (&expr -> data.func -> statements, cfile, lose, + case_context))) { + if (*lose) + goto badx; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "expecting rigt brace."); + goto badx; + } + } else { + if (token != EQUAL) { + parse_warn (cfile, + "expecting '=' in %s statement.", + flag ? "define" : "set"); + goto badset; + } + + if (!parse_expression (&(*result) -> data.set.expr, + cfile, lose, context_any, + (struct expression **)0, + expr_none)) { + if (!*lose) + parse_warn (cfile, + "expecting expression."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + } + break; + + case UNSET: + token = next_token (&val, (unsigned *)0, cfile); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NAME && token != NUMBER_OR_NAME) { + parse_warn (cfile, + "%s can't be a variable name", val); + badunset: + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for set statement."); + (*result) -> op = unset_statement; + (*result) -> data.unset = dmalloc (strlen (val) + 1, MDL); + if (!(*result)->data.unset) + log_fatal ("can't allocate variable name"); + strcpy ((*result) -> data.unset, val); + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + break; + + case EVAL: + token = next_token (&val, (unsigned *)0, cfile); + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for eval statement."); + (*result) -> op = eval_statement; + + if (!parse_expression (&(*result) -> data.eval, + cfile, lose, context_data, /* XXX */ + (struct expression **)0, expr_none)) { + if (!*lose) + parse_warn (cfile, + "expecting data expression."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + } + break; + + case RETURN: + token = next_token (&val, (unsigned *)0, cfile); + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for return statement."); + (*result) -> op = return_statement; + + if (!parse_expression (&(*result) -> data.retval, + cfile, lose, context_data, + (struct expression **)0, expr_none)) { + if (!*lose) + parse_warn (cfile, + "expecting data expression."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + break; + + case LOG: + token = next_token (&val, (unsigned *)0, cfile); + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for log statement."); + (*result) -> op = log_statement; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + token = peek_token (&val, (unsigned *)0, cfile); + i = 1; + if (token == FATAL) { + (*result) -> data.log.priority = log_priority_fatal; + } else if (token == ERROR) { + (*result) -> data.log.priority = log_priority_error; + } else if (token == TOKEN_DEBUG) { + (*result) -> data.log.priority = log_priority_debug; + } else if (token == INFO) { + (*result) -> data.log.priority = log_priority_info; + } else { + (*result) -> data.log.priority = log_priority_debug; + i = 0; + } + if (i) { + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + parse_warn (cfile, "comma expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + } + + if (!(parse_data_expression + (&(*result) -> data.log.expr, cfile, lose))) { + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != SEMI) { + parse_warn (cfile, "semicolon expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + break; + + /* Not really a statement, but we parse it here anyway + because it's appropriate for all DHCP agents with + parsers. */ + case ZONE: + token = next_token (&val, (unsigned *)0, cfile); + zone = (struct dns_zone *)0; + if (!dns_zone_allocate (&zone, MDL)) + log_fatal ("no memory for new zone."); + zone -> name = parse_host_name (cfile); + if (!zone -> name) { + parse_warn (cfile, "expecting hostname."); + badzone: + *lose = 1; + skip_to_semi (cfile); + dns_zone_dereference (&zone, MDL); + return 0; + } + i = strlen (zone -> name); + if (zone -> name [i - 1] != '.') { + s = dmalloc ((unsigned)i + 2, MDL); + if (!s) { + parse_warn (cfile, "no trailing '.' on zone"); + goto badzone; + } + strcpy (s, zone -> name); + s [i] = '.'; + s [i + 1] = 0; + dfree (zone -> name, MDL); + zone -> name = s; + } + if (!parse_zone (zone, cfile)) + goto badzone; + status = enter_dns_zone (zone); + if (status != ISC_R_SUCCESS) { + parse_warn (cfile, "dns zone key %s: %s", + zone -> name, isc_result_totext (status)); + dns_zone_dereference (&zone, MDL); + return 0; + } + dns_zone_dereference (&zone, MDL); + return 1; + + /* Also not really a statement, but same idea as above. */ + case KEY: + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_key (cfile)) { + *lose = 1; + return 0; + } + return 1; + + default: + if (config_universe && is_identifier (token)) { + option = (struct option *)0; + option_hash_lookup (&option, config_universe -> hash, + val, 0, MDL); + if (option) { + token = next_token (&val, + (unsigned *)0, cfile); + return parse_option_statement + (result, cfile, 1, option, + supersede_option_statement); + } + } + + if (token == NUMBER_OR_NAME || token == NAME) { + /* This is rather ugly. Since function calls are + data expressions, fake up an eval statement. */ + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for eval statement."); + (*result) -> op = eval_statement; + + if (!parse_expression (&(*result) -> data.eval, + cfile, lose, context_data, + (struct expression **)0, + expr_none)) { + if (!*lose) + parse_warn (cfile, "expecting " + "function call."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + break; + } + + *lose = 0; + return 0; + } + + return 1; +} + +/* zone-statements :== zone-statement | + zone-statement zone-statements + zone-statement :== + PRIMARY ip-addresses SEMI | + SECONDARY ip-addresses SEMI | + key-reference SEMI + ip-addresses :== ip-addr-or-hostname | + ip-addr-or-hostname COMMA ip-addresses + key-reference :== KEY STRING | + KEY identifier */ + +int parse_zone (struct dns_zone *zone, struct parse *cfile) +{ + int token; + const char *val; + char *key_name; + struct option_cache *oc; + int done = 0; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace"); + return 0; + } + + do { + token = peek_token (&val, (unsigned *)0, cfile); + switch (token) { + case PRIMARY: + if (zone -> primary) { + parse_warn (cfile, + "more than one primary."); + skip_to_semi (cfile); + return 0; + } + if (!option_cache_allocate (&zone -> primary, MDL)) + log_fatal ("can't allocate primary option cache."); + oc = zone -> primary; + goto consemup; + + case SECONDARY: + if (zone -> secondary) { + parse_warn (cfile, "more than one secondary."); + skip_to_semi (cfile); + return 0; + } + if (!option_cache_allocate (&zone -> secondary, MDL)) + log_fatal ("can't allocate secondary."); + oc = zone -> secondary; + consemup: + token = next_token (&val, (unsigned *)0, cfile); + do { + struct expression *expr = (struct expression *)0; + if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) { + parse_warn (cfile, + "expecting IP addr or hostname."); + skip_to_semi (cfile); + return 0; + } + if (oc -> expression) { + struct expression *old = + (struct expression *)0; + expression_reference (&old, + oc -> expression, + MDL); + expression_dereference (&oc -> expression, + MDL); + if (!make_concat (&oc -> expression, + old, expr)) + log_fatal ("no memory for concat."); + expression_dereference (&expr, MDL); + expression_dereference (&old, MDL); + } else { + expression_reference (&oc -> expression, + expr, MDL); + expression_dereference (&expr, MDL); + } + token = next_token (&val, (unsigned *)0, cfile); + } while (token == COMMA); + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + return 0; + } + break; + + case KEY: + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == STRING) { + token = next_token (&val, (unsigned *)0, cfile); + key_name = (char *)0; + } else { + key_name = parse_host_name (cfile); + if (!key_name) { + parse_warn (cfile, "expecting key name."); + skip_to_semi (cfile); + return 0; + } + val = key_name; + } + if (omapi_auth_key_lookup_name (&zone -> key, val) != + ISC_R_SUCCESS) + parse_warn (cfile, "unknown key %s", val); + if (key_name) + dfree (key_name, MDL); + if (!parse_semi (cfile)) + return 0; + break; + + default: + done = 1; + break; + } + } while (!done); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "expecting right brace."); + return 0; + } + return 1; +} + +/* key-statements :== key-statement | + key-statement key-statements + key-statement :== + ALGORITHM host-name SEMI | + secret-definition SEMI + secret-definition :== SECRET base64val | + SECRET STRING */ + +int parse_key (struct parse *cfile) +{ + int token; + const char *val; + int done = 0; + struct auth_key *key; + struct data_string ds; + isc_result_t status; + char *s; + + key = (struct auth_key *)0; + if (omapi_auth_key_new (&key, MDL) != ISC_R_SUCCESS) + log_fatal ("no memory for key"); + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == STRING) { + token = next_token (&val, (unsigned *)0, cfile); + key -> name = dmalloc (strlen (val) + 1, MDL); + if (!key -> name) + log_fatal ("no memory for key name."); + strcpy (key -> name, val); + + } else { + key -> name = parse_host_name (cfile); + if (!key -> name) { + parse_warn (cfile, "expecting key name."); + skip_to_semi (cfile); + goto bad; + } + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace"); + goto bad; + } + + do { + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + case ALGORITHM: + if (key -> algorithm) { + parse_warn (cfile, + "key %s: too many algorithms", + key -> name); + goto rbad; + } + key -> algorithm = parse_host_name (cfile); + if (!key -> algorithm) { + parse_warn (cfile, + "expecting key algorithm name."); + goto rbad; + } + if (!parse_semi (cfile)) + goto rbad; + /* If the algorithm name isn't an FQDN, tack on + the .SIG-ALG.REG.NET. domain. */ + s = strrchr (key -> algorithm, '.'); + if (!s) { + static char add [] = ".SIG-ALG.REG.INT."; + s = dmalloc (strlen (key -> algorithm) + + sizeof (add), MDL); + if (!s) { + log_error ("no memory for key %s.", + "algorithm"); + goto rbad; + } + strcpy (s, key -> algorithm); + strcat (s, add); + dfree (key -> algorithm, MDL); + key -> algorithm = s; + } else if (s [1]) { + /* If there is no trailing '.', hack one in. */ + s = dmalloc (strlen (key -> algorithm) + 2, MDL); + if (!s) { + log_error ("no memory for key %s.", + key -> algorithm); + goto rbad; + } + strcpy (s, key -> algorithm); + strcat (s, "."); + dfree (key -> algorithm, MDL); + key -> algorithm = s; + } + break; + + case SECRET: + if (key -> key) { + parse_warn (cfile, "key %s: too many secrets", + key -> name); + goto rbad; + } + + memset (&ds, 0, sizeof(ds)); + if (!parse_base64 (&ds, cfile)) + goto rbad; + status = omapi_data_string_new (&key -> key, ds.len, + MDL); + if (status != ISC_R_SUCCESS) + goto rbad; + memcpy (key -> key -> value, + ds.buffer -> data, ds.len); + data_string_forget (&ds, MDL); + + if (!parse_semi (cfile)) + goto rbad; + break; + + default: + done = 1; + break; + } + } while (!done); + if (token != RBRACE) { + parse_warn (cfile, "expecting right brace."); + goto rbad; + } + /* Allow the BIND 8 syntax, which has a semicolon after each + closing brace. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SEMI) + token = next_token (&val, (unsigned *)0, cfile); + + /* Remember the key. */ + status = omapi_auth_key_enter (key); + if (status != ISC_R_SUCCESS) { + parse_warn (cfile, "tsig key %s: %s", + key -> name, isc_result_totext (status)); + goto bad; + } + omapi_auth_key_dereference (&key, MDL); + return 1; + + rbad: + skip_to_rbrace (cfile, 1); + bad: + omapi_auth_key_dereference (&key, MDL); + return 0; +} + +/* + * on-statement :== event-types LBRACE executable-statements RBRACE + * event-types :== event-type OR event-types | + * event-type + * event-type :== EXPIRY | COMMIT | RELEASE + */ + +int parse_on_statement (result, cfile, lose) + struct executable_statement **result; + struct parse *cfile; + int *lose; +{ + enum dhcp_token token; + const char *val; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = on_statement; + + do { + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + case EXPIRY: + (*result) -> data.on.evtypes |= ON_EXPIRY; + break; + + case COMMIT: + (*result) -> data.on.evtypes |= ON_COMMIT; + break; + + case RELEASE: + (*result) -> data.on.evtypes |= ON_RELEASE; + break; + + case TRANSMISSION: + (*result) -> data.on.evtypes |= ON_TRANSMISSION; + break; + + default: + parse_warn (cfile, "expecting a lease event type"); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + } while (token == OR); + + /* Semicolon means no statements. */ + if (token == SEMI) + return 1; + + if (token != LBRACE) { + parse_warn (cfile, "left brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_executable_statements (&(*result) -> data.on.statements, + cfile, lose, context_any)) { + if (*lose) { + /* Try to even things up. */ + do { + token = next_token (&val, + (unsigned *)0, cfile); + } while (token != END_OF_FILE && token != RBRACE); + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + return 1; +} + +/* + * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE + * + */ + +int parse_switch_statement (result, cfile, lose) + struct executable_statement **result; + struct parse *cfile; + int *lose; +{ + enum dhcp_token token; + const char *val; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = switch_statement; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "expecting left brace."); + pfui: + *lose = 1; + skip_to_semi (cfile); + gnorf: + executable_statement_dereference (result, MDL); + return 0; + } + + if (!parse_expression (&(*result) -> data.s_switch.expr, + cfile, lose, context_data_or_numeric, + (struct expression **)0, expr_none)) { + if (!*lose) { + parse_warn (cfile, + "expecting data or numeric expression."); + goto pfui; + } + goto gnorf; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right paren expected."); + goto pfui; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "left brace expected."); + goto pfui; + } + if (!(parse_executable_statements + (&(*result) -> data.s_switch.statements, cfile, lose, + (is_data_expression ((*result) -> data.s_switch.expr) + ? context_data : context_numeric)))) { + if (*lose) { + skip_to_rbrace (cfile, 1); + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + goto pfui; + } + return 1; +} + +/* + * case-statement :== CASE expr COLON + * + */ + +int parse_case_statement (result, cfile, lose, case_context) + struct executable_statement **result; + struct parse *cfile; + int *lose; + enum expression_context case_context; +{ + enum dhcp_token token; + const char *val; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = case_statement; + + if (!parse_expression (&(*result) -> data.c_case, + cfile, lose, case_context, + (struct expression **)0, expr_none)) + { + if (!*lose) { + parse_warn (cfile, "expecting %s expression.", + (case_context == context_data + ? "data" : "numeric")); + } + pfui: + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COLON) { + parse_warn (cfile, "colon expected."); + goto pfui; + } + return 1; +} + +/* + * if-statement :== boolean-expression LBRACE executable-statements RBRACE + * else-statement + * + * else-statement :== | + * ELSE LBRACE executable-statements RBRACE | + * ELSE IF if-statement | + * ELSIF if-statement + */ + +int parse_if_statement (result, cfile, lose) + struct executable_statement **result; + struct parse *cfile; + int *lose; +{ + enum dhcp_token token; + const char *val; + int parenp; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for if statement."); + + (*result) -> op = if_statement; + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == LPAREN) { + parenp = 1; + next_token (&val, (unsigned *)0, cfile); + } else + parenp = 0; + + + if (!parse_boolean_expression (&(*result) -> data.ie.expr, + cfile, lose)) { + if (!*lose) + parse_warn (cfile, "boolean expression expected."); + executable_statement_dereference (result, MDL); + *lose = 1; + return 0; + } +#if defined (DEBUG_EXPRESSION_PARSE) + print_expression ("if condition", (*result) -> data.ie.expr); +#endif + if (parenp) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "expecting right paren."); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "left brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_executable_statements (&(*result) -> data.ie.tc, + cfile, lose, context_any)) { + if (*lose) { + /* Try to even things up. */ + do { + token = next_token (&val, + (unsigned *)0, cfile); + } while (token != END_OF_FILE && token != RBRACE); + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + token = peek_token (&val, (unsigned *)0, cfile); + if (token == ELSE) { + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == IF) { + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_if_statement (&(*result) -> data.ie.fc, + cfile, lose)) { + if (!*lose) + parse_warn (cfile, + "expecting if statement"); + executable_statement_dereference (result, MDL); + *lose = 1; + return 0; + } + } else if (token != LBRACE) { + parse_warn (cfile, "left brace or if expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } else { + token = next_token (&val, (unsigned *)0, cfile); + if (!(parse_executable_statements + (&(*result) -> data.ie.fc, + cfile, lose, context_any))) { + executable_statement_dereference (result, MDL); + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + } + } else if (token == ELSIF) { + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_if_statement (&(*result) -> data.ie.fc, + cfile, lose)) { + if (!*lose) + parse_warn (cfile, + "expecting conditional."); + executable_statement_dereference (result, MDL); + *lose = 1; + return 0; + } + } else + (*result) -> data.ie.fc = (struct executable_statement *)0; + + return 1; +} + +/* + * boolean_expression :== CHECK STRING | + * NOT boolean-expression | + * data-expression EQUAL data-expression | + * data-expression BANG EQUAL data-expression | + * boolean-expression AND boolean-expression | + * boolean-expression OR boolean-expression + * EXISTS OPTION-NAME + */ + +int parse_boolean_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_boolean, + (struct expression **)0, expr_none)) + return 0; + + if (!is_boolean_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + parse_warn (cfile, "Expecting a boolean expression."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + return 1; +} + +/* + * data_expression :== SUBSTRING LPAREN data-expression COMMA + * numeric-expression COMMA + * numeric-expression RPAREN | + * CONCAT LPAREN data-expression COMMA + data-expression RPAREN + * SUFFIX LPAREN data_expression COMMA + * numeric-expression RPAREN | + * OPTION option_name | + * HARDWARE | + * PACKET LPAREN numeric-expression COMMA + * numeric-expression RPAREN | + * STRING | + * colon_seperated_hex_list + */ + +int parse_data_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_data, + (struct expression **)0, expr_none)) + return 0; + + if (!is_data_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + expression_dereference (expr, MDL); + parse_warn (cfile, "Expecting a data expression."); + *lose = 1; + return 0; + } + return 1; +} + +/* + * numeric-expression :== EXTRACT_INT LPAREN data-expression + * COMMA number RPAREN | + * NUMBER + */ + +int parse_numeric_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_numeric, + (struct expression **)0, expr_none)) + return 0; + + if (!is_numeric_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + expression_dereference (expr, MDL); + parse_warn (cfile, "Expecting a numeric expression."); + *lose = 1; + return 0; + } + return 1; +} + +/* + * dns-expression :== + * UPDATE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression COMMA numeric-expression RPAREN + * DELETE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression RPAREN + * EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression RPAREN + * NOT EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression RPAREN + * ns-class :== IN | CHAOS | HS | NUMBER + * ns-type :== A | PTR | MX | TXT | NUMBER + */ + +int parse_dns_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_dns, + (struct expression **)0, expr_none)) + return 0; + + if (!is_dns_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + expression_dereference (expr, MDL); + parse_warn (cfile, "Expecting a dns update subexpression."); + *lose = 1; + return 0; + } + return 1; +} + +/* Parse a subexpression that does not contain a binary operator. */ + +int parse_non_binary (expr, cfile, lose, context) + struct expression **expr; + struct parse *cfile; + int *lose; + enum expression_context context; +{ + enum dhcp_token token; + const char *val; + struct collection *col; + struct option *option; + struct expression *nexp, **ep; + int known; + enum expr_op opcode; + const char *s; + char *cptr; + struct executable_statement *stmt; + int i; + unsigned long u; + isc_result_t status, code; + unsigned len; + + token = peek_token (&val, (unsigned *)0, cfile); + + /* Check for unary operators... */ + switch (token) { + case CHECK: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "string expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + for (col = collections; col; col = col -> next) + if (!strcmp (col -> name, val)) + break; + if (!col) { + parse_warn (cfile, "unknown collection."); + *lose = 1; + return 0; + } + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_check; + (*expr) -> data.check = col; + break; + + case TOKEN_NOT: + token = next_token (&val, (unsigned *)0, cfile); + if (context == context_dns) { + token = peek_token (&val, (unsigned *)0, cfile); + goto not_exists; + } + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_not; + if (!parse_non_binary (&(*expr) -> data.not, + cfile, lose, context_boolean)) { + if (!*lose) { + parse_warn (cfile, "expression expected"); + skip_to_semi (cfile); + } + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + if (!is_boolean_expression ((*expr) -> data.not)) { + *lose = 1; + parse_warn (cfile, "boolean expression expected"); + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + break; + + case LPAREN: + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_expression (expr, cfile, lose, context, + (struct expression **)0, expr_none)) { + if (!*lose) { + parse_warn (cfile, "expression expected"); + skip_to_semi (cfile); + } + *lose = 1; + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + *lose = 1; + parse_warn (cfile, "right paren expected"); + skip_to_semi (cfile); + return 0; + } + break; + + case EXISTS: + if (context == context_dns) + goto ns_exists; + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_exists; + known = 0; + (*expr) -> data.option = parse_option_name (cfile, 0, &known); + if (!(*expr) -> data.option) { + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case STATIC: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_static; + break; + + case KNOWN: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_known; + break; + + case SUBSTRING: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_substring; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + nolparen: + expression_dereference (expr, MDL); + parse_warn (cfile, "left parenthesis expected."); + *lose = 1; + return 0; + } + + if (!parse_data_expression (&(*expr) -> data.substring.expr, + cfile, lose)) { + nodata: + expression_dereference (expr, MDL); + if (!*lose) { + parse_warn (cfile, + "expecting data expression."); + skip_to_semi (cfile); + *lose = 1; + } + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + nocomma: + expression_dereference (expr, MDL); + parse_warn (cfile, "comma expected."); + *lose = 1; + + return 0; + } + + if (!parse_numeric_expression + (&(*expr) -> data.substring.offset,cfile, lose)) { + nonum: + if (!*lose) { + parse_warn (cfile, + "expecting numeric expression."); + skip_to_semi (cfile); + *lose = 1; + } + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression + (&(*expr) -> data.substring.len, cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + norparen: + parse_warn (cfile, "right parenthesis expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case SUFFIX: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_suffix; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_data_expression (&(*expr) -> data.suffix.expr, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression (&(*expr) -> data.suffix.len, + cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case CONCAT: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_concat; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_data_expression (&(*expr) -> data.concat [0], + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + concat_another: + if (!parse_data_expression (&(*expr) -> data.concat [1], + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + + if (token == COMMA) { + nexp = (struct expression *)0; + if (!expression_allocate (&nexp, MDL)) + log_fatal ("can't allocate at CONCAT2"); + nexp -> op = expr_concat; + expression_reference (&nexp -> data.concat [0], + *expr, MDL); + expression_dereference (expr, MDL); + expression_reference (expr, nexp, MDL); + expression_dereference (&nexp, MDL); + goto concat_another; + } + + if (token != RPAREN) + goto norparen; + break; + + case BINARY_TO_ASCII: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_binary_to_ascii; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_numeric_expression (&(*expr) -> data.b2a.base, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression (&(*expr) -> data.b2a.width, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_data_expression (&(*expr) -> data.b2a.seperator, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_data_expression (&(*expr) -> data.b2a.buffer, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case REVERSE: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_reverse; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!(parse_numeric_expression + (&(*expr) -> data.reverse.width, cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_data_expression + (&(*expr) -> data.reverse.buffer, cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case PICK: + /* pick (a, b, c) actually produces an internal representation + that looks like pick (a, pick (b, pick (c, nil))). */ + token = next_token (&val, (unsigned *)0, cfile); + if (!(expression_allocate (expr, MDL))) + log_fatal ("can't allocate expression"); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + nexp = (struct expression *)0; + expression_reference (&nexp, *expr, MDL); + do { + nexp -> op = expr_pick_first_value; + if (!(parse_data_expression + (&nexp -> data.pick_first_value.car, + cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token == COMMA) { + struct expression *foo = (struct expression *)0; + if (!expression_allocate (&foo, MDL)) + log_fatal ("can't allocate expr"); + expression_reference + (&nexp -> data.pick_first_value.cdr, foo, MDL); + expression_dereference (&nexp, MDL); + expression_reference (&nexp, foo, MDL); + expression_dereference (&foo, MDL); + } + } while (token == COMMA); + expression_dereference (&nexp, MDL); + + if (token != RPAREN) + goto norparen; + break; + + /* dns-update and dns-delete are present for historical + purposes, but are deprecated in favor of ns-update + in combination with update, delete, exists and not + exists. */ + case DNS_UPDATE: + case DNS_DELETE: +#if !defined (NSUPDATE) + parse_warn (cfile, + "Please rebuild dhcpd with --with-nsupdate."); +#endif + token = next_token (&val, (unsigned *)0, cfile); + if (token == DNS_UPDATE) + opcode = expr_ns_add; + else + opcode = expr_ns_delete; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, + "parse_expression: expecting string."); + badnsupdate: + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!strcasecmp (val, "a")) + u = T_A; + else if (!strcasecmp (val, "ptr")) + u = T_PTR; + else if (!strcasecmp (val, "mx")) + u = T_MX; + else if (!strcasecmp (val, "cname")) + u = T_CNAME; + else if (!strcasecmp (val, "TXT")) + u = T_TXT; + else { + parse_warn (cfile, "unexpected rrtype: %s", val); + goto badnsupdate; + } + + s = (opcode == expr_ns_add + ? "old-dns-update" + : "old-dns-delete"); + cptr = dmalloc (strlen (s) + 1, MDL); + if (!cptr) + log_fatal ("can't allocate name for %s", s); + strcpy (cptr, s); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_funcall; + (*expr) -> data.funcall.name = cptr; + + /* Fake up a function call. */ + ep = &(*expr) -> data.funcall.arglist; + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!make_const_int (&(*ep) -> data.arg.val, u)) + log_fatal ("can't allocate rrtype value."); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + ep = &((*ep) -> data.arg.next); + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!(parse_data_expression (&(*ep) -> data.arg.val, + cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + ep = &((*ep) -> data.arg.next); + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!(parse_data_expression (&(*ep) -> data.arg.val, + cfile, lose))) + goto nodata; + + if (opcode == expr_ns_add) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + ep = &((*ep) -> data.arg.next); + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!(parse_numeric_expression (&(*ep) -> data.arg.val, + cfile, lose))) { + parse_warn (cfile, + "expecting numeric expression."); + goto badnsupdate; + } + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case NS_UPDATE: +#if !defined (NSUPDATE) + parse_warn (cfile, + "Please rebuild dhcpd with --with-nsupdate."); +#endif + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + nexp = *expr; + do { + nexp -> op = expr_dns_transaction; + if (!(parse_dns_expression + (&nexp -> data.dns_transaction.car, + cfile, lose))) + { + if (!*lose) + parse_warn + (cfile, + "expecting dns expression."); + badnstrans: + expression_dereference (expr, MDL); + *lose = 1; + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + + if (token == COMMA) { + if (!(expression_allocate + (&nexp -> data.dns_transaction.cdr, + MDL))) + log_fatal + ("can't allocate expression"); + nexp = nexp -> data.dns_transaction.cdr; + } + } while (token == COMMA); + + if (token != RPAREN) + goto norparen; + break; + + /* NOT EXISTS is special cased above... */ + not_exists: + token = peek_token (&val, (unsigned *)0, cfile); + if (token != EXISTS) { + parse_warn (cfile, "expecting DNS prerequisite."); + *lose = 1; + return 0; + } + opcode = expr_ns_not_exists; + goto nsupdatecode; + case TOKEN_ADD: + opcode = expr_ns_add; + goto nsupdatecode; + case TOKEN_DELETE: + opcode = expr_ns_delete; + goto nsupdatecode; + ns_exists: + opcode = expr_ns_exists; + nsupdatecode: + token = next_token (&val, (unsigned *)0, cfile); + +#if !defined (NSUPDATE) + parse_warn (cfile, + "Please rebuild dhcpd with --with-nsupdate."); +#endif + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = opcode; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token) && token != NUMBER) { + parse_warn (cfile, "expecting identifier or number."); + badnsop: + expression_dereference (expr, MDL); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (token == NUMBER) + (*expr) -> data.ns_add.rrclass = atoi (val); + else if (!strcasecmp (val, "in")) + (*expr) -> data.ns_add.rrclass = C_IN; + else if (!strcasecmp (val, "chaos")) + (*expr) -> data.ns_add.rrclass = C_CHAOS; + else if (!strcasecmp (val, "hs")) + (*expr) -> data.ns_add.rrclass = C_HS; + else { + parse_warn (cfile, "unexpected rrclass: %s", val); + goto badnsop; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token) && token != NUMBER) { + parse_warn (cfile, "expecting identifier or number."); + goto badnsop; + } + + if (token == NUMBER) + (*expr) -> data.ns_add.rrtype = atoi (val); + else if (!strcasecmp (val, "a")) + (*expr) -> data.ns_add.rrtype = T_A; + else if (!strcasecmp (val, "ptr")) + (*expr) -> data.ns_add.rrtype = T_PTR; + else if (!strcasecmp (val, "mx")) + (*expr) -> data.ns_add.rrtype = T_MX; + else if (!strcasecmp (val, "cname")) + (*expr) -> data.ns_add.rrtype = T_CNAME; + else if (!strcasecmp (val, "TXT")) + (*expr) -> data.ns_add.rrtype = T_TXT; + else { + parse_warn (cfile, "unexpected rrtype: %s", val); + goto badnsop; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_data_expression + (&(*expr) -> data.ns_add.rrname, cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_data_expression + (&(*expr) -> data.ns_add.rrdata, cfile, lose))) + goto nodata; + + if (opcode == expr_ns_add) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_numeric_expression + (&(*expr) -> data.ns_add.ttl, cfile, + lose))) { + if (!*lose) + parse_warn (cfile, + "expecting numeric expression."); + goto badnsupdate; + } + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case OPTION: + case CONFIG_OPTION: + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = (token == OPTION + ? expr_option + : expr_config_option); + token = next_token (&val, (unsigned *)0, cfile); + known = 0; + (*expr) -> data.option = parse_option_name (cfile, 0, &known); + if (!(*expr) -> data.option) { + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case HARDWARE: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_hardware; + break; + + case LEASED_ADDRESS: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_leased_address; + break; + + case CLIENT_STATE: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_client_state; + break; + + case FILENAME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_filename; + break; + + case SERVER_NAME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_sname; + break; + + case LEASE_TIME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_lease_time; + break; + + case TOKEN_NULL: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_null; + break; + + case HOST_DECL_NAME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_host_decl_name; + break; + + case UPDATED_DNS_RR: + token = next_token (&val, (unsigned *)0, cfile); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "expecting string."); + bad_rrtype: + *lose = 1; + return 0; + } + if (!strcasecmp (val, "a")) + s = "ddns-fwd-name"; + else if (!strcasecmp (val, "ptr")) + s = "ddns-rev-name"; + else { + parse_warn (cfile, "invalid DNS rrtype: %s", val); + goto bad_rrtype; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_variable_reference; + (*expr) -> data.variable = + dmalloc (strlen (s) + 1, MDL); + if (!(*expr) -> data.variable) + log_fatal ("can't allocate variable name."); + strcpy ((*expr) -> data.variable, s); + break; + + case PACKET: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_packet; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_numeric_expression (&(*expr) -> data.packet.offset, + cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression (&(*expr) -> data.packet.len, + cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case STRING: + token = next_token (&val, &len, cfile); + if (!make_const_data (expr, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("can't make constant string expression."); + break; + + case EXTRACT_INT: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + *lose = 1; + return 0; + } + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + if (!parse_data_expression (&(*expr) -> data.extract_int, + cfile, lose)) { + if (!*lose) { + parse_warn (cfile, + "expecting data expression."); + skip_to_semi (cfile); + *lose = 1; + } + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + parse_warn (cfile, "comma expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "number expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + switch (atoi (val)) { + case 8: + (*expr) -> op = expr_extract_int8; + break; + + case 16: + (*expr) -> op = expr_extract_int16; + break; + + case 32: + (*expr) -> op = expr_extract_int32; + break; + + default: + parse_warn (cfile, + "unsupported integer size %d", atoi (val)); + *lose = 1; + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right parenthesis expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case ENCODE_INT: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + *lose = 1; + return 0; + } + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + if (!parse_numeric_expression (&(*expr) -> data.encode_int, + cfile, lose)) { + parse_warn (cfile, "expecting numeric expression."); + skip_to_semi (cfile); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + parse_warn (cfile, "comma expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "number expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + switch (atoi (val)) { + case 8: + (*expr) -> op = expr_encode_int8; + break; + + case 16: + (*expr) -> op = expr_encode_int16; + break; + + case 32: + (*expr) -> op = expr_encode_int32; + break; + + default: + parse_warn (cfile, + "unsupported integer size %d", atoi (val)); + *lose = 1; + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right parenthesis expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case NUMBER: + /* If we're in a numeric context, this should just be a + number, by itself. */ + if (context == context_numeric || + context == context_data_or_numeric) { + next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_const_int; + (*expr) -> data.const_int = atoi (val); + break; + } + + case NUMBER_OR_NAME: + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + (*expr) -> op = expr_const_data; + if (!parse_cshl (&(*expr) -> data.const_data, cfile)) { + expression_dereference (expr, MDL); + return 0; + } + break; + + case NS_FORMERR: + known = FORMERR; + goto ns_const; + ns_const: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_const_int; + (*expr) -> data.const_int = known; + break; + + case NS_NOERROR: + known = ISC_R_SUCCESS; + goto ns_const; + + case NS_NOTAUTH: + known = ISC_R_NOTAUTH; + goto ns_const; + + case NS_NOTIMP: + known = ISC_R_NOTIMPLEMENTED; + goto ns_const; + + case NS_NOTZONE: + known = ISC_R_NOTZONE; + goto ns_const; + + case NS_NXDOMAIN: + known = ISC_R_NXDOMAIN; + goto ns_const; + + case NS_NXRRSET: + known = ISC_R_NXRRSET; + goto ns_const; + + case NS_REFUSED: + known = ISC_R_REFUSED; + goto ns_const; + + case NS_SERVFAIL: + known = ISC_R_SERVFAIL; + goto ns_const; + + case NS_YXDOMAIN: + known = ISC_R_YXDOMAIN; + goto ns_const; + + case NS_YXRRSET: + known = ISC_R_YXRRSET; + goto ns_const; + + case BOOTING: + known = S_INIT; + goto ns_const; + + case REBOOT: + known = S_REBOOTING; + goto ns_const; + + case SELECT: + known = S_SELECTING; + goto ns_const; + + case REQUEST: + known = S_REQUESTING; + goto ns_const; + + case BOUND: + known = S_BOUND; + goto ns_const; + + case RENEW: + known = S_RENEWING; + goto ns_const; + + case REBIND: + known = S_REBINDING; + goto ns_const; + + case DEFINED: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NAME && token != NUMBER_OR_NAME) { + parse_warn (cfile, "%s can't be a variable name", val); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_variable_exists; + (*expr) -> data.variable = dmalloc (strlen (val) + 1, MDL); + if (!(*expr)->data.variable) + log_fatal ("can't allocate variable name"); + strcpy ((*expr) -> data.variable, val); + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + /* Not a valid start to an expression... */ + default: + if (token != NAME && token != NUMBER_OR_NAME) + return 0; + + token = next_token (&val, (unsigned *)0, cfile); + + /* Save the name of the variable being referenced. */ + cptr = dmalloc (strlen (val) + 1, MDL); + if (!cptr) + log_fatal ("can't allocate variable name"); + strcpy (cptr, val); + + /* Simple variable reference, as far as we can tell. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_variable_reference; + (*expr) -> data.variable = cptr; + break; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_funcall; + (*expr) -> data.funcall.name = cptr; + + /* Now parse the argument list. */ + ep = &(*expr) -> data.funcall.arglist; + do { + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!parse_expression (&(*ep) -> data.arg.val, + cfile, lose, context_any, + (struct expression **)0, + expr_none)) { + if (!*lose) { + parse_warn (cfile, + "expecting expression."); + *lose = 1; + } + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + ep = &((*ep) -> data.arg.next); + token = next_token (&val, (unsigned *)0, cfile); + } while (token == COMMA); + if (token != RPAREN) { + parse_warn (cfile, "Right parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + } + return 1; +} + +/* Parse an expression. */ + +int parse_expression (expr, cfile, lose, context, plhs, binop) + struct expression **expr; + struct parse *cfile; + int *lose; + enum expression_context context; + struct expression **plhs; + enum expr_op binop; +{ + enum dhcp_token token; + const char *val; + struct expression *rhs = (struct expression *)0, *tmp; + struct expression *lhs = (struct expression *)0; + enum expr_op next_op; + enum expression_context + lhs_context = context_any, + rhs_context = context_any; + + /* Consume the left hand side we were passed. */ + if (plhs) { + expression_reference (&lhs, *plhs, MDL); + expression_dereference (plhs, MDL); + } + + new_rhs: + if (!parse_non_binary (&rhs, cfile, lose, context)) { + /* If we already have a left-hand side, then it's not + okay for there not to be a right-hand side here, so + we need to flag it as an error. */ + if (lhs) { + if (!*lose) { + parse_warn (cfile, + "expecting right-hand side."); + *lose = 1; + skip_to_semi (cfile); + } + expression_dereference (&lhs, MDL); + } + return 0; + } + + /* At this point, rhs contains either an entire subexpression, + or at least a left-hand-side. If we do not see a binary token + as the next token, we're done with the expression. */ + + token = peek_token (&val, (unsigned *)0, cfile); + switch (token) { + case BANG: + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token != EQUAL) { + parse_warn (cfile, "! in boolean context without ="); + *lose = 1; + skip_to_semi (cfile); + if (lhs) + expression_dereference (&lhs, MDL); + return 0; + } + next_op = expr_not_equal; + context = expression_context (rhs); + break; + + case EQUAL: + next_op = expr_equal; + context = expression_context (rhs); + break; + + case AND: + next_op = expr_and; + context = expression_context (rhs); + break; + + case OR: + next_op = expr_or; + context = expression_context (rhs); + break; + + case PLUS: + next_op = expr_add; + context = expression_context (rhs); + break; + + case MINUS: + next_op = expr_subtract; + context = expression_context (rhs); + break; + + case SLASH: + next_op = expr_divide; + context = expression_context (rhs); + break; + + case ASTERISK: + next_op = expr_multiply; + context = expression_context (rhs); + break; + + case PERCENT: + next_op = expr_remainder; + context = expression_context (rhs); + break; + + case AMPERSAND: + next_op = expr_binary_and; + context = expression_context (rhs); + break; + + case PIPE: + next_op = expr_binary_or; + context = expression_context (rhs); + break; + + case CARET: + next_op = expr_binary_xor; + context = expression_context (rhs); + break; + + default: + next_op = expr_none; + } + + /* If we have no lhs yet, we just parsed it. */ + if (!lhs) { + /* If there was no operator following what we just parsed, + then we're done - return it. */ + if (next_op == expr_none) { + *expr = rhs; + return 1; + } + lhs = rhs; + rhs = (struct expression *)0; + binop = next_op; + next_token (&val, (unsigned *)0, cfile); + goto new_rhs; + } + + /* If the next binary operator is of greater precedence than the + * current operator, then rhs we have parsed so far is actually + * the lhs of the next operator. To get this value, we have to + * recurse. + */ + if (binop != expr_none && next_op != expr_none && + op_precedence (binop, next_op) < 0) { + + /* Eat the subexpression operator token, which we pass to + * parse_expression...we only peek()'d earlier. + */ + token = next_token (&val, (unsigned *)0, cfile); + + /* Continue parsing of the right hand side with that token. */ + tmp = rhs; + rhs = (struct expression *)0; + if (!parse_expression (&rhs, cfile, lose, op_context (next_op), + &tmp, next_op)) { + if (!*lose) { + parse_warn (cfile, + "expecting a subexpression"); + *lose = 1; + } + return 0; + } + next_op = expr_none; + } + + if (binop != expr_none) { + rhs_context = expression_context(rhs); + lhs_context = expression_context(lhs); + + if ((rhs_context != context_any) && (lhs_context != context_any) && + (rhs_context != lhs_context)) { + parse_warn (cfile, "illegal expression relating different types"); + skip_to_semi (cfile); + expression_dereference (&rhs, MDL); + expression_dereference (&lhs, MDL); + *lose = 1; + return 0; + } + + switch(binop) { + case expr_not_equal: + case expr_equal: + if ((rhs_context != context_data_or_numeric) && + (rhs_context != context_data) && + (rhs_context != context_numeric) && + (rhs_context != context_any)) { + parse_warn (cfile, "expecting data/numeric expression"); + skip_to_semi (cfile); + expression_dereference (&rhs, MDL); + *lose = 1; + return 0; + } + break; + + case expr_and: + case expr_or: + if ((rhs_context != context_boolean) && + (rhs_context != context_any)) { + parse_warn (cfile, "expecting boolean expressions"); + skip_to_semi (cfile); + expression_dereference (&rhs, MDL); + *lose = 1; + return 0; + } + break; + + case expr_add: + case expr_subtract: + case expr_divide: + case expr_multiply: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + if ((rhs_context != context_numeric) && + (rhs_context != context_any)) { + parse_warn (cfile, "expecting numeric expressions"); + skip_to_semi (cfile); + expression_dereference (&rhs, MDL); + *lose = 1; + return 0; + } + break; + + default: + break; + } + } + + /* Now, if we didn't find a binary operator, we're done parsing + this subexpression, so combine it with the preceding binary + operator and return the result. */ + if (next_op == expr_none) { + if (!expression_allocate (expr, MDL)) + log_fatal ("Can't allocate expression!"); + + (*expr) -> op = binop; + /* All the binary operators' data union members + are the same, so we'll cheat and use the member + for the equals operator. */ + (*expr) -> data.equal [0] = lhs; + (*expr) -> data.equal [1] = rhs; + return 1; + } + + /* Eat the operator token - we now know it was a binary operator... */ + token = next_token (&val, (unsigned *)0, cfile); + + /* Now combine the LHS and the RHS using binop. */ + tmp = (struct expression *)0; + if (!expression_allocate (&tmp, MDL)) + log_fatal ("No memory for equal precedence combination."); + + /* Store the LHS and RHS. */ + tmp -> data.equal [0] = lhs; + tmp -> data.equal [1] = rhs; + tmp -> op = binop; + + lhs = tmp; + tmp = (struct expression *)0; + rhs = (struct expression *)0; + + /* Recursions don't return until we have parsed the end of the + expression, so if we recursed earlier, we can now return what + we got. */ + if (next_op == expr_none) { + *expr = lhs; + return 1; + } + + binop = next_op; + goto new_rhs; +} + +/* option-statement :== identifier DOT identifier SEMI + | identifier SEMI + + Option syntax is handled specially through format strings, so it + would be painful to come up with BNF for it. However, it always + starts as above and ends in a SEMI. */ + +int parse_option_statement (result, cfile, lookups, option, op) + struct executable_statement **result; + struct parse *cfile; + int lookups; + struct option *option; + enum statement_op op; +{ + const char *val; + enum dhcp_token token; + const char *fmt = NULL; + struct expression *expr = (struct expression *)0; + struct expression *tmp; + int lose; + struct executable_statement *stmt; + int ftt = 1; + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SEMI) { + /* Eat the semicolon... */ + token = next_token (&val, (unsigned *)0, cfile); + goto done; + } + + if (token == EQUAL) { + /* Eat the equals sign. */ + token = next_token (&val, (unsigned *)0, cfile); + + /* Parse a data expression and use its value for the data. */ + if (!parse_data_expression (&expr, cfile, &lose)) { + /* In this context, we must have an executable + statement, so if we found something else, it's + still an error. */ + if (!lose) { + parse_warn (cfile, + "expecting a data expression."); + skip_to_semi (cfile); + } + return 0; + } + + /* We got a valid expression, so use it. */ + goto done; + } + + /* Parse the option data... */ + do { + /* Set a flag if this is an array of a simple type (i.e., + not an array of pairs of IP addresses, or something + like that. */ + int uniform = option -> format [1] == 'A'; + + and_again: + /* Set fmt to start of format for 'A' and one char back + for 'a' */ + if ((fmt != NULL) && + (fmt != option -> format) && (*fmt == 'a')) + fmt -= 1; + else + fmt = ((fmt == NULL) || + (*fmt == 'A')) ? option -> format : fmt; + + /* 'a' means always uniform */ + uniform |= (fmt [1] == 'a'); + + for ( ; *fmt; fmt++) { + if ((*fmt == 'A') || (*fmt == 'a')) + break; + if (*fmt == 'o') + continue; + tmp = expr; + expr = (struct expression *)0; + if (!parse_option_token (&expr, cfile, &fmt, + tmp, uniform, lookups)) { + if (fmt [1] != 'o') { + if (tmp) + expression_dereference (&tmp, + MDL); + return 0; + } + expr = tmp; + tmp = (struct expression *)0; + } + if (tmp) + expression_dereference (&tmp, MDL); + } + if ((*fmt == 'A') || (*fmt == 'a')) { + token = peek_token (&val, (unsigned *)0, cfile); + /* Comma means: continue with next element in array */ + if (token == COMMA) { + token = next_token (&val, + (unsigned *)0, cfile); + continue; + } + /* no comma: end of array. + 'A' or end of string means: leave the loop */ + if ((*fmt == 'A') || (fmt[1] == '\0')) + break; + /* 'a' means: go on with next char */ + if (*fmt == 'a') { + fmt++; + goto and_again; + } + } + } while ((*fmt == 'A') || (*fmt == 'a')); + + done: + if (!parse_semi (cfile)) + return 0; + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for option statement."); + (*result) -> op = op; + if (expr && !option_cache (&(*result) -> data.option, + (struct data_string *)0, expr, option, MDL)) + log_fatal ("no memory for option cache"); + if (expr) + expression_dereference (&expr, MDL); + return 1; +} + +int parse_option_token (rv, cfile, fmt, expr, uniform, lookups) + struct expression **rv; + struct parse *cfile; + const char **fmt; + struct expression *expr; + int uniform; + int lookups; +{ + const char *val; + enum dhcp_token token; + struct expression *t = (struct expression *)0; + unsigned char buf [4]; + unsigned len; + unsigned char *ob; + struct iaddr addr; + int num; + const char *f, *g; + struct enumeration_value *e; + + switch (**fmt) { + case 'U': + token = peek_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting identifier."); + skip_to_semi (cfile); + } + return 0; + } + token = next_token (&val, &len, cfile); + if (!make_const_data (&t, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("No memory for %s", val); + break; + + case 'E': + g = strchr (*fmt, '.'); + if (!g) { + parse_warn (cfile, + "malformed encapsulation format (bug!)"); + skip_to_semi (cfile); + return 0; + } + *fmt = g; + case 'X': + token = peek_token (&val, (unsigned *)0, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + if (!expression_allocate (&t, MDL)) + return 0; + if (!parse_cshl (&t -> data.const_data, cfile)) { + expression_dereference (&t, MDL); + return 0; + } + t -> op = expr_const_data; + } else if (token == STRING) { + token = next_token (&val, &len, cfile); + if (!make_const_data (&t, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("No memory for \"%s\"", val); + } else { + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting string %s.", + "or hexadecimal data"); + skip_to_semi (cfile); + } + return 0; + } + break; + + case 'd': /* Domain name... */ + val = parse_host_name (cfile); + if (!val) { + parse_warn (cfile, "not a valid domain name."); + skip_to_semi (cfile); + return 0; + } + len = strlen (val); + goto make_string; + + case 't': /* Text string... */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != STRING && !is_identifier (token)) { + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting string."); + if (token != SEMI) + skip_to_semi (cfile); + } + return 0; + } + token = next_token (&val, &len, cfile); + make_string: + if (!make_const_data (&t, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("No memory for concatenation"); + break; + + case 'N': + f = (*fmt) + 1; + g = strchr (*fmt, '.'); + if (!g) { + parse_warn (cfile, "malformed %s (bug!)", + "enumeration format"); + foo: + skip_to_semi (cfile); + return 0; + } + *fmt = g; + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "identifier expected"); + goto foo; + } + e = find_enumeration_value (f, (*fmt) - f, val); + if (!e) { + parse_warn (cfile, "unknown value"); + goto foo; + } + if (!make_const_data (&t, &e -> value, 1, 0, 1, MDL)) + return 0; + break; + + case 'I': /* IP address or hostname. */ + if (lookups) { + if (!parse_ip_addr_or_hostname (&t, cfile, uniform)) + return 0; + } else { + if (!parse_ip_addr (cfile, &addr)) + return 0; + if (!make_const_data (&t, addr.iabuf, addr.len, + 0, 1, MDL)) + return 0; + } + break; + + case 'T': /* Lease interval. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != INFINITE) + goto check_number; + token = next_token (&val, (unsigned *)0, cfile); + putLong (buf, -1); + if (!make_const_data (&t, buf, 4, 0, 1, MDL)) + return 0; + break; + + case 'L': /* Unsigned 32-bit integer... */ + case 'l': /* Signed 32-bit integer... */ + token = peek_token (&val, (unsigned *)0, cfile); + check_number: + if (token != NUMBER) { + need_number: + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting number."); + if (token != SEMI) + skip_to_semi (cfile); + } + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + convert_num (cfile, buf, val, 0, 32); + if (!make_const_data (&t, buf, 4, 0, 1, MDL)) + return 0; + break; + + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + token = next_token (&val, (unsigned *)0, cfile); + convert_num (cfile, buf, val, 0, 16); + if (!make_const_data (&t, buf, 2, 0, 1, MDL)) + return 0; + break; + + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + token = next_token (&val, (unsigned *)0, cfile); + convert_num (cfile, buf, val, 0, 8); + if (!make_const_data (&t, buf, 1, 0, 1, MDL)) + return 0; + break; + + case 'f': /* Boolean flag. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + if ((*fmt) [1] != 'o') + parse_warn (cfile, "expecting identifier."); + bad_flag: + if ((*fmt) [1] != 'o') { + if (token != SEMI) + skip_to_semi (cfile); + } + return 0; + } + if (!strcasecmp (val, "true") + || !strcasecmp (val, "on")) + buf [0] = 1; + else if (!strcasecmp (val, "false") + || !strcasecmp (val, "off")) + buf [0] = 0; + else if (!strcasecmp (val, "ignore")) + buf [0] = 2; + else { + if ((*fmt) [1] != 'o') + parse_warn (cfile, "expecting boolean."); + goto bad_flag; + } + token = next_token (&val, (unsigned *)0, cfile); + if (!make_const_data (&t, buf, 1, 0, 1, MDL)) + return 0; + break; + + default: + parse_warn (cfile, "Bad format %c in parse_option_token.", + **fmt); + skip_to_semi (cfile); + return 0; + } + if (expr) { + if (!make_concat (rv, expr, t)) + return 0; + } else + expression_reference (rv, t, MDL); + expression_dereference (&t, MDL); + return 1; +} + +int parse_option_decl (oc, cfile) + struct option_cache **oc; + struct parse *cfile; +{ + const char *val; + int token; + u_int8_t buf [4]; + u_int8_t hunkbuf [1024]; + unsigned hunkix = 0; + const char *fmt, *f; + struct option *option; + struct iaddr ip_addr; + u_int8_t *dp; + unsigned len; + int nul_term = 0; + struct buffer *bp; + int known = 0; + struct enumeration_value *e; + + option = parse_option_name (cfile, 0, &known); + if (!option) + return 0; + + /* Parse the option data... */ + do { + /* Set a flag if this is an array of a simple type (i.e., + not an array of pairs of IP addresses, or something + like that. */ + int uniform = option -> format [1] == 'A'; + + for (fmt = option -> format; *fmt; fmt++) { + if (*fmt == 'A') + break; + switch (*fmt) { + case 'E': + fmt = strchr (fmt, '.'); + if (!fmt) { + parse_warn (cfile, + "malformed %s (bug!)", + "encapsulation format"); + skip_to_semi (cfile); + return 0; + } + case 'X': + len = parse_X (cfile, &hunkbuf [hunkix], + sizeof hunkbuf - hunkix); + hunkix += len; + break; + + case 't': /* Text string... */ + token = next_token (&val, + &len, cfile); + if (token != STRING) { + parse_warn (cfile, + "expecting string."); + skip_to_semi (cfile); + return 0; + } + if (hunkix + len + 1 > sizeof hunkbuf) { + parse_warn (cfile, + "option data buffer %s", + "overflow"); + skip_to_semi (cfile); + return 0; + } + memcpy (&hunkbuf [hunkix], val, len + 1); + nul_term = 1; + hunkix += len; + break; + + case 'N': + f = fmt; + fmt = strchr (fmt, '.'); + if (!fmt) { + parse_warn (cfile, + "malformed %s (bug!)", + "enumeration format"); + foo: + skip_to_semi (cfile); + return 0; + } + token = next_token (&val, + (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "identifier expected"); + goto foo; + } + e = find_enumeration_value (f, fmt - f, val); + if (!e) { + parse_warn (cfile, + "unknown value"); + goto foo; + } + len = 1; + dp = &e -> value; + goto alloc; + + case 'I': /* IP address. */ + if (!parse_ip_addr (cfile, &ip_addr)) + return 0; + len = ip_addr.len; + dp = ip_addr.iabuf; + + alloc: + if (hunkix + len > sizeof hunkbuf) { + parse_warn (cfile, + "option data buffer %s", + "overflow"); + skip_to_semi (cfile); + return 0; + } + memcpy (&hunkbuf [hunkix], dp, len); + hunkix += len; + break; + + case 'L': /* Unsigned 32-bit integer... */ + case 'l': /* Signed 32-bit integer... */ + token = next_token (&val, + (unsigned *)0, cfile); + if (token != NUMBER) { + need_number: + parse_warn (cfile, + "expecting number."); + if (token != SEMI) + skip_to_semi (cfile); + return 0; + } + convert_num (cfile, buf, val, 0, 32); + len = 4; + dp = buf; + goto alloc; + + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = next_token (&val, + (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + convert_num (cfile, buf, val, 0, 16); + len = 2; + dp = buf; + goto alloc; + + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = next_token (&val, + (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + convert_num (cfile, buf, val, 0, 8); + len = 1; + dp = buf; + goto alloc; + + case 'f': /* Boolean flag. */ + token = next_token (&val, + (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "expecting identifier."); + bad_flag: + if (token != SEMI) + skip_to_semi (cfile); + return 0; + } + if (!strcasecmp (val, "true") + || !strcasecmp (val, "on")) + buf [0] = 1; + else if (!strcasecmp (val, "false") + || !strcasecmp (val, "off")) + buf [0] = 0; + else { + parse_warn (cfile, + "expecting boolean."); + goto bad_flag; + } + len = 1; + dp = buf; + goto alloc; + + default: + log_error ("parse_option_param: Bad format %c", + *fmt); + skip_to_semi (cfile); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + } while (*fmt == 'A' && token == COMMA); + + if (token != SEMI) { + parse_warn (cfile, "semicolon expected."); + skip_to_semi (cfile); + return 0; + } + + bp = (struct buffer *)0; + if (!buffer_allocate (&bp, hunkix + nul_term, MDL)) + log_fatal ("no memory to store option declaration."); + if (!bp -> data) + log_fatal ("out of memory allocating option data."); + memcpy (bp -> data, hunkbuf, hunkix + nul_term); + + if (!option_cache_allocate (oc, MDL)) + log_fatal ("out of memory allocating option cache."); + + (*oc) -> data.buffer = bp; + (*oc) -> data.data = &bp -> data [0]; + (*oc) -> data.terminated = nul_term; + (*oc) -> data.len = hunkix; + (*oc) -> option = option; + return 1; +} + +/* Consider merging parse_cshl into this. */ + +int parse_X (cfile, buf, max) + struct parse *cfile; + u_int8_t *buf; + unsigned max; +{ + int token; + const char *val; + unsigned len; + u_int8_t *s; + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + len = 0; + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER && token != NUMBER_OR_NAME) { + parse_warn (cfile, + "expecting hexadecimal constant."); + skip_to_semi (cfile); + return 0; + } + convert_num (cfile, &buf [len], val, 16, 8); + if (len++ > max) { + parse_warn (cfile, + "hexadecimal constant too long."); + skip_to_semi (cfile); + return 0; + } + token = peek_token (&val, (unsigned *)0, cfile); + if (token == COLON) + token = next_token (&val, + (unsigned *)0, cfile); + } while (token == COLON); + val = (char *)buf; + } else if (token == STRING) { + token = next_token (&val, &len, cfile); + if (len + 1 > max) { + parse_warn (cfile, "string constant too long."); + skip_to_semi (cfile); + return 0; + } + memcpy (buf, val, len + 1); + } else { + parse_warn (cfile, "expecting string or hexadecimal data"); + skip_to_semi (cfile); + return 0; + } + return len; +} + +int parse_warn (struct parse *cfile, const char *fmt, ...) +{ + va_list list; + char lexbuf [256]; + char mbuf [1024]; + char fbuf [1024]; + unsigned i, lix; + + do_percentm (mbuf, fmt); + /* %Audit% This is log output. %2004.06.17,Safe% + * If we truncate we hope the user can get a hint from the log. + */ + snprintf (fbuf, sizeof fbuf, "%s line %d: %s", + cfile -> tlname, cfile -> lexline, mbuf); + + va_start (list, fmt); + vsnprintf (mbuf, sizeof mbuf, fbuf, list); + va_end (list); + + lix = 0; + for (i = 0; + cfile -> token_line [i] && i < (cfile -> lexchar - 1); i++) { + if (lix < (sizeof lexbuf) - 1) + lexbuf [lix++] = ' '; + if (cfile -> token_line [i] == '\t') { + for (lix; + lix < (sizeof lexbuf) - 1 && (lix & 7); lix++) + lexbuf [lix] = ' '; + } + } + lexbuf [lix] = 0; + +#ifndef DEBUG + syslog (log_priority | LOG_ERR, "%s", mbuf); + syslog (log_priority | LOG_ERR, "%s", cfile -> token_line); + if (cfile -> lexchar < 81) + syslog (log_priority | LOG_ERR, "%s^", lexbuf); +#endif + + if (log_perror) { + write (STDERR_FILENO, mbuf, strlen (mbuf)); + write (STDERR_FILENO, "\n", 1); + write (STDERR_FILENO, cfile -> token_line, + strlen (cfile -> token_line)); + write (STDERR_FILENO, "\n", 1); + if (cfile -> lexchar < 81) + write (STDERR_FILENO, lexbuf, lix); + write (STDERR_FILENO, "^\n", 2); + } + + cfile -> warnings_occurred = 1; + + return 0; +} diff --git a/contrib/dhcp-3.0/common/print.c b/contrib/dhcp-3.0/common/print.c new file mode 100644 index 0000000000..dba117038f --- /dev/null +++ b/contrib/dhcp-3.0/common/print.c @@ -0,0 +1,1405 @@ +/* print.c + + Turn data structures into printable text. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: print.c,v 1.53.2.11 2004/06/17 20:54:39 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +char *quotify_string (const char *s, const char *file, int line) +{ + unsigned len = 0; + const char *sp; + char *buf, *nsp; + + for (sp = s; sp && *sp; sp++) { + if (*sp == ' ') + len++; + else if (!isascii (*sp) || !isprint (*sp)) + len += 4; + else if (*sp == '"' || *sp == '\\') + len += 2; + else + len++; + } + + buf = dmalloc (len + 1, file, line); + if (buf) { + nsp = buf; + for (sp = s; sp && *sp; sp++) { + if (*sp == ' ') + *nsp++ = ' '; + else if (!isascii (*sp) || !isprint (*sp)) { + sprintf (nsp, "\\%03o", + *(const unsigned char *)sp); + nsp += 4; + } else if (*sp == '"' || *sp == '\\') { + *nsp++ = '\\'; + *nsp++ = *sp; + } else + *nsp++ = *sp; + } + *nsp++ = 0; + } + return buf; +} + +char *quotify_buf (const unsigned char *s, unsigned len, + const char *file, int line) +{ + unsigned nulen = 0; + char *buf, *nsp; + int i; + + for (i = 0; i < len; i++) { + if (s [i] == ' ') + nulen++; + else if (!isascii (s [i]) || !isprint (s [i])) + nulen += 4; + else if (s [i] == '"' || s [i] == '\\') + nulen += 2; + else + nulen++; + } + + buf = dmalloc (nulen + 1, MDL); + if (buf) { + nsp = buf; + for (i = 0; i < len; i++) { + if (s [i] == ' ') + *nsp++ = ' '; + else if (!isascii (s [i]) || !isprint (s [i])) { + sprintf (nsp, "\\%03o", s [i]); + nsp += 4; + } else if (s [i] == '"' || s [i] == '\\') { + *nsp++ = '\\'; + *nsp++ = s [i]; + } else + *nsp++ = s [i]; + } + *nsp++ = 0; + } + return buf; +} + +char *print_base64 (const unsigned char *buf, unsigned len, + const char *file, int line) +{ + char *s, *b; + unsigned bl; + int i; + unsigned val, extra; + static char to64 [] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + bl = ((len * 4 + 2) / 3) + 1; + b = dmalloc (bl + 1, file, line); + if (!b) + return (char *)0; + + i = 0; + s = b; + while (i != len) { + val = buf [i++]; + extra = val & 3; + val = val >> 2; + *s++ = to64 [val]; + if (i == len) { + *s++ = to64 [extra << 4]; + *s++ = '='; + break; + } + val = (extra << 8) + buf [i++]; + extra = val & 15; + val = val >> 4; + *s++ = to64 [val]; + if (i == len) { + *s++ = to64 [extra << 2]; + *s++ = '='; + break; + } + val = (extra << 8) + buf [i++]; + extra = val & 0x3f; + val = val >> 6; + *s++ = to64 [val]; + *s++ = to64 [extra]; + } + if (!len) + *s++ = '='; + *s++ = 0; + if (s > b + bl + 1) + abort (); + return b; +} + +char *print_hw_addr (htype, hlen, data) + int htype; + int hlen; + unsigned char *data; +{ + static char habuf [49]; + char *s; + int i; + + if (hlen <= 0) + habuf [0] = 0; + else { + s = habuf; + for (i = 0; i < hlen; i++) { + sprintf (s, "%02x", data [i]); + s += strlen (s); + *s++ = ':'; + } + *--s = 0; + } + return habuf; +} + +void print_lease (lease) + struct lease *lease; +{ + struct tm *t; + char tbuf [32]; + + log_debug (" Lease %s", + piaddr (lease -> ip_addr)); + + t = gmtime (&lease -> starts); + strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); + log_debug (" start %s", tbuf); + + t = gmtime (&lease -> ends); + strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); + log_debug (" end %s", tbuf); + + if (lease -> hardware_addr.hlen) + log_debug (" hardware addr = %s", + print_hw_addr (lease -> hardware_addr.hbuf [0], + lease -> hardware_addr.hlen - 1, + &lease -> hardware_addr.hbuf [1])); + log_debug (" host %s ", + lease -> host ? lease -> host -> name : ""); +} + +#if defined (DEBUG_PACKET) +void dump_packet_option (struct option_cache *oc, + struct packet *packet, + struct lease *lease, + struct client_state *client, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *foo) +{ + const char *name, *dot; + struct data_string ds; + memset (&ds, 0, sizeof ds); + + if (u != &dhcp_universe) { + name = u -> name; + dot = "."; + } else { + name = ""; + dot = ""; + } + if (evaluate_option_cache (&ds, packet, lease, client, + in_options, cfg_options, scope, oc, MDL)) { + log_debug (" option %s%s%s %s;\n", + name, dot, oc -> option -> name, + pretty_print_option (oc -> option, + ds.data, ds.len, 1, 1)); + data_string_forget (&ds, MDL); + } +} + +void dump_packet (tp) + struct packet *tp; +{ + struct dhcp_packet *tdp = tp -> raw; + + log_debug ("packet length %d", tp -> packet_length); + log_debug ("op = %d htype = %d hlen = %d hops = %d", + tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops); + log_debug ("xid = %x secs = %ld flags = %x", + tdp -> xid, (unsigned long)tdp -> secs, tdp -> flags); + log_debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr)); + log_debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr)); + log_debug ("siaddr = %s", inet_ntoa (tdp -> siaddr)); + log_debug ("giaddr = %s", inet_ntoa (tdp -> giaddr)); + log_debug ("chaddr = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", + ((unsigned char *)(tdp -> chaddr)) [0], + ((unsigned char *)(tdp -> chaddr)) [1], + ((unsigned char *)(tdp -> chaddr)) [2], + ((unsigned char *)(tdp -> chaddr)) [3], + ((unsigned char *)(tdp -> chaddr)) [4], + ((unsigned char *)(tdp -> chaddr)) [5]); + log_debug ("filename = %s", tdp -> file); + log_debug ("server_name = %s", tdp -> sname); + if (tp -> options_valid) { + int i; + + for (i = 0; i < tp -> options -> universe_count; i++) { + if (tp -> options -> universes [i]) { + option_space_foreach (tp, (struct lease *)0, + (struct client_state *)0, + (struct option_state *)0, + tp -> options, + &global_scope, + universes [i], 0, + dump_packet_option); + } + } + } + log_debug ("%s", ""); +} +#endif + +void dump_raw (buf, len) + const unsigned char *buf; + unsigned len; +{ + int i; + char lbuf [80]; + int lbix = 0; + +/* + 1 2 3 4 5 6 7 +01234567890123456789012345678901234567890123456789012345678901234567890123 +280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................. +*/ + + memset(lbuf, ' ', 79); + lbuf [79] = 0; + + for (i = 0; i < len; i++) { + if ((i & 15) == 0) { + if (lbix) { + lbuf[53]=' '; + lbuf[54]=' '; + lbuf[55]=' '; + lbuf[73]='\0'; + log_info (lbuf); + } + memset(lbuf, ' ', 79); + lbuf [79] = 0; + sprintf (lbuf, "%03x:", i); + lbix = 4; + } else if ((i & 7) == 0) + lbuf [lbix++] = ' '; + + if(isprint(buf[i])) { + lbuf[56+(i%16)]=buf[i]; + } else { + lbuf[56+(i%16)]='.'; + } + + sprintf (&lbuf [lbix], " %02x", buf [i]); + lbix += 3; + lbuf[lbix]=' '; + + } + lbuf[53]=' '; + lbuf[54]=' '; + lbuf[55]=' '; + lbuf[73]='\0'; + log_info (lbuf); +} + +void hash_dump (table) + struct hash_table *table; +{ + int i; + struct hash_bucket *bp; + + if (!table) + return; + + for (i = 0; i < table -> hash_count; i++) { + if (!table -> buckets [i]) + continue; + log_info ("hash bucket %d:", i); + for (bp = table -> buckets [i]; bp; bp = bp -> next) { + if (bp -> len) + dump_raw (bp -> name, bp -> len); + else + log_info ("%s", (const char *)bp -> name); + } + } +} + +#define HBLEN 60 + +#define DECLARE_HEX_PRINTER(x) \ +char *print_hex##x (len, data, limit) \ + unsigned len; \ + const u_int8_t *data; \ + unsigned limit; \ +{ \ + \ + static char hex_buf##x [HBLEN + 1]; \ + unsigned i; \ + \ + if (limit > HBLEN) \ + limit = HBLEN; \ + \ + for (i = 0; i < (limit - 2) && i < len; i++) { \ + if (!isascii (data [i]) || !isprint (data [i])) { \ + for (i = 0; i < limit / 3 && i < len; i++) { \ + sprintf (&hex_buf##x [i * 3], \ + "%02x:", data [i]); \ + } \ + hex_buf##x [i * 3 - 1] = 0; \ + return hex_buf##x; \ + } \ + } \ + hex_buf##x [0] = '"'; \ + i = len; \ + if (i > limit - 2) \ + i = limit - 2; \ + memcpy (&hex_buf##x [1], data, i); \ + hex_buf##x [i + 1] = '"'; \ + hex_buf##x [i + 2] = 0; \ + return hex_buf##x; \ +} + +DECLARE_HEX_PRINTER (_1) +DECLARE_HEX_PRINTER (_2) +DECLARE_HEX_PRINTER (_3) + +#define DQLEN 80 + +char *print_dotted_quads (len, data) + unsigned len; + const u_int8_t *data; +{ + static char dq_buf [DQLEN + 1]; + int i; + char *s, *last; + + s = &dq_buf [0]; + last = s; + + i = 0; + + /* %Audit% Loop bounds checks to 21 bytes. %2004.06.17,Safe% + * The sprintf can't exceed 18 bytes, and since the loop enforces + * 21 bytes of space per iteration at no time can we exit the + * loop without at least 3 bytes spare. + */ + do { + sprintf (s, "%u.%u.%u.%u, ", + data [i], data [i + 1], data [i + 2], data [i + 3]); + s += strlen (s); + i += 4; + } while ((s - &dq_buf [0] > DQLEN - 21) && + i + 3 < len); + if (i == len) + s [-2] = 0; + else + strcpy (s, "..."); + return dq_buf; +} + +char *print_dec_1 (val) + unsigned long val; +{ + static char vbuf [32]; + sprintf (vbuf, "%lu", val); + return vbuf; +} + +char *print_dec_2 (val) + unsigned long val; +{ + static char vbuf [32]; + sprintf (vbuf, "%lu", val); + return vbuf; +} + +static unsigned print_subexpression PROTO ((struct expression *, + char *, unsigned)); + +static unsigned print_subexpression (expr, buf, len) + struct expression *expr; + char *buf; + unsigned len; +{ + unsigned rv, left; + const char *s; + + switch (expr -> op) { + case expr_none: + if (len > 3) { + strcpy (buf, "nil"); + return 3; + } + break; + + case expr_match: + if (len > 7) { + strcpy (buf, "(match)"); + return 7; + } + break; + + case expr_check: + rv = 10 + strlen (expr -> data.check -> name); + if (len > rv) { + sprintf (buf, "(check %s)", + expr -> data.check -> name); + return rv; + } + break; + + case expr_equal: + if (len > 6) { + rv = 4; + strcpy (buf, "(eq "); + rv += print_subexpression (expr -> data.equal [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.equal [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_not_equal: + if (len > 7) { + rv = 5; + strcpy (buf, "(neq "); + rv += print_subexpression (expr -> data.equal [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.equal [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_substring: + if (len > 11) { + rv = 8; + strcpy (buf, "(substr "); + rv += print_subexpression (expr -> data.substring.expr, + buf + rv, len - rv - 3); + buf [rv++] = ' '; + rv += print_subexpression + (expr -> data.substring.offset, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.substring.len, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_suffix: + if (len > 10) { + rv = 8; + strcpy (buf, "(suffix "); + rv += print_subexpression (expr -> data.suffix.expr, + buf + rv, len - rv - 2); + if (len > rv) + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.suffix.len, + buf + rv, len - rv - 1); + if (len > rv) + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_concat: + if (len > 10) { + rv = 8; + strcpy (buf, "(concat "); + rv += print_subexpression (expr -> data.concat [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.concat [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_pick_first_value: + if (len > 8) { + rv = 6; + strcpy (buf, "(pick1st "); + rv += print_subexpression + (expr -> data.pick_first_value.car, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression + (expr -> data.pick_first_value.cdr, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_host_lookup: + rv = 15 + strlen (expr -> data.host_lookup -> hostname); + if (len > rv) { + sprintf (buf, "(dns-lookup %s)", + expr -> data.host_lookup -> hostname); + return rv; + } + break; + + case expr_and: + s = "and"; + binop: + rv = strlen (s); + if (len > rv + 4) { + buf [0] = '('; + strcpy (&buf [1], s); + rv += 1; + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.and [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.and [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_or: + s = "or"; + goto binop; + + case expr_add: + s = "+"; + goto binop; + + case expr_subtract: + s = "-"; + goto binop; + + case expr_multiply: + s = "*"; + goto binop; + + case expr_divide: + s = "/"; + goto binop; + + case expr_remainder: + s = "%"; + goto binop; + + case expr_binary_and: + s = "&"; + goto binop; + + case expr_binary_or: + s = "|"; + goto binop; + + case expr_binary_xor: + s = "^"; + goto binop; + + case expr_not: + if (len > 6) { + rv = 5; + strcpy (buf, "(not "); + rv += print_subexpression (expr -> data.not, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_config_option: + s = "cfg-option"; + goto dooption; + + case expr_option: + s = "option"; + dooption: + rv = strlen (s) + 2 + (strlen (expr -> data.option -> name) + + strlen (expr -> data.option -> universe -> name)); + if (len > rv) { + sprintf (buf, "(option %s.%s)", + expr -> data.option -> universe -> name, + expr -> data.option -> name); + return rv; + } + break; + + case expr_hardware: + if (len > 10) { + strcpy (buf, "(hardware)"); + return 10; + } + break; + + case expr_packet: + if (len > 10) { + rv = 8; + strcpy (buf, "(substr "); + rv += print_subexpression (expr -> data.packet.offset, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.packet.len, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_const_data: + s = print_hex_1 (expr -> data.const_data.len, + expr -> data.const_data.data, len); + rv = strlen (s); + if (rv >= len) + rv = len - 1; + strncpy (buf, s, rv); + buf [rv] = 0; + return rv; + + case expr_encapsulate: + rv = 13; + strcpy (buf, "(encapsulate "); + rv += expr -> data.encapsulate.len; + if (rv + 2 > len) + rv = len - 2; + strncpy (buf, + (const char *)expr -> data.encapsulate.data, rv - 13); + buf [rv++] = ')'; + buf [rv++] = 0; + break; + + case expr_extract_int8: + if (len > 7) { + rv = 6; + strcpy (buf, "(int8 "); + rv += print_subexpression (expr -> data.extract_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_extract_int16: + if (len > 8) { + rv = 7; + strcpy (buf, "(int16 "); + rv += print_subexpression (expr -> data.extract_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_extract_int32: + if (len > 8) { + rv = 7; + strcpy (buf, "(int32 "); + rv += print_subexpression (expr -> data.extract_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_encode_int8: + if (len > 7) { + rv = 6; + strcpy (buf, "(to-int8 "); + rv += print_subexpression (expr -> data.encode_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_encode_int16: + if (len > 8) { + rv = 7; + strcpy (buf, "(to-int16 "); + rv += print_subexpression (expr -> data.encode_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_encode_int32: + if (len > 8) { + rv = 7; + strcpy (buf, "(to-int32 "); + rv += print_subexpression (expr -> data.encode_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_const_int: + s = print_dec_1 (expr -> data.const_int); + rv = strlen (s); + if (len > rv) { + strcpy (buf, s); + return rv; + } + break; + + case expr_exists: + rv = 10 + (strlen (expr -> data.option -> name) + + strlen (expr -> data.option -> universe -> name)); + if (len > rv) { + sprintf (buf, "(exists %s.%s)", + expr -> data.option -> universe -> name, + expr -> data.option -> name); + return rv; + } + break; + + case expr_variable_exists: + rv = 10 + strlen (expr -> data.variable); + if (len > rv) { + sprintf (buf, "(defined %s)", expr -> data.variable); + return rv; + } + break; + + case expr_variable_reference: + rv = strlen (expr -> data.variable); + if (len > rv) { + sprintf (buf, "%s", expr -> data.variable); + return rv; + } + break; + + case expr_known: + s = "known"; + astring: + rv = strlen (s); + if (len > rv) { + strcpy (buf, s); + return rv; + } + break; + + case expr_leased_address: + s = "leased-address"; + goto astring; + + case expr_client_state: + s = "client-state"; + goto astring; + + case expr_host_decl_name: + s = "host-decl-name"; + goto astring; + + case expr_lease_time: + s = "lease-time"; + goto astring; + + case expr_static: + s = "static"; + goto astring; + + case expr_filename: + s = "filename"; + goto astring; + + case expr_sname: + s = "server-name"; + goto astring; + + case expr_reverse: + if (len > 11) { + rv = 13; + strcpy (buf, "(reverse "); + rv += print_subexpression (expr -> data.reverse.width, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.reverse.buffer, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_binary_to_ascii: + if (len > 5) { + rv = 9; + strcpy (buf, "(b2a "); + rv += print_subexpression (expr -> data.b2a.base, + buf + rv, len - rv - 4); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.b2a.width, + buf + rv, len - rv - 3); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.b2a.seperator, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.b2a.buffer, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_dns_transaction: + rv = 10; + if (len < rv + 2) { + buf [0] = '('; + strcpy (&buf [1], "ns-update "); + while (len < rv + 2) { + rv += print_subexpression + (expr -> data.dns_transaction.car, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + expr = expr -> data.dns_transaction.cdr; + } + buf [rv - 1] = ')'; + buf [rv] = 0; + return rv; + } + return 0; + + case expr_ns_delete: + s = "delete"; + left = 4; + goto dodnsupd; + case expr_ns_exists: + s = "exists"; + left = 4; + goto dodnsupd; + case expr_ns_not_exists: + s = "not_exists"; + left = 4; + goto dodnsupd; + case expr_ns_add: + s = "update"; + left = 5; + dodnsupd: + rv = strlen (s); + if (len > strlen (s) + 1) { + buf [0] = '('; + strcpy (buf + 1, s); + rv++; + buf [rv++] = ' '; + s = print_dec_1 (expr -> data.ns_add.rrclass); + if (len > rv + strlen (s) + left) { + strcpy (&buf [rv], s); + rv += strlen (&buf [rv]); + } + buf [rv++] = ' '; + left--; + s = print_dec_1 (expr -> data.ns_add.rrtype); + if (len > rv + strlen (s) + left) { + strcpy (&buf [rv], s); + rv += strlen (&buf [rv]); + } + buf [rv++] = ' '; + left--; + rv += print_subexpression + (expr -> data.ns_add.rrname, + buf + rv, len - rv - left); + buf [rv++] = ' '; + left--; + rv += print_subexpression + (expr -> data.ns_add.rrdata, + buf + rv, len - rv - left); + buf [rv++] = ' '; + left--; + rv += print_subexpression + (expr -> data.ns_add.ttl, + buf + rv, len - rv - left); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_null: + if (len > 6) { + strcpy (buf, "(null)"); + return 6; + } + break; + case expr_funcall: + rv = 12 + strlen (expr -> data.funcall.name); + if (len > rv + 1) { + strcpy (buf, "(funcall "); + strcpy (buf + 9, expr -> data.funcall.name); + buf [rv++] = ' '; + rv += print_subexpression + (expr -> data.funcall.arglist, buf + rv, + len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_arg: + rv = print_subexpression (expr -> data.arg.val, buf, len); + if (expr -> data.arg.next && rv + 2 < len) { + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.arg.next, + buf, len); + if (rv + 1 < len) + buf [rv++] = 0; + return rv; + } + break; + case expr_function: + rv = 9; + if (len > rv + 1) { + struct string_list *foo; + strcpy (buf, "(function"); + for (foo = expr -> data.func -> args; + foo; foo = foo -> next) { + if (len > rv + 2 + strlen (foo -> string)) { + buf [rv - 1] = ' '; + strcpy (&buf [rv], foo -> string); + rv += strlen (foo -> string); + } + } + buf [rv] = ')'; + buf [rv++] = 0; + return rv; + } + } + return 0; +} + +void print_expression (name, expr) + const char *name; + struct expression *expr; +{ + char buf [1024]; + + print_subexpression (expr, buf, sizeof buf); + log_info ("%s: %s", name, buf); +} + +int token_print_indent_concat (FILE *file, int col, int indent, + const char *prefix, + const char *suffix, ...) +{ + va_list list; + char *buf; + unsigned len; + char *s, *t, *u; + + va_start (list, suffix); + s = va_arg (list, char *); + len = 0; + while (s) { + len += strlen (s); + s = va_arg (list, char *); + } + va_end (list); + + t = dmalloc (len + 1, MDL); + if (!t) + log_fatal ("token_print_indent: no memory for copy buffer"); + + va_start (list, suffix); + s = va_arg (list, char *); + u = t; + while (s) { + len = strlen (s); + strcpy (u, s); + u += len; + } + va_end (list); + + len = token_print_indent (file, col, indent, + prefix, suffix, t); + dfree (t, MDL); + return col; +} + +int token_indent_data_string (FILE *file, int col, int indent, + const char *prefix, const char *suffix, + struct data_string *data) +{ + int i; + char *buf; + char obuf [3]; + + /* See if this is just ASCII. */ + for (i = 0; i < data -> len; i++) + if (!isascii (data -> data [i]) || + !isprint (data -> data [i])) + break; + + /* If we have a purely ASCII string, output it as text. */ + if (i == data -> len) { + char *buf = dmalloc (data -> len + 3, MDL); + if (buf) { + buf [0] = '"'; + memcpy (buf + 1, data -> data, data -> len); + buf [data -> len + 1] = '"'; + buf [data -> len + 2] = 0; + i = token_print_indent (file, col, indent, + prefix, suffix, buf); + dfree (buf, MDL); + return i; + } + } + + for (i = 0; i < data -> len; i++) { + sprintf (obuf, "%2.2x", data -> data [i]); + col = token_print_indent (file, col, indent, + i == 0 ? prefix : "", + (i + 1 == data -> len + ? suffix + : ""), obuf); + if (i + 1 != data -> len) + col = token_print_indent (file, col, indent, + prefix, suffix, ":"); + } + return col; +} + +int token_print_indent (FILE *file, int col, int indent, + const char *prefix, + const char *suffix, const char *buf) +{ + int len = strlen (buf) + strlen (prefix); + if (col + len > 79) { + if (indent + len < 79) { + indent_spaces (file, indent); + col = indent; + } else { + indent_spaces (file, col); + col = len > 79 ? 0 : 79 - len - 1; + } + } else if (prefix && *prefix) { + fputs (prefix, file); + col += strlen (prefix); + } + fputs (buf, file); + col += len; + if (suffix && *suffix) { + if (col + strlen (suffix) > 79) { + indent_spaces (file, indent); + col = indent; + } else { + fputs (suffix, file); + col += strlen (suffix); + } + } + return col; +} + +void indent_spaces (FILE *file, int indent) +{ + int i; + fputc ('\n', file); + for (i = 0; i < indent; i++) + fputc (' ', file); +} + +#if defined (NSUPDATE) +void print_dns_status (int status, ns_updque *uq) +{ + char obuf [1024]; + char *s = &obuf [0], *end = &obuf [1022]; + ns_updrec *u; + int position; + int ttlp; + const char *predicate = "if", *en, *op; + int errorp; + + for (u = ISC_LIST_HEAD (*uq); u; u = ISC_LIST_NEXT (u, r_link)) { + ttlp = 0; + + switch (u -> r_opcode) + { + case NXRRSET: + op = "rrset doesn't exist"; + position = 1; + break; + case YXRRSET: + op = "rrset exists"; + position = 1; + break; + case NXDOMAIN: + op = "domain doesn't exist"; + position = 1; + break; + case YXDOMAIN: + op = "domain exists"; + position = 1; + break; + case ADD: + op = "add"; + position = 0; + ttlp = 1; + break; + case DELETE: + op = "delete"; + position = 0; + break; + default: + op = "unknown"; + position = 0; + break; + } + if (!position) { + if (s != &obuf [0] && s + 1 < end) + *s++ = ' '; + if (s + strlen (op) < end) { + strcpy (s, op); + s += strlen (s); + } + } else { + if (s != &obuf [0] && s + 1 < end) + *s++ = ' '; + if (s + strlen (predicate) < end) { + strcpy (s, predicate); + s += strlen (s); + } + predicate = "and"; + } + if (u -> r_dname) { + if (s + 1 < end) + *s++ = ' '; + if (s + strlen (u -> r_dname) < end) { + strcpy (s, u -> r_dname); + s += strlen (s); + } + } + if (ttlp) { + if (s + 1 < end) + *s++ = ' '; + /* 27 is as big as a ttl can get. */ + if (s + 27 < end) { + sprintf (s, "%lu", + (unsigned long)(u -> r_ttl)); + s += strlen (s); + } + } + switch (u -> r_class) { + case C_IN: + en = "IN"; + break; + case C_CHAOS: + en = "CHAOS"; + break; + case C_HS: + en = "HS"; + break; + default: + en = "UNKNOWN"; + break; + } + if (s + strlen (en) < end) { + if (s + 1 < end) + *s++ = ' '; + strcpy (s, en); + s += strlen (en); + } + switch (u -> r_type) { + case T_A: + en = "A"; + break; + case T_PTR: + en = "PTR"; + break; + case T_MX: + en = "MX"; + break; + case T_TXT: + en = "TXT"; + break; + case T_KEY: + en = "KEY"; + break; + case T_CNAME: + en = "CNAME"; + break; + default: + en = "UNKNOWN"; + break; + } + if (s + strlen (en) < end) { + if (s + 1 < end) + *s++ = ' '; + strcpy (s, en); + s += strlen (en); + } + if (u -> r_data) { + if (s + 1 < end) + *s++ = ' '; + if (u -> r_type == T_TXT) { + if (s + 1 < end) + *s++ = '"'; + } + if(u->r_type == T_KEY) { + strcat(s, ""); + s+=strlen(""); + } + else { + if (s + u -> r_size < end) { + memcpy (s, u -> r_data, u -> r_size); + s += u -> r_size; + if (u -> r_type == T_TXT) { + if (s + 1 < end) + *s++ = '"'; + } + } + } + } + if (position) { + if (s + 1 < end) + *s++ = ' '; + if (s + strlen (op) < end) { + strcpy (s, op); + s += strlen (s); + } + } + if (u == ISC_LIST_TAIL (*uq)) + break; + } + if (s == &obuf [0]) { + strcpy (s, "empty update"); + s += strlen (s); + } + if (status == NOERROR) + errorp = 0; + else + errorp = 1; + en = isc_result_totext (status); +#if 0 + switch (status) { + case -1: + en = "resolver failed"; + break; + + case FORMERR: + en = "format error"; + break; + + case NOERROR: + en = "succeeded"; + errorp = 0; + break; + + case NOTAUTH: + en = "not authorized"; + break; + + case NOTIMP: + en = "not implemented"; + break; + + case NOTZONE: + en = "not a single valid zone"; + break; + + case NXDOMAIN: + en = "no such domain"; + break; + + case NXRRSET: + en = "no such record"; + break; + + case REFUSED: + en = "refused"; + break; + + case SERVFAIL: + en = "server failed"; + break; + + case YXDOMAIN: + en = "domain exists"; + break; + + case YXRRSET: + en = "record exists"; + break; + + default: + en = "unknown error"; + break; + } +#endif + + if (s + 2 < end) { + *s++ = ':'; + *s++ = ' '; + } + if (s + strlen (en) < end) { + strcpy (s, en); + s += strlen (en); + } + if (s + 1 < end) + *s++ = '.'; + *s++ = 0; + if (errorp) + log_error ("%s", obuf); + else + log_info ("%s", obuf); +} +#endif /* NSUPDATE */ diff --git a/contrib/dhcp-3.0/common/raw.c b/contrib/dhcp-3.0/common/raw.c new file mode 100644 index 0000000000..1194ffc161 --- /dev/null +++ b/contrib/dhcp-3.0/common/raw.c @@ -0,0 +1,144 @@ +/* socket.c + + BSD raw socket interface code... */ + +/* XXX + + It's not clear how this should work, and that lack of clarity is + terribly detrimental to the NetBSD 1.1 kernel - it crashes and + burns. + + Using raw sockets ought to be a big win over using BPF or something + like it, because you don't need to deal with the complexities of + the physical layer, but it appears not to be possible with existing + raw socket implementations. This may be worth revisiting in the + future. For now, this code can probably be considered a curiosity. + Sigh. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: raw.c,v 1.17.2.2 2004/06/10 17:59:20 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (USE_RAW_SEND) +#include + +/* Generic interface registration routine... */ +void if_register_send (info) + struct interface_info *info; +{ + struct sockaddr_in name; + int sock; + struct socklist *tmp; + int flag; + + /* Set up the address we're going to connect to. */ + name.sin_family = AF_INET; + name.sin_port = local_port; + name.sin_addr.s_addr = htonl (INADDR_BROADCAST); + memset (name.sin_zero, 0, sizeof (name.sin_zero)); + + /* List addresses on which we're listening. */ + if (!quiet_interface_discovery) + log_info ("Sending on %s, port %d", + piaddr (info -> address), htons (local_port)); + if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + log_fatal ("Can't create dhcp socket: %m"); + + /* Set the BROADCAST option so that we can broadcast DHCP responses. */ + flag = 1; + if (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, + &flag, sizeof flag) < 0) + log_fatal ("Can't set SO_BROADCAST option on dhcp socket: %m"); + + /* Set the IP_HDRINCL flag so that we can supply our own IP + headers... */ + if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &flag, sizeof flag) < 0) + log_fatal ("Can't set IP_HDRINCL flag: %m"); + + info -> wfdesc = sock; + if (!quiet_interface_discovery) + log_info ("Sending on Raw/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_send (info) + struct interface_info *info; +{ + close (info -> wfdesc); + info -> wfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling output on Raw/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +size_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + unsigned char buf [256]; + int bufp = 0; + struct iovec iov [2]; + int result; + + /* Assemble the headers... */ + assemble_udp_ip_header (interface, buf, &bufp, from.s_addr, + to -> sin_addr.s_addr, to -> sin_port, + (unsigned char *)raw, len); + + /* Fire it off */ + iov [0].iov_base = (char *)buf; + iov [0].iov_len = bufp; + iov [1].iov_base = (char *)raw; + iov [1].iov_len = len; + + result = writev(interface -> wfdesc, iov, 2); + if (result < 0) + log_error ("send_packet: %m"); + return result; +} +#endif /* USE_SOCKET_SEND */ diff --git a/contrib/dhcp-3.0/common/resolv.c b/contrib/dhcp-3.0/common/resolv.c new file mode 100644 index 0000000000..b84de02701 --- /dev/null +++ b/contrib/dhcp-3.0/common/resolv.c @@ -0,0 +1,203 @@ +/* resolv.c + + Parser for /etc/resolv.conf file. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: resolv.c,v 1.16.2.2 2004/06/10 17:59:20 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +struct name_server *name_servers; +struct domain_search_list *domains; +char path_resolv_conf [] = _PATH_RESOLV_CONF; + +void read_resolv_conf (parse_time) + TIME parse_time; +{ + int file; + struct parse *cfile; + const char *val; + int token; + int declaration = 0; + struct name_server *sp, *sl, *ns; + struct domain_search_list *dp, *dl, *nd; + struct iaddr *iaddr; + + if ((file = open (path_resolv_conf, O_RDONLY)) < 0) { + log_error ("Can't open %s: %m", path_resolv_conf); + return; + } + + cfile = (struct parse *)0; + new_parse (&cfile, file, (char *)0, 0, path_resolv_conf, 1); + + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) + break; + else if (token == EOL) + continue; + else if (token == DOMAIN || token == SEARCH) { + do { + struct domain_search_list *nd, **dp; + char *dn; + + dn = parse_host_name (cfile); + if (!dn) + break; + + dp = &domains; + for (nd = domains; nd; nd = nd -> next) { + dp = &nd -> next; + if (!strcmp (nd -> domain, dn)) + break; + } + if (!nd) { + nd = new_domain_search_list (MDL); + if (!nd) + log_fatal ("No memory for %s", + dn); + nd -> next = + (struct domain_search_list *)0; + *dp = nd; + nd -> domain = dn; + dn = (char *)0; + } + nd -> rcdate = parse_time; + token = peek_token (&val, + (unsigned *)0, cfile); + } while (token != EOL); + if (token != EOL) { + parse_warn (cfile, + "junk after domain declaration"); + skip_to_semi (cfile); + } + token = next_token (&val, (unsigned *)0, cfile); + } else if (token == NAMESERVER) { + struct name_server *ns, **sp; + struct iaddr iaddr; + + parse_ip_addr (cfile, &iaddr); + + sp = &name_servers; + for (ns = name_servers; ns; ns = ns -> next) { + sp = &ns -> next; + if (!memcmp (&ns -> addr.sin_addr, + iaddr.iabuf, iaddr.len)) + break; + } + if (!ns) { + ns = new_name_server (MDL); + if (!ns) + log_fatal ("No memory for nameserver %s", + piaddr (iaddr)); + ns -> next = (struct name_server *)0; + *sp = ns; + memcpy (&ns -> addr.sin_addr, + iaddr.iabuf, iaddr.len); +#ifdef HAVE_SA_LEN + ns -> addr.sin_len = sizeof ns -> addr; +#endif + ns -> addr.sin_family = AF_INET; + ns -> addr.sin_port = htons (53); + memset (ns -> addr.sin_zero, 0, + sizeof ns -> addr.sin_zero); + } + ns -> rcdate = parse_time; + skip_to_semi (cfile); + } else + skip_to_semi (cfile); /* Ignore what we don't grok. */ + } while (1); + token = next_token (&val, (unsigned *)0, cfile); + + /* Lose servers that are no longer in /etc/resolv.conf. */ + sl = (struct name_server *)0; + for (sp = name_servers; sp; sp = ns) { + ns = sp -> next; + if (sp -> rcdate != parse_time) { + if (sl) + sl -> next = sp -> next; + else + name_servers = sp -> next; + /* We can't actually free the name server structure, + because somebody might be hanging on to it. If + your /etc/resolv.conf file changes a lot, this + could be a noticable memory leak. */ + } else + sl = sp; + } + + /* Lose domains that are no longer in /etc/resolv.conf. */ + dl = (struct domain_search_list *)0; + for (dp = domains; dp; dp = nd) { + nd = dp -> next; + if (dp -> rcdate != parse_time) { + if (dl) + dl -> next = dp -> next; + else + domains = dp -> next; + free_domain_search_list (dp, MDL); + } else + dl = dp; + } + close (file); + end_parse (&cfile); +} + +/* Pick a name server from the /etc/resolv.conf file. */ + +struct name_server *first_name_server () +{ + FILE *rc; + static TIME rcdate; + struct stat st; + + /* Check /etc/resolv.conf and reload it if it's changed. */ + if (cur_time > rcdate) { + if (stat (path_resolv_conf, &st) < 0) { + log_error ("Can't stat %s", path_resolv_conf); + return (struct name_server *)0; + } + if (st.st_mtime > rcdate) { + char rcbuf [512]; + char *s, *t, *u; + rcdate = cur_time + 1; + + read_resolv_conf (rcdate); + } + } + + return name_servers; +} diff --git a/contrib/dhcp-3.0/common/socket.c b/contrib/dhcp-3.0/common/socket.c new file mode 100644 index 0000000000..6aa45078d5 --- /dev/null +++ b/contrib/dhcp-3.0/common/socket.c @@ -0,0 +1,371 @@ +/* socket.c + + BSD socket interface code... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +/* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). + * This sockopt allows a socket to be bound to a particular interface, + * thus enabling the use of DHCPD on a multihomed host. + * If SO_BINDTODEVICE is defined in your system header files, the use of + * this sockopt will be automatically enabled. + * I have implemented it under Linux; other systems should be doable also. + */ + +#ifndef lint +static char copyright[] = +"$Id: socket.c,v 1.55.2.4 2004/06/10 17:59:21 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#ifdef USE_SOCKET_FALLBACK +# if !defined (USE_SOCKET_SEND) +# define if_register_send if_register_fallback +# define send_packet send_fallback +# define if_reinitialize_send if_reinitialize_fallback +# endif +#endif + +static int once = 0; + +/* Reinitializes the specified interface after an address change. This + is not required for packet-filter APIs. */ + +#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) +void if_reinitialize_send (info) + struct interface_info *info; +{ +#if 0 +#ifndef USE_SOCKET_RECEIVE + once = 0; + close (info -> wfdesc); +#endif + if_register_send (info); +#endif +} +#endif + +#ifdef USE_SOCKET_RECEIVE +void if_reinitialize_receive (info) + struct interface_info *info; +{ +#if 0 + once = 0; + close (info -> rfdesc); + if_register_receive (info); +#endif +} +#endif + +#if defined (USE_SOCKET_SEND) || \ + defined (USE_SOCKET_RECEIVE) || \ + defined (USE_SOCKET_FALLBACK) +/* Generic interface registration routine... */ +int if_register_socket (info) + struct interface_info *info; +{ + struct sockaddr_in name; + int sock; + int flag; + +#if !defined (HAVE_SO_BINDTODEVICE) && !defined (USE_FALLBACK) + /* Make sure only one interface is registered. */ + if (once) + log_fatal ("The standard socket API can only support %s", + "hosts with a single network interface."); + once = 1; +#endif + + memset (&name, 0, sizeof (name)); + /* Set up the address we're going to bind to. */ + name.sin_family = AF_INET; + name.sin_port = local_port; + name.sin_addr = local_address; + + /* Make a socket... */ + if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + log_fatal ("Can't create dhcp socket: %m"); + + /* Set the REUSEADDR option so that we don't fail to start if + we're being restarted. */ + flag = 1; + if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&flag, sizeof flag) < 0) + log_fatal ("Can't set SO_REUSEADDR option on dhcp socket: %m"); + + /* Set the BROADCAST option so that we can broadcast DHCP responses. + We shouldn't do this for fallback devices, and we can detect that + a device is a fallback because it has no ifp structure. */ + if (info -> ifp && + (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, + (char *)&flag, sizeof flag) < 0)) + log_fatal ("Can't set SO_BROADCAST option on dhcp socket: %m"); + + /* Bind the socket to this interface's IP address. */ + if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0) { + log_error ("Can't bind to dhcp address: %m"); + log_error ("Please make sure there is no other dhcp server"); + log_error ("running and that there's no entry for dhcp or"); + log_error ("bootp in /etc/inetd.conf. Also make sure you"); + log_error ("are not running HP JetAdmin software, which"); + log_fatal ("includes a bootp server."); + } + +#if defined (HAVE_SO_BINDTODEVICE) + /* Bind this socket to this interface. */ + if (info -> ifp && + setsockopt (sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *)(info -> ifp), sizeof *(info -> ifp)) < 0) { + log_fatal ("setsockopt: SO_BINDTODEVICE: %m"); + } +#endif + + return sock; +} +#endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */ + +#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) +void if_register_send (info) + struct interface_info *info; +{ +#ifndef USE_SOCKET_RECEIVE + info -> wfdesc = if_register_socket (info); +#if defined (USE_SOCKET_FALLBACK) + /* Fallback only registers for send, but may need to receive as + well. */ + info -> rfdesc = info -> wfdesc; +#endif +#else + info -> wfdesc = info -> rfdesc; +#endif + if (!quiet_interface_discovery) + log_info ("Sending on Socket/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +#if defined (USE_SOCKET_SEND) +void if_deregister_send (info) + struct interface_info *info; +{ +#ifndef USE_SOCKET_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling output on Socket/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_SOCKET_SEND */ +#endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ + +#ifdef USE_SOCKET_RECEIVE +void if_register_receive (info) + struct interface_info *info; +{ + /* If we're using the socket API for sending and receiving, + we don't need to register this interface twice. */ + info -> rfdesc = if_register_socket (info); + if (!quiet_interface_discovery) + log_info ("Listening on Socket/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + close (info -> rfdesc); + info -> rfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling input on Socket/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_SOCKET_RECEIVE */ + +#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK) +ssize_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + int result; +#ifdef IGNORE_HOSTUNREACH + int retry = 0; + do { +#endif + result = sendto (interface -> wfdesc, (char *)raw, len, 0, + (struct sockaddr *)to, sizeof *to); +#ifdef IGNORE_HOSTUNREACH + } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) && + result < 0 && + (errno == EHOSTUNREACH || + errno == ECONNREFUSED) && + retry++ < 10); +#endif + if (result < 0) { + log_error ("send_packet: %m"); + if (errno == ENETUNREACH) + log_error ("send_packet: please consult README file%s", + " regarding broadcast address."); + } + return result; +} +#endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ + +#ifdef USE_SOCKET_RECEIVE +ssize_t receive_packet (interface, buf, len, from, hfrom) + struct interface_info *interface; + unsigned char *buf; + size_t len; + struct sockaddr_in *from; + struct hardware *hfrom; +{ + SOCKLEN_T flen = sizeof *from; + int result; + +#ifdef IGNORE_HOSTUNREACH + int retry = 0; + do { +#endif + result = recvfrom (interface -> rfdesc, (char *)buf, len, 0, + (struct sockaddr *)from, &flen); +#ifdef IGNORE_HOSTUNREACH + } while (result < 0 && + (errno == EHOSTUNREACH || + errno == ECONNREFUSED) && + retry++ < 10); +#endif + return result; +} +#endif /* USE_SOCKET_RECEIVE */ + +#if defined (USE_SOCKET_FALLBACK) +/* This just reads in a packet and silently discards it. */ + +isc_result_t fallback_discard (object) + omapi_object_t *object; +{ + char buf [1540]; + struct sockaddr_in from; + SOCKLEN_T flen = sizeof from; + int status; + struct interface_info *interface; + + if (object -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)object; + + status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, + (struct sockaddr *)&from, &flen); +#if defined (DEBUG) + /* Only report fallback discard errors if we're debugging. */ + if (status < 0) { + log_error ("fallback_discard: %m"); + return ISC_R_UNEXPECTED; + } +#endif + return ISC_R_SUCCESS; +} +#endif /* USE_SOCKET_FALLBACK */ + +#if defined (USE_SOCKET_SEND) +int can_unicast_without_arp (ip) + struct interface_info *ip; +{ + return 0; +} + +int can_receive_unicast_unconfigured (ip) + struct interface_info *ip; +{ +#if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED) + return 1; +#else + return 0; +#endif +} + +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ +#if defined (SO_BINDTODEVICE) + return 1; +#else + return 0; +#endif +} + +/* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, + do not. */ + +void maybe_setup_fallback () +{ +#if defined (USE_SOCKET_FALLBACK) + isc_result_t status; + struct interface_info *fbi = (struct interface_info *)0; + if (setup_fallback (&fbi, MDL)) { + fbi -> wfdesc = if_register_socket (fbi); + fbi -> rfdesc = fbi -> wfdesc; + log_info ("Sending on Socket/%s%s%s", + fbi -> name, + (fbi -> shared_network ? "/" : ""), + (fbi -> shared_network ? + fbi -> shared_network -> name : "")); + + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); + } +#endif +} +#endif /* USE_SOCKET_SEND */ diff --git a/contrib/dhcp-3.0/common/tables.c b/contrib/dhcp-3.0/common/tables.c new file mode 100644 index 0000000000..8c4529f84a --- /dev/null +++ b/contrib/dhcp-3.0/common/tables.c @@ -0,0 +1,1240 @@ +/* tables.c + + Tables of information... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: tables.c,v 1.51.2.9 2004/09/01 17:06:35 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +/* XXXDPN: Moved here from hash.c, when it moved to libomapi. Not sure + where these really belong. */ +HASH_FUNCTIONS (group, const char *, struct group_object, group_hash_t, + group_reference, group_dereference) +HASH_FUNCTIONS (universe, const char *, struct universe, universe_hash_t, 0, 0) +HASH_FUNCTIONS (option, const char *, struct option, option_hash_t, 0, 0) + +/* DHCP Option names, formats and codes, from RFC1533. + + Format codes: + + I - IP address + l - 32-bit signed integer + L - 32-bit unsigned integer + s - 16-bit signed integer + S - 16-bit unsigned integer + b - 8-bit signed integer + B - 8-bit unsigned integer + t - ASCII text + f - flag (true or false) + A - array of whatever precedes (e.g., IA means array of IP addresses) + a - array of the preceding character (e.g., IIa means two or more IP + addresses) + U - name of an option space (universe) + F - implicit flag - the presence of the option indicates that the + flag is true. + o - the preceding value is optional. + E - encapsulation, string or colon-seperated hex list (the latter + two for parsing). E is followed by a text string containing + the name of the option space to encapsulate, followed by a '.'. + If the E is immediately followed by '.', the applicable vendor + option space is used if one is defined. + e - If an encapsulation directive is not the first thing in the string, + the option scanner requires an efficient way to find the encapsulation. + This is done by placing a 'e' at the beginning of the option. The + 'e' has no other purpose, and is not required if 'E' is the first + thing in the option. + X - either an ASCII string or binary data. On output, the string is + scanned to see if it's printable ASCII and, if so, output as a + quoted string. If not, it's output as colon-seperated hex. On + input, the option can be specified either as a quoted string or as + a colon-seperated hex list. + N - enumeration. N is followed by a text string containing + the name of the set of enumeration values to parse or emit, + followed by a '.'. The width of the data is specified in the + named enumeration. Named enumerations are tracked in parse.c. + d - Domain name (i.e., FOO or FOO.BAR). +*/ + +struct universe dhcp_universe; +struct option dhcp_options [256] = { + { "pad", "", &dhcp_universe, 0 }, + { "subnet-mask", "I", &dhcp_universe, 1 }, + { "time-offset", "l", &dhcp_universe, 2 }, + { "routers", "IA", &dhcp_universe, 3 }, + { "time-servers", "IA", &dhcp_universe, 4 }, + { "ien116-name-servers", "IA", &dhcp_universe, 5 }, + { "domain-name-servers", "IA", &dhcp_universe, 6 }, + { "log-servers", "IA", &dhcp_universe, 7 }, + { "cookie-servers", "IA", &dhcp_universe, 8 }, + { "lpr-servers", "IA", &dhcp_universe, 9 }, + { "impress-servers", "IA", &dhcp_universe, 10 }, + { "resource-location-servers", "IA", &dhcp_universe, 11 }, + { "host-name", "X", &dhcp_universe, 12 }, + { "boot-size", "S", &dhcp_universe, 13 }, + { "merit-dump", "t", &dhcp_universe, 14 }, + { "domain-name", "t", &dhcp_universe, 15 }, + { "swap-server", "I", &dhcp_universe, 16 }, + { "root-path", "t", &dhcp_universe, 17 }, + { "extensions-path", "t", &dhcp_universe, 18 }, + { "ip-forwarding", "f", &dhcp_universe, 19 }, + { "non-local-source-routing", "f", &dhcp_universe, 20 }, + { "policy-filter", "IIA", &dhcp_universe, 21 }, + { "max-dgram-reassembly", "S", &dhcp_universe, 22 }, + { "default-ip-ttl", "B", &dhcp_universe, 23 }, + { "path-mtu-aging-timeout", "L", &dhcp_universe, 24 }, + { "path-mtu-plateau-table", "SA", &dhcp_universe, 25 }, + { "interface-mtu", "S", &dhcp_universe, 26 }, + { "all-subnets-local", "f", &dhcp_universe, 27 }, + { "broadcast-address", "I", &dhcp_universe, 28 }, + { "perform-mask-discovery", "f", &dhcp_universe, 29 }, + { "mask-supplier", "f", &dhcp_universe, 30 }, + { "router-discovery", "f", &dhcp_universe, 31 }, + { "router-solicitation-address", "I", &dhcp_universe, 32 }, + { "static-routes", "IIA", &dhcp_universe, 33 }, + { "trailer-encapsulation", "f", &dhcp_universe, 34 }, + { "arp-cache-timeout", "L", &dhcp_universe, 35 }, + { "ieee802-3-encapsulation", "f", &dhcp_universe, 36 }, + { "default-tcp-ttl", "B", &dhcp_universe, 37 }, + { "tcp-keepalive-interval", "L", &dhcp_universe, 38 }, + { "tcp-keepalive-garbage", "f", &dhcp_universe, 39 }, + { "nis-domain", "t", &dhcp_universe, 40 }, + { "nis-servers", "IA", &dhcp_universe, 41 }, + { "ntp-servers", "IA", &dhcp_universe, 42 }, + { "vendor-encapsulated-options", "E.", &dhcp_universe, 43 }, + { "netbios-name-servers", "IA", &dhcp_universe, 44 }, + { "netbios-dd-server", "IA", &dhcp_universe, 45 }, + { "netbios-node-type", "B", &dhcp_universe, 46 }, + { "netbios-scope", "t", &dhcp_universe, 47 }, + { "font-servers", "IA", &dhcp_universe, 48 }, + { "x-display-manager", "IA", &dhcp_universe, 49 }, + { "dhcp-requested-address", "I", &dhcp_universe, 50 }, + { "dhcp-lease-time", "L", &dhcp_universe, 51 }, + { "dhcp-option-overload", "B", &dhcp_universe, 52 }, + { "dhcp-message-type", "B", &dhcp_universe, 53 }, + { "dhcp-server-identifier", "I", &dhcp_universe, 54 }, + { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55 }, + { "dhcp-message", "t", &dhcp_universe, 56 }, + { "dhcp-max-message-size", "S", &dhcp_universe, 57 }, + { "dhcp-renewal-time", "L", &dhcp_universe, 58 }, + { "dhcp-rebinding-time", "L", &dhcp_universe, 59 }, + { "vendor-class-identifier", "X", &dhcp_universe, 60 }, + { "dhcp-client-identifier", "X", &dhcp_universe, 61 }, + { "nwip-domain", "X", &dhcp_universe, 62 }, + { "nwip-suboptions", "Enwip.", &dhcp_universe, 63 }, + { "nisplus-domain", "t", &dhcp_universe, 64 }, + { "nisplus-servers", "IA", &dhcp_universe, 65 }, + { "tftp-server-name", "t", &dhcp_universe, 66 }, + { "bootfile-name", "t", &dhcp_universe, 67 }, + { "mobile-ip-home-agent", "IA", &dhcp_universe, 68 }, + { "smtp-server", "IA", &dhcp_universe, 69 }, + { "pop-server", "IA", &dhcp_universe, 70 }, + { "nntp-server", "IA", &dhcp_universe, 71 }, + { "www-server", "IA", &dhcp_universe, 72 }, + { "finger-server", "IA", &dhcp_universe, 73 }, + { "irc-server", "IA", &dhcp_universe, 74 }, + { "streettalk-server", "IA", &dhcp_universe, 75 }, + { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 }, + { "user-class", "t", &dhcp_universe, 77 }, + { "slp-directory-agent", "fIa", &dhcp_universe, 78 }, + { "slp-service-scope", "fto", &dhcp_universe, 79 }, + { "unknown-80", "X", &dhcp_universe, 80 }, + { "fqdn", "Efqdn.", &dhcp_universe, 81 }, + { "relay-agent-information", "Eagent.", &dhcp_universe, 82 }, + { "unknown-83", "X", &dhcp_universe, 83 }, + { "unknown-84", "X", &dhcp_universe, 84 }, + { "nds-servers", "IA", &dhcp_universe, 85 }, + { "nds-tree-name", "X", &dhcp_universe, 86 }, + { "nds-context", "X", &dhcp_universe, 87 }, + { "unknown-88", "X", &dhcp_universe, 88 }, + { "unknown-89", "X", &dhcp_universe, 89 }, + { "unknown-90", "X", &dhcp_universe, 90 }, + { "unknown-91", "X", &dhcp_universe, 91 }, + { "unknown-92", "X", &dhcp_universe, 92 }, + { "unknown-93", "X", &dhcp_universe, 93 }, + { "unknown-94", "X", &dhcp_universe, 94 }, + { "unknown-95", "X", &dhcp_universe, 95 }, + { "unknown-96", "X", &dhcp_universe, 96 }, + { "unknown-97", "X", &dhcp_universe, 97 }, + { "uap-servers", "t", &dhcp_universe, 98 }, + { "unknown-99", "X", &dhcp_universe, 99 }, + { "unknown-100", "X", &dhcp_universe, 100 }, + { "unknown-101", "X", &dhcp_universe, 101 }, + { "unknown-102", "X", &dhcp_universe, 102 }, + { "unknown-103", "X", &dhcp_universe, 103 }, + { "unknown-104", "X", &dhcp_universe, 104 }, + { "unknown-105", "X", &dhcp_universe, 105 }, + { "unknown-106", "X", &dhcp_universe, 106 }, + { "unknown-107", "X", &dhcp_universe, 107 }, + { "unknown-108", "X", &dhcp_universe, 108 }, + { "unknown-109", "X", &dhcp_universe, 109 }, + { "unknown-110", "X", &dhcp_universe, 110 }, + { "unknown-111", "X", &dhcp_universe, 111 }, + { "unknown-112", "X", &dhcp_universe, 112 }, + { "unknown-113", "X", &dhcp_universe, 113 }, + { "unknown-114", "X", &dhcp_universe, 114 }, + { "unknown-115", "X", &dhcp_universe, 115 }, + { "unknown-116", "X", &dhcp_universe, 116 }, + { "unknown-117", "X", &dhcp_universe, 117 }, + { "subnet-selection", "X", &dhcp_universe, 118 }, + { "unknown-119", "X", &dhcp_universe, 119 }, + { "unknown-120", "X", &dhcp_universe, 120 }, + { "unknown-121", "X", &dhcp_universe, 121 }, + { "unknown-122", "X", &dhcp_universe, 122 }, + { "unknown-123", "X", &dhcp_universe, 123 }, + { "unknown-124", "X", &dhcp_universe, 124 }, + { "unknown-125", "X", &dhcp_universe, 125 }, + { "unknown-126", "X", &dhcp_universe, 126 }, + { "unknown-127", "X", &dhcp_universe, 127 }, + { "unknown-128", "X", &dhcp_universe, 128 }, + { "unknown-129", "X", &dhcp_universe, 129 }, + { "unknown-130", "X", &dhcp_universe, 130 }, + { "unknown-131", "X", &dhcp_universe, 131 }, + { "unknown-132", "X", &dhcp_universe, 132 }, + { "unknown-133", "X", &dhcp_universe, 133 }, + { "unknown-134", "X", &dhcp_universe, 134 }, + { "unknown-135", "X", &dhcp_universe, 135 }, + { "unknown-136", "X", &dhcp_universe, 136 }, + { "unknown-137", "X", &dhcp_universe, 137 }, + { "unknown-138", "X", &dhcp_universe, 138 }, + { "unknown-139", "X", &dhcp_universe, 139 }, + { "unknown-140", "X", &dhcp_universe, 140 }, + { "unknown-141", "X", &dhcp_universe, 141 }, + { "unknown-142", "X", &dhcp_universe, 142 }, + { "unknown-143", "X", &dhcp_universe, 143 }, + { "unknown-144", "X", &dhcp_universe, 144 }, + { "unknown-145", "X", &dhcp_universe, 145 }, + { "unknown-146", "X", &dhcp_universe, 146 }, + { "unknown-147", "X", &dhcp_universe, 147 }, + { "unknown-148", "X", &dhcp_universe, 148 }, + { "unknown-149", "X", &dhcp_universe, 149 }, + { "unknown-150", "X", &dhcp_universe, 150 }, + { "unknown-151", "X", &dhcp_universe, 151 }, + { "unknown-152", "X", &dhcp_universe, 152 }, + { "unknown-153", "X", &dhcp_universe, 153 }, + { "unknown-154", "X", &dhcp_universe, 154 }, + { "unknown-155", "X", &dhcp_universe, 155 }, + { "unknown-156", "X", &dhcp_universe, 156 }, + { "unknown-157", "X", &dhcp_universe, 157 }, + { "unknown-158", "X", &dhcp_universe, 158 }, + { "unknown-159", "X", &dhcp_universe, 159 }, + { "unknown-160", "X", &dhcp_universe, 160 }, + { "unknown-161", "X", &dhcp_universe, 161 }, + { "unknown-162", "X", &dhcp_universe, 162 }, + { "unknown-163", "X", &dhcp_universe, 163 }, + { "unknown-164", "X", &dhcp_universe, 164 }, + { "unknown-165", "X", &dhcp_universe, 165 }, + { "unknown-166", "X", &dhcp_universe, 166 }, + { "unknown-167", "X", &dhcp_universe, 167 }, + { "unknown-168", "X", &dhcp_universe, 168 }, + { "unknown-169", "X", &dhcp_universe, 169 }, + { "unknown-170", "X", &dhcp_universe, 170 }, + { "unknown-171", "X", &dhcp_universe, 171 }, + { "unknown-172", "X", &dhcp_universe, 172 }, + { "unknown-173", "X", &dhcp_universe, 173 }, + { "unknown-174", "X", &dhcp_universe, 174 }, + { "unknown-175", "X", &dhcp_universe, 175 }, + { "unknown-176", "X", &dhcp_universe, 176 }, + { "unknown-177", "X", &dhcp_universe, 177 }, + { "unknown-178", "X", &dhcp_universe, 178 }, + { "unknown-179", "X", &dhcp_universe, 179 }, + { "unknown-180", "X", &dhcp_universe, 180 }, + { "unknown-181", "X", &dhcp_universe, 181 }, + { "unknown-182", "X", &dhcp_universe, 182 }, + { "unknown-183", "X", &dhcp_universe, 183 }, + { "unknown-184", "X", &dhcp_universe, 184 }, + { "unknown-185", "X", &dhcp_universe, 185 }, + { "unknown-186", "X", &dhcp_universe, 186 }, + { "unknown-187", "X", &dhcp_universe, 187 }, + { "unknown-188", "X", &dhcp_universe, 188 }, + { "unknown-189", "X", &dhcp_universe, 189 }, + { "unknown-190", "X", &dhcp_universe, 190 }, + { "unknown-191", "X", &dhcp_universe, 191 }, + { "unknown-192", "X", &dhcp_universe, 192 }, + { "unknown-193", "X", &dhcp_universe, 193 }, + { "unknown-194", "X", &dhcp_universe, 194 }, + { "unknown-195", "X", &dhcp_universe, 195 }, + { "unknown-196", "X", &dhcp_universe, 196 }, + { "unknown-197", "X", &dhcp_universe, 197 }, + { "unknown-198", "X", &dhcp_universe, 198 }, + { "unknown-199", "X", &dhcp_universe, 199 }, + { "unknown-200", "X", &dhcp_universe, 200 }, + { "unknown-201", "X", &dhcp_universe, 201 }, + { "unknown-202", "X", &dhcp_universe, 202 }, + { "unknown-203", "X", &dhcp_universe, 203 }, + { "unknown-204", "X", &dhcp_universe, 204 }, + { "unknown-205", "X", &dhcp_universe, 205 }, + { "unknown-206", "X", &dhcp_universe, 206 }, + { "unknown-207", "X", &dhcp_universe, 207 }, + { "unknown-208", "X", &dhcp_universe, 208 }, + { "unknown-209", "X", &dhcp_universe, 209 }, + { "authenticate", "X", &dhcp_universe, 210 }, + { "unknown-211", "X", &dhcp_universe, 211 }, + { "unknown-212", "X", &dhcp_universe, 212 }, + { "unknown-213", "X", &dhcp_universe, 213 }, + { "unknown-214", "X", &dhcp_universe, 214 }, + { "unknown-215", "X", &dhcp_universe, 215 }, + { "unknown-216", "X", &dhcp_universe, 216 }, + { "unknown-217", "X", &dhcp_universe, 217 }, + { "unknown-218", "X", &dhcp_universe, 218 }, + { "unknown-219", "X", &dhcp_universe, 219 }, + { "unknown-220", "X", &dhcp_universe, 220 }, + { "unknown-221", "X", &dhcp_universe, 221 }, + { "unknown-222", "X", &dhcp_universe, 222 }, + { "unknown-223", "X", &dhcp_universe, 223 }, + { "unknown-224", "X", &dhcp_universe, 224 }, + { "unknown-225", "X", &dhcp_universe, 225 }, + { "unknown-226", "X", &dhcp_universe, 226 }, + { "unknown-227", "X", &dhcp_universe, 227 }, + { "unknown-228", "X", &dhcp_universe, 228 }, + { "unknown-229", "X", &dhcp_universe, 229 }, + { "unknown-230", "X", &dhcp_universe, 230 }, + { "unknown-231", "X", &dhcp_universe, 231 }, + { "unknown-232", "X", &dhcp_universe, 232 }, + { "unknown-233", "X", &dhcp_universe, 233 }, + { "unknown-234", "X", &dhcp_universe, 234 }, + { "unknown-235", "X", &dhcp_universe, 235 }, + { "unknown-236", "X", &dhcp_universe, 236 }, + { "unknown-237", "X", &dhcp_universe, 237 }, + { "unknown-238", "X", &dhcp_universe, 238 }, + { "unknown-239", "X", &dhcp_universe, 239 }, + { "unknown-240", "X", &dhcp_universe, 240 }, + { "unknown-241", "X", &dhcp_universe, 241 }, + { "unknown-242", "X", &dhcp_universe, 242 }, + { "unknown-243", "X", &dhcp_universe, 243 }, + { "unknown-244", "X", &dhcp_universe, 244 }, + { "unknown-245", "X", &dhcp_universe, 245 }, + { "unknown-246", "X", &dhcp_universe, 246 }, + { "unknown-247", "X", &dhcp_universe, 247 }, + { "unknown-248", "X", &dhcp_universe, 248 }, + { "unknown-249", "X", &dhcp_universe, 249 }, + { "unknown-250", "X", &dhcp_universe, 250 }, + { "unknown-251", "X", &dhcp_universe, 251 }, + { "unknown-252", "X", &dhcp_universe, 252 }, + { "unknown-253", "X", &dhcp_universe, 253 }, + { "unknown-254", "X", &dhcp_universe, 254 }, + { "option-end", "e", &dhcp_universe, 255 }, +}; + +struct universe nwip_universe; +struct option nwip_options [256] = { + { "pad", "", &nwip_universe, 0 }, + { "illegal-1", "", &nwip_universe, 1 }, + { "illegal-2", "", &nwip_universe, 2 }, + { "illegal-3", "", &nwip_universe, 3 }, + { "illegal-4", "", &nwip_universe, 4 }, + { "nsq-broadcast", "f", &nwip_universe, 5 }, + { "preferred-dss", "IA", &nwip_universe, 6 }, + { "nearest-nwip-server", "IA", &nwip_universe, 7 }, + { "autoretries", "B", &nwip_universe, 8 }, + { "autoretry-secs", "B", &nwip_universe, 9 }, + { "nwip-1-1", "f", &nwip_universe, 10 }, + { "primary-dss", "I", &nwip_universe, 11 }, + { "unknown-12", "X", &nwip_universe, 12 }, + { "unknown-13", "X", &nwip_universe, 13 }, + { "unknown-14", "X", &nwip_universe, 14 }, + { "unknown-15", "X", &nwip_universe, 15 }, + { "unknown-16", "X", &nwip_universe, 16 }, + { "unknown-17", "X", &nwip_universe, 17 }, + { "unknown-18", "X", &nwip_universe, 18 }, + { "unknown-19", "X", &nwip_universe, 19 }, + { "unknown-20", "X", &nwip_universe, 20 }, + { "unknown-21", "X", &nwip_universe, 21 }, + { "unknown-22", "X", &nwip_universe, 22 }, + { "unknown-23", "X", &nwip_universe, 23 }, + { "unknown-24", "X", &nwip_universe, 24 }, + { "unknown-25", "X", &nwip_universe, 25 }, + { "unknown-26", "X", &nwip_universe, 26 }, + { "unknown-27", "X", &nwip_universe, 27 }, + { "unknown-28", "X", &nwip_universe, 28 }, + { "unknown-29", "X", &nwip_universe, 29 }, + { "unknown-30", "X", &nwip_universe, 30 }, + { "unknown-31", "X", &nwip_universe, 31 }, + { "unknown-32", "X", &nwip_universe, 32 }, + { "unknown-33", "X", &nwip_universe, 33 }, + { "unknown-34", "X", &nwip_universe, 34 }, + { "unknown-35", "X", &nwip_universe, 35 }, + { "unknown-36", "X", &nwip_universe, 36 }, + { "unknown-37", "X", &nwip_universe, 37 }, + { "unknown-38", "X", &nwip_universe, 38 }, + { "unknown-39", "X", &nwip_universe, 39 }, + { "unknown-40", "X", &nwip_universe, 40 }, + { "unknown-41", "X", &nwip_universe, 41 }, + { "unknown-42", "X", &nwip_universe, 42 }, + { "unknown-43", "X", &nwip_universe, 43 }, + { "unknown-44", "X", &nwip_universe, 44 }, + { "unknown-45", "X", &nwip_universe, 45 }, + { "unknown-46", "X", &nwip_universe, 46 }, + { "unknown-47", "X", &nwip_universe, 47 }, + { "unknown-48", "X", &nwip_universe, 48 }, + { "unknown-49", "X", &nwip_universe, 49 }, + { "unknown-50", "X", &nwip_universe, 50 }, + { "unknown-51", "X", &nwip_universe, 51 }, + { "unknown-52", "X", &nwip_universe, 52 }, + { "unknown-53", "X", &nwip_universe, 53 }, + { "unknown-54", "X", &nwip_universe, 54 }, + { "unknown-55", "X", &nwip_universe, 55 }, + { "unknown-56", "X", &nwip_universe, 56 }, + { "unknown-57", "X", &nwip_universe, 57 }, + { "unknown-58", "X", &nwip_universe, 58 }, + { "unknown-59", "X", &nwip_universe, 59 }, + { "unknown-60", "X", &nwip_universe, 60 }, + { "unknown-61", "X", &nwip_universe, 61 }, + { "unknown-62", "X", &nwip_universe, 62 }, + { "unknown-63", "X", &nwip_universe, 63 }, + { "unknown-64", "X", &nwip_universe, 64 }, + { "unknown-65", "X", &nwip_universe, 65 }, + { "unknown-66", "X", &nwip_universe, 66 }, + { "unknown-67", "X", &nwip_universe, 67 }, + { "unknown-68", "X", &nwip_universe, 68 }, + { "unknown-69", "X", &nwip_universe, 69 }, + { "unknown-70", "X", &nwip_universe, 70 }, + { "unknown-71", "X", &nwip_universe, 71 }, + { "unknown-72", "X", &nwip_universe, 72 }, + { "unknown-73", "X", &nwip_universe, 73 }, + { "unknown-74", "X", &nwip_universe, 74 }, + { "unknown-75", "X", &nwip_universe, 75 }, + { "unknown-76", "X", &nwip_universe, 76 }, + { "unknown-77", "X", &nwip_universe, 77 }, + { "unknown-78", "X", &nwip_universe, 78 }, + { "unknown-79", "X", &nwip_universe, 79 }, + { "unknown-80", "X", &nwip_universe, 80 }, + { "unknown-81", "X", &nwip_universe, 81 }, + { "unknown-82", "X", &nwip_universe, 82 }, + { "unknown-83", "X", &nwip_universe, 83 }, + { "unknown-84", "X", &nwip_universe, 84 }, + { "unknown-85", "X", &nwip_universe, 85 }, + { "unknown-86", "X", &nwip_universe, 86 }, + { "unknown-87", "X", &nwip_universe, 87 }, + { "unknown-88", "X", &nwip_universe, 88 }, + { "unknown-89", "X", &nwip_universe, 89 }, + { "unknown-90", "X", &nwip_universe, 90 }, + { "unknown-91", "X", &nwip_universe, 91 }, + { "unknown-92", "X", &nwip_universe, 92 }, + { "unknown-93", "X", &nwip_universe, 93 }, + { "unknown-94", "X", &nwip_universe, 94 }, + { "unknown-95", "X", &nwip_universe, 95 }, + { "unknown-96", "X", &nwip_universe, 96 }, + { "unknown-97", "X", &nwip_universe, 97 }, + { "unknown-98", "X", &nwip_universe, 98 }, + { "unknown-99", "X", &nwip_universe, 99 }, + { "unknown-100", "X", &nwip_universe, 100 }, + { "unknown-101", "X", &nwip_universe, 101 }, + { "unknown-102", "X", &nwip_universe, 102 }, + { "unknown-103", "X", &nwip_universe, 103 }, + { "unknown-104", "X", &nwip_universe, 104 }, + { "unknown-105", "X", &nwip_universe, 105 }, + { "unknown-106", "X", &nwip_universe, 106 }, + { "unknown-107", "X", &nwip_universe, 107 }, + { "unknown-108", "X", &nwip_universe, 108 }, + { "unknown-109", "X", &nwip_universe, 109 }, + { "unknown-110", "X", &nwip_universe, 110 }, + { "unknown-111", "X", &nwip_universe, 111 }, + { "unknown-112", "X", &nwip_universe, 112 }, + { "unknown-113", "X", &nwip_universe, 113 }, + { "unknown-114", "X", &nwip_universe, 114 }, + { "unknown-115", "X", &nwip_universe, 115 }, + { "unknown-116", "X", &nwip_universe, 116 }, + { "unknown-117", "X", &nwip_universe, 117 }, + { "unknown-118", "X", &nwip_universe, 118 }, + { "unknown-119", "X", &nwip_universe, 119 }, + { "unknown-120", "X", &nwip_universe, 120 }, + { "unknown-121", "X", &nwip_universe, 121 }, + { "unknown-122", "X", &nwip_universe, 122 }, + { "unknown-123", "X", &nwip_universe, 123 }, + { "unknown-124", "X", &nwip_universe, 124 }, + { "unknown-125", "X", &nwip_universe, 125 }, + { "unknown-126", "X", &nwip_universe, 126 }, + { "unknown-127", "X", &nwip_universe, 127 }, + { "unknown-128", "X", &nwip_universe, 128 }, + { "unknown-129", "X", &nwip_universe, 129 }, + { "unknown-130", "X", &nwip_universe, 130 }, + { "unknown-131", "X", &nwip_universe, 131 }, + { "unknown-132", "X", &nwip_universe, 132 }, + { "unknown-133", "X", &nwip_universe, 133 }, + { "unknown-134", "X", &nwip_universe, 134 }, + { "unknown-135", "X", &nwip_universe, 135 }, + { "unknown-136", "X", &nwip_universe, 136 }, + { "unknown-137", "X", &nwip_universe, 137 }, + { "unknown-138", "X", &nwip_universe, 138 }, + { "unknown-139", "X", &nwip_universe, 139 }, + { "unknown-140", "X", &nwip_universe, 140 }, + { "unknown-141", "X", &nwip_universe, 141 }, + { "unknown-142", "X", &nwip_universe, 142 }, + { "unknown-143", "X", &nwip_universe, 143 }, + { "unknown-144", "X", &nwip_universe, 144 }, + { "unknown-145", "X", &nwip_universe, 145 }, + { "unknown-146", "X", &nwip_universe, 146 }, + { "unknown-147", "X", &nwip_universe, 147 }, + { "unknown-148", "X", &nwip_universe, 148 }, + { "unknown-149", "X", &nwip_universe, 149 }, + { "unknown-150", "X", &nwip_universe, 150 }, + { "unknown-151", "X", &nwip_universe, 151 }, + { "unknown-152", "X", &nwip_universe, 152 }, + { "unknown-153", "X", &nwip_universe, 153 }, + { "unknown-154", "X", &nwip_universe, 154 }, + { "unknown-155", "X", &nwip_universe, 155 }, + { "unknown-156", "X", &nwip_universe, 156 }, + { "unknown-157", "X", &nwip_universe, 157 }, + { "unknown-158", "X", &nwip_universe, 158 }, + { "unknown-159", "X", &nwip_universe, 159 }, + { "unknown-160", "X", &nwip_universe, 160 }, + { "unknown-161", "X", &nwip_universe, 161 }, + { "unknown-162", "X", &nwip_universe, 162 }, + { "unknown-163", "X", &nwip_universe, 163 }, + { "unknown-164", "X", &nwip_universe, 164 }, + { "unknown-165", "X", &nwip_universe, 165 }, + { "unknown-166", "X", &nwip_universe, 166 }, + { "unknown-167", "X", &nwip_universe, 167 }, + { "unknown-168", "X", &nwip_universe, 168 }, + { "unknown-169", "X", &nwip_universe, 169 }, + { "unknown-170", "X", &nwip_universe, 170 }, + { "unknown-171", "X", &nwip_universe, 171 }, + { "unknown-172", "X", &nwip_universe, 172 }, + { "unknown-173", "X", &nwip_universe, 173 }, + { "unknown-174", "X", &nwip_universe, 174 }, + { "unknown-175", "X", &nwip_universe, 175 }, + { "unknown-176", "X", &nwip_universe, 176 }, + { "unknown-177", "X", &nwip_universe, 177 }, + { "unknown-178", "X", &nwip_universe, 178 }, + { "unknown-179", "X", &nwip_universe, 179 }, + { "unknown-180", "X", &nwip_universe, 180 }, + { "unknown-181", "X", &nwip_universe, 181 }, + { "unknown-182", "X", &nwip_universe, 182 }, + { "unknown-183", "X", &nwip_universe, 183 }, + { "unknown-184", "X", &nwip_universe, 184 }, + { "unknown-185", "X", &nwip_universe, 185 }, + { "unknown-186", "X", &nwip_universe, 186 }, + { "unknown-187", "X", &nwip_universe, 187 }, + { "unknown-188", "X", &nwip_universe, 188 }, + { "unknown-189", "X", &nwip_universe, 189 }, + { "unknown-190", "X", &nwip_universe, 190 }, + { "unknown-191", "X", &nwip_universe, 191 }, + { "unknown-192", "X", &nwip_universe, 192 }, + { "unknown-193", "X", &nwip_universe, 193 }, + { "unknown-194", "X", &nwip_universe, 194 }, + { "unknown-195", "X", &nwip_universe, 195 }, + { "unknown-196", "X", &nwip_universe, 196 }, + { "unknown-197", "X", &nwip_universe, 197 }, + { "unknown-198", "X", &nwip_universe, 198 }, + { "unknown-199", "X", &nwip_universe, 199 }, + { "unknown-200", "X", &nwip_universe, 200 }, + { "unknown-201", "X", &nwip_universe, 201 }, + { "unknown-202", "X", &nwip_universe, 202 }, + { "unknown-203", "X", &nwip_universe, 203 }, + { "unknown-204", "X", &nwip_universe, 204 }, + { "unknown-205", "X", &nwip_universe, 205 }, + { "unknown-206", "X", &nwip_universe, 206 }, + { "unknown-207", "X", &nwip_universe, 207 }, + { "unknown-208", "X", &nwip_universe, 208 }, + { "unknown-209", "X", &nwip_universe, 209 }, + { "unknown-210", "X", &nwip_universe, 210 }, + { "unknown-211", "X", &nwip_universe, 211 }, + { "unknown-212", "X", &nwip_universe, 212 }, + { "unknown-213", "X", &nwip_universe, 213 }, + { "unknown-214", "X", &nwip_universe, 214 }, + { "unknown-215", "X", &nwip_universe, 215 }, + { "unknown-216", "X", &nwip_universe, 216 }, + { "unknown-217", "X", &nwip_universe, 217 }, + { "unknown-218", "X", &nwip_universe, 218 }, + { "unknown-219", "X", &nwip_universe, 219 }, + { "unknown-220", "X", &nwip_universe, 220 }, + { "unknown-221", "X", &nwip_universe, 221 }, + { "unknown-222", "X", &nwip_universe, 222 }, + { "unknown-223", "X", &nwip_universe, 223 }, + { "unknown-224", "X", &nwip_universe, 224 }, + { "unknown-225", "X", &nwip_universe, 225 }, + { "unknown-226", "X", &nwip_universe, 226 }, + { "unknown-227", "X", &nwip_universe, 227 }, + { "unknown-228", "X", &nwip_universe, 228 }, + { "unknown-229", "X", &nwip_universe, 229 }, + { "unknown-230", "X", &nwip_universe, 230 }, + { "unknown-231", "X", &nwip_universe, 231 }, + { "unknown-232", "X", &nwip_universe, 232 }, + { "unknown-233", "X", &nwip_universe, 233 }, + { "unknown-234", "X", &nwip_universe, 234 }, + { "unknown-235", "X", &nwip_universe, 235 }, + { "unknown-236", "X", &nwip_universe, 236 }, + { "unknown-237", "X", &nwip_universe, 237 }, + { "unknown-238", "X", &nwip_universe, 238 }, + { "unknown-239", "X", &nwip_universe, 239 }, + { "unknown-240", "X", &nwip_universe, 240 }, + { "unknown-241", "X", &nwip_universe, 241 }, + { "unknown-242", "X", &nwip_universe, 242 }, + { "unknown-243", "X", &nwip_universe, 243 }, + { "unknown-244", "X", &nwip_universe, 244 }, + { "unknown-245", "X", &nwip_universe, 245 }, + { "unknown-246", "X", &nwip_universe, 246 }, + { "unknown-247", "X", &nwip_universe, 247 }, + { "unknown-248", "X", &nwip_universe, 248 }, + { "unknown-249", "X", &nwip_universe, 249 }, + { "unknown-250", "X", &nwip_universe, 250 }, + { "unknown-251", "X", &nwip_universe, 251 }, + { "unknown-252", "X", &nwip_universe, 252 }, + { "unknown-253", "X", &nwip_universe, 253 }, + { "unknown-254", "X", &nwip_universe, 254 }, + { "unknown-end", "e", &nwip_universe, 255 }, +}; + +struct universe fqdn_universe; +struct option fqdn_options [256] = { + { "pad", "", &fqdn_universe, 0 }, + { "no-client-update", "f", &fqdn_universe, 1 }, + { "server-update", "f", &fqdn_universe, 2 }, + { "encoded", "f", &fqdn_universe, 3 }, + { "rcode1", "B", &fqdn_universe, 4 }, + { "rcode2", "B", &fqdn_universe, 5 }, + { "hostname", "t", &fqdn_universe, 6 }, + { "domainname", "t", &fqdn_universe, 7 }, + { "fqdn", "t", &fqdn_universe, 8 }, + { "unknown-9", "X", &fqdn_universe, 9 }, + { "unknown-10", "X", &fqdn_universe, 10 }, + { "unknown-11", "X", &fqdn_universe, 11 }, + { "unknown-12", "X", &fqdn_universe, 12 }, + { "unknown-13", "X", &fqdn_universe, 13 }, + { "unknown-14", "X", &fqdn_universe, 14 }, + { "unknown-15", "X", &fqdn_universe, 15 }, + { "unknown-16", "X", &fqdn_universe, 16 }, + { "unknown-17", "X", &fqdn_universe, 17 }, + { "unknown-18", "X", &fqdn_universe, 18 }, + { "unknown-19", "X", &fqdn_universe, 19 }, + { "unknown-20", "X", &fqdn_universe, 20 }, + { "unknown-21", "X", &fqdn_universe, 21 }, + { "unknown-22", "X", &fqdn_universe, 22 }, + { "unknown-23", "X", &fqdn_universe, 23 }, + { "unknown-24", "X", &fqdn_universe, 24 }, + { "unknown-25", "X", &fqdn_universe, 25 }, + { "unknown-26", "X", &fqdn_universe, 26 }, + { "unknown-27", "X", &fqdn_universe, 27 }, + { "unknown-28", "X", &fqdn_universe, 28 }, + { "unknown-29", "X", &fqdn_universe, 29 }, + { "unknown-30", "X", &fqdn_universe, 30 }, + { "unknown-31", "X", &fqdn_universe, 31 }, + { "unknown-32", "X", &fqdn_universe, 32 }, + { "unknown-33", "X", &fqdn_universe, 33 }, + { "unknown-34", "X", &fqdn_universe, 34 }, + { "unknown-35", "X", &fqdn_universe, 35 }, + { "unknown-36", "X", &fqdn_universe, 36 }, + { "unknown-37", "X", &fqdn_universe, 37 }, + { "unknown-38", "X", &fqdn_universe, 38 }, + { "unknown-39", "X", &fqdn_universe, 39 }, + { "unknown-40", "X", &fqdn_universe, 40 }, + { "unknown-41", "X", &fqdn_universe, 41 }, + { "unknown-42", "X", &fqdn_universe, 42 }, + { "unknown-43", "X", &fqdn_universe, 43 }, + { "unknown-44", "X", &fqdn_universe, 44 }, + { "unknown-45", "X", &fqdn_universe, 45 }, + { "unknown-46", "X", &fqdn_universe, 46 }, + { "unknown-47", "X", &fqdn_universe, 47 }, + { "unknown-48", "X", &fqdn_universe, 48 }, + { "unknown-49", "X", &fqdn_universe, 49 }, + { "unknown-50", "X", &fqdn_universe, 50 }, + { "unknown-51", "X", &fqdn_universe, 51 }, + { "unknown-52", "X", &fqdn_universe, 52 }, + { "unknown-53", "X", &fqdn_universe, 53 }, + { "unknown-54", "X", &fqdn_universe, 54 }, + { "unknown-55", "X", &fqdn_universe, 55 }, + { "unknown-56", "X", &fqdn_universe, 56 }, + { "unknown-57", "X", &fqdn_universe, 57 }, + { "unknown-58", "X", &fqdn_universe, 58 }, + { "unknown-59", "X", &fqdn_universe, 59 }, + { "unknown-60", "X", &fqdn_universe, 60 }, + { "unknown-61", "X", &fqdn_universe, 61 }, + { "unknown-62", "X", &fqdn_universe, 62 }, + { "unknown-63", "X", &fqdn_universe, 63 }, + { "unknown-64", "X", &fqdn_universe, 64 }, + { "unknown-65", "X", &fqdn_universe, 65 }, + { "unknown-66", "X", &fqdn_universe, 66 }, + { "unknown-67", "X", &fqdn_universe, 67 }, + { "unknown-68", "X", &fqdn_universe, 68 }, + { "unknown-69", "X", &fqdn_universe, 69 }, + { "unknown-70", "X", &fqdn_universe, 70 }, + { "unknown-71", "X", &fqdn_universe, 71 }, + { "unknown-72", "X", &fqdn_universe, 72 }, + { "unknown-73", "X", &fqdn_universe, 73 }, + { "unknown-74", "X", &fqdn_universe, 74 }, + { "unknown-75", "X", &fqdn_universe, 75 }, + { "unknown-76", "X", &fqdn_universe, 76 }, + { "unknown-77", "X", &fqdn_universe, 77 }, + { "unknown-78", "X", &fqdn_universe, 78 }, + { "unknown-79", "X", &fqdn_universe, 79 }, + { "unknown-80", "X", &fqdn_universe, 80 }, + { "unknown-81", "X", &fqdn_universe, 81 }, + { "unknown-82", "X", &fqdn_universe, 82 }, + { "unknown-83", "X", &fqdn_universe, 83 }, + { "unknown-84", "X", &fqdn_universe, 84 }, + { "unknown-85", "X", &fqdn_universe, 85 }, + { "unknown-86", "X", &fqdn_universe, 86 }, + { "unknown-87", "X", &fqdn_universe, 87 }, + { "unknown-88", "X", &fqdn_universe, 88 }, + { "unknown-89", "X", &fqdn_universe, 89 }, + { "unknown-90", "X", &fqdn_universe, 90 }, + { "unknown-91", "X", &fqdn_universe, 91 }, + { "unknown-92", "X", &fqdn_universe, 92 }, + { "unknown-93", "X", &fqdn_universe, 93 }, + { "unknown-94", "X", &fqdn_universe, 94 }, + { "unknown-95", "X", &fqdn_universe, 95 }, + { "unknown-96", "X", &fqdn_universe, 96 }, + { "unknown-97", "X", &fqdn_universe, 97 }, + { "unknown-98", "X", &fqdn_universe, 98 }, + { "unknown-99", "X", &fqdn_universe, 99 }, + { "unknown-100", "X", &fqdn_universe, 100 }, + { "unknown-101", "X", &fqdn_universe, 101 }, + { "unknown-102", "X", &fqdn_universe, 102 }, + { "unknown-103", "X", &fqdn_universe, 103 }, + { "unknown-104", "X", &fqdn_universe, 104 }, + { "unknown-105", "X", &fqdn_universe, 105 }, + { "unknown-106", "X", &fqdn_universe, 106 }, + { "unknown-107", "X", &fqdn_universe, 107 }, + { "unknown-108", "X", &fqdn_universe, 108 }, + { "unknown-109", "X", &fqdn_universe, 109 }, + { "unknown-110", "X", &fqdn_universe, 110 }, + { "unknown-111", "X", &fqdn_universe, 111 }, + { "unknown-112", "X", &fqdn_universe, 112 }, + { "unknown-113", "X", &fqdn_universe, 113 }, + { "unknown-114", "X", &fqdn_universe, 114 }, + { "unknown-115", "X", &fqdn_universe, 115 }, + { "unknown-116", "X", &fqdn_universe, 116 }, + { "unknown-117", "X", &fqdn_universe, 117 }, + { "unknown-118", "X", &fqdn_universe, 118 }, + { "unknown-119", "X", &fqdn_universe, 119 }, + { "unknown-120", "X", &fqdn_universe, 120 }, + { "unknown-121", "X", &fqdn_universe, 121 }, + { "unknown-122", "X", &fqdn_universe, 122 }, + { "unknown-123", "X", &fqdn_universe, 123 }, + { "unknown-124", "X", &fqdn_universe, 124 }, + { "unknown-125", "X", &fqdn_universe, 125 }, + { "unknown-126", "X", &fqdn_universe, 126 }, + { "unknown-127", "X", &fqdn_universe, 127 }, + { "unknown-128", "X", &fqdn_universe, 128 }, + { "unknown-129", "X", &fqdn_universe, 129 }, + { "unknown-130", "X", &fqdn_universe, 130 }, + { "unknown-131", "X", &fqdn_universe, 131 }, + { "unknown-132", "X", &fqdn_universe, 132 }, + { "unknown-133", "X", &fqdn_universe, 133 }, + { "unknown-134", "X", &fqdn_universe, 134 }, + { "unknown-135", "X", &fqdn_universe, 135 }, + { "unknown-136", "X", &fqdn_universe, 136 }, + { "unknown-137", "X", &fqdn_universe, 137 }, + { "unknown-138", "X", &fqdn_universe, 138 }, + { "unknown-139", "X", &fqdn_universe, 139 }, + { "unknown-140", "X", &fqdn_universe, 140 }, + { "unknown-141", "X", &fqdn_universe, 141 }, + { "unknown-142", "X", &fqdn_universe, 142 }, + { "unknown-143", "X", &fqdn_universe, 143 }, + { "unknown-144", "X", &fqdn_universe, 144 }, + { "unknown-145", "X", &fqdn_universe, 145 }, + { "unknown-146", "X", &fqdn_universe, 146 }, + { "unknown-147", "X", &fqdn_universe, 147 }, + { "unknown-148", "X", &fqdn_universe, 148 }, + { "unknown-149", "X", &fqdn_universe, 149 }, + { "unknown-150", "X", &fqdn_universe, 150 }, + { "unknown-151", "X", &fqdn_universe, 151 }, + { "unknown-152", "X", &fqdn_universe, 152 }, + { "unknown-153", "X", &fqdn_universe, 153 }, + { "unknown-154", "X", &fqdn_universe, 154 }, + { "unknown-155", "X", &fqdn_universe, 155 }, + { "unknown-156", "X", &fqdn_universe, 156 }, + { "unknown-157", "X", &fqdn_universe, 157 }, + { "unknown-158", "X", &fqdn_universe, 158 }, + { "unknown-159", "X", &fqdn_universe, 159 }, + { "unknown-160", "X", &fqdn_universe, 160 }, + { "unknown-161", "X", &fqdn_universe, 161 }, + { "unknown-162", "X", &fqdn_universe, 162 }, + { "unknown-163", "X", &fqdn_universe, 163 }, + { "unknown-164", "X", &fqdn_universe, 164 }, + { "unknown-165", "X", &fqdn_universe, 165 }, + { "unknown-166", "X", &fqdn_universe, 166 }, + { "unknown-167", "X", &fqdn_universe, 167 }, + { "unknown-168", "X", &fqdn_universe, 168 }, + { "unknown-169", "X", &fqdn_universe, 169 }, + { "unknown-170", "X", &fqdn_universe, 170 }, + { "unknown-171", "X", &fqdn_universe, 171 }, + { "unknown-172", "X", &fqdn_universe, 172 }, + { "unknown-173", "X", &fqdn_universe, 173 }, + { "unknown-174", "X", &fqdn_universe, 174 }, + { "unknown-175", "X", &fqdn_universe, 175 }, + { "unknown-176", "X", &fqdn_universe, 176 }, + { "unknown-177", "X", &fqdn_universe, 177 }, + { "unknown-178", "X", &fqdn_universe, 178 }, + { "unknown-179", "X", &fqdn_universe, 179 }, + { "unknown-180", "X", &fqdn_universe, 180 }, + { "unknown-181", "X", &fqdn_universe, 181 }, + { "unknown-182", "X", &fqdn_universe, 182 }, + { "unknown-183", "X", &fqdn_universe, 183 }, + { "unknown-184", "X", &fqdn_universe, 184 }, + { "unknown-185", "X", &fqdn_universe, 185 }, + { "unknown-186", "X", &fqdn_universe, 186 }, + { "unknown-187", "X", &fqdn_universe, 187 }, + { "unknown-188", "X", &fqdn_universe, 188 }, + { "unknown-189", "X", &fqdn_universe, 189 }, + { "unknown-190", "X", &fqdn_universe, 190 }, + { "unknown-191", "X", &fqdn_universe, 191 }, + { "unknown-192", "X", &fqdn_universe, 192 }, + { "unknown-193", "X", &fqdn_universe, 193 }, + { "unknown-194", "X", &fqdn_universe, 194 }, + { "unknown-195", "X", &fqdn_universe, 195 }, + { "unknown-196", "X", &fqdn_universe, 196 }, + { "unknown-197", "X", &fqdn_universe, 197 }, + { "unknown-198", "X", &fqdn_universe, 198 }, + { "unknown-199", "X", &fqdn_universe, 199 }, + { "unknown-200", "X", &fqdn_universe, 200 }, + { "unknown-201", "X", &fqdn_universe, 201 }, + { "unknown-202", "X", &fqdn_universe, 202 }, + { "unknown-203", "X", &fqdn_universe, 203 }, + { "unknown-204", "X", &fqdn_universe, 204 }, + { "unknown-205", "X", &fqdn_universe, 205 }, + { "unknown-206", "X", &fqdn_universe, 206 }, + { "unknown-207", "X", &fqdn_universe, 207 }, + { "unknown-208", "X", &fqdn_universe, 208 }, + { "unknown-209", "X", &fqdn_universe, 209 }, + { "unknown-210", "X", &fqdn_universe, 210 }, + { "unknown-211", "X", &fqdn_universe, 211 }, + { "unknown-212", "X", &fqdn_universe, 212 }, + { "unknown-213", "X", &fqdn_universe, 213 }, + { "unknown-214", "X", &fqdn_universe, 214 }, + { "unknown-215", "X", &fqdn_universe, 215 }, + { "unknown-216", "X", &fqdn_universe, 216 }, + { "unknown-217", "X", &fqdn_universe, 217 }, + { "unknown-218", "X", &fqdn_universe, 218 }, + { "unknown-219", "X", &fqdn_universe, 219 }, + { "unknown-220", "X", &fqdn_universe, 220 }, + { "unknown-221", "X", &fqdn_universe, 221 }, + { "unknown-222", "X", &fqdn_universe, 222 }, + { "unknown-223", "X", &fqdn_universe, 223 }, + { "unknown-224", "X", &fqdn_universe, 224 }, + { "unknown-225", "X", &fqdn_universe, 225 }, + { "unknown-226", "X", &fqdn_universe, 226 }, + { "unknown-227", "X", &fqdn_universe, 227 }, + { "unknown-228", "X", &fqdn_universe, 228 }, + { "unknown-229", "X", &fqdn_universe, 229 }, + { "unknown-230", "X", &fqdn_universe, 230 }, + { "unknown-231", "X", &fqdn_universe, 231 }, + { "unknown-232", "X", &fqdn_universe, 232 }, + { "unknown-233", "X", &fqdn_universe, 233 }, + { "unknown-234", "X", &fqdn_universe, 234 }, + { "unknown-235", "X", &fqdn_universe, 235 }, + { "unknown-236", "X", &fqdn_universe, 236 }, + { "unknown-237", "X", &fqdn_universe, 237 }, + { "unknown-238", "X", &fqdn_universe, 238 }, + { "unknown-239", "X", &fqdn_universe, 239 }, + { "unknown-240", "X", &fqdn_universe, 240 }, + { "unknown-241", "X", &fqdn_universe, 241 }, + { "unknown-242", "X", &fqdn_universe, 242 }, + { "unknown-243", "X", &fqdn_universe, 243 }, + { "unknown-244", "X", &fqdn_universe, 244 }, + { "unknown-245", "X", &fqdn_universe, 245 }, + { "unknown-246", "X", &fqdn_universe, 246 }, + { "unknown-247", "X", &fqdn_universe, 247 }, + { "unknown-248", "X", &fqdn_universe, 248 }, + { "unknown-249", "X", &fqdn_universe, 249 }, + { "unknown-250", "X", &fqdn_universe, 250 }, + { "unknown-251", "X", &fqdn_universe, 251 }, + { "unknown-252", "X", &fqdn_universe, 252 }, + { "unknown-253", "X", &fqdn_universe, 253 }, + { "unknown-254", "X", &fqdn_universe, 254 }, + { "unknown-end", "e", &fqdn_universe, 255 }, +}; + +const char *hardware_types [] = { + "unknown-0", + "ethernet", + "unknown-2", + "unknown-3", + "unknown-4", + "unknown-5", + "token-ring", + "unknown-7", + "fddi", + "unknown-9", + "unknown-10", + "unknown-11", + "unknown-12", + "unknown-13", + "unknown-14", + "unknown-15", + "unknown-16", + "unknown-17", + "unknown-18", + "unknown-19", + "unknown-20", + "unknown-21", + "unknown-22", + "unknown-23", + "unknown-24", + "unknown-25", + "unknown-26", + "unknown-27", + "unknown-28", + "unknown-29", + "unknown-30", + "unknown-31", + "unknown-32", + "unknown-33", + "unknown-34", + "unknown-35", + "unknown-36", + "unknown-37", + "unknown-38", + "unknown-39", + "unknown-40", + "unknown-41", + "unknown-42", + "unknown-43", + "unknown-44", + "unknown-45", + "unknown-46", + "unknown-47", + "unknown-48", + "unknown-49", + "unknown-50", + "unknown-51", + "unknown-52", + "unknown-53", + "unknown-54", + "unknown-55", + "unknown-56", + "unknown-57", + "unknown-58", + "unknown-59", + "unknown-60", + "unknown-61", + "unknown-62", + "unknown-63", + "unknown-64", + "unknown-65", + "unknown-66", + "unknown-67", + "unknown-68", + "unknown-69", + "unknown-70", + "unknown-71", + "unknown-72", + "unknown-73", + "unknown-74", + "unknown-75", + "unknown-76", + "unknown-77", + "unknown-78", + "unknown-79", + "unknown-80", + "unknown-81", + "unknown-82", + "unknown-83", + "unknown-84", + "unknown-85", + "unknown-86", + "unknown-87", + "unknown-88", + "unknown-89", + "unknown-90", + "unknown-91", + "unknown-92", + "unknown-93", + "unknown-94", + "unknown-95", + "unknown-96", + "unknown-97", + "unknown-98", + "unknown-99", + "unknown-100", + "unknown-101", + "unknown-102", + "unknown-103", + "unknown-104", + "unknown-105", + "unknown-106", + "unknown-107", + "unknown-108", + "unknown-109", + "unknown-110", + "unknown-111", + "unknown-112", + "unknown-113", + "unknown-114", + "unknown-115", + "unknown-116", + "unknown-117", + "unknown-118", + "unknown-119", + "unknown-120", + "unknown-121", + "unknown-122", + "unknown-123", + "unknown-124", + "unknown-125", + "unknown-126", + "unknown-127", + "unknown-128", + "unknown-129", + "unknown-130", + "unknown-131", + "unknown-132", + "unknown-133", + "unknown-134", + "unknown-135", + "unknown-136", + "unknown-137", + "unknown-138", + "unknown-139", + "unknown-140", + "unknown-141", + "unknown-142", + "unknown-143", + "unknown-144", + "unknown-145", + "unknown-146", + "unknown-147", + "unknown-148", + "unknown-149", + "unknown-150", + "unknown-151", + "unknown-152", + "unknown-153", + "unknown-154", + "unknown-155", + "unknown-156", + "unknown-157", + "unknown-158", + "unknown-159", + "unknown-160", + "unknown-161", + "unknown-162", + "unknown-163", + "unknown-164", + "unknown-165", + "unknown-166", + "unknown-167", + "unknown-168", + "unknown-169", + "unknown-170", + "unknown-171", + "unknown-172", + "unknown-173", + "unknown-174", + "unknown-175", + "unknown-176", + "unknown-177", + "unknown-178", + "unknown-179", + "unknown-180", + "unknown-181", + "unknown-182", + "unknown-183", + "unknown-184", + "unknown-185", + "unknown-186", + "unknown-187", + "unknown-188", + "unknown-189", + "unknown-190", + "unknown-191", + "unknown-192", + "unknown-193", + "unknown-194", + "unknown-195", + "unknown-196", + "unknown-197", + "unknown-198", + "unknown-199", + "unknown-200", + "unknown-201", + "unknown-202", + "unknown-203", + "unknown-204", + "unknown-205", + "unknown-206", + "unknown-207", + "unknown-208", + "unknown-209", + "unknown-210", + "unknown-211", + "unknown-212", + "unknown-213", + "unknown-214", + "unknown-215", + "unknown-216", + "unknown-217", + "unknown-218", + "unknown-219", + "unknown-220", + "unknown-221", + "unknown-222", + "unknown-223", + "unknown-224", + "unknown-225", + "unknown-226", + "unknown-227", + "unknown-228", + "unknown-229", + "unknown-230", + "unknown-231", + "unknown-232", + "unknown-233", + "unknown-234", + "unknown-235", + "unknown-236", + "unknown-237", + "unknown-238", + "unknown-239", + "unknown-240", + "unknown-241", + "unknown-242", + "unknown-243", + "unknown-244", + "unknown-245", + "unknown-246", + "unknown-247", + "unknown-248", + "unknown-249", + "unknown-250", + "unknown-251", + "unknown-252", + "unknown-253", + "unknown-254", + "unknown-255" }; + +universe_hash_t *universe_hash; +struct universe **universes; +int universe_count, universe_max; + +/* Universe containing names of configuration options, which, rather than + writing "option universe-name.option-name ...;", can be set by writing + "option-name ...;". */ + +struct universe *config_universe; + +void initialize_common_option_spaces() +{ + int i; + + universe_max = 10; + universes = ((struct universe **) + dmalloc (universe_max * sizeof (struct universe *), MDL)); + if (!universes) + log_fatal ("Can't allocate option space table."); + memset (universes, 0, universe_max * sizeof (struct universe *)); + + /* Set up the DHCP option universe... */ + dhcp_universe.name = "dhcp"; + dhcp_universe.lookup_func = lookup_hashed_option; + dhcp_universe.option_state_dereference = + hashed_option_state_dereference; + dhcp_universe.save_func = save_hashed_option; + dhcp_universe.delete_func = delete_hashed_option; + dhcp_universe.encapsulate = hashed_option_space_encapsulate; + dhcp_universe.foreach = hashed_option_space_foreach; + dhcp_universe.decode = parse_option_buffer; + dhcp_universe.length_size = 1; + dhcp_universe.tag_size = 1; + dhcp_universe.store_tag = putUChar; + dhcp_universe.store_length = putUChar; + dhcp_universe.index = universe_count++; + universes [dhcp_universe.index] = &dhcp_universe; + if (!option_new_hash (&dhcp_universe.hash, 1, MDL)) + log_fatal ("Can't allocate dhcp option hash table."); + for (i = 0; i < 256; i++) { + dhcp_universe.options [i] = &dhcp_options [i]; + option_hash_add (dhcp_universe.hash, + dhcp_options [i].name, 0, + &dhcp_options [i], MDL); + } + + /* Set up the Novell option universe (for option 63)... */ + nwip_universe.name = "nwip"; + nwip_universe.lookup_func = lookup_linked_option; + nwip_universe.option_state_dereference = + linked_option_state_dereference; + nwip_universe.save_func = save_linked_option; + nwip_universe.delete_func = delete_linked_option; + nwip_universe.encapsulate = nwip_option_space_encapsulate; + nwip_universe.foreach = linked_option_space_foreach; + nwip_universe.decode = parse_option_buffer; + nwip_universe.length_size = 1; + nwip_universe.tag_size = 1; + nwip_universe.store_tag = putUChar; + nwip_universe.store_length = putUChar; + nwip_universe.enc_opt = &dhcp_options [DHO_NWIP_SUBOPTIONS]; + nwip_universe.index = universe_count++; + universes [nwip_universe.index] = &nwip_universe; + option_new_hash (&nwip_universe.hash, 1, MDL); + if (!nwip_universe.hash) + log_fatal ("Can't allocate nwip option hash table."); + for (i = 0; i < 256; i++) { + nwip_universe.options [i] = &nwip_options [i]; + option_hash_add (nwip_universe.hash, + nwip_options [i].name, 0, + &nwip_options [i], MDL); + } + + /* Set up the FQDN option universe... */ + fqdn_universe.name = "fqdn"; + fqdn_universe.lookup_func = lookup_linked_option; + fqdn_universe.option_state_dereference = + linked_option_state_dereference; + fqdn_universe.save_func = save_linked_option; + fqdn_universe.delete_func = delete_linked_option; + fqdn_universe.encapsulate = fqdn_option_space_encapsulate; + fqdn_universe.foreach = linked_option_space_foreach; + fqdn_universe.decode = fqdn_universe_decode; + fqdn_universe.length_size = 1; + fqdn_universe.tag_size = 1; + fqdn_universe.store_tag = putUChar; + fqdn_universe.store_length = putUChar; + fqdn_universe.index = universe_count++; + fqdn_universe.enc_opt = &dhcp_options [DHO_FQDN]; + universes [fqdn_universe.index] = &fqdn_universe; + option_new_hash (&fqdn_universe.hash, 1, MDL); + if (!fqdn_universe.hash) + log_fatal ("Can't allocate fqdn option hash table."); + for (i = 0; i < 256; i++) { + fqdn_universe.options [i] = &fqdn_options [i]; + option_hash_add (fqdn_universe.hash, + fqdn_options [i].name, 0, + &fqdn_options [i], MDL); + } + + /* Set up the hash of universes. */ + universe_new_hash (&universe_hash, 1, MDL); + universe_hash_add (universe_hash, + dhcp_universe.name, 0, + &dhcp_universe, MDL); + universe_hash_add (universe_hash, + nwip_universe.name, 0, + &nwip_universe, MDL); + universe_hash_add (universe_hash, + fqdn_universe.name, 0, + &fqdn_universe, MDL); +} diff --git a/contrib/dhcp-3.0/common/tr.c b/contrib/dhcp-3.0/common/tr.c new file mode 100644 index 0000000000..c3f097b3b9 --- /dev/null +++ b/contrib/dhcp-3.0/common/tr.c @@ -0,0 +1,335 @@ +/* tr.c + + token ring interface support + Contributed in May of 1999 by Andrew Chittenden */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static char copyright[] = +"$Id: tr.c,v 1.7.2.2 2004/06/10 17:59:21 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +#if defined (HAVE_TR_SUPPORT) && \ + (defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)) +#include "includes/netinet/ip.h" +#include "includes/netinet/udp.h" +#include "includes/netinet/if_ether.h" +#include "netinet/if_tr.h" +#include + +/* + * token ring device handling subroutines. These are required as token-ring + * does not have a simple on-the-wire header but requires the use of + * source routing + */ + +static int insert_source_routing PROTO ((struct trh_hdr *trh, struct interface_info* interface)); +static void save_source_routing PROTO ((struct trh_hdr *trh, struct interface_info* interface)); +static void expire_routes PROTO ((void)); + +/* + * As we keep a list of interesting routing information only, a singly + * linked list is all we need + */ +struct routing_entry { + struct routing_entry *next; + unsigned char addr[TR_ALEN]; + unsigned char iface[5]; + __u16 rcf; /* route control field */ + __u16 rseg[8]; /* routing registers */ + unsigned long access_time; /* time we last used this entry */ +}; + +static struct routing_entry *routing_info = NULL; + +static int routing_timeout = 10; +static struct timeval routing_timer; + +void assemble_tr_header (interface, buf, bufix, to) + struct interface_info *interface; + unsigned char *buf; + unsigned *bufix; + struct hardware *to; +{ + struct trh_hdr *trh; + int hdr_len; + struct trllc *llc; + + + /* set up the token header */ + trh = (struct trh_hdr *) &buf[*bufix]; + if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr)) + memcpy (trh->saddr, &interface -> hw_address.hbuf [1], + sizeof (trh->saddr)); + else + memset (trh->saddr, 0x00, sizeof (trh->saddr)); + + if (to && to -> hlen == 7) /* XXX */ + memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr); + else + memset (trh->daddr, 0xff, sizeof (trh->daddr)); + + hdr_len = insert_source_routing (trh, interface); + + trh->ac = AC; + trh->fc = LLC_FRAME; + + /* set up the llc header for snap encoding after the tr header */ + llc = (struct trllc *)(buf + *bufix + hdr_len); + llc->dsap = EXTENDED_SAP; + llc->ssap = EXTENDED_SAP; + llc->llc = UI_CMD; + llc->protid[0] = 0; + llc->protid[1] = 0; + llc->protid[2] = 0; + llc->ethertype = htons(ETHERTYPE_IP); + + hdr_len += sizeof(struct trllc); + + *bufix += hdr_len; +} + + +static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +/* + * decoding the token header is a bit complex as you can see here. It is + * further complicated by the linux kernel stripping off some valuable + * information (see comment below) even though we've asked for the raw + * packets. + */ +ssize_t decode_tr_header (interface, buf, bufix, from) + struct interface_info *interface; + unsigned char *buf; + unsigned bufix; + struct hardware *from; +{ + struct trh_hdr *trh = (struct trh_hdr *) buf + bufix; + struct trllc *llc; + struct ip *ip; + struct udphdr *udp; + unsigned int route_len = 0; + ssize_t hdr_len; + struct timeval now; + + /* see whether any source routing information has expired */ + gettimeofday(&now, NULL); + + if (routing_timer.tv_sec == 0) + routing_timer.tv_sec = now.tv_sec + routing_timeout; + else if ((now.tv_sec - routing_timer.tv_sec) > 0) + expire_routes(); + + /* the kernel might have stripped off the source + * routing bit. We try a heuristic to determine whether + * this is the case and put it back on if so + */ + route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; + llc = (struct trllc *)(buf + bufix + sizeof(struct trh_hdr)-TR_MAXRIFLEN+route_len); + if (llc->dsap == EXTENDED_SAP + && llc->ssap == EXTENDED_SAP + && llc->llc == UI_CMD + && llc->protid[0] == 0 + && llc->protid[1] == 0 + && llc->protid[2] == 0) { + /* say there is source routing information present */ + trh->saddr[0] |= TR_RII; + } + + if (trh->saddr[0] & TR_RII) + route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; + else + route_len = 0; + + hdr_len = sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len; + + /* now filter out unwanted packets: this is based on the packet + * filter code in bpf.c */ + llc = (struct trllc *)(buf + bufix + hdr_len); + ip = (struct ip *) (llc + 1); + udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip)); + + /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent + * to our port */ + if (llc->dsap != EXTENDED_SAP + || ntohs(llc->ethertype) != ETHERTYPE_IP + || ip->ip_p != IPPROTO_UDP + || (ntohs (ip->ip_off) & IP_OFFMASK) != 0 + || udp->uh_dport != local_port) + return -1; + + /* only save source routing information for packets from valued hosts */ + save_source_routing(trh, interface); + + return hdr_len + sizeof (struct trllc); +} + +/* insert_source_routing inserts source route information into the token ring + * header + */ +static int insert_source_routing (trh, interface) + struct trh_hdr *trh; + struct interface_info* interface; +{ + struct routing_entry *rover; + struct timeval now; + unsigned int route_len = 0; + + gettimeofday(&now, NULL); + + /* single route broadcasts as per rfc 1042 */ + if (memcmp(trh->daddr, tr_broadcast,TR_ALEN) == 0) { + trh->saddr[0] |= TR_RII; + trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK; + trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); + trh->rcf = htons(trh->rcf); + } else { + /* look for a routing entry */ + for (rover = routing_info; rover != NULL; rover = rover->next) { + if (memcmp(rover->addr, trh->daddr, TR_ALEN) == 0) + break; + } + + if (rover != NULL) { + /* success: route that frame */ + if ((rover->rcf & TR_RCF_LEN_MASK) >> 8) { + __u16 rcf = rover->rcf; + memcpy(trh->rseg,rover->rseg,sizeof(trh->rseg)); + rcf ^= TR_RCF_DIR_BIT; + rcf &= ~TR_RCF_BROADCAST_MASK; + trh->rcf = htons(rcf); + trh->saddr[0] |= TR_RII; + } + rover->access_time = now.tv_sec; + } else { + /* we don't have any routing information so send a + * limited broadcast */ + trh->saddr[0] |= TR_RII; + trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK; + trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); + trh->rcf = htons(trh->rcf); + } + } + + /* return how much of the header we've actually used */ + if (trh->saddr[0] & TR_RII) + route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; + else + route_len = 0; + + return sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len; +} + +/* + * save any source routing information + */ +static void save_source_routing(trh, interface) + struct trh_hdr *trh; + struct interface_info *interface; +{ + struct routing_entry *rover; + struct timeval now; + unsigned char saddr[TR_ALEN]; + __u16 rcf = 0; + + gettimeofday(&now, NULL); + + memcpy(saddr, trh->saddr, sizeof(saddr)); + saddr[0] &= 0x7f; /* strip off source routing present flag */ + + /* scan our table to see if we've got it */ + for (rover = routing_info; rover != NULL; rover = rover->next) { + if (memcmp(&rover->addr[0], &saddr[0], TR_ALEN) == 0) + break; + } + + /* found an entry so update it with fresh information */ + if (rover != NULL) { + if ((trh->saddr[0] & TR_RII) && + ((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) { + rcf = ntohs(trh->rcf); + rcf &= ~TR_RCF_BROADCAST_MASK; + memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg)); + } + rover->rcf = rcf; + rover->access_time = now.tv_sec; + return; /* that's all folks */ + } + + /* no entry found, so create one */ + rover = dmalloc (sizeof (struct routing_entry), MDL); + if (rover == NULL) { + fprintf(stderr, + "%s: unable to save source routing information\n", + __FILE__); + return; + } + + memcpy(rover->addr, saddr, sizeof(rover->addr)); + memcpy(rover->iface, interface->name, 5); + rover->access_time = now.tv_sec; + if (trh->saddr[0] & TR_RII) { + if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) { + rcf = ntohs(trh->rcf); + rcf &= ~TR_RCF_BROADCAST_MASK; + memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg)); + } + rover->rcf = rcf; + } + + /* insert into list */ + rover->next = routing_info; + routing_info = rover; + + return; +} + +/* + * get rid of old routes + */ +static void expire_routes() +{ + struct routing_entry *rover; + struct routing_entry **prover = &routing_info; + struct timeval now; + + gettimeofday(&now, NULL); + + while((rover = *prover) != NULL) { + if ((now.tv_sec - rover->access_time) > routing_timeout) { + *prover = rover->next; + dfree (rover, MDL); + } else + prover = &rover->next; + } + + /* Reset the timer */ + routing_timer.tv_sec = now.tv_sec + routing_timeout; + routing_timer.tv_usec = now.tv_usec; +} + +#endif diff --git a/contrib/dhcp-3.0/common/tree.c b/contrib/dhcp-3.0/common/tree.c new file mode 100644 index 0000000000..f44bffe5aa --- /dev/null +++ b/contrib/dhcp-3.0/common/tree.c @@ -0,0 +1,4100 @@ +/* tree.c + + Routines for manipulating parse trees... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: tree.c,v 1.101.2.11 2004/11/24 17:39:16 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#include + +struct binding_scope *global_scope; + +static int do_host_lookup PROTO ((struct data_string *, + struct dns_host_entry *)); + +#ifdef NSUPDATE +struct __res_state resolver_state; +int resolver_inited = 0; +#endif + +pair cons (car, cdr) + caddr_t car; + pair cdr; +{ + pair foo = (pair)dmalloc (sizeof *foo, MDL); + if (!foo) + log_fatal ("no memory for cons."); + foo -> car = car; + foo -> cdr = cdr; + return foo; +} + +int make_const_option_cache (oc, buffer, data, len, option, file, line) + struct option_cache **oc; + struct buffer **buffer; + u_int8_t *data; + unsigned len; + struct option *option; + const char *file; + int line; +{ + struct buffer *bp; + + if (buffer) { + bp = *buffer; + *buffer = 0; + } else { + bp = (struct buffer *)0; + if (!buffer_allocate (&bp, len, file, line)) { + log_error ("%s(%d): can't allocate buffer.", + file, line); + return 0; + } + } + + if (!option_cache_allocate (oc, file, line)) { + log_error ("%s(%d): can't allocate option cache.", file, line); + buffer_dereference (&bp, file, line); + return 0; + } + + (*oc) -> data.len = len; + (*oc) -> data.buffer = bp; + (*oc) -> data.data = &bp -> data [0]; + (*oc) -> data.terminated = 0; + if (data) + memcpy (&bp -> data [0], data, len); + (*oc) -> option = option; + return 1; +} + +int make_host_lookup (expr, name) + struct expression **expr; + const char *name; +{ + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for host lookup tree node."); + return 0; + } + (*expr) -> op = expr_host_lookup; + if (!enter_dns_host (&((*expr) -> data.host_lookup), name)) { + expression_dereference (expr, MDL); + return 0; + } + return 1; +} + +int enter_dns_host (dh, name) + struct dns_host_entry **dh; + const char *name; +{ + /* XXX This should really keep a hash table of hostnames + XXX and just add a new reference to a hostname that + XXX already exists, if possible, rather than creating + XXX a new structure. */ + if (!dns_host_entry_allocate (dh, name, MDL)) { + log_error ("Can't allocate space for new host."); + return 0; + } + return 1; +} + +int make_const_data (struct expression **expr, const unsigned char *data, + unsigned len, int terminated, int allocate, + const char *file, int line) +{ + struct expression *nt; + + if (!expression_allocate (expr, file, line)) { + log_error ("No memory for make_const_data tree node."); + return 0; + } + nt = *expr; + + if (len) { + if (allocate) { + if (!buffer_allocate (&nt -> data.const_data.buffer, + len + terminated, file, line)) { + log_error ("Can't allocate const_data buffer"); + expression_dereference (expr, file, line); + return 0; + } + nt -> data.const_data.data = + &nt -> data.const_data.buffer -> data [0]; + memcpy (nt -> data.const_data.buffer -> data, + data, len + terminated); + } else + nt -> data.const_data.data = data; + nt -> data.const_data.terminated = terminated; + } else + nt -> data.const_data.data = 0; + + nt -> op = expr_const_data; + nt -> data.const_data.len = len; + return 1; +} + +int make_const_int (expr, val) + struct expression **expr; + unsigned long val; +{ + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for make_const_int tree node."); + return 0; + } + + (*expr) -> op = expr_const_int; + (*expr) -> data.const_int = val; + return 1; +} + +int make_concat (expr, left, right) + struct expression **expr; + struct expression *left, *right; +{ + /* If we're concatenating a null tree to a non-null tree, just + return the non-null tree; if both trees are null, return + a null tree. */ + if (!left) { + if (!right) + return 0; + expression_reference (expr, right, MDL); + return 1; + } + if (!right) { + expression_reference (expr, left, MDL); + return 1; + } + + /* Otherwise, allocate a new node to concatenate the two. */ + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for concatenation expression node."); + return 0; + } + + (*expr) -> op = expr_concat; + expression_reference (&(*expr) -> data.concat [0], left, MDL); + expression_reference (&(*expr) -> data.concat [1], right, MDL); + return 1; +} + +int make_encapsulation (expr, name) + struct expression **expr; + struct data_string *name; +{ + /* Allocate a new node to store the encapsulation. */ + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for encapsulation expression node."); + return 0; + } + + (*expr) -> op = expr_encapsulate; + data_string_copy (&(*expr) -> data.encapsulate, name, MDL); + return 1; +} + +int make_substring (new, expr, offset, length) + struct expression **new; + struct expression *expr; + struct expression *offset; + struct expression *length; +{ + /* Allocate an expression node to compute the substring. */ + if (!expression_allocate (new, MDL)) { + log_error ("no memory for substring expression."); + return 0; + } + (*new) -> op = expr_substring; + expression_reference (&(*new) -> data.substring.expr, expr, MDL); + expression_reference (&(*new) -> data.substring.offset, offset, MDL); + expression_reference (&(*new) -> data.substring.len, length, MDL); + return 1; +} + +int make_limit (new, expr, limit) + struct expression **new; + struct expression *expr; + int limit; +{ + struct expression *rv; + + /* Allocate a node to enforce a limit on evaluation. */ + if (!expression_allocate (new, MDL)) + log_error ("no memory for limit expression"); + (*new) -> op = expr_substring; + expression_reference (&(*new) -> data.substring.expr, expr, MDL); + + /* Offset is a constant 0. */ + if (!expression_allocate (&(*new) -> data.substring.offset, MDL)) { + log_error ("no memory for limit offset expression"); + expression_dereference (new, MDL); + return 0; + } + (*new) -> data.substring.offset -> op = expr_const_int; + (*new) -> data.substring.offset -> data.const_int = 0; + + /* Length is a constant: the specified limit. */ + if (!expression_allocate (&(*new) -> data.substring.len, MDL)) { + log_error ("no memory for limit length expression"); + expression_dereference (new, MDL); + return 0; + } + (*new) -> data.substring.len -> op = expr_const_int; + (*new) -> data.substring.len -> data.const_int = limit; + + return 1; +} + +int option_cache (struct option_cache **oc, struct data_string *dp, + struct expression *expr, struct option *option, + const char *file, int line) +{ + if (!option_cache_allocate (oc, file, line)) + return 0; + if (dp) + data_string_copy (&(*oc) -> data, dp, file, line); + if (expr) + expression_reference (&(*oc) -> expression, expr, file, line); + (*oc) -> option = option; + return 1; +} + +int make_let (result, name) + struct executable_statement **result; + const char *name; +{ + if (!(executable_statement_allocate (result, MDL))) + return 0; + + (*result) -> op = let_statement; + (*result) -> data.let.name = dmalloc (strlen (name) + 1, MDL); + if (!(*result) -> data.let.name) { + executable_statement_dereference (result, MDL); + return 0; + } + strcpy ((*result) -> data.let.name, name); + return 1; +} + +static int do_host_lookup (result, dns) + struct data_string *result; + struct dns_host_entry *dns; +{ + struct hostent *h; + unsigned i, count; + unsigned new_len; + +#ifdef DEBUG_EVAL + log_debug ("time: now = %d dns = %d diff = %d", + cur_time, dns -> timeout, cur_time - dns -> timeout); +#endif + + /* If the record hasn't timed out, just copy the data and return. */ + if (cur_time <= dns -> timeout) { +#ifdef DEBUG_EVAL + log_debug ("easy copy: %d %s", + dns -> data.len, + (dns -> data.len > 4 + ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) + : 0)); +#endif + data_string_copy (result, &dns -> data, MDL); + return 1; + } +#ifdef DEBUG_EVAL + log_debug ("Looking up %s", dns -> hostname); +#endif + + /* Otherwise, look it up... */ + h = gethostbyname (dns -> hostname); + if (!h) { +#ifndef NO_H_ERRNO + switch (h_errno) { + case HOST_NOT_FOUND: +#endif + log_error ("%s: host unknown.", dns -> hostname); +#ifndef NO_H_ERRNO + break; + case TRY_AGAIN: + log_error ("%s: temporary name server failure", + dns -> hostname); + break; + case NO_RECOVERY: + log_error ("%s: name server failed", dns -> hostname); + break; + case NO_DATA: + log_error ("%s: no A record associated with address", + dns -> hostname); + } +#endif /* !NO_H_ERRNO */ + + /* Okay to try again after a minute. */ + dns -> timeout = cur_time + 60; + data_string_forget (&dns -> data, MDL); + return 0; + } + +#ifdef DEBUG_EVAL + log_debug ("Lookup succeeded; first address is %s", + inet_ntoa (h -> h_addr_list [0])); +#endif + + /* Count the number of addresses we got... */ + for (count = 0; h -> h_addr_list [count]; count++) + ; + + /* Dereference the old data, if any. */ + data_string_forget (&dns -> data, MDL); + + /* Do we need to allocate more memory? */ + new_len = count * h -> h_length; + if (!buffer_allocate (&dns -> data.buffer, new_len, MDL)) + { + log_error ("No memory for %s.", dns -> hostname); + return 0; + } + + dns -> data.data = &dns -> data.buffer -> data [0]; + dns -> data.len = new_len; + dns -> data.terminated = 0; + + /* Addresses are conveniently stored one to the buffer, so we + have to copy them out one at a time... :'( */ + for (i = 0; i < count; i++) { + memcpy (&dns -> data.buffer -> data [h -> h_length * i], + h -> h_addr_list [i], (unsigned)(h -> h_length)); + } +#ifdef DEBUG_EVAL + log_debug ("dns -> data: %x h -> h_addr_list [0]: %x", + *(int *)(dns -> buffer), h -> h_addr_list [0]); +#endif + + /* XXX Set the timeout for an hour from now. + XXX This should really use the time on the DNS reply. */ + dns -> timeout = cur_time + 3600; + +#ifdef DEBUG_EVAL + log_debug ("hard copy: %d %s", dns -> data.len, + (dns -> data.len > 4 + ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0)); +#endif + data_string_copy (result, &dns -> data, MDL); + return 1; +} + +int evaluate_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr, file, line) + struct binding_value **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; + const char *file; + int line; +{ + struct binding_value *bv; + int status; + struct binding *binding; + + bv = (struct binding_value *)0; + + if (expr -> op == expr_variable_reference) { + if (!scope || !*scope) + return 0; + + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (result) + binding_value_reference (result, + binding -> value, + file, line); + return 1; + } else + return 0; + } else if (expr -> op == expr_funcall) { + struct string_list *s; + struct expression *arg; + struct binding_scope *ns; + struct binding *nb; + + if (!scope || !*scope) { + log_error ("%s: no such function.", + expr -> data.funcall.name); + return 0; + } + + binding = find_binding (*scope, expr -> data.funcall.name); + + if (!binding || !binding -> value) { + log_error ("%s: no such function.", + expr -> data.funcall.name); + return 0; + } + if (binding -> value -> type != binding_function) { + log_error ("%s: not a function.", + expr -> data.funcall.name); + return 0; + } + + /* Create a new binding scope in which to define + the arguments to the function. */ + ns = (struct binding_scope *)0; + if (!binding_scope_allocate (&ns, MDL)) { + log_error ("%s: can't allocate argument scope.", + expr -> data.funcall.name); + return 0; + } + + arg = expr -> data.funcall.arglist; + s = binding -> value -> value.fundef -> args; + while (arg && s) { + nb = dmalloc (sizeof *nb, MDL); + if (!nb) { + blb: + binding_scope_dereference (&ns, MDL); + return 0; + } else { + memset (nb, 0, sizeof *nb); + nb -> name = dmalloc (strlen (s -> string) + 1, + MDL); + if (nb -> name) + strcpy (nb -> name, s -> string); + else { + dfree (nb, MDL); + nb = (struct binding *)0; + goto blb; + } + } + evaluate_expression (&nb -> value, packet, lease, + client_state, + in_options, cfg_options, scope, + arg -> data.arg.val, file, line); + nb -> next = ns -> bindings; + ns -> bindings = nb; + arg = arg -> data.arg.next; + s = s -> next; + } + if (arg) { + log_error ("%s: too many arguments.", + expr -> data.funcall.name); + binding_scope_dereference (&ns, MDL); + return 0; + } + if (s) { + log_error ("%s: too few arguments.", + expr -> data.funcall.name); + binding_scope_dereference (&ns, MDL); + return 0; + } + + if (scope && *scope) + binding_scope_reference (&ns -> outer, *scope, MDL); + + status = (execute_statements + (&bv, packet, + lease, client_state, in_options, cfg_options, &ns, + binding -> value -> value.fundef -> statements)); + binding_scope_dereference (&ns, MDL); + + if (!bv) + return 1; + } else if (is_boolean_expression (expr)) { + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_boolean; + status = (evaluate_boolean_expression + (&bv -> value.boolean, packet, lease, client_state, + in_options, cfg_options, scope, expr)); + } else if (is_numeric_expression (expr)) { + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_numeric; + status = (evaluate_numeric_expression + (&bv -> value.intval, packet, lease, client_state, + in_options, cfg_options, scope, expr)); + } else if (is_data_expression (expr)) { + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_data; + status = (evaluate_data_expression + (&bv -> value.data, packet, lease, client_state, + in_options, cfg_options, scope, expr, MDL)); + } else if (is_dns_expression (expr)) { +#if defined (NSUPDATE) + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_dns; + status = (evaluate_dns_expression + (&bv -> value.dns, packet, lease, client_state, + in_options, cfg_options, scope, expr)); +#endif + } else { + log_error ("%s: invalid expression type: %d", + "evaluate_expression", expr -> op); + return 0; + } + if (result && status) + binding_value_reference (result, bv, file, line); + binding_value_dereference (&bv, MDL); + + return status; +} + +int binding_value_dereference (struct binding_value **v, + const char *file, int line) +{ + struct binding_value *bv = *v; + + *v = (struct binding_value *)0; + + /* Decrement the reference count. If it's nonzero, we're + done. */ + --(bv -> refcnt); + rc_register (file, line, v, bv, bv -> refcnt, 1, RC_MISC); + if (bv -> refcnt > 0) + return 1; + if (bv -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (bv); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + switch (bv -> type) { + case binding_boolean: + case binding_numeric: + break; + case binding_data: + if (bv -> value.data.buffer) + data_string_forget (&bv -> value.data, file, line); + break; + case binding_dns: +#if defined (NSUPDATE) + if (bv -> value.dns) { + if (bv -> value.dns -> r_data) { + dfree (bv -> value.dns -> r_data_ephem, MDL); + bv -> value.dns -> r_data = (unsigned char *)0; + bv -> value.dns -> r_data_ephem = + (unsigned char *)0; + } + minires_freeupdrec (bv -> value.dns); + } + break; +#endif + default: + log_error ("%s(%d): invalid binding type: %d", + file, line, bv -> type); + return 0; + } + dfree (bv, file, line); + return 1; +} + +#if defined (NSUPDATE) +int evaluate_dns_expression (result, packet, lease, client_state, in_options, + cfg_options, scope, expr) + ns_updrec **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + ns_updrec *foo; + unsigned long ttl = 0; + char *tname; + struct data_string name, data; + int r0, r1, r2, r3; + + if (!result || *result) { + log_error ("evaluate_dns_expression called with non-null %s", + "result pointer"); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + switch (expr -> op) { +#if defined (NSUPDATE) + case expr_ns_add: + r0 = evaluate_numeric_expression (&ttl, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.ns_add.ttl); + goto nsfinish; + + case expr_ns_exists: + ttl = 1; + + case expr_ns_delete: + case expr_ns_not_exists: + r0 = 1; + nsfinish: + memset (&name, 0, sizeof name); + r1 = evaluate_data_expression (&name, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.ns_add.rrname, + MDL); + if (r1) { + /* The result of the evaluation may or may not + be NUL-terminated, but we need it + terminated for sure, so we have to allocate + a buffer and terminate it. */ + tname = dmalloc (name.len + 1, MDL); + if (!tname) { + r2 = 0; + r1 = 0; + data_string_forget (&name, MDL); + } else { + memcpy (tname, name.data, name.len); + tname [name.len] = 0; + memset (&data, 0, sizeof data); + r2 = evaluate_data_expression + (&data, packet, lease, client_state, + in_options, cfg_options, scope, + expr -> data.ns_add.rrdata, MDL); + } + } else { + r2 = 0; + tname = NULL; + } + if (r0 && r1 && (r2 || expr -> op != expr_ns_add)) { + *result = minires_mkupdrec (((expr -> op == expr_ns_add || + expr -> op == expr_ns_delete) + ? S_UPDATE : S_PREREQ), + tname, + expr -> data.ns_add.rrclass, + expr -> data.ns_add.rrtype, + ttl); + if (!*result) { + ngood: + if (r2) { + data_string_forget (&data, MDL); + r2 = 0; + } + } else { + if (data.len) { + /* As a special case, if we get exactly + four bytes of data, it's an IP address + represented as a 32-bit quantity, which + is actually what we *should* be getting + here. Because res_mkupdrec is currently + broken and expects a dotted quad, convert + it. This should be fixed when the new + resolver is merged. */ + if (data.len == 4) { + (*result) -> r_data_ephem = + dmalloc (16, MDL); + if (!(*result) -> r_data_ephem) + goto dpngood; + (*result) -> r_data = + (*result) -> r_data_ephem; + /*%Audit% 16 bytes max. %2004.06.17,Safe%*/ + sprintf ((char *)(*result) -> r_data_ephem, + "%u.%u.%u.%u", + data.data [0] & 0xff, + data.data [1] & 0xff, + data.data [2] & 0xff, + data.data [3] & 0xff); + (*result) -> r_size = + strlen ((const char *) + (*result) -> r_data); + } else { + (*result) -> r_size = data.len; + (*result) -> r_data_ephem = + dmalloc (data.len, MDL); + if (!(*result) -> r_data_ephem) { + dpngood: /* double plus ungood. */ + minires_freeupdrec (*result); + *result = 0; + goto ngood; + } + (*result) -> r_data = + (*result) -> r_data_ephem; + memcpy ((*result) -> r_data_ephem, + data.data, data.len); + } + } else { + (*result) -> r_data = 0; + (*result) -> r_size = 0; + } + switch (expr -> op) { + case expr_ns_add: + (*result) -> r_opcode = ADD; + break; + case expr_ns_delete: + (*result) -> r_opcode = DELETE; + break; + case expr_ns_exists: + (*result) -> r_opcode = YXRRSET; + break; + case expr_ns_not_exists: + (*result) -> r_opcode = NXRRSET; + break; + + /* Can't happen, but satisfy gcc. */ + default: + break; + } + } + } + if (r1) { + data_string_forget (&name, MDL); + dfree (tname, MDL); + } + if (r2) + data_string_forget (&data, MDL); + /* One flaw in the thinking here: an IP address and an + ASCII string both look like data expressions, but + for A records, we want an ASCII string, not a + binary IP address. Do I need to turn binary IP + addresses into a seperate type? */ + return (r0 && r1 && + (r2 || expr -> op != expr_ns_add) && *result); + +#else + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + return 0; +#endif + case expr_funcall: + log_error ("%s: dns values for functions not supported.", + expr -> data.funcall.name); + break; + + case expr_variable_reference: + log_error ("%s: dns values for variables not supported.", + expr -> data.variable); + break; + + case expr_check: + case expr_equal: + case expr_not_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_match: + case expr_static: + case expr_known: + case expr_exists: + case expr_variable_exists: + log_error ("Boolean opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_none: + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_null: + log_error ("Data opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + case expr_lease_time: + case expr_dns_transaction: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + log_error ("Numeric opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("Function opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_arg: + break; + } + + log_error ("Bogus opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; +} +#endif /* defined (NSUPDATE) */ + +int evaluate_boolean_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr) + int *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + struct data_string left, right; + struct data_string rrtype, rrname, rrdata; + unsigned long ttl; + int srrtype, srrname, srrdata, sttl; + int bleft, bright; + int sleft, sright; + struct binding *binding; + struct binding_value *bv, *obv; + + switch (expr -> op) { + case expr_check: + *result = check_collection (packet, lease, + expr -> data.check); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: check (%s) returns %s", + expr -> data.check -> name, + *result ? "true" : "false"); +#endif + return 1; + + case expr_equal: + case expr_not_equal: + bv = obv = (struct binding_value *)0; + sleft = evaluate_expression (&bv, packet, lease, client_state, + in_options, cfg_options, scope, + expr -> data.equal [0], MDL); + sright = evaluate_expression (&obv, packet, lease, + client_state, in_options, + cfg_options, scope, + expr -> data.equal [1], MDL); + if (sleft && sright) { + if (bv -> type != obv -> type) + *result = expr -> op == expr_not_equal; + else { + switch (obv -> type) { + case binding_boolean: + if (bv -> value.boolean == obv -> value.boolean) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + + case binding_data: + if ((bv -> value.data.len == + obv -> value.data.len) && + !memcmp (bv -> value.data.data, + obv -> value.data.data, + obv -> value.data.len)) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + + case binding_numeric: + if (bv -> value.intval == obv -> value.intval) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + + case binding_dns: +#if defined (NSUPDATE) + /* XXX This should be a comparison for equal + XXX values, not for identity. */ + if (bv -> value.dns == obv -> value.dns) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; +#else + *result = expr -> op == expr_not_equal; +#endif + break; + + case binding_function: + if (bv -> value.fundef == obv -> value.fundef) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + default: + *result = expr -> op == expr_not_equal; + break; + } + } + } else if (!sleft && !sright) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: %sequal = %s", + expr -> op == expr_not_equal ? "not" : "", + (*result ? "true" : "false")); +#endif + if (sleft) + binding_value_dereference (&bv, MDL); + if (sright) + binding_value_dereference (&obv, MDL); + return 1; + + case expr_and: + sleft = evaluate_boolean_expression (&bleft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + if (sleft && bleft) + sright = evaluate_boolean_expression + (&bright, packet, lease, client_state, + in_options, cfg_options, + scope, expr -> data.and [1]); + else + sright = bright = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: and (%s, %s) = %s", + sleft ? (bleft ? "true" : "false") : "NULL", + sright ? (bright ? "true" : "false") : "NULL", + ((sleft && sright) + ? (bleft && bright ? "true" : "false") : "NULL")); +#endif + if (sleft && sright) { + *result = bleft && bright; + return 1; + } + return 0; + + case expr_or: + bleft = bright = 0; + sleft = evaluate_boolean_expression (&bleft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.or [0]); + if (!sleft || !bleft) + sright = evaluate_boolean_expression + (&bright, packet, lease, client_state, + in_options, cfg_options, + scope, expr -> data.or [1]); + else + sright = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: or (%s, %s) = %s", + sleft ? (bleft ? "true" : "false") : "NULL", + sright ? (bright ? "true" : "false") : "NULL", + ((sleft || sright) + ? (bleft || bright ? "true" : "false") : "NULL")); +#endif + if (sleft || sright) { + *result = bleft || bright; + return 1; + } + return 0; + + case expr_not: + sleft = evaluate_boolean_expression (&bleft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.not); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: not (%s) = %s", + sleft ? (bleft ? "true" : "false") : "NULL", + ((sleft && sright) + ? (!bleft ? "true" : "false") : "NULL")); + +#endif + if (sleft) { + *result = !bleft; + return 1; + } + return 0; + + case expr_exists: + memset (&left, 0, sizeof left); + if (!in_options || + !get_option (&left, expr -> data.exists -> universe, + packet, lease, client_state, + in_options, cfg_options, in_options, + scope, expr -> data.exists -> code, MDL)) + *result = 0; + else { + *result = 1; + data_string_forget (&left, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: exists %s.%s = %s", + expr -> data.option -> universe -> name, + expr -> data.option -> name, + *result ? "true" : "false"); +#endif + return 1; + + case expr_known: + if (!packet) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: known = NULL"); +#endif + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: known = %s", + packet -> known ? "true" : "false"); +#endif + *result = packet -> known; + return 1; + + case expr_static: + if (!lease || !(lease -> flags & STATIC_LEASE)) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: static = false (%s %s %s %d)", + lease ? "y" : "n", + (lease && (lease -> flags & STATIC_LEASE) + ? "y" : "n"), + piaddr (lease -> ip_addr), + lease ? lease -> flags : 0); +#endif + *result = 0; + return 1; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: static = true"); +#endif + *result = 1; + return 1; + + case expr_variable_exists: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding) { + if (binding -> value) + *result = 1; + else + *result = 0; + } else + *result = 0; + } else + *result = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("boolean: %s? = %s", expr -> data.variable, + *result ? "true" : "false"); +#endif + return 1; + + case expr_variable_reference: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (binding -> value -> type == + binding_boolean) { + *result = binding -> value -> value.boolean; + sleft = 1; + } else { + log_error ("binding type %d in %s.", + binding -> value -> type, + "evaluate_boolean_expression"); + sleft = 0; + } + } else + sleft = 0; + } else + sleft = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("boolean: %s = %s", expr -> data.variable, + sleft ? (*result ? "true" : "false") : "NULL"); +#endif + return sleft; + + case expr_funcall: + bv = (struct binding_value *)0; + sleft = evaluate_expression (&bv, packet, lease, client_state, + in_options, cfg_options, + scope, expr, MDL); + if (sleft) { + if (bv -> type != binding_boolean) + log_error ("%s() returned type %d in %s.", + expr -> data.funcall.name, + bv -> type, + "evaluate_boolean_expression"); + else + *result = bv -> value.boolean; + binding_value_dereference (&bv, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("boolean: %s() = %s", expr -> data.funcall.name, + sleft ? (*result ? "true" : "false") : "NULL"); +#endif + break; + + case expr_none: + case expr_match: + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_binary_to_ascii: + case expr_reverse: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_null: + case expr_filename: + case expr_sname: + log_error ("Data opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + case expr_lease_time: + case expr_dns_transaction: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + log_error ("Numeric opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + log_error ("dns opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("function definition in evaluate_boolean_expr"); + return 0; + + case expr_arg: + break; + } + + log_error ("Bogus opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; +} + +int evaluate_data_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr, file, line) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; + const char *file; + int line; +{ + struct data_string data, other; + unsigned long offset, len, i; + int s0, s1, s2, s3; + int status; + struct binding *binding; + char *s; + struct binding_value *bv; + + switch (expr -> op) { + /* Extract N bytes starting at byte M of a data string. */ + case expr_substring: + memset (&data, 0, sizeof data); + s0 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.substring.expr, + MDL); + + /* Evaluate the offset and length. */ + s1 = evaluate_numeric_expression + (&offset, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.substring.offset); + s2 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.substring.len); + + if (s0 && s1 && s2) { + /* If the offset is after end of the string, + return an empty string. Otherwise, do the + adjustments and return what's left. */ + if (data.len > offset) { + data_string_copy (result, &data, file, line); + result -> len -= offset; + if (result -> len > len) { + result -> len = len; + result -> terminated = 0; + } + result -> data += offset; + } + s3 = 1; + } else + s3 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: substring (%s, %s, %s) = %s", + s0 ? print_hex_1 (data.len, data.data, 30) : "NULL", + s1 ? print_dec_1 (offset) : "NULL", + s2 ? print_dec_2 (len) : "NULL", + (s3 ? print_hex_2 (result -> len, result -> data, 30) + : "NULL")); +#endif + if (s0) + data_string_forget (&data, MDL); + if (s3) + return 1; + return 0; + + + /* Extract the last N bytes of a data string. */ + case expr_suffix: + memset (&data, 0, sizeof data); + s0 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.suffix.expr, MDL); + /* Evaluate the length. */ + s1 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.suffix.len); + if (s0 && s1) { + data_string_copy (result, &data, file, line); + + /* If we are returning the last N bytes of a + string whose length is <= N, just return + the string - otherwise, compute a new + starting address and decrease the + length. */ + if (data.len > len) { + result -> data += data.len - len; + result -> len = len; + } + data_string_forget (&data, MDL); + } + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: suffix (%s, %s) = %s", + s0 ? print_hex_1 (data.len, data.data, 30) : "NULL", + s1 ? print_dec_1 (len) : "NULL", + ((s0 && s1) + ? print_hex_2 (result -> len, result -> data, 30) + : "NULL")); +#endif + return s0 && s1; + + /* Extract an option. */ + case expr_option: + if (in_options) + s0 = get_option (result, + expr -> data.option -> universe, + packet, lease, client_state, + in_options, cfg_options, in_options, + scope, expr -> data.option -> code, + file, line); + else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: option %s.%s = %s", + expr -> data.option -> universe -> name, + expr -> data.option -> name, + s0 ? print_hex_1 (result -> len, result -> data, 60) + : "NULL"); +#endif + return s0; + + case expr_config_option: + if (cfg_options) + s0 = get_option (result, + expr -> data.option -> universe, + packet, lease, client_state, + in_options, cfg_options, cfg_options, + scope, expr -> data.option -> code, + file, line); + else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: config-option %s.%s = %s", + expr -> data.option -> universe -> name, + expr -> data.option -> name, + s0 ? print_hex_1 (result -> len, result -> data, 60) + : "NULL"); +#endif + return s0; + + /* Combine the hardware type and address. */ + case expr_hardware: + /* On the client, hardware is our hardware. */ + if (client_state) { + memset (result, 0, sizeof *result); + result -> data = + client_state -> interface -> hw_address.hbuf; + result -> len = + client_state -> interface -> hw_address.hlen; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: hardware = %s", + print_hex_1 (result -> len, + result -> data, 60)); +#endif + return 1; + } + + /* The server cares about the client's hardware address, + so only in the case where we are examining a packet can + we return anything. */ + if (!packet || !packet -> raw) { + log_error ("data: hardware: raw packet not available"); + return 0; + } + if (packet -> raw -> hlen > sizeof packet -> raw -> chaddr) { + log_error ("data: hardware: invalid hlen (%d)\n", + packet -> raw -> hlen); + return 0; + } + result -> len = packet -> raw -> hlen + 1; + if (buffer_allocate (&result -> buffer, result -> len, + file, line)) { + result -> data = &result -> buffer -> data [0]; + result -> buffer -> data [0] = packet -> raw -> htype; + memcpy (&result -> buffer -> data [1], + packet -> raw -> chaddr, + packet -> raw -> hlen); + result -> terminated = 0; + } else { + log_error ("data: hardware: no memory for buffer."); + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: hardware = %s", + print_hex_1 (result -> len, result -> data, 60)); +#endif + return 1; + + /* Extract part of the raw packet. */ + case expr_packet: + if (!packet || !packet -> raw) { + log_error ("data: packet: raw packet not available"); + return 0; + } + + s0 = evaluate_numeric_expression (&offset, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.packet.offset); + s1 = evaluate_numeric_expression (&len, + packet, lease, client_state, + in_options, cfg_options, + scope, + expr -> data.packet.len); + if (s0 && s1 && offset < packet -> packet_length) { + if (offset + len > packet -> packet_length) + result -> len = + packet -> packet_length - offset; + else + result -> len = len; + if (buffer_allocate (&result -> buffer, + result -> len, file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (result -> buffer -> data, + (((unsigned char *)(packet -> raw)) + + offset), result -> len); + result -> terminated = 0; + } else { + log_error ("data: packet: no buffer memory."); + return 0; + } + s2 = 1; + } else + s2 = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: packet (%ld, %ld) = %s", + offset, len, + s2 ? print_hex_1 (result -> len, + result -> data, 60) : NULL); +#endif + return s2; + + /* The encapsulation of all defined options in an + option space... */ + case expr_encapsulate: + if (cfg_options) + s0 = option_space_encapsulate + (result, packet, lease, client_state, + in_options, cfg_options, scope, + &expr -> data.encapsulate); + else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: encapsulate (%s) = %s", + expr -> data.encapsulate.data, + s0 ? print_hex_1 (result -> len, + result -> data, 60) : "NULL"); +#endif + return s0; + + /* Some constant data... */ + case expr_const_data: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: const = %s", + print_hex_1 (expr -> data.const_data.len, + expr -> data.const_data.data, 60)); +#endif + data_string_copy (result, + &expr -> data.const_data, file, line); + return 1; + + /* Hostname lookup... */ + case expr_host_lookup: + s0 = do_host_lookup (result, expr -> data.host_lookup); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: DNS lookup (%s) = %s", + expr -> data.host_lookup -> hostname, + (s0 + ? print_dotted_quads (result -> len, result -> data) + : "NULL")); +#endif + return s0; + + /* Concatenation... */ + case expr_concat: + memset (&data, 0, sizeof data); + s0 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.concat [0], MDL); + memset (&other, 0, sizeof other); + s1 = evaluate_data_expression (&other, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.concat [1], MDL); + + if (s0 && s1) { + result -> len = data.len + other.len; + if (!buffer_allocate (&result -> buffer, + (result -> len + other.terminated), + file, line)) { + log_error ("data: concat: no memory"); + result -> len = 0; + data_string_forget (&data, MDL); + data_string_forget (&other, MDL); + return 0; + } + result -> data = &result -> buffer -> data [0]; + memcpy (result -> buffer -> data, data.data, data.len); + memcpy (&result -> buffer -> data [data.len], + other.data, other.len + other.terminated); + } + + if (s0) + data_string_forget (&data, MDL); + if (s1) + data_string_forget (&other, MDL); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: concat (%s, %s) = %s", + s0 ? print_hex_1 (data.len, data.data, 20) : "NULL", + s1 ? print_hex_2 (other.len, other.data, 20) : "NULL", + ((s0 && s1) + ? print_hex_3 (result -> len, result -> data, 30) + : "NULL")); +#endif + return s0 && s1; + + case expr_encode_int8: + s0 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.encode_int); + if (s0) { + result -> len = 1; + if (!buffer_allocate (&result -> buffer, + 1, file, line)) { + log_error ("data: encode_int8: no memory"); + result -> len = 0; + s0 = 0; + } else { + result -> data = &result -> buffer -> data [0]; + result -> buffer -> data [0] = len; + } + } else + result -> len = 0; + +#if defined (DEBUG_EXPRESSIONS) + if (!s0) + log_debug ("data: encode_int8 (NULL) = NULL"); + else + log_debug ("data: encode_int8 (%ld) = %s", len, + print_hex_2 (result -> len, + result -> data, 20)); +#endif + return s0; + + + case expr_encode_int16: + s0 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.encode_int); + if (s0) { + result -> len = 2; + if (!buffer_allocate (&result -> buffer, 2, + file, line)) { + log_error ("data: encode_int16: no memory"); + result -> len = 0; + s0 = 0; + } else { + result -> data = &result -> buffer -> data [0]; + putUShort (result -> buffer -> data, len); + } + } else + result -> len = 0; + +#if defined (DEBUG_EXPRESSIONS) + if (!s0) + log_debug ("data: encode_int16 (NULL) = NULL"); + else + log_debug ("data: encode_int16 (%ld) = %s", len, + print_hex_2 (result -> len, + result -> data, 20)); +#endif + return s0; + + case expr_encode_int32: + s0 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.encode_int); + if (s0) { + result -> len = 4; + if (!buffer_allocate (&result -> buffer, 4, + file, line)) { + log_error ("data: encode_int32: no memory"); + result -> len = 0; + s0 = 0; + } else { + result -> data = &result -> buffer -> data [0]; + putULong (result -> buffer -> data, len); + } + } else + result -> len = 0; + +#if defined (DEBUG_EXPRESSIONS) + if (!s0) + log_debug ("data: encode_int32 (NULL) = NULL"); + else + log_debug ("data: encode_int32 (%ld) = %s", len, + print_hex_2 (result -> len, + result -> data, 20)); +#endif + return s0; + + case expr_binary_to_ascii: + /* Evaluate the base (offset) and width (len): */ + s0 = evaluate_numeric_expression + (&offset, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.b2a.base); + s1 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.b2a.width); + + /* Evaluate the seperator string. */ + memset (&data, 0, sizeof data); + s2 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.b2a.seperator, + MDL); + + /* Evaluate the data to be converted. */ + memset (&other, 0, sizeof other); + s3 = evaluate_data_expression (&other, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.b2a.buffer, MDL); + + if (s0 && s1 && s2 && s3) { + unsigned buflen, i; + + if (len != 8 && len != 16 && len != 32) { + log_info ("binary_to_ascii: %s %ld!", + "invalid width", len); + status = 0; + goto b2a_out; + } + len /= 8; + + /* The buffer must be a multiple of the number's + width. */ + if (other.len % len) { + log_info ("binary-to-ascii: %s %d %s %ld!", + "length of buffer", other.len, + "not a multiple of width", len); + status = 0; + goto b2a_out; + } + + /* Count the width of the output. */ + buflen = 0; + for (i = 0; i < other.len; i += len) { + if (len == 1) { + if (offset == 8) { + if (other.data [i] < 8) + buflen++; + else if (other.data [i] < 64) + buflen += 2; + else + buflen += 3; + } else if (offset == 10) { + if (other.data [i] < 10) + buflen++; + else if (other.data [i] < 100) + buflen += 2; + else + buflen += 3; + } else if (offset == 16) { + if (other.data [i] < 16) + buflen++; + else + buflen += 2; + } else + buflen += (converted_length + (&other.data [i], + offset, 1)); + } else + buflen += (converted_length + (&other.data [i], + offset, len)); + if (i + len != other.len) + buflen += data.len; + } + + if (!buffer_allocate (&result -> buffer, + buflen + 1, file, line)) { + log_error ("data: binary-to-ascii: no memory"); + status = 0; + goto b2a_out; + } + result -> data = &result -> buffer -> data [0]; + result -> len = buflen; + result -> terminated = 1; + + buflen = 0; + for (i = 0; i < other.len; i += len) { + buflen += (binary_to_ascii + (&result -> buffer -> data [buflen], + &other.data [i], offset, len)); + if (i + len != other.len) { + memcpy (&result -> + buffer -> data [buflen], + data.data, data.len); + buflen += data.len; + } + } + /* NUL terminate. */ + result -> buffer -> data [buflen] = 0; + status = 1; + } else + status = 0; + + b2a_out: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: binary-to-ascii (%s, %s, %s, %s) = %s", + s0 ? print_dec_1 (offset) : "NULL", + s1 ? print_dec_2 (len) : "NULL", + s2 ? print_hex_1 (data.len, data.data, 30) : "NULL", + s3 ? print_hex_2 (other.len, other.data, 30) : "NULL", + (status ? print_hex_3 (result -> len, result -> data, 30) + : "NULL")); +#endif + if (s2) + data_string_forget (&data, MDL); + if (s3) + data_string_forget (&other, MDL); + if (status) + return 1; + return 0; + + case expr_reverse: + /* Evaluate the width (len): */ + s0 = evaluate_numeric_expression + (&len, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.reverse.width); + + /* Evaluate the data. */ + memset (&data, 0, sizeof data); + s1 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.reverse.buffer, + MDL); + + if (s0 && s1) { + char *upper; + int i; + + /* The buffer must be a multiple of the number's + width. */ + if (data.len % len) { + log_info ("reverse: %s %d %s %ld!", + "length of buffer", data.len, + "not a multiple of width", len); + status = 0; + goto reverse_out; + } + + /* XXX reverse in place? I don't think we can. */ + if (!buffer_allocate (&result -> buffer, + data.len, file, line)) { + log_error ("data: reverse: no memory"); + status = 0; + goto reverse_out; + } + result -> data = &result -> buffer -> data [0]; + result -> len = data.len; + result -> terminated = 0; + + for (i = 0; i < data.len; i += len) { + memcpy (&result -> buffer -> data [i], + &data.data [data.len - i - len], len); + } + status = 1; + } else + status = 0; + + reverse_out: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: reverse (%s, %s) = %s", + s0 ? print_dec_1 (len) : "NULL", + s1 ? print_hex_1 (data.len, data.data, 30) : "NULL", + (status ? print_hex_3 (result -> len, result -> data, 30) + : "NULL")); +#endif + if (s0) + data_string_forget (&data, MDL); + if (status) + return 1; + return 0; + + case expr_leased_address: + if (!lease) { + log_error ("data: leased_address: not available"); + return 0; + } + result -> len = lease -> ip_addr.len; + if (buffer_allocate (&result -> buffer, result -> len, + file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (&result -> buffer -> data [0], + lease -> ip_addr.iabuf, lease -> ip_addr.len); + result -> terminated = 0; + } else { + log_error ("data: leased-address: no memory."); + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: leased-address = %s", + print_hex_1 (result -> len, result -> data, 60)); +#endif + return 1; + + case expr_pick_first_value: + memset (&data, 0, sizeof data); + if ((evaluate_data_expression + (result, packet, + lease, client_state, in_options, cfg_options, + scope, expr -> data.pick_first_value.car, MDL))) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: pick_first_value (%s, xxx)", + print_hex_1 (result -> len, + result -> data, 40)); +#endif + return 1; + } + + if (expr -> data.pick_first_value.cdr && + (evaluate_data_expression + (result, packet, + lease, client_state, in_options, cfg_options, + scope, expr -> data.pick_first_value.cdr, MDL))) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: pick_first_value (NULL, %s)", + print_hex_1 (result -> len, + result -> data, 40)); +#endif + return 1; + } + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: pick_first_value (NULL, NULL) = NULL"); +#endif + return 0; + + case expr_host_decl_name: + if (!lease || !lease -> host) { + log_error ("data: host_decl_name: not available"); + return 0; + } + result -> len = strlen (lease -> host -> name); + if (buffer_allocate (&result -> buffer, + result -> len + 1, file, line)) { + result -> data = &result -> buffer -> data [0]; + strcpy ((char *)&result -> buffer -> data [0], + lease -> host -> name); + result -> terminated = 1; + } else { + log_error ("data: host-decl-name: no memory."); + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: host-decl-name = %s", lease -> host -> name); +#endif + return 1; + + case expr_null: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: null = NULL"); +#endif + return 0; + + case expr_variable_reference: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (binding -> value -> type == binding_data) { + data_string_copy (result, + &binding -> value -> value.data, + file, line); + s0 = 1; + } else if (binding -> value -> type != binding_data) { + log_error ("binding type %d in %s.", + binding -> value -> type, + "evaluate_data_expression"); + s0 = 0; + } else + s0 = 0; + } else + s0 = 0; + } else + s0 = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: %s = %s", expr -> data.variable, + s0 ? print_hex_1 (result -> len, + result -> data, 50) : "NULL"); +#endif + return s0; + + case expr_funcall: + bv = (struct binding_value *)0; + s0 = evaluate_expression (&bv, packet, lease, client_state, + in_options, cfg_options, + scope, expr, MDL); + if (s0) { + if (bv -> type != binding_data) + log_error ("%s() returned type %d in %s.", + expr -> data.funcall.name, + bv -> type, + "evaluate_data_expression"); + else + data_string_copy (result, &bv -> value.data, + file, line); + binding_value_dereference (&bv, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: %s = %s", expr -> data.funcall.name, + s0 ? print_hex_1 (result -> len, + result -> data, 50) : "NULL"); +#endif + break; + + /* Extract the filename. */ + case expr_filename: + if (packet && packet -> raw -> file [0]) { + char *fn = + memchr (packet -> raw -> file, 0, + sizeof packet -> raw -> file); + if (!fn) + fn = ((char *)packet -> raw -> file + + sizeof packet -> raw -> file); + result -> len = fn - &(packet -> raw -> file [0]); + if (buffer_allocate (&result -> buffer, + result -> len + 1, file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (&result -> buffer -> data [0], + packet -> raw -> file, + result -> len); + result -> buffer -> data [result -> len] = 0; + result -> terminated = 1; + s0 = 1; + } else { + log_error ("data: filename: no memory."); + s0 = 0; + } + } else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_info ("data: filename = \"%s\"", + s0 ? (const char *)(result -> data) : "NULL"); +#endif + return s0; + + /* Extract the server name. */ + case expr_sname: + if (packet && packet -> raw -> sname [0]) { + char *fn = + memchr (packet -> raw -> sname, 0, + sizeof packet -> raw -> sname); + if (!fn) + fn = ((char *)packet -> raw -> sname + + sizeof packet -> raw -> sname); + result -> len = fn - &packet -> raw -> sname [0]; + if (buffer_allocate (&result -> buffer, + result -> len + 1, file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (&result -> buffer -> data [0], + packet -> raw -> sname, + result -> len); + result -> buffer -> data [result -> len] = 0; + result -> terminated = 1; + s0 = 1; + } else { + log_error ("data: sname: no memory."); + s0 = 0; + } + } else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_info ("data: sname = \"%s\"", + s0 ? (const char *)(result -> data) : "NULL"); +#endif + return s0; + + case expr_check: + case expr_equal: + case expr_not_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_match: + case expr_static: + case expr_known: + case expr_none: + case expr_exists: + case expr_variable_exists: + log_error ("Boolean opcode in evaluate_data_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + case expr_lease_time: + case expr_dns_transaction: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + log_error ("Numeric opcode in evaluate_data_expression: %d", + expr -> op); + return 0; + + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + log_error ("dns update opcode in evaluate_data_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("function definition in evaluate_data_expression"); + return 0; + + case expr_arg: + break; + } + + log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op); + return 0; +} + +int evaluate_numeric_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr) + unsigned long *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + struct data_string data; + int status, sleft, sright; +#if defined (NSUPDATE) + ns_updrec *nut; + ns_updque uq; +#endif + struct expression *cur, *next; + struct binding *binding; + struct binding_value *bv; + unsigned long ileft, iright; + + switch (expr -> op) { + case expr_check: + case expr_equal: + case expr_not_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_match: + case expr_static: + case expr_known: + case expr_none: + case expr_exists: + case expr_variable_exists: + log_error ("Boolean opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_null: + log_error ("Data opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + memset (&data, 0, sizeof data); + status = evaluate_data_expression + (&data, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.extract_int, MDL); + if (status) + *result = data.data [0]; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: extract_int8 (%s) = %s", + status ? print_hex_1 (data.len, data.data, 60) : "NULL", + status ? print_dec_1 (*result) : "NULL" ); +#endif + if (status) data_string_forget (&data, MDL); + return status; + + case expr_extract_int16: + memset (&data, 0, sizeof data); + status = (evaluate_data_expression + (&data, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.extract_int, MDL)); + if (status && data.len >= 2) + *result = getUShort (data.data); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: extract_int16 (%s) = %ld", + ((status && data.len >= 2) ? + print_hex_1 (data.len, data.data, 60) : "NULL"), + *result); +#endif + if (status) data_string_forget (&data, MDL); + return (status && data.len >= 2); + + case expr_extract_int32: + memset (&data, 0, sizeof data); + status = (evaluate_data_expression + (&data, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.extract_int, MDL)); + if (status && data.len >= 4) + *result = getULong (data.data); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: extract_int32 (%s) = %ld", + ((status && data.len >= 4) ? + print_hex_1 (data.len, data.data, 60) : "NULL"), + *result); +#endif + if (status) data_string_forget (&data, MDL); + return (status && data.len >= 4); + + case expr_const_int: + *result = expr -> data.const_int; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("number: CONSTANT = %ld", *result); +#endif + return 1; + + case expr_lease_time: + if (!lease) { + log_error ("data: leased_lease: not available"); + return 0; + } + if (lease -> ends < cur_time) { + log_error ("%s %lu when it is now %lu", + "data: lease_time: lease ends at", + (long)(lease -> ends), (long)cur_time); + return 0; + } + *result = lease -> ends - cur_time; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("number: lease-time = (%lu - %lu) = %ld", + lease -> ends, + cur_time, *result); +#endif + return 1; + + case expr_dns_transaction: +#if !defined (NSUPDATE) + return 0; +#else + if (!resolver_inited) { + minires_ninit (&resolver_state); + resolver_inited = 1; + resolver_state.retrans = 1; + resolver_state.retry = 1; + } + ISC_LIST_INIT (uq); + cur = expr; + do { + next = cur -> data.dns_transaction.cdr; + nut = 0; + status = (evaluate_dns_expression + (&nut, packet, + lease, client_state, in_options, cfg_options, + scope, cur -> data.dns_transaction.car)); + if (!status) + goto dns_bad; + ISC_LIST_APPEND (uq, nut, r_link); + cur = next; + } while (next); + + /* Do the update and record the error code, if there was + an error; otherwise set it to NOERROR. */ + *result = minires_nupdate (&resolver_state, + ISC_LIST_HEAD (uq)); + status = 1; + + print_dns_status ((int)*result, &uq); + + dns_bad: + while (!ISC_LIST_EMPTY (uq)) { + ns_updrec *tmp = ISC_LIST_HEAD (uq); + ISC_LIST_UNLINK (uq, tmp, r_link); + if (tmp -> r_data_ephem) { + dfree (tmp -> r_data_ephem, MDL); + tmp -> r_data = (unsigned char *)0; + tmp -> r_data_ephem = (unsigned char *)0; + } + minires_freeupdrec (tmp); + } + return status; +#endif /* NSUPDATE */ + + case expr_variable_reference: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (binding -> value -> type == binding_numeric) { + *result = binding -> value -> value.intval; + status = 1; + } else { + log_error ("binding type %d in %s.", + binding -> value -> type, + "evaluate_numeric_expression"); + status = 0; + } + } else + status = 0; + } else + status = 0; +#if defined (DEBUG_EXPRESSIONS) + if (status) + log_debug ("numeric: %s = %ld", + expr -> data.variable, *result); + else + log_debug ("numeric: %s = NULL", + expr -> data.variable); +#endif + return status; + + case expr_funcall: + bv = (struct binding_value *)0; + status = evaluate_expression (&bv, packet, lease, + client_state, + in_options, cfg_options, + scope, expr, MDL); + if (status) { + if (bv -> type != binding_numeric) + log_error ("%s() returned type %d in %s.", + expr -> data.funcall.name, + bv -> type, + "evaluate_numeric_expression"); + else + *result = bv -> value.intval; + binding_value_dereference (&bv, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: %s = %ld", expr -> data.funcall.name, + status ? *result : 0); +#endif + break; + + case expr_add: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld + %ld = %ld", + ileft, iright, ileft + iright); + else if (sleft) + log_debug ("num: %ld + NULL = NULL", ileft); + else + log_debug ("num: NULL + %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft + iright; + return 1; + } + return 0; + + case expr_subtract: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld - %ld = %ld", + ileft, iright, ileft - iright); + else if (sleft) + log_debug ("num: %ld - NULL = NULL", ileft); + else + log_debug ("num: NULL - %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft - iright; + return 1; + } + return 0; + + case expr_multiply: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld * %ld = %ld", + ileft, iright, ileft * iright); + else if (sleft) + log_debug ("num: %ld * NULL = NULL", ileft); + else + log_debug ("num: NULL * %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft * iright; + return 1; + } + return 0; + + case expr_divide: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) { + if (iright != 0) + log_debug ("num: %ld / %ld = %ld", + ileft, iright, ileft / iright); + else + log_debug ("num: %ld / %ld = NULL", + ileft, iright); + } else if (sleft) + log_debug ("num: %ld / NULL = NULL", ileft); + else + log_debug ("num: NULL / %ld = NULL", iright); +#endif + if (sleft && sright && iright) { + *result = ileft / iright; + return 1; + } + return 0; + + case expr_remainder: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) { + if (iright != 0) + log_debug ("num: %ld %% %ld = %ld", + ileft, iright, ileft % iright); + else + log_debug ("num: %ld %% %ld = NULL", + ileft, iright); + } else if (sleft) + log_debug ("num: %ld %% NULL = NULL", ileft); + else + log_debug ("num: NULL %% %ld = NULL", iright); +#endif + if (sleft && sright && iright) { + *result = ileft % iright; + return 1; + } + return 0; + + case expr_binary_and: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld | %ld = %ld", + ileft, iright, ileft & iright); + else if (sleft) + log_debug ("num: %ld & NULL = NULL", ileft); + else + log_debug ("num: NULL & %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft & iright; + return 1; + } + return 0; + + case expr_binary_or: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld | %ld = %ld", + ileft, iright, ileft | iright); + else if (sleft) + log_debug ("num: %ld | NULL = NULL", ileft); + else + log_debug ("num: NULL | %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft | iright; + return 1; + } + return 0; + + case expr_binary_xor: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld ^ %ld = %ld", + ileft, iright, ileft ^ iright); + else if (sleft) + log_debug ("num: %ld ^ NULL = NULL", ileft); + else + log_debug ("num: NULL ^ %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft ^ iright; + return 1; + } + return 0; + + case expr_client_state: + if (client_state) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: client-state = %d", + client_state -> state); +#endif + *result = client_state -> state; + return 1; + } else { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: client-state = NULL"); +#endif + return 0; + } + + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + log_error ("dns opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("function definition in evaluate_numeric_expr"); + return 0; + + case expr_arg: + break; + } + + log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op); + return 0; +} + +/* Return data hanging off of an option cache structure, or if there + isn't any, evaluate the expression hanging off of it and return the + result of that evaluation. There should never be both an expression + and a valid data_string. */ + +int evaluate_option_cache (result, packet, lease, client_state, + in_options, cfg_options, scope, oc, file, line) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct option_cache *oc; + const char *file; + int line; +{ + if (oc -> data.len) { + data_string_copy (result, &oc -> data, file, line); + return 1; + } + if (!oc -> expression) + return 0; + return evaluate_data_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, + oc -> expression, file, line); +} + +/* Evaluate an option cache and extract a boolean from the result, + returning the boolean. Return false if there is no data. */ + +int evaluate_boolean_option_cache (ignorep, packet, + lease, client_state, in_options, + cfg_options, scope, oc, file, line) + int *ignorep; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct option_cache *oc; + const char *file; + int line; +{ + struct data_string ds; + int result; + + /* So that we can be called with option_lookup as an argument. */ + if (!oc || !in_options) + return 0; + + memset (&ds, 0, sizeof ds); + if (!evaluate_option_cache (&ds, packet, + lease, client_state, in_options, + cfg_options, scope, oc, file, line)) + return 0; + + if (ds.len) { + result = ds.data [0]; + if (result == 2) { + result = 0; + *ignorep = 1; + } else + *ignorep = 0; + } else + result = 0; + data_string_forget (&ds, MDL); + return result; +} + + +/* Evaluate a boolean expression and return the result of the evaluation, + or FALSE if it failed. */ + +int evaluate_boolean_expression_result (ignorep, packet, lease, client_state, + in_options, cfg_options, scope, expr) + int *ignorep; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + int result; + + /* So that we can be called with option_lookup as an argument. */ + if (!expr) + return 0; + + if (!evaluate_boolean_expression (&result, packet, lease, client_state, + in_options, cfg_options, + scope, expr)) + return 0; + + if (result == 2) { + *ignorep = 1; + result = 0; + } else + *ignorep = 0; + return result; +} + + +/* Dereference an expression node, and if the reference count goes to zero, + dereference any data it refers to, and then free it. */ +void expression_dereference (eptr, file, line) + struct expression **eptr; + const char *file; + int line; +{ + struct expression *expr = *eptr; + + /* Zero the pointer. */ + *eptr = (struct expression *)0; + + /* Decrement the reference count. If it's nonzero, we're + done. */ + --(expr -> refcnt); + rc_register (file, line, eptr, expr, expr -> refcnt, 1, RC_MISC); + if (expr -> refcnt > 0) + return; + if (expr -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (expr); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return; +#endif + } + + /* Dereference subexpressions. */ + switch (expr -> op) { + /* All the binary operators can be handled the same way. */ + case expr_equal: + case expr_not_equal: + case expr_concat: + case expr_and: + case expr_or: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + if (expr -> data.equal [0]) + expression_dereference (&expr -> data.equal [0], + file, line); + if (expr -> data.equal [1]) + expression_dereference (&expr -> data.equal [1], + file, line); + break; + + case expr_substring: + if (expr -> data.substring.expr) + expression_dereference (&expr -> data.substring.expr, + file, line); + if (expr -> data.substring.offset) + expression_dereference (&expr -> data.substring.offset, + file, line); + if (expr -> data.substring.len) + expression_dereference (&expr -> data.substring.len, + file, line); + break; + + case expr_suffix: + if (expr -> data.suffix.expr) + expression_dereference (&expr -> data.suffix.expr, + file, line); + if (expr -> data.suffix.len) + expression_dereference (&expr -> data.suffix.len, + file, line); + break; + + case expr_not: + if (expr -> data.not) + expression_dereference (&expr -> data.not, file, line); + break; + + case expr_packet: + if (expr -> data.packet.offset) + expression_dereference (&expr -> data.packet.offset, + file, line); + if (expr -> data.packet.len) + expression_dereference (&expr -> data.packet.len, + file, line); + break; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + if (expr -> data.extract_int) + expression_dereference (&expr -> data.extract_int, + file, line); + break; + + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + if (expr -> data.encode_int) + expression_dereference (&expr -> data.encode_int, + file, line); + break; + + case expr_encapsulate: + case expr_const_data: + data_string_forget (&expr -> data.const_data, file, line); + break; + + case expr_host_lookup: + if (expr -> data.host_lookup) + dns_host_entry_dereference (&expr -> data.host_lookup, + file, line); + break; + + case expr_binary_to_ascii: + if (expr -> data.b2a.base) + expression_dereference (&expr -> data.b2a.base, + file, line); + if (expr -> data.b2a.width) + expression_dereference (&expr -> data.b2a.width, + file, line); + if (expr -> data.b2a.seperator) + expression_dereference (&expr -> data.b2a.seperator, + file, line); + if (expr -> data.b2a.buffer) + expression_dereference (&expr -> data.b2a.buffer, + file, line); + break; + + case expr_pick_first_value: + if (expr -> data.pick_first_value.car) + expression_dereference (&expr -> data.pick_first_value.car, + file, line); + if (expr -> data.pick_first_value.cdr) + expression_dereference (&expr -> data.pick_first_value.cdr, + file, line); + break; + + case expr_reverse: + if (expr -> data.reverse.width) + expression_dereference (&expr -> data.reverse.width, + file, line); + if (expr -> data.reverse.buffer) + expression_dereference + (&expr -> data.reverse.buffer, file, line); + break; + + case expr_dns_transaction: + if (expr -> data.dns_transaction.car) + expression_dereference (&expr -> data.dns_transaction.car, + file, line); + if (expr -> data.dns_transaction.cdr) + expression_dereference (&expr -> data.dns_transaction.cdr, + file, line); + break; + + case expr_ns_add: + if (expr -> data.ns_add.rrname) + expression_dereference (&expr -> data.ns_add.rrname, + file, line); + if (expr -> data.ns_add.rrdata) + expression_dereference (&expr -> data.ns_add.rrdata, + file, line); + if (expr -> data.ns_add.ttl) + expression_dereference (&expr -> data.ns_add.ttl, + file, line); + break; + + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + if (expr -> data.ns_delete.rrname) + expression_dereference (&expr -> data.ns_delete.rrname, + file, line); + if (expr -> data.ns_delete.rrdata) + expression_dereference (&expr -> data.ns_delete.rrdata, + file, line); + break; + + case expr_variable_reference: + case expr_variable_exists: + if (expr -> data.variable) + dfree (expr -> data.variable, file, line); + break; + + case expr_funcall: + if (expr -> data.funcall.name) + dfree (expr -> data.funcall.name, file, line); + if (expr -> data.funcall.arglist) + expression_dereference (&expr -> data.funcall.arglist, + file, line); + break; + + case expr_arg: + if (expr -> data.arg.val) + expression_dereference (&expr -> data.arg.val, + file, line); + if (expr -> data.arg.next) + expression_dereference (&expr -> data.arg.next, + file, line); + break; + + case expr_function: + fundef_dereference (&expr -> data.func, file, line); + break; + + /* No subexpressions. */ + case expr_leased_address: + case expr_lease_time: + case expr_filename: + case expr_sname: + case expr_const_int: + case expr_check: + case expr_option: + case expr_hardware: + case expr_exists: + case expr_known: + case expr_null: + break; + + default: + break; + } + free_expression (expr, MDL); +} + +int is_dns_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_ns_add || + expr -> op == expr_ns_delete || + expr -> op == expr_ns_exists || + expr -> op == expr_ns_not_exists); +} + +int is_boolean_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_check || + expr -> op == expr_exists || + expr -> op == expr_variable_exists || + expr -> op == expr_equal || + expr -> op == expr_not_equal || + expr -> op == expr_and || + expr -> op == expr_or || + expr -> op == expr_not || + expr -> op == expr_known || + expr -> op == expr_static); +} + +int is_data_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_substring || + expr -> op == expr_suffix || + expr -> op == expr_option || + expr -> op == expr_hardware || + expr -> op == expr_const_data || + expr -> op == expr_packet || + expr -> op == expr_concat || + expr -> op == expr_encapsulate || + expr -> op == expr_encode_int8 || + expr -> op == expr_encode_int16 || + expr -> op == expr_encode_int32 || + expr -> op == expr_host_lookup || + expr -> op == expr_binary_to_ascii || + expr -> op == expr_filename || + expr -> op == expr_sname || + expr -> op == expr_reverse || + expr -> op == expr_pick_first_value || + expr -> op == expr_host_decl_name || + expr -> op == expr_leased_address || + expr -> op == expr_config_option || + expr -> op == expr_null); +} + +int is_numeric_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_extract_int8 || + expr -> op == expr_extract_int16 || + expr -> op == expr_extract_int32 || + expr -> op == expr_const_int || + expr -> op == expr_lease_time || + expr -> op == expr_dns_transaction || + expr -> op == expr_add || + expr -> op == expr_subtract || + expr -> op == expr_multiply || + expr -> op == expr_divide || + expr -> op == expr_remainder || + expr -> op == expr_binary_and || + expr -> op == expr_binary_or || + expr -> op == expr_binary_xor || + expr -> op == expr_client_state); +} + +int is_compound_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_ns_add || + expr -> op == expr_ns_delete || + expr -> op == expr_ns_exists || + expr -> op == expr_ns_not_exists || + expr -> op == expr_substring || + expr -> op == expr_suffix || + expr -> op == expr_option || + expr -> op == expr_concat || + expr -> op == expr_encode_int8 || + expr -> op == expr_encode_int16 || + expr -> op == expr_encode_int32 || + expr -> op == expr_binary_to_ascii || + expr -> op == expr_reverse || + expr -> op == expr_pick_first_value || + expr -> op == expr_config_option || + expr -> op == expr_extract_int8 || + expr -> op == expr_extract_int16 || + expr -> op == expr_extract_int32 || + expr -> op == expr_dns_transaction); +} + +static int op_val PROTO ((enum expr_op)); + +static int op_val (op) + enum expr_op op; +{ + switch (op) { + case expr_none: + case expr_match: + case expr_static: + case expr_check: + case expr_substring: + case expr_suffix: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_not: + case expr_option: + case expr_hardware: + case expr_packet: + case expr_const_data: + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_const_int: + case expr_exists: + case expr_variable_exists: + case expr_known: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_lease_time: + case expr_dns_transaction: + case expr_null: + case expr_variable_reference: + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + case expr_arg: + case expr_funcall: + case expr_function: + /* XXXDPN: Need to assign sane precedences to these. */ + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + return 100; + + case expr_equal: + case expr_not_equal: + return 4; + + case expr_or: + case expr_and: + return 3; + + case expr_add: + case expr_subtract: + return 2; + + case expr_multiply: + case expr_divide: + case expr_remainder: + return 1; + } + return 100; +} + +int op_precedence (op1, op2) + enum expr_op op1, op2; +{ + int ov1, ov2; + + return op_val (op1) - op_val (op2); +} + +enum expression_context expression_context (struct expression *expr) +{ + if (is_data_expression (expr)) + return context_data; + if (is_numeric_expression (expr)) + return context_numeric; + if (is_boolean_expression (expr)) + return context_boolean; + if (is_dns_expression (expr)) + return context_dns; + return context_any; +} + +enum expression_context op_context (op) + enum expr_op op; +{ + switch (op) { +/* XXX Why aren't these specific? */ + case expr_none: + case expr_match: + case expr_static: + case expr_check: + case expr_substring: + case expr_suffix: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_not: + case expr_option: + case expr_hardware: + case expr_packet: + case expr_const_data: + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_const_int: + case expr_exists: + case expr_variable_exists: + case expr_known: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_lease_time: + case expr_null: + case expr_variable_reference: + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + case expr_dns_transaction: + case expr_arg: + case expr_funcall: + case expr_function: + return context_any; + + case expr_equal: + case expr_not_equal: + return context_data; + + case expr_and: + return context_boolean; + + case expr_or: + return context_boolean; + + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + return context_numeric; + } + return context_any; +} + +int write_expression (file, expr, col, indent, firstp) + FILE *file; + struct expression *expr; + int col; + int indent; + int firstp; +{ + struct expression *e; + const char *s; + char obuf [65]; + int scol; + int width; + + /* If this promises to be a fat expression, start a new line. */ + if (!firstp && is_compound_expression (expr)) { + indent_spaces (file, indent); + col = indent; + } + + switch (expr -> op) { + case expr_none: + col = token_print_indent (file, col, indent, "", "", "null"); + break; + + case expr_check: + col = token_print_indent (file, col, indent, "", "", "check"); + col = token_print_indent_concat (file, col, indent, + " ", "", "\"", + expr -> data.check -> name, + "\"", (char *)0); + break; + + case expr_not_equal: + s = "!="; + goto binary; + + case expr_equal: + s = "="; + binary: + col = write_expression (file, expr -> data.equal [0], + col, indent, 1); + col = token_print_indent (file, col, indent, " ", " ", s); + col = write_expression (file, expr -> data.equal [1], + col, indent + 2, 0); + break; + + case expr_substring: + col = token_print_indent (file, col, indent, "", "", + "substring"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.substring.expr, + col, scol, 1); + col = token_print_indent (file, col, indent, "", " ", ","); + col = write_expression (file, expr -> data.substring.offset, + col, indent, 0); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.substring.len, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_suffix: + col = token_print_indent (file, col, indent, "", "", "suffix"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.suffix.expr, + col, scol, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.suffix.len, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_concat: + e = expr; + col = token_print_indent (file, col, indent, "", "", + "concat"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + firstp = 1; + concat_again: + col = write_expression (file, e -> data.concat [0], + col, scol, firstp); + firstp = 0; + if (!e -> data.concat [1]) + goto no_concat_cdr; + col = token_print_indent (file, col, scol, "", " ", ","); + if (e -> data.concat [1] -> op == expr_concat) { + e = e -> data.concat [1]; + goto concat_again; + } + col = write_expression (file, e -> data.concat [1], + col, scol, 0); + no_concat_cdr: + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_host_lookup: + col = token_print_indent (file, col, indent, "", "", + "gethostbyname"); + col = token_print_indent (file, col, indent, " ", "", "("); + col = token_print_indent_concat + (file, col, indent, "", "", + "\"", expr -> data.host_lookup -> hostname, "\"", + (char *)0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_add: + s = "+"; + goto binary; + + case expr_subtract: + s = "-"; + goto binary; + + case expr_multiply: + s = "*"; + goto binary; + + case expr_divide: + s = "/"; + goto binary; + + case expr_remainder: + s = "%"; + goto binary; + + case expr_binary_and: + s = "&"; + goto binary; + + case expr_binary_or: + s = "|"; + goto binary; + + case expr_binary_xor: + s = "^"; + goto binary; + + case expr_and: + s = "and"; + goto binary; + + case expr_or: + s = "or"; + goto binary; + + case expr_not: + col = token_print_indent (file, col, indent, "", " ", "not"); + col = write_expression (file, + expr -> data.not, col, indent + 2, 1); + break; + + case expr_option: + s = "option"; + + print_option_name: + col = token_print_indent (file, col, indent, "", "", s); + + if (expr -> data.option -> universe != &dhcp_universe) { + col = token_print_indent (file, col, indent, + " ", "", + (expr -> data.option -> + universe -> name)); + col = token_print_indent (file, col, indent, "", "", + "."); + col = token_print_indent (file, col, indent, "", "", + expr -> data.option -> name); + } else { + col = token_print_indent (file, col, indent, " ", "", + expr -> data.option -> name); + } + break; + + case expr_hardware: + col = token_print_indent (file, col, indent, "", "", + "hardware"); + break; + + case expr_packet: + col = token_print_indent (file, col, indent, "", "", + "packet"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.packet.offset, + col, indent, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.packet.len, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_const_data: + col = token_indent_data_string (file, col, indent, "", "", + &expr -> data.const_data); + break; + + case expr_extract_int8: + width = 8; + extract_int: + col = token_print_indent (file, col, indent, "", "", + "extract-int"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.extract_int, + col, indent, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + sprintf (obuf, "%d", width); + col = token_print_indent (file, col, scol, " ", "", obuf); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_extract_int16: + width = 16; + goto extract_int; + + case expr_extract_int32: + width = 32; + goto extract_int; + + case expr_encode_int8: + width = 8; + encode_int: + col = token_print_indent (file, col, indent, "", "", + "encode-int"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.extract_int, + col, indent, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + sprintf (obuf, "%d", width); + col = token_print_indent (file, col, scol, " ", "", obuf); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_encode_int16: + width = 16; + goto encode_int; + + case expr_encode_int32: + width = 32; + goto encode_int; + + case expr_const_int: + sprintf (obuf, "%lu", expr -> data.const_int); + col = token_print_indent (file, col, indent, "", "", obuf); + break; + + case expr_exists: + s = "exists"; + goto print_option_name; + + case expr_encapsulate: + col = token_print_indent (file, col, indent, "", "", + "encapsulate"); + col = token_indent_data_string (file, col, indent, " ", "", + &expr -> data.encapsulate); + break; + + case expr_known: + col = token_print_indent (file, col, indent, "", "", "known"); + break; + + case expr_reverse: + col = token_print_indent (file, col, indent, "", "", + "reverse"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.reverse.width, + col, scol, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.reverse.buffer, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_leased_address: + col = token_print_indent (file, col, indent, "", "", + "leased-address"); + break; + + case expr_client_state: + col = token_print_indent (file, col, indent, "", "", + "client-state"); + break; + + case expr_binary_to_ascii: + col = token_print_indent (file, col, indent, "", "", + "binary-to-ascii"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = col; + col = write_expression (file, expr -> data.b2a.base, + col, scol, 1); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.b2a.width, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.b2a.seperator, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.b2a.buffer, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_config_option: + s = "config-option"; + goto print_option_name; + + case expr_host_decl_name: + col = token_print_indent (file, col, indent, "", "", + "host-decl-name"); + break; + + case expr_pick_first_value: + e = expr; + col = token_print_indent (file, col, indent, "", "", + "concat"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = col; + firstp = 1; + pick_again: + col = write_expression (file, + e -> data.pick_first_value.car, + col, scol, firstp); + firstp = 0; + /* We're being very lisp-like right now - instead of + representing this expression as (first middle . last) we're + representing it as (first middle last), which means that the + tail cdr is always nil. Apologies to non-wisp-lizards - may + this obscure way of describing the problem motivate you to + learn more about the one true computing language. */ + if (!e -> data.pick_first_value.cdr) + goto no_pick_cdr; + col = token_print_indent (file, col, scol, "", " ", + ","); + if (e -> data.pick_first_value.cdr -> op == + expr_pick_first_value) { + e = e -> data.pick_first_value.cdr; + goto pick_again; + } + col = write_expression (file, + e -> data.pick_first_value.cdr, + col, scol, 0); + no_pick_cdr: + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_lease_time: + col = token_print_indent (file, col, indent, "", "", + "lease-time"); + break; + + case expr_dns_transaction: + col = token_print_indent (file, col, indent, "", "", + "ns-update"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = 0; + for (e = expr; + e && e -> op == expr_dns_transaction; + e = e -> data.dns_transaction.cdr) { + if (!scol) { + scol = col; + firstp = 1; + } else + firstp = 0; + col = write_expression (file, + e -> data.dns_transaction.car, + col, scol, firstp); + if (e -> data.dns_transaction.cdr) + col = token_print_indent (file, col, scol, + "", " ", ","); + } + if (e) + col = write_expression (file, e, col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_ns_add: + col = token_print_indent (file, col, indent, "", "", + "update"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = col; + sprintf (obuf, "%d", expr -> data.ns_add.rrclass); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + sprintf (obuf, "%d", expr -> data.ns_add.rrtype); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrname, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrdata, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.ttl, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_ns_delete: + col = token_print_indent (file, col, indent, "", "", + "delete"); + col = token_print_indent (file, col, indent, " ", "", + "("); + finish_ns_small: + scol = col; + sprintf (obuf, "%d", expr -> data.ns_add.rrclass); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + sprintf (obuf, "%d", expr -> data.ns_add.rrtype); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrname, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrdata, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_ns_exists: + col = token_print_indent (file, col, indent, "", "", + "exists"); + col = token_print_indent (file, col, indent, " ", "", + "("); + goto finish_ns_small; + + case expr_ns_not_exists: + col = token_print_indent (file, col, indent, "", "", + "not exists"); + col = token_print_indent (file, col, indent, " ", "", + "("); + goto finish_ns_small; + + case expr_static: + col = token_print_indent (file, col, indent, "", "", + "static"); + break; + + case expr_null: + col = token_print_indent (file, col, indent, "", "", "null"); + break; + + case expr_variable_reference: + col = token_print_indent (file, indent, indent, "", "", + expr -> data.variable); + break; + + case expr_variable_exists: + col = token_print_indent (file, indent, indent, "", "", + "defined"); + col = token_print_indent (file, col, indent, " ", "", "("); + col = token_print_indent (file, col, indent, "", "", + expr -> data.variable); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + default: + log_fatal ("invalid expression type in print_expression: %d", + expr -> op); + } + return col; +} + +struct binding *find_binding (struct binding_scope *scope, const char *name) +{ + struct binding *bp; + struct binding_scope *s; + + for (s = scope; s; s = s -> outer) { + for (bp = s -> bindings; bp; bp = bp -> next) { + if (!strcasecmp (name, bp -> name)) { + return bp; + } + } + } + return (struct binding *)0; +} + +int free_bindings (struct binding_scope *scope, const char *file, int line) +{ + struct binding *bp, *next; + + for (bp = scope -> bindings; bp; bp = next) { + next = bp -> next; + if (bp -> name) + dfree (bp -> name, file, line); + if (bp -> value) + binding_value_dereference (&bp -> value, file, line); + dfree (bp, file, line); + } + scope -> bindings = (struct binding *)0; + return 1; +} + +int binding_scope_dereference (ptr, file, line) + struct binding_scope **ptr; + const char *file; + int line; +{ + int i; + struct binding_scope *binding_scope; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + binding_scope = *ptr; + *ptr = (struct binding_scope *)0; + --binding_scope -> refcnt; + rc_register (file, line, ptr, + binding_scope, binding_scope -> refcnt, 1, RC_MISC); + if (binding_scope -> refcnt > 0) + return 1; + + if (binding_scope -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (binding_scope); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + free_bindings (binding_scope, file, line); + if (binding_scope -> outer) + binding_scope_dereference (&binding_scope -> outer, MDL); + dfree (binding_scope, file, line); + return 1; +} + +int fundef_dereference (ptr, file, line) + struct fundef **ptr; + const char *file; + int line; +{ + struct fundef *bp = *ptr; + struct string_list *sp, *next; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (!bp) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + bp -> refcnt--; + rc_register (file, line, ptr, bp, bp -> refcnt, 1, RC_MISC); + if (bp -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (bp); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (!bp -> refcnt) { + for (sp = bp -> args; sp; sp = next) { + next = sp -> next; + dfree (sp, file, line); + } + if (bp -> statements) + executable_statement_dereference (&bp -> statements, + file, line); + dfree (bp, file, line); + } + *ptr = (struct fundef *)0; + return 1; +} + +#if defined (NOTYET) /* Post 3.0 final. */ +int data_subexpression_length (int *rv, + struct expression *expr) +{ + int crhs, clhs, llhs, lrhs; + switch (expr -> op) { + case expr_substring: + if (expr -> data.substring.len && + expr -> data.substring.len -> op == expr_const_int) { + (*rv = + (int)expr -> data.substring.len -> data.const_int); + return 1; + } + return 0; + + case expr_packet: + case expr_suffix: + if (expr -> data.suffix.len && + expr -> data.suffix.len -> op == expr_const_int) { + (*rv = + (int)expr -> data.suffix.len -> data.const_int); + return 1; + } + return 0; + + case expr_concat: + clhs = data_subexpression_length (&llhs, + expr -> data.concat [0]); + crhs = data_subexpression_length (&lrhs, + expr -> data.concat [1]); + if (crhs == 0 || clhs == 0) + return 0; + *rv = llhs + lrhs; + return 1; + break; + + case expr_hardware: + return 0; + + case expr_const_data: + *rv = expr -> data.const_data.len; + return 2; + + case expr_reverse: + return data_subexpression_length (rv, + expr -> data.reverse.buffer); + + case expr_leased_address: + case expr_lease_time: + *rv = 4; + return 2; + + case expr_pick_first_value: + clhs = data_subexpression_length (&llhs, + expr -> data.concat [0]); + crhs = data_subexpression_length (&lrhs, + expr -> data.concat [1]); + if (crhs == 0 || clhs == 0) + return 0; + if (llhs > lrhs) + *rv = llhs; + else + *rv = lrhs; + return 1; + + case expr_binary_to_ascii: + case expr_config_option: + case expr_host_decl_name: + case expr_encapsulate: + case expr_filename: + case expr_sname: + case expr_host_lookup: + case expr_option: + case expr_none: + case expr_match: + case expr_check: + case expr_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_const_int: + case expr_exists: + case expr_known: + case expr_dns_transaction: + case expr_static: + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + case expr_not_equal: + case expr_null: + case expr_variable_exists: + case expr_variable_reference: + case expr_arg: + case expr_funcall: + case expr_function: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + return 0; + } + return 0; +} + +int expr_valid_for_context (struct expression *expr, + enum expression_context context) +{ + /* We don't know at parse time what type of value a function may + return, so we can't flag an error on it. */ + if (expr -> op == expr_funcall || + expr -> op == expr_variable_reference) + return 1; + + switch (context) { + case context_any: + return 1; + + case context_boolean: + if (is_boolean_expression (expr)) + return 1; + return 0; + + case context_data: + if (is_data_expression (expr)) + return 1; + return 0; + + case context_numeric: + if (is_numeric_expression (expr)) + return 1; + return 0; + + case context_dns: + if (is_dns_expression (expr)) { + return 1; + } + return 0; + + case context_data_or_numeric: + if (is_numeric_expression (expr) || + is_data_expression (expr)) { + return 1; + } + return 0; + + case context_function: + if (expr -> op == expr_function) + return 1; + return 0; + } + return 0; +} +#endif /* NOTYET */ + +struct binding *create_binding (struct binding_scope **scope, const char *name) +{ + struct binding *binding; + + if (!*scope) { + if (!binding_scope_allocate (scope, MDL)) + return (struct binding *)0; + } + + binding = find_binding (*scope, name); + if (!binding) { + binding = dmalloc (sizeof *binding, MDL); + if (!binding) + return (struct binding *)0; + + memset (binding, 0, sizeof *binding); + binding -> name = dmalloc (strlen (name) + 1, MDL); + if (!binding -> name) { + dfree (binding, MDL); + return (struct binding *)0; + } + strcpy (binding -> name, name); + + binding -> next = (*scope) -> bindings; + (*scope) -> bindings = binding; + } + + return binding; +} + + +int bind_ds_value (struct binding_scope **scope, + const char *name, + struct data_string *value) +{ + struct binding *binding; + + binding = create_binding (scope, name); + if (!binding) + return 0; + + if (binding -> value) + binding_value_dereference (&binding -> value, MDL); + + if (!binding_value_allocate (&binding -> value, MDL)) + return 0; + + data_string_copy (&binding -> value -> value.data, value, MDL); + binding -> value -> type = binding_data; + + return 1; +} + + +int find_bound_string (struct data_string *value, + struct binding_scope *scope, + const char *name) +{ + struct binding *binding; + + binding = find_binding (scope, name); + if (!binding || + !binding -> value || + binding -> value -> type != binding_data) + return 0; + + if (binding -> value -> value.data.terminated) { + data_string_copy (value, &binding -> value -> value.data, MDL); + } else { + buffer_allocate (&value -> buffer, + binding -> value -> value.data.len, + MDL); + if (!value -> buffer) + return 0; + + memcpy (value -> buffer -> data, + binding -> value -> value.data.data, + binding -> value -> value.data.len); + value -> data = value -> buffer -> data; + value -> len = binding -> value -> value.data.len; + } + + return 1; +} + +int unset (struct binding_scope *scope, const char *name) +{ + struct binding *binding; + + binding = find_binding (scope, name); + if (binding) { + if (binding -> value) + binding_value_dereference + (&binding -> value, MDL); + return 1; + } + return 0; +} + +/* vim: set tabstop=8: */ diff --git a/contrib/dhcp-3.0/common/upf.c b/contrib/dhcp-3.0/common/upf.c new file mode 100644 index 0000000000..cb18362b2a --- /dev/null +++ b/contrib/dhcp-3.0/common/upf.c @@ -0,0 +1,371 @@ +/* upf.c + + Ultrix PacketFilter interface code. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: upf.c,v 1.21.2.5 2004/11/24 17:39:16 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" +#if defined (USE_UPF_SEND) || defined (USE_UPF_RECEIVE) +#include +#include + +#include +#include +#include "includes/netinet/ip.h" +#include "includes/netinet/udp.h" +#include "includes/netinet/if_ether.h" + +/* Reinitializes the specified interface after an address change. This + is not required for packet-filter APIs. */ + +#ifdef USE_UPF_SEND +void if_reinitialize_send (info) + struct interface_info *info; +{ +} +#endif + +#ifdef USE_UPF_RECEIVE +void if_reinitialize_receive (info) + struct interface_info *info; +{ +} +#endif + +/* Called by get_interface_list for each interface that's discovered. + Opens a packet filter for each interface and adds it to the select + mask. */ + +int if_register_upf (info) + struct interface_info *info; +{ + int sock; + char filename[50]; + int b; + struct endevp param; + + /* Open a UPF device */ + for (b = 0; 1; b++) { + /* %Audit% Cannot exceed 36 bytes. %2004.06.17,Safe% */ + sprintf(filename, "/dev/pf/pfilt%d", b); + + sock = open (filename, O_RDWR, 0); + if (sock < 0) { + if (errno == EBUSY) { + continue; + } else { + log_fatal ("Can't find free upf: %m"); + } + } else { + break; + } + } + + /* Set the UPF device to point at this interface. */ + if (ioctl (sock, EIOCSETIF, info -> ifp) < 0) + log_fatal ("Can't attach interface %s to upf device %s: %m", + info -> name, filename); + + /* Get the hardware address. */ + if (ioctl (sock, EIOCDEVP, ¶m) < 0) + log_fatal ("Can't get interface %s hardware address: %m", + info -> name); + + /* We only know how to do ethernet. */ + if (param.end_dev_type != ENDT_10MB) + log_fatal ("Invalid device type on network interface %s: %d", + info -> name, param.end_dev_type); + + if (param.end_addr_len != 6) + log_fatal ("Invalid hardware address length on %s: %d", + info -> name, param.end_addr_len); + + info -> hw_address.hlen = 7; + info -> hw_address.hbuf [0] = ARPHRD_ETHER; + memcpy (&info -> hw_address.hbuf [1], param.end_addr, 6); + + return sock; +} +#endif /* USE_UPF_SEND || USE_UPF_RECEIVE */ + +#ifdef USE_UPF_SEND +void if_register_send (info) + struct interface_info *info; +{ + /* If we're using the upf API for sending and receiving, + we don't need to register this interface twice. */ +#ifndef USE_UPF_RECEIVE + info -> wfdesc = if_register_upf (info, interface); +#else + info -> wfdesc = info -> rfdesc; +#endif + if (!quiet_interface_discovery) + log_info ("Sending on UPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_send (info) + struct interface_info *info; +{ +#ifndef USE_UPF_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + if (!quiet_interface_discovery) + log_info ("Disabling output on UPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_UPF_SEND */ + +#ifdef USE_UPF_RECEIVE +/* Packet filter program... + XXX Changes to the filter program may require changes to the constant + offsets used in if_register_send to patch the UPF program! XXX */ + + +void if_register_receive (info) + struct interface_info *info; +{ + int flag = 1; + u_int32_t addr; + struct enfilter pf; + u_int32_t bits; + + /* Open a UPF device and hang it on this interface... */ + info -> rfdesc = if_register_upf (info); + + /* Allow the copyall flag to be set... */ + if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0) + log_fatal ("Can't set ALLOWCOPYALL: %m"); + + /* Clear all the packet filter mode bits first... */ + flag = (ENHOLDSIG | ENBATCH | ENTSTAMP | ENPROMISC | + ENNONEXCL | ENCOPYALL); + if (ioctl (info -> rfdesc, EIOCMBIC, &flag) < 0) + log_fatal ("Can't clear pfilt bits: %m"); + + /* Set the ENBATCH and ENCOPYALL bits... */ + bits = ENBATCH | ENCOPYALL; + if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) + log_fatal ("Can't set ENBATCH|ENCOPYALL: %m"); + + /* Set up the UPF filter program. */ + /* XXX Unlike the BPF filter program, this one won't work if the + XXX IP packet is fragmented or if there are options on the IP + XXX header. */ + pf.enf_Priority = 0; + pf.enf_FilterLen = 0; + + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 6; + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; + pf.enf_Filter [pf.enf_FilterLen++] = htons (ETHERTYPE_IP); + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT; + pf.enf_Filter [pf.enf_FilterLen++] = htons (IPPROTO_UDP); + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 11; + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_AND; + pf.enf_Filter [pf.enf_FilterLen++] = htons (0xFF); + pf.enf_Filter [pf.enf_FilterLen++] = ENF_CAND; + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 18; + pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND; + pf.enf_Filter [pf.enf_FilterLen++] = local_port; + + if (ioctl (info -> rfdesc, EIOCSETF, &pf) < 0) + log_fatal ("Can't install packet filter program: %m"); + if (!quiet_interface_discovery) + log_info ("Listening on UPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + close (info -> rfdesc); + info -> rfdesc = -1; + if (!quiet_interface_discovery) + log_info ("Disabling input on UPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} +#endif /* USE_UPF_RECEIVE */ + +#ifdef USE_UPF_SEND +ssize_t send_packet (interface, packet, raw, len, from, to, hto) + struct interface_info *interface; + struct packet *packet; + struct dhcp_packet *raw; + size_t len; + struct in_addr from; + struct sockaddr_in *to; + struct hardware *hto; +{ + unsigned hbufp = 0, ibufp = 0; + double hw [4]; + double ip [32]; + struct iovec iov [3]; + int result; + int fudge; + + if (!strcmp (interface -> name, "fallback")) + return send_fallback (interface, packet, raw, + len, from, to, hto); + + /* Assemble the headers... */ + assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto); + assemble_udp_ip_header (interface, + (unsigned char *)ip, &ibufp, from.s_addr, + to -> sin_addr.s_addr, to -> sin_port, + (unsigned char *)raw, len); + + /* Fire it off */ + iov [0].iov_base = ((char *)hw); + iov [0].iov_len = hbufp; + iov [1].iov_base = ((char *)ip); + iov [1].iov_len = ibufp; + iov [2].iov_base = (char *)raw; + iov [2].iov_len = len; + + result = writev(interface -> wfdesc, iov, 3); + if (result < 0) + log_error ("send_packet: %m"); + return result; +} +#endif /* USE_UPF_SEND */ + +#ifdef USE_UPF_RECEIVE +ssize_t receive_packet (interface, buf, len, from, hfrom) + struct interface_info *interface; + unsigned char *buf; + size_t len; + struct sockaddr_in *from; + struct hardware *hfrom; +{ + int nread; + int length = 0; + int offset = 0; + unsigned char ibuf [1500 + sizeof (struct enstamp)]; + int bufix = 0; + + length = read (interface -> rfdesc, ibuf, sizeof ibuf); + if (length <= 0) + return length; + + bufix = sizeof (struct enstamp); + /* Decode the physical header... */ + offset = decode_hw_header (interface, ibuf, bufix, hfrom); + + /* If a physical layer checksum failed (dunno of any + physical layer that supports this, but WTH), skip this + packet. */ + if (offset < 0) { + return 0; + } + + bufix += offset; + length -= offset; + + /* Decode the IP and UDP headers... */ + offset = decode_udp_ip_header (interface, ibuf, bufix, + from, length); + + /* If the IP or UDP checksum was bad, skip the packet... */ + if (offset < 0) + return 0; + + bufix += offset; + length -= offset; + + /* Copy out the data in the packet... */ + memcpy (buf, &ibuf [bufix], length); + return length; +} + +int can_unicast_without_arp (ip) + struct interface_info *ip; +{ + return 1; +} + +int can_receive_unicast_unconfigured (ip) + struct interface_info *ip; +{ + return 1; +} + +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ + return 1; +} + +void maybe_setup_fallback () +{ + isc_result_t status; + struct interface_info *fbi = (struct interface_info *)0; + if (setup_fallback (&fbi, MDL)) { + if_register_fallback (fbi); + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); + } +} +#endif diff --git a/contrib/dhcp-3.0/dhcpctl/callback.c b/contrib/dhcp-3.0/dhcpctl/callback.c new file mode 100644 index 0000000000..09b25960ea --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/callback.c @@ -0,0 +1,175 @@ +/* callback.c + + The dhcpctl callback object. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: callback.c,v 1.5.2.2 2004/06/10 17:59:23 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include +#include "dhcpctl.h" + +/* dhcpctl_set_callback + + synchronous, with asynchronous aftereffect + handle is some object upon which some kind of process has been + started - e.g., an open, an update or a refresh. + data is an anonymous pointer containing some information that + the callback will use to figure out what event completed. + return value of 0 means callback was successfully set, a nonzero + status code is returned otherwise. + Upon completion of whatever task is in process, the callback + will be passed the handle to the object, a status code + indicating what happened, and the anonymous pointer passed to */ + +dhcpctl_status dhcpctl_set_callback (dhcpctl_handle h, void *data, + void (*func) (dhcpctl_handle, + dhcpctl_status, void *)) +{ + dhcpctl_callback_object_t *callback; + omapi_object_t *inner; + isc_result_t status; + + callback = dmalloc (sizeof *callback, MDL); + if (!callback) + return ISC_R_NOMEMORY; + + /* Tie the callback object to the innermost object in the chain. */ + for (inner = h; inner -> inner; inner = inner -> inner) + ; + omapi_object_reference (&inner -> inner, + (omapi_object_t *)callback, MDL); + omapi_object_reference ((omapi_object_t **)&callback -> outer, + inner, MDL); + + /* Save the actual handle pointer we were passed for the callback. */ + omapi_object_reference (&callback -> object, h, MDL); + callback -> data = data; + callback -> callback = func; + + return ISC_R_SUCCESS; +} + +/* Callback methods (not meant to be called directly) */ + +isc_result_t dhcpctl_callback_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != dhcpctl_callback_type) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t dhcpctl_callback_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != dhcpctl_callback_type) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t dhcpctl_callback_signal_handler (omapi_object_t *o, + const char *name, va_list ap) +{ + dhcpctl_callback_object_t *p; + isc_result_t waitstatus; + + if (o -> type != dhcpctl_callback_type) + return ISC_R_INVALIDARG; + p = (dhcpctl_callback_object_t *)o; + + /* Not a signal we recognize? */ + if (strcmp (name, "ready")) { + if (p -> inner && p -> inner -> type -> signal_handler) + return (*(p -> inner -> type -> signal_handler)) + (p -> inner, name, ap); + return ISC_R_NOTFOUND; + } + + if (p -> object -> type == dhcpctl_remote_type) { + waitstatus = (((dhcpctl_remote_object_t *) + (p -> object)) -> waitstatus); + } else + waitstatus = ISC_R_SUCCESS; + + /* Do the callback. */ + if (p -> callback) + (*(p -> callback)) (p -> object, waitstatus, p -> data); + + return ISC_R_SUCCESS; +} + +isc_result_t dhcpctl_callback_destroy (omapi_object_t *h, + const char *file, int line) +{ + dhcpctl_callback_object_t *p; + if (h -> type != dhcpctl_callback_type) + return ISC_R_INVALIDARG; + p = (dhcpctl_callback_object_t *)h; + if (p -> handle) + omapi_object_dereference ((omapi_object_t **)&p -> handle, + file, line); + return ISC_R_SUCCESS; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t dhcpctl_callback_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *p) +{ + int i; + + if (p -> type != dhcpctl_callback_type) + return ISC_R_INVALIDARG; + + if (p -> inner && p -> inner -> type -> stuff_values) + return (*(p -> inner -> type -> stuff_values)) (c, id, + p -> inner); + return ISC_R_SUCCESS; +} + diff --git a/contrib/dhcp-3.0/dhcpctl/dhcpctl.3 b/contrib/dhcp-3.0/dhcpctl/dhcpctl.3 new file mode 100644 index 0000000000..352bf3b14c --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/dhcpctl.3 @@ -0,0 +1,488 @@ +.\" -*- nroff -*- +.\" +.\" Project: DHCP +.\" File: dhcpctl.3 +.\" RCSId: $Id: dhcpctl.3,v 1.3.2.5 2004/09/24 21:08:38 dhankins Exp $ +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 2000-2003 by Internet Software Consortium +.\" Copyright (c) 2000 Nominum, Inc. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" Description: dhcpctl man page. +.\" +.\" +.Dd Nov 15, 2000 +.Dt DHCPCTL 3 +.Os DHCP 3 +.ds vT DHCP Programmer's Manual +.\" +.\" +.\" +.Sh NAME +.Nm dhcpctl_initialize +.Nd dhcpctl library initialization. +.\" +.\" +.\" +.Sh SYNOPSIS +.Fd #include +.Ft dhcpctl_status +.Fo dhcpctl_initialize +.Fa void +.Fc +.\" +.Ft dhcpctl_status +.Fo dhcpctl_connect +.Fa "dhcpctl_handle *cxn" +.Fa "const char *host" +.Fa "int port" +.Fa "dhcpctl_handle auth" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_wait_for_completion +.Fa "dhcpctl_handle object" +.Fa "dhcpctl_status *status" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_get_value +.Fa "dhcpctl_data_string *value" +.Fa "dhcpctl_handle object" +.Fa "const char *name" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_get_boolean +.Fa "int *value" +.Fa "dhcpctl_handle object" +.Fa "const char *name" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_set_value +.Fa "dhcpctl_handle object" +.Fa "dhcpctl_data_string value" +.Fa "const char *name" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_set_string_value +.Fa "dhcpctl_handle object" +.Fa "const char *value" +.Fa "const char *name" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_set_boolean_value +.Fa "dhcpctl_handle object" +.Fa "int value" +.Fa "const char *name" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_set_int_value +.Fa "dhcpctl_handle object" +.Fa "int value" +.Fa "const char *name" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_object_update +.Fa "dhcpctl_handle connection" +.Fa "dhcpctl_handle object" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_object_refresh +.Fa "dhcpctl_handle connection" +.Fa "dhcpctl_handle object" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_object_remove +.Fa "dhcpctl_handle connection" +.Fa "dhcpctl_handle object" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_set_callback +.Fa "dhcpctl_handle object" +.Fa "void *data" +.Fa "void (*function) (dhcpctl_handle, dhcpctl_status, void *)" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_new_authenticator +.Fa "dhcpctl_handle *object" +.Fa "const char *name" +.Fa "const char *algorithm" +.Fa "const char *secret" +.Fa "unsigned secret_len" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_new_object +.Fa "dhcpctl_handle *object" +.Fa "dhcpctl_handle connection" +.Fa "const char *object_type" +.Fc +.\" +.\" +.\" +.Ft dhcpctl_status +.Fo dhcpctl_open_object +.Fa "dhcpctl_handle object" +.Fa "dhcpctl_handle connection" +.Fa "int flags" +.Fc +.\" +.\" +.\" +.Ft isc_result_t +.Fo omapi_data_string_new +.Fa dhcpctl_data_string *data +.Fa unsigned int length +.Fa const char *filename, +.Fa int lineno +.Fc +.\" +.\" +.\" +.Ft isc_result_t +.Fo dhcpctl_data_string_dereference +.Fa "dhcpctl_data_string *" +.Fa "const char *" +.Fa "int" +.Fc +.Sh DESCRIPTION +The dhcpctl set of functions provide an API that can be used to communicate +with and manipulate a running ISC DHCP server. All functions return a value of +.Dv isc_result_t . +The return values reflects the result of operations to local data +structures. If an operation fails on the server for any reason, then the error +result will be returned through the +second parameter of the +.Fn dhcpctl_wait_for_completion +call. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_initialize +sets up the data structures the library needs to do its work. This function +must be called once before any other. +.Pp +.Fn dhcpctl_connect +opens a connection to the DHCP server at the given host and port. If an +authenticator has been created for the connection, then it is given as the 4th +argument. On a successful return the address pointed at by the first +argument will have a new connection object assigned to it. +.Pp +For example: +.Bd -literal -offset indent +s = dhcpctl_connect(&cxn, "127.0.0.1", 7911, NULL); +.Ed +.Pp +connects to the DHCP server on the localhost via port 7911 (the standard +OMAPI port). No authentication is used for the connection. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_wait_for_completion +flushes a pending message to the server and waits for the response. The result +of the request as processed on the server is returned via the second +parameter. +.Bd -literal -offset indent +s = dhcpctl_wait_for_completion(cxn, &wv); +if (s != ISC_R_SUCCESS) + local_failure(s); +else if (wv != ISC_R_SUCCESS) + server_failure(wc); +.Ed +.Pp +The call to +.Fn dhcpctl_wait_for_completion +won't return until the remote message processing completes or the connection +to the server is lost. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_get_value +extracts a value of an attribute from the handle. The value can be of any +length and is treated as a sequence of bytes. The handle must have been +created first with +.Fn dhcpctl_new_object +and opened with +.Fn dhcpctl_open_object . +The value is returned via the parameter named +.Dq value . +The last parameter is the name of attribute to retrieve. +.Bd -literal -offset indent +dhcpctl_data_string value = NULL; +dhcpctl_handle lease; +time_t thetime; + +s = dhcpctl_get_value (&value, lease, "ends"); +assert(s == ISC_R_SUCCESS && value->len == sizeof(thetime)); +memcpy(&thetime, value->value, value->len); +.Ed +.\" +.\" +.\" +.Pp +.Fn dhcpctl_get_boolean +extracts a boolean valued attribute from the object handle. +.\" +.\" +.\" +.Pp +The +.Fn dhcpctl_set_value , +.Fn dhcpctl_set_string_value , +.Fn dhcpctl_set_boolean_value , +and +.Fn dhcpctl_set_int_value +functions all set a value on the object handle. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_object_update +function queues a request for +all the changes made to the object handle be be sent to the remote +for processing. The changes made to the atributes on the handle will be +applied to remote object if permitted. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_object_refresh +queues up a request for a fresh copy of all the attribute values to be sent +from the remote to +refresh the values in the local object handle. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_object_remove +queues a request for the removal on the server of the object referenced by the +handle. +.\" +.\" +.\" +.Pp +The +.Fn dhcpctl_set_callback +function sets up a user-defined function to be called when an event completes +on the given object handle. This is needed for asynchronous handling of +events, versus the synchronous handling given by +.Fn dhcpctl_wait_for_completion . +When the function is called the first parameter is the object the event +arrived for, the second is the status of the message that was processed, the +third is the same value as the second parameter given to +.Fn dhcpctl_set_callback . +.\" +.\" +.\" +.Pp +The +.Fn dhcpctl_new_authenticator +creates a new authenticator object to be used for signing the messages +that cross over the network. The +.Dq name , +.Dq algorithm , +and +.Dq secret +values must all match what the server uses and are defined in its +configuration file. The created object is returned through the first parameter +and must be used as the 4th parameter to +.Fn dhcpctl_connect . +Note that the 'secret' value must not be base64 encoded, which is different +from how the value appears in the dhcpd.conf file. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_new_object +creates a local handle for an object on the the server. The +.Dq object_type +parameter is the ascii name of the type of object being accessed. e.g. +.Qq lease . +This function only sets up local data structures, it does not queue any +messages +to be sent to the remote side, +.Fn dhcpctl_open_object +does that. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_open_object +builds and queues the request to the remote side. This function is used with +handle created via +.Fn dhcpctl_new_object . +The flags argument is a bit mask with the following values available for +setting: +.Bl -tag -offset indent -width 20 +.It DHCPCTL_CREATE +if the object does not exist then the remote will create it +.It DHCPCTL_UPDATE +update the object on the remote side using the +attributes already set in the handle. +.It DHCPCTL_EXCL +return and error if the object exists and DHCPCTL_CREATE +was also specified +.El +.\" +.\" +.\" +.Pp +The +.Fn omapi_data_string_new +function allocates a new +.Ft dhcpctl_data_string +object. The data string will be large enough to hold +.Dq length +bytes of data. The +.Dq file +and +.Dq lineno +arguments are the source file location the call is made from, typically by +using the +.Dv __FILE__ +and +.Dv __LINE__ +macros or the +.Dv MDL +macro defined in +. +.\" +.\" +.\" +.Pp +.Fn dhcpctl_data_string_dereference +deallocates a data string created by +.Fn omapi_data_string_new . +The memory for the object won't be freed until the last reference is +released. +.Sh EXAMPLES +.Pp +The following program will connect to the DHCP server running on the local +host and will get the details of the existing lease for IP address +10.0.0.101. It will then print out the time the lease is due to expire. Note +that most error checking has been ommitted for brevity. +.Bd -literal -offset indent +#include +#include +#include +#include +#include + +#include +#include + +int main (int argc, char **argv) { + dhcpctl_data_string ipaddrstring = NULL; + dhcpctl_data_string value = NULL; + dhcpctl_handle connection = NULL; + dhcpctl_handle lease = NULL; + isc_result_t waitstatus; + struct in_addr convaddr; + time_t thetime; + + dhcpctl_initialize (); + + dhcpctl_connect (&connection, "127.0.0.1", + 7911, 0); + + dhcpctl_new_object (&lease, connection, + "lease"); + + memset (&ipaddrstring, 0, sizeof + ipaddrstring); + + inet_pton(AF_INET, "10.0.0.101", + &convaddr); + + omapi_data_string_new (&ipaddrstring, + 4, MDL); + memcpy(ipaddrstring->value, &convaddr.s_addr, 4); + + dhcpctl_set_value (lease, ipaddrstring, + "ip-address"); + + dhcpctl_open_object (lease, connection, 0); + + dhcpctl_wait_for_completion (lease, + &waitstatus); + if (waitstatus != ISC_R_SUCCESS) { + /* server not authoritative */ + exit (0); + } + + dhcpctl_data_string_dereference(&ipaddrstring, + MDL); + + dhcpctl_get_value (&value, lease, "ends"); + + memcpy(&thetime, value->value, value->len); + + dhcpctl_data_string_dereference(&value, MDL); + + fprintf (stdout, "ending time is %s", + ctime(&thetime)); +} +.Ed +.Sh SEE ALSO +omapi(3), omshell(3), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5). +.Sh AUTHOR +.Em dhcpctl +was written by Ted Lemon of Nominum, Inc. +This preliminary documentation was written by James Brister of Nominum, Inc. diff --git a/contrib/dhcp-3.0/dhcpctl/dhcpctl.c b/contrib/dhcp-3.0/dhcpctl/dhcpctl.c new file mode 100644 index 0000000000..b596b751b3 --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/dhcpctl.c @@ -0,0 +1,595 @@ +/* dhcpctl.c + + Subroutines providing general support for objects. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: dhcpctl.c,v 1.22.2.7 2004/06/10 17:59:24 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include +#include "dhcpctl.h" + +omapi_object_type_t *dhcpctl_callback_type; +omapi_object_type_t *dhcpctl_remote_type; + +/* dhcpctl_initialize () + + Must be called before any other dhcpctl function. */ + +dhcpctl_status dhcpctl_initialize () +{ + isc_result_t status; + + status = omapi_init(); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&dhcpctl_callback_type, + "dhcpctl-callback", + dhcpctl_callback_set_value, + dhcpctl_callback_get_value, + dhcpctl_callback_destroy, + dhcpctl_callback_signal_handler, + dhcpctl_callback_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof + (dhcpctl_callback_object_t), 0, + RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&dhcpctl_remote_type, + "dhcpctl-remote", + dhcpctl_remote_set_value, + dhcpctl_remote_get_value, + dhcpctl_remote_destroy, + dhcpctl_remote_signal_handler, + dhcpctl_remote_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof (dhcpctl_remote_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + return ISC_R_SUCCESS; +} + +/* dhcpctl_connect + + synchronous + returns nonzero status code if it didn't connect, zero otherwise + stores connection handle through connection, which can be used + for subsequent access to the specified server. + server_name is the name of the server, and port is the TCP + port on which it is listening. + authinfo is the handle to an object containing authentication + information. */ + +dhcpctl_status dhcpctl_connect (dhcpctl_handle *connection, + const char *server_name, int port, + dhcpctl_handle authinfo) +{ + isc_result_t status; + dhcpctl_status waitstatus; + + status = omapi_generic_new (connection, MDL); + if (status != ISC_R_SUCCESS) { + return status; + } + + status = omapi_protocol_connect (*connection, server_name, + (unsigned)port, authinfo); + if (status == ISC_R_SUCCESS) + return status; + if (status != ISC_R_INCOMPLETE) { + omapi_object_dereference (connection, MDL); + return status; + } + + status = omapi_wait_for_completion (*connection, 0); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (connection, MDL); + return status; + } + + return status; +} + +/* dhcpctl_wait_for_completion + + synchronous + returns zero if the callback completes, a nonzero status if + there was some problem relating to the wait operation. The + status of the queued request will be stored through s, and + will also be either zero for success or nonzero for some kind + of failure. Never returns until completion or until the + connection to the server is lost. This performs the same + function as dhcpctl_set_callback and the subsequent callback, + for programs that want to do inline execution instead of using + callbacks. */ + +dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle h, + dhcpctl_status *s) +{ + isc_result_t status; + status = omapi_wait_for_completion (h, 0); + if (status != ISC_R_SUCCESS) + return status; + if (h -> type == dhcpctl_remote_type) + *s = ((dhcpctl_remote_object_t *)h) -> waitstatus; + return ISC_R_SUCCESS; +} + +/* dhcpctl_get_value + + synchronous + returns zero if the call succeeded, a nonzero status code if + it didn't. + result is the address of an empty data string (initialized + with bzero or cleared with data_string_forget). On + successful completion, the addressed data string will contain + the value that was fetched. + dhcpctl_handle refers to some dhcpctl item + value_name refers to some value related to that item - e.g., + for a handle associated with a completed host lookup, value + could be one of "hardware-address", "dhcp-client-identifier", + "known" or "client-hostname". */ + +dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *result, + dhcpctl_handle h, const char *value_name) +{ + isc_result_t status; + omapi_value_t *tv = (omapi_value_t *)0; + omapi_data_string_t *value = (omapi_data_string_t *)0; + unsigned len; + int ip; + + status = omapi_get_value_str (h, (omapi_object_t *)0, value_name, &tv); + if (status != ISC_R_SUCCESS) + return status; + + switch (tv -> value -> type) { + case omapi_datatype_int: + len = sizeof (int); + break; + + case omapi_datatype_string: + case omapi_datatype_data: + len = tv -> value -> u.buffer.len; + break; + + case omapi_datatype_object: + len = sizeof (omapi_handle_t); + break; + + default: + omapi_typed_data_dereference (&tv -> value, MDL); + return ISC_R_UNEXPECTED; + } + + status = omapi_data_string_new (result, len, MDL); + if (status != ISC_R_SUCCESS) { + omapi_typed_data_dereference (&tv -> value, MDL); + return status; + } + + switch (tv -> value -> type) { + case omapi_datatype_int: + ip = htonl (tv -> value -> u.integer); + memcpy ((*result) -> value, &ip, sizeof ip); + break; + + case omapi_datatype_string: + case omapi_datatype_data: + memcpy ((*result) -> value, + tv -> value -> u.buffer.value, + tv -> value -> u.buffer.len); + break; + + case omapi_datatype_object: + ip = htonl (tv -> value -> u.object -> handle); + memcpy ((*result) -> value, &ip, sizeof ip); + break; + } + + omapi_value_dereference (&tv, MDL); + return ISC_R_SUCCESS; +} + +/* dhcpctl_get_boolean + + like dhcpctl_get_value, but more convenient for boolean + values, since no data_string needs to be dealt with. */ + +dhcpctl_status dhcpctl_get_boolean (int *result, + dhcpctl_handle h, const char *value_name) +{ + isc_result_t status; + dhcpctl_data_string data = (dhcpctl_data_string)0; + int rv; + + status = dhcpctl_get_value (&data, h, value_name); + if (status != ISC_R_SUCCESS) + return status; + if (data -> len != sizeof rv) { + omapi_data_string_dereference (&data, MDL); + return ISC_R_UNEXPECTED; + } + memcpy (&rv, data -> value, sizeof rv); + *result = ntohl (rv); + return ISC_R_SUCCESS; +} + +/* dhcpctl_set_value + + Sets a value on an object referred to by a dhcpctl_handle. + The opposite of dhcpctl_get_value. Does not update the + server - just sets the value on the handle. */ + +dhcpctl_status dhcpctl_set_value (dhcpctl_handle h, dhcpctl_data_string value, + const char *value_name) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *name = (omapi_data_string_t *)0; + int len; + + status = omapi_data_string_new (&name, strlen (value_name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (name -> value, value_name, strlen (value_name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_data, + value -> len); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&name, MDL); + return status; + } + memcpy (tv -> u.buffer.value, value -> value, value -> len); + + status = omapi_set_value (h, (omapi_object_t *)0, name, tv); + omapi_data_string_dereference (&name, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +/* dhcpctl_set_string_value + + Sets a NUL-terminated ASCII value on an object referred to by + a dhcpctl_handle. like dhcpctl_set_value, but saves the + trouble of creating a data_string for a NUL-terminated string. + Does not update the server - just sets the value on the handle. */ + +dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle h, const char *value, + const char *value_name) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *name = (omapi_data_string_t *)0; + int len; + + status = omapi_data_string_new (&name, strlen (value_name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (name -> value, value_name, strlen (value_name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&name, MDL); + return status; + } + + status = omapi_set_value (h, (omapi_object_t *)0, name, tv); + omapi_data_string_dereference (&name, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +/* dhcpctl_set_buffer_value + + Sets a value on an object referred to by a dhcpctl_handle. like + dhcpctl_set_value, but saves the trouble of creating a data_string + for string for which we have a buffer and length. Does not update + the server - just sets the value on the handle. */ + +dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle h, + const char *value, unsigned len, + const char *value_name) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *name = (omapi_data_string_t *)0; + unsigned ll; + + ll = strlen (value_name); + status = omapi_data_string_new (&name, ll, MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (name -> value, value_name, ll); + + status = omapi_typed_data_new (MDL, &tv, + omapi_datatype_data, len, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&name, MDL); + return status; + } + memcpy (tv -> u.buffer.value, value, len); + + status = omapi_set_value (h, (omapi_object_t *)0, name, tv); + omapi_data_string_dereference (&name, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +/* dhcpctl_set_null_value + + Sets a null value on an object referred to by a dhcpctl_handle. */ + +dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle h, + const char *value_name) +{ + isc_result_t status; + omapi_data_string_t *name = (omapi_data_string_t *)0; + unsigned ll; + + ll = strlen (value_name); + status = omapi_data_string_new (&name, ll, MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (name -> value, value_name, ll); + + status = omapi_set_value (h, (omapi_object_t *)0, name, + (omapi_typed_data_t *)0); + omapi_data_string_dereference (&name, MDL); + return status; +} + +/* dhcpctl_set_boolean_value + + Sets a boolean value on an object - like dhcpctl_set_value, + only more convenient for booleans. */ + +dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle h, int value, + const char *value_name) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *name = (omapi_data_string_t *)0; + int len; + + status = omapi_data_string_new (&name, strlen (value_name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (name -> value, value_name, strlen (value_name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&name, MDL); + return status; + } + + status = omapi_set_value (h, (omapi_object_t *)0, name, tv); + omapi_data_string_dereference (&name, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +/* dhcpctl_set_int_value + + Sets a boolean value on an object - like dhcpctl_set_value, + only more convenient for booleans. */ + +dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle h, int value, + const char *value_name) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *name = (omapi_data_string_t *)0; + int len; + + status = omapi_data_string_new (&name, strlen (value_name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (name -> value, value_name, strlen (value_name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&name, MDL); + return status; + } + + status = omapi_set_value (h, (omapi_object_t *)0, name, tv); + omapi_data_string_dereference (&name, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +/* dhcpctl_object_update + + Queues an update on the object referenced by the handle (there + can't be any other work in progress on the handle). An + update means local parameters will be sent to the server. */ + +dhcpctl_status dhcpctl_object_update (dhcpctl_handle connection, + dhcpctl_handle h) +{ + isc_result_t status; + omapi_object_t *message = (omapi_object_t *)0; + dhcpctl_remote_object_t *ro; + + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + ro = (dhcpctl_remote_object_t *)h; + + status = omapi_message_new (&message, MDL); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + status = omapi_set_int_value (message, (omapi_object_t *)0, + "op", OMAPI_OP_UPDATE); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + status = omapi_set_object_value (message, (omapi_object_t *)0, + "object", h); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + status = omapi_set_int_value (message, (omapi_object_t *)0, "handle", + (int)(ro -> remote_handle)); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + omapi_message_register (message); + status = omapi_protocol_send_message (connection -> outer, + (omapi_object_t *)0, + message, (omapi_object_t *)0); + omapi_object_dereference (&message, MDL); + return status; +} + +/* Requests a refresh on the object referenced by the handle (there + can't be any other work in progress on the handle). A + refresh means local parameters are updated from the server. */ + +dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle connection, + dhcpctl_handle h) +{ + isc_result_t status; + omapi_object_t *message = (omapi_object_t *)0; + dhcpctl_remote_object_t *ro; + + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + ro = (dhcpctl_remote_object_t *)h; + + status = omapi_message_new (&message, MDL); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + status = omapi_set_int_value (message, (omapi_object_t *)0, + "op", OMAPI_OP_REFRESH); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + status = omapi_set_int_value (message, (omapi_object_t *)0, + "handle", (int)(ro -> remote_handle)); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + omapi_message_register (message); + status = omapi_protocol_send_message (connection -> outer, + (omapi_object_t *)0, + message, (omapi_object_t *)0); + + /* We don't want to send the contents of the object down the + wire, but we do need to reference it so that we know what + to do with the update. */ + status = omapi_set_object_value (message, (omapi_object_t *)0, + "object", h); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + omapi_object_dereference (&message, MDL); + return status; +} + +/* Requests the removal of the object referenced by the handle (there + can't be any other work in progress on the handle). A + removal means that all searchable references to the object on the + server are deleted. */ + +dhcpctl_status dhcpctl_object_remove (dhcpctl_handle connection, + dhcpctl_handle h) +{ + isc_result_t status; + omapi_object_t *message = (omapi_object_t *)0; + dhcpctl_remote_object_t *ro; + + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + ro = (dhcpctl_remote_object_t *)h; + + status = omapi_message_new (&message, MDL); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + status = omapi_set_int_value (message, (omapi_object_t *)0, + "op", OMAPI_OP_DELETE); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + status = omapi_set_int_value (message, (omapi_object_t *)0, "handle", + (int)(ro -> remote_handle)); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + status = omapi_set_object_value (message, (omapi_object_t *)0, + "notify-object", h); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + omapi_message_register (message); + status = omapi_protocol_send_message (connection -> outer, + (omapi_object_t *)0, + message, (omapi_object_t *)0); + omapi_object_dereference (&message, MDL); + return status; +} + +isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *vp, + const char *file, int line) +{ + return omapi_data_string_dereference (vp, file, line); +} diff --git a/contrib/dhcp-3.0/dhcpctl/dhcpctl.h b/contrib/dhcp-3.0/dhcpctl/dhcpctl.h new file mode 100644 index 0000000000..00484aca73 --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/dhcpctl.h @@ -0,0 +1,125 @@ +/* $Id: dhcpctl.h,v 1.13.2.4 2004/06/10 17:59:24 dhankins Exp $ + + Subroutines providing general support for objects. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef _DHCPCTL_H_ +#define _DHCPCTL_H_ + +#include + +typedef isc_result_t dhcpctl_status; +typedef omapi_object_t *dhcpctl_handle; +typedef omapi_data_string_t *dhcpctl_data_string; + +#define dhcpctl_null_handle ((dhcpctl_handle) 0) + +#define DHCPCTL_CREATE OMAPI_CREATE +#define DHCPCTL_UPDATE OMAPI_UPDATE +#define DHCPCTL_EXCL OMAPI_EXCL + +typedef struct { + OMAPI_OBJECT_PREAMBLE; + omapi_object_t *object; + void *data; + void (*callback) (dhcpctl_handle, dhcpctl_status, void *); +} dhcpctl_callback_object_t; + +typedef struct { + OMAPI_OBJECT_PREAMBLE; + omapi_typed_data_t *rtype; + isc_result_t waitstatus; + omapi_typed_data_t *message; + omapi_handle_t remote_handle; +} dhcpctl_remote_object_t; + +extern omapi_object_type_t *dhcpctl_callback_type; +extern omapi_object_type_t *dhcpctl_remote_type; + +dhcpctl_status dhcpctl_initialize (void); +dhcpctl_status dhcpctl_connect (dhcpctl_handle *, + const char *, int, dhcpctl_handle); +dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle, dhcpctl_status *); +dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *, + dhcpctl_handle, const char *); +dhcpctl_status dhcpctl_get_boolean (int *, dhcpctl_handle, const char *); +dhcpctl_status dhcpctl_set_value (dhcpctl_handle, + dhcpctl_data_string, const char *); +dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle, const char *, + const char *); +dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle, + const char *, unsigned, const char *); +dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle, const char *); +dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle, int, const char *); +dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle, int, const char *); +dhcpctl_status dhcpctl_object_update (dhcpctl_handle, dhcpctl_handle); +dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle, dhcpctl_handle); +dhcpctl_status dhcpctl_object_remove (dhcpctl_handle, dhcpctl_handle); + +dhcpctl_status dhcpctl_set_callback (dhcpctl_handle, void *, + void (*) (dhcpctl_handle, + dhcpctl_status, void *)); +isc_result_t dhcpctl_callback_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcpctl_callback_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcpctl_callback_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcpctl_callback_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcpctl_callback_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); + +dhcpctl_status dhcpctl_new_authenticator (dhcpctl_handle *, + const char *, const char *, + const unsigned char *, unsigned); + +dhcpctl_status dhcpctl_open_object (dhcpctl_handle, dhcpctl_handle, int); +dhcpctl_status dhcpctl_new_object (dhcpctl_handle *, + dhcpctl_handle, const char *); +isc_result_t dhcpctl_remote_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcpctl_remote_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcpctl_remote_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcpctl_remote_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcpctl_remote_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *, + const char *, int); +#endif /* _DHCPCTL_H_ */ diff --git a/contrib/dhcp-3.0/dhcpctl/omshell.1 b/contrib/dhcp-3.0/dhcpctl/omshell.1 new file mode 100644 index 0000000000..f0ec0e46bd --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/omshell.1 @@ -0,0 +1,334 @@ +.\" $Id: omshell.1,v 1.1.2.5 2004/06/10 17:59:24 dhankins Exp $ +.\" +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 2001-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" +.\" http://www.isc.org/ +.\" +.\" This software has been written for Internet Systems Consortium +.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. +.\" To learn more about Internet Systems Consortium, see +.\" ``http://www.isc.org/''. To learn more about Vixie Enterprises, +.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see +.\" ``http://www.nominum.com''. +.TH omshell 1 +.SH NAME +omshell - OMAPI Command Shell +.SH SYNOPSIS +.B omshell +.SH DESCRIPTION +The OMAPI Command Shell, omshell, provides an interactive way to connect to, +query, and possibly change, the ISC DHCP Server's state via OMAPI, the Object +Management API. By using OMAPI and omshell, you do not have to stop, make +changes, and then restart the DHCP server, but can make the changes +while the server is running. Omshell provides a way of accessing +OMAPI. +.PP +OMAPI is simply a communications mechanism that allows you to +manipulate objects. In order to actually \fIuse\fR omshell, you +.I must +understand what objects are available and how to use them. +Documentation for OMAPI objects can be found in the documentation for +the server that provides them - for example, in the \fBdhcpd(1)\fR +manual page and the \fBdhclient(1)\fR manual page. +.SH CONTRIBUTIONS +.PP +This software is free software. At various times its development has +been underwritten by various organizations, including the ISC and +Vixie Enterprises. The development of 3.0 has been funded almost +entirely by Nominum, Inc. +.PP +At this point development is being shepherded by Ted Lemon, and hosted +by the ISC, but the future of this project depends on you. If you +have features you want, please consider implementing them. +.SH LOCAL AND REMOTE OBJECTS +.PP +Throughout this document, there are references to local and remote objects. +Local objects are ones created in omshell with the \fBnew\fR command. Remote +objects are ones on the server: leases, hosts, and groups that the DHCP +server knows about. Local and remote objects are associated together to +enable viewing and modification of object attributes. Also, new remote +objects can be created to match local objects. +.SH OPENING A CONNECTION +.PP +omshell is started from the command line. Once omshell is started, there are +several commands that can be issued: +.PP +.B server \fIaddress\fR +.RS 0.5i +where address is the IP address of the DHCP server to connect to. If this is +not specified, the default server is 127.0.0.1 (localhost). +.RE +.PP +.B port \fInumber\fR +.RS 0.5i +where number is the port that OMAPI listens on. By default, this is 7911. +.RE +.PP +.B key \fIname secret\fR +.RS 0.5i +This specifies the TSIG key to use to authenticate the OMAPI transactions. +\fIname\fR is the name of a key defined in \fIdhcpd.conf\fR with the +\fBomapi-key\fR statement. The \fIsecret\fR is the secret generated from +\fBdnssec-keygen\fR or another key generation program. +.RE +.PP +.B connect +.RS 0.5i +This starts the OMAPI connection to the server as specified by the \fIserver\fR +statement. +.SH CREATING LOCAL OBJECTS +.PP +Any object defined in OMAPI can be created, queried, and/or modified. The +object types available to OMAPI are defined in \fBdhcpd(8)\fR and +\fBdhclient\fR. When using omshell, objects are first defined locally, +manipulated as desired, and then associated with an object on the server. +Only one object can be manipulated at a time. To create a local object, use +.PP +.B new \fIobject-type\fR +.RS 0.5i +\fIobject-type\fR is one of group, host, or lease. +.RE +.PP +At this point, you now have an object that you can set properties on. For +example, if a new lease object was created with \fInew lease\fR, any of a +lease's attributes can be set as follows: +.PP +.B set \fIattribute-name = value\fR +.RS 0.5i +\fBAttribute names are defined in \fBdhcpd(8)\fR and \fBdhclient(8)\fR. +Values should be quoted if they are strings. So, to set a lease's IP address, +you would do the following: +\fB set ip-address = 192.168.4.50\fR +.SH ASSOCIATING LOCAL AND REMOTE OBJECTS +.PP +At this point, you can query the server for information about this lease, by +.PP +.B open +.PP +Now, the local lease object you created and set the IP address for is associated +with the corresponding lease object on the DHCP server. All of the lease +attributes from the DHCP server are now also the attributes on the local +object, and will be shown in omshell. +.SH VIEWING A REMOTE OBJECT +.PP +To query a lease of address 192.168.4.50, and find out its attributes, after +connecting to the server, take the following steps: +.PP +.B new "lease" +.PP +This creates a new local lease object. +.PP +.B set ip-address = 192.168.4.50 +.PP +This sets the \fIlocal\fR object's IP address to be 192.168.4.50 +.PP +.B open +.PP +Now, if a lease with that IP address exists, you will see all the information +the DHCP server has about that particular lease. Any data that isn't readily +printable text will show up in colon-separated hexadecimal values. In this +example, output back from the server for the entire transaction might look +like this: +.nf +.sp 1 +> new "lease" +obj: lease +> set ip-address = 192.168.4.50 +obj: lease +ip-address = c0:a8:04:32 +> open +obj: lease +ip-address = c0:a8:04:32 +state = 00:00:00:02 +dhcp-client-identifier = 01:00:10:a4:b2:36:2c +client-hostname = "wendelina" +subnet = 00:00:00:06 +pool = 00:00:00:07 +hardware-address = 00:10:a4:b2:36:2c +hardware-type = 00:00:00:01 +ends = dc:d9:0d:3b +starts = 5c:9f:04:3b +tstp = 00:00:00:00 +tsfp = 00:00:00:00 +cltt = 00:00:00:00 +.fi +.PP +As you can see here, the IP address is represented in hexadecimal, as are the +starting and ending times of the lease. +.SH MODIFYING A REMOTE OBJECT +.PP +Attributes of remote objects are updated by using the \fBset\fR command as +before, and then issuing an \fBupdate\fR command. The \fBset\fR command sets +the attributes on the current local object, and the \fBupdate\fR command +pushes those changes out to the server. +.PP +Continuing with the previous example, if a \fBset client-hostname = +"something-else"\fR was issued, followed by an \fBupdate\fR command, the +output would look about like this: +.nf +.sp 1 +> set client-hostname = "something-else" +obj: lease +ip-address = c0:a8:04:32 +state = 00:00:00:02 +dhcp-client-identifier = 01:00:10:a4:b2:36:2c +client-hostname = "something-else" +subnet = 00:00:00:06 +pool = 00:00:00:07 +hardware-address = 00:10:a4:b2:36:2c +hardware-type = 00:00:00:01 +ends = dc:d9:0d:3b +starts = 5c:9f:04:3b +tstp = 00:00:00:00 +tsfp = 00:00:00:00 +cltt = 00:00:00:00 +> update +obj: lease +ip-address = c0:a8:04:32 +state = 00:00:00:02 +dhcp-client-identifier = 01:00:10:a4:b2:36:2c +client-hostname = "something-else" +subnet = 00:00:00:06 +pool = 00:00:00:07 +hardware-address = 00:10:a4:b2:36:2c +hardware-type = 00:00:00:01 +ends = dc:d9:0d:3b +starts = 5c:9f:04:3b +tstp = 00:00:00:00 +tsfp = 00:00:00:00 +cltt = 00:00:00:00 +.fi +.SH NEW REMOTE OBJECTS +.PP +New remote objects are created much in the same way that existing server +objects are modified. Create a local object using \fBnew\fR, set the +attributes as you'd wish them to be, and then create the remote object with +the same properties by using +.PP +.B create +.PP +Now a new object exists on the DHCP server which matches the properties that +you gave your local object. Objects created via OMAPI are saved into the +dhcpd.leases file. +.PP +For example, if a new host with the IP address of 192.168.4.40 needs to be +created it would be done as follows: +.nf +.sp 1 +> new host +obj: host +> set name = "some-host" +obj: host +name = "some-host" +> set hardware-address = 00:80:c7:84:b1:94 +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +> set hardware-type = 1 +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +hardware-type = 1 +> set ip-address = 192.168.4.40 +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +hardware-type = 1 +ip-address = c0:a8:04:28 +> create +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +hardware-type = 00:00:00:01 +ip-address = c0:a8:04:28 +> +.fi +.PP +Your dhcpd.leases file would then have an entry like this in it: +.nf +.sp 1 +host some-host { + dynamic; + hardware ethernet 00:80:c7:84:b1:94; + fixed-address 192.168.4.40; +} +.fi +.PP +The \fIdynamic;\fR line is to denote that this host entry did not come from +dhcpd.conf, but was created dynamically via OMAPI. +.SH RESETTING ATTRIBUTES +.PP +If you want to remove an attribute from an object, you can do this with the +\fBunset\fR command. Once you have unset an attribute, you must use the +\fBupdate\fR command to update the remote object. So, if the host "some-host" +from the previous example will not have a static IP address anymore, the +commands in omshell would look like this: +.nf +.sp 1 +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +hardware-type = 00:00:00:01 +ip-address = c0:a8:04:28 +> unset ip-address +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +hardware-type = 00:00:00:01 +ip-address = +> +.fi +.SH REFRESHING OBJECTS +.PP +A local object may be refreshed with the current remote object properties +using the \fBrefresh\fR command. This is useful for object that change +periodically, like leases, to see if they have been updated. This isn't +particularly useful for hosts. +.SH DELETING OBJECTS +.PP +Any remote object that can be created can also be destroyed. This is done by +creating a new local object, setting attributes, associating the local and +remote object using \fBopen\fI, and then using the \fBremove\fR command. +If the host "some-host" from before was created in error, this could be +corrected as follows: +.nf +.sp 1 +obj: host +name = "some-host" +hardware-address = 00:80:c7:84:b1:94 +hardware-type = 00:00:00:01 +ip-address = c0:a8:04:28 +> remove +obj: +> +.fi +.SH HELP +.PP +The \fBhelp\fR command will print out all of the commands available in +omshell, with some syntax pointers. +.SH SEE ALSO +dhcpctl(3), omapi(3), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5). +.SH AUTHOR +.B omshell +was written by Ted Lemon of Nominum, Inc. Information about Nominum +and support contracts for DHCP and BIND can be found at +.B http://www.nominum.com. This preliminary documentation was +written by Wendy Verschoor of Nominum, Inc., while she was testing +omshell. diff --git a/contrib/dhcp-3.0/dhcpctl/omshell.c b/contrib/dhcp-3.0/dhcpctl/omshell.c new file mode 100644 index 0000000000..aabbe0419f --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/omshell.c @@ -0,0 +1,725 @@ +/* omshell.c + + Examine and modify omapi objects. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: omshell.c,v 1.7.2.17 2004/09/30 23:11:50 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "dhcpctl.h" +#include "dhcpd.h" + +/* Fixups */ +isc_result_t find_class (struct class **c, const char *n, const char *f, int l) +{ + return 0; +} +int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag) +{ + return 0; +} +void dhcp (struct packet *packet) { } +void bootp (struct packet *packet) { } +int check_collection (struct packet *p, struct lease *l, struct collection *c) +{ + return 0; +} +void classify (struct packet *packet, struct class *class) { } + +static void usage (char *s) { + fprintf (stderr, "Usage: %s\n", s); + exit (1); +} + +static void check (isc_result_t status, const char *func) { + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "%s: %s\n", func, isc_result_totext (status)); + exit (1); + } +} + +int main (int argc, char **argv, char **envp) +{ + isc_result_t status, waitstatus; + dhcpctl_handle connection; + dhcpctl_handle authenticator; + dhcpctl_handle oh; + dhcpctl_data_string cid, ip_addr; + dhcpctl_data_string result, groupname, identifier; + struct data_string secret; + const char *name = 0, *algorithm = "hmac-md5"; + int i, j; + int port = 7911; + const char *server = "127.0.0.1"; + struct parse *cfile; + enum dhcp_token token; + const char *val; + char *s; + char buf[1024]; + char s1[1024]; + int connected = 0; + + for (i = 1; i < argc; i++) { + usage(argv[0]); + } + + /* Initially, log errors to stderr as well as to syslogd. */ +#ifdef SYSLOG_4_2 + openlog ("omshell", LOG_NDELAY); + log_priority = DHCPD_LOG_FACILITY; +#else + openlog ("omshell", LOG_NDELAY, DHCPD_LOG_FACILITY); +#endif + status = dhcpctl_initialize (); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "dhcpctl_initialize: %s\n", + isc_result_totext (status)); + exit (1); + } + + memset (&oh, 0, sizeof oh); + + do { + if (!connected) { + } else if (oh == NULL) { + printf ("obj: \n"); + } else { + dhcpctl_remote_object_t *r = (dhcpctl_remote_object_t *)oh; + omapi_generic_object_t *g = + (omapi_generic_object_t *)(r -> inner); + + printf ("obj: "); + + if (r -> rtype -> type != omapi_datatype_string) { + printf ("?\n"); + } else { + printf ("%.*s\n", + (int)(r -> rtype -> u . buffer . len), + r -> rtype -> u . buffer . value); + } + + for (i = 0; i < g -> nvalues; i++) { + omapi_value_t *v = g -> values [i]; + + if (!g -> values [i]) + continue; + + printf ("%.*s = ", (int)v -> name -> len, + v -> name -> value); + + if (!v -> value) { + printf ("\n"); + continue; + } + switch (v -> value -> type) { + case omapi_datatype_int: + printf ("%d\n", + v -> value -> u . integer); + break; + + case omapi_datatype_string: + printf ("\"%.*s\"\n", + (int) v -> value -> u.buffer.len, + v -> value -> u.buffer.value); + break; + + case omapi_datatype_data: + printf ("%s\n", + print_hex_1 (v -> value -> u.buffer.len, + v -> value -> u.buffer.value, + 60)); + break; + + case omapi_datatype_object: + printf ("\n"); + break; + } + } + } + + fputs ("> ", stdout); + fflush (stdout); + if (fgets (buf, sizeof(buf), stdin) == NULL) + break; + + status = new_parse (&cfile, 0, buf, strlen(buf), "", 1); + check(status, "new_parse()"); + + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + default: + parse_warn (cfile, "unknown token: %s", val); + skip_to_semi (cfile); + break; + + case END_OF_FILE: + case EOL: + break; + + case TOKEN_HELP: + case '?': + printf ("Commands:\n"); + printf (" port \n"); + printf (" server \n"); + printf (" key \n"); + printf (" connect\n"); + printf (" new \n"); + printf (" set = \n"); + printf (" create\n"); + printf (" open\n"); + printf (" update\n"); + printf (" unset \n"); + printf (" refresh\n"); + printf (" remove\n"); + skip_to_semi (cfile); + break; + + case PORT: + token = next_token (&val, (unsigned *)0, cfile); + if (is_identifier (token)) { + struct servent *se; + se = getservbyname (val, "tcp"); + if (se) + port = ntohs (se -> s_port); + else { + printf ("unknown service name: %s\n", val); + break; + } + } else if (token == NUMBER) { + port = atoi (val); + } else { + skip_to_semi (cfile); + printf ("usage: port \n"); + break; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: port \n"); + skip_to_semi (cfile); + break; + } + break; + + case SERVER: + token = next_token (&val, (unsigned *)0, cfile); + if (token == NUMBER) { + int alen = (sizeof buf) - 1; + int len; + + s = &buf [0]; + len = strlen (val); + if (len + 1 > alen) { + baddq: + printf ("usage: server \n"); + skip_to_semi (cfile); + break; + } strcpy (buf, val); + s += len; + token = next_token (&val, (unsigned *)0, cfile); + if (token != DOT) + goto baddq; + *s++ = '.'; + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto baddq; + len = strlen (val); + if (len + 1 > alen) + goto baddq; + strcpy (s, val); + s += len; + token = next_token (&val, (unsigned *)0, cfile); + if (token != DOT) + goto baddq; + *s++ = '.'; + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto baddq; + len = strlen (val); + if (len + 1 > alen) + goto baddq; + strcpy (s, val); + s += len; + token = next_token (&val, (unsigned *)0, cfile); + if (token != DOT) + goto baddq; + *s++ = '.'; + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto baddq; + len = strlen (val); + if (len + 1 > alen) + goto baddq; + strcpy (s, val); + val = &buf [0]; + } else if (is_identifier (token)) { + /* Use val directly. */ + } else { + printf ("usage: server \n"); + skip_to_semi (cfile); + break; + } + + s = dmalloc (strlen (val) + 1, MDL); + if (!server) { + printf ("no memory to store server name.\n"); + skip_to_semi (cfile); + break; + } + strcpy (s, val); + server = s; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: server \n"); + skip_to_semi (cfile); + break; + } + break; + + case KEY: + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + printf ("usage: key \n"); + skip_to_semi (cfile); + break; + } + s = dmalloc (strlen (val) + 1, MDL); + if (!s) { + printf ("no memory for key name.\n"); + skip_to_semi (cfile); + break; + } + strcpy (s, val); + name = s; + memset (&secret, 0, sizeof secret); + if (!parse_base64 (&secret, cfile)) { + skip_to_semi (cfile); + break; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: key \n"); + skip_to_semi (cfile); + break; + } + break; + + case CONNECT: + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: connect\n"); + skip_to_semi (cfile); + break; + } + + authenticator = dhcpctl_null_handle; + + if (name) { + status = dhcpctl_new_authenticator (&authenticator, + name, algorithm, + secret.data, + secret.len); + + if (status != ISC_R_SUCCESS) { + fprintf (stderr, + "Cannot create authenticator: %s\n", + isc_result_totext (status)); + break; + } + } + + memset (&connection, 0, sizeof connection); + status = dhcpctl_connect (&connection, + server, port, authenticator); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "dhcpctl_connect: %s\n", + isc_result_totext (status)); + break; + } + connected = 1; + break; + + case TOKEN_NEW: + token = next_token (&val, (unsigned *)0, cfile); + if ((!is_identifier (token) && token != STRING)) { + printf ("usage: new \n"); + break; + } + + if (oh) { + printf ("an object is already open.\n"); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + skip_to_semi (cfile); + break; + } + + status = dhcpctl_new_object (&oh, connection, val); + if (status != ISC_R_SUCCESS) { + printf ("can't create object: %s\n", + isc_result_totext (status)); + break; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: new \n"); + skip_to_semi (cfile); + break; + } + break; + + case TOKEN_CLOSE: + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: close\n"); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + skip_to_semi (cfile); + break; + } + + if (!oh) { + printf ("not open.\n"); + skip_to_semi (cfile); + break; + } + omapi_object_dereference (&oh, MDL); + + break; + + case TOKEN_SET: + token = next_token (&val, (unsigned *)0, cfile); + + if ((!is_identifier (token) && token != STRING)) { + set_usage: + printf ("usage: set = \n"); + skip_to_semi (cfile); + break; + } + + if (oh == NULL) { + printf ("no open object.\n"); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + skip_to_semi (cfile); + break; + } + + s1[0] = '\0'; + strncat (s1, val, sizeof(s1)-1); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != EQUAL) + goto set_usage; + + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + case STRING: + dhcpctl_set_string_value (oh, val, s1); + token = next_token (&val, (unsigned *)0, cfile); + break; + + case NUMBER: + strcpy (buf, val); + token = peek_token (&val, (unsigned *)0, cfile); + /* Colon-seperated hex list? */ + if (token == COLON) + goto cshl; + else if (token == DOT) { + s = buf; + val = buf; + do { + int intval = atoi (val); + dotiszero: + if (intval > 255) { + parse_warn (cfile, + "dotted octet > 255: %s", + val); + skip_to_semi (cfile); + goto badnum; + } + *s++ = intval; + token = next_token (&val, + (unsigned *)0, cfile); + if (token != DOT) + break; + /* DOT is zero. */ + while ((token = next_token (&val, + (unsigned *)0, cfile)) == DOT) + *s++ = 0; + } while (token == NUMBER); + dhcpctl_set_data_value (oh, buf, + (unsigned)(s - buf), + s1); + break; + } + dhcpctl_set_int_value (oh, atoi (buf), s1); + token = next_token (&val, (unsigned *)0, cfile); + badnum: + break; + + case NUMBER_OR_NAME: + strcpy (buf, val); + cshl: + s = buf; + val = buf; + do { + convert_num (cfile, (unsigned char *)s, + val, 16, 8); + ++s; + token = next_token (&val, + (unsigned *)0, cfile); + if (token != COLON) + break; + token = next_token (&val, + (unsigned *)0, cfile); + } while (token == NUMBER || + token == NUMBER_OR_NAME); + dhcpctl_set_data_value (oh, buf, + (unsigned)(s - buf), s1); + break; + + default: + printf ("invalid value.\n"); + skip_to_semi (cfile); + } + + if (token != END_OF_FILE && token != EOL) + goto set_usage; + break; + + case UNSET: + token = next_token (&val, (unsigned *)0, cfile); + + if ((!is_identifier (token) && token != STRING)) { + unset_usage: + printf ("usage: unset \n"); + skip_to_semi (cfile); + break; + } + + if (!oh) { + printf ("no open object.\n"); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + skip_to_semi (cfile); + break; + } + + s1[0] = '\0'; + strncat (s1, val, sizeof(s1)-1); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) + goto unset_usage; + + dhcpctl_set_null_value (oh, s1); + break; + + + case TOKEN_CREATE: + case TOKEN_OPEN: + i = token; + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: %s\n", val); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + skip_to_semi (cfile); + break; + } + + if (!oh) { + printf ("you must make a new object first!\n"); + skip_to_semi (cfile); + break; + } + + if (i == TOKEN_CREATE) + i = DHCPCTL_CREATE | DHCPCTL_EXCL; + else + i = 0; + + status = dhcpctl_open_object (oh, connection, i); + if (status == ISC_R_SUCCESS) + status = dhcpctl_wait_for_completion + (oh, &waitstatus); + if (status == ISC_R_SUCCESS) + status = waitstatus; + if (status != ISC_R_SUCCESS) { + printf ("can't open object: %s\n", + isc_result_totext (status)); + break; + } + + break; + + case UPDATE: + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: %s\n", val); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + skip_to_semi (cfile); + break; + } + + if (!oh) { + printf ("you haven't opened an object yet!\n"); + skip_to_semi (cfile); + break; + } + + status = dhcpctl_object_update(connection, oh); + if (status == ISC_R_SUCCESS) + status = dhcpctl_wait_for_completion + (oh, &waitstatus); + if (status == ISC_R_SUCCESS) + status = waitstatus; + if (status != ISC_R_SUCCESS) { + printf ("can't update object: %s\n", + isc_result_totext (status)); + break; + } + + break; + + case REMOVE: + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: remove\n"); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + break; + } + + if (!oh) { + printf ("no object.\n"); + break; + } + + status = dhcpctl_object_remove(connection, oh); + if (status == ISC_R_SUCCESS) + status = dhcpctl_wait_for_completion + (oh, &waitstatus); + if (status == ISC_R_SUCCESS) + status = waitstatus; + if (status != ISC_R_SUCCESS) { + printf ("can't destroy object: %s\n", + isc_result_totext (status)); + break; + } + omapi_object_dereference (&oh, MDL); + break; + + case REFRESH: + token = next_token (&val, (unsigned *)0, cfile); + if (token != END_OF_FILE && token != EOL) { + printf ("usage: refresh\n"); + skip_to_semi (cfile); + break; + } + + if (!connected) { + printf ("not connected.\n"); + break; + } + + if (!oh) { + printf ("no object.\n"); + break; + } + + status = dhcpctl_object_refresh(connection, oh); + if (status == ISC_R_SUCCESS) + status = dhcpctl_wait_for_completion + (oh, &waitstatus); + if (status == ISC_R_SUCCESS) + status = waitstatus; + if (status != ISC_R_SUCCESS) { + printf ("can't refresh object: %s\n", + isc_result_totext (status)); + break; + } + + break; + } + end_parse (&cfile); + } while (1); + + exit (0); +} + +/* Sigh */ +isc_result_t dhcp_set_control_state (control_object_state_t oldstate, + control_object_state_t newstate) +{ + return ISC_R_SUCCESS; +} diff --git a/contrib/dhcp-3.0/dhcpctl/remote.c b/contrib/dhcp-3.0/dhcpctl/remote.c new file mode 100644 index 0000000000..1c1e5da2a3 --- /dev/null +++ b/contrib/dhcp-3.0/dhcpctl/remote.c @@ -0,0 +1,367 @@ +/* remote.c + + The dhcpctl remote object. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef lint +static char copyright[] = +"$Id: remote.c,v 1.12.2.6 2004/06/10 17:59:24 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include +#include "dhcpctl.h" + +/* dhcpctl_new_authenticator + + synchronous - creates an authenticator object. + returns nonzero status code if the object couldn't be created + stores handle to authenticator through h if successful, and returns zero. + name is the authenticator name (NUL-terminated string). + algorithm is the NUL-terminated string name of the algorithm to use + (currently, only "hmac-md5" is supported). + secret and secret_len is the key secret. */ + +dhcpctl_status dhcpctl_new_authenticator (dhcpctl_handle *h, + const char *name, + const char *algorithm, + const unsigned char *secret, + unsigned secret_len) +{ + struct auth_key *key = (struct auth_key *)0; + isc_result_t status; + + status = omapi_auth_key_new (&key, MDL); + if (status != ISC_R_SUCCESS) + return status; + + key -> name = dmalloc (strlen (name) + 1, MDL); + if (!key -> name) { + omapi_auth_key_dereference (&key, MDL); + return ISC_R_NOMEMORY; + } + strcpy (key -> name, name); + + /* If the algorithm name isn't an FQDN, tack on the + .SIG-ALG.REG.NET. domain. */ + if (strchr (algorithm, '.') == 0) { + static char add[] = ".SIG-ALG.REG.INT."; + key -> algorithm = dmalloc (strlen (algorithm) + + sizeof (add), MDL); + if (!key -> algorithm) { + omapi_auth_key_dereference (&key, MDL); + return ISC_R_NOMEMORY; + } + strcpy (key -> algorithm, algorithm); + strcat (key -> algorithm, add); + } else { + key -> algorithm = dmalloc (strlen (algorithm) + 1, MDL); + if (!key -> algorithm) { + omapi_auth_key_dereference (&key, MDL); + return ISC_R_NOMEMORY; + } + strcpy (key -> algorithm, algorithm); + } + + status = omapi_data_string_new (&key -> key, secret_len, MDL); + if (status != ISC_R_SUCCESS) { + omapi_auth_key_dereference (&key, MDL); + return status; + } + memcpy (key -> key -> value, secret, secret_len); + key -> key -> len = secret_len; + + *h = (dhcpctl_handle) key; + return ISC_R_SUCCESS; +} + + +/* dhcpctl_new_object + + synchronous - creates a local handle for a host entry. + returns nonzero status code if the local host entry couldn't + be created + stores handle to host through h if successful, and returns zero. + object_type is a pointer to a NUL-terminated string containing + the ascii name of the type of object being accessed - e.g., "host" */ + +dhcpctl_status dhcpctl_new_object (dhcpctl_handle *h, + dhcpctl_handle connection, + const char *object_type) +{ + dhcpctl_remote_object_t *m; + omapi_object_t *g; + isc_result_t status; + + m = (dhcpctl_remote_object_t *)0; + status = omapi_object_allocate ((omapi_object_t **)&m, + dhcpctl_remote_type, 0, MDL); + if (status != ISC_R_SUCCESS) + return status; + + g = (omapi_object_t *)0; + status = omapi_generic_new (&g, MDL); + if (status != ISC_R_SUCCESS) { + dfree (m, MDL); + return status; + } + status = omapi_object_reference (&m -> inner, g, MDL); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, MDL); + omapi_object_dereference (&g, MDL); + return status; + } + status = omapi_object_reference (&g -> outer, + (omapi_object_t *)m, MDL); + + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, MDL); + omapi_object_dereference (&g, MDL); + return status; + } + + status = omapi_typed_data_new (MDL, &m -> rtype, + omapi_datatype_string, + object_type); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, MDL); + omapi_object_dereference (&g, MDL); + return status; + } + + status = omapi_object_reference (h, (omapi_object_t *)m, MDL); + omapi_object_dereference ((omapi_object_t **)&m, MDL); + omapi_object_dereference (&g, MDL); + if (status != ISC_R_SUCCESS) + return status; + + return status; +} + +/* asynchronous - just queues the request + returns nonzero status code if open couldn't be queued + returns zero if open was queued + h is a handle to an object created by dhcpctl_new_object + connection is a connection to a DHCP server + flags include: + DHCPCTL_CREATE - if the object doesn't exist, create it + DHCPCTL_UPDATE - update the object on the server using the + attached parameters + DHCPCTL_EXCL - error if the object exists and DHCPCTL_CREATE + was also specified */ + +dhcpctl_status dhcpctl_open_object (dhcpctl_handle h, + dhcpctl_handle connection, + int flags) +{ + isc_result_t status; + omapi_object_t *message = (omapi_object_t *)0; + dhcpctl_remote_object_t *remote; + + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + remote = (dhcpctl_remote_object_t *)h; + + status = omapi_message_new (&message, MDL); + if (status != ISC_R_SUCCESS) + return status; + status = omapi_set_int_value (message, (omapi_object_t *)0, + "op", OMAPI_OP_OPEN); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + status = omapi_set_object_value (message, (omapi_object_t *)0, + "object", h); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + if (flags & DHCPCTL_CREATE) { + status = omapi_set_boolean_value (message, (omapi_object_t *)0, + "create", 1); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + } + if (flags & DHCPCTL_UPDATE) { + status = omapi_set_boolean_value (message, (omapi_object_t *)0, + "update", 1); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + } + if (flags & DHCPCTL_EXCL) { + status = omapi_set_boolean_value (message, (omapi_object_t *)0, + "exclusive", 1); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + } + + if (remote -> rtype) { + status = omapi_set_value_str (message, (omapi_object_t *)0, + "type", remote -> rtype); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + } + + status = omapi_message_register (message); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, MDL); + return status; + } + + status = omapi_protocol_send_message (connection -> outer, + (omapi_object_t *)0, + message, (omapi_object_t *)0); + + if (status != ISC_R_SUCCESS) + omapi_message_unregister (message); + + omapi_object_dereference (&message, MDL); + return status; +} + +/* Callback methods (not meant to be called directly) */ + +isc_result_t dhcpctl_remote_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + dhcpctl_remote_object_t *ro; + unsigned long rh; + isc_result_t status; + + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + ro = (dhcpctl_remote_object_t *)h; + + if (!omapi_ds_strcmp (name, "remote-handle")) { + status = omapi_get_int_value (&rh, value); + if (status == ISC_R_SUCCESS) + ro -> remote_handle = rh; + return status; + } + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t dhcpctl_remote_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t dhcpctl_remote_signal_handler (omapi_object_t *o, + const char *name, va_list ap) +{ + dhcpctl_remote_object_t *p; + omapi_typed_data_t *tv; + + if (o -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + p = (dhcpctl_remote_object_t *)o; + + if (!strcmp (name, "updated")) { + p -> waitstatus = ISC_R_SUCCESS; + if (o -> inner -> type == omapi_type_generic) + omapi_generic_clear_flags (o -> inner); + return omapi_signal_in (o -> inner, "ready"); + } + if (!strcmp (name, "status")) { + p -> waitstatus = va_arg (ap, isc_result_t); + if (p -> message) + omapi_typed_data_dereference (&p -> message, MDL); + tv = va_arg (ap, omapi_typed_data_t *); + if (tv) + omapi_typed_data_reference (&p -> message, tv, MDL); + return omapi_signal_in (o -> inner, "ready"); + } + + if (p -> inner && p -> inner -> type -> signal_handler) + return (*(p -> inner -> type -> signal_handler)) + (p -> inner, name, ap); + + return ISC_R_SUCCESS; +} + +isc_result_t dhcpctl_remote_destroy (omapi_object_t *h, + const char *file, int line) +{ + dhcpctl_remote_object_t *p; + if (h -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + p = (dhcpctl_remote_object_t *)h; + if (p -> handle) + omapi_object_dereference ((omapi_object_t **)&p -> handle, + file, line); + if (p -> rtype) + omapi_typed_data_dereference ((omapi_typed_data_t **)&p->rtype, + file, line); + return ISC_R_SUCCESS; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t dhcpctl_remote_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *p) +{ + int i; + + if (p -> type != dhcpctl_remote_type) + return ISC_R_INVALIDARG; + + if (p -> inner && p -> inner -> type -> stuff_values) + return (*(p -> inner -> type -> stuff_values)) (c, id, + p -> inner); + return ISC_R_SUCCESS; +} + diff --git a/contrib/dhcp-3.0/dst/base64.c b/contrib/dhcp-3.0/dst/base64.c new file mode 100644 index 0000000000..0f6d14bb53 --- /dev/null +++ b/contrib/dhcp-3.0/dst/base64.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: base64.c,v 1.1.2.1 2004/06/10 17:59:28 dhankins Exp $"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(src, target, targsize) + char const *src; + u_char *target; + size_t targsize; +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/contrib/dhcp-3.0/dst/dst_api.c b/contrib/dhcp-3.0/dst/dst_api.c new file mode 100644 index 0000000000..64114cb1eb --- /dev/null +++ b/contrib/dhcp-3.0/dst/dst_api.c @@ -0,0 +1,1081 @@ +#ifndef LINT +static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/dst_api.c,v 1.1 2001/02/22 07:22:08 mellon Exp $"; +#endif + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ +/* + * This file contains the interface between the DST API and the crypto API. + * This is the only file that needs to be changed if the crypto system is + * changed. Exported functions are: + * void dst_init() Initialize the toolkit + * int dst_check_algorithm() Function to determines if alg is suppored. + * int dst_compare_keys() Function to compare two keys for equality. + * int dst_sign_data() Incremental signing routine. + * int dst_verify_data() Incremental verify routine. + * int dst_generate_key() Function to generate new KEY + * DST_KEY *dst_read_key() Function to retrieve private/public KEY. + * void dst_write_key() Function to write out a key. + * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST + * KEY structure. + * int dst_key_to_dnskey() Function to return a public key in DNS + * format binary + * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY + * int *dst_key_to_buffer() Writes out DST_KEY key matterial in buffer + * void dst_free_key() Releases all memory referenced by key structure + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +#include "dst_internal.h" + +/* static variables */ +static int done_init = 0; +dst_func *dst_t_func[DST_MAX_ALGS]; +const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n"; +const char *dst_path = ""; + +/* internal I/O functions */ +static DST_KEY *dst_s_read_public_key(const char *in_name, + const unsigned in_id, int in_alg); +static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, + unsigned in_id, int in_alg); +static int dst_s_write_public_key(const DST_KEY *key); +static int dst_s_write_private_key(const DST_KEY *key); + +/* internal function to set up data structure */ +static DST_KEY *dst_s_get_key_struct(const char *name, const int alg, + const u_int32_t flags, const int protocol, + const int bits); + +/* + * dst_init + * This function initializes the Digital Signature Toolkit. + * Right now, it just checks the DSTKEYPATH environment variable. + * Parameters + * none + * Returns + * none + */ +void +dst_init() +{ + char *s; + unsigned len; + + if (done_init != 0) + return; + done_init = 1; + + s = getenv("DSTKEYPATH"); + len = 0; + if (s) { + struct stat statbuf; + + len = strlen(s); + if (len > PATH_MAX) { + EREPORT(("%s is longer than %d characters, ignoring\n", + s, PATH_MAX)); + } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { + EREPORT(("%s is not a valid directory\n", s)); + } else { + char *dp = (char *) malloc(len + 2); + int l; + memcpy(dp, s, len + 1); + l = strlen (dp); + if (dp[l - 1] != '/') { + dp[l + 1] = 0; + dp[l] = '/'; + } + dst_path = dp; + } + } + memset(dst_t_func, 0, sizeof(dst_t_func)); + /* first one is selected */ +#if 0 + dst_bsafe_init(); + dst_rsaref_init(); +#endif + dst_hmac_md5_init(); +#if 0 + dst_eay_dss_init(); + dst_cylink_init(); +#endif +} + +/* + * dst_check_algorithm + * This function determines if the crypto system for the specified + * algorithm is present. + * Parameters + * alg 1 KEY_RSA + * 3 KEY_DSA + * 157 KEY_HMAC_MD5 + * future algorithms TBD and registered with IANA. + * Returns + * 1 - The algorithm is available. + * 0 - The algorithm is not available. + */ +int +dst_check_algorithm(const int alg) +{ + return (dst_t_func[alg] != NULL); +} + +/* + * dst_s_get_key_struct + * This function allocates key structure and fills in some of the + * fields of the structure. + * Parameters: + * name: the name of the key + * alg: the algorithm number + * flags: the dns flags of the key + * protocol: the dns protocol of the key + * bits: the size of the key + * Returns: + * NULL if error + * valid pointer otherwise + */ +static DST_KEY * +dst_s_get_key_struct(const char *name, const int alg, const u_int32_t flags, + const int protocol, const int bits) +{ + DST_KEY *new_key = NULL; + + if (dst_check_algorithm(alg)) /* make sure alg is available */ + new_key = (DST_KEY *) malloc(sizeof(*new_key)); + if (new_key == NULL) + return (NULL); + + memset(new_key, 0, sizeof(*new_key)); + new_key->dk_key_name = strdup(name); + new_key->dk_alg = alg; + new_key->dk_flags = flags; + new_key->dk_proto = protocol; + new_key->dk_KEY_struct = NULL; + new_key->dk_key_size = bits; + new_key->dk_func = dst_t_func[alg]; + return (new_key); +} + +/* + * dst_compare_keys + * Compares two keys for equality. + * Parameters + * key1, key2 Two keys to be compared. + * Returns + * 0 The keys are equal. + * non-zero The keys are not equal. + */ + +int +dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2) +{ + if (key1 == key2) + return (0); + if (key1 == NULL || key2 == NULL) + return (4); + if (key1->dk_alg != key2->dk_alg) + return (1); + if (key1->dk_key_size != key2->dk_key_size) + return (2); + if (key1->dk_id != key2->dk_id) + return (3); + return (key1->dk_func->compare(key1, key2)); +} + + +/* + * dst_sign_data + * An incremental signing function. Data is signed in steps. + * First the context must be initialized (SIG_MODE_INIT). + * Then data is hashed (SIG_MODE_UPDATE). Finally the signature + * itself is created (SIG_MODE_FINAL). This function can be called + * once with INIT, UPDATE and FINAL modes all set, or it can be + + * called separately with a different mode set for each step. The + * UPDATE step can be repeated. + * Parameters + * mode A bit mask used to specify operation(s) to be performed. + * SIG_MODE_INIT 1 Initialize digest + * SIG_MODE_UPDATE 2 Add data to digest + * SIG_MODE_FINAL 4 Generate signature + * from signature + * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL + * data Data to be signed. + * len The length in bytes of data to be signed. + * in_key Contains a private key to sign with. + * KEY structures should be handled (created, converted, + * compared, stored, freed) by the DST. + * signature + * The location to which the signature will be written. + * sig_len Length of the signature field in bytes. + * Return + * 0 Successfull INIT or Update operation + * >0 success FINAL (sign) operation + * <0 failure + */ + +int +dst_sign_data(const int mode, DST_KEY *in_key, void **context, + const u_char *data, const unsigned len, + u_char *signature, const unsigned sig_len) +{ + DUMP(data, mode, len, "dst_sign_data()"); + + if (mode & SIG_MODE_FINAL && + (in_key->dk_KEY_struct == NULL || signature == NULL)) + return (MISSING_KEY_OR_SIGNATURE); + + if (in_key->dk_func && in_key->dk_func->sign) + return (in_key->dk_func->sign(mode, in_key, context, data, len, + signature, sig_len)); + return (UNKNOWN_KEYALG); +} + + +/* + * dst_verify_data + * An incremental verify function. Data is verified in steps. + * First the context must be initialized (SIG_MODE_INIT). + * Then data is hashed (SIG_MODE_UPDATE). Finally the signature + * is verified (SIG_MODE_FINAL). This function can be called + * once with INIT, UPDATE and FINAL modes all set, or it can be + * called separately with a different mode set for each step. The + * UPDATE step can be repeated. + * Parameters + * mode Operations to perform this time. + * SIG_MODE_INIT 1 Initialize digest + * SIG_MODE_UPDATE 2 add data to digest + * SIG_MODE_FINAL 4 verify signature + * SIG_MODE_ALL + * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL) + * data Data to pass through the hash function. + * len Length of the data in bytes. + * in_key Key for verification. + * signature Location of signature. + * sig_len Length of the signature in bytes. + * Returns + * 0 Verify success + * Non-Zero Verify Failure + */ + +int +dst_verify_data(const int mode, DST_KEY *in_key, void **context, + const u_char *data, const unsigned len, + const u_char *signature, const unsigned sig_len) +{ + DUMP(data, mode, len, "dst_verify_data()"); + if (mode & SIG_MODE_FINAL && + (in_key->dk_KEY_struct == NULL || signature == NULL)) + return (MISSING_KEY_OR_SIGNATURE); + + if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL) + return (UNSUPPORTED_KEYALG); + return (in_key->dk_func->verify(mode, in_key, context, data, len, + signature, sig_len)); +} + + +/* + * dst_read_private_key + * Access a private key. First the list of private keys that have + * already been read in is searched, then the key accessed on disk. + * If the private key can be found, it is returned. If the key cannot + * be found, a null pointer is returned. The options specify required + * key characteristics. If the private key requested does not have + * these characteristics, it will not be read. + * Parameters + * in_keyname The private key name. + * in_id The id of the private key. + * options DST_FORCE_READ Read from disk - don't use a previously + * read key. + * DST_CAN_SIGN The key must be useable for signing. + * DST_NO_AUTHEN The key must be useable for authentication. + * DST_STANDARD Return any key + * Returns + * NULL If there is no key found in the current directory or + * this key has not been loaded before. + * !NULL Success - KEY structure returned. + */ + +DST_KEY * +dst_read_key(const char *in_keyname, const unsigned in_id, + const int in_alg, const int type) +{ + char keyname[PATH_MAX]; + DST_KEY *dg_key = NULL, *pubkey = NULL; + + if (!dst_check_algorithm(in_alg)) { /* make sure alg is available */ + EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n", + in_alg)); + return (NULL); + } + if ((type && (DST_PUBLIC | DST_PRIVATE)) == 0) + return (NULL); + if (in_keyname == NULL) { + EREPORT(("dst_read_private_key(): Null key name passed in\n")); + return (NULL); + } else + strcpy(keyname, in_keyname); + + /* before I read in the public key, check if it is allowed to sign */ + if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL) + return (NULL); + + if (type == DST_PUBLIC) + return pubkey; + + if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg, + pubkey->dk_flags, pubkey->dk_proto, + 0))) + return (dg_key); + /* Fill in private key and some fields in the general key structure */ + if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id, + pubkey->dk_alg) == 0) + dg_key = dst_free_key(dg_key); + + pubkey = dst_free_key(pubkey); + return (dg_key); +} + +int +dst_write_key(const DST_KEY *key, const int type) +{ + int pub = 0, priv = 0; + + if (key == NULL) + return (0); + if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ + EREPORT(("dst_write_key(): Algorithm %d not suppored\n", + key->dk_alg)); + return (UNSUPPORTED_KEYALG); + } + if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0) + return (0); + + if (type & DST_PUBLIC) + if ((pub = dst_s_write_public_key(key)) < 0) + return (pub); + if (type & DST_PRIVATE) + if ((priv = dst_s_write_private_key(key)) < 0) + return (priv); + return (priv+pub); +} + +/* + * dst_write_private_key + * Write a private key to disk. The filename will be of the form: + * Kdk_name>+dk_alg>+dk_id>.. + * If there is already a file with this name, an error is returned. + * + * Parameters + * key A DST managed key structure that contains + * all information needed about a key. + * Return + * >= 0 Correct behavior. Returns length of encoded key value + * written to disk. + * < 0 error. + */ + +static int +dst_s_write_private_key(const DST_KEY *key) +{ + u_char encoded_block[RAW_KEY_SIZE]; + char file[PATH_MAX]; + unsigned len; + FILE *fp; + + /* First encode the key into the portable key format */ + if (key == NULL) + return (-1); + if (key->dk_KEY_struct == NULL) + return (0); /* null key has no private key */ + + if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) { + EREPORT(("dst_write_private_key(): Unsupported operation %d\n", + key->dk_alg)); + return (-5); + } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block, + sizeof(encoded_block))) <= 0) { + EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len)); + return (-8); + } + /* Now I can create the file I want to use */ + dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg, + PRIVATE_KEY, PATH_MAX); + + /* Do not overwrite an existing file */ + if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) { + int nn; + if ((nn = fwrite(encoded_block, 1, len, fp)) != len) { + EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n", + file, out_len, nn, errno)); + return (-5); + } + fclose(fp); + } else { + EREPORT(("dst_write_private_key(): Can not create file %s\n" + ,file)); + return (-6); + } + memset(encoded_block, 0, len); + return (len); +} + +/* +* + * dst_read_public_key + * Read a public key from disk and store in a DST key structure. + * Parameters + * in_name K. is the + * filename of the key file to be read. + * Returns + * NULL If the key does not exist or no name is supplied. + * NON-NULL Initalized key structure if the key exists. + */ + +static DST_KEY * +dst_s_read_public_key(const char *in_name, const unsigned in_id, int in_alg) +{ + unsigned flags, len; + int proto, alg, dlen; + int c; + char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace; + u_char deckey[RAW_KEY_SIZE]; + FILE *fp; + + if (in_name == NULL) { + EREPORT(("dst_read_public_key(): No key name given\n")); + return (NULL); + } + if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY, + PATH_MAX) == -1) { + EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n", + in_name, in_id, PUBLIC_KEY)); + return (NULL); + } + /* + * Open the file and read it's formatted contents up to key + * File format: + * domain.name [ttl] [IN] KEY + * flags, proto, alg stored as decimal (or hex numbers FIXME). + * (FIXME: handle parentheses for line continuation.) + */ + if ((fp = dst_s_fopen(name, "r", 0)) == NULL) { + EREPORT(("dst_read_public_key(): Public Key not found %s\n", + name)); + return (NULL); + } + /* Skip domain name, which ends at first blank */ + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + /* Skip blank to get to next field */ + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + + /* Skip optional TTL -- if initial digit, skip whole word. */ + if (isdigit(c)) { + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + } + /* Skip optional "IN" */ + if (c == 'I' || c == 'i') { + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + } + /* Locate and skip "KEY" */ + if (c != 'K' && c != 'k') { + EREPORT(("\"KEY\" doesn't appear in file: %s", name)); + return NULL; + } + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + ungetc(c, fp); /* return the charcter to the input field */ + /* Handle hex!! FIXME. */ + + if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) { + EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n" + ,name)); + return (NULL); + } + /* read in the key string */ + fgets(enckey, sizeof(enckey), fp); + + /* If we aren't at end-of-file, something is wrong. */ + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + if (!feof(fp)) { + EREPORT(("Key too long in file: %s", name)); + return NULL; + } + fclose(fp); + + if ((len = strlen(enckey)) <= 0) + return (NULL); + + /* discard \n */ + enckey[--len] = '\0'; + + /* remove leading spaces */ + for (notspace = (char *) enckey; isspace(*notspace); len--) + notspace++; + + dlen = b64_pton(notspace, deckey, sizeof(deckey)); + if (dlen < 0) { + EREPORT(("dst_read_public_key: bad return from b64_pton = %d", + dlen)); + return (NULL); + } + /* store key and info in a key structure that is returned */ +/* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey, + dlen);*/ + return dst_buffer_to_key(in_name, alg, + flags, proto, deckey, (unsigned)dlen); +} + + +/* + * dst_write_public_key + * Write a key to disk in DNS format. + * Parameters + * key Pointer to a DST key structure. + * Returns + * 0 Failure + * 1 Success + */ + +static int +dst_s_write_public_key(const DST_KEY *key) +{ + FILE *fp; + char filename[PATH_MAX]; + u_char out_key[RAW_KEY_SIZE]; + char enc_key[RAW_KEY_SIZE]; + int len = 0; + + memset(out_key, 0, sizeof(out_key)); + if (key == NULL) { + EREPORT(("dst_write_public_key(): No key specified \n")); + return (0); + } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0) + return (0); + + /* Make the filename */ + if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id, + key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) { + EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n", + key->dk_key_name, key->dk_id, PUBLIC_KEY)); + return (0); + } + /* create public key file */ + if ((fp = dst_s_fopen(filename, "w+", 0644)) == NULL) { + EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n", + filename, errno)); + return (0); + } + /*write out key first base64 the key data */ + if (key->dk_flags & DST_EXTEND_FLAG) + b64_ntop(&out_key[6], + (unsigned)(len - 6), enc_key, sizeof(enc_key)); + else + b64_ntop(&out_key[4], + (unsigned)(len - 4), enc_key, sizeof(enc_key)); + fprintf(fp, "%s IN KEY %d %d %d %s\n", + key->dk_key_name, + key->dk_flags, key->dk_proto, key->dk_alg, enc_key); + fclose(fp); + return (1); +} + + +/* + * dst_dnskey_to_public_key + * This function converts the contents of a DNS KEY RR into a DST + * key structure. + * Paramters + * len Length of the RDATA of the KEY RR RDATA + * rdata A pointer to the the KEY RR RDATA. + * in_name Key name to be stored in key structure. + * Returns + * NULL Failure + * NON-NULL Success. Pointer to key structure. + * Caller's responsibility to free() it. + */ + +DST_KEY * +dst_dnskey_to_key(const char *in_name, + const u_char *rdata, const unsigned len) +{ + DST_KEY *key_st; + int alg ; + int start = DST_KEY_START; + + if (rdata == NULL || len <= DST_KEY_ALG) /* no data */ + return (NULL); + alg = (u_int8_t) rdata[DST_KEY_ALG]; + if (!dst_check_algorithm(alg)) { /* make sure alg is available */ + EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n", + alg)); + return (NULL); + } + if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL) + return (NULL); + + if (in_name == NULL) + return (NULL); + key_st->dk_flags = dst_s_get_int16(rdata); + key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT]; + if (key_st->dk_flags & DST_EXTEND_FLAG) { + u_int32_t ext_flags; + ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]); + key_st->dk_flags = key_st->dk_flags | (ext_flags << 16); + start += 2; + } + /* + * now point to the begining of the data representing the encoding + * of the key + */ + if (key_st->dk_func && key_st->dk_func->from_dns_key) { + if (key_st->dk_func->from_dns_key(key_st, &rdata[start], + len - start) > 0) + return (key_st); + } else + EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n", + alg)); + + SAFE_FREE(key_st); + return (key_st); +} + + +/* + * dst_public_key_to_dnskey + * Function to encode a public key into DNS KEY wire format + * Parameters + * key Key structure to encode. + * out_storage Location to write the encoded key to. + * out_len Size of the output array. + * Returns + * <0 Failure + * >=0 Number of bytes written to out_storage + */ + +int +dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage, + const unsigned out_len) +{ + u_int16_t val; + int loc = 0; + int enc_len = 0; + if (key == NULL) + return (-1); + + if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ + EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n", + key->dk_alg)); + return (UNSUPPORTED_KEYALG); + } + memset(out_storage, 0, out_len); + val = (u_int16_t)(key->dk_flags & 0xffff); + out_storage[0] = (val >> 8) & 0xff; + out_storage[1] = val & 0xff; + loc += 2; + + out_storage[loc++] = (u_char) key->dk_proto; + out_storage[loc++] = (u_char) key->dk_alg; + + if (key->dk_flags > 0xffff) { /* Extended flags */ + val = (u_int16_t)((key->dk_flags >> 16) & 0xffff); + out_storage[loc] = (val >> 8) & 0xff; + out_storage[loc+1] = val & 0xff; + loc += 2; + } + if (key->dk_KEY_struct == NULL) + return (loc); + if (key->dk_func && key->dk_func->to_dns_key) { + enc_len = key->dk_func->to_dns_key(key, + (u_char *) &out_storage[loc], + out_len - loc); + if (enc_len > 0) + return (enc_len + loc); + else + return (-1); + } else + EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n", + key->dk_alg)); + return (-1); +} + + +/* + * dst_buffer_to_key + * Function to encode a string of raw data into a DST key + * Parameters + * alg The algorithm (HMAC only) + * key A pointer to the data + * keylen The length of the data + * Returns + * NULL an error occurred + * NON-NULL the DST key + */ +DST_KEY * +dst_buffer_to_key(const char *key_name, /* name of the key */ + const int alg, /* algorithm */ + const unsigned flags, /* dns flags */ + const int protocol, /* dns protocol */ + const u_char *key_buf, /* key in dns wire fmt */ + const unsigned key_len) /* size of key */ +{ + + DST_KEY *dkey = NULL; + + if (!dst_check_algorithm(alg)) { /* make sure alg is available */ + EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg)); + return (NULL); + } + + dkey = dst_s_get_key_struct(key_name, alg, flags, protocol, -1); + + if (dkey == NULL) + return (NULL); + if (dkey->dk_func != NULL && + dkey->dk_func->from_dns_key != NULL) { + if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) { + EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n")); + return (dst_free_key(dkey)); + } + return (dkey); + } + return (NULL); +} + +int +dst_key_to_buffer(DST_KEY *key, u_char *out_buff, unsigned buf_len) +{ + int len; + /* this function will extrac the secret of HMAC into a buffer */ + if(key == NULL) + return (0); + if(key->dk_func != NULL && key->dk_func != NULL) { + len = key->dk_func->to_dns_key(key, out_buff, buf_len); + if (len < 0) + return (0); + return (len); + } + return (0); +} + + +/* + * dst_s_read_private_key_file + * Function reads in private key from a file. + * Fills out the KEY structure. + * Parameters + * name Name of the key to be read. + * pk_key Structure that the key is returned in. + * in_id Key identifier (tag) + * Return + * 1 if everthing works + * 0 if there is any problem + */ + +static int +dst_s_read_private_key_file(char *name, DST_KEY *pk_key, unsigned in_id, + int in_alg) +{ + int cnt, alg, len, major, minor, file_major, file_minor; + int id; + char filename[PATH_MAX]; + u_char in_buff[RAW_KEY_SIZE], *p; + FILE *fp; + + if (name == NULL || pk_key == NULL) { + EREPORT(("dst_read_private_key_file(): No key name given\n")); + return (0); + } + /* Make the filename */ + if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY, + PATH_MAX) == -1) { + EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n", + name, in_id, PRIVATE_KEY)); + return (0); + } + /* first check if we can find the key file */ + if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) { + EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n", + filename, dst_path[0] ? dst_path : + (char *) getcwd(NULL, PATH_MAX - 1))); + return (0); + } + /* now read the header info from the file */ + if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) { + fclose(fp); + EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n", + filename)); + return (0); + } + /* decrypt key */ + fclose(fp); + if (memcmp(in_buff, "Private-key-format: v", 20) != 0) + goto fail; + len = cnt; + p = in_buff; + + if (!dst_s_verify_str((const char **) &p, "Private-key-format: v")) { + EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name)); + goto fail; + } + /* read in file format */ + sscanf((char *)p, "%d.%d", &file_major, &file_minor); + sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor); + if (file_major < 1) { + EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n", + file_major, file_minor, name)); + goto fail; + } else if (file_major > major || file_minor > minor) + EREPORT(( + "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n", + name, file_major, file_minor)); + + while (*p++ != '\n') ; /* skip to end of line */ + + if (!dst_s_verify_str((const char **) &p, "Algorithm: ")) + goto fail; + + if (sscanf((char *)p, "%d", &alg) != 1) + goto fail; + while (*p++ != '\n') ; /* skip to end of line */ + + if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name)) + SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name)); + pk_key->dk_key_name = (char *) strdup(name); + + /* allocate and fill in key structure */ + if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL) + goto fail; + + id = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, + (unsigned)(&in_buff[len] - p)); + if (id < 0) + goto fail; + + /* Make sure the actual key tag matches the input tag used in the filename + */ + if (id != in_id) { + EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id)); + goto fail; + } + pk_key->dk_id = (u_int16_t) id; + pk_key->dk_alg = alg; + memset(in_buff, 0, (unsigned)cnt); + return (1); + + fail: + memset(in_buff, 0, (unsigned)cnt); + return (0); +} + + +/* + * dst_generate_key + * Generate and store a public/private keypair. + * Keys will be stored in formatted files. + * Parameters + * name Name of the new key. Used to create key files + * K++.public and K++.private. + * bits Size of the new key in bits. + * exp What exponent to use: + * 0 use exponent 3 + * non-zero use Fermant4 + * flags The default value of the DNS Key flags. + * The DNS Key RR Flag field is defined in RFC 2065, + * section 3.3. The field has 16 bits. + * protocol + * Default value of the DNS Key protocol field. + * The DNS Key protocol field is defined in RFC 2065, + * section 3.4. The field has 8 bits. + * alg What algorithm to use. Currently defined: + * KEY_RSA 1 + * KEY_DSA 3 + * KEY_HMAC 157 + * out_id The key tag is returned. + * + * Return + * NULL Failure + * non-NULL the generated key pair + * Caller frees the result, and its dk_name pointer. + */ +DST_KEY * +dst_generate_key(const char *name, const int bits, const int exp, + const unsigned flags, const int protocol, const int alg) +{ + DST_KEY *new_key = NULL; + int res; + if (name == NULL) + return (NULL); + + if (!dst_check_algorithm(alg)) { /* make sure alg is available */ + EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg)); + return (NULL); + } + + new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits); + if (new_key == NULL) + return (NULL); + if (bits == 0) /* null key we are done */ + return (new_key); + if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) { + EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n", + alg)); + return (dst_free_key(new_key)); + } + if ((res = new_key->dk_func->generate(new_key, exp)) <= 0) { + EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n", + new_key->dk_key_name, new_key->dk_alg, + new_key->dk_key_size, exp)); + return (dst_free_key(new_key)); + } + return (new_key); +} + + +/* + * dst_free_key + * Release all data structures pointed to by a key structure. + * Parameters + * f_key Key structure to be freed. + */ + +DST_KEY * +dst_free_key(DST_KEY *f_key) +{ + + if (f_key == NULL) + return (f_key); + if (f_key->dk_func && f_key->dk_func->destroy) + f_key->dk_KEY_struct = + f_key->dk_func->destroy(f_key->dk_KEY_struct); + else { + EREPORT(("dst_free_key(): Unknown key alg %d\n", + f_key->dk_alg)); + free(f_key->dk_KEY_struct); /* SHOULD NOT happen */ + } + if (f_key->dk_KEY_struct) { + free(f_key->dk_KEY_struct); + f_key->dk_KEY_struct = NULL; + } + if (f_key->dk_key_name) + SAFE_FREE(f_key->dk_key_name); + SAFE_FREE(f_key); + return (NULL); +} + +/* + * dst_sig_size + * Return the maximim size of signature from the key specified in bytes + * Parameters + * key + * Returns + * bytes + */ +int +dst_sig_size(DST_KEY *key) { + switch (key->dk_alg) { + case KEY_HMAC_MD5: + return (16); + case KEY_HMAC_SHA1: + return (20); + case KEY_RSA: + return (key->dk_key_size + 7) / 8; + case KEY_DSA: + return (40); + default: + EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg)); + return -1; + } +} + +/* + * dst_random + * function that multiplexes number of random number generators + * Parameters + * mode: select the random number generator + * wanted is how many bytes of random data are requested + * outran is a buffer of size at least wanted for the output data + * + * Returns + * number of bytes written to outran + */ +int +dst_random(const int mode, unsigned wanted, u_char *outran) +{ + u_int32_t *buff = NULL, *bp = NULL; + int i; + if (wanted <= 0 || outran == NULL) + return (0); + + switch (mode) { + case DST_RAND_SEMI: + bp = buff = (u_int32_t *) malloc(wanted+sizeof(u_int32_t)); + for (i = 0; i < wanted; i+= sizeof(u_int32_t), bp++) { + *bp = dst_s_quick_random(i); + } + memcpy(outran, buff, (unsigned)wanted); + SAFE_FREE(buff); + return (wanted); + case DST_RAND_STD: + return (dst_s_semi_random(outran, wanted)); + case DST_RAND_KEY: + return (dst_s_random(outran, wanted)); + case DST_RAND_DSS: + default: + /* need error case here XXX OG */ + return (0); + } +} + diff --git a/contrib/dhcp-3.0/dst/dst_internal.h b/contrib/dhcp-3.0/dst/dst_internal.h new file mode 100644 index 0000000000..0890d803a8 --- /dev/null +++ b/contrib/dhcp-3.0/dst/dst_internal.h @@ -0,0 +1,160 @@ +#ifndef DST_INTERNAL_H +#define DST_INTERNAL_H + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ +#include +#include + +#ifndef PATH_MAX +# ifdef POSIX_PATH_MAX +# define PATH_MAX POSIX_PATH_MAX +# else +# define PATH_MAX 255 /* this is the value of POSIX_PATH_MAX */ +# endif +#endif + +typedef struct dst_key { + char *dk_key_name; /* name of the key */ + int dk_key_size; /* this is the size of the key in bits */ + int dk_proto; /* what protocols this key can be used for */ + int dk_alg; /* algorithm number from key record */ + unsigned dk_flags; /* and the flags of the public key */ + unsigned dk_id; /* identifier of the key */ + void *dk_KEY_struct; /* pointer to key in crypto pkg fmt */ + struct dst_func *dk_func; /* point to cryptto pgk specific function table */ +} DST_KEY; +#define HAS_DST_KEY + +#include +/* + * define what crypto systems are supported for RSA, + * BSAFE is prefered over RSAREF; only one can be set at any time + */ +#if defined(BSAFE) && defined(RSAREF) +# error "Cannot have both BSAFE and RSAREF defined" +#endif + +/* Declare dst_lib specific constants */ +#define KEY_FILE_FORMAT "1.2" + +/* suffixes for key file names */ +#define PRIVATE_KEY "private" +#define PUBLIC_KEY "key" + +/* error handling */ +#ifdef REPORT_ERRORS +#define EREPORT(str) printf str +#else +#define EREPORT(str) +#endif + +/* use our own special macro to FRRE memory */ + +#ifndef SAFE_FREE +#define SAFE_FREE(a) if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;} +#define SAFE_FREE2(a,s) if (a != NULL && s > 0){memset(a,0, s);free(a); a=NULL;} +#endif + +typedef struct dst_func { + int (*sign)(const int mode, DST_KEY *key, void **context, + const u_int8_t *data, const unsigned len, + u_int8_t *signature, const unsigned sig_len); + int (*verify)(const int mode, DST_KEY *key, void **context, + const u_int8_t *data, const unsigned len, + const u_int8_t *signature, const unsigned sig_len); + int (*compare)(const DST_KEY *key1, const DST_KEY *key2); + int (*generate)(DST_KEY *key, int parms); + void *(*destroy)(void *key); + /* conversion functions */ + int (*to_dns_key)(const DST_KEY *key, u_int8_t *out, + const unsigned out_len); + int (*from_dns_key)(DST_KEY *key, const u_int8_t *str, + const unsigned str_len); + int (*to_file_fmt)(const DST_KEY *key, char *out, + const unsigned out_len); + int (*from_file_fmt)(DST_KEY *key, const char *out, + const unsigned out_len); + +} dst_func; + +extern dst_func *dst_t_func[DST_MAX_ALGS]; +extern const char *key_file_fmt_str; +extern const char *dst_path; + +#ifndef DST_HASH_SIZE +#define DST_HASH_SIZE 20 /* RIPEMD160 and SHA-1 are 20 bytes MD5 is 16 */ +#endif + +#if 0 +int dst_bsafe_init(void); +int dst_rsaref_init(void); +#endif + +int dst_hmac_md5_init(void); + +#if 0 +int dst_cylink_init(void); +int dst_eay_dss_init(void); +#endif + +/* support functions */ +/* base64 to bignum conversion routines */ +int dst_s_conv_bignum_u8_to_b64( char *out_buf, const unsigned out_len, + const char *header, + const u_int8_t *bin_data, + const unsigned bin_len); +int dst_s_conv_bignum_b64_to_u8( const char **buf, u_int8_t *loc, + const unsigned loclen) ; +/* from higher level support routines */ +int dst_s_calculate_bits( const u_int8_t *str, const int max_bits); +int dst_s_verify_str( const char **buf, const char *str); + + +/* conversion between dns names and key file names */ +size_t dst_s_filename_length( const char *name, const char *suffix); +int dst_s_build_filename( char *filename, const char *name, + unsigned id, int alg, const char *suffix, + size_t filename_length); + +FILE *dst_s_fopen (const char *filename, const char *mode, unsigned perm); + +/* from file prandom.c */ +int dst_s_random( u_int8_t *output, unsigned size); +int dst_s_semi_random( u_int8_t *output, unsigned size); +u_int32_t dst_s_quick_random( int inc); +void dst_s_quick_random_set( u_int32_t val, u_int32_t cnt); + +/* + * read and write network byte order into u_int?_t + * all of these should be retired + */ +u_int16_t dst_s_get_int16( const u_int8_t *buf); +void dst_s_put_int16( u_int8_t *buf, const u_int16_t val); + +u_int32_t dst_s_get_int32( const u_int8_t *buf); +void dst_s_put_int32( u_int8_t *buf, const u_int32_t val); + +#ifdef DUMP +# undef DUMP +# define DUMP(a,b,c,d) dst_s_dump(a,b,c,d) +#else +# define DUMP(a,b,c,d) +#endif + + +#endif /* DST_INTERNAL_H */ diff --git a/contrib/dhcp-3.0/dst/dst_support.c b/contrib/dhcp-3.0/dst/dst_support.c new file mode 100644 index 0000000000..2d7a67f120 --- /dev/null +++ b/contrib/dhcp-3.0/dst/dst_support.c @@ -0,0 +1,463 @@ +static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/dst_support.c,v 1.1 2001/02/22 07:22:08 mellon Exp $"; + + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +#include "dst_internal.h" + +/* + * dst_s_conv_bignum_u8_to_b64 + * This function converts binary data stored as a u_char[] to a + * base-64 string. Leading zeroes are discarded. If a header is + * supplied, it is prefixed to the input prior to encoding. The + * output is \n\0 terminated (the \0 is not included in output length). + * Parameters + * out_buf binary data to convert + * header character string to prefix to the output (label) + * bin_data binary data + * bin_len size of binary data + * Return + * -1 not enough space in output work area + * 0 no output + * >0 number of bytes written to output work area + */ + +int +dst_s_conv_bignum_u8_to_b64(char *out_buf, const unsigned out_len, + const char *header, const u_char *bin_data, + const unsigned bin_len) +{ + const u_char *bp = bin_data; + char *op = out_buf; + unsigned lenh = 0, len64 = 0; + unsigned local_in_len = bin_len; + unsigned local_out_len = out_len; + + if (bin_data == NULL) /* no data no */ + return (0); + + if (out_buf == NULL || out_len <= 0) /* no output_work area */ + return (-1); + + /* suppress leading \0 */ + for (; (*bp == 0x0) && (local_in_len > 0); local_in_len--) + bp++; + + if (header) { /* add header to output string */ + lenh = strlen(header); + if (lenh < out_len) + memcpy(op, header, lenh); + else + return (-1); + local_out_len -= lenh; + op += lenh; + } + len64 = b64_ntop(bp, local_in_len, op, local_out_len - 2); + if (len64 < 0) + return (-1); + op += len64++; + *(op++) = '\n'; /* put CR in the output */ + *op = '\0'; /* make sure output is 0 terminated */ + return (lenh + len64); +} + + +/* + * dst_s_verify_str() + * Validate that the input string(*str) is at the head of the input + * buffer(**buf). If so, move the buffer head pointer (*buf) to + * the first byte of data following the string(*str). + * Parameters + * buf Input buffer. + * str Input string. + * Return + * 0 *str is not the head of **buff + * 1 *str is the head of **buff, *buf is is advanced to + * the tail of **buf. + */ + +int +dst_s_verify_str(const char **buf, const char *str) +{ + unsigned b, s; + if (*buf == NULL) /* error checks */ + return (0); + if (str == NULL || *str == '\0') + return (1); + + b = strlen(*buf); /* get length of strings */ + s = strlen(str); + if (s > b || strncmp(*buf, str, s)) /* check if same */ + return (0); /* not a match */ + (*buf) += s; /* advance pointer */ + return (1); +} + + +/* + * dst_s_conv_bignum_b64_to_u8 + * Read a line of base-64 encoded string from the input buffer, + * convert it to binary, and store it in an output area. The + * input buffer is read until reaching a newline marker or the + * end of the buffer. The binary data is stored in the last X + * number of bytes of the output area where X is the size of the + * binary output. If the operation is successful, the input buffer + * pointer is advanced. This procedure does not do network to host + * byte order conversion. + * Parameters + * buf Pointer to encoded input string. Pointer is updated if + * function is successfull. + * loc Output area. + * loclen Size in bytes of output area. + * Return + * >0 Return = number of bytes of binary data stored in loc. + * 0 Failure. + */ + +int +dst_s_conv_bignum_b64_to_u8(const char **buf, + u_char *loc, const unsigned loclen) +{ + unsigned blen; + char *bp; + u_char bstr[RAW_KEY_SIZE]; + + if (buf == NULL || *buf == NULL) { /* error checks */ + EREPORT(("dst_s_conv_bignum_b64_to_u8: null input buffer.\n")); + return (0); + } + bp = strchr(*buf, '\n'); /* find length of input line */ + if (bp != NULL) + *bp = (u_char) NULL; + + blen = b64_pton(*buf, bstr, sizeof(bstr)); + if (blen <= 0) { + EREPORT(("dst_s_conv_bignum_b64_to_u8: decoded value is null.\n")); + return (0); + } + else if (loclen < blen) { + EREPORT(("dst_s_conv_bignum_b64_to_u8: decoded value is longer than output buffer.\n")); + return (0); + } + if (bp) + *buf = bp; /* advancing buffer past \n */ + memset(loc, 0, loclen - blen); /* clearing unused output area */ + memcpy(loc + loclen - blen, bstr, blen); /* write last blen bytes */ + return (blen); +} + + +/* + * dst_s_calculate_bits + * Given a binary number represented in a u_char[], determine + * the number of significant bits used. + * Parameters + * str An input character string containing a binary number. + * max_bits The maximum possible significant bits. + * Return + * N The number of significant bits in str. + */ + +int +dst_s_calculate_bits(const u_char *str, const int max_bits) +{ + const u_char *p = str; + u_char i, j = 0x80; + int bits; + for (bits = max_bits; *p == 0x00 && bits > 0; p++) + bits -= 8; + for (i = *p; (i & j) != j; j >>= 1) + bits--; + return (bits); +} + + +/* + * calculates a checksum used in kmt for a id. + * takes an array of bytes and a length. + * returns a 16 bit checksum. + */ +u_int16_t +dst_s_id_calc(const u_char *key, const unsigned keysize) +{ + u_int32_t ac; + const u_char *kp = key; + unsigned size = keysize; + + if (!key) + return 0; + + for (ac = 0; size > 1; size -= 2, kp += 2) + ac += ((*kp) << 8) + *(kp + 1); + + if (size > 0) + ac += ((*kp) << 8); + ac += (ac >> 16) & 0xffff; + + return (ac & 0xffff); +} + +/* + * dst_s_dns_key_id() Function to calculated DNSSEC footprint from KEY reocrd + * rdata (all of record) + * Input: + * dns_key_rdata: the raw data in wire format + * rdata_len: the size of the input data + * Output: + * the key footprint/id calcuated from the key data + */ +u_int16_t +dst_s_dns_key_id(const u_char *dns_key_rdata, const unsigned rdata_len) +{ + unsigned key_data = 4; + + if (!dns_key_rdata || (rdata_len < key_data)) + return 0; + + /* check the extended parameters bit in the DNS Key RR flags */ + if (dst_s_get_int16(dns_key_rdata) & DST_EXTEND_FLAG) + key_data += 2; + + /* compute id */ + if (dns_key_rdata[3] == KEY_RSA) /* Algorithm RSA */ + return dst_s_get_int16((const u_char *) + &dns_key_rdata[rdata_len - 3]); + else + /* compute a checksum on the key part of the key rr */ + return dst_s_id_calc(&dns_key_rdata[key_data], + (rdata_len - key_data)); +} + +/* + * dst_s_get_int16 + * This routine extracts a 16 bit integer from a two byte character + * string. The character string is assumed to be in network byte + * order and may be unaligned. The number returned is in host order. + * Parameter + * buf A two byte character string. + * Return + * The converted integer value. + */ + +u_int16_t +dst_s_get_int16(const u_char *buf) +{ + register u_int16_t a = 0; + a = ((u_int16_t)(buf[0] << 8)) | ((u_int16_t)(buf[1])); + return (a); +} + + +/* + * dst_s_get_int32 + * This routine extracts a 32 bit integer from a four byte character + * string. The character string is assumed to be in network byte + * order and may be unaligned. The number returned is in host order. + * Parameter + * buf A four byte character string. + * Return + * The converted integer value. + */ + +u_int32_t +dst_s_get_int32(const u_char *buf) +{ + register u_int32_t a = 0; + a = ((u_int32_t)(buf[0] << 24)) | ((u_int32_t)(buf[1] << 16)) | + ((u_int32_t)(buf[2] << 8)) | ((u_int32_t)(buf[3])); + return (a); +} + + +/* + * dst_s_put_int16 + * Take a 16 bit integer and store the value in a two byte + * character string. The integer is assumed to be in network + * order and the string is returned in host order. + * + * Parameters + * buf Storage for a two byte character string. + * val 16 bit integer. + */ + +void +dst_s_put_int16(u_int8_t *buf, const u_int16_t val) +{ + buf[0] = (u_int8_t)(val >> 8); + buf[1] = (u_int8_t)(val); +} + + +/* + * dst_s_put_int32 + * Take a 32 bit integer and store the value in a four byte + * character string. The integer is assumed to be in network + * order and the string is returned in host order. + * + * Parameters + * buf Storage for a four byte character string. + * val 32 bit integer. + */ + +void +dst_s_put_int32(u_int8_t *buf, const u_int32_t val) +{ + buf[0] = (u_int8_t)(val >> 24); + buf[1] = (u_int8_t)(val >> 16); + buf[2] = (u_int8_t)(val >> 8); + buf[3] = (u_int8_t)(val); +} + + +/* + * dst_s_filename_length + * + * This function returns the number of bytes needed to hold the + * filename for a key file. '/', '\' and ':' are not allowed. + * form: K++. + * + * Returns 0 if the filename would contain either '\', '/' or ':' + */ +size_t +dst_s_filename_length(const char *name, const char *suffix) +{ + if (name == NULL) + return (0); + if (strrchr(name, '\\')) + return (0); + if (strrchr(name, '/')) + return (0); + if (strrchr(name, ':')) + return (0); + if (suffix == NULL) + return (0); + if (strrchr(suffix, '\\')) + return (0); + if (strrchr(suffix, '/')) + return (0); + if (strrchr(suffix, ':')) + return (0); + return (1 + strlen(name) + 6 + strlen(suffix)); +} + + +/* + * dst_s_build_filename () + * Builds a key filename from the key name, it's id, and a + * suffix. '\', '/' and ':' are not allowed. fA filename is of the + * form: K. + * form: K++. + * + * Returns -1 if the conversion fails: + * if the filename would be too long for space allotted + * if the filename would contain a '\', '/' or ':' + * Returns 0 on success + */ + +int +dst_s_build_filename(char *filename, const char *name, unsigned id, + int alg, const char *suffix, size_t filename_length) +{ + unsigned my_id; + if (filename == NULL) + return (-1); + memset(filename, 0, filename_length); + if (name == NULL) + return (-1); + if (suffix == NULL) + return (-1); + if (filename_length < 1 + strlen(name) + 4 + 6 + 1 + strlen(suffix)) + return (-1); + my_id = id; + sprintf(filename, "K%s+%03d+%05d.%s", name, alg, my_id, + (const char *) suffix); + if (strrchr(filename, '/')) + return (-1); + if (strrchr(filename, '\\')) + return (-1); + if (strrchr(filename, ':')) + return (-1); + return (0); +} + +/* + * dst_s_fopen () + * Open a file in the dst_path directory. If perm is specified, the + * file is checked for existence first, and not opened if it exists. + * Parameters + * filename File to open + * mode Mode to open the file (passed directly to fopen) + * perm File permission, if creating a new file. + * Returns + * NULL Failure + * NON-NULL (FILE *) of opened file. + */ +FILE * +dst_s_fopen(const char *filename, const char *mode, unsigned perm) +{ + FILE *fp; + char pathname[PATH_MAX]; + unsigned plen = sizeof(pathname); + + if (*dst_path != '\0') { + strcpy(pathname, dst_path); + plen -= strlen(pathname); + } + else + pathname[0] = '\0'; + + if (plen > strlen(filename)) + strncpy(&pathname[PATH_MAX - plen], filename, plen-1); + else + return (NULL); + + fp = fopen(pathname, mode); + if (perm) + chmod(pathname, perm); + return (fp); +} + +#if 0 +void +dst_s_dump(const int mode, const u_char *data, const int size, + const char *msg) +{ + if (size > 0) { +#ifdef LONG_TEST + static u_char scratch[1000]; + int n ; + n = b64_ntop(data, scratch, size, sizeof(scratch)); + printf("%s: %x %d %s\n", msg, mode, n, scratch); +#else + printf("%s,%x %d\n", msg, mode, size); +#endif + } +} +#endif diff --git a/contrib/dhcp-3.0/dst/hmac_link.c b/contrib/dhcp-3.0/dst/hmac_link.c new file mode 100644 index 0000000000..90b92e2c23 --- /dev/null +++ b/contrib/dhcp-3.0/dst/hmac_link.c @@ -0,0 +1,494 @@ +#ifdef HMAC_MD5 +#ifndef LINT +static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/hmac_link.c,v 1.1 2001/02/22 07:22:08 mellon Exp $"; +#endif +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ + +/* + * This file contains an implementation of the HMAC-MD5 algorithm. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +#include "dst_internal.h" + +#ifdef USE_MD5 +# include "md5.h" +# ifndef _MD5_H_ +# define _MD5_H_ 1 /* make sure we do not include rsaref md5.h file */ +# endif +#endif + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + + +typedef struct hmackey { + u_char hk_ipad[64], hk_opad[64]; +} HMAC_Key; + + +/************************************************************************** + * dst_hmac_md5_sign + * Call HMAC signing functions to sign a block of data. + * There are three steps to signing, INIT (initialize structures), + * UPDATE (hash (more) data), FINAL (generate a signature). This + * routine performs one or more of these steps. + * Parameters + * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. + * priv_key key to use for signing. + * context the context to be used in this digest + * data data to be signed. + * len length in bytes of data. + * signature location to store signature. + * sig_len size of the signature location + * returns + * N Success on SIG_MODE_FINAL = returns signature length in bytes + * 0 Success on SIG_MODE_INIT and UPDATE + * <0 Failure + */ + +static int +dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context, + const u_char *data, const unsigned len, + u_char *signature, const unsigned sig_len) +{ + HMAC_Key *key; + int sign_len = 0; + MD5_CTX *ctx = NULL; + + if (mode & SIG_MODE_INIT) + ctx = (MD5_CTX *) malloc(sizeof(*ctx)); + else if (context) + ctx = (MD5_CTX *) *context; + if (ctx == NULL) + return (-1); + + if (d_key == NULL || d_key->dk_KEY_struct == NULL) + return (-1); + key = (HMAC_Key *) d_key->dk_KEY_struct; + + if (mode & SIG_MODE_INIT) { + MD5Init(ctx); + MD5Update(ctx, key->hk_ipad, HMAC_LEN); + } + + if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) + MD5Update(ctx, (const unsigned char *)data, len); + + if (mode & SIG_MODE_FINAL) { + if (signature == NULL || sig_len < MD5_LEN) + return (SIGN_FINAL_FAILURE); + MD5Final(signature, ctx); + + /* perform outer MD5 */ + MD5Init(ctx); + MD5Update(ctx, key->hk_opad, HMAC_LEN); + MD5Update(ctx, signature, MD5_LEN); + MD5Final(signature, ctx); + sign_len = MD5_LEN; + SAFE_FREE(ctx); + } + else { + if (context == NULL) + return (-1); + *context = (void *) ctx; + } + return (sign_len); +} + + +/************************************************************************** + * dst_hmac_md5_verify() + * Calls HMAC verification routines. There are three steps to + * verification, INIT (initialize structures), UPDATE (hash (more) data), + * FINAL (generate a signature). This routine performs one or more of + * these steps. + * Parameters + * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. + * dkey key to use for verify. + * data data signed. + * len length in bytes of data. + * signature signature. + * sig_len length in bytes of signature. + * returns + * 0 Success + * <0 Failure + */ + +static int +dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context, + const u_char *data, const unsigned len, + const u_char *signature, const unsigned sig_len) +{ + HMAC_Key *key; + MD5_CTX *ctx = NULL; + + if (mode & SIG_MODE_INIT) + ctx = (MD5_CTX *) malloc(sizeof(*ctx)); + else if (context) + ctx = (MD5_CTX *) *context; + if (ctx == NULL) + return (-1); + + if (d_key == NULL || d_key->dk_KEY_struct == NULL) + return (-1); + + key = (HMAC_Key *) d_key->dk_KEY_struct; + if (mode & SIG_MODE_INIT) { + MD5Init(ctx); + MD5Update(ctx, key->hk_ipad, HMAC_LEN); + } + if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) + MD5Update(ctx, (const unsigned char *)data, len); + + if (mode & SIG_MODE_FINAL) { + u_char digest[MD5_LEN]; + if (signature == NULL || key == NULL || sig_len != MD5_LEN) + return (VERIFY_FINAL_FAILURE); + MD5Final(digest, ctx); + + /* perform outer MD5 */ + MD5Init(ctx); + MD5Update(ctx, key->hk_opad, HMAC_LEN); + MD5Update(ctx, digest, MD5_LEN); + MD5Final(digest, ctx); + + SAFE_FREE(ctx); + if (memcmp(digest, signature, MD5_LEN) != 0) + return (VERIFY_FINAL_FAILURE); + } + else { + if (context == NULL) + return (-1); + *context = (void *) ctx; + } + return (0); +} + + +/************************************************************************** + * dst_buffer_to_hmac_md5 + * Converts key from raw data to an HMAC Key + * This function gets in a pointer to the data + * Parameters + * hkey the HMAC key to be filled in + * key the key in raw format + * keylen the length of the key + * Return + * 0 Success + * <0 Failure + */ +static int +dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const unsigned keylen) +{ + int i; + HMAC_Key *hkey = NULL; + MD5_CTX ctx; + unsigned local_keylen = keylen; + + if (dkey == NULL || key == NULL || keylen < 0) + return (-1); + + if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL) + return (-2); + + memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad)); + memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad)); + + /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */ + if (keylen > HMAC_LEN) { + u_char tk[MD5_LEN]; + MD5Init(&ctx); + MD5Update(&ctx, (const unsigned char *)key, keylen); + MD5Final(tk, &ctx); + memset((void *) &ctx, 0, sizeof(ctx)); + key = tk; + local_keylen = MD5_LEN; + } + /* start out by storing key in pads */ + memcpy(hkey->hk_ipad, key, local_keylen); + memcpy(hkey->hk_opad, key, local_keylen); + + /* XOR key with hk_ipad and opad values */ + for (i = 0; i < HMAC_LEN; i++) { + hkey->hk_ipad[i] ^= HMAC_IPAD; + hkey->hk_opad[i] ^= HMAC_OPAD; + } + dkey->dk_key_size = local_keylen; + dkey->dk_KEY_struct = (void *) hkey; + return (1); +} + + +/************************************************************************** + * dst_hmac_md5_key_to_file_format + * Encodes an HMAC Key into the portable file format. + * Parameters + * hkey HMAC KEY structure + * buff output buffer + * buff_len size of output buffer + * Return + * 0 Failure - null input hkey + * -1 Failure - not enough space in output area + * N Success - Length of data returned in buff + */ + +static int +dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff, + const unsigned buff_len) +{ + char *bp; + int i; + unsigned len, b_len, key_len; + u_char key[HMAC_LEN]; + HMAC_Key *hkey; + + if (dkey == NULL || dkey->dk_KEY_struct == NULL) + return (0); + if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str)) + return (-1); /* no OR not enough space in output area */ + + hkey = (HMAC_Key *) dkey->dk_KEY_struct; + memset(buff, 0, buff_len); /* just in case */ + /* write file header */ + sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC"); + + bp = (char *) strchr(buff, '\0'); + b_len = buff_len - (bp - buff); + + memset(key, 0, HMAC_LEN); + for (i = 0; i < HMAC_LEN; i++) + key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; + for (i = HMAC_LEN - 1; i >= 0; i--) + if (key[i] != 0) + break; + key_len = i + 1; + + strcat(bp, "Key: "); + bp += strlen("Key: "); + b_len = buff_len - (bp - buff); + + len = b64_ntop(key, key_len, bp, b_len); + if (len < 0) + return (-1); + bp += len; + *(bp++) = '\n'; + *bp = '\0'; + b_len = buff_len - (bp - buff); + + return (buff_len - b_len); +} + + +/************************************************************************** + * dst_hmac_md5_key_from_file_format + * Converts contents of a key file into an HMAC key. + * Parameters + * hkey structure to put key into + * buff buffer containing the encoded key + * buff_len the length of the buffer + * Return + * n >= 0 Foot print of the key converted + * n < 0 Error in conversion + */ + +static int +dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff, + const unsigned buff_len) +{ + const char *p = buff, *eol; + u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode + * it should probably be fixed rather than doing + * this + */ + u_char *tmp; + unsigned key_len, len; + + if (dkey == NULL) + return (-2); + if (buff == NULL) + return (-1); + + memset(key, 0, sizeof(key)); + + if (!dst_s_verify_str(&p, "Key: ")) + return (-3); + + eol = strchr(p, '\n'); + if (eol == NULL) + return (-4); + len = eol - p; + tmp = malloc(len + 2); + memcpy(tmp, p, len); + *(tmp + len) = 0x0; + key_len = b64_pton((char *)tmp, key, HMAC_LEN+1); /* see above */ + SAFE_FREE2(tmp, len + 2); + + if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) { + return (-6); + } + return (0); +} + +/* + * dst_hmac_md5_to_dns_key() + * function to extract hmac key from DST_KEY structure + * intput: + * in_key: HMAC-MD5 key + * output: + * out_str: buffer to write ot + * out_len: size of output buffer + * returns: + * number of bytes written to output buffer + */ +static int +dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str, + const unsigned out_len) +{ + + HMAC_Key *hkey; + int i; + + if (in_key == NULL || in_key->dk_KEY_struct == NULL || + out_len <= in_key->dk_key_size || out_str == NULL) + return (-1); + + hkey = (HMAC_Key *) in_key->dk_KEY_struct; + for (i = 0; i < in_key->dk_key_size; i++) + out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; + return (i); +} + +/************************************************************************** + * dst_hmac_md5_compare_keys + * Compare two keys for equality. + * Return + * 0 The keys are equal + * NON-ZERO The keys are not equal + */ + +static int +dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2) +{ + HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct; + HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct; + return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN); +} + +/************************************************************************** + * dst_hmac_md5_free_key_structure + * Frees all (none) dynamically allocated structures in hkey + */ + +static void * +dst_hmac_md5_free_key_structure(void *key) +{ + HMAC_Key *hkey = key; + SAFE_FREE(hkey); + return (NULL); +} + + +/*************************************************************************** + * dst_hmac_md5_generate_key + * Creates a HMAC key of size size with a maximum size of 63 bytes + * generating a HMAC key larger than 63 bytes makes no sense as that key + * is digested before use. + */ + +static int +dst_hmac_md5_generate_key(DST_KEY *key, const int nothing) +{ + u_char *buff; + int n; + unsigned size, len; + + if (key == NULL || key->dk_alg != KEY_HMAC_MD5) + return (0); + size = (key->dk_key_size + 7) / 8; /* convert to bytes */ + if (size <= 0) + return(0); + + len = size > 64 ? 64 : size; + buff = malloc(len+8); + + n = dst_random(DST_RAND_SEMI, len, buff); + n += dst_random(DST_RAND_KEY, len, buff); + if (n <= len) { /* failed getting anything */ + SAFE_FREE2(buff, len); + return (-1); + } + n = dst_buffer_to_hmac_md5(key, buff, len); + SAFE_FREE2(buff, len); + if (n <= 0) + return (n); + return (1); +} + +/* + * dst_hmac_md5_init() Function to answer set up function pointers for HMAC + * related functions + */ +int +dst_hmac_md5_init() +{ + if (dst_t_func[KEY_HMAC_MD5] != NULL) + return (1); + dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func)); + if (dst_t_func[KEY_HMAC_MD5] == NULL) + return (0); + memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func)); + dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign; + dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify; + dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys; + dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key; + dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure; + dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key; + dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5; + dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format; + dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format; + return (1); +} + +#else +int +dst_hmac_md5_init(){ + return (0); +} +#endif + + + + + + + diff --git a/contrib/dhcp-3.0/dst/md5.h b/contrib/dhcp-3.0/dst/md5.h new file mode 100644 index 0000000000..c886d17bb0 --- /dev/null +++ b/contrib/dhcp-3.0/dst/md5.h @@ -0,0 +1,101 @@ +/* crypto/md/md5.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_MD5_H +#define HEADER_MD5_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MD5_CBLOCK 64 +#define MD5_LBLOCK 16 +#define MD5_BLOCK 16 +#define MD5_LAST_BLOCK 56 +#define MD5_LENGTH_BLOCK 8 +#define MD5_DIGEST_LENGTH 16 + +typedef struct MD5state_st + { + unsigned long A,B,C,D; + unsigned long Nl,Nh; + unsigned long data[MD5_LBLOCK]; + int num; + } MD5_CTX; + +#ifndef NOPROTO +void MD5_Init(MD5_CTX *c); +void MD5_Update(MD5_CTX *c, const unsigned char *data, unsigned long len); +void MD5_Final(unsigned char *md, MD5_CTX *c); +unsigned char *MD5(unsigned char *d, unsigned long n, unsigned char *md); +#else +void MD5_Init(); +void MD5_Update(); +void MD5_Final(); +unsigned char *MD5(); +#endif + +/* to provide backward compatabilty to RSAREF calls ogud@tis.com 1997/11/14 */ +#define MD5Init(c) MD5_Init(c) +#define MD5Update(c,data, len) MD5_Update(c,data,len) +#define MD5Final(md, c) MD5_Final(md, c) +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/dhcp-3.0/dst/md5_dgst.c b/contrib/dhcp-3.0/dst/md5_dgst.c new file mode 100644 index 0000000000..25dd51b759 --- /dev/null +++ b/contrib/dhcp-3.0/dst/md5_dgst.c @@ -0,0 +1,373 @@ +/* crypto/md/md5_dgst.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include +#include +#include +#include +#include "md5_locl.h" +#include "minires/minires.h" + +#ifdef USE_MD5 /* Added by ogud@tis.com 1998/1/26 */ + +const char *MD5_version="MD5 part of SSLeay 0.8.1 19-Jul-1997"; + +/* Implemented from RFC1321 The MD5 Message-Digest Algorithm + */ + +#define INIT_DATA_A (unsigned long)0x67452301L +#define INIT_DATA_B (unsigned long)0xefcdab89L +#define INIT_DATA_C (unsigned long)0x98badcfeL +#define INIT_DATA_D (unsigned long)0x10325476L + +#ifndef NOPROTO +static void md5_block(MD5_CTX *c, unsigned long *p); +#else +static void md5_block(); +#endif + +void MD5_Init(c) +MD5_CTX *c; + { + c->A=INIT_DATA_A; + c->B=INIT_DATA_B; + c->C=INIT_DATA_C; + c->D=INIT_DATA_D; + c->Nl=0; + c->Nh=0; + c->num=0; + } + +void MD5_Update(c, data, len) +MD5_CTX *c; +const register unsigned char *data; +unsigned long len; + { + register ULONG *p; + int sw,sc; + ULONG l; + + if (len == 0) return; + + l=(c->Nl+(len<<3))&0xffffffffL; + /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to + * Wei Dai for pointing it out. */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + p=c->data; + sw=c->num>>2; + sc=c->num&0x03; + + if ((c->num+len) >= MD5_CBLOCK) + { + l= p[sw]; + p_c2l(data,l,sc); + p[sw++]=l; + for (; swnum); + + md5_block(c,p); + c->num=0; + /* drop through and do the rest */ + } + else + { + int ew,ec; + + c->num+=(int)len; + if ((sc+len) < 4) /* ugly, add char's to a word */ + { + l= p[sw]; + p_c2l_p(data,l,sc,len); + p[sw]=l; + } + else + { + ew=(c->num>>2); + ec=(c->num&0x03); + l= p[sw]; + p_c2l(data,l,sc); + p[sw++]=l; + for (; sw < ew; sw++) + { c2l(data,l); p[sw]=l; } + if (ec) + { + c2l_p(data,l,ec); + p[sw]=l; + } + } + return; + } + } + /* we now can process the input data in blocks of MD5_CBLOCK + * chars and save the leftovers to c->data. */ + p=c->data; + while (len >= MD5_CBLOCK) + { +#if defined(L_ENDIAN) || defined(B_ENDIAN) + memcpy(p,data,MD5_CBLOCK); + data+=MD5_CBLOCK; +#ifdef B_ENDIAN + for (sw=(MD5_LBLOCK/4); sw; sw--) + { + Endian_Reverse32(p[0]); + Endian_Reverse32(p[1]); + Endian_Reverse32(p[2]); + Endian_Reverse32(p[3]); + p+=4; + } +#endif +#else + for (sw=(MD5_LBLOCK/4); sw; sw--) + { + c2l(data,l); *(p++)=l; + c2l(data,l); *(p++)=l; + c2l(data,l); *(p++)=l; + c2l(data,l); *(p++)=l; + } +#endif + p=c->data; + md5_block(c,p); + len-=MD5_CBLOCK; + } + sc=(int)len; + c->num=sc; + if (sc) + { + sw=sc>>2; /* words to copy */ +#ifdef L_ENDIAN + p[sw]=0; + memcpy(p,data,sc); +#else + sc&=0x03; + for ( ; sw; sw--) + { c2l(data,l); *(p++)=l; } + c2l_p(data,l,sc); + *p=l; +#endif + } + } + +static void md5_block(c, X) +MD5_CTX *c; +register ULONG *X; + { + register ULONG A,B,C,D; + + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + /* Round 0 */ + LOCL_R0(A,B,C,D,X[ 0], 7,0xd76aa478L); + LOCL_R0(D,A,B,C,X[ 1],12,0xe8c7b756L); + LOCL_R0(C,D,A,B,X[ 2],17,0x242070dbL); + LOCL_R0(B,C,D,A,X[ 3],22,0xc1bdceeeL); + LOCL_R0(A,B,C,D,X[ 4], 7,0xf57c0fafL); + LOCL_R0(D,A,B,C,X[ 5],12,0x4787c62aL); + LOCL_R0(C,D,A,B,X[ 6],17,0xa8304613L); + LOCL_R0(B,C,D,A,X[ 7],22,0xfd469501L); + LOCL_R0(A,B,C,D,X[ 8], 7,0x698098d8L); + LOCL_R0(D,A,B,C,X[ 9],12,0x8b44f7afL); + LOCL_R0(C,D,A,B,X[10],17,0xffff5bb1L); + LOCL_R0(B,C,D,A,X[11],22,0x895cd7beL); + LOCL_R0(A,B,C,D,X[12], 7,0x6b901122L); + LOCL_R0(D,A,B,C,X[13],12,0xfd987193L); + LOCL_R0(C,D,A,B,X[14],17,0xa679438eL); + LOCL_R0(B,C,D,A,X[15],22,0x49b40821L); + /* Round 1 */ + LOCL_R1(A,B,C,D,X[ 1], 5,0xf61e2562L); + LOCL_R1(D,A,B,C,X[ 6], 9,0xc040b340L); + LOCL_R1(C,D,A,B,X[11],14,0x265e5a51L); + LOCL_R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL); + LOCL_R1(A,B,C,D,X[ 5], 5,0xd62f105dL); + LOCL_R1(D,A,B,C,X[10], 9,0x02441453L); + LOCL_R1(C,D,A,B,X[15],14,0xd8a1e681L); + LOCL_R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L); + LOCL_R1(A,B,C,D,X[ 9], 5,0x21e1cde6L); + LOCL_R1(D,A,B,C,X[14], 9,0xc33707d6L); + LOCL_R1(C,D,A,B,X[ 3],14,0xf4d50d87L); + LOCL_R1(B,C,D,A,X[ 8],20,0x455a14edL); + LOCL_R1(A,B,C,D,X[13], 5,0xa9e3e905L); + LOCL_R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L); + LOCL_R1(C,D,A,B,X[ 7],14,0x676f02d9L); + LOCL_R1(B,C,D,A,X[12],20,0x8d2a4c8aL); + /* Round 2 */ + LOCL_R2(A,B,C,D,X[ 5], 4,0xfffa3942L); + LOCL_R2(D,A,B,C,X[ 8],11,0x8771f681L); + LOCL_R2(C,D,A,B,X[11],16,0x6d9d6122L); + LOCL_R2(B,C,D,A,X[14],23,0xfde5380cL); + LOCL_R2(A,B,C,D,X[ 1], 4,0xa4beea44L); + LOCL_R2(D,A,B,C,X[ 4],11,0x4bdecfa9L); + LOCL_R2(C,D,A,B,X[ 7],16,0xf6bb4b60L); + LOCL_R2(B,C,D,A,X[10],23,0xbebfbc70L); + LOCL_R2(A,B,C,D,X[13], 4,0x289b7ec6L); + LOCL_R2(D,A,B,C,X[ 0],11,0xeaa127faL); + LOCL_R2(C,D,A,B,X[ 3],16,0xd4ef3085L); + LOCL_R2(B,C,D,A,X[ 6],23,0x04881d05L); + LOCL_R2(A,B,C,D,X[ 9], 4,0xd9d4d039L); + LOCL_R2(D,A,B,C,X[12],11,0xe6db99e5L); + LOCL_R2(C,D,A,B,X[15],16,0x1fa27cf8L); + LOCL_R2(B,C,D,A,X[ 2],23,0xc4ac5665L); + /* Round 3 */ + LOCL_R3(A,B,C,D,X[ 0], 6,0xf4292244L); + LOCL_R3(D,A,B,C,X[ 7],10,0x432aff97L); + LOCL_R3(C,D,A,B,X[14],15,0xab9423a7L); + LOCL_R3(B,C,D,A,X[ 5],21,0xfc93a039L); + LOCL_R3(A,B,C,D,X[12], 6,0x655b59c3L); + LOCL_R3(D,A,B,C,X[ 3],10,0x8f0ccc92L); + LOCL_R3(C,D,A,B,X[10],15,0xffeff47dL); + LOCL_R3(B,C,D,A,X[ 1],21,0x85845dd1L); + LOCL_R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL); + LOCL_R3(D,A,B,C,X[15],10,0xfe2ce6e0L); + LOCL_R3(C,D,A,B,X[ 6],15,0xa3014314L); + LOCL_R3(B,C,D,A,X[13],21,0x4e0811a1L); + LOCL_R3(A,B,C,D,X[ 4], 6,0xf7537e82L); + LOCL_R3(D,A,B,C,X[11],10,0xbd3af235L); + LOCL_R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL); + LOCL_R3(B,C,D,A,X[ 9],21,0xeb86d391L); + + c->A+=A&0xffffffffL; + c->B+=B&0xffffffffL; + c->C+=C&0xffffffffL; + c->D+=D&0xffffffffL; + } + +void MD5_Final(md, c) +unsigned char *md; +MD5_CTX *c; + { + register int i,j; + register ULONG l; + register ULONG *p; + static unsigned char end[4]={0x80,0x00,0x00,0x00}; + unsigned char *cp=end; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + j=c->num; + i=j>>2; + + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + * Many thanks to Alex Tang for pickup this + * 'potential bug' */ +#ifdef PURIFY + if ((j&0x03) == 0) p[i]=0; +#endif + l=p[i]; + p_c2l(cp,l,j&0x03); + p[i]=l; + i++; + /* i is the next 'undefined word' */ + if (c->num >= MD5_LAST_BLOCK) + { + for (; iNl; + p[MD5_LBLOCK-1]=c->Nh; + md5_block(c,p); + cp=md; + l=c->A; l2c(l,cp); + l=c->B; l2c(l,cp); + l=c->C; l2c(l,cp); + l=c->D; l2c(l,cp); + + /* clear stuff, md5_block may be leaving some stuff on the stack + * but I'm not worried :-) */ + c->num=0; +/* memset((char *)&c,0,sizeof(c));*/ + } + +#ifdef undef +int printit(l) +unsigned long *l; + { + int i,ii; + + for (i=0; i<2; i++) + { + for (ii=0; ii<8; ii++) + { + fprintf(stderr,"%08lx ",l[i*8+ii]); + } + fprintf(stderr,"\n"); + } + } +#endif +#endif /* USE_MD5 */ diff --git a/contrib/dhcp-3.0/dst/md5_locl.h b/contrib/dhcp-3.0/dst/md5_locl.h new file mode 100644 index 0000000000..c52d73377d --- /dev/null +++ b/contrib/dhcp-3.0/dst/md5_locl.h @@ -0,0 +1,189 @@ +/* crypto/md/md5_locl.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include +#include +#include "md5.h" + +#define ULONG unsigned long +#define UCHAR unsigned char +#define UINT unsigned int + +#if defined(NOCONST) +#define const +#endif + +#undef c2l +#define c2l(c,l) (l = ((unsigned long)(*((c)++))) , \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24)) + +#undef p_c2l +#define p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++))); \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + case 3: l|=((unsigned long)(*((c)++)))<<24; \ + } \ + } + +/* NOTE the pointer is not incremented at the end of this */ +#undef c2l_p +#define c2l_p(c,l,n) { \ + l=0; \ + (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<<16; \ + case 2: l|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l|=((unsigned long)(*(--(c)))) ; \ + } \ + } + +#undef p_c2l_p +#define p_c2l_p(c,l,sc,len) { \ + switch (sc) \ + { \ + case 0: l =((unsigned long)(*((c)++))); \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + } \ + } + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#undef l2cn +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +/* A nice byte order reversal from Wei Dai */ +#if defined(WIN32) +/* 5 instructions with rotate instruction, else 9 */ +#define Endian_Reverse32(a) \ + { \ + unsigned long l=(a); \ + (a)=((ROTATE(l,8)&0x00FF00FF)|(ROTATE(l,24)&0xFF00FF00)); \ + } +#else +/* 6 instructions with rotate instruction, else 8 */ +#define Endian_Reverse32(a) \ + { \ + unsigned long l=(a); \ + l=(((l&0xFF00FF00)>>8L)|((l&0x00FF00FF)<<8L)); \ + (a)=ROTATE(l,16L); \ + } +#endif +/* +#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) +#define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) +*/ + +/* As pointed out by Wei Dai , the above can be + * simplified to the code below. Wei attributes these optimisations + * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel. + */ +#define F(x,y,z) ((((y) ^ (z)) & (x)) ^ (z)) +#define G(x,y,z) ((((x) ^ (y)) & (z)) ^ (y)) +#define H(x,y,z) ((x) ^ (y) ^ (z)) +#define I(x,y,z) (((x) | (~(z))) ^ (y)) + +#undef ROTATE +#if defined(WIN32) +#define ROTATE(a,n) _lrotl(a,n) +#else +#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +#define LOCL_R0(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+F((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; };\ + +#define LOCL_R1(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+G((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +#define LOCL_R2(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+H((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +#define LOCL_R3(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+I((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; diff --git a/contrib/dhcp-3.0/dst/prandom.c b/contrib/dhcp-3.0/dst/prandom.c new file mode 100644 index 0000000000..7d9853ed91 --- /dev/null +++ b/contrib/dhcp-3.0/dst/prandom.c @@ -0,0 +1,862 @@ +#ifndef LINT +static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/prandom.c,v 1.1 2001/02/22 07:22:09 mellon Exp $"; +#endif +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define NEED_PRAND_CONF +#include "minires/minires.h" +#include "dst_internal.h" +#include "arpa/nameser.h" + + +#ifndef DST_NUM_HASHES +#define DST_NUM_HASHES 4 +#endif +#ifndef DST_NUMBER_OF_COUNTERS +#define DST_NUMBER_OF_COUNTERS 5 /* 32 * 5 == 160 == SHA(1) > MD5 */ +#endif + +/* + * the constant below is a prime number to make fixed data structues like + * stat and time wrap over blocks. This adds certain uncertanty to what is + * in each digested block. + * The prime number 2879 has the special property that when + * divided by 2,4 and 6 the result is also a prime numbers + */ + +#ifndef DST_RANDOM_BLOCK_SIZE +#define DST_RANDOM_BLOCK_SIZE 2879 +#endif + +/* + * This constant dictatates how many bits we shift to the right before using a + */ +#ifndef DST_SHIFT +#define DST_SHIFT 9 +#endif + +/* + * An initalizer that is as bad as any other with half the bits set + */ +#ifndef DST_RANDOM_PATTERN +#define DST_RANDOM_PATTERN 0x8765CA93 +#endif +/* + * things must have changed in the last 3600 seconds to be used + */ +#define MAX_OLD 3600 + + +/* + * these two data structure are used to process input data into digests, + * + * The first structure is containts a pointer to a DST HMAC key + * the variables accompanying are used for + * step : select every step byte from input data for the hash + * block: number of data elements going into each hash + * digested: number of data elements digested so far + * curr: offset into the next input data for the first byte. + */ +typedef struct hash { + DST_KEY *key; + void *ctx; + int digested, block, step, curr; +} prand_hash; + +/* + * This data structure controlls number of hashes and keeps track of + * overall progress in generating correct number of bytes of output. + * output : array to store the output data in + * needed : how many bytes of output are needed + * filled : number of bytes in output so far. + * bytes : total number of bytes processed by this structure + * file_digest : the HMAC key used to digest files. + */ +typedef struct work { + unsigned needed, filled, bytes; + u_char *output; + prand_hash *hash[DST_NUM_HASHES]; + DST_KEY *file_digest; +} dst_work; + + +/* + * forward function declarations + */ +static int get_dev_random(u_char *output, unsigned size); +static int do_time(dst_work *work); +static int do_ls(dst_work *work); +static int unix_cmd(dst_work *work); +static int digest_file(dst_work *work); + +static void force_hash(dst_work *work, prand_hash *hash); +static int do_hash(dst_work *work, prand_hash *hash, const u_char *input, + unsigned size); +static int my_digest(dst_work *tmp, const u_char *input, unsigned size); +static prand_hash *get_hmac_key(int step, int block); + +static unsigned own_random(dst_work *work); + + +/* + * variables used in the quick random number generator + */ +static u_int32_t ran_val = DST_RANDOM_PATTERN; +static u_int32_t ran_cnt = (DST_RANDOM_PATTERN >> 10); + +/* + * setting the quick_random generator to particular values or if both + * input parameters are 0 then set it to initial vlaues + */ + +void +dst_s_quick_random_set(u_int32_t val, u_int32_t cnt) +{ + ran_val = (val == 0) ? DST_RANDOM_PATTERN : val; + ran_cnt = (cnt == 0) ? (DST_RANDOM_PATTERN >> 10) : cnt; +} + +/* + * this is a quick and random number generator that seems to generate quite + * good distribution of data + */ +u_int32_t +dst_s_quick_random(int inc) +{ + ran_val = ((ran_val >> 13) ^ (ran_val << 19)) ^ + ((ran_val >> 7) ^ (ran_val << 25)); + if (inc > 0) /* only increasing values accepted */ + ran_cnt += inc; + ran_val += ran_cnt++; + return (ran_val); +} + +/* + * get_dev_random: Function to read /dev/random reliably + * this function returns how many bytes where read from the device. + * port_after.h should set the control variable HAVE_DEV_RANDOM + */ +static int +get_dev_random(u_char *output, unsigned size) +{ +#ifdef HAVE_DEV_RANDOM + struct stat st; + int n = 0, fd = -1, s; + + s = stat("/dev/random", &st); + if (s == 0 && S_ISCHR(st.st_mode)) { + if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) != -1) { + if ((n = read(fd, output, size)) < 0) + n = 0; + close(fd); + } + return (n); + } +#endif + return (0); +} + +/* + * Portable way of getting the time values if gettimeofday is missing + * then compile with -DMISSING_GETTIMEOFDAY time() is POSIX compliant but + * gettimeofday() is not. + * Time of day is predictable, we are looking for the randomness that comes + * the last few bits in the microseconds in the timer are hard to predict when + * this is invoked at the end of other operations + */ +struct timeval *mtime; +static int +do_time(dst_work *work) +{ + int cnt = 0; + static u_char tmp[sizeof(struct timeval) + sizeof(struct timezone)]; + struct timezone *zone; + + zone = (struct timezone *) tmp; + mtime = (struct timeval *)(tmp + sizeof(struct timezone)); + gettimeofday(mtime, zone); + cnt = sizeof(tmp); + my_digest(work, tmp, sizeof(tmp)); + + return (cnt); +} + +/* + * this function simulates the ls command, but it uses stat which gives more + * information and is harder to guess + * Each call to this function will visit the next directory on the list of + * directories, in a circular manner. + * return value is the number of bytes added to the temp buffer + * + * do_ls() does not visit subdirectories + * if attacker has access to machine it can guess most of the values seen + * thus it is important to only visit directories that are freqently updated + * Attacker that has access to the network can see network traffic + * when NFS mounted directories are accessed and know exactly the data used + * but may not know exactly in what order data is used. + * Returns the number of bytes that where returned in stat structures + */ +static int +do_ls(dst_work *work) +{ + struct dir_info { + uid_t uid; + gid_t gid; + off_t size; + time_t atime, mtime, ctime; + }; + static struct dir_info dir_info; + struct stat buf; + struct dirent *entry; + static int i = 0; + static unsigned long d_round = 0; + struct timeval tv; + int n = 0, tb_i = 0, out = 0; + unsigned dir_len; + + char file_name[1024]; + u_char tmp_buff[1024]; + DIR *dir = NULL; + + if (dirs[i] == NULL) /* if at the end of the list start over */ + i = 0; + if (stat(dirs[i++], &buf)) /* directory does not exist */ + return (0); + + gettimeofday(&tv,NULL); + if (d_round == 0) + d_round = tv.tv_sec - MAX_OLD; + else if (i==1) /* if starting a new round cut what we accept */ + d_round += (tv.tv_sec - d_round)/2; + + if (buf.st_atime < d_round) + return (0); + + EREPORT(("do_ls i %d filled %4d in_temp %4d\n", + i-1, work->filled, work->in_temp)); + memcpy(tmp_buff, &buf, sizeof(buf)); + tb_i += sizeof(buf); + + + if ((dir = opendir(dirs[i-1])) == NULL)/* open it for read */ + return (0); + strcpy(file_name, dirs[i-1]); + dir_len = strlen(file_name); + file_name[dir_len++] = '/'; + while ((entry = readdir(dir))) { + unsigned len = strlen(entry->d_name); + out += len; + if (my_digest(work, (u_char *)entry->d_name, len)) + break; + + memcpy(&file_name[dir_len], entry->d_name, len); + file_name[dir_len + len] = 0x0; + /* for all entries in dir get the stats */ + if (stat(file_name, &buf) == 0) { + n++; /* count successfull stat calls */ + /* copy non static fields */ + dir_info.uid += buf.st_uid; + dir_info.gid += buf.st_gid; + dir_info.size += buf.st_size; + dir_info.atime += buf.st_atime; + dir_info.mtime += buf.st_mtime; + dir_info.ctime += buf.st_ctime; + out += sizeof(dir_info); + if(my_digest(work, (u_char *)&dir_info, + sizeof(dir_info))) + break; + } + } + closedir(dir); /* done */ + out += do_time(work); /* add a time stamp */ + return (out); +} + + +/* + * unix_cmd() + * this function executes the a command from the cmds[] list of unix commands + * configured in the prand_conf.h file + * return value is the number of bytes added to the randomness temp buffer + * + * it returns the number of bytes that where read in + * if more data is needed at the end time is added to the data. + * This function maintains a state to selects the next command to run + * returns the number of bytes read in from the command + */ +static int +unix_cmd(dst_work *work) +{ + static int cmd_index = 0; + int cnt = 0, n; + FILE *pipe; + u_char buffer[4096]; + + if (cmds[cmd_index] == NULL) + cmd_index = 0; + EREPORT(("unix_cmd() i %d filled %4d in_temp %4d\n", + cmd_index, work->filled, work->in_temp)); + pipe = popen(cmds[cmd_index++], "r"); /* execute the command */ + + while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) { + cnt += n; /* process the output */ + if (my_digest(work, buffer, (unsigned)n)) + break; + /* this adds some randomness to the output */ + cnt += do_time(work); + } + while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) + NULL; /* drain the pipe */ + pclose(pipe); + return (cnt); /* read how many bytes where read in */ +} + +/* + * digest_file() This function will read a file and run hash over it + * input is a file name + */ +static int +digest_file(dst_work *work) +{ + static int f_cnt = 0; + static unsigned long f_round = 0; + FILE *fp; + void *ctx; + const char *name; + int no, i; + struct stat st; + struct timeval tv; + u_char buf[1024]; + + if (f_round == 0 || files[f_cnt] == NULL || work->file_digest == NULL) + if (gettimeofday(&tv, NULL)) /* only do this if needed */ + return (0); + if (f_round == 0) /* first time called set to one hour ago */ + f_round = (tv.tv_sec - MAX_OLD); + name = files[f_cnt++]; + if (files[f_cnt] == NULL) { /* end of list of files */ + if(f_cnt <= 1) /* list is too short */ + return (0); + f_cnt = 0; /* start again on list */ + f_round += (tv.tv_sec - f_round)/2; /* set new cutoff */ + work->file_digest = dst_free_key(work->file_digest); + } + if (work->file_digest == NULL) { + work->file_digest = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, + (u_char *)&tv, sizeof(tv)); + if (work->file_digest == NULL) + return (0); + } + if (access(name, R_OK) || stat(name, &st)) + return (0); /* no such file or not allowed to read it */ + if (strncmp(name, "/proc/", 6) && st.st_mtime < f_round) + return(0); /* file has not changed recently enough */ + if (dst_sign_data(SIG_MODE_INIT, work->file_digest, &ctx, + NULL, 0, NULL, 0)) { + work->file_digest = dst_free_key(work->file_digest); + return (0); + } + if ((fp = fopen(name, "r")) == NULL) + return (0); + for (no = 0; (i = fread(buf, sizeof(*buf), sizeof(buf), fp)) > 0; + no += i) + dst_sign_data(SIG_MODE_UPDATE, work->file_digest, &ctx, + buf, (unsigned)i, NULL, 0); + + fclose(fp); + if (no >= 64) { + i = dst_sign_data(SIG_MODE_FINAL, work->file_digest, &ctx, + NULL, 0, &work->output[work->filled], + DST_HASH_SIZE); + if (i > 0) + work->filled += i; + } + else if (i > 0) + my_digest(work, buf, (unsigned)i); + my_digest(work, (const u_char *)name, strlen(name)); + return (no + strlen(name)); +} + +/* + * function to perform the FINAL and INIT operation on a hash if allowed + */ +static void +force_hash(dst_work *work, prand_hash *hash) +{ + int i = 0; + + /* + * if more than half a block then add data to output + * otherwise adde the digest to the next hash + */ + if ((hash->digested * 2) > hash->block) { + i = dst_sign_data(SIG_MODE_FINAL, hash->key, &hash->ctx, + NULL, 0, &work->output[work->filled], + DST_HASH_SIZE); + + hash->digested = 0; + dst_sign_data(SIG_MODE_INIT, hash->key, &hash->ctx, + NULL, 0, NULL, 0); + if (i > 0) + work->filled += i; + } + return; +} + +/* + * This function takes the input data does the selection of data specified + * by the hash control block. + * The step varialbe in the work sturcture determines which 1/step bytes + * are used, + * + */ +static int +do_hash(dst_work *work, prand_hash *hash, const u_char *input, unsigned size) +{ + const u_char *tmp = input; + u_char *tp, *abuf = (u_char *)0; + int i, n; + unsigned needed, avail, dig, cnt = size; + unsigned tmp_size = 0; + + if (cnt <= 0 || input == NULL) + return (0); + + if (hash->step > 1) { /* if using subset of input data */ + tmp_size = size / hash->step + 2; + abuf = tp = malloc(tmp_size); + tmp = tp; + for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++) + *(tp++) = input[i]; + /* calcutate the starting point in the next input set */ + hash->curr = (hash->step - (i - size)) % hash->step; + } + /* digest the data in block sizes */ + for (n = 0; n < cnt; n += needed) { + avail = (cnt - n); + needed = hash->block - hash->digested; + dig = (avail < needed) ? avail : needed; + dst_sign_data(SIG_MODE_UPDATE, hash->key, &hash->ctx, + &tmp[n], dig, NULL, 0); + hash->digested += dig; + if (hash->digested >= hash->block) + force_hash(work, hash); + if (work->needed < work->filled) { + if (abuf) + SAFE_FREE2(abuf, tmp_size); + return (1); + } + } + if (tmp_size > 0) + SAFE_FREE2(abuf, tmp_size); + return (0); +} + +/* + * Copy data from INPUT for length SIZE into the work-block TMP. + * If we fill the work-block, digest it; then, + * if work-block needs more data, keep filling with the rest of the input. + */ +static int +my_digest(dst_work *work, const u_char *input, unsigned size) +{ + + int i, full = 0; + static unsigned counter; + + counter += size; + /* first do each one of the hashes */ + for (i = 0; i < DST_NUM_HASHES && full == 0; i++) + full = do_hash(work, work->hash[i], input, size) + + do_hash(work, work->hash[i], (u_char *) &counter, + sizeof(counter)); +/* + * if enough data has be generated do final operation on all hashes + * that have enough date for that + */ + for (i = 0; full && (i < DST_NUM_HASHES); i++) + force_hash(work, work->hash[i]); + + return (full); +} + +/* + * this function gets some semi random data and sets that as an HMAC key + * If we get a valid key this function returns that key initalized + * otherwise it returns NULL; + */ +static prand_hash * +get_hmac_key(int step, int block) +{ + + u_char *buff; + int temp = 0, n = 0; + unsigned size = 70; + DST_KEY *new_key = NULL; + prand_hash *new = NULL; + + /* use key that is larger than digest algorithms (64) for key size */ + buff = malloc(size); + if (buff == NULL) + return (NULL); + /* do not memset the allocated memory to get random bytes there */ + /* time of day is somewhat random expecialy in the last bytes */ + gettimeofday((struct timeval *) &buff[n], NULL); + n += sizeof(struct timeval); + +/* get some semi random stuff in here stir it with micro seconds */ + if (n < size) { + temp = dst_s_quick_random((int) buff[n - 1]); + memcpy(&buff[n], &temp, sizeof(temp)); + n += sizeof(temp); + } +/* get the pid of this process and its parent */ + if (n < size) { + temp = (int) getpid(); + memcpy(&buff[n], &temp, sizeof(temp)); + n += sizeof(temp); + } + if (n < size) { + temp = (int) getppid(); + memcpy(&buff[n], &temp, sizeof(temp)); + n += sizeof(temp); + } +/* get the user ID */ + if (n < size) { + temp = (int) getuid(); + memcpy(&buff[n], &temp, sizeof(temp)); + n += sizeof(temp); + } +#ifndef GET_HOST_ID_MISSING + if (n < size) { + temp = (int) gethostid(); + memcpy(&buff[n], &temp, sizeof(temp)); + n += sizeof(temp); + } +#endif +/* get some more random data */ + if (n < size) { + temp = dst_s_quick_random((int) buff[n - 1]); + memcpy(&buff[n], &temp, sizeof(temp)); + n += sizeof(temp); + } +/* covert this into a HMAC key */ + new_key = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, buff, size); + SAFE_FREE(buff); + +/* get the control structure */ + if ((new = malloc(sizeof(prand_hash))) == NULL) + return (NULL); + new->digested = new->curr = 0; + new->step = step; + new->block = block; + new->key = new_key; + if (dst_sign_data(SIG_MODE_INIT, new_key, &new->ctx, NULL, 0, NULL, 0)) + return (NULL); + + return (new); +} + +/* + * own_random() + * This function goes out and from various sources tries to generate enough + * semi random data that a hash function can generate a random data. + * This function will iterate between the two main random source sources, + * information from programs and directores in random order. + * This function return the number of bytes added to the random output buffer. + */ +static unsigned +own_random(dst_work *work) +{ + int dir = 0, b; + int bytes, n, cmd = 0, dig = 0; + int start =0; +/* + * now get the initial seed to put into the quick random function from + * the address of the work structure + */ + bytes = (int) getpid(); +/* + * proceed while needed + */ + while (work->filled < work->needed) { + EREPORT(("own_random r %08x b %6d t %6d f %6d\n", + ran_val, bytes, work->in_temp, work->filled)); +/* pick a random number in the range of 0..7 based on that random number + * perform some operations that yield random data + */ + start = work->filled; + n = (dst_s_quick_random(bytes) >> DST_SHIFT) & 0x07; + switch (n) { + case 0: + case 3: + if (sizeof(cmds) > 2 *sizeof(*cmds)) { + b = unix_cmd(work); + cmd += b; + } + break; + + case 1: + case 7: + if (sizeof(dirs) > 2 *sizeof(*dirs)) { + b = do_ls(work); + dir += b; + } + break; + + case 4: + case 5: + /* retry getting data from /dev/random */ + b = get_dev_random(&work->output[work->filled], + work->needed - work->filled); + if (b > 0) + work->filled += b; + break; + + case 6: + if (sizeof(files) > 2 * sizeof(*files)) { + b = digest_file(work); + dig += b; + } + break; + + case 2: + default: /* to make sure we make some progress */ + work->output[work->filled++] = 0xff & + dst_s_quick_random(bytes); + b = 1; + break; + } + if (b > 0) + bytes += b; + } + return (work->filled); +} + + +/* + * dst_s_random() This function will return the requested number of bytes + * of randomness to the caller it will use the best available sources of + * randomness. + * The current order is to use /dev/random, precalculated randomness, and + * finaly use some system calls and programs to generate semi random data that + * is then digested to generate randomness. + * This function is thread safe as each thread uses its own context, but + * concurrent treads will affect each other as they update shared state + * information. + * It is strongly recommended that this function be called requesting a size + * that is not a multiple of the output of the hash function used. + * + * If /dev/random is not available this function is not suitable to generate + * large ammounts of data, rather it is suitable to seed a pseudo-random + * generator + * Returns the number of bytes put in the output buffer + */ +int +dst_s_random(u_char *output, unsigned size) +{ + int n = 0, i; + unsigned s; + static u_char old_unused[DST_HASH_SIZE * DST_NUM_HASHES]; + static unsigned unused = 0; + + if (size <= 0 || output == NULL) + return (0); + + if (size >= 2048) + return (-1); + /* + * Read from /dev/random + */ + n = get_dev_random(output, size); + /* + * If old data is available and needed use it + */ + if (n < size && unused > 0) { + unsigned need = size - n; + if (unused <= need) { + memcpy(output, old_unused, unused); + n += unused; + unused = 0; + } else { + memcpy(output, old_unused, need); + n += need; + unused -= need; + memcpy(old_unused, &old_unused[need], unused); + } + } + /* + * If we need more use the simulated randomness here. + */ + if (n < size) { + dst_work *my_work = (dst_work *) malloc(sizeof(dst_work)); + if (my_work == NULL) + return (n); + my_work->needed = size - n; + my_work->filled = 0; + my_work->output = (u_char *) malloc(my_work->needed + + DST_HASH_SIZE * + DST_NUM_HASHES); + my_work->file_digest = NULL; + if (my_work->output == NULL) + return (n); + memset(my_work->output, 0x0, my_work->needed); +/* allocate upto 4 different HMAC hash functions out of order */ +#if DST_NUM_HASHES >= 3 + my_work->hash[2] = get_hmac_key(3, DST_RANDOM_BLOCK_SIZE / 2); +#endif +#if DST_NUM_HASHES >= 2 + my_work->hash[1] = get_hmac_key(7, DST_RANDOM_BLOCK_SIZE / 6); +#endif +#if DST_NUM_HASHES >= 4 + my_work->hash[3] = get_hmac_key(5, DST_RANDOM_BLOCK_SIZE / 4); +#endif + my_work->hash[0] = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE); + if (my_work->hash[0] == NULL) /* if failure bail out */ + return (n); + s = own_random(my_work); +/* if more generated than needed store it for future use */ + if (s >= my_work->needed) { + EREPORT(("dst_s_random(): More than needed %d >= %d\n", + s, my_work->needed)); + memcpy(&output[n], my_work->output, my_work->needed); + n += my_work->needed; + /* saving unused data for next time */ + unused = s - my_work->needed; + memcpy(old_unused, &my_work->output[my_work->needed], + unused); + } else { + /* XXXX This should not happen */ + EREPORT(("Not enough %d >= %d\n", s, my_work->needed)); + memcpy(&output[n], my_work->output, s); + n += my_work->needed; + } + +/* delete the allocated work area */ + for (i = 0; i < DST_NUM_HASHES; i++) { + dst_free_key(my_work->hash[i]->key); + SAFE_FREE(my_work->hash[i]); + } + SAFE_FREE(my_work->output); + SAFE_FREE(my_work); + } + return (n); +} + +/* + * A random number generator that is fast and strong + * this random number generator is based on HASHing data, + * the input to the digest function is a collection of + * counters that is incremented between digest operations + * each increment operation amortizes to 2 bits changed in that value + * for 5 counters thus the input will amortize to have 10 bits changed + * The counters are initaly set using the strong random function above + * the HMAC key is selected by the same methold as the HMAC keys for the + * strong random function. + * Each set of counters is used for 2^25 operations + * + * returns the number of bytes written to the output buffer + * or negative number in case of error + */ +int +dst_s_semi_random(u_char *output, unsigned size) +{ + static u_int32_t counter[DST_NUMBER_OF_COUNTERS]; + static u_char semi_old[DST_HASH_SIZE]; + static int semi_loc = 0, cnt = 0; + static unsigned hb_size = 0; + static DST_KEY *my_key = NULL; + prand_hash *hash; + unsigned out = 0; + unsigned i; + int n; + + if (output == NULL || size <= 0) + return (-2); + +/* check if we need a new key */ + if (my_key == NULL || cnt > (1 << 25)) { /* get HMAC KEY */ + if (my_key) + my_key->dk_func->destroy(my_key); + if ((hash = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE)) == NULL) + return (0); + my_key = hash->key; +/* check if the key works stir the new key using some old random data */ + hb_size = dst_sign_data(SIG_MODE_ALL, my_key, NULL, + (u_char *) counter, sizeof(counter), + semi_old, sizeof(semi_old)); + if (hb_size <= 0) { + EREPORT(("dst_s_semi_random() Sign of alg %d failed %d\n", + my_key->dk_alg, hb_size)); + return (-1); + } +/* new set the counters to random values */ + dst_s_random((u_char *) counter, sizeof(counter)); + cnt = 0; + } +/* if old data around use it first */ + if (semi_loc < hb_size) { + if (size <= hb_size - semi_loc) { /* need less */ + memcpy(output, &semi_old[semi_loc], size); + semi_loc += size; + return (size); /* DONE */ + } else { + out = hb_size - semi_loc; + memcpy(output, &semi_old[semi_loc], out); + semi_loc += out; + } + } +/* generate more randome stuff */ + while (out < size) { + /* + * modify at least one bit by incrementing at least one counter + * based on the last bit of the last counter updated update + * the next one. + * minimaly this operation will modify at least 1 bit, + * amortized 2 bits + */ + for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++) + i = (int) counter[n]++; + + i = dst_sign_data(SIG_MODE_ALL, my_key, NULL, + (u_char *) counter, hb_size, + semi_old, sizeof(semi_old)); + if (i != hb_size) + EREPORT(("HMAC SIGNATURE FAILURE %d\n", i)); + cnt++; + if (size - out < i) /* Not all data is needed */ + semi_loc = i = size - out; + memcpy(&output[out], semi_old, i); + out += i; + } + return (out); +} diff --git a/contrib/dhcp-3.0/includes/arpa/nameser.h b/contrib/dhcp-3.0/includes/arpa/nameser.h new file mode 100644 index 0000000000..1b0d2e93bc --- /dev/null +++ b/contrib/dhcp-3.0/includes/arpa/nameser.h @@ -0,0 +1,467 @@ +/* + * Copyright (c) 1983, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +/* + * $Id: nameser.h,v 1.4.2.1 2004/06/10 17:59:31 dhankins Exp $ + */ + +#ifndef _ARPA_NAMESER_H_ +#define _ARPA_NAMESER_H_ + +/* + * Revision information. This is the release date in YYYYMMDD format. + * It can change every day so the right thing to do with it is use it + * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not + * compare for equality; rather, use it to determine whether your libbind.a + * contains a new enough lib/nameser/ to support the feature you need. + */ + +#define __NAMESER 19991006 /* New interface version stamp. */ + +/* + * Define constants based on RFC 883, RFC 1034, RFC 1035 + */ +#define NS_PACKETSZ 512 /* maximum packet size */ +#define NS_MAXDNAME 1025 /* maximum domain name */ +#define NS_MAXCDNAME 255 /* maximum compressed domain name */ +#define NS_MAXLABEL 63 /* maximum length of domain label */ +#define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ +#define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ +#define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ +#define NS_INT32SZ 4 /* #/bytes of data in a u_int32_t */ +#define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */ +#define NS_INT8SZ 1 /* #/bytes of data in a u_int8_t */ +#define NS_INADDRSZ 4 /* IPv4 T_A */ +#define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */ +#define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */ +#define NS_DEFAULTPORT 53 /* For both TCP and UDP. */ + +/* + * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord() + * in synch with it. + */ +typedef enum __ns_sect { + ns_s_qd = 0, /* Query: Question. */ + ns_s_zn = 0, /* Update: Zone. */ + ns_s_an = 1, /* Query: Answer. */ + ns_s_pr = 1, /* Update: Prerequisites. */ + ns_s_ns = 2, /* Query: Name servers. */ + ns_s_ud = 2, /* Update: Update. */ + ns_s_ar = 3, /* Query|Update: Additional records. */ + ns_s_max = 4 +} ns_sect; + +/* + * This is a message handle. It is caller allocated and has no dynamic data. + * This structure is intended to be opaque to all but ns_parse.c, thus the + * leading _'s on the member names. Use the accessor functions, not the _'s. + */ +typedef struct __ns_msg { + const u_int8_t *_msg, *_eom; + u_int16_t _id, _flags, _counts[ns_s_max]; + const u_int8_t *_sections[ns_s_max]; + ns_sect _sect; + int _rrnum; + const u_int8_t *_ptr; +} ns_msg; + +/* Private data structure - do not use from outside library. */ +struct _ns_flagdata { int mask, shift; }; +extern struct _ns_flagdata _ns_flagdata[]; + +/* Accessor macros - this is part of the public interface. */ +#define ns_msg_getflag(handle, flag) ( \ + ((handle)._flags & _ns_flagdata[flag].mask) \ + >> _ns_flagdata[flag].shift \ + ) +#define ns_msg_id(handle) ((handle)._id + 0) +#define ns_msg_base(handle) ((handle)._msg + 0) +#define ns_msg_end(handle) ((handle)._eom + 0) +#define ns_msg_size(handle) ((handle)._eom - (handle)._msg) +#define ns_msg_count(handle, section) ((handle)._counts[section] + 0) + +/* + * This is a parsed record. It is caller allocated and has no dynamic data. + */ +typedef struct __ns_rr { + char name[NS_MAXDNAME]; + u_int16_t type; + u_int16_t rr_class; + u_int32_t ttl; + u_int16_t rdlength; + const u_int8_t *rdata; +} ns_rr; + +/* Accessor macros - this is part of the public interface. */ +#define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".") +#define ns_rr_type(rr) ((ns_type)((rr).type + 0)) +#define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0)) +#define ns_rr_ttl(rr) ((rr).ttl + 0) +#define ns_rr_rdlen(rr) ((rr).rdlength + 0) +#define ns_rr_rdata(rr) ((rr).rdata + 0) + +/* + * These don't have to be in the same order as in the packet flags word, + * and they can even overlap in some cases, but they will need to be kept + * in synch with ns_parse.c:ns_flagdata[]. + */ +typedef enum __ns_flag { + ns_f_qr, /* Question/Response. */ + ns_f_opcode, /* Operation code. */ + ns_f_aa, /* Authoritative Answer. */ + ns_f_tc, /* Truncation occurred. */ + ns_f_rd, /* Recursion Desired. */ + ns_f_ra, /* Recursion Available. */ + ns_f_z, /* MBZ. */ + ns_f_ad, /* Authentic Data (DNSSEC). */ + ns_f_cd, /* Checking Disabled (DNSSEC). */ + ns_f_rcode, /* Response code. */ + ns_f_max +} ns_flag; + +/* + * Currently defined opcodes. + */ +typedef enum __ns_opcode { + ns_o_query = 0, /* Standard query. */ + ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */ + ns_o_status = 2, /* Name server status query (unsupported). */ + /* Opcode 3 is undefined/reserved. */ + ns_o_notify = 4, /* Zone change notification. */ + ns_o_update = 5, /* Zone update message. */ + ns_o_max = 6 +} ns_opcode; + +/* + * Currently defined response codes. + */ +typedef enum __ns_rcode { + ns_r_noerror = 0, /* No error occurred. */ + ns_r_formerr = 1, /* Format error. */ + ns_r_servfail = 2, /* Server failure. */ + ns_r_nxdomain = 3, /* Name error. */ + ns_r_notimpl = 4, /* Unimplemented. */ + ns_r_refused = 5, /* Operation refused. */ + /* these are for BIND_UPDATE */ + ns_r_yxdomain = 6, /* Name exists */ + ns_r_yxrrset = 7, /* RRset exists */ + ns_r_nxrrset = 8, /* RRset does not exist */ + ns_r_notauth = 9, /* Not authoritative for zone */ + ns_r_notzone = 10, /* Zone of record different from zone section */ + ns_r_max = 11, + /* The following are TSIG extended errors */ + ns_r_badsig = 16, + ns_r_badkey = 17, + ns_r_badtime = 18 +} ns_rcode; + +/* BIND_UPDATE */ +typedef enum __ns_update_operation { + ns_uop_delete = 0, + ns_uop_add = 1, + ns_uop_max = 2 +} ns_update_operation; + +/* + * This structure is used for TSIG authenticated messages + */ +struct ns_tsig_key { + char name[NS_MAXDNAME], alg[NS_MAXDNAME]; + unsigned char *data; + unsigned len; +}; +typedef struct ns_tsig_key ns_tsig_key; + +/* + * This structure is used for TSIG authenticated TCP messages + */ +struct ns_tcp_tsig_state { + int counter; + struct dst_key *key; + void *ctx; + unsigned char sig[NS_PACKETSZ]; + unsigned siglen; +}; +typedef struct ns_tcp_tsig_state ns_tcp_tsig_state; + +#define NS_TSIG_FUDGE 300 +#define NS_TSIG_TCP_COUNT 100 +#define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT" + +#define NS_TSIG_ERROR_NO_TSIG -10 +#define NS_TSIG_ERROR_NO_SPACE -11 +#define NS_TSIG_ERROR_FORMERR -12 + +/* + * Currently defined type values for resources and queries. + */ +typedef enum __ns_type { + ns_t_invalid = 0, /* Cookie. */ + ns_t_a = 1, /* Host address. */ + ns_t_ns = 2, /* Authoritative server. */ + ns_t_md = 3, /* Mail destination. */ + ns_t_mf = 4, /* Mail forwarder. */ + ns_t_cname = 5, /* Canonical name. */ + ns_t_soa = 6, /* Start of authority zone. */ + ns_t_mb = 7, /* Mailbox domain name. */ + ns_t_mg = 8, /* Mail group member. */ + ns_t_mr = 9, /* Mail rename name. */ + ns_t_null = 10, /* Null resource record. */ + ns_t_wks = 11, /* Well known service. */ + ns_t_ptr = 12, /* Domain name pointer. */ + ns_t_hinfo = 13, /* Host information. */ + ns_t_minfo = 14, /* Mailbox information. */ + ns_t_mx = 15, /* Mail routing information. */ + ns_t_txt = 16, /* Text strings. */ + ns_t_rp = 17, /* Responsible person. */ + ns_t_afsdb = 18, /* AFS cell database. */ + ns_t_x25 = 19, /* X_25 calling address. */ + ns_t_isdn = 20, /* ISDN calling address. */ + ns_t_rt = 21, /* Router. */ + ns_t_nsap = 22, /* NSAP address. */ + ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ + ns_t_sig = 24, /* Security signature. */ + ns_t_key = 25, /* Security key. */ + ns_t_px = 26, /* X.400 mail mapping. */ + ns_t_gpos = 27, /* Geographical position (withdrawn). */ + ns_t_aaaa = 28, /* Ip6 Address. */ + ns_t_loc = 29, /* Location Information. */ + ns_t_nxt = 30, /* Next domain (security). */ + ns_t_eid = 31, /* Endpoint identifier. */ + ns_t_nimloc = 32, /* Nimrod Locator. */ + ns_t_srv = 33, /* Server Selection. */ + ns_t_atma = 34, /* ATM Address */ + ns_t_naptr = 35, /* Naming Authority PoinTeR */ + ns_t_kx = 36, /* Key Exchange */ + ns_t_cert = 37, /* Certification record */ + ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ + ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ + ns_t_sink = 40, /* Kitchen sink (experimentatl) */ + ns_t_opt = 41, /* EDNS0 option (meta-RR) */ + ns_t_tsig = 250, /* Transaction signature. */ + ns_t_ixfr = 251, /* Incremental zone transfer. */ + ns_t_axfr = 252, /* Transfer zone of authority. */ + ns_t_mailb = 253, /* Transfer mailbox records. */ + ns_t_maila = 254, /* Transfer mail agent records. */ + ns_t_any = 255, /* Wildcard match. */ + ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ + ns_t_max = 65536 +} ns_type; + +/* Exclusively a QTYPE? (not also an RTYPE) */ +#define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \ + (t) == ns_t_mailb || (t) == ns_t_maila) +/* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */ +#define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt) +/* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */ +#define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t)) +#define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr) +#define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \ + (t) == ns_t_zxfr) + +/* + * Values for class field + */ +typedef enum __ns_class { + ns_c_invalid = 0, /* Cookie. */ + ns_c_in = 1, /* Internet. */ + ns_c_2 = 2, /* unallocated/unsupported. */ + ns_c_chaos = 3, /* MIT Chaos-net. */ + ns_c_hs = 4, /* MIT Hesiod. */ + /* Query class values which do not appear in resource records */ + ns_c_none = 254, /* for prereq. sections in update requests */ + ns_c_any = 255, /* Wildcard match. */ + ns_c_max = 65536 +} ns_class; + +/* DNSSEC constants. */ + +typedef enum __ns_key_types { + ns_kt_rsa = 1, /* key type RSA/MD5 */ + ns_kt_dh = 2, /* Diffie Hellman */ + ns_kt_dsa = 3, /* Digital Signature Standard (MANDATORY) */ + ns_kt_private = 254 /* Private key type starts with OID */ +} ns_key_types; + +typedef enum __ns_cert_types { + cert_t_pkix = 1, /* PKIX (X.509v3) */ + cert_t_spki = 2, /* SPKI */ + cert_t_pgp = 3, /* PGP */ + cert_t_url = 253, /* URL private type */ + cert_t_oid = 254 /* OID private type */ +} ns_cert_types; + +/* Flags field of the KEY RR rdata. */ +#define NS_KEY_TYPEMASK 0xC000 /* Mask for "type" bits */ +#define NS_KEY_TYPE_AUTH_CONF 0x0000 /* Key usable for both */ +#define NS_KEY_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */ +#define NS_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */ +#define NS_KEY_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */ +/* The type bits can also be interpreted independently, as single bits: */ +#define NS_KEY_NO_AUTH 0x8000 /* Key unusable for authentication */ +#define NS_KEY_NO_CONF 0x4000 /* Key unusable for confidentiality */ +#define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */ +#define NS_KEY_EXTENDED_FLAGS 0x1000 /* reserved - must be zero */ +#define NS_KEY_RESERVED4 0x0800 /* reserved - must be zero */ +#define NS_KEY_RESERVED5 0x0400 /* reserved - must be zero */ +#define NS_KEY_NAME_TYPE 0x0300 /* these bits determine the type */ +#define NS_KEY_NAME_USER 0x0000 /* key is assoc. with user */ +#define NS_KEY_NAME_ENTITY 0x0200 /* key is assoc. with entity eg host */ +#define NS_KEY_NAME_ZONE 0x0100 /* key is zone key */ +#define NS_KEY_NAME_RESERVED 0x0300 /* reserved meaning */ +#define NS_KEY_RESERVED8 0x0080 /* reserved - must be zero */ +#define NS_KEY_RESERVED9 0x0040 /* reserved - must be zero */ +#define NS_KEY_RESERVED10 0x0020 /* reserved - must be zero */ +#define NS_KEY_RESERVED11 0x0010 /* reserved - must be zero */ +#define NS_KEY_SIGNATORYMASK 0x000F /* key can sign RR's of same name */ +#define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \ + NS_KEY_RESERVED4 | \ + NS_KEY_RESERVED5 | \ + NS_KEY_RESERVED8 | \ + NS_KEY_RESERVED9 | \ + NS_KEY_RESERVED10 | \ + NS_KEY_RESERVED11 ) +#define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */ + +/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */ +#define NS_ALG_MD5RSA 1 /* MD5 with RSA */ +#define NS_ALG_DH 2 /* Diffie Hellman KEY */ +#define NS_ALG_DSA 3 /* DSA KEY */ +#define NS_ALG_DSS NS_ALG_DSA +#define NS_ALG_EXPIRE_ONLY 253 /* No alg, no security */ +#define NS_ALG_PRIVATE_OID 254 /* Key begins with OID giving alg */ + +/* Protocol values */ +/* value 0 is reserved */ +#define NS_KEY_PROT_TLS 1 +#define NS_KEY_PROT_EMAIL 2 +#define NS_KEY_PROT_DNSSEC 3 +#define NS_KEY_PROT_IPSEC 4 +#define NS_KEY_PROT_ANY 255 + +/* Signatures */ +#define NS_MD5RSA_MIN_BITS 512 /* Size of a mod or exp in bits */ +#define NS_MD5RSA_MAX_BITS 2552 + /* Total of binary mod and exp */ +#define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3) + /* Max length of text sig block */ +#define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4) +#define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8) +#define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8) + +#define NS_DSA_SIG_SIZE 41 +#define NS_DSA_MIN_SIZE 213 +#define NS_DSA_MAX_BYTES 405 + +/* Offsets into SIG record rdata to find various values */ +#define NS_SIG_TYPE 0 /* Type flags */ +#define NS_SIG_ALG 2 /* Algorithm */ +#define NS_SIG_LABELS 3 /* How many labels in name */ +#define NS_SIG_OTTL 4 /* Original TTL */ +#define NS_SIG_EXPIR 8 /* Expiration time */ +#define NS_SIG_SIGNED 12 /* Signature time */ +#define NS_SIG_FOOT 16 /* Key footprint */ +#define NS_SIG_SIGNER 18 /* Domain name of who signed it */ + +/* How RR types are represented as bit-flags in NXT records */ +#define NS_NXT_BITS 8 +#define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS))) +#define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS))) +#define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS))) +#define NS_NXT_MAX 127 + +/* + * Inline versions of get/put short/long. Pointer is advanced. + */ +#define NS_GET16(s, cp) do { \ + register u_int8_t *t_cp = (u_int8_t *)(cp); \ + (s) = ((u_int16_t)t_cp[0] << 8) \ + | ((u_int16_t)t_cp[1]) \ + ; \ + (cp) += NS_INT16SZ; \ +} while (0) + +#define NS_GET32(l, cp) do { \ + register u_int8_t *t_cp = (u_int8_t *)(cp); \ + (l) = ((u_int32_t)t_cp[0] << 24) \ + | ((u_int32_t)t_cp[1] << 16) \ + | ((u_int32_t)t_cp[2] << 8) \ + | ((u_int32_t)t_cp[3]) \ + ; \ + (cp) += NS_INT32SZ; \ +} while (0) + +#define NS_PUT16(s, cp) do { \ + register u_int16_t t_s = (u_int16_t)(s); \ + register u_int8_t *t_cp = (u_int8_t *)(cp); \ + *t_cp++ = t_s >> 8; \ + *t_cp = t_s; \ + (cp) += NS_INT16SZ; \ +} while (0) + +#define NS_PUT32(l, cp) do { \ + register u_int32_t t_l = (u_int32_t)(l); \ + register u_int8_t *t_cp = (u_int8_t *)(cp); \ + *t_cp++ = t_l >> 24; \ + *t_cp++ = t_l >> 16; \ + *t_cp++ = t_l >> 8; \ + *t_cp = t_l; \ + (cp) += NS_INT32SZ; \ +} while (0) + +#include + +#endif /* !_ARPA_NAMESER_H_ */ diff --git a/contrib/dhcp-3.0/includes/arpa/nameser_compat.h b/contrib/dhcp-3.0/includes/arpa/nameser_compat.h new file mode 100644 index 0000000000..3ae17f0ee6 --- /dev/null +++ b/contrib/dhcp-3.0/includes/arpa/nameser_compat.h @@ -0,0 +1,183 @@ +/* Copyright (c) 1983, 1989 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * from nameser.h 8.1 (Berkeley) 6/2/93 + * $Id: nameser_compat.h,v 1.2 2000/01/27 23:28:08 mellon Exp $ + */ + +#ifndef _ARPA_NAMESER_COMPAT_ +#define _ARPA_NAMESER_COMPAT_ + +/* + * 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; /* authoritive 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; /* authoritive 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; + +#define PACKETSZ NS_PACKETSZ +#define MAXDNAME NS_MAXDNAME +#define MAXCDNAME NS_MAXCDNAME +#define MAXLABEL NS_MAXLABEL +#define HFIXEDSZ NS_HFIXEDSZ +#define QFIXEDSZ NS_QFIXEDSZ +#define RRFIXEDSZ NS_RRFIXEDSZ +#define INT32SZ NS_INT32SZ +#define INT16SZ NS_INT16SZ +#define INADDRSZ NS_INADDRSZ +#define IN6ADDRSZ NS_IN6ADDRSZ +#define INDIR_MASK NS_CMPRSFLGS +#define NAMESERVER_PORT NS_DEFAULTPORT + +#define S_ZONE ns_s_zn +#define S_PREREQ ns_s_pr +#define S_UPDATE ns_s_ud +#define S_ADDT ns_s_ar + +#define QUERY ns_o_query +#define IQUERY ns_o_iquery +#define STATUS ns_o_status +#define NS_NOTIFY_OP ns_o_notify +#define NS_UPDATE_OP ns_o_update + +#define NOERROR ns_r_noerror +#define FORMERR ns_r_formerr +#define SERVFAIL ns_r_servfail +#define NXDOMAIN ns_r_nxdomain +#define NOTIMP ns_r_notimpl +#define REFUSED ns_r_refused +#define YXDOMAIN ns_r_yxdomain +#define YXRRSET ns_r_yxrrset +#define NXRRSET ns_r_nxrrset +#define NOTAUTH ns_r_notauth +#define NOTZONE ns_r_notzone +/*#define BADSIG ns_r_badsig*/ +/*#define BADKEY ns_r_badkey*/ +/*#define BADTIME ns_r_badtime*/ + + +#define DELETE ns_uop_delete +#define ADD ns_uop_add + +#define T_A ns_t_a +#define T_NS ns_t_ns +#define T_MD ns_t_md +#define T_MF ns_t_mf +#define T_CNAME ns_t_cname +#define T_SOA ns_t_soa +#define T_MB ns_t_mb +#define T_MG ns_t_mg +#define T_MR ns_t_mr +#define T_NULL ns_t_null +#define T_WKS ns_t_wks +#define T_PTR ns_t_ptr +#define T_HINFO ns_t_hinfo +#define T_MINFO ns_t_minfo +#define T_MX ns_t_mx +#define T_TXT ns_t_txt +#define T_RP ns_t_rp +#define T_AFSDB ns_t_afsdb +#define T_X25 ns_t_x25 +#define T_ISDN ns_t_isdn +#define T_RT ns_t_rt +#define T_NSAP ns_t_nsap +#define T_NSAP_PTR ns_t_nsap_ptr +#define T_SIG ns_t_sig +#define T_KEY ns_t_key +#define T_PX ns_t_px +#define T_GPOS ns_t_gpos +#define T_AAAA ns_t_aaaa +#define T_LOC ns_t_loc +#define T_NXT ns_t_nxt +#define T_EID ns_t_eid +#define T_NIMLOC ns_t_nimloc +#define T_SRV ns_t_srv +#define T_ATMA ns_t_atma +#define T_NAPTR ns_t_naptr +#define T_TSIG ns_t_tsig +#define T_IXFR ns_t_ixfr +#define T_AXFR ns_t_axfr +#define T_MAILB ns_t_mailb +#define T_MAILA ns_t_maila +#define T_ANY ns_t_any + +#define C_IN ns_c_in +#define C_CHAOS ns_c_chaos +#define C_HS ns_c_hs +/* BIND_UPDATE */ +#define C_NONE ns_c_none +#define C_ANY ns_c_any + +#define GETSHORT NS_GET16 +#define GETLONG NS_GET32 +#define PUTSHORT NS_PUT16 +#define PUTLONG NS_PUT32 + +#endif /* _ARPA_NAMESER_COMPAT_ */ diff --git a/contrib/dhcp-3.0/includes/cdefs.h b/contrib/dhcp-3.0/includes/cdefs.h new file mode 100644 index 0000000000..3e344c9145 --- /dev/null +++ b/contrib/dhcp-3.0/includes/cdefs.h @@ -0,0 +1,57 @@ +/* cdefs.h + + Standard C definitions... */ + +/* + * Copyright (c) 1995 RadioMail Corporation. All rights reserved. + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software was written for RadioMail Corporation by Ted Lemon + * under a contract with Vixie Enterprises. Further modifications have + * been made for Internet Systems Consortium under a contract + * with Vixie Laboratories. + */ + +#if !defined (__ISC_DHCP_CDEFS_H__) +#define __ISC_DHCP_CDEFS_H__ +/* Delete attributes if not gcc or not the right version of gcc. */ +#if !defined(__GNUC__) || __GNUC__ < 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || defined (darwin) +#define __attribute__(x) +#endif + +#if (defined (__GNUC__) || defined (__STDC__)) && !defined (BROKEN_ANSI) +#define PROTO(x) x +#define KandR(x) +#define ANSI_DECL(x) x +#if defined (__GNUC__) +#define INLINE inline +#else +#define INLINE +#endif /* __GNUC__ */ +#else +#define PROTO(x) () +#define KandR(x) x +#define ANSI_DECL(x) +#define INLINE +#endif /* __GNUC__ || __STDC__ */ +#endif /* __ISC_DHCP_CDEFS_H__ */ diff --git a/contrib/dhcp-3.0/includes/cf/freebsd.h b/contrib/dhcp-3.0/includes/cf/freebsd.h new file mode 100644 index 0000000000..75804de742 --- /dev/null +++ b/contrib/dhcp-3.0/includes/cf/freebsd.h @@ -0,0 +1,142 @@ +/* freebsd.h + + System dependencies for FreeBSD... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern int h_errno; + +#include +#include +#include +#if !defined (INADDR_LOOPBACK) +# define INADDR_LOOPBACK ((u_int32_t)0x7f000001) +#endif + +/* Varargs stuff... */ +#include +#define VA_DOTDOTDOT ... +#define va_dcl +#define VA_start(list, last) va_start (list, last) + +#if defined(__alpha__) || defined(__amd64__) || defined(__ia64__) || \ + defined(__sparc64__) +# define PTRSIZE_64BIT +#endif + +#ifndef _PATH_DHCPD_PID +#define _PATH_DHCPD_PID "/var/run/dhcpd.pid" +#endif +#ifndef _PATH_DHCPD_DB +#define _PATH_DHCPD_DB "/var/db/dhcpd.leases" +#endif +#ifndef _PATH_DHCLIENT_PID +#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid" +#endif +#ifndef _PATH_DHCLIENT_DB +#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases" +#endif + +#define EOL '\n' +#define VOIDPTR void * + +/* Time stuff... */ +#include +#define TIME time_t +#define GET_TIME(x) time ((x)) + +#define HAVE_SA_LEN + +/* socklen_t was first defined on November 24 in sys/socket.h, and + __FreeBSD_version was changed to 400013 on December 4, so if you + get a compile error on this, and you updated between those dates, + that's why. Also, it may be that some 3.x version after 3.4 will + have socklen_t, but no such change has been made so far. */ + +#if __FreeBSD_version < 400013 +#define SOCKLEN_T int +#endif + +#if defined (USE_DEFAULT_NETWORK) +# define USE_BPF +#endif +#define HAVE_MKSTEMP +#ifdef NEED_PRAND_CONF +#ifndef HAVE_DEV_RANDOM + # define HAVE_DEV_RANDOM 1 + #endif /* HAVE_DEV_RANDOM */ + +const char *cmds[] = { + "/bin/ps -axlw 2>&1", + "/usr/sbin/arp -an 2>&1", + "/usr/bin/netstat -an 2>&1", + "/bin/df 2>&1", + "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1", + "/usr/bin/netstat -an 2>&1", + "/usr/bin/dig . soa +ti=1 +retry=0 2>&1", + "/usr/sbin/iostat 2>&1", + "/usr/bin/vmstat 2>&1", + "/usr/bin/w 2>&1", + NULL +}; + +const char *dirs[] = { + "/tmp", + "/usr/tmp", + ".", + "/", + "/var/spool", + "/dev", + "/var/mail", + "/home", + "/usr/home", + NULL +}; + +const char *files[] = { + "/var/log/messages", + "/var/log/wtmp", + "/var/log/lastlog", + NULL +}; +#endif /* NEED_PRAND_CONF */ diff --git a/contrib/dhcp-3.0/includes/ctrace.h b/contrib/dhcp-3.0/includes/ctrace.h new file mode 100644 index 0000000000..6b0ad756c1 --- /dev/null +++ b/contrib/dhcp-3.0/includes/ctrace.h @@ -0,0 +1,77 @@ +/* trace.h + + Definitions for dhcp tracing facility... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon, as part of a project for Nominum, Inc. To learn more + * about Internet Systems Consortium, see http://www.isc.org/. To + * learn more about Nominum, Inc., see ``http://www.nominum.com''. + */ + +typedef struct { + struct in_addr primary_address; + u_int32_t index; + struct hardware hw_address; + char name [IFNAMSIZ]; +} trace_interface_packet_t; + +typedef struct { + u_int32_t index; + struct iaddr from; + u_int16_t from_port; + struct hardware hfrom; + u_int8_t havehfrom; +} trace_inpacket_t; + +typedef struct { + u_int32_t index; + struct iaddr from; + struct iaddr to; + u_int16_t to_port; + struct hardware hto; + u_int8_t havehto; +} trace_outpacket_t; + +void trace_interface_register (trace_type_t *, struct interface_info *); +void trace_interface_input (trace_type_t *, unsigned, char *); +void trace_interface_stop (trace_type_t *); +void trace_inpacket_stash (struct interface_info *, + struct dhcp_packet *, unsigned, unsigned int, + struct iaddr, struct hardware *); +void trace_inpacket_input (trace_type_t *, unsigned, char *); +void trace_inpacket_stop (trace_type_t *); +void trace_outpacket_input (trace_type_t *, unsigned, char *); +void trace_outpacket_stop (trace_type_t *); +ssize_t trace_packet_send (struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *); +void trace_icmp_input_input (trace_type_t *, unsigned, char *); +void trace_icmp_input_stop (trace_type_t *); +void trace_icmp_output_input (trace_type_t *, unsigned, char *); +void trace_icmp_output_stop (trace_type_t *); +void trace_seed_stash (trace_type_t *, unsigned); +void trace_seed_input (trace_type_t *, unsigned, char *); +void trace_seed_stop (trace_type_t *); diff --git a/contrib/dhcp-3.0/includes/dhcp.h b/contrib/dhcp-3.0/includes/dhcp.h new file mode 100644 index 0000000000..76dd07bb41 --- /dev/null +++ b/contrib/dhcp-3.0/includes/dhcp.h @@ -0,0 +1,186 @@ +/* dhcp.h + + Protocol structures... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises. To learn more + * about Internet Systems Consortium, see ``http://www.isc.org''. + * To learn more about Vixie Enterprises, see ``http://www.vix.com''. + */ + +#define DHCP_UDP_OVERHEAD (14 + /* Ethernet header */ \ + 20 + /* IP header */ \ + 8) /* UDP header */ +#define DHCP_SNAME_LEN 64 +#define DHCP_FILE_LEN 128 +#define DHCP_FIXED_NON_UDP 236 +#define DHCP_FIXED_LEN (DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD) + /* Everything but options. */ +#define DHCP_MTU_MAX 1500 +#define DHCP_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN) + +#define BOOTP_MIN_LEN 300 +#define DHCP_MIN_LEN 548 + +struct dhcp_packet { + u_int8_t op; /* 0: Message opcode/type */ + u_int8_t htype; /* 1: Hardware addr type (net/if_types.h) */ + u_int8_t hlen; /* 2: Hardware addr length */ + u_int8_t hops; /* 3: Number of relay agent hops from client */ + u_int32_t xid; /* 4: Transaction ID */ + u_int16_t secs; /* 8: Seconds since client started looking */ + u_int16_t flags; /* 10: Flag bits */ + struct in_addr ciaddr; /* 12: Client IP address (if already in use) */ + struct in_addr yiaddr; /* 16: Client IP address */ + struct in_addr siaddr; /* 18: IP address of next server to talk to */ + struct in_addr giaddr; /* 20: DHCP relay agent IP address */ + unsigned char chaddr [16]; /* 24: Client hardware address */ + char sname [DHCP_SNAME_LEN]; /* 40: Server name */ + char file [DHCP_FILE_LEN]; /* 104: Boot filename */ + unsigned char options [DHCP_OPTION_LEN]; + /* 212: Optional parameters + (actual length dependent on MTU). */ +}; + +/* BOOTP (rfc951) message types */ +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +/* Possible values for flags field... */ +#define BOOTP_BROADCAST 32768L + +/* Possible values for hardware type (htype) field... */ +#define HTYPE_ETHER 1 /* Ethernet 10Mbps */ +#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ +#define HTYPE_FDDI 8 /* FDDI... */ + +/* Magic cookie validating dhcp options field (and bootp vendor + extensions field). */ +#define DHCP_OPTIONS_COOKIE "\143\202\123\143" + +/* DHCP Option codes: */ + +#define DHO_PAD 0 +#define DHO_SUBNET_MASK 1 +#define DHO_TIME_OFFSET 2 +#define DHO_ROUTERS 3 +#define DHO_TIME_SERVERS 4 +#define DHO_NAME_SERVERS 5 +#define DHO_DOMAIN_NAME_SERVERS 6 +#define DHO_LOG_SERVERS 7 +#define DHO_COOKIE_SERVERS 8 +#define DHO_LPR_SERVERS 9 +#define DHO_IMPRESS_SERVERS 10 +#define DHO_RESOURCE_LOCATION_SERVERS 11 +#define DHO_HOST_NAME 12 +#define DHO_BOOT_SIZE 13 +#define DHO_MERIT_DUMP 14 +#define DHO_DOMAIN_NAME 15 +#define DHO_SWAP_SERVER 16 +#define DHO_ROOT_PATH 17 +#define DHO_EXTENSIONS_PATH 18 +#define DHO_IP_FORWARDING 19 +#define DHO_NON_LOCAL_SOURCE_ROUTING 20 +#define DHO_POLICY_FILTER 21 +#define DHO_MAX_DGRAM_REASSEMBLY 22 +#define DHO_DEFAULT_IP_TTL 23 +#define DHO_PATH_MTU_AGING_TIMEOUT 24 +#define DHO_PATH_MTU_PLATEAU_TABLE 25 +#define DHO_INTERFACE_MTU 26 +#define DHO_ALL_SUBNETS_LOCAL 27 +#define DHO_BROADCAST_ADDRESS 28 +#define DHO_PERFORM_MASK_DISCOVERY 29 +#define DHO_MASK_SUPPLIER 30 +#define DHO_ROUTER_DISCOVERY 31 +#define DHO_ROUTER_SOLICITATION_ADDRESS 32 +#define DHO_STATIC_ROUTES 33 +#define DHO_TRAILER_ENCAPSULATION 34 +#define DHO_ARP_CACHE_TIMEOUT 35 +#define DHO_IEEE802_3_ENCAPSULATION 36 +#define DHO_DEFAULT_TCP_TTL 37 +#define DHO_TCP_KEEPALIVE_INTERVAL 38 +#define DHO_TCP_KEEPALIVE_GARBAGE 39 +#define DHO_NIS_DOMAIN 40 +#define DHO_NIS_SERVERS 41 +#define DHO_NTP_SERVERS 42 +#define DHO_VENDOR_ENCAPSULATED_OPTIONS 43 +#define DHO_NETBIOS_NAME_SERVERS 44 +#define DHO_NETBIOS_DD_SERVER 45 +#define DHO_NETBIOS_NODE_TYPE 46 +#define DHO_NETBIOS_SCOPE 47 +#define DHO_FONT_SERVERS 48 +#define DHO_X_DISPLAY_MANAGER 49 +#define DHO_DHCP_REQUESTED_ADDRESS 50 +#define DHO_DHCP_LEASE_TIME 51 +#define DHO_DHCP_OPTION_OVERLOAD 52 +#define DHO_DHCP_MESSAGE_TYPE 53 +#define DHO_DHCP_SERVER_IDENTIFIER 54 +#define DHO_DHCP_PARAMETER_REQUEST_LIST 55 +#define DHO_DHCP_MESSAGE 56 +#define DHO_DHCP_MAX_MESSAGE_SIZE 57 +#define DHO_DHCP_RENEWAL_TIME 58 +#define DHO_DHCP_REBINDING_TIME 59 +#define DHO_VENDOR_CLASS_IDENTIFIER 60 +#define DHO_DHCP_CLIENT_IDENTIFIER 61 +#define DHO_NWIP_DOMAIN_NAME 62 +#define DHO_NWIP_SUBOPTIONS 63 +#define DHO_USER_CLASS 77 +#define DHO_FQDN 81 +#define DHO_DHCP_AGENT_OPTIONS 82 +#define DHO_SUBNET_SELECTION 118 /* RFC3011! */ +/* The DHO_AUTHENTICATE option is not a standard yet, so I've + allocated an option out of the "local" option space for it on a + temporary basis. Once an option code number is assigned, I will + immediately and shamelessly break this, so don't count on it + continuing to work. */ +#define DHO_AUTHENTICATE 210 + +#define DHO_END 255 + +/* DHCP message types. */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +/* Relay Agent Information option subtypes: */ +#define RAI_CIRCUIT_ID 1 +#define RAI_REMOTE_ID 2 +#define RAI_AGENT_ID 3 + +/* FQDN suboptions: */ +#define FQDN_NO_CLIENT_UPDATE 1 +#define FQDN_SERVER_UPDATE 2 +#define FQDN_ENCODED 3 +#define FQDN_RCODE1 4 +#define FQDN_RCODE2 5 +#define FQDN_HOSTNAME 6 +#define FQDN_DOMAINNAME 7 +#define FQDN_FQDN 8 +#define FQDN_SUBOPTION_COUNT 8 diff --git a/contrib/dhcp-3.0/includes/dhcpd.h b/contrib/dhcp-3.0/includes/dhcpd.h new file mode 100644 index 0000000000..37dba4c7df --- /dev/null +++ b/contrib/dhcp-3.0/includes/dhcpd.h @@ -0,0 +1,2624 @@ +/* dhcpd.h + + Definitions for dhcpd... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef __CYGWIN32__ +#include +#include +#include +#include +#include + +#include +#else +#define fd_set cygwin_fd_set +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdefs.h" +#include "osdep.h" + +#include "arpa/nameser.h" +#if defined (NSUPDATE) +# include "minires/minires.h" +#endif + +struct hash_table; +typedef struct hash_table group_hash_t; +typedef struct hash_table universe_hash_t; +typedef struct hash_table option_hash_t; +typedef struct hash_table dns_zone_hash_t; +typedef struct hash_table lease_hash_t; +typedef struct hash_table host_hash_t; +typedef struct hash_table class_hash_t; + +#include "dhcp.h" +#include "statement.h" +#include "tree.h" +#include "inet.h" +#include "dhctoken.h" + +#include +#include + +#if !defined (OPTION_HASH_SIZE) +# define OPTION_HASH_SIZE 17 +# define OPTION_HASH_PTWO 32 /* Next power of two above option hash. */ +# define OPTION_HASH_EXP 5 /* The exponent for that power of two. */ +#endif + +#define compute_option_hash(x) \ + (((x) & (OPTION_HASH_PTWO - 1)) + \ + (((x) >> OPTION_HASH_EXP) & \ + (OPTION_HASH_PTWO - 1))) % OPTION_HASH_SIZE; + +enum dhcp_shutdown_state { + shutdown_listeners, + shutdown_omapi_connections, + shutdown_drop_omapi_connections, + shutdown_dhcp, + shutdown_done +}; + +/* Client FQDN option, failover FQDN option, etc. */ +typedef struct { + u_int8_t codes [2]; + unsigned length; + u_int8_t *data; +} ddns_fqdn_t; + +#include "failover.h" + +/* A parsing context. */ + +struct parse { + int lexline; + int lexchar; + char *token_line; + char *prev_line; + char *cur_line; + const char *tlname; + int eol_token; + + char line1 [81]; + char line2 [81]; + int lpos; + int line; + int tlpos; + int tline; + enum dhcp_token token; + int ugflag; + char *tval; + int tlen; + char tokbuf [1500]; + +#ifdef OLD_LEXER + char comments [4096]; + int comment_index; +#endif + int warnings_occurred; + int file; + char *inbuf; + unsigned bufix, buflen; + unsigned bufsiz; +}; + +/* Variable-length array of data. */ + +struct string_list { + struct string_list *next; + char string [1]; +}; + +/* A name server, from /etc/resolv.conf. */ +struct name_server { + struct name_server *next; + struct sockaddr_in addr; + TIME rcdate; +}; + +/* A domain search list element. */ +struct domain_search_list { + struct domain_search_list *next; + char *domain; + TIME rcdate; +}; + +/* Option tag structures are used to build chains of option tags, for + when we're sure we're not going to have enough of them to justify + maintaining an array. */ + +struct option_tag { + struct option_tag *next; + u_int8_t data [1]; +}; + +/* An agent option structure. We need a special structure for the + Relay Agent Information option because if more than one appears in + a message, we have to keep them seperate. */ + +struct agent_options { + struct agent_options *next; + int length; + struct option_tag *first; +}; + +struct option_cache { + int refcnt; + struct option_cache *next; + struct expression *expression; + struct option *option; + struct data_string data; +}; + +struct option_state { + int refcnt; + int universe_count; + int site_universe; + int site_code_min; + VOIDPTR universes [1]; +}; + +/* A dhcp packet and the pointers to its option values. */ +struct packet { + struct dhcp_packet *raw; + int refcnt; + unsigned packet_length; + int packet_type; + int options_valid; + int client_port; + struct iaddr client_addr; + struct interface_info *interface; /* Interface on which packet + was received. */ + struct hardware *haddr; /* Physical link address + of local sender (maybe gateway). */ + + /* Information for relay agent options (see + draft-ietf-dhc-agent-options-xx.txt). */ + u_int8_t *circuit_id; /* Circuit ID of client connection. */ + int circuit_id_len; + u_int8_t *remote_id; /* Remote ID of client. */ + int remote_id_len; + + int got_requested_address; /* True if client sent the + dhcp-requested-address option. */ + + struct shared_network *shared_network; + struct option_state *options; + +#if !defined (PACKET_MAX_CLASSES) +# define PACKET_MAX_CLASSES 5 +#endif + int class_count; + struct class *classes [PACKET_MAX_CLASSES]; + + int known; + int authenticated; +}; + +/* A network interface's MAC address. */ + +struct hardware { + u_int8_t hlen; + u_int8_t hbuf [17]; +}; + +typedef enum { + server_startup = 0, + server_running = 1, + server_shutdown = 2, + server_hibernate = 3, + server_awaken = 4 +} control_object_state_t; + +typedef struct { + OMAPI_OBJECT_PREAMBLE; + control_object_state_t state; +} dhcp_control_object_t; + +/* Lease states: */ +typedef enum { + FTS_FREE = 1, + FTS_ACTIVE = 2, + FTS_EXPIRED = 3, + FTS_RELEASED = 4, + FTS_ABANDONED = 5, + FTS_RESET = 6, + FTS_BACKUP = 7 +} binding_state_t; + +/* FTS_LAST is the highest value that is valid for a lease binding state. */ +#define FTS_LAST FTS_BACKUP + +/* A dhcp lease declaration structure. */ +struct lease { + OMAPI_OBJECT_PREAMBLE; + struct lease *next; + struct lease *n_uid, *n_hw; + + struct iaddr ip_addr; + TIME starts, ends, timestamp, sort_time; + char *client_hostname; + struct binding_scope *scope; + struct host_decl *host; + struct subnet *subnet; + struct pool *pool; + struct class *billing_class; + struct option_chain_head *agent_options; + + struct executable_statement *on_expiry; + struct executable_statement *on_commit; + struct executable_statement *on_release; + + unsigned char *uid; + unsigned short uid_len; + unsigned short uid_max; + unsigned char uid_buf [7]; + struct hardware hardware_addr; + + u_int8_t flags; +# define STATIC_LEASE 1 +# define BOOTP_LEASE 2 +# define PERSISTENT_FLAGS (ON_ACK_QUEUE | ON_UPDATE_QUEUE) +# define MS_NULL_TERMINATION 8 +# define ON_UPDATE_QUEUE 16 +# define ON_ACK_QUEUE 32 +# define UNICAST_BROADCAST_HACK 64 +# define ON_DEFERRED_QUEUE 128 +# define EPHEMERAL_FLAGS (MS_NULL_TERMINATION | \ + UNICAST_BROADCAST_HACK) + + binding_state_t __attribute__ ((mode (__byte__))) binding_state; + binding_state_t __attribute__ ((mode (__byte__))) next_binding_state; + binding_state_t __attribute__ ((mode (__byte__))) desired_binding_state; + + struct lease_state *state; + + TIME tstp; /* Time sent to partner. */ + TIME tsfp; /* Time sent from partner. */ + TIME cltt; /* Client last transaction time. */ + struct lease *next_pending; +}; + +struct lease_state { + struct lease_state *next; + + struct interface_info *ip; + + struct packet *packet; /* The incoming packet. */ + + TIME offered_expiry; + + struct option_state *options; + struct data_string parameter_request_list; + int max_message_size; + u_int32_t expiry, renewal, rebind; + struct data_string filename, server_name; + int got_requested_address; + int got_server_identifier; + struct shared_network *shared_network; /* Shared network of interface + on which request arrived. */ + + u_int32_t xid; + u_int16_t secs; + u_int16_t bootp_flags; + struct in_addr ciaddr; + struct in_addr siaddr; + struct in_addr giaddr; + u_int8_t hops; + u_int8_t offer; + struct iaddr from; +}; + +#define ROOT_GROUP 0 +#define HOST_DECL 1 +#define SHARED_NET_DECL 2 +#define SUBNET_DECL 3 +#define CLASS_DECL 4 +#define GROUP_DECL 5 +#define POOL_DECL 6 + +/* Possible modes in which discover_interfaces can run. */ + +#define DISCOVER_RUNNING 0 +#define DISCOVER_SERVER 1 +#define DISCOVER_UNCONFIGURED 2 +#define DISCOVER_RELAY 3 +#define DISCOVER_REQUESTED 4 + +/* Server option names. */ + +#define SV_DEFAULT_LEASE_TIME 1 +#define SV_MAX_LEASE_TIME 2 +#define SV_MIN_LEASE_TIME 3 +#define SV_BOOTP_LEASE_CUTOFF 4 +#define SV_BOOTP_LEASE_LENGTH 5 +#define SV_BOOT_UNKNOWN_CLIENTS 6 +#define SV_DYNAMIC_BOOTP 7 +#define SV_ALLOW_BOOTP 8 +#define SV_ALLOW_BOOTING 9 +#define SV_ONE_LEASE_PER_CLIENT 10 +#define SV_GET_LEASE_HOSTNAMES 11 +#define SV_USE_HOST_DECL_NAMES 12 +#define SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 13 +#define SV_MIN_SECS 14 +#define SV_FILENAME 15 +#define SV_SERVER_NAME 16 +#define SV_NEXT_SERVER 17 +#define SV_AUTHORITATIVE 18 +#define SV_VENDOR_OPTION_SPACE 19 +#define SV_ALWAYS_REPLY_RFC1048 20 +#define SV_SITE_OPTION_SPACE 21 +#define SV_ALWAYS_BROADCAST 22 +#define SV_DDNS_DOMAIN_NAME 23 +#define SV_DDNS_HOST_NAME 24 +#define SV_DDNS_REV_DOMAIN_NAME 25 +#define SV_LEASE_FILE_NAME 26 +#define SV_PID_FILE_NAME 27 +#define SV_DUPLICATES 28 +#define SV_DECLINES 29 +#define SV_DDNS_UPDATES 30 +#define SV_OMAPI_PORT 31 +#define SV_LOCAL_PORT 32 +#define SV_LIMITED_BROADCAST_ADDRESS 33 +#define SV_REMOTE_PORT 34 +#define SV_LOCAL_ADDRESS 35 +#define SV_OMAPI_KEY 36 +#define SV_STASH_AGENT_OPTIONS 37 +#define SV_DDNS_TTL 38 +#define SV_DDNS_UPDATE_STYLE 39 +#define SV_CLIENT_UPDATES 40 +#define SV_UPDATE_OPTIMIZATION 41 +#define SV_PING_CHECKS 42 +#define SV_UPDATE_STATIC_LEASES 43 +#define SV_LOG_FACILITY 44 +#define SV_DO_FORWARD_UPDATES 45 +#define SV_PING_TIMEOUT 46 + +#if !defined (DEFAULT_PING_TIMEOUT) +# define DEFAULT_PING_TIMEOUT 1 +#endif + +#if !defined (DEFAULT_DEFAULT_LEASE_TIME) +# define DEFAULT_DEFAULT_LEASE_TIME 43200 +#endif + +#if !defined (DEFAULT_MIN_LEASE_TIME) +# define DEFAULT_MIN_LEASE_TIME 0 +#endif + +#if !defined (DEFAULT_MAX_LEASE_TIME) +# define DEFAULT_MAX_LEASE_TIME 86400 +#endif + +#if !defined (DEFAULT_DDNS_TTL) +# define DEFAULT_DDNS_TTL 3600 +#endif + +/* Client option names */ + +#define CL_TIMEOUT 1 +#define CL_SELECT_INTERVAL 2 +#define CL_REBOOT_TIMEOUT 3 +#define CL_RETRY_INTERVAL 4 +#define CL_BACKOFF_CUTOFF 5 +#define CL_INITIAL_INTERVAL 6 +#define CL_BOOTP_POLICY 7 +#define CL_SCRIPT_NAME 8 +#define CL_REQUESTED_OPTIONS 9 +#define CL_REQUESTED_LEASE_TIME 10 +#define CL_SEND_OPTIONS 11 +#define CL_MEDIA 12 +#define CL_REJECT_LIST 13 + +#ifndef CL_DEFAULT_TIMEOUT +# define CL_DEFAULT_TIMEOUT 60 +#endif + +#ifndef CL_DEFAULT_SELECT_INTERVAL +# define CL_DEFAULT_SELECT_INTERVAL 0 +#endif + +#ifndef CL_DEFAULT_REBOOT_TIMEOUT +# define CL_DEFAULT_REBOOT_TIMEOUT 10 +#endif + +#ifndef CL_DEFAULT_RETRY_INTERVAL +# define CL_DEFAULT_RETRY_INTERVAL 300 +#endif + +#ifndef CL_DEFAULT_BACKOFF_CUTOFF +# define CL_DEFAULT_BACKOFF_CUTOFF 120 +#endif + +#ifndef CL_DEFAULT_INITIAL_INTERVAL +# define CL_DEFAULT_INITIAL_INTERVAL 10 +#endif + +#ifndef CL_DEFAULT_BOOTP_POLICY +# define CL_DEFAULT_BOOTP_POLICY P_ACCEPT +#endif + +#ifndef CL_DEFAULT_REQUESTED_OPTIONS +# define CL_DEFAULT_REQUESTED_OPTIONS \ + { DHO_SUBNET_MASK, \ + DHO_BROADCAST_ADDRESS, \ + DHO_TIME_OFFSET, \ + DHO_ROUTERS, \ + DHO_DOMAIN_NAME, \ + DHO_DOMAIN_NAME_SERVERS, \ + DHO_HOST_NAME } +#endif + +struct group_object { + OMAPI_OBJECT_PREAMBLE; + + struct group_object *n_dynamic; + struct group *group; + char *name; + int flags; +#define GROUP_OBJECT_DELETED 1 +#define GROUP_OBJECT_DYNAMIC 2 +#define GROUP_OBJECT_STATIC 4 +}; + +/* Group of declarations that share common parameters. */ +struct group { + struct group *next; + + int refcnt; + struct group_object *object; + struct subnet *subnet; + struct shared_network *shared_network; + int authoritative; + struct executable_statement *statements; +}; + +/* A dhcp host declaration structure. */ +struct host_decl { + OMAPI_OBJECT_PREAMBLE; + struct host_decl *n_ipaddr; + struct host_decl *n_dynamic; + char *name; + struct hardware interface; + struct data_string client_identifier; + struct option_cache *fixed_addr; + struct group *group; + struct group_object *named_group; + struct data_string auth_key_id; + int flags; +#define HOST_DECL_DELETED 1 +#define HOST_DECL_DYNAMIC 2 +#define HOST_DECL_STATIC 4 +}; + +struct permit { + struct permit *next; + enum { + permit_unknown_clients, + permit_known_clients, + permit_authenticated_clients, + permit_unauthenticated_clients, + permit_all_clients, + permit_dynamic_bootp_clients, + permit_class + } type; + struct class *class; +}; + +struct pool { + OMAPI_OBJECT_PREAMBLE; + struct pool *next; + struct group *group; + struct shared_network *shared_network; + struct permit *permit_list; + struct permit *prohibit_list; + struct lease *active; + struct lease *expired; + struct lease *free; + struct lease *backup; + struct lease *abandoned; + TIME next_event_time; + int lease_count; + int free_leases; + int backup_leases; + int index; +#if defined (FAILOVER_PROTOCOL) + dhcp_failover_state_t *failover_peer; +#endif +}; + +struct shared_network { + OMAPI_OBJECT_PREAMBLE; + struct shared_network *next; + char *name; + struct subnet *subnets; + struct interface_info *interface; + struct pool *pools; + struct group *group; +#if defined (FAILOVER_PROTOCOL) + dhcp_failover_state_t *failover_peer; +#endif +}; + +struct subnet { + OMAPI_OBJECT_PREAMBLE; + struct subnet *next_subnet; + struct subnet *next_sibling; + struct shared_network *shared_network; + struct interface_info *interface; + struct iaddr interface_address; + struct iaddr net; + struct iaddr netmask; + + struct group *group; +}; + +struct collection { + struct collection *next; + + const char *name; + struct class *classes; +}; + +/* XXX classes must be reference-counted. */ +struct class { + OMAPI_OBJECT_PREAMBLE; + struct class *nic; /* Next in collection. */ + struct class *superclass; /* Set for spawned classes only. */ + char *name; /* Not set for spawned classes. */ + + /* A class may be configured to permit a limited number of leases. */ + int lease_limit; + int leases_consumed; + struct lease **billed_leases; + + /* If nonzero, class has not been saved since it was last + modified. */ + int dirty; + + /* Hash table containing subclasses. */ + class_hash_t *hash; + struct data_string hash_string; + + /* Expression used to match class. */ + struct expression *expr; + + /* Expression used to compute subclass identifiers for spawning + and to do subclass matching. */ + struct expression *submatch; + int spawning; + + struct group *group; + + /* Statements to execute if class matches. */ + struct executable_statement *statements; +}; + +/* DHCP client lease structure... */ +struct client_lease { + struct client_lease *next; /* Next lease in list. */ + TIME expiry, renewal, rebind; /* Lease timeouts. */ + struct iaddr address; /* Address being leased. */ + char *server_name; /* Name of boot server. */ + char *filename; /* Name of file we're supposed to boot. */ + struct string_list *medium; /* Network medium. */ + struct auth_key *key; /* Key used in basic DHCP authentication. */ + + unsigned int is_static : 1; /* If set, lease is from config file. */ + unsigned int is_bootp: 1; /* If set, lease was acquired with BOOTP. */ + + struct option_state *options; /* Options supplied with lease. */ +}; + +/* Possible states in which the client can be. */ +enum dhcp_state { + S_REBOOTING = 1, + S_INIT = 2, + S_SELECTING = 3, + S_REQUESTING = 4, + S_BOUND = 5, + S_RENEWING = 6, + S_REBINDING = 7, + S_STOPPED = 8 +}; + +/* Authentication and BOOTP policy possibilities (not all values work + for each). */ +enum policy { P_IGNORE, P_ACCEPT, P_PREFER, P_REQUIRE, P_DONT }; + +/* Configuration information from the config file... */ +struct client_config { + /* + * When a message has been received, run these statements + * over it. + */ + struct group *on_receipt; + + /* + * When a message is sent, run these statements. + */ + struct group *on_transmission; + + u_int32_t *required_options; /* Options server must supply. */ + u_int32_t *requested_options; /* Options to request from server. */ + + TIME timeout; /* Start to panic if we don't get a + lease in this time period when + SELECTING. */ + TIME initial_interval; /* All exponential backoff intervals + start here. */ + TIME retry_interval; /* If the protocol failed to produce + an address before the timeout, + try the protocol again after this + many seconds. */ + TIME select_interval; /* Wait this many seconds from the + first DHCPDISCOVER before + picking an offered lease. */ + TIME reboot_timeout; /* When in INIT-REBOOT, wait this + long before giving up and going + to INIT. */ + TIME backoff_cutoff; /* When doing exponential backoff, + never back off to an interval + longer than this amount. */ + u_int32_t requested_lease; /* Requested lease time, if user + doesn't configure one. */ + struct string_list *media; /* Possible network media values. */ + char *script_name; /* Name of config script. */ + char *vendor_space_name; /* Name of config script. */ + enum policy bootp_policy; + /* Ignore, accept or prefer BOOTP + responses. */ + enum policy auth_policy; + /* Require authentication, prefer + authentication, or don't try to + authenticate. */ + struct string_list *medium; /* Current network medium. */ + + struct iaddrlist *reject_list; /* Servers to reject. */ + + int omapi_port; /* port on which to accept OMAPI + connections, or -1 for no + listener. */ + int do_forward_update; /* If nonzero, and if we have the + information we need, update the + A record for the address we get. */ +}; + +/* Per-interface state used in the dhcp client... */ +struct client_state { + struct client_state *next; + struct interface_info *interface; + char *name; + + struct client_lease *active; /* Currently active lease. */ + struct client_lease *new; /* New lease. */ + struct client_lease *offered_leases; /* Leases offered to us. */ + struct client_lease *leases; /* Leases we currently hold. */ + struct client_lease *alias; /* Alias lease. */ + + enum dhcp_state state; /* Current state for this interface. */ + struct iaddr destination; /* Where to send packet. */ + u_int32_t xid; /* Transaction ID. */ + u_int16_t secs; /* secs value from DHCPDISCOVER. */ + TIME first_sending; /* When was first copy sent? */ + TIME interval; /* What's the current resend interval? */ + int dns_update_timeout; /* Last timeout set for DNS update. */ + struct string_list *medium; /* Last media type tried. */ + struct dhcp_packet packet; /* Outgoing DHCP packet. */ + unsigned packet_length; /* Actual length of generated packet. */ + + struct iaddr requested_address; /* Address we would like to get. */ + + struct client_config *config; /* Client configuration. */ + struct string_list *env; /* Client script environment. */ + int envc; /* Number of entries in environment. */ + + struct option_state *sent_options; /* Options we sent. */ +}; + +/* Information about each network interface. */ + +struct interface_info { + OMAPI_OBJECT_PREAMBLE; + struct interface_info *next; /* Next interface in list... */ + struct shared_network *shared_network; + /* Networks connected to this interface. */ + struct hardware hw_address; /* Its physical address. */ + struct in_addr primary_address; /* Primary interface address. */ + + u_int8_t *circuit_id; /* Circuit ID associated with this + interface. */ + unsigned circuit_id_len; /* Length of Circuit ID, if there + is one. */ + u_int8_t *remote_id; /* Remote ID associated with this + interface (if any). */ + unsigned remote_id_len; /* Length of Remote ID. */ + + char name [IFNAMSIZ]; /* Its name... */ + int index; /* Its index. */ + int rfdesc; /* Its read file descriptor. */ + int wfdesc; /* Its write file descriptor, if + different. */ + unsigned char *rbuf; /* Read buffer, if required. */ + unsigned int rbuf_max; /* Size of read buffer. */ + size_t rbuf_offset; /* Current offset into buffer. */ + size_t rbuf_len; /* Length of data in buffer. */ + + struct ifreq *ifp; /* Pointer to ifreq struct. */ + u_int32_t flags; /* Control flags... */ +#define INTERFACE_REQUESTED 1 +#define INTERFACE_AUTOMATIC 2 +#define INTERFACE_RUNNING 4 + + /* Only used by DHCP client code. */ + struct client_state *client; +# if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) + int dlpi_sap_length; + struct hardware dlpi_broadcast_addr; +# endif /* DLPI_SEND || DLPI_RECEIVE */ +}; + +struct hardware_link { + struct hardware_link *next; + char name [IFNAMSIZ]; + struct hardware address; +}; + +typedef void (*tvref_t)(void *, void *, const char *, int); +typedef void (*tvunref_t)(void *, const char *, int); +struct timeout { + struct timeout *next; + TIME when; + void (*func) PROTO ((void *)); + void *what; + tvref_t ref; + tvunref_t unref; +}; + +struct protocol { + struct protocol *next; + int fd; + void (*handler) PROTO ((struct protocol *)); + void *local; +}; + +struct dns_query; /* forward */ + +struct dns_wakeup { + struct dns_wakeup *next; /* Next wakeup in chain. */ + void (*func) PROTO ((struct dns_query *)); +}; + +struct dns_question { + u_int16_t type; /* Type of query. */ + u_int16_t class; /* Class of query. */ + unsigned char data [1]; /* Query data. */ +}; + +struct dns_answer { + u_int16_t type; /* Type of answer. */ + u_int16_t class; /* Class of answer. */ + int count; /* Number of answers. */ + unsigned char *answers[1]; /* Pointers to answers. */ +}; + +struct dns_query { + struct dns_query *next; /* Next query in hash bucket. */ + u_int32_t hash; /* Hash bucket index. */ + TIME expiry; /* Query expiry time (zero if not yet + answered. */ + u_int16_t id; /* Query ID (also hash table index) */ + caddr_t waiters; /* Pointer to list of things waiting + on this query. */ + + struct dns_question *question; /* Question, internal format. */ + struct dns_answer *answer; /* Answer, internal format. */ + + unsigned char *query; /* Query formatted for DNS server. */ + unsigned len; /* Length of entire query. */ + int sent; /* The query has been sent. */ + struct dns_wakeup *wakeups; /* Wakeups to call if this query is + answered. */ + struct name_server *next_server; /* Next server to try. */ + int backoff; /* Current backoff, in seconds. */ +}; + +struct dns_zone { + int refcnt; + TIME timeout; + char *name; + struct option_cache *primary; + struct option_cache *secondary; + struct auth_key *key; +}; + +struct icmp_state { + OMAPI_OBJECT_PREAMBLE; + int socket; + void (*icmp_handler) PROTO ((struct iaddr, u_int8_t *, int)); +}; + +#include "ctrace.h" + +/* Bitmask of dhcp option codes. */ +typedef unsigned char option_mask [16]; + +/* DHCP Option mask manipulation macros... */ +#define OPTION_ZERO(mask) (memset (mask, 0, 16)) +#define OPTION_SET(mask, bit) (mask [bit >> 8] |= (1 << (bit & 7))) +#define OPTION_CLR(mask, bit) (mask [bit >> 8] &= ~(1 << (bit & 7))) +#define OPTION_ISSET(mask, bit) (mask [bit >> 8] & (1 << (bit & 7))) +#define OPTION_ISCLR(mask, bit) (!OPTION_ISSET (mask, bit)) + +/* An option occupies its length plus two header bytes (code and + length) for every 255 bytes that must be stored. */ +#define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1)) + +/* Default path to dhcpd config file. */ +#ifdef DEBUG +#undef _PATH_DHCPD_CONF +#define _PATH_DHCPD_CONF "dhcpd.conf" +#undef _PATH_DHCPD_DB +#define _PATH_DHCPD_DB "dhcpd.leases" +#undef _PATH_DHCPD_PID +#define _PATH_DHCPD_PID "dhcpd.pid" +#else +#ifndef _PATH_DHCPD_CONF +#define _PATH_DHCPD_CONF "/etc/dhcpd.conf" +#endif + +#ifndef _PATH_DHCPD_DB +#define _PATH_DHCPD_DB "/etc/dhcpd.leases" +#endif + +#ifndef _PATH_DHCPD_PID +#define _PATH_DHCPD_PID "/var/run/dhcpd.pid" +#endif +#endif + +#ifndef _PATH_DHCLIENT_CONF +#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf" +#endif + +#ifndef _PATH_DHCLIENT_SCRIPT +#define _PATH_DHCLIENT_SCRIPT "/sbin/dhclient-script" +#endif + +#ifndef _PATH_DHCLIENT_PID +#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid" +#endif + +#ifndef _PATH_DHCLIENT_DB +#define _PATH_DHCLIENT_DB "/etc/dhclient.leases" +#endif + +#ifndef _PATH_RESOLV_CONF +#define _PATH_RESOLV_CONF "/etc/resolv.conf" +#endif + +#ifndef _PATH_DHCRELAY_PID +#define _PATH_DHCRELAY_PID "/var/run/dhcrelay.pid" +#endif + +#ifndef DHCPD_LOG_FACILITY +#define DHCPD_LOG_FACILITY LOG_DAEMON +#endif + +#define MAX_TIME 0x7fffffff +#define MIN_TIME 0 + +/* External definitions... */ + +HASH_FUNCTIONS_DECL (group, const char *, struct group_object, group_hash_t) +HASH_FUNCTIONS_DECL (universe, const char *, struct universe, universe_hash_t) +HASH_FUNCTIONS_DECL (option, const char *, struct option, option_hash_t) +HASH_FUNCTIONS_DECL (dns_zone, const char *, struct dns_zone, dns_zone_hash_t) +HASH_FUNCTIONS_DECL (lease, const unsigned char *, struct lease, lease_hash_t) +HASH_FUNCTIONS_DECL (host, const unsigned char *, struct host_decl, host_hash_t) +HASH_FUNCTIONS_DECL (class, const char *, struct class, class_hash_t) + +/* options.c */ + +extern struct option *vendor_cfg_option; +int parse_options PROTO ((struct packet *)); +int parse_option_buffer PROTO ((struct option_state *, const unsigned char *, + unsigned, struct universe *)); +struct universe *find_option_universe (struct option *, const char *); +int parse_encapsulated_suboptions (struct option_state *, struct option *, + const unsigned char *, unsigned, + struct universe *, const char *); +int cons_options PROTO ((struct packet *, struct dhcp_packet *, struct lease *, + struct client_state *, + int, struct option_state *, struct option_state *, + struct binding_scope **, + int, int, int, struct data_string *, const char *)); +int fqdn_universe_decode (struct option_state *, + const unsigned char *, unsigned, struct universe *); +int store_options PROTO ((int *, unsigned char *, unsigned, struct packet *, + struct lease *, struct client_state *, + struct option_state *, + struct option_state *, struct binding_scope **, + unsigned *, int, unsigned, unsigned, + int, const char *)); +const char *pretty_print_option PROTO ((struct option *, const unsigned char *, + unsigned, int, int)); +int get_option (struct data_string *, struct universe *, + struct packet *, struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct option_state *, struct binding_scope **, unsigned, + const char *, int); +void set_option (struct universe *, struct option_state *, + struct option_cache *, enum statement_op); +struct option_cache *lookup_option PROTO ((struct universe *, + struct option_state *, unsigned)); +struct option_cache *lookup_hashed_option PROTO ((struct universe *, + struct option_state *, + unsigned)); +int save_option_buffer (struct universe *, struct option_state *, + struct buffer *, unsigned char *, unsigned, + struct option *, int); +void save_option PROTO ((struct universe *, + struct option_state *, struct option_cache *)); +void save_hashed_option PROTO ((struct universe *, + struct option_state *, struct option_cache *)); +void delete_option PROTO ((struct universe *, struct option_state *, int)); +void delete_hashed_option PROTO ((struct universe *, + struct option_state *, int)); +int option_cache_dereference PROTO ((struct option_cache **, + const char *, int)); +int hashed_option_state_dereference PROTO ((struct universe *, + struct option_state *, + const char *, int)); +int store_option PROTO ((struct data_string *, + struct universe *, struct packet *, struct lease *, + struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct option_cache *)); +int option_space_encapsulate PROTO ((struct data_string *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct data_string *)); +int hashed_option_space_encapsulate PROTO ((struct data_string *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *)); +int nwip_option_space_encapsulate PROTO ((struct data_string *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *)); +int fqdn_option_space_encapsulate (struct data_string *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *); +void suboption_foreach (struct packet *, struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct universe *, void *, + void (*) (struct option_cache *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, + struct universe *, void *), + struct option_cache *, const char *); +void option_space_foreach (struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *, + void (*) (struct option_cache *, + struct packet *, + struct lease *, struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)); +void hashed_option_space_foreach (struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *, + void (*) (struct option_cache *, + struct packet *, + struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)); +int linked_option_get PROTO ((struct data_string *, struct universe *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, struct option_state *, + struct option_state *, struct binding_scope **, + unsigned)); +int linked_option_state_dereference PROTO ((struct universe *, + struct option_state *, + const char *, int)); +void save_linked_option (struct universe *, struct option_state *, + struct option_cache *); +void linked_option_space_foreach (struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *, + void (*) (struct option_cache *, + struct packet *, + struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)); +int linked_option_space_encapsulate (struct data_string *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *); +void delete_linked_option (struct universe *, struct option_state *, int); +struct option_cache *lookup_linked_option (struct universe *, + struct option_state *, unsigned); +void do_packet PROTO ((struct interface_info *, + struct dhcp_packet *, unsigned, + unsigned int, struct iaddr, struct hardware *)); + +/* dhcpd.c */ +extern TIME cur_time; + +int ddns_update_style; + +extern const char *path_dhcpd_conf; +extern const char *path_dhcpd_db; +extern const char *path_dhcpd_pid; + +extern int dhcp_max_agent_option_packet_length; + +int main PROTO ((int, char **, char **)); +void postconf_initialization (int); +void postdb_startup (void); +void cleanup PROTO ((void)); +void lease_pinged PROTO ((struct iaddr, u_int8_t *, int)); +void lease_ping_timeout PROTO ((void *)); +int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia); +enum dhcp_shutdown_state shutdown_state; +isc_result_t dhcp_io_shutdown (omapi_object_t *, void *); +isc_result_t dhcp_set_control_state (control_object_state_t oldstate, + control_object_state_t newstate); + +/* conflex.c */ +isc_result_t new_parse PROTO ((struct parse **, int, + char *, unsigned, const char *, int)); +isc_result_t end_parse PROTO ((struct parse **)); +enum dhcp_token next_token PROTO ((const char **, unsigned *, struct parse *)); +enum dhcp_token peek_token PROTO ((const char **, unsigned *, struct parse *)); + +/* confpars.c */ +void parse_trace_setup (void); +isc_result_t readconf PROTO ((void)); +isc_result_t read_conf_file (const char *, struct group *, int, int); +#if defined (TRACING) +void trace_conf_input (trace_type_t *, unsigned, char *); +void trace_conf_stop (trace_type_t *ttype); +#endif +isc_result_t conf_file_subparse (struct parse *, struct group *, int); +isc_result_t lease_file_subparse (struct parse *); +int parse_statement PROTO ((struct parse *, + struct group *, int, struct host_decl *, int)); +#if defined (FAILOVER_PROTOCOL) +void parse_failover_peer PROTO ((struct parse *, struct group *, int)); +void parse_failover_state_declaration (struct parse *, + dhcp_failover_state_t *); +void parse_failover_state PROTO ((struct parse *, + enum failover_state *, TIME *)); +#endif +int permit_list_match (struct permit *, struct permit *); +void parse_pool_statement PROTO ((struct parse *, struct group *, int)); +int parse_boolean PROTO ((struct parse *)); +int parse_lbrace PROTO ((struct parse *)); +void parse_host_declaration PROTO ((struct parse *, struct group *)); +int parse_class_declaration PROTO ((struct class **, struct parse *, + struct group *, int)); +void parse_shared_net_declaration PROTO ((struct parse *, struct group *)); +void parse_subnet_declaration PROTO ((struct parse *, + struct shared_network *)); +void parse_group_declaration PROTO ((struct parse *, struct group *)); +int parse_fixed_addr_param PROTO ((struct option_cache **, struct parse *)); +TIME parse_timestamp PROTO ((struct parse *)); +int parse_lease_declaration PROTO ((struct lease **, struct parse *)); +void parse_address_range PROTO ((struct parse *, struct group *, int, + struct pool *, struct lease **)); + +/* ddns.c */ +int ddns_updates PROTO ((struct packet *, struct lease *, struct lease *, + struct lease_state *)); +int ddns_removals PROTO ((struct lease *)); + +/* parse.c */ +void add_enumeration (struct enumeration *); +struct enumeration *find_enumeration (const char *, int); +struct enumeration_value *find_enumeration_value (const char *, int, + const char *); +void skip_to_semi PROTO ((struct parse *)); +void skip_to_rbrace PROTO ((struct parse *, int)); +int parse_semi PROTO ((struct parse *)); +int parse_string PROTO ((struct parse *, char **, unsigned *)); +char *parse_host_name PROTO ((struct parse *)); +int parse_ip_addr_or_hostname PROTO ((struct expression **, + struct parse *, int)); +void parse_hardware_param PROTO ((struct parse *, struct hardware *)); +void parse_lease_time PROTO ((struct parse *, TIME *)); +unsigned char *parse_numeric_aggregate PROTO ((struct parse *, + unsigned char *, unsigned *, + int, int, unsigned)); +void convert_num PROTO ((struct parse *, unsigned char *, const char *, + int, unsigned)); +TIME parse_date PROTO ((struct parse *)); +struct option *parse_option_name PROTO ((struct parse *, int, int *)); +void parse_option_space_decl PROTO ((struct parse *)); +int parse_option_code_definition PROTO ((struct parse *, struct option *)); +int parse_base64 (struct data_string *, struct parse *); +int parse_cshl PROTO ((struct data_string *, struct parse *)); +int parse_executable_statement PROTO ((struct executable_statement **, + struct parse *, int *, + enum expression_context)); +int parse_executable_statements PROTO ((struct executable_statement **, + struct parse *, int *, + enum expression_context)); +int parse_zone (struct dns_zone *, struct parse *); +int parse_key (struct parse *); +int parse_on_statement PROTO ((struct executable_statement **, + struct parse *, int *)); +int parse_switch_statement PROTO ((struct executable_statement **, + struct parse *, int *)); +int parse_case_statement PROTO ((struct executable_statement **, + struct parse *, int *, + enum expression_context)); +int parse_if_statement PROTO ((struct executable_statement **, + struct parse *, int *)); +int parse_boolean_expression PROTO ((struct expression **, + struct parse *, int *)); +int parse_data_expression PROTO ((struct expression **, + struct parse *, int *)); +int parse_numeric_expression PROTO ((struct expression **, + struct parse *, int *)); +int parse_dns_expression PROTO ((struct expression **, struct parse *, int *)); +int parse_non_binary PROTO ((struct expression **, struct parse *, int *, + enum expression_context)); +int parse_expression PROTO ((struct expression **, struct parse *, int *, + enum expression_context, + struct expression **, enum expr_op)); +int parse_option_statement PROTO ((struct executable_statement **, + struct parse *, int, + struct option *, enum statement_op)); +int parse_option_token PROTO ((struct expression **, struct parse *, + const char **, struct expression *, int, int)); +int parse_allow_deny PROTO ((struct option_cache **, struct parse *, int)); +int parse_auth_key PROTO ((struct data_string *, struct parse *)); +int parse_warn (struct parse *, const char *, ...) + __attribute__((__format__(__printf__,2,3))); + +/* tree.c */ +#if defined (NSUPDATE) +extern struct __res_state resolver_state; +extern int resolver_inited; +#endif + +extern struct binding_scope *global_scope; +pair cons PROTO ((caddr_t, pair)); +int make_const_option_cache PROTO ((struct option_cache **, struct buffer **, + u_int8_t *, unsigned, struct option *, + const char *, int)); +int make_host_lookup PROTO ((struct expression **, const char *)); +int enter_dns_host PROTO ((struct dns_host_entry **, const char *)); +int make_const_data (struct expression **, + const unsigned char *, unsigned, int, int, + const char *, int); +int make_const_int PROTO ((struct expression **, unsigned long)); +int make_concat PROTO ((struct expression **, + struct expression *, struct expression *)); +int make_encapsulation PROTO ((struct expression **, struct data_string *)); +int make_substring PROTO ((struct expression **, struct expression *, + struct expression *, struct expression *)); +int make_limit PROTO ((struct expression **, struct expression *, int)); +int make_let PROTO ((struct executable_statement **, const char *)); +int option_cache PROTO ((struct option_cache **, struct data_string *, + struct expression *, struct option *, + const char *, int)); +int evaluate_expression (struct binding_value **, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct expression *, + const char *, int); +int binding_value_dereference (struct binding_value **, const char *, int); +#if defined (NSUPDATE) +int evaluate_dns_expression PROTO ((ns_updrec **, struct packet *, + struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct expression *)); +#endif +int evaluate_boolean_expression PROTO ((int *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct expression *)); +int evaluate_data_expression PROTO ((struct data_string *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct expression *, const char *, int)); +int evaluate_numeric_expression (unsigned long *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, + struct expression *); +int evaluate_option_cache PROTO ((struct data_string *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, + struct option_cache *, + const char *, int)); +int evaluate_boolean_option_cache PROTO ((int *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct option_cache *, + const char *, int)); +int evaluate_boolean_expression_result PROTO ((int *, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct expression *)); +void expression_dereference PROTO ((struct expression **, const char *, int)); +int is_dns_expression PROTO ((struct expression *)); +int is_boolean_expression PROTO ((struct expression *)); +int is_data_expression PROTO ((struct expression *)); +int is_numeric_expression PROTO ((struct expression *)); +int is_compound_expression PROTO ((struct expression *)); +int op_precedence PROTO ((enum expr_op, enum expr_op)); +enum expression_context expression_context (struct expression *); +enum expression_context op_context PROTO ((enum expr_op)); +int write_expression PROTO ((FILE *, struct expression *, int, int, int)); +struct binding *find_binding PROTO ((struct binding_scope *, const char *)); +int free_bindings PROTO ((struct binding_scope *, const char *, int)); +int binding_scope_dereference PROTO ((struct binding_scope **, + const char *, int)); +int fundef_dereference (struct fundef **, const char *, int); +int data_subexpression_length (int *, struct expression *); +int expr_valid_for_context (struct expression *, enum expression_context); +struct binding *create_binding (struct binding_scope **, const char *); +int bind_ds_value (struct binding_scope **, + const char *, struct data_string *); +int find_bound_string (struct data_string *, + struct binding_scope *, const char *); +int unset (struct binding_scope *, const char *); + +/* dhcp.c */ +extern int outstanding_pings; + +void dhcp PROTO ((struct packet *)); +void dhcpdiscover PROTO ((struct packet *, int)); +void dhcprequest PROTO ((struct packet *, int, struct lease *)); +void dhcprelease PROTO ((struct packet *, int)); +void dhcpdecline PROTO ((struct packet *, int)); +void dhcpinform PROTO ((struct packet *, int)); +void nak_lease PROTO ((struct packet *, struct iaddr *cip)); +void ack_lease PROTO ((struct packet *, struct lease *, + unsigned int, TIME, char *, int)); +void dhcp_reply PROTO ((struct lease *)); +int find_lease PROTO ((struct lease **, struct packet *, + struct shared_network *, int *, int *, struct lease *, + const char *, int)); +int mockup_lease PROTO ((struct lease **, struct packet *, + struct shared_network *, + struct host_decl *)); +void static_lease_dereference PROTO ((struct lease *, const char *, int)); + +int allocate_lease PROTO ((struct lease **, struct packet *, + struct pool *, int *)); +int permitted PROTO ((struct packet *, struct permit *)); +int locate_network PROTO ((struct packet *)); +int parse_agent_information_option PROTO ((struct packet *, int, u_int8_t *)); +unsigned cons_agent_information_options PROTO ((struct option_state *, + struct dhcp_packet *, + unsigned, unsigned)); + +/* bootp.c */ +void bootp PROTO ((struct packet *)); + +/* memory.c */ +int (*group_write_hook) (struct group_object *); +extern struct group *root_group; +extern group_hash_t *group_name_hash; +isc_result_t delete_group (struct group_object *, int); +isc_result_t supersede_group (struct group_object *, int); +int clone_group (struct group **, struct group *, const char *, int); +int write_group PROTO ((struct group_object *)); + +/* salloc.c */ +void relinquish_lease_hunks (void); +struct lease *new_leases PROTO ((unsigned, const char *, int)); +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_lease_states (void); +#endif +OMAPI_OBJECT_ALLOC_DECL (lease, struct lease, dhcp_type_lease) +OMAPI_OBJECT_ALLOC_DECL (class, struct class, dhcp_type_class) +OMAPI_OBJECT_ALLOC_DECL (pool, struct pool, dhcp_type_pool) +OMAPI_OBJECT_ALLOC_DECL (host, struct host_decl, dhcp_type_host) + +/* alloc.c */ +OMAPI_OBJECT_ALLOC_DECL (subnet, struct subnet, dhcp_type_subnet) +OMAPI_OBJECT_ALLOC_DECL (shared_network, struct shared_network, + dhcp_type_shared_network) +OMAPI_OBJECT_ALLOC_DECL (group_object, struct group_object, dhcp_type_group) +OMAPI_OBJECT_ALLOC_DECL (dhcp_control, + dhcp_control_object_t, dhcp_type_control) + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void relinquish_free_pairs (void); +void relinquish_free_expressions (void); +void relinquish_free_binding_values (void); +void relinquish_free_option_caches (void); +void relinquish_free_packets (void); +#endif + +int option_chain_head_allocate (struct option_chain_head **, + const char *, int); +int option_chain_head_reference (struct option_chain_head **, + struct option_chain_head *, + const char *, int); +int option_chain_head_dereference (struct option_chain_head **, + const char *, int); +int group_allocate (struct group **, const char *, int); +int group_reference (struct group **, struct group *, const char *, int); +int group_dereference (struct group **, const char *, int); +struct dhcp_packet *new_dhcp_packet PROTO ((const char *, int)); +struct protocol *new_protocol PROTO ((const char *, int)); +struct lease_state *new_lease_state PROTO ((const char *, int)); +struct domain_search_list *new_domain_search_list PROTO ((const char *, int)); +struct name_server *new_name_server PROTO ((const char *, int)); +void free_name_server PROTO ((struct name_server *, const char *, int)); +struct option *new_option PROTO ((const char *, int)); +int group_allocate (struct group **, const char *, int); +int group_reference (struct group **, struct group *, const char *, int); +int group_dereference (struct group **, const char *, int); +void free_option PROTO ((struct option *, const char *, int)); +struct universe *new_universe PROTO ((const char *, int)); +void free_universe PROTO ((struct universe *, const char *, int)); +void free_domain_search_list PROTO ((struct domain_search_list *, + const char *, int)); +void free_lease_state PROTO ((struct lease_state *, const char *, int)); +void free_protocol PROTO ((struct protocol *, const char *, int)); +void free_dhcp_packet PROTO ((struct dhcp_packet *, const char *, int)); +struct client_lease *new_client_lease PROTO ((const char *, int)); +void free_client_lease PROTO ((struct client_lease *, const char *, int)); +struct permit *new_permit PROTO ((const char *, int)); +void free_permit PROTO ((struct permit *, const char *, int)); +pair new_pair PROTO ((const char *, int)); +void free_pair PROTO ((pair, const char *, int)); +int expression_allocate PROTO ((struct expression **, const char *, int)); +int expression_reference PROTO ((struct expression **, + struct expression *, const char *, int)); +void free_expression PROTO ((struct expression *, const char *, int)); +int binding_value_allocate PROTO ((struct binding_value **, + const char *, int)); +int binding_value_reference PROTO ((struct binding_value **, + struct binding_value *, + const char *, int)); +void free_binding_value PROTO ((struct binding_value *, const char *, int)); +int fundef_allocate PROTO ((struct fundef **, const char *, int)); +int fundef_reference PROTO ((struct fundef **, + struct fundef *, const char *, int)); +int option_cache_allocate PROTO ((struct option_cache **, const char *, int)); +int option_cache_reference PROTO ((struct option_cache **, + struct option_cache *, const char *, int)); +int buffer_allocate PROTO ((struct buffer **, unsigned, const char *, int)); +int buffer_reference PROTO ((struct buffer **, struct buffer *, + const char *, int)); +int buffer_dereference PROTO ((struct buffer **, const char *, int)); +int dns_host_entry_allocate PROTO ((struct dns_host_entry **, + const char *, const char *, int)); +int dns_host_entry_reference PROTO ((struct dns_host_entry **, + struct dns_host_entry *, + const char *, int)); +int dns_host_entry_dereference PROTO ((struct dns_host_entry **, + const char *, int)); +int option_state_allocate PROTO ((struct option_state **, const char *, int)); +int option_state_reference PROTO ((struct option_state **, + struct option_state *, const char *, int)); +int option_state_dereference PROTO ((struct option_state **, + const char *, int)); +void data_string_copy PROTO ((struct data_string *, + struct data_string *, const char *, int)); +void data_string_forget PROTO ((struct data_string *, const char *, int)); +void data_string_truncate PROTO ((struct data_string *, int)); +int executable_statement_allocate PROTO ((struct executable_statement **, + const char *, int)); +int executable_statement_reference PROTO ((struct executable_statement **, + struct executable_statement *, + const char *, int)); +int packet_allocate PROTO ((struct packet **, const char *, int)); +int packet_reference PROTO ((struct packet **, + struct packet *, const char *, int)); +int packet_dereference PROTO ((struct packet **, const char *, int)); +int binding_scope_allocate PROTO ((struct binding_scope **, + const char *, int)); +int binding_scope_reference PROTO ((struct binding_scope **, + struct binding_scope *, + const char *, int)); +int dns_zone_allocate PROTO ((struct dns_zone **, const char *, int)); +int dns_zone_reference PROTO ((struct dns_zone **, + struct dns_zone *, const char *, int)); + +/* print.c */ +char *quotify_string (const char *, const char *, int); +char *quotify_buf (const unsigned char *, unsigned, const char *, int); +char *print_base64 (const unsigned char *, unsigned, const char *, int); +char *print_hw_addr PROTO ((int, int, unsigned char *)); +void print_lease PROTO ((struct lease *)); +void dump_raw PROTO ((const unsigned char *, unsigned)); +void dump_packet_option (struct option_cache *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct universe *, void *); +void dump_packet PROTO ((struct packet *)); +void hash_dump PROTO ((struct hash_table *)); +char *print_hex_1 PROTO ((unsigned, const u_int8_t *, unsigned)); +char *print_hex_2 PROTO ((unsigned, const u_int8_t *, unsigned)); +char *print_hex_3 PROTO ((unsigned, const u_int8_t *, unsigned)); +char *print_dotted_quads PROTO ((unsigned, const u_int8_t *)); +char *print_dec_1 PROTO ((unsigned long)); +char *print_dec_2 PROTO ((unsigned long)); +void print_expression PROTO ((const char *, struct expression *)); +int token_print_indent_concat (FILE *, int, int, + const char *, const char *, ...); +int token_indent_data_string (FILE *, int, int, const char *, const char *, + struct data_string *); +int token_print_indent (FILE *, int, int, + const char *, const char *, const char *); +void indent_spaces (FILE *, int); +#if defined (NSUPDATE) +void print_dns_status (int, ns_updque *); +#endif + +/* socket.c */ +#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \ + || defined (USE_SOCKET_FALLBACK) +int if_register_socket PROTO ((struct interface_info *)); +#endif + +#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND) +void if_reinitialize_fallback PROTO ((struct interface_info *)); +void if_register_fallback PROTO ((struct interface_info *)); +ssize_t send_fallback PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +#endif + +#ifdef USE_SOCKET_SEND +void if_reinitialize_send PROTO ((struct interface_info *)); +void if_register_send PROTO ((struct interface_info *)); +void if_deregister_send PROTO ((struct interface_info *)); +ssize_t send_packet PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +#endif +#ifdef USE_SOCKET_RECEIVE +void if_reinitialize_receive PROTO ((struct interface_info *)); +void if_register_receive PROTO ((struct interface_info *)); +void if_deregister_receive PROTO ((struct interface_info *)); +ssize_t receive_packet PROTO ((struct interface_info *, + unsigned char *, size_t, + struct sockaddr_in *, struct hardware *)); +#endif + +#if defined (USE_SOCKET_FALLBACK) +isc_result_t fallback_discard PROTO ((omapi_object_t *)); +#endif + +#if defined (USE_SOCKET_SEND) +int can_unicast_without_arp PROTO ((struct interface_info *)); +int can_receive_unicast_unconfigured PROTO ((struct interface_info *)); +int supports_multiple_interfaces (struct interface_info *); +void maybe_setup_fallback PROTO ((void)); +#endif + +/* bpf.c */ +#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) +int if_register_bpf PROTO ( (struct interface_info *)); +#endif +#ifdef USE_BPF_SEND +void if_reinitialize_send PROTO ((struct interface_info *)); +void if_register_send PROTO ((struct interface_info *)); +void if_deregister_send PROTO ((struct interface_info *)); +ssize_t send_packet PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +#endif +#ifdef USE_BPF_RECEIVE +void if_reinitialize_receive PROTO ((struct interface_info *)); +void if_register_receive PROTO ((struct interface_info *)); +void if_deregister_receive PROTO ((struct interface_info *)); +ssize_t receive_packet PROTO ((struct interface_info *, + unsigned char *, size_t, + struct sockaddr_in *, struct hardware *)); +#endif +#if defined (USE_BPF_SEND) +int can_unicast_without_arp PROTO ((struct interface_info *)); +int can_receive_unicast_unconfigured PROTO ((struct interface_info *)); +int supports_multiple_interfaces (struct interface_info *); +void maybe_setup_fallback PROTO ((void)); +#endif + +/* lpf.c */ +#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE) +int if_register_lpf PROTO ( (struct interface_info *)); +#endif +#ifdef USE_LPF_SEND +void if_reinitialize_send PROTO ((struct interface_info *)); +void if_register_send PROTO ((struct interface_info *)); +void if_deregister_send PROTO ((struct interface_info *)); +ssize_t send_packet PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +#endif +#ifdef USE_LPF_RECEIVE +void if_reinitialize_receive PROTO ((struct interface_info *)); +void if_register_receive PROTO ((struct interface_info *)); +void if_deregister_receive PROTO ((struct interface_info *)); +ssize_t receive_packet PROTO ((struct interface_info *, + unsigned char *, size_t, + struct sockaddr_in *, struct hardware *)); +#endif +#if defined (USE_LPF_SEND) +int can_unicast_without_arp PROTO ((struct interface_info *)); +int can_receive_unicast_unconfigured PROTO ((struct interface_info *)); +int supports_multiple_interfaces (struct interface_info *); +void maybe_setup_fallback PROTO ((void)); +#endif + +/* nit.c */ +#if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE) +int if_register_nit PROTO ( (struct interface_info *)); +#endif + +#ifdef USE_NIT_SEND +void if_reinitialize_send PROTO ((struct interface_info *)); +void if_register_send PROTO ((struct interface_info *)); +void if_deregister_send PROTO ((struct interface_info *)); +ssize_t send_packet PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +#endif +#ifdef USE_NIT_RECEIVE +void if_reinitialize_receive PROTO ((struct interface_info *)); +void if_register_receive PROTO ((struct interface_info *)); +void if_deregister_receive PROTO ((struct interface_info *)); +ssize_t receive_packet PROTO ((struct interface_info *, + unsigned char *, size_t, + struct sockaddr_in *, struct hardware *)); +#endif +#if defined (USE_NIT_SEND) +int can_unicast_without_arp PROTO ((struct interface_info *)); +int can_receive_unicast_unconfigured PROTO ((struct interface_info *)); +int supports_multiple_interfaces (struct interface_info *); +void maybe_setup_fallback PROTO ((void)); +#endif + +/* dlpi.c */ +#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) +int if_register_dlpi PROTO ( (struct interface_info *)); +#endif + +#ifdef USE_DLPI_SEND +void if_reinitialize_send PROTO ((struct interface_info *)); +void if_register_send PROTO ((struct interface_info *)); +void if_deregister_send PROTO ((struct interface_info *)); +ssize_t send_packet PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +#endif +#ifdef USE_DLPI_RECEIVE +void if_reinitialize_receive PROTO ((struct interface_info *)); +void if_register_receive PROTO ((struct interface_info *)); +void if_deregister_receive PROTO ((struct interface_info *)); +ssize_t receive_packet PROTO ((struct interface_info *, + unsigned char *, size_t, + struct sockaddr_in *, struct hardware *)); +#endif + + +/* raw.c */ +#ifdef USE_RAW_SEND +void if_reinitialize_send PROTO ((struct interface_info *)); +void if_register_send PROTO ((struct interface_info *)); +void if_deregister_send PROTO ((struct interface_info *)); +ssize_t send_packet PROTO ((struct interface_info *, + struct packet *, struct dhcp_packet *, size_t, + struct in_addr, + struct sockaddr_in *, struct hardware *)); +int can_unicast_without_arp PROTO ((struct interface_info *)); +int can_receive_unicast_unconfigured PROTO ((struct interface_info *)); +int supports_multiple_interfaces (struct interface_info *); +void maybe_setup_fallback PROTO ((void)); +#endif + +/* discover.c */ +extern struct interface_info *interfaces, + *dummy_interfaces, *fallback_interface; +extern struct protocol *protocols; +extern int quiet_interface_discovery; +isc_result_t interface_setup (void); +void interface_trace_setup (void); + +extern struct in_addr limited_broadcast; +extern struct in_addr local_address; + +extern u_int16_t local_port; +extern u_int16_t remote_port; +extern int (*dhcp_interface_setup_hook) (struct interface_info *, + struct iaddr *); +extern int (*dhcp_interface_discovery_hook) (struct interface_info *); +isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); + +extern void (*bootp_packet_handler) PROTO ((struct interface_info *, + struct dhcp_packet *, unsigned, + unsigned int, + struct iaddr, struct hardware *)); +extern struct timeout *timeouts; +extern omapi_object_type_t *dhcp_type_interface; +#if defined (TRACING) +trace_type_t *interface_trace; +trace_type_t *inpacket_trace; +trace_type_t *outpacket_trace; +#endif +extern struct interface_info **interface_vector; +extern int interface_count; +extern int interface_max; +isc_result_t interface_initialize (omapi_object_t *, const char *, int); +void discover_interfaces PROTO ((int)); +int setup_fallback (struct interface_info **, const char *, int); +int if_readsocket PROTO ((omapi_object_t *)); +void reinitialize_interfaces PROTO ((void)); + +/* dispatch.c */ +void set_time (u_int32_t); +struct timeval *process_outstanding_timeouts (struct timeval *); +void dispatch PROTO ((void)); +isc_result_t got_one PROTO ((omapi_object_t *)); +isc_result_t interface_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, omapi_typed_data_t *); +isc_result_t interface_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, omapi_value_t **); +isc_result_t interface_destroy (omapi_object_t *, const char *, int); +isc_result_t interface_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t interface_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); + +void add_timeout PROTO ((TIME, void (*) PROTO ((void *)), void *, + tvref_t, tvunref_t)); +void cancel_timeout PROTO ((void (*) PROTO ((void *)), void *)); +void cancel_all_timeouts (void); +void relinquish_timeouts (void); +#if 0 +struct protocol *add_protocol PROTO ((const char *, int, + void (*) PROTO ((struct protocol *)), + void *)); + +void remove_protocol PROTO ((struct protocol *)); +#endif +OMAPI_OBJECT_ALLOC_DECL (interface, + struct interface_info, dhcp_type_interface) + +/* tables.c */ +extern struct universe dhcp_universe; +extern struct option dhcp_options [256]; +extern struct universe nwip_universe; +extern struct option nwip_options [256]; +extern struct universe fqdn_universe; +extern struct option fqdn_options [256]; +extern int dhcp_option_default_priority_list []; +extern int dhcp_option_default_priority_list_count; +extern const char *hardware_types [256]; +int universe_count, universe_max; +struct universe **universes; +extern universe_hash_t *universe_hash; +void initialize_common_option_spaces PROTO ((void)); +struct universe *config_universe; + +/* stables.c */ +#if defined (FAILOVER_PROTOCOL) +extern failover_option_t null_failover_option; +extern failover_option_t skip_failover_option; +extern struct failover_option_info ft_options []; +extern u_int32_t fto_allowed []; +extern int ft_sizes []; +extern const char *dhcp_flink_state_names []; +#endif +extern const char *binding_state_names []; + +extern struct universe agent_universe; +extern struct option agent_options [256]; +extern struct universe server_universe; +extern struct option server_options [256]; + +extern struct enumeration ddns_styles; +extern struct enumeration syslog_enum; +void initialize_server_option_spaces PROTO ((void)); + +/* inet.c */ +struct iaddr subnet_number PROTO ((struct iaddr, struct iaddr)); +struct iaddr ip_addr PROTO ((struct iaddr, struct iaddr, u_int32_t)); +struct iaddr broadcast_addr PROTO ((struct iaddr, struct iaddr)); +u_int32_t host_addr PROTO ((struct iaddr, struct iaddr)); +int addr_eq PROTO ((struct iaddr, struct iaddr)); +char *piaddr PROTO ((struct iaddr)); +char *piaddrmask (struct iaddr, struct iaddr, const char *, int); +char *piaddr1 PROTO ((struct iaddr)); + +/* dhclient.c */ +extern const char *path_dhclient_conf; +extern const char *path_dhclient_db; +extern const char *path_dhclient_pid; +extern char *path_dhclient_script; +extern int interfaces_requested; + +extern struct client_config top_level_config; + +void dhcpoffer PROTO ((struct packet *)); +void dhcpack PROTO ((struct packet *)); +void dhcpnak PROTO ((struct packet *)); + +void send_discover PROTO ((void *)); +void send_request PROTO ((void *)); +void send_release PROTO ((void *)); +void send_decline PROTO ((void *)); + +void state_reboot PROTO ((void *)); +void state_init PROTO ((void *)); +void state_selecting PROTO ((void *)); +void state_requesting PROTO ((void *)); +void state_bound PROTO ((void *)); +void state_stop PROTO ((void *)); +void state_panic PROTO ((void *)); + +void bind_lease PROTO ((struct client_state *)); + +void make_client_options PROTO ((struct client_state *, + struct client_lease *, u_int8_t *, + struct option_cache *, struct iaddr *, + u_int32_t *, struct option_state **)); +void make_discover PROTO ((struct client_state *, struct client_lease *)); +void make_request PROTO ((struct client_state *, struct client_lease *)); +void make_decline PROTO ((struct client_state *, struct client_lease *)); +void make_release PROTO ((struct client_state *, struct client_lease *)); + +void destroy_client_lease PROTO ((struct client_lease *)); +void rewrite_client_leases PROTO ((void)); +void write_lease_option (struct option_cache *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct universe *, void *); +int write_client_lease PROTO ((struct client_state *, + struct client_lease *, int, int)); +int dhcp_option_ev_name (char *, size_t, struct option *); + +void script_init PROTO ((struct client_state *, const char *, + struct string_list *)); +void client_option_envadd (struct option_cache *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct universe *, void *); +void script_write_params PROTO ((struct client_state *, + const char *, struct client_lease *)); +int script_go PROTO ((struct client_state *)); +void client_envadd (struct client_state *, + const char *, const char *, const char *, ...) + __attribute__((__format__(__printf__,4,5))); + +struct client_lease *packet_to_lease (struct packet *, struct client_state *); +void go_daemon PROTO ((void)); +void write_client_pid_file PROTO ((void)); +void client_location_changed PROTO ((void)); +void do_release PROTO ((struct client_state *)); +int dhclient_interface_shutdown_hook (struct interface_info *); +int dhclient_interface_discovery_hook (struct interface_info *); +isc_result_t dhclient_interface_startup_hook (struct interface_info *); +void client_dns_update_timeout (void *cp); +isc_result_t client_dns_update (struct client_state *client, int, int); + +/* db.c */ +int write_lease PROTO ((struct lease *)); +int write_host PROTO ((struct host_decl *)); +#if defined (FAILOVER_PROTOCOL) +int write_failover_state (dhcp_failover_state_t *); +#endif +int db_printable PROTO ((const char *)); +int db_printable_len PROTO ((const unsigned char *, unsigned)); +void write_named_billing_class (const char *, unsigned, struct class *); +void write_billing_classes (void); +int write_billing_class PROTO ((struct class *)); +void commit_leases_timeout PROTO ((void *)); +int commit_leases PROTO ((void)); +void db_startup PROTO ((int)); +int new_lease_file PROTO ((void)); +int group_writer (struct group_object *); + +/* packet.c */ +u_int32_t checksum PROTO ((unsigned char *, unsigned, u_int32_t)); +u_int32_t wrapsum PROTO ((u_int32_t)); +void assemble_hw_header PROTO ((struct interface_info *, unsigned char *, + unsigned *, struct hardware *)); +void assemble_udp_ip_header PROTO ((struct interface_info *, unsigned char *, + unsigned *, u_int32_t, u_int32_t, + u_int32_t, unsigned char *, unsigned)); +ssize_t decode_hw_header PROTO ((struct interface_info *, unsigned char *, + unsigned, struct hardware *)); +ssize_t decode_udp_ip_header PROTO ((struct interface_info *, unsigned char *, + unsigned, struct sockaddr_in *, + unsigned)); + +/* ethernet.c */ +void assemble_ethernet_header PROTO ((struct interface_info *, unsigned char *, + unsigned *, struct hardware *)); +ssize_t decode_ethernet_header PROTO ((struct interface_info *, + unsigned char *, + unsigned, struct hardware *)); + +/* tr.c */ +void assemble_tr_header PROTO ((struct interface_info *, unsigned char *, + unsigned *, struct hardware *)); +ssize_t decode_tr_header PROTO ((struct interface_info *, + unsigned char *, + unsigned, struct hardware *)); + +/* dhxpxlt.c */ +void convert_statement PROTO ((struct parse *)); +void convert_host_statement PROTO ((struct parse *, jrefproto)); +void convert_host_name PROTO ((struct parse *, jrefproto)); +void convert_class_statement PROTO ((struct parse *, jrefproto, int)); +void convert_class_decl PROTO ((struct parse *, jrefproto)); +void convert_lease_time PROTO ((struct parse *, jrefproto, char *)); +void convert_shared_net_statement PROTO ((struct parse *, jrefproto)); +void convert_subnet_statement PROTO ((struct parse *, jrefproto)); +void convert_subnet_decl PROTO ((struct parse *, jrefproto)); +void convert_host_decl PROTO ((struct parse *, jrefproto)); +void convert_hardware_decl PROTO ((struct parse *, jrefproto)); +void convert_hardware_addr PROTO ((struct parse *, jrefproto)); +void convert_filename_decl PROTO ((struct parse *, jrefproto)); +void convert_servername_decl PROTO ((struct parse *, jrefproto)); +void convert_ip_addr_or_hostname PROTO ((struct parse *, jrefproto, int)); +void convert_fixed_addr_decl PROTO ((struct parse *, jrefproto)); +void convert_option_decl PROTO ((struct parse *, jrefproto)); +void convert_timestamp PROTO ((struct parse *, jrefproto)); +void convert_lease_statement PROTO ((struct parse *, jrefproto)); +void convert_address_range PROTO ((struct parse *, jrefproto)); +void convert_date PROTO ((struct parse *, jrefproto, char *)); +void convert_numeric_aggregate PROTO ((struct parse *, jrefproto, int, int, int, int)); +void indent PROTO ((int)); + +/* route.c */ +void add_route_direct PROTO ((struct interface_info *, struct in_addr)); +void add_route_net PROTO ((struct interface_info *, struct in_addr, + struct in_addr)); +void add_route_default_gateway PROTO ((struct interface_info *, + struct in_addr)); +void remove_routes PROTO ((struct in_addr)); +void remove_if_route PROTO ((struct interface_info *, struct in_addr)); +void remove_all_if_routes PROTO ((struct interface_info *)); +void set_netmask PROTO ((struct interface_info *, struct in_addr)); +void set_broadcast_addr PROTO ((struct interface_info *, struct in_addr)); +void set_ip_address PROTO ((struct interface_info *, struct in_addr)); + +/* clparse.c */ +isc_result_t read_client_conf PROTO ((void)); +int read_client_conf_file (const char *, + struct interface_info *, struct client_config *); +void read_client_leases PROTO ((void)); +void parse_client_statement PROTO ((struct parse *, struct interface_info *, + struct client_config *)); +int parse_X PROTO ((struct parse *, u_int8_t *, unsigned)); +void parse_option_list PROTO ((struct parse *, u_int32_t **)); +void parse_interface_declaration PROTO ((struct parse *, + struct client_config *, char *)); +int interface_or_dummy PROTO ((struct interface_info **, const char *)); +void make_client_state PROTO ((struct client_state **)); +void make_client_config PROTO ((struct client_state *, + struct client_config *)); +void parse_client_lease_statement PROTO ((struct parse *, int)); +void parse_client_lease_declaration PROTO ((struct parse *, + struct client_lease *, + struct interface_info **, + struct client_state **)); +int parse_option_decl PROTO ((struct option_cache **, struct parse *)); +void parse_string_list PROTO ((struct parse *, struct string_list **, int)); +int parse_ip_addr PROTO ((struct parse *, struct iaddr *)); +void parse_reject_statement PROTO ((struct parse *, struct client_config *)); + +/* dhcrelay.c */ +void relay PROTO ((struct interface_info *, struct dhcp_packet *, unsigned, + unsigned int, struct iaddr, struct hardware *)); +int strip_relay_agent_options PROTO ((struct interface_info *, + struct interface_info **, + struct dhcp_packet *, unsigned)); +int find_interface_by_agent_option PROTO ((struct dhcp_packet *, + struct interface_info **, + u_int8_t *, int)); +int add_relay_agent_options PROTO ((struct interface_info *, + struct dhcp_packet *, + unsigned, struct in_addr)); + +/* icmp.c */ +OMAPI_OBJECT_ALLOC_DECL (icmp_state, struct icmp_state, dhcp_type_icmp) +extern struct icmp_state *icmp_state; +void icmp_startup PROTO ((int, void (*) PROTO ((struct iaddr, + u_int8_t *, int)))); +int icmp_readsocket PROTO ((omapi_object_t *)); +int icmp_echorequest PROTO ((struct iaddr *)); +isc_result_t icmp_echoreply PROTO ((omapi_object_t *)); + +/* dns.c */ +#if defined (NSUPDATE) +isc_result_t find_tsig_key (ns_tsig_key **, const char *, struct dns_zone *); +void tkey_free (ns_tsig_key **); +#endif +isc_result_t enter_dns_zone (struct dns_zone *); +isc_result_t dns_zone_lookup (struct dns_zone **, const char *); +int dns_zone_dereference PROTO ((struct dns_zone **, const char *, int)); +#if defined (NSUPDATE) +isc_result_t find_cached_zone (const char *, ns_class, char *, + size_t, struct in_addr *, int, int *, + struct dns_zone **); +void forget_zone (struct dns_zone **); +void repudiate_zone (struct dns_zone **); +void cache_found_zone (ns_class, char *, struct in_addr *, int); +int get_dhcid (struct data_string *, int, const u_int8_t *, unsigned); +isc_result_t ddns_update_a (struct data_string *, struct iaddr, + struct data_string *, unsigned long, int); +isc_result_t ddns_remove_a (struct data_string *, + struct iaddr, struct data_string *); +#endif /* NSUPDATE */ + +/* resolv.c */ +extern char path_resolv_conf []; +struct name_server *name_servers; +struct domain_search_list *domains; + +void read_resolv_conf PROTO ((TIME)); +struct name_server *first_name_server PROTO ((void)); + +/* inet_addr.c */ +#ifdef NEED_INET_ATON +int inet_aton PROTO ((const char *, struct in_addr *)); +#endif + +/* class.c */ +extern int have_billing_classes; +struct class unknown_class; +struct class known_class; +struct collection default_collection; +struct collection *collections; +struct executable_statement *default_classification_rules; + +void classification_setup PROTO ((void)); +void classify_client PROTO ((struct packet *)); +int check_collection PROTO ((struct packet *, struct lease *, + struct collection *)); +void classify PROTO ((struct packet *, struct class *)); +isc_result_t find_class PROTO ((struct class **, const char *, + const char *, int)); +int unbill_class PROTO ((struct lease *, struct class *)); +int bill_class PROTO ((struct lease *, struct class *)); + +/* execute.c */ +int execute_statements PROTO ((struct binding_value **result, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, + struct executable_statement *)); +void execute_statements_in_scope PROTO ((struct binding_value **result, + struct packet *, struct lease *, + struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct group *, struct group *)); +int executable_statement_dereference PROTO ((struct executable_statement **, + const char *, int)); +void write_statements (FILE *, struct executable_statement *, int); +int find_matching_case (struct executable_statement **, + struct packet *, struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, + struct expression *, struct executable_statement *); +int executable_statement_foreach (struct executable_statement *, + int (*) (struct executable_statement *, + void *, int), void *, int); + +/* comapi.c */ +extern omapi_object_type_t *dhcp_type_interface; +extern omapi_object_type_t *dhcp_type_group; +extern omapi_object_type_t *dhcp_type_shared_network; +extern omapi_object_type_t *dhcp_type_subnet; +extern omapi_object_type_t *dhcp_type_control; +extern dhcp_control_object_t *dhcp_control_object; + +void dhcp_common_objects_setup (void); + +isc_result_t dhcp_group_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_group_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_group_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_group_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_group_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_group_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_group_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_group_remove (omapi_object_t *, + omapi_object_t *); + +isc_result_t dhcp_control_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_control_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_control_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_control_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_control_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_control_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_control_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_control_remove (omapi_object_t *, + omapi_object_t *); + +isc_result_t dhcp_subnet_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_subnet_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_subnet_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_subnet_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_subnet_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_subnet_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_subnet_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_subnet_remove (omapi_object_t *, + omapi_object_t *); + +isc_result_t dhcp_shared_network_set_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_shared_network_get_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_shared_network_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_shared_network_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_shared_network_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_shared_network_remove (omapi_object_t *, + omapi_object_t *); + +/* omapi.c */ +extern int (*dhcp_interface_shutdown_hook) (struct interface_info *); + +extern omapi_object_type_t *dhcp_type_lease; +extern omapi_object_type_t *dhcp_type_pool; +extern omapi_object_type_t *dhcp_type_class; + +#if defined (FAILOVER_PROTOCOL) +extern omapi_object_type_t *dhcp_type_failover_state; +extern omapi_object_type_t *dhcp_type_failover_link; +extern omapi_object_type_t *dhcp_type_failover_listener; +#endif + +void dhcp_db_objects_setup (void); + +isc_result_t dhcp_lease_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_lease_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_lease_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_lease_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_lease_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_lease_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_lease_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_lease_remove (omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_group_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_group_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_group_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_group_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_group_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_group_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_group_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_group_remove (omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_host_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_host_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_host_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_host_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_host_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_host_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_host_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_host_remove (omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_pool_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_pool_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_pool_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_pool_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_pool_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_pool_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_pool_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_pool_remove (omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_class_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_class_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_class_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_class_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_class_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_class_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_class_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_class_remove (omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_subclass_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_subclass_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_subclass_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_subclass_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_subclass_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_subclass_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_subclass_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_subclass_remove (omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_shared_network_set_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_shared_network_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_shared_network_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_shared_network_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_shared_network_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_subnet_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_subnet_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_subnet_destroy (omapi_object_t *, const char *, int); +isc_result_t dhcp_subnet_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t dhcp_subnet_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_subnet_lookup (omapi_object_t **, + omapi_object_t *, omapi_object_t *); +isc_result_t dhcp_subnet_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_interface_set_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t dhcp_interface_get_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t dhcp_interface_destroy (omapi_object_t *, + const char *, int); +isc_result_t dhcp_interface_signal_handler (omapi_object_t *, + const char *, + va_list ap); +isc_result_t dhcp_interface_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_interface_lookup (omapi_object_t **, + omapi_object_t *, + omapi_object_t *); +isc_result_t dhcp_interface_create (omapi_object_t **, + omapi_object_t *); +isc_result_t dhcp_interface_remove (omapi_object_t *, + omapi_object_t *); +void interface_stash (struct interface_info *); +void interface_snorf (struct interface_info *, int); + +isc_result_t binding_scope_set_value (struct binding_scope *, int, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t binding_scope_get_value (omapi_value_t **, + struct binding_scope *, + omapi_data_string_t *); +isc_result_t binding_scope_stuff_values (omapi_object_t *, + struct binding_scope *); + +/* mdb.c */ + +extern struct subnet *subnets; +extern struct shared_network *shared_networks; +extern host_hash_t *host_hw_addr_hash; +extern host_hash_t *host_uid_hash; +extern host_hash_t *host_name_hash; +extern lease_hash_t *lease_uid_hash; +extern lease_hash_t *lease_ip_addr_hash; +extern lease_hash_t *lease_hw_addr_hash; + +extern omapi_object_type_t *dhcp_type_host; + +isc_result_t enter_host PROTO ((struct host_decl *, int, int)); +isc_result_t delete_host PROTO ((struct host_decl *, int)); +int find_hosts_by_haddr PROTO ((struct host_decl **, int, + const unsigned char *, unsigned, + const char *, int)); +int find_hosts_by_uid PROTO ((struct host_decl **, const unsigned char *, + unsigned, const char *, int)); +int find_host_for_network PROTO ((struct subnet **, struct host_decl **, + struct iaddr *, struct shared_network *)); +void new_address_range PROTO ((struct parse *, struct iaddr, struct iaddr, + struct subnet *, struct pool *, + struct lease **)); +isc_result_t dhcp_lease_free (omapi_object_t *, const char *, int); +isc_result_t dhcp_lease_get (omapi_object_t **, const char *, int); +int find_grouped_subnet PROTO ((struct subnet **, struct shared_network *, + struct iaddr, const char *, int)); +int find_subnet (struct subnet **, struct iaddr, const char *, int); +void enter_shared_network PROTO ((struct shared_network *)); +void new_shared_network_interface PROTO ((struct parse *, + struct shared_network *, + const char *)); +int subnet_inner_than PROTO ((struct subnet *, struct subnet *, int)); +void enter_subnet PROTO ((struct subnet *)); +void enter_lease PROTO ((struct lease *)); +int supersede_lease PROTO ((struct lease *, struct lease *, int, int, int)); +void make_binding_state_transition (struct lease *); +int lease_copy PROTO ((struct lease **, struct lease *, const char *, int)); +void release_lease PROTO ((struct lease *, struct packet *)); +void abandon_lease PROTO ((struct lease *, const char *)); +void dissociate_lease PROTO ((struct lease *)); +void pool_timer PROTO ((void *)); +int find_lease_by_uid PROTO ((struct lease **, const unsigned char *, + unsigned, const char *, int)); +int find_lease_by_hw_addr PROTO ((struct lease **, const unsigned char *, + unsigned, const char *, int)); +int find_lease_by_ip_addr PROTO ((struct lease **, struct iaddr, + const char *, int)); +void uid_hash_add PROTO ((struct lease *)); +void uid_hash_delete PROTO ((struct lease *)); +void hw_hash_add PROTO ((struct lease *)); +void hw_hash_delete PROTO ((struct lease *)); +int write_leases PROTO ((void)); +int lease_enqueue (struct lease *); +void lease_instantiate (const unsigned char *, unsigned, struct lease *); +void expire_all_pools PROTO ((void)); +void dump_subnets PROTO ((void)); +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void free_everything (void); +#endif + +/* nsupdate.c */ +char *ddns_rev_name (struct lease *, struct lease_state *, struct packet *); +char *ddns_fwd_name (struct lease *, struct lease_state *, struct packet *); +int nsupdateA (const char *, const unsigned char *, u_int32_t, int); +int nsupdatePTR (const char *, const unsigned char *, u_int32_t, int); +void nsupdate (struct lease *, struct lease_state *, struct packet *, int); +int updateA (const struct data_string *, const struct data_string *, + unsigned int, struct lease *); +int updatePTR (const struct data_string *, const struct data_string *, + unsigned int, struct lease *); +int deleteA (const struct data_string *, const struct data_string *, + struct lease *); +int deletePTR (const struct data_string *, const struct data_string *, + struct lease *); + +/* failover.c */ +#if defined (FAILOVER_PROTOCOL) +extern dhcp_failover_state_t *failover_states; +void dhcp_failover_startup PROTO ((void)); +int dhcp_failover_write_all_states (void); +isc_result_t enter_failover_peer PROTO ((dhcp_failover_state_t *)); +isc_result_t find_failover_peer PROTO ((dhcp_failover_state_t **, + const char *, const char *, int)); +isc_result_t dhcp_failover_link_initiate PROTO ((omapi_object_t *)); +isc_result_t dhcp_failover_link_signal PROTO ((omapi_object_t *, + const char *, va_list)); +isc_result_t dhcp_failover_link_set_value PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *)); +isc_result_t dhcp_failover_link_get_value PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **)); +isc_result_t dhcp_failover_link_destroy PROTO ((omapi_object_t *, + const char *, int)); +isc_result_t dhcp_failover_link_stuff_values PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_object_t *)); +isc_result_t dhcp_failover_listen PROTO ((omapi_object_t *)); + +isc_result_t dhcp_failover_listener_signal PROTO ((omapi_object_t *, + const char *, + va_list)); +isc_result_t dhcp_failover_listener_set_value PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *)); +isc_result_t dhcp_failover_listener_get_value PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **)); +isc_result_t dhcp_failover_listener_destroy PROTO ((omapi_object_t *, + const char *, int)); +isc_result_t dhcp_failover_listener_stuff PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_object_t *)); +isc_result_t dhcp_failover_register PROTO ((omapi_object_t *)); +isc_result_t dhcp_failover_state_signal PROTO ((omapi_object_t *, + const char *, va_list)); +isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *, + const char *); +isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state); +isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *, + enum failover_state); +isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *, + failover_message_t *); +int dhcp_failover_pool_rebalance (dhcp_failover_state_t *); +int dhcp_failover_pool_check (struct pool *); +int dhcp_failover_state_pool_check (dhcp_failover_state_t *); +void dhcp_failover_timeout (void *); +void dhcp_failover_send_contact (void *); +isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *); +isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *); +int dhcp_failover_queue_update (struct lease *, int); +int dhcp_failover_send_acks (dhcp_failover_state_t *); +void dhcp_failover_toack_queue_timeout (void *); +int dhcp_failover_queue_ack (dhcp_failover_state_t *, failover_message_t *msg); +void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *, struct lease *); +isc_result_t dhcp_failover_state_set_value PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *)); +void dhcp_failover_keepalive (void *); +void dhcp_failover_reconnect (void *); +void dhcp_failover_startup_timeout (void *); +void dhcp_failover_link_startup_timeout (void *); +void dhcp_failover_listener_restart (void *); +isc_result_t dhcp_failover_state_get_value PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **)); +isc_result_t dhcp_failover_state_destroy PROTO ((omapi_object_t *, + const char *, int)); +isc_result_t dhcp_failover_state_stuff PROTO ((omapi_object_t *, + omapi_object_t *, + omapi_object_t *)); +isc_result_t dhcp_failover_state_lookup PROTO ((omapi_object_t **, + omapi_object_t *, + omapi_object_t *)); +isc_result_t dhcp_failover_state_create PROTO ((omapi_object_t **, + omapi_object_t *)); +isc_result_t dhcp_failover_state_remove PROTO ((omapi_object_t *, + omapi_object_t *)); +int dhcp_failover_state_match (dhcp_failover_state_t *, u_int8_t *, unsigned); +const char *dhcp_failover_reject_reason_print (int); +const char *dhcp_failover_state_name_print (enum failover_state); +const char *dhcp_failover_message_name (unsigned); +const char *dhcp_failover_option_name (unsigned); +failover_option_t *dhcp_failover_option_printf (unsigned, char *, + unsigned *, + unsigned, + const char *, ...) + __attribute__((__format__(__printf__,5,6))); +failover_option_t *dhcp_failover_make_option (unsigned, char *, + unsigned *, unsigned, ...); +isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *, + omapi_object_t *, int, ...); +isc_result_t dhcp_failover_send_connect PROTO ((omapi_object_t *)); +isc_result_t dhcp_failover_send_connectack PROTO ((omapi_object_t *, + dhcp_failover_state_t *, + int, const char *)); +isc_result_t dhcp_failover_send_disconnect PROTO ((omapi_object_t *, + int, const char *)); +isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *, + struct lease *); +isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *, + failover_message_t *, + int, const char *); +isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *); +isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *, int); +isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *); +isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t *); +isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *); +isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *, + failover_message_t *); +isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *, + failover_message_t *); +isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *, + int); +isc_result_t dhcp_failover_process_update_request (dhcp_failover_state_t *, + failover_message_t *); +isc_result_t dhcp_failover_process_update_request_all (dhcp_failover_state_t *, + failover_message_t *); +isc_result_t dhcp_failover_process_update_done (dhcp_failover_state_t *, + failover_message_t *); +void dhcp_failover_recover_done (void *); +void failover_print PROTO ((char *, unsigned *, unsigned, const char *)); +void update_partner PROTO ((struct lease *)); +int load_balance_mine (struct packet *, dhcp_failover_state_t *); +binding_state_t normal_binding_state_transition_check (struct lease *, + dhcp_failover_state_t *, + binding_state_t, + u_int32_t); +binding_state_t +conflict_binding_state_transition_check (struct lease *, + dhcp_failover_state_t *, + binding_state_t, u_int32_t); +int lease_mine_to_reallocate (struct lease *); + +OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_state, dhcp_failover_state_t, + dhcp_type_failover_state) +OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_listener, dhcp_failover_listener_t, + dhcp_type_failover_listener) +OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_link, dhcp_failover_link_t, + dhcp_type_failover_link) +#endif /* FAILOVER_PROTOCOL */ + +const char *binding_state_print (enum failover_state); diff --git a/contrib/dhcp-3.0/includes/dhctoken.h b/contrib/dhcp-3.0/includes/dhctoken.h new file mode 100644 index 0000000000..858e3c989b --- /dev/null +++ b/contrib/dhcp-3.0/includes/dhctoken.h @@ -0,0 +1,317 @@ +/* dhctoken.h + + Tokens for config file lexer and parser. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +enum dhcp_token { + SEMI = ';', + DOT = '.', + COLON = ':', + COMMA = ',', + SLASH = '/', + LBRACE = '{', + RBRACE = '}', + LPAREN = '(', + RPAREN = ')', + EQUAL = '=', + BANG = '!', + PERCENT = '%', + PLUS = '+', + MINUS = '-', + ASTERISK = '*', + AMPERSAND = '&', + PIPE = '|', + CARET = '^', + + HOST = 256, + FIRST_TOKEN = HOST, + HARDWARE = 257, + FILENAME = 258, + FIXED_ADDR = 259, + OPTION = 260, + ETHERNET = 261, + STRING = 262, + NUMBER = 263, + NUMBER_OR_NAME = 264, + NAME = 265, + TIMESTAMP = 266, + STARTS = 267, + ENDS = 268, + UID = 269, + CLASS = 270, + LEASE = 271, + RANGE = 272, + PACKET = 273, + CIADDR = 274, + YIADDR = 275, + SIADDR = 276, + GIADDR = 277, + SUBNET = 278, + NETMASK = 279, + DEFAULT_LEASE_TIME = 280, + MAX_LEASE_TIME = 281, + VENDOR_CLASS = 282, + USER_CLASS = 283, + SHARED_NETWORK = 284, + SERVER_NAME = 285, + DYNAMIC_BOOTP = 286, + SERVER_IDENTIFIER = 287, + DYNAMIC_BOOTP_LEASE_CUTOFF = 288, + DYNAMIC_BOOTP_LEASE_LENGTH = 289, + BOOT_UNKNOWN_CLIENTS = 290, + NEXT_SERVER = 291, + TOKEN_RING = 292, + GROUP = 293, + ONE_LEASE_PER_CLIENT = 294, + GET_LEASE_HOSTNAMES = 295, + USE_HOST_DECL_NAMES = 296, + SEND = 297, + CLIENT_IDENTIFIER = 298, + REQUEST = 299, + REQUIRE = 300, + TIMEOUT = 301, + RETRY = 302, + SELECT_TIMEOUT = 303, + SCRIPT = 304, + INTERFACE = 305, + RENEW = 306, + REBIND = 307, + EXPIRE = 308, + UNKNOWN_CLIENTS = 309, + ALLOW = 310, + DENY = 312, + BOOTING = 313, + DEFAULT = 314, + MEDIA = 315, + MEDIUM = 316, + ALIAS = 317, + REBOOT = 318, + TOKEN_ABANDONED = 319, + BACKOFF_CUTOFF = 320, + INITIAL_INTERVAL = 321, + NAMESERVER = 322, + DOMAIN = 323, + SEARCH = 324, + SUPERSEDE = 325, + APPEND = 326, + PREPEND = 327, + HOSTNAME = 328, + CLIENT_HOSTNAME = 329, + REJECT = 330, + USE_LEASE_ADDR_FOR_DEFAULT_ROUTE = 331, + MIN_LEASE_TIME = 332, + MIN_SECS = 333, + AND = 334, + OR = 335, + SUBSTRING = 337, + SUFFIX = 338, + CHECK = 339, + EXTRACT_INT = 340, + IF = 341, + TOKEN_ADD = 342, + BREAK = 343, + ELSE = 344, + ELSIF = 345, + SUBCLASS = 346, + MATCH = 347, + SPAWN = 348, + WITH = 349, + EXISTS = 350, + POOL = 351, + UNKNOWN = 352, + CLIENTS = 353, + KNOWN = 354, + AUTHENTICATED = 355, + UNAUTHENTICATED = 356, + ALL = 357, + DYNAMIC = 358, + MEMBERS = 359, + OF = 360, + PSEUDO = 361, + LIMIT = 362, + BILLING = 363, + PEER = 364, + FAILOVER = 365, + MY = 366, + PARTNER = 367, + PRIMARY = 368, + SECONDARY = 369, + IDENTIFIER = 370, + PORT = 371, + MAX_TRANSMIT_IDLE = 372, + MAX_RESPONSE_DELAY = 373, + PARTNER_DOWN = 374, + NORMAL = 375, + COMMUNICATIONS_INTERRUPTED = 376, + POTENTIAL_CONFLICT = 377, + RECOVER = 378, + FDDI = 379, + AUTHORITATIVE = 380, + TOKEN_NOT = 381, + AUTHENTICATION = 383, + IGNORE = 384, + ACCEPT = 385, + PREFER = 386, + DONT = 387, + CODE = 388, + ARRAY = 389, + BOOLEAN = 390, + INTEGER = 391, + SIGNED = 392, + UNSIGNED = 393, + IP_ADDRESS = 394, + TEXT = 395, + STRING_TOKEN = 396, + SPACE = 397, + CONCAT = 398, + ENCODE_INT = 399, + REVERSE = 402, + LEASED_ADDRESS = 403, + BINARY_TO_ASCII = 404, + PICK = 405, + CONFIG_OPTION = 406, + HOST_DECL_NAME = 407, + ON = 408, + EXPIRY = 409, + RELEASE = 410, + COMMIT = 411, + DNS_UPDATE = 412, + LEASE_TIME = 413, + STATIC = 414, + NEVER = 415, + INFINITE = 416, + TOKEN_DELETED = 417, + UPDATED_DNS_RR = 418, + DNS_DELETE = 419, + DUPLICATES = 420, + DECLINES = 421, + TSTP = 422, + TSFP = 423, + OWNER = 424, + IS = 425, + HBA = 426, + MAX_UNACKED_UPDATES = 427, + MCLT = 428, + SPLIT = 429, + AT = 430, + NO = 431, + TOKEN_DELETE = 432, + NS_UPDATE = 433, + UPDATE = 434, + SWITCH = 435, + CASE = 436, + NS_FORMERR = 437, + NS_NOERROR = 438, + NS_NOTAUTH = 439, + NS_NOTIMP = 440, + NS_NOTZONE = 441, + NS_NXDOMAIN = 442, + NS_NXRRSET = 443, + NS_REFUSED = 444, + NS_SERVFAIL = 445, + NS_YXDOMAIN = 446, + NS_YXRRSET = 447, + TOKEN_NULL = 448, + TOKEN_SET = 449, + DEFINED = 450, + UNSET = 451, + EVAL = 452, + LET = 453, + FUNCTION = 454, + DEFINE = 455, + ZONE = 456, + KEY = 457, + SECRET = 458, + ALGORITHM = 459, + LOAD = 460, + BALANCE = 461, + TOKEN_MAX = 462, + SECONDS = 463, + ADDRESS = 464, + RESOLUTION_INTERRUPTED = 465, + STATE = 466, + UNKNOWN_STATE = 567, + CLTT = 568, + INCLUDE = 569, + BINDING = 570, + TOKEN_FREE = 571, + TOKEN_ACTIVE = 572, + TOKEN_EXPIRED = 573, + TOKEN_RELEASED = 574, + TOKEN_RESET = 575, + TOKEN_BACKUP = 576, + TOKEN_RESERVED = 577, + TOKEN_BOOTP = 578, + TOKEN_NEXT = 579, + OMAPI = 580, + LOG = 581, + FATAL = 582, + ERROR = 583, + TOKEN_DEBUG = 584, + INFO = 585, + RETURN = 586, + PAUSED = 587, + RECOVER_DONE = 588, + SHUTDOWN = 589, + STARTUP = 590, + ENCAPSULATE = 591, + VENDOR = 592, + CLIENT_STATE = 593, + INIT_REBOOT = 594, + TOKEN_INIT = 595, + SELECT = 596, + BOUND = 597, + RENEWING = 598, + REBINDING = 599, + RECONTACT_INTERVAL = 600, + CLIENT_UPDATES = 601, + TOKEN_NEW = 601, + TRANSMISSION = 602, + TOKEN_CLOSE = 603, + TOKEN_CREATE = 604, + TOKEN_OPEN = 605, + TOKEN_HELP = 606, + END_OF_FILE = 607, + RECOVER_WAIT = 608, + SERVER = 609, + CONNECT = 610, + REMOVE = 611, + REFRESH = 612, + DOMAIN_NAME = 613, + DO_FORWARD_UPDATE = 614, + KNOWN_CLIENTS = 615 +}; + +#define is_identifier(x) ((x) >= FIRST_TOKEN && \ + (x) != STRING && \ + (x) != NUMBER && \ + (x) != END_OF_FILE) diff --git a/contrib/dhcp-3.0/includes/failover.h b/contrib/dhcp-3.0/includes/failover.h new file mode 100644 index 0000000000..2fd880d1e1 --- /dev/null +++ b/contrib/dhcp-3.0/includes/failover.h @@ -0,0 +1,313 @@ +/* failover.h + + Definitions for address trees... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2000-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#if defined (FAILOVER_PROTOCOL) +struct failover_option_info { + int code; + const char *name; + enum { FT_UINT8, FT_IPADDR, FT_UINT32, FT_BYTES, FT_TEXT_OR_BYTES, + FT_DDNS, FT_DDNS1, FT_UINT16, FT_TEXT, + FT_UNDEF, FT_DIGEST } type; + int num_present; + int offset; + u_int32_t bit; +}; + +typedef struct { + unsigned count; + u_int8_t *data; +} failover_option_t; + +#define FM_OFFSET(x) (long)(&(((failover_message_t *)0) -> x)) + +/* Failover message options: */ +#define FTO_BINDING_STATUS 1 +#define FTB_BINDING_STATUS 0x00000002 +#define FTO_ASSIGNED_IP_ADDRESS 2 +#define FTB_ASSIGNED_IP_ADDRESS 0x00000004 +#define FTO_SERVER_ADDR 3 +#define FTB_SERVER_ADDR 0x00000008 +#define FTO_ADDRESSES_TRANSFERRED 4 +#define FTB_ADDRESSES_TRANSFERRED 0x00000010 +#define FTO_CLIENT_IDENTIFIER 5 +#define FTB_CLIENT_IDENTIFIER 0x00000020 +#define FTO_CHADDR 6 +#define FTB_CHADDR 0x00000040 +#define FTO_DDNS 7 +#define FTB_DDNS 0x00000080 +#define FTO_REJECT_REASON 8 +#define FTB_REJECT_REASON 0x00000100 +#define FTO_MESSAGE 9 +#define FTB_MESSAGE 0x00000200 +#define FTO_MCLT 10 +#define FTB_MCLT 0x00000400 +#define FTO_VENDOR_CLASS 11 +#define FTB_VENDOR_CLASS 0x00000800 +#define FTO_LEASE_EXPIRY 13 +#define FTB_LEASE_EXPIRY 0x00002000 +#define FTO_POTENTIAL_EXPIRY 14 +#define FTB_POTENTIAL_EXPIRY 0x00004000 +#define FTO_GRACE_EXPIRY 15 +#define FTB_GRACE_EXPIRY 0x00008000 +#define FTO_CLTT 16 +#define FTB_CLTT 0x00010000 +#define FTO_STOS 17 +#define FTB_STOS 0x00020000 +#define FTO_SERVER_STATE 18 +#define FTB_SERVER_STATE 0x00040000 +#define FTO_SERVER_FLAGS 19 +#define FTB_SERVER_FLAGS 0x00080000 +#define FTO_VENDOR_OPTIONS 20 +#define FTB_VENDOR_OPTIONS 0x00100000 +#define FTO_MAX_UNACKED 21 +#define FTB_MAX_UNACKED 0x00200000 +#define FTO_RECEIVE_TIMER 23 +#define FTB_RECEIVE_TIMER 0x00800000 +#define FTO_HBA 24 +#define FTB_HBA 0x01000000 +#define FTO_MESSAGE_DIGEST 25 +#define FTB_MESSAGE_DIGEST 0x02000000 +#define FTO_PROTOCOL_VERSION 26 +#define FTB_PROTOCOL_VERSION 0x04000000 +#define FTO_TLS_REQUEST 27 +#define FTB_TLS_REQUEST 0x08000000 +#define FTO_TLS_REPLY 28 +#define FTB_TLS_REPLY 0x10000000 +#define FTO_REQUEST_OPTIONS 29 +#define FTB_REQUEST_OPTIONS 0x20000000 +#define FTO_REPLY_OPTIONS 30 +#define FTB_REPLY_OPTIONS 0x40000000 +#define FTO_MAX FTO_REPLY_OPTIONS + +/* Failover protocol message types: */ +#define FTM_POOLREQ 1 +#define FTM_POOLRESP 2 +#define FTM_BNDUPD 3 +#define FTM_BNDACK 4 +#define FTM_CONNECT 5 +#define FTM_CONNECTACK 6 +#define FTM_UPDREQ 7 +#define FTM_UPDDONE 8 +#define FTM_UPDREQALL 9 +#define FTM_STATE 10 +#define FTM_CONTACT 11 +#define FTM_DISCONNECT 12 + +/* Reject reasons: */ + +#define FTR_ILLEGAL_IP_ADDR 1 +#define FTR_FATAL_CONFLICT 2 +#define FTR_MISSING_BINDINFO 3 +#define FTR_TIMEMISMATCH 4 +#define FTR_INVALID_MCLT 5 +#define FTR_MISC_REJECT 6 +#define FTR_DUP_CONNECTION 7 +#define FTR_INVALID_PARTNER 8 +#define FTR_TLS_UNSUPPORTED 9 +#define FTR_TLS_UNCONFIGURED 10 +#define FTR_TLS_REQUIRED 11 +#define FTR_DIGEST_UNSUPPORTED 12 +#define FTR_DIGEST_UNCONFIGURED 13 +#define FTR_VERSION_MISMATCH 14 +#define FTR_MISSING_BIND_INFO 15 +#define FTR_OUTDATED_BIND_INFO 16 +#define FTR_LESS_CRIT_BIND_INFO 17 +#define FTR_NO_TRAFFIC 18 +#define FTR_HBA_CONFLICT 19 +#define FTR_UNKNOWN 254 + +#define DHCP_FAILOVER_MAX_MESSAGE_SIZE 2048 + +/* Failover server flags. */ +#define FTF_STARTUP 1 + +typedef struct failover_message { + int refcnt; + struct failover_message *next; + + u_int8_t type; + + u_int8_t binding_status; + u_int8_t protocol_version; + u_int8_t reject_reason; + u_int8_t server_flags; + u_int8_t server_state; + u_int8_t tls_reply; + u_int8_t tls_request; + u_int32_t stos; + u_int32_t time; + u_int32_t xid; + u_int32_t addresses_transferred; + u_int32_t assigned_addr; + u_int32_t client_ltt; + u_int32_t expiry; + u_int32_t grace_expiry; + u_int32_t max_unacked; + u_int32_t mclt; + u_int32_t potential_expiry; + u_int32_t receive_timer; + u_int32_t server_addr; + failover_option_t chaddr; + failover_option_t client_identifier; + failover_option_t hba; + failover_option_t message; + failover_option_t reply_options; + failover_option_t request_options; + ddns_fqdn_t ddns; + failover_option_t vendor_class; + failover_option_t vendor_options; + + int options_present; +} failover_message_t; + +typedef struct { + OMAPI_OBJECT_PREAMBLE; + struct option_cache *peer_address; + unsigned peer_port; + int options_present; + enum dhcp_flink_state { + dhcp_flink_start, + dhcp_flink_message_length_wait, + dhcp_flink_message_wait, + dhcp_flink_disconnected, + dhcp_flink_state_max + } state; + failover_message_t *imsg; + struct _dhcp_failover_state *state_object; + u_int16_t imsg_len; + unsigned imsg_count; + u_int8_t imsg_payoff; /* Pay*load* offset. :') */ + u_int32_t xid; +} dhcp_failover_link_t; + +typedef struct _dhcp_failover_listener { + OMAPI_OBJECT_PREAMBLE; + struct _dhcp_failover_listener *next; + omapi_addr_t address; +} dhcp_failover_listener_t; +#endif /* FAILOVER_PROTOCOL */ + +/* A failover peer. */ +enum failover_state { + unknown_state, + partner_down, + normal, + communications_interrupted, + resolution_interrupted, + potential_conflict, + recover, + recover_done, + shut_down, + paused, + startup, + recover_wait +}; + +/* Service states are simplifications of failover states, particularly + useful because the startup state isn't actually implementable as a + seperate failover state without maintaining a state stack. */ + +enum service_state { + unknown_service_state, + cooperating, + not_cooperating, + service_partner_down, + not_responding, + service_startup +}; + +#if defined (FAILOVER_PROTOCOL) +typedef struct _dhcp_failover_config { + struct option_cache *address; + int port; + u_int32_t max_flying_updates; + enum failover_state state; + TIME stos; + u_int32_t max_response_delay; +} dhcp_failover_config_t; + +typedef struct _dhcp_failover_state { + OMAPI_OBJECT_PREAMBLE; + struct _dhcp_failover_state *next; + char *name; /* Name of this failover instance. */ + dhcp_failover_config_t me; /* My configuration. */ + dhcp_failover_config_t partner; /* Partner's configuration. */ + enum failover_state saved_state; /* Saved state during startup. */ + struct data_string server_identifier; /* Server identifier (IP addr) */ + u_int32_t mclt; + + u_int8_t *hba; /* Hash bucket array for load balancing. */ + int load_balance_max_secs; + + enum service_state service_state; + const char *nrr; /* Printable reason why we're in the + not_responding service state (empty + string if we are responding. */ + + dhcp_failover_link_t *link_to_peer; /* Currently-established link + to peer. */ + + enum { + primary, secondary + } i_am; /* We are primary or secondary in this relationship. */ + + TIME last_packet_sent; /* Timestamp on last packet we sent. */ + TIME last_timestamp_received; /* The last timestamp we sent that + has been returned by our partner. */ + TIME skew; /* The skew between our clock and our partner's. */ + struct lease *update_queue_head; /* List of leases we haven't sent + to peer. */ + struct lease *update_queue_tail; + + struct lease *ack_queue_head; /* List of lease updates the peer + hasn't yet acked. */ + struct lease *ack_queue_tail; + + struct lease *send_update_done; /* When we get a BNDACK for this + lease, send an UPDDONE message. */ + int cur_unacked_updates; /* Number of updates we've sent + that have not yet been acked. */ + + /* List of messages which we haven't + acked yet. */ + failover_message_t *toack_queue_head; + failover_message_t *toack_queue_tail; + int pending_acks; /* Number of messages in the toack + queue. */ + int pool_count; /* Number of pools referencing this + failover state object. */ +} dhcp_failover_state_t; + +#define DHCP_FAILOVER_VERSION 1 +#endif /* FAILOVER_PROTOCOL */ diff --git a/contrib/dhcp-3.0/includes/inet.h b/contrib/dhcp-3.0/includes/inet.h new file mode 100644 index 0000000000..d037d78be4 --- /dev/null +++ b/contrib/dhcp-3.0/includes/inet.h @@ -0,0 +1,45 @@ +/* inet.h + + Portable definitions for internet addresses */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +/* An internet address of up to 128 bits. */ + +struct iaddr { + unsigned len; + unsigned char iabuf [16]; +}; + +struct iaddrlist { + struct iaddrlist *next; + struct iaddr addr; +}; diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/boolean.h b/contrib/dhcp-3.0/includes/isc-dhcp/boolean.h new file mode 100644 index 0000000000..e0dd968ca1 --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/boolean.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef ISC_BOOLEAN_H +#define ISC_BOOLEAN_H 1 + +#include + +ISC_LANG_BEGINDECLS + +typedef enum { isc_boolean_false = 0, isc_boolean_true = 1 } isc_boolean_t; + +#define ISC_FALSE isc_boolean_false +#define ISC_TRUE isc_boolean_true + +ISC_LANG_ENDDECLS + +#endif /* ISC_BOOLEAN_H */ diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/dst.h b/contrib/dhcp-3.0/includes/isc-dhcp/dst.h new file mode 100644 index 0000000000..65c54d24eb --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/dst.h @@ -0,0 +1,142 @@ +#ifndef DST_H +#define DST_H + +#ifndef HAS_DST_KEY +typedef struct dst_key { + char *dk_key_name; /* name of the key */ + int dk_key_size; /* this is the size of the key in bits */ + int dk_proto; /* what protocols this key can be used for */ + int dk_alg; /* algorithm number from key record */ + unsigned dk_flags; /* and the flags of the public key */ + unsigned dk_id; /* identifier of the key */ +} DST_KEY; +#endif /* HAS_DST_KEY */ + +/* + * DST Crypto API defintions + */ +void dst_init(void); +int dst_check_algorithm(const int); + +int dst_sign_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *in_key, /* the key to use */ + void **context, /* pointer to state structure */ + const u_char *data, /* data to be signed */ + const unsigned len, /* length of input data */ + u_char *signature, /* buffer to write signature to */ + const unsigned sig_len); /* size of output buffer */ + +int dst_verify_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *in_key, /* the key to use */ + void **context, /* pointer to state structure */ + const u_char *data, /* data to be verified */ + const unsigned len, /* length of input data */ + const u_char *signature,/* buffer containing signature */ + const unsigned sig_len); /* length of signature */ + + +DST_KEY *dst_read_key(const char *in_name, /* name of key */ + const unsigned in_id, /* key tag identifier */ + const int in_alg, /* key algorithm */ + const int key_type); /* Private/PublicKey wanted*/ + +int dst_write_key(const DST_KEY *key, /* key to write out */ + const int key_type); /* Public/Private */ + +DST_KEY *dst_dnskey_to_key(const char *in_name, /* KEY record name */ + const u_char *key, /* KEY RDATA */ + const unsigned len); /* size of input buffer*/ + + +int dst_key_to_dnskey(const DST_KEY *key, /* key to translate */ + u_char *out_storage, /* output buffer */ + const unsigned out_len); /* size of out_storage*/ + + +DST_KEY *dst_buffer_to_key(const char *key_name, /* name of the key */ + const int alg, /* algorithm */ + const unsigned flags, /* dns flags */ + const int protocol, /* dns protocol */ + const u_char *key_buf, /* key in dns wire fmt */ + const unsigned key_len); /* size of key */ + + +int dst_key_to_buffer(DST_KEY *key, u_char *out_buff, unsigned buf_len); + +DST_KEY *dst_generate_key(const char *name, /* name of new key */ + const int bits, /* size of new key */ + const int exp, /* alg dependent parameter*/ + const unsigned flags, /* key DNS flags */ + const int protocol, /* key DNS protocol */ + const int alg); /* key algorithm to generate */ + +DST_KEY *dst_free_key(DST_KEY *f_key); +int dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2); + +int dst_sig_size(DST_KEY *key); + +int dst_random(const int mode, unsigned wanted, u_char *outran); + + +/* support for dns key tags/ids */ +u_int16_t dst_s_dns_key_id(const u_char *dns_key_rdata, + const unsigned rdata_len); +u_int16_t dst_s_id_calc(const u_char *key_data, const unsigned key_len); + +/* Used by callers as well as by the library. */ +#define RAW_KEY_SIZE 8192 /* large enough to store any key */ + +/* DST_API control flags */ +/* These are used used in functions dst_sign_data and dst_verify_data */ +#define SIG_MODE_INIT 1 /* initalize digest */ +#define SIG_MODE_UPDATE 2 /* add data to digest */ +#define SIG_MODE_FINAL 4 /* generate/verify signature */ +#define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL) + +/* Flags for dst_read_private_key() */ +#define DST_FORCE_READ 0x1000000 +#define DST_CAN_SIGN 0x010F +#define DST_NO_AUTHEN 0x8000 +#define DST_EXTEND_FLAG 0x1000 +#define DST_STANDARD 0 +#define DST_PRIVATE 0x2000000 +#define DST_PUBLIC 0x4000000 +#define DST_RAND_SEMI 1 +#define DST_RAND_STD 2 +#define DST_RAND_KEY 3 +#define DST_RAND_DSS 4 + + +/* DST algorithm codes */ +#define KEY_RSA 1 +#define KEY_DH 2 +#define KEY_DSA 3 +#define KEY_PRIVATE 254 +#define KEY_EXPAND 255 +#define KEY_HMAC_MD5 157 +#define KEY_HMAC_SHA1 158 +#define UNKNOWN_KEYALG 0 +#define DST_MAX_ALGS KEY_HMAC_SHA1 + +/* DST constants to locations in KEY record changes in new KEY record */ +#define DST_FLAGS_SIZE 2 +#define DST_KEY_PROT 2 +#define DST_KEY_ALG 3 +#define DST_EXT_FLAG 4 +#define DST_KEY_START 4 + +#ifndef SIGN_F_NOKEY +#define SIGN_F_NOKEY 0xC000 +#endif + +/* error codes from dst routines */ +#define SIGN_INIT_FAILURE (-23) +#define SIGN_UPDATE_FAILURE (-24) +#define SIGN_FINAL_FAILURE (-25) +#define VERIFY_INIT_FAILURE (-26) +#define VERIFY_UPDATE_FAILURE (-27) +#define VERIFY_FINAL_FAILURE (-28) +#define MISSING_KEY_OR_SIGNATURE (-30) +#define UNSUPPORTED_KEYALG (-31) + +#endif /* DST_H */ diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/int.h b/contrib/dhcp-3.0/includes/isc-dhcp/int.h new file mode 100644 index 0000000000..c5deef5d95 --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/int.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef ISC_INT_H +#define ISC_INT_H 1 + +#include + +ISC_LANG_BEGINDECLS + +typedef char isc_int8_t; +typedef unsigned char isc_uint8_t; +typedef short isc_int16_t; +typedef unsigned short isc_uint16_t; +typedef int isc_int32_t; +typedef unsigned int isc_uint32_t; +typedef long long isc_int64_t; +typedef unsigned long long isc_uint64_t; + +ISC_LANG_ENDDECLS + +#endif /* ISC_INT_H */ diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/lang.h b/contrib/dhcp-3.0/includes/isc-dhcp/lang.h new file mode 100644 index 0000000000..00de9bfcf6 --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/lang.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef ISC_LANG_H +#define ISC_LANG_H 1 + +#ifdef __cplusplus +#define ISC_LANG_BEGINDECLS extern "C" { +#define ISC_LANG_ENDDECLS } +#else +#define ISC_LANG_BEGINDECLS +#define ISC_LANG_ENDDECLS +#endif + +#endif /* ISC_LANG_H */ diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/list.h b/contrib/dhcp-3.0/includes/isc-dhcp/list.h new file mode 100644 index 0000000000..0d06244e55 --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/list.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef ISC_LIST_H +#define ISC_LIST_H 1 + +#define ISC_LIST(type) struct { type *head, *tail; } +#define ISC_LIST_INIT(list) \ + do { (list).head = NULL; (list).tail = NULL; } while (0) + +#define ISC_LINK(type) struct { type *prev, *next; } +#define ISC_LINK_INIT(elt, link) \ + do { \ + (elt)->link.prev = (void *)(-1); \ + (elt)->link.next = (void *)(-1); \ + } while (0) +#define ISC_LINK_LINKED(elt, link) ((elt)->link.prev != (void *)(-1)) + +#define ISC_LIST_HEAD(list) ((list).head) +#define ISC_LIST_TAIL(list) ((list).tail) +#define ISC_LIST_EMPTY(list) ((list).head == NULL) + +#define ISC_LIST_PREPEND(list, elt, link) \ + do { \ + if ((list).head != NULL) \ + (list).head->link.prev = (elt); \ + else \ + (list).tail = (elt); \ + (elt)->link.prev = NULL; \ + (elt)->link.next = (list).head; \ + (list).head = (elt); \ + } while (0) + +#define ISC_LIST_APPEND(list, elt, link) \ + do { \ + if ((list).tail != NULL) \ + (list).tail->link.next = (elt); \ + else \ + (list).head = (elt); \ + (elt)->link.prev = (list).tail; \ + (elt)->link.next = NULL; \ + (list).tail = (elt); \ + } while (0) + +#define ISC_LIST_UNLINK(list, elt, link) \ + do { \ + if ((elt)->link.next != NULL) \ + (elt)->link.next->link.prev = (elt)->link.prev; \ + else \ + (list).tail = (elt)->link.prev; \ + if ((elt)->link.prev != NULL) \ + (elt)->link.prev->link.next = (elt)->link.next; \ + else \ + (list).head = (elt)->link.next; \ + (elt)->link.prev = (void *)(-1); \ + (elt)->link.next = (void *)(-1); \ + } while (0) + +#define ISC_LIST_PREV(elt, link) ((elt)->link.prev) +#define ISC_LIST_NEXT(elt, link) ((elt)->link.next) + +#define ISC_LIST_INSERTBEFORE(list, before, elt, link) \ + do { \ + if ((before)->link.prev == NULL) \ + ISC_LIST_PREPEND(list, elt, link); \ + else { \ + (elt)->link.prev = (before)->link.prev; \ + (before)->link.prev = (elt); \ + (elt)->link.prev->link.next = (elt); \ + (elt)->link.next = (before); \ + } \ + } while (0) + +#define ISC_LIST_INSERTAFTER(list, after, elt, link) \ + do { \ + if ((after)->link.next == NULL) \ + ISC_LIST_APPEND(list, elt, link); \ + else { \ + (elt)->link.next = (after)->link.next; \ + (after)->link.next = (elt); \ + (elt)->link.next->link.prev = (elt); \ + (elt)->link.prev = (after); \ + } \ + } while (0) + +#define ISC_LIST_APPENDLIST(list1, list2, link) \ + do { \ + if (ISC_LIST_EMPTY(list1)) \ + (list1) = (list2); \ + else if (!ISC_LIST_EMPTY(list2)) { \ + (list1).tail->link.next = (list2).head; \ + (list2).head->link.prev = (list1).tail; \ + (list1).tail = (list2).tail; \ + (list2).head = NULL; \ + (list2).tail = NULL; \ + } \ + } while (0) + +#define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link) +#define ISC_LIST_DEQUEUE(list, elt, link) ISC_LIST_UNLINK(list, elt, link) + +#endif /* ISC_LIST_H */ diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/result.h b/contrib/dhcp-3.0/includes/isc-dhcp/result.h new file mode 100644 index 0000000000..0590f86af0 --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/result.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef ISC_RESULT_H +#define ISC_RESULT_H 1 + +#include +#include +#include +#include + +ISC_LANG_BEGINDECLS + +typedef enum { + ISC_R_SUCCESS = 0, + ISC_R_NOMEMORY = 1, + ISC_R_TIMEDOUT = 2, + ISC_R_NOTHREADS = 3, + ISC_R_ADDRNOTAVAIL = 4, + ISC_R_ADDRINUSE = 5, + ISC_R_NOPERM = 6, + ISC_R_NOCONN = 7, + ISC_R_NETUNREACH = 8, + ISC_R_HOSTUNREACH = 9, + ISC_R_NETDOWN = 10, + ISC_R_HOSTDOWN = 11, + ISC_R_CONNREFUSED = 12, + ISC_R_NORESOURCES = 13, + ISC_R_EOF = 14, + ISC_R_BOUND = 15, + ISC_R_TASKDONE = 16, + ISC_R_LOCKBUSY = 17, + ISC_R_EXISTS = 18, + ISC_R_NOSPACE = 19, + ISC_R_CANCELED = 20, + ISC_R_TASKNOSEND = 21, + ISC_R_SHUTTINGDOWN = 22, + ISC_R_NOTFOUND = 23, + ISC_R_UNEXPECTEDEND = 24, + ISC_R_FAILURE = 25, + ISC_R_IOERROR = 26, + ISC_R_NOTIMPLEMENTED = 27, + ISC_R_UNBALANCED = 28, + ISC_R_NOMORE = 29, + ISC_R_INVALIDFILE = 30, + ISC_R_BADBASE64 = 31, + ISC_R_UNEXPECTEDTOKEN = 32, + ISC_R_QUOTA = 33, + ISC_R_UNEXPECTED = 34, + ISC_R_ALREADYRUNNING = 35, + ISC_R_HOSTUNKNOWN = 36, + ISC_R_VERSIONMISMATCH = 37, + ISC_R_PROTOCOLERROR = 38, + ISC_R_INVALIDARG = 39, + ISC_R_NOTCONNECTED = 40, + ISC_R_NOTYET = 41, + ISC_R_UNCHANGED = 42, + ISC_R_MULTIPLE = 43, + ISC_R_KEYCONFLICT = 44, + ISC_R_BADPARSE = 45, + ISC_R_NOKEYS = 46, + ISC_R_KEY_UNKNOWN = 47, + ISC_R_INVALIDKEY = 48, + ISC_R_INCOMPLETE = 49, + ISC_R_FORMERR = 50, + ISC_R_SERVFAIL = 51, + ISC_R_NXDOMAIN = 52, + ISC_R_NOTIMPL = 53, + ISC_R_REFUSED = 54, + ISC_R_YXDOMAIN = 55, + ISC_R_YXRRSET = 56, + ISC_R_NXRRSET = 57, + ISC_R_NOTAUTH = 58, + ISC_R_NOTZONE = 59, + ISC_R_BADSIG = 60, + ISC_R_BADKEY = 61, + ISC_R_BADTIME = 62, + ISC_R_NOROOTZONE = 63, + ISC_R_DESTADDRREQ = 64, + ISC_R_CROSSZONE = 65, + ISC_R_NO_TSIG = 66, + ISC_R_NOT_EQUAL = 67, + ISC_R_CONNRESET = 68, + ISC_R_UNKNOWNATTRIBUTE = 69 +} isc_result_t; + + +#define ISC_R_NRESULTS 70 /* Number of results */ + +const char * isc_result_totext(isc_result_t); +isc_result_t isc_result_register(unsigned int base, + unsigned int nresults, + char **text, + isc_msgcat_t *msgcat, + int set); + +ISC_LANG_ENDDECLS + +#endif /* ISC_RESULT_H */ diff --git a/contrib/dhcp-3.0/includes/isc-dhcp/types.h b/contrib/dhcp-3.0/includes/isc-dhcp/types.h new file mode 100644 index 0000000000..5da4e2e8b6 --- /dev/null +++ b/contrib/dhcp-3.0/includes/isc-dhcp/types.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef ISC_TYPES_H +#define ISC_TYPES_H 1 + +#include +#include +#include + +/*** + *** Core Types. + ***/ + +typedef struct isc_mem isc_mem_t; +typedef struct isc_mempool isc_mempool_t; +typedef struct isc_msgcat isc_msgcat_t; +typedef unsigned int isc_eventtype_t; +typedef struct isc_event isc_event_t; +typedef struct isc_task isc_task_t; +typedef struct isc_taskmgr isc_taskmgr_t; +typedef struct isc_rwlock isc_rwlock_t; + +typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *); + +#endif /* ISC_TYPES_H */ diff --git a/contrib/dhcp-3.0/includes/minires/minires.h b/contrib/dhcp-3.0/includes/minires/minires.h new file mode 100644 index 0000000000..19d2131868 --- /dev/null +++ b/contrib/dhcp-3.0/includes/minires/minires.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#include "cdefs.h" +#include "osdep.h" + +#define _ns_flagdata MR_ns_flagdata + +#include "minires/resolv.h" +#include "minires/res_update.h" +#include "isc-dhcp/result.h" + +/* + * Based on the Dynamic DNS reference implementation by Viraj Bais + * + */ + +int minires_mkupdate (ns_updrec *, unsigned char *, unsigned); +int minires_update (ns_updrec *); +ns_updrec *minires_mkupdrec (int, const char *, unsigned int, + unsigned int, unsigned long); +void minires_freeupdrec (ns_updrec *); +int minires_nmkupdate (res_state, ns_updrec *, double *, unsigned *); +isc_result_t minires_nupdate (res_state, ns_updrec *); +int minires_ninit (res_state); +ns_rcode isc_rcode_to_ns (isc_result_t); + +#if defined (MINIRES_LIB) +#define res_update minires_update +#define res_mkupdate minires_mkupdate +#define res_mkupdrec minires_mkupdrec +#define res_freeupdrec minires_freeupdrec +#define res_nmkupdate minires_nmkupdate +#define res_nupdate minires_nupdate +#define __p_type_syms MR__p_type_syms +#define dn_comp MRdn_comp +#define loc_aton MRloc_aton +#define sym_ston MRsym_ston +#define res_buildservicelist MRres_buildservicelist +#define res_destroyservicelist MRres_destroyservicelist +#define res_buildprotolist MRres_buildprotolist +#define res_destroyprotolist MRres_destroyprotolist +#define res_servicenumber MRres_servicenumber +#define res_protocolnumber MRres_protocolnumber +#define res_protocolname MRres_protocolname +#define res_servicename MRres_servicename +#define ns_datetosecs MRns_datetosecs +#define b64_pton MRb64_pton +#define res_ninit minires_ninit +#define res_randomid MRres_randomid +#define res_findzonecut MRres_findzonecut +#define res_nsend MRres_nsend +#define res_nsendsigned MRres_nsendsigned +#define ns_samename MRns_samename +#define res_nameinquery MRres_nameinquery +#define res_queriesmatch MRres_queriesmatch +#define dn_expand MRdn_expand +#define ns_get16 MRns_get16 +#define res_close MRres_close +#define res_nclose MRres_nclose +#define res_ourserver_p MRres_ourserver_p +#define ns_sign MRns_sign +#define p_class MRp_class +#define p_section MRp_section +#define ns_makecanon MRns_makecanon +#define ns_parserr MRns_parserr +#define ns_samedomain MRns_samedomain +#define ns_name_uncompress MRns_name_uncompress +#define res_nmkquery MRres_nmkquery +#define ns_initparse MRns_initparse +#define res_nquery MRres_nquery +#define res_nsearch MRres_nsearch +#define res_hostalias MRres_hostalias +#define res_nquerydomain MRres_nquerydomain +#define ns_skiprr MRns_skiprr +#define dn_skipname MRdn_skipname +#define ns_name_ntol MRns_name_ntol +#define ns_sign_tcp_init MRns_sign_tcp_init +#define ns_sign_tcp MRns_sign_tcp +#define ns_name_ntop MRns_name_ntop +#define ns_name_pton MRns_name_pton +#define ns_name_unpack MRns_name_unpack +#define ns_name_pack MRns_name_pack +#define ns_name_compress MRns_name_compress +#define ns_name_skip MRns_name_skip +#define ns_subdomain MRns_subdomain +#define ns_find_tsig MRns_find_tsig +#define ns_verify MRns_verify +#define ns_verify_tcp_init MRns_verify_tcp_init +#define ns_verify_tcp MRns_verify_tcp +#define b64_ntop MRb64_ntop + +extern const struct res_sym __p_type_syms[]; +extern time_t cur_time; + +int dn_comp (const char *, + unsigned char *, unsigned, unsigned char **, unsigned char **); +int loc_aton (const char *, u_char *); +int sym_ston (const struct res_sym *, const char *, int *); +void res_buildservicelist (void); +void res_destroyservicelist (void); +void res_buildprotolist(void); +void res_destroyprotolist(void); +int res_servicenumber(const char *); +int res_protocolnumber(const char *); +const char *res_protocolname(int); +const char *res_servicename(u_int16_t, const char *); +u_int32_t ns_datetosecs (const char *cp, int *errp); +int b64_pton (char const *, unsigned char *, size_t); +unsigned int res_randomid (void); +isc_result_t res_findzonecut (res_state, const char *, ns_class, int, char *, + size_t, struct in_addr *, int, int *, void *); +isc_result_t res_nsend (res_state, + double *, unsigned, double *, unsigned, unsigned *); +isc_result_t res_nsendsigned (res_state, double *, unsigned, ns_tsig_key *, + double *, unsigned, unsigned *); +int ns_samename (const char *, const char *); +int res_nameinquery (const char *, int, int, + const unsigned char *, const unsigned char *); +int res_queriesmatch (const unsigned char *, const unsigned char *, + const unsigned char *, const unsigned char *); +int dn_expand (const unsigned char *, + const unsigned char *, const unsigned char *, char *, unsigned); +unsigned int ns_get16 (const unsigned char *); +void res_close (void); +void res_nclose (res_state); +int res_ourserver_p (const res_state, const struct sockaddr_in *); +isc_result_t ns_sign (unsigned char *, unsigned *, + unsigned, int, void *, const unsigned char *, + unsigned, unsigned char *, unsigned *, time_t); +const char *p_class (int); +const char *p_section (int section, int opcode); +isc_result_t ns_makecanon (const char *, char *, size_t); +isc_result_t ns_parserr (ns_msg *, ns_sect, int, ns_rr *); +int ns_samedomain (const char *, const char *); +int ns_name_uncompress (const u_char *, const u_char *, + const u_char *, char *, size_t); +isc_result_t res_nmkquery (res_state, int, const char *, ns_class, ns_type, + const unsigned char *, unsigned, + const unsigned char *, double *, + unsigned, unsigned *); +isc_result_t ns_initparse (const unsigned char *, unsigned, ns_msg *); +isc_result_t res_nquery(res_state, const char *, + ns_class, ns_type, double *, unsigned, unsigned *); +isc_result_t res_nsearch(res_state, const char *, + ns_class, ns_type, double *, unsigned, unsigned *); +const char *res_hostalias (const res_state, const char *, char *, size_t); +isc_result_t res_nquerydomain(res_state, const char *, const char *, + ns_class class, ns_type type, + double *, unsigned, unsigned *); + +isc_result_t ns_skiprr(const unsigned char *, + const unsigned char *, ns_sect, int, int *); +int dn_skipname (const unsigned char *, const unsigned char *); +u_int32_t getULong (const unsigned char *); +int32_t getLong (const unsigned char *); +u_int32_t getUShort (const unsigned char *); +int32_t getShort (const unsigned char *); +u_int32_t getUChar (const unsigned char *); +void putULong (unsigned char *, u_int32_t); +void putLong (unsigned char *, int32_t); +void putUShort (unsigned char *, u_int32_t); +void putShort (unsigned char *, int32_t); +void putUChar (unsigned char *, u_int32_t); +int ns_name_ntol (const unsigned char *, unsigned char *, size_t); +isc_result_t ns_sign_tcp_init (void *, const unsigned char *, + unsigned, ns_tcp_tsig_state *); +isc_result_t ns_sign_tcp (unsigned char *, + unsigned *, unsigned, int, ns_tcp_tsig_state *, int); +int ns_name_ntop (const unsigned char *, char *, size_t); +int ns_name_pton (const char *, unsigned char *, size_t); +int ns_name_unpack (const unsigned char *, const unsigned char *, + const unsigned char *, unsigned char *, size_t); +int ns_name_pack (const unsigned char *, unsigned char *, + unsigned, const unsigned char **, const unsigned char **); +int ns_name_compress (const char *, unsigned char *, + size_t, const unsigned char **, const unsigned char **); +int ns_name_skip (const unsigned char **, const unsigned char *); +int ns_subdomain (const char *, const char *); +unsigned char *ns_find_tsig (unsigned char *, unsigned char *); +isc_result_t ns_verify (unsigned char *, unsigned *, void *, + const unsigned char *, + unsigned, unsigned char *, unsigned *, time_t *, int); +isc_result_t ns_verify_tcp_init (void *, const unsigned char *, unsigned, + ns_tcp_tsig_state *); +isc_result_t ns_verify_tcp (unsigned char *, unsigned *, + ns_tcp_tsig_state *, int); +int b64_ntop (unsigned char const *, size_t, char *, size_t); + +ns_rcode find_cached_zone (const char *, ns_class, char *, + size_t, struct in_addr *, int, int *, void *); +int find_tsig_key (ns_tsig_key **, const char *, void *); +int forget_zone (void *); +int repudiate_zone (void *); +void cache_found_zone (ns_class, char *, struct in_addr *, int); +isc_result_t uerr2isc (int); +isc_result_t ns_rcode_to_isc (int); + +#define DprintQ(a,b,c,d) +#define Dprint(a,b) +#define Perror(a, b, c, d) +#define Aerror(a, b, c, d, e) +#define DPRINTF(x) + +#define USE_MD5 +#endif + +#if defined (TRACING) +void trace_mr_statp_setup (res_state); +#endif diff --git a/contrib/dhcp-3.0/includes/minires/res_update.h b/contrib/dhcp-3.0/includes/minires/res_update.h new file mode 100644 index 0000000000..24569c5398 --- /dev/null +++ b/contrib/dhcp-3.0/includes/minires/res_update.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +/* + * $Id: res_update.h,v 1.2.2.2 2004/06/10 17:59:37 dhankins Exp $ + */ + +#ifndef __RES_UPDATE_H +#define __RES_UPDATE_H + +#include +#include "arpa/nameser.h" +#include + +/* + * This RR-like structure is particular to UPDATE. + */ +typedef struct ns_updrec { + ISC_LINK(struct ns_updrec) r_link, r_glink; + ns_sect r_section; /* ZONE/PREREQUISITE/UPDATE */ + char *r_dname; /* owner of the RR */ + ns_class r_class; /* class number */ + ns_type r_type; /* type number */ + u_int32_t r_ttl; /* time to live */ + const unsigned char *r_data; /* rdata fields as text string */ + unsigned char *r_data_ephem; /* pointer to freeable r_data */ + unsigned int r_size; /* size of r_data field */ + int r_opcode; /* type of operation */ + /* following fields for private use by the resolver/server + routines */ + struct databuf *r_dp; /* databuf to process */ + struct databuf *r_deldp; /* databuf's deleted/overwritten */ + unsigned int r_zone; /* zone number on server */ +} ns_updrec; +typedef ISC_LIST(ns_updrec) ns_updque; + +#endif /*__RES_UPDATE_H*/ diff --git a/contrib/dhcp-3.0/includes/minires/resolv.h b/contrib/dhcp-3.0/includes/minires/resolv.h new file mode 100644 index 0000000000..15bcd2ec86 --- /dev/null +++ b/contrib/dhcp-3.0/includes/minires/resolv.h @@ -0,0 +1,320 @@ +/* + * Copyright (c) 1983, 1987, 1989 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +/* + * @(#)resolv.h 8.1 (Berkeley) 6/2/93 + * $Id: resolv.h,v 1.3.2.1 2004/06/10 17:59:37 dhankins Exp $ + */ + +#ifndef _RESOLV_H_ +#define _RESOLV_H_ + +/* + * This used to be defined in res_query.c, now it's in herror.c. + * [XXX no it's not. It's in irs/irs_data.c] + * It was + * never extern'd by any *.h file before it was placed here. For thread + * aware programs, the last h_errno value set is stored in res->h_errno. + * + * XXX: There doesn't seem to be a good reason for exposing RES_SET_H_ERRNO + * (and __h_errno_set) to the public via . + * XXX: __h_errno_set is really part of IRS, not part of the resolver. + * If somebody wants to build and use a resolver that doesn't use IRS, + * what do they do? Perhaps something like + * #ifdef WANT_IRS + * # define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x) + * #else + * # define RES_SET_H_ERRNO(r,x) (h_errno = (r)->res_h_errno = (x)) + * #endif + */ + +#define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x) +struct __res_state; /* forward */ +void __h_errno_set(struct __res_state *res, int err); + +/* + * Resolver configuration file. + * Normally not present, but may contain the address of the + * inital name server(s) to query and the domain search list. + */ + +#ifndef _PATH_RESCONF +#define _PATH_RESCONF "/etc/resolv.conf" +#endif + +typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error } + res_sendhookact; + +typedef res_sendhookact (*res_send_qhook) (struct sockaddr_in * const *ns, + double **query, + unsigned *querylen, + double *ans, + unsigned anssiz, + int *resplen); + +typedef res_sendhookact (*res_send_rhook) (const struct sockaddr_in *ns, + double *query, + unsigned querylen, + double *ans, + unsigned anssiz, + int *resplen); + +struct res_sym { + int number; /* Identifying number, like T_MX */ + char * name; /* Its symbolic name, like "MX" */ + char * humanname; /* Its fun name, like "mail exchanger" */ +}; + +/* + * Global defines and variables for resolver stub. + */ +#define MAXNS 3 /* max # name servers we'll track */ +#define MAXDFLSRCH 3 /* # default domain levels to try */ +#define MAXDNSRCH 6 /* max # domains in search path */ +#define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */ + +#define RES_TIMEOUT 5 /* min. seconds between retries */ +#define MAXRESOLVSORT 10 /* number of net to sort on */ +#define RES_MAXNDOTS 15 /* should reflect bit field size */ +#define RES_MAXRETRANS 30 /* only for resolv.conf/RES_OPTIONS */ +#define RES_MAXRETRY 5 /* only for resolv.conf/RES_OPTIONS */ +#define RES_DFLRETRY 2 /* Default #/tries. */ + +struct __res_state { + int retrans; /* retransmition time interval */ + int retry; /* number of times to retransmit */ + u_long options; /* option flags - see below. */ + int nscount; /* number of name servers */ + struct sockaddr_in + nsaddr_list[MAXNS]; /* address of name server */ +#define nsaddr nsaddr_list[0] /* for backward compatibility */ + u_short id; /* current message id */ + char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */ + char defdname[256]; /* default domain (deprecated) */ + u_long pfcode; /* RES_PRF_ flags - see below. */ + unsigned ndots:4; /* threshold for initial abs. query */ + unsigned nsort:4; /* number of elements in sort_list[] */ + char unused[3]; + struct { + struct in_addr addr; + u_int32_t mask; + } sort_list[MAXRESOLVSORT]; + res_send_qhook qhook; /* query hook */ + res_send_rhook rhook; /* response hook */ + int res_h_errno; /* last one set for this context */ + int _sock; /* PRIVATE: for res_send i/o */ + u_int _flags; /* PRIVATE: see below */ + char pad[52]; /* On an i386 this means 512b total. */ +}; + +typedef struct __res_state *res_state; + +/* + * Resolver flags (used to be discrete per-module statics ints). + */ +#define RES_F_VC 0x00000001 /* socket is TCP */ +#define RES_F_CONN 0x00000002 /* socket is connected */ + +/* res_findzonecut() options */ +#define RES_EXHAUSTIVE 0x00000001 /* always do all queries */ + +/* + * Resolver options (keep these in synch with res_debug.c, please) + */ +#define RES_INIT 0x00000001 /* address initialized */ +#define RES_DEBUG 0x00000002 /* print debug messages */ +#define RES_AAONLY 0x00000004 /* authoritative answers only (!IMPL)*/ +#define RES_USEVC 0x00000008 /* use virtual circuit */ +#define RES_PRIMARY 0x00000010 /* query primary server only (!IMPL) */ +#define RES_IGNTC 0x00000020 /* ignore trucation errors */ +#define RES_RECURSE 0x00000040 /* recursion desired */ +#define RES_DEFNAMES 0x00000080 /* use default domain name */ +#define RES_STAYOPEN 0x00000100 /* Keep TCP socket open */ +#define RES_DNSRCH 0x00000200 /* search up local domain tree */ +#define RES_INSECURE1 0x00000400 /* type 1 security disabled */ +#define RES_INSECURE2 0x00000800 /* type 2 security disabled */ +#define RES_NOALIASES 0x00001000 /* shuts off HOSTALIASES feature */ +#define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */ +#define RES_ROTATE 0x00004000 /* rotate ns list after each query */ +#define RES_NOCHECKNAME 0x00008000 /* do not check names for sanity. */ +#define RES_KEEPTSIG 0x00010000 /* do not strip TSIG records */ + +#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH) + +/* + * Resolver "pfcode" values. Used by dig. + */ +#define RES_PRF_STATS 0x00000001 +#define RES_PRF_UPDATE 0x00000002 +#define RES_PRF_CLASS 0x00000004 +#define RES_PRF_CMD 0x00000008 +#define RES_PRF_QUES 0x00000010 +#define RES_PRF_ANS 0x00000020 +#define RES_PRF_AUTH 0x00000040 +#define RES_PRF_ADD 0x00000080 +#define RES_PRF_HEAD1 0x00000100 +#define RES_PRF_HEAD2 0x00000200 +#define RES_PRF_TTLID 0x00000400 +#define RES_PRF_HEADX 0x00000800 +#define RES_PRF_QUERY 0x00001000 +#define RES_PRF_REPLY 0x00002000 +#define RES_PRF_INIT 0x00004000 +/* 0x00008000 */ + +#if 0 +/* Things involving an internal (static) resolver context. */ +#ifdef _REENTRANT +extern struct __res_state *__res_state(void); +#define _res (*__res_state()) +#else +#ifndef __BIND_NOSTATIC +extern struct __res_state _res; +#endif +#endif + +void fp_nquery (const u_char *, int, FILE *); +void fp_query (const u_char *, FILE *); +const char * hostalias (const char *); +void p_query (const u_char *); +void res_close (void); +int res_init (void); +int res_isourserver (const struct sockaddr_in *); +int res_mkquery (int, const char *, int, int, const u_char *, + int, const u_char *, u_char *, int); +int res_query (const char *, int, int, u_char *, int); +int res_querydomain (const char *, const char *, int, int, + u_char *, int); +int res_search (const char *, int, int, u_char *, int); +int res_send (const u_char *, int, u_char *, int); +int res_sendsigned (const u_char *, int, ns_tsig_key *, + u_char *, int); + +#if !defined(SHARED_LIBBIND) || defined(LIB) +/* + * If libbind is a shared object (well, DLL anyway) + * these externs break the linker when resolv.h is + * included by a lib client (like named) + * Make them go away if a client is including this + * + */ +extern const struct res_sym __p_key_syms[]; +extern const struct res_sym __p_cert_syms[]; +extern const struct res_sym __p_class_syms[]; +extern const struct res_sym __p_type_syms[]; +extern const struct res_sym __p_rcode_syms[]; +#endif /* SHARED_LIBBIND */ + +int res_hnok (const char *); +int res_ownok (const char *); +int res_mailok (const char *); +int res_dnok (const char *); +int sym_ston (const struct res_sym *, const char *, int *); +const char * sym_ntos (const struct res_sym *, int, int *); +const char * sym_ntop (const struct res_sym *, int, int *); +int b64_ntop (u_char const *, size_t, char *, size_t); +int b64_pton (char const *, u_char *, size_t); +int loc_aton (const char *ascii, u_char *binary); +const char * loc_ntoa (const u_char *binary, char *ascii); +int dn_skipname (const u_char *, const u_char *); +void putlong (u_int32_t, u_char *); +void putshort (u_int16_t, u_char *); +const char * p_class (int); +const char * p_time (u_int32_t); +const char * p_type (int); +const char * p_rcode (int); +const u_char * p_cdnname (const u_char *, const u_char *, int, FILE *); +const u_char * p_cdname (const u_char *, const u_char *, FILE *); +const u_char * p_fqnname (const u_char *cp, const u_char *msg, + int, char *, int); +const u_char * p_fqname (const u_char *, const u_char *, FILE *); +const char * p_option (u_long option); +char * p_secstodate (u_long); +int dn_count_labels (const char *); +int dn_expand (const u_char *, const u_char *, const u_char *, + char *, int); +u_int res_randomid (void); +int res_nameinquery (const char *, int, int, + const u_char *, const u_char *); +int res_queriesmatch (const u_char *, const u_char *, + const u_char *, const u_char *); +const char * p_section (int section, int opcode); +/* Things involving a resolver context. */ +int res_ninit (res_state); +int res_nisourserver (const res_state, + const struct sockaddr_in *); +void fp_resstat (const res_state, FILE *); +void res_npquery (const res_state, const u_char *, int, FILE *); +const char * res_hostalias (const res_state, const char *, + char *, size_t); +int res_nquery (res_state, + const char *, int, int, u_char *, int); +int res_nsearch (res_state, const char *, int, + int, u_char *, int); +int res_nquerydomain (res_state, + const char *, const char *, int, int, + u_char *, int); +int res_nmkquery (res_state, + int, const char *, int, int, const u_char *, + int, const u_char *, u_char *, int); +int res_nsend (res_state, const u_char *, int, u_char *, int); +int res_nsendsigned (res_state, const u_char *, int, + ns_tsig_key *, u_char *, int); +int res_findzonecut (res_state, const char *, ns_class, int, + char *, size_t, struct in_addr *, int); +void res_nclose (res_state); + +#endif /* 0 */ +#endif /* !_RESOLV_H_ */ diff --git a/contrib/dhcp-3.0/includes/netinet/if_ether.h b/contrib/dhcp-3.0/includes/netinet/if_ether.h new file mode 100644 index 0000000000..e53b4c7f66 --- /dev/null +++ b/contrib/dhcp-3.0/includes/netinet/if_ether.h @@ -0,0 +1,61 @@ +/* $NetBSD: if_ether.h,v 1.20 1995/06/12 00:47:27 mycroft Exp $ */ + +/* + * Copyright (c) 1982, 1986, 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. + * + * @(#)if_ether.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Ethernet address - 6 octets + * this is only used by the ethers(3) functions. + */ +struct ether_addr { + u_int8_t ether_addr_octet[6]; +}; + +/* + * Structure of a 10Mb/s Ethernet header. + */ +#define ETHER_ADDR_LEN 6 + +struct isc_ether_header { + u_int8_t ether_dhost[ETHER_ADDR_LEN]; + u_int8_t ether_shost[ETHER_ADDR_LEN]; + u_int16_t ether_type; +}; + +#define ETHERTYPE_PUP 0x0200 /* PUP protocol */ +#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#define ETHERTYPE_ARP 0x0806 /* address resolution protocol */ + +#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof (u_int16_t)) diff --git a/contrib/dhcp-3.0/includes/netinet/ip.h b/contrib/dhcp-3.0/includes/netinet/ip.h new file mode 100644 index 0000000000..0a1e358404 --- /dev/null +++ b/contrib/dhcp-3.0/includes/netinet/ip.h @@ -0,0 +1,163 @@ +/* $NetBSD: ip.h,v 1.9 1995/05/15 01:22:44 cgd Exp $ */ + +/* + * Copyright (c) 1982, 1986, 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. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ +#define IPVERSION 4 + +/* + * Structure of an internet header, naked of options. + * + * We declare ip_len and ip_off to be short, rather than u_short + * pragmatically since otherwise unsigned comparisons can result + * against negative integers quite easily, and fail in subtle ways. + */ +struct ip { + u_int8_t ip_fvhl; /* header length, version */ + u_int8_t ip_tos; /* type of service */ + int16_t ip_len; /* total length */ + u_int16_t ip_id; /* identification */ + int16_t ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_int8_t ip_ttl; /* time to live */ + u_int8_t ip_p; /* protocol */ + u_int16_t ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ +}; + +#define IP_V(iph) ((iph)->ip_fvhl >> 4) +#define IP_HL(iph) (((iph)->ip_fvhl & 0x0F) << 2) +#define IP_V_SET(iph,x) ((iph)->ip_fvhl = ((iph)->ip_fvhl & 0x0F) | ((x) << 4)) +#define IP_HL_SET(iph,x) ((iph)->ip_fvhl = \ + ((iph)->ip_fvhl & 0xF0) | (((x) >> 2) & 0x0F)) + +#define IP_MAXPACKET 65535 /* maximum packet size */ + +/* + * Definitions for IP type of service (ip_tos) + */ +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +/* IPTOS_LOWCOST 0x02 XXX */ + +/* + * Definitions for IP precedence (also in ip_tos) (hopefully unused) + */ +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + +/* + * Definitions for options. + */ +#define IPOPT_COPIED(o) ((o)&0x80) +#define IPOPT_CLASS(o) ((o)&0x60) +#define IPOPT_NUMBER(o) ((o)&0x1f) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_DEBMEAS 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_EOL 0 /* end of option list */ +#define IPOPT_NOP 1 /* no operation */ + +#define IPOPT_RR 7 /* record packet route */ +#define IPOPT_TS 68 /* timestamp */ +#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ +#define IPOPT_LSRR 131 /* loose source route */ +#define IPOPT_SATID 136 /* satnet id */ +#define IPOPT_SSRR 137 /* strict source route */ + +/* + * Offsets to fields in options other than EOL and NOP. + */ +#define IPOPT_OPTVAL 0 /* option ID */ +#define IPOPT_OLEN 1 /* option length */ +#define IPOPT_OFFSET 2 /* offset within option */ +#define IPOPT_MINOFF 4 /* min value of above */ + +/* + * Time stamp option structure. + */ +struct ip_timestamp { + u_int8_t ipt_code; /* IPOPT_TS */ + u_int8_t ipt_len; /* size of structure (variable) */ + u_int8_t ipt_ptr; /* index of current entry */ + u_int8_t ipt_flg_oflw; /* flags, see below, overflow counter */ + union ipt_timestamp { + u_int32_t ipt_time[1]; + struct ipt_ta { + struct in_addr ipt_addr; + u_int32_t ipt_time; + } ipt_ta[1]; + } ipt_timestamp; +}; + +/* flag bits for ipt_flg */ +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +/* bits for security (not byte swapped) */ +#define IPOPT_SECUR_UNCLASS 0x0000 +#define IPOPT_SECUR_CONFID 0xf135 +#define IPOPT_SECUR_EFTO 0x789a +#define IPOPT_SECUR_MMMM 0xbc4d +#define IPOPT_SECUR_RESTR 0xaf13 +#define IPOPT_SECUR_SECRET 0xd788 +#define IPOPT_SECUR_TOPSECRET 0x6bc5 + +/* + * Internet implementation parameters. + */ +#define MAXTTL 255 /* maximum time to live (seconds) */ +#define IPDEFTTL 64 /* default ttl, from RFC 1340 */ +#define IPFRAGTTL 60 /* time to live for frags, slowhz */ +#define IPTTLDEC 1 /* subtracted when forwarding */ + +#define IP_MSS 576 /* default maximum segment size */ diff --git a/contrib/dhcp-3.0/includes/netinet/ip_icmp.h b/contrib/dhcp-3.0/includes/netinet/ip_icmp.h new file mode 100644 index 0000000000..8fffb58b73 --- /dev/null +++ b/contrib/dhcp-3.0/includes/netinet/ip_icmp.h @@ -0,0 +1,182 @@ +/* $NetBSD: ip_icmp.h,v 1.11 1996/08/03 15:48:18 neil Exp $ */ + +/* + * Copyright (c) 1982, 1986, 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. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 + */ + + +/* + * Interface Control Message Protocol Definitions. + * Per RFC 792, September 1981. + */ + +/* + * Internal of an ICMP Router Advertisement + */ +struct icmp_ra_addr { + u_int32_t ira_addr; + u_int32_t ira_preference; +}; + +/* + * Structure of an icmp header. + */ +struct icmp { + u_int8_t icmp_type; /* type of message, see below */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement cksum of struct */ + union { + u_int8_t ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + int16_t icd_id; + int16_t icd_seq; + } ih_idseq; + int32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + int16_t ipm_void; + int16_t ipm_nextmtu; + } ih_pmtu; + struct ih_rtradv { + u_int8_t irt_num_addrs; + u_int8_t irt_wpa; + u_int16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + union { + struct id_ts { + u_int32_t its_otime; + u_int32_t its_rtime; + u_int32_t its_ttime; + } id_ts; + struct id_ip { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + u_int32_t id_mask; + int8_t id_data[1]; + } icmp_dun; +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_mask +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data +}; + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first insure that the + * packet is large enought to contain the returned ip header. + * Only then can we do the check to see if 64 bits of packet + * data have been returned, since we need to check the returned + * ip header length. + */ +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_TSLEN (8 + 3 * sizeof (u_int32_t)) /* timestamp */ +#define ICMP_MASKLEN 12 /* address mask */ +#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */ +#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) + /* N.B.: must separately check that ip_hl >= 5 */ + +/* + * Definition of type and code field values. + */ +#define ICMP_ECHOREPLY 0 /* echo reply */ +#define ICMP_UNREACH 3 /* dest unreachable, codes: */ +#define ICMP_UNREACH_NET 0 /* bad net */ +#define ICMP_UNREACH_HOST 1 /* bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ +#define ICMP_UNREACH_PORT 3 /* bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ +#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ +#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ +#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ +#define ICMP_REDIRECT 5 /* shorter route, codes: */ +#define ICMP_REDIRECT_NET 0 /* for network */ +#define ICMP_REDIRECT_HOST 1 /* for host */ +#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ +#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ +#define ICMP_ECHO 8 /* echo service */ +#define ICMP_ROUTERADVERT 9 /* router advertisement */ +#define ICMP_ROUTERSOLICIT 10 /* router solicitation */ +#define ICMP_TIMXCEED 11 /* time exceeded, code: */ +#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ +#define ICMP_PARAMPROB 12 /* ip header bad */ +#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ +#define ICMP_TSTAMP 13 /* timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* timestamp reply */ +#define ICMP_IREQ 15 /* information request */ +#define ICMP_IREQREPLY 16 /* information reply */ +#define ICMP_MASKREQ 17 /* address mask request */ +#define ICMP_MASKREPLY 18 /* address mask reply */ + +#define ICMP_MAXTYPE 18 + +#define ICMP_INFOTYPE(type) \ + ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ + (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ + (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ + (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ + (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) + +#ifdef _KERNEL +void icmp_error __P((struct mbuf *, int, int, n_long, struct ifnet *)); +void icmp_input __P((struct mbuf *, ...)); +void icmp_reflect __P((struct mbuf *)); +void icmp_send __P((struct mbuf *, struct mbuf *)); +int icmp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); +#endif + diff --git a/contrib/dhcp-3.0/includes/netinet/udp.h b/contrib/dhcp-3.0/includes/netinet/udp.h new file mode 100644 index 0000000000..350f1ece2a --- /dev/null +++ b/contrib/dhcp-3.0/includes/netinet/udp.h @@ -0,0 +1,70 @@ +/* $NetBSD: udp.h,v 1.6 1995/04/13 06:37:10 cgd Exp $ */ + +/* + * Copyright (c) 1982, 1986, 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. + * + * @(#)udp.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Portions Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 2000-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +/* + * Udp protocol header. + * Per RFC 768, September, 1981. + */ +struct udphdr { + u_int16_t uh_sport; /* source port */ + u_int16_t uh_dport; /* destination port */ + u_int16_t uh_ulen; /* udp length */ + u_int16_t uh_sum; /* udp checksum */ +}; diff --git a/contrib/dhcp-3.0/includes/omapip/alloc.h b/contrib/dhcp-3.0/includes/omapip/alloc.h new file mode 100644 index 0000000000..5d1dec0f1a --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/alloc.h @@ -0,0 +1,111 @@ +/* alloc.h + + Definitions for the object management API protocol memory allocation... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +isc_result_t omapi_buffer_new (omapi_buffer_t **, const char *, int); +isc_result_t omapi_buffer_reference (omapi_buffer_t **, + omapi_buffer_t *, const char *, int); +isc_result_t omapi_buffer_dereference (omapi_buffer_t **, const char *, int); + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +#define DMDOFFSET (sizeof (struct dmalloc_preamble)) +#define DMLFSIZE 16 +#define DMUFSIZE 16 +#define DMDSIZE (DMDOFFSET + DMLFSIZE + DMUFSIZE) + +struct dmalloc_preamble { + struct dmalloc_preamble *prev, *next; + const char *file; + int line; + size_t size; + unsigned long generation; + unsigned char low_fence [DMLFSIZE]; +}; +#else +#define DMDOFFSET 0 +#define DMDSIZE 0 +#endif + +/* rc_history flags... */ +#define RC_LEASE 1 +#define RC_MISC 2 + +#if defined (DEBUG_RC_HISTORY) +#if !defined (RC_HISTORY_MAX) +# define RC_HISTORY_MAX 256 +#endif + +#if !defined (RC_HISTORY_FLAGS) +# define RC_HISTORY_FLAGS (RC_LEASE | RC_MISC) +#endif + +struct rc_history_entry { + const char *file; + int line; + void *reference; + void *addr; + int refcnt; +}; + +#define rc_register(x, l, r, y, z, d, f) do { \ + if (RC_HISTORY_FLAGS & ~(f)) { \ + rc_history [rc_history_index].file = (x); \ + rc_history [rc_history_index].line = (l); \ + rc_history [rc_history_index].reference = (r); \ + rc_history [rc_history_index].addr = (y); \ + rc_history [rc_history_index].refcnt = (z); \ + rc_history_next (d); \ + } \ + } while (0) +#define rc_register_mdl(r, y, z, d, f) \ + rc_register (__FILE__, __LINE__, r, y, z, d, f) +#else +#define rc_register(file, line, reference, addr, refcnt, d, f) +#define rc_register_mdl(reference, addr, refcnt, d, f) +#endif + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +extern struct dmalloc_preamble *dmalloc_list; +extern unsigned long dmalloc_outstanding; +extern unsigned long dmalloc_longterm; +extern unsigned long dmalloc_generation; +extern unsigned long dmalloc_cutoff_generation; +#endif + +#if defined (DEBUG_RC_HISTORY) +extern struct rc_history_entry rc_history [RC_HISTORY_MAX]; +extern int rc_history_index; +extern int rc_history_count; +#endif diff --git a/contrib/dhcp-3.0/includes/omapip/buffer.h b/contrib/dhcp-3.0/includes/omapip/buffer.h new file mode 100644 index 0000000000..cc6b6a9859 --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/buffer.h @@ -0,0 +1,83 @@ +/* buffer.h + + Definitions for the object management API protocol buffering... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +/* OMAPI buffers are ring buffers, which means that the beginning of the + buffer and the end of the buffer chase each other around. As long as + the tail never catches up to the head, there's room in the buffer for + data. + + - If the tail and the head are equal, the buffer is empty. + + - If the tail is less than the head, the contents of the buffer + are the bytes from the head to the end of buffer, and in addition, + the bytes between the beginning of the buffer and the tail, not + including the byte addressed by the tail. + + - If the tail is greater than the head, then the buffer contains + valid bytes starting with the byte addressed by the head, and + ending with the byte before the byte addressed by the tail. + + There will always be at least one byte of waste, because the tail can't + increase so that it's equal to the head (that would represent an empty + buffer. */ +#define OMAPI_BUF_SIZE 4048 +typedef struct _omapi_buffer { + struct _omapi_buffer *next; /* Buffers can be chained. */ + u_int32_t refcnt; /* Buffers are reference counted. */ + u_int16_t head, tail; /* Buffers are organized in a ring. */ + char buf [OMAPI_BUF_SIZE]; /* The actual buffer is included in + the buffer data structure. */ +} omapi_buffer_t; + +#define BUFFER_BYTES_FREE(x) \ + ((x) -> tail > (x) -> head \ + ? sizeof ((x) -> buf) - ((x) -> tail - (x) -> head) \ + : (x) -> head - (x) -> tail) + +#define BYTES_IN_BUFFER(x) \ + ((x) -> tail > (x) -> head \ + ? (x) -> tail - (x) -> head - 1 \ + : sizeof ((x) -> buf) - ((x) -> head - (x) -> tail) - 1) + +isc_result_t omapi_connection_require (omapi_object_t *, unsigned); +isc_result_t omapi_connection_copyout (unsigned char *, + omapi_object_t *, unsigned); +isc_result_t omapi_connection_copyin (omapi_object_t *, + const unsigned char *, unsigned); +isc_result_t omapi_connection_flush (omapi_object_t *); +isc_result_t omapi_connection_get_uint32 (omapi_object_t *, u_int32_t *); +isc_result_t omapi_connection_put_uint32 (omapi_object_t *, u_int32_t); +isc_result_t omapi_connection_get_uint16 (omapi_object_t *, u_int16_t *); +isc_result_t omapi_connection_put_uint16 (omapi_object_t *, u_int32_t); + diff --git a/contrib/dhcp-3.0/includes/omapip/convert.h b/contrib/dhcp-3.0/includes/omapip/convert.h new file mode 100644 index 0000000000..3fbf347dc9 --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/convert.h @@ -0,0 +1,52 @@ +/* convert.h + + Safe copying of integers into and out of a non-aligned memory buffer. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef OMAPI_CONVERT_H +#define OMAPI_CONVERT_H + +u_int32_t getULong (const unsigned char *); +int32_t getLong (const unsigned char *); +u_int32_t getUShort (const unsigned char *); +int32_t getShort (const unsigned char *); +u_int32_t getUChar (const unsigned char *); +void putULong (unsigned char *, u_int32_t); +void putLong (unsigned char *, int32_t); +void putUShort (unsigned char *, u_int32_t); +void putShort (unsigned char *, int32_t); +void putUChar (unsigned char *, u_int32_t); +int converted_length (const unsigned char *, unsigned int, unsigned int); +int binary_to_ascii (unsigned char *, const unsigned char *, + unsigned int, unsigned int); + +#endif /* OMAPI_CONVERT_H */ diff --git a/contrib/dhcp-3.0/includes/omapip/hash.h b/contrib/dhcp-3.0/includes/omapip/hash.h new file mode 100644 index 0000000000..b8ecbe6c5b --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/hash.h @@ -0,0 +1,150 @@ +/* hash.h + + Definitions for hashing... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef OMAPI_HASH_H +#define OMAPI_HASH_H + +#define DEFAULT_HASH_SIZE 9973 + +/* The purpose of the hashed_object_t struct is to not match anything else. */ +typedef struct { + int foo; +} hashed_object_t; + +typedef void (*hash_foreach_func) (const unsigned char *, + unsigned, hashed_object_t *); +typedef int (*hash_reference) (hashed_object_t **, hashed_object_t *, + const char *, int); +typedef int (*hash_dereference) (hashed_object_t **, const char *, int); + +struct hash_bucket { + struct hash_bucket *next; + const unsigned char *name; + unsigned len; + hashed_object_t *value; +}; + +typedef int (*hash_comparator_t)(const void *, const void *, unsigned long); + +struct hash_table { + unsigned hash_count; + struct hash_bucket *buckets [DEFAULT_HASH_SIZE]; + hash_reference referencer; + hash_dereference dereferencer; + hash_comparator_t cmp; + int (*do_hash) (const unsigned char *, unsigned, unsigned); +}; + +struct named_hash { + struct named_hash *next; + const char *name; + struct hash_table *hash; +}; + +#define HASH_FUNCTIONS_DECL(name, bufarg, type, hashtype) \ +void name##_hash_add (hashtype *, bufarg, unsigned, type *, \ + const char *, int); \ +void name##_hash_delete (hashtype *, bufarg, unsigned, \ + const char *, int); \ +int name##_hash_lookup (type **, hashtype *, bufarg, unsigned, \ + const char *, int); \ +int name##_hash_foreach (hashtype *, \ + void (*) (bufarg, unsigned, type *)); \ +int name##_new_hash (hashtype **, int, const char *, int); \ +void name##_free_hash_table (hashtype **, const char *, int); + + +#define HASH_FUNCTIONS(name, bufarg, type, hashtype, ref, deref) \ +void name##_hash_add (hashtype *table, \ + bufarg buf, unsigned len, type *ptr, \ + const char *file, int line) \ +{ \ + add_hash ((struct hash_table *)table, \ + (const unsigned char *)buf, \ + len, (hashed_object_t *)ptr, file, line); \ +} \ + \ +void name##_hash_delete (hashtype *table, \ + bufarg buf, unsigned len, const char *file, int line)\ +{ \ + delete_hash_entry ((struct hash_table *)table, \ + (const unsigned char *)buf, \ + len, file, line); \ +} \ + \ +int name##_hash_lookup (type **ptr, hashtype *table, \ + bufarg buf, unsigned len, const char *file, int line) \ +{ \ + return hash_lookup ((hashed_object_t **)ptr, \ + (struct hash_table *)table, \ + (const unsigned char *)buf, len, file, line); \ +} \ + \ +int name##_hash_foreach (hashtype *table, \ + void (*func) (bufarg, unsigned, type *)) \ +{ \ + return hash_foreach ((struct hash_table *)table, \ + (hash_foreach_func)func); \ +} \ + \ +int name##_new_hash (hashtype **tp, int c, const char *file, int line) \ +{ \ + return new_hash ((struct hash_table **)tp, \ + (hash_reference)ref, (hash_dereference)deref, c, \ + file, line); \ +} \ + \ +void name##_free_hash_table (hashtype **table, const char *file, int line) \ +{ \ + free_hash_table ((struct hash_table **)table, file, line); \ +} + +void relinquish_hash_bucket_hunks (void); +int new_hash_table (struct hash_table **, int, const char *, int); +void free_hash_table (struct hash_table **, const char *, int); +struct hash_bucket *new_hash_bucket (const char *, int); +void free_hash_bucket (struct hash_bucket *, const char *, int); +int new_hash (struct hash_table **, + hash_reference, hash_dereference, int, const char *, int); +void add_hash (struct hash_table *, + const unsigned char *, unsigned, hashed_object_t *, + const char *, int); +void delete_hash_entry (struct hash_table *, const unsigned char *, + unsigned, const char *, int); +int hash_lookup (hashed_object_t **, struct hash_table *, + const unsigned char *, unsigned, const char *, int); +int hash_foreach (struct hash_table *, hash_foreach_func); +int casecmp (const void *s, const void *t, unsigned long len); + +#endif /* OMAPI_HASH_H */ diff --git a/contrib/dhcp-3.0/includes/omapip/omapip.h b/contrib/dhcp-3.0/includes/omapip/omapip.h new file mode 100644 index 0000000000..3c8f15670f --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/omapip.h @@ -0,0 +1,611 @@ +/* omapip.h + + Definitions for the object management API and protocol... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef _OMAPIP_H_ +#define _OMAPIP_H_ +#include + +typedef unsigned int omapi_handle_t; + +struct __omapi_object; +typedef struct __omapi_object omapi_object_t; + +typedef enum { + omapi_datatype_int, + omapi_datatype_string, + omapi_datatype_data, + omapi_datatype_object +} omapi_datatype_t; + +typedef struct { + int refcnt; + omapi_datatype_t type; + union { + struct { + unsigned len; +#define OMAPI_TYPED_DATA_NOBUFFER_LEN (sizeof (int) + \ + sizeof (omapi_datatype_t) + \ + sizeof (int)) + unsigned char value [1]; + } buffer; +#define OMAPI_TYPED_DATA_OBJECT_LEN (sizeof (int) + \ + sizeof (omapi_datatype_t) + \ + sizeof (omapi_object_t *)) + omapi_object_t *object; +#define OMAPI_TYPED_DATA_REF_LEN (sizeof (int) + \ + sizeof (omapi_datatype_t) + \ + 3 * sizeof (void *)) + struct { + void *ptr; + isc_result_t (*reference) (void *, + void *, const char *, int); + isc_result_t (*dereference) (void *, + const char *, int); + } ref; +#define OMAPI_TYPED_DATA_INT_LEN (sizeof (int) + \ + sizeof (omapi_datatype_t) + \ + sizeof (int)) + int integer; + } u; +} omapi_typed_data_t; + +typedef struct { + int refcnt; + unsigned len; +#define OMAPI_DATA_STRING_EMPTY_SIZE (2 * sizeof (int)) + unsigned char value [1]; +} omapi_data_string_t; + +typedef struct { + int refcnt; + omapi_data_string_t *name; + omapi_typed_data_t *value; +} omapi_value_t; + +typedef struct __omapi_object_type_t { + const char *name; + struct __omapi_object_type_t *next; + + isc_result_t (*set_value) (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); + isc_result_t (*get_value) (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, omapi_value_t **); + isc_result_t (*destroy) (omapi_object_t *, const char *, int); + isc_result_t (*signal_handler) (omapi_object_t *, + const char *, va_list); + isc_result_t (*stuff_values) (omapi_object_t *, + omapi_object_t *, omapi_object_t *); + isc_result_t (*lookup) (omapi_object_t **, omapi_object_t *, + omapi_object_t *); + isc_result_t (*create) (omapi_object_t **, omapi_object_t *); + isc_result_t (*remove) (omapi_object_t *, omapi_object_t *); + isc_result_t (*freer) (omapi_object_t *, const char *, int); + isc_result_t (*allocator) (omapi_object_t **, const char *, int); + isc_result_t (*sizer) (size_t); + size_t size; + int rc_flag; + isc_result_t (*initialize) (omapi_object_t *, const char *, int); +} omapi_object_type_t; + +#define OMAPI_OBJECT_PREAMBLE \ + omapi_object_type_t *type; \ + int refcnt; \ + omapi_handle_t handle; \ + omapi_object_t *outer, *inner + +/* The omapi handle structure. */ +struct __omapi_object { + OMAPI_OBJECT_PREAMBLE; +}; + +/* The port on which applications should listen for OMAPI connections. */ +#define OMAPI_PROTOCOL_PORT 7911 + +typedef struct { + unsigned addrtype; + unsigned addrlen; + unsigned char address [16]; + unsigned port; +} omapi_addr_t; + +typedef struct { + int refcnt; + unsigned count; + omapi_addr_t *addresses; +} omapi_addr_list_t; + +typedef struct auth_key { + OMAPI_OBJECT_PREAMBLE; + char *name; + char *algorithm; + omapi_data_string_t *key; +} omapi_auth_key_t; + +#define OMAPI_CREATE 1 +#define OMAPI_UPDATE 2 +#define OMAPI_EXCL 4 +#define OMAPI_NOTIFY_PROTOCOL 8 + +#define OMAPI_OBJECT_ALLOC(name, stype, type) \ +isc_result_t name##_allocate (stype **p, const char *file, int line) \ +{ \ + return omapi_object_allocate ((omapi_object_t **)p, \ + type, 0, file, line); \ +} \ + \ +isc_result_t name##_reference (stype **pptr, stype *ptr, \ + const char *file, int line) \ +{ \ + return omapi_object_reference ((omapi_object_t **)pptr, \ + (omapi_object_t *)ptr, file, line); \ +} \ + \ +isc_result_t name##_dereference (stype **ptr, const char *file, int line) \ +{ \ + return omapi_object_dereference ((omapi_object_t **)ptr, file, line); \ +} + +#define OMAPI_OBJECT_ALLOC_DECL(name, stype, type) \ +isc_result_t name##_allocate (stype **p, const char *file, int line); \ +isc_result_t name##_reference (stype **pptr, stype *ptr, \ + const char *file, int line); \ +isc_result_t name##_dereference (stype **ptr, const char *file, int line); + +typedef isc_result_t (*omapi_array_ref_t) (char **, char *, const char *, int); +typedef isc_result_t (*omapi_array_deref_t) (char **, const char *, int); + +/* An extensible array type. */ +typedef struct { + char **data; + omapi_array_ref_t ref; + omapi_array_deref_t deref; + int count; + int max; +} omapi_array_t; + +#define OMAPI_ARRAY_TYPE(name, stype) \ +isc_result_t name##_array_allocate (omapi_array_t **p, \ + const char *file, int line) \ +{ \ + return (omapi_array_allocate \ + (p, \ + (omapi_array_ref_t)name##_reference, \ + (omapi_array_deref_t)name##_dereference, \ + file, line)); \ +} \ + \ +isc_result_t name##_array_free (omapi_array_t **p, \ + const char *file, int line) \ +{ \ + return omapi_array_free (p, file, line); \ +} \ + \ +isc_result_t name##_array_extend (omapi_array_t *pptr, stype *ptr, int *index,\ + const char *file, int line) \ +{ \ + return omapi_array_extend (pptr, (char *)ptr, index, file, line); \ +} \ + \ +isc_result_t name##_array_set (omapi_array_t *pptr, stype *ptr, int index, \ + const char *file, int line) \ +{ \ + return omapi_array_set (pptr, (char *)ptr, index, file, line); \ +} \ + \ +isc_result_t name##_array_lookup (stype **ptr, omapi_array_t *pptr, \ + int index, const char *file, int line) \ +{ \ + return omapi_array_lookup ((char **)ptr, pptr, index, file, line); \ +} + +#define OMAPI_ARRAY_TYPE_DECL(name, stype) \ +isc_result_t name##_array_allocate (omapi_array_t **, const char *, int); \ +isc_result_t name##_array_free (omapi_array_t **, const char *, int); \ +isc_result_t name##_array_extend (omapi_array_t *, stype *, int *, \ + const char *, int); \ +isc_result_t name##_array_set (omapi_array_t *, \ + stype *, int, const char *, int); \ +isc_result_t name##_array_lookup (stype **, \ + omapi_array_t *, int, const char *, int) + +#define omapi_array_foreach_begin(array, stype, var) \ + { \ + int omapi_array_foreach_index; \ + stype *var = (stype *)0; \ + for (omapi_array_foreach_index = 0; \ + array && \ + omapi_array_foreach_index < (array) -> count; \ + omapi_array_foreach_index++) { \ + if ((array) -> data [omapi_array_foreach_index]) { \ + ((*(array) -> ref) \ + ((char **)&var, \ + (array) -> data [omapi_array_foreach_index],\ + MDL)); + +#define omapi_array_foreach_end(array, stype, var) \ + (*(array) -> deref) ((char **)&var, MDL); \ + } \ + } \ + } + +isc_result_t omapi_protocol_connect (omapi_object_t *, + const char *, unsigned, omapi_object_t *); +isc_result_t omapi_connect_list (omapi_object_t *, omapi_addr_list_t *, + omapi_addr_t *); +isc_result_t omapi_protocol_listen (omapi_object_t *, unsigned, int); +isc_boolean_t omapi_protocol_authenticated (omapi_object_t *); +isc_result_t omapi_protocol_configure_security (omapi_object_t *, + isc_result_t (*) + (omapi_object_t *, + omapi_addr_t *), + isc_result_t (*) + (omapi_object_t *, + omapi_auth_key_t *)); +isc_result_t omapi_protocol_accept (omapi_object_t *); +isc_result_t omapi_protocol_send_intro (omapi_object_t *, unsigned, unsigned); +isc_result_t omapi_protocol_ready (omapi_object_t *); +isc_result_t omapi_protocol_add_auth (omapi_object_t *, omapi_object_t *, + omapi_handle_t); +isc_result_t omapi_protocol_lookup_auth (omapi_object_t **, omapi_object_t *, + omapi_handle_t); +isc_result_t omapi_protocol_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_protocol_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_protocol_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); + +isc_result_t omapi_protocol_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_protocol_send_message (omapi_object_t *, + omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_protocol_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_protocol_listener_set_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_protocol_listener_get_value (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_protocol_listener_destroy (omapi_object_t *, + const char *, int); +isc_result_t omapi_protocol_listener_signal (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_protocol_listener_stuff (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_protocol_send_status (omapi_object_t *, omapi_object_t *, + isc_result_t, unsigned, const char *); +isc_result_t omapi_protocol_send_open (omapi_object_t *, omapi_object_t *, + const char *, omapi_object_t *, + unsigned); +isc_result_t omapi_protocol_send_update (omapi_object_t *, omapi_object_t *, + unsigned, omapi_object_t *); + +isc_result_t omapi_connect (omapi_object_t *, const char *, unsigned); +isc_result_t omapi_disconnect (omapi_object_t *, int); +int omapi_connection_readfd (omapi_object_t *); +int omapi_connection_writefd (omapi_object_t *); +isc_result_t omapi_connection_connect (omapi_object_t *); +isc_result_t omapi_connection_reader (omapi_object_t *); +isc_result_t omapi_connection_writer (omapi_object_t *); +isc_result_t omapi_connection_reaper (omapi_object_t *); +isc_result_t omapi_connection_output_auth_length (omapi_object_t *, + unsigned *); +isc_result_t omapi_connection_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_connection_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_connection_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_connection_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_connection_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_connection_write_typed_data (omapi_object_t *, + omapi_typed_data_t *); +isc_result_t omapi_connection_put_name (omapi_object_t *, const char *); +isc_result_t omapi_connection_put_string (omapi_object_t *, const char *); +isc_result_t omapi_connection_put_handle (omapi_object_t *c, + omapi_object_t *h); + +isc_result_t omapi_listen (omapi_object_t *, unsigned, int); +isc_result_t omapi_listen_addr (omapi_object_t *, + omapi_addr_t *, int); +isc_result_t omapi_listener_accept (omapi_object_t *); +int omapi_listener_readfd (omapi_object_t *); +isc_result_t omapi_accept (omapi_object_t *); +isc_result_t omapi_listener_configure_security (omapi_object_t *, + isc_result_t (*) + (omapi_object_t *, + omapi_addr_t *)); +isc_result_t omapi_listener_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_listener_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_listener_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_listener_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_listener_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); + +isc_result_t omapi_register_io_object (omapi_object_t *, + int (*)(omapi_object_t *), + int (*)(omapi_object_t *), + isc_result_t (*)(omapi_object_t *), + isc_result_t (*)(omapi_object_t *), + isc_result_t (*)(omapi_object_t *)); +isc_result_t omapi_unregister_io_object (omapi_object_t *); +isc_result_t omapi_dispatch (struct timeval *); +isc_result_t omapi_wait_for_completion (omapi_object_t *, struct timeval *); +isc_result_t omapi_one_dispatch (omapi_object_t *, struct timeval *); +isc_result_t omapi_io_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_io_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, omapi_value_t **); +isc_result_t omapi_io_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_io_signal_handler (omapi_object_t *, const char *, va_list); +isc_result_t omapi_io_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_waiter_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *, + void *), + void *p); + +isc_result_t omapi_generic_new (omapi_object_t **, const char *, int); +isc_result_t omapi_generic_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_generic_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_generic_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_generic_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_generic_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_generic_clear_flags (omapi_object_t *); + +isc_result_t omapi_message_new (omapi_object_t **, const char *, int); +isc_result_t omapi_message_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_message_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_message_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_message_signal_handler (omapi_object_t *, + const char *, va_list); +isc_result_t omapi_message_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_message_register (omapi_object_t *); +isc_result_t omapi_message_unregister (omapi_object_t *); +isc_result_t omapi_message_process (omapi_object_t *, omapi_object_t *); + +OMAPI_OBJECT_ALLOC_DECL (omapi_auth_key, + omapi_auth_key_t, omapi_type_auth_key) +isc_result_t omapi_auth_key_new (omapi_auth_key_t **, const char *, int); +isc_result_t omapi_auth_key_destroy (omapi_object_t *, const char *, int); +isc_result_t omapi_auth_key_enter (omapi_auth_key_t *); +isc_result_t omapi_auth_key_lookup_name (omapi_auth_key_t **, const char *); +isc_result_t omapi_auth_key_lookup (omapi_object_t **, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_auth_key_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_auth_key_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); + +extern omapi_object_type_t *omapi_type_connection; +extern omapi_object_type_t *omapi_type_listener; +extern omapi_object_type_t *omapi_type_io_object; +extern omapi_object_type_t *omapi_type_generic; +extern omapi_object_type_t *omapi_type_protocol; +extern omapi_object_type_t *omapi_type_protocol_listener; +extern omapi_object_type_t *omapi_type_waiter; +extern omapi_object_type_t *omapi_type_remote; +extern omapi_object_type_t *omapi_type_message; +extern omapi_object_type_t *omapi_type_auth_key; + +extern omapi_object_type_t *omapi_object_types; + +void omapi_type_relinquish (void); +isc_result_t omapi_init (void); +isc_result_t omapi_object_type_register (omapi_object_type_t **, + const char *, + isc_result_t (*) + (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *), + isc_result_t (*) + (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **), + isc_result_t (*) (omapi_object_t *, + const char *, int), + isc_result_t (*) (omapi_object_t *, + const char *, + va_list), + isc_result_t (*) (omapi_object_t *, + omapi_object_t *, + omapi_object_t *), + isc_result_t (*) (omapi_object_t **, + omapi_object_t *, + omapi_object_t *), + isc_result_t (*) (omapi_object_t **, + omapi_object_t *), + isc_result_t (*) (omapi_object_t *, + omapi_object_t *), + isc_result_t (*) (omapi_object_t *, + const char *, int), + isc_result_t (*) (omapi_object_t **, + const char *, int), + isc_result_t (*) (size_t), size_t, + isc_result_t (*) (omapi_object_t *, + const char *, int), + int); +isc_result_t omapi_signal (omapi_object_t *, const char *, ...); +isc_result_t omapi_signal_in (omapi_object_t *, const char *, ...); +isc_result_t omapi_set_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *); +isc_result_t omapi_set_value_str (omapi_object_t *, omapi_object_t *, + const char *, omapi_typed_data_t *); +isc_result_t omapi_set_boolean_value (omapi_object_t *, omapi_object_t *, + const char *, int); +isc_result_t omapi_set_int_value (omapi_object_t *, omapi_object_t *, + const char *, int); +isc_result_t omapi_set_object_value (omapi_object_t *, omapi_object_t *, + const char *, omapi_object_t *); +isc_result_t omapi_set_string_value (omapi_object_t *, omapi_object_t *, + const char *, const char *); +isc_result_t omapi_get_value (omapi_object_t *, omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **); +isc_result_t omapi_get_value_str (omapi_object_t *, omapi_object_t *, + const char *, omapi_value_t **); +isc_result_t omapi_stuff_values (omapi_object_t *, + omapi_object_t *, + omapi_object_t *); +isc_result_t omapi_object_create (omapi_object_t **, omapi_object_t *, + omapi_object_type_t *); +isc_result_t omapi_object_update (omapi_object_t *, omapi_object_t *, + omapi_object_t *, omapi_handle_t); +int omapi_data_string_cmp (omapi_data_string_t *, omapi_data_string_t *); +int omapi_ds_strcmp (omapi_data_string_t *, const char *); +int omapi_td_strcmp (omapi_typed_data_t *, const char *); +int omapi_td_strcasecmp (omapi_typed_data_t *, const char *); +isc_result_t omapi_make_value (omapi_value_t **, omapi_data_string_t *, + omapi_typed_data_t *, const char *, int); +isc_result_t omapi_make_const_value (omapi_value_t **, omapi_data_string_t *, + const unsigned char *, + unsigned, const char *, int); +isc_result_t omapi_make_int_value (omapi_value_t **, omapi_data_string_t *, + int, const char *, int); +isc_result_t omapi_make_uint_value (omapi_value_t **, omapi_data_string_t *, + unsigned int, const char *, int); +isc_result_t omapi_make_object_value (omapi_value_t **, omapi_data_string_t *, + omapi_object_t *, const char *, int); +isc_result_t omapi_make_handle_value (omapi_value_t **, omapi_data_string_t *, + omapi_object_t *, const char *, int); +isc_result_t omapi_make_string_value (omapi_value_t **, omapi_data_string_t *, + const char *, const char *, int); +isc_result_t omapi_get_int_value (unsigned long *, omapi_typed_data_t *); + +isc_result_t omapi_object_handle (omapi_handle_t *, omapi_object_t *); +isc_result_t omapi_handle_lookup (omapi_object_t **, omapi_handle_t); +isc_result_t omapi_handle_td_lookup (omapi_object_t **, omapi_typed_data_t *); + +void * dmalloc (unsigned, const char *, int); +void dfree (void *, const char *, int); +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void dmalloc_reuse (void *, const char *, int, int); +void dmalloc_dump_outstanding (void); +#else +#define dmalloc_reuse(x,y,l,z) +#endif +#define MDL __FILE__, __LINE__ +#if defined (DEBUG_RC_HISTORY) +void dump_rc_history (void *); +void rc_history_next (int); +#endif +void omapi_print_dmalloc_usage_by_caller (void); +isc_result_t omapi_object_allocate (omapi_object_t **, + omapi_object_type_t *, + size_t, const char *, int); +isc_result_t omapi_object_initialize (omapi_object_t *, + omapi_object_type_t *, + size_t, size_t, const char *, int); +isc_result_t omapi_object_reference (omapi_object_t **, + omapi_object_t *, const char *, int); +isc_result_t omapi_object_dereference (omapi_object_t **, const char *, int); +isc_result_t omapi_typed_data_new (const char *, int, omapi_typed_data_t **, + omapi_datatype_t, ...); +isc_result_t omapi_typed_data_reference (omapi_typed_data_t **, + omapi_typed_data_t *, + const char *, int); +isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **, + const char *, int); +isc_result_t omapi_data_string_new (omapi_data_string_t **, + unsigned, const char *, int); +isc_result_t omapi_data_string_reference (omapi_data_string_t **, + omapi_data_string_t *, + const char *, int); +isc_result_t omapi_data_string_dereference (omapi_data_string_t **, + const char *, int); +isc_result_t omapi_value_new (omapi_value_t **, const char *, int); +isc_result_t omapi_value_reference (omapi_value_t **, + omapi_value_t *, const char *, int); +isc_result_t omapi_value_dereference (omapi_value_t **, const char *, int); +isc_result_t omapi_addr_list_new (omapi_addr_list_t **, unsigned, + const char *, int); +isc_result_t omapi_addr_list_reference (omapi_addr_list_t **, + omapi_addr_list_t *, + const char *, int); +isc_result_t omapi_addr_list_dereference (omapi_addr_list_t **, + const char *, int); + +isc_result_t omapi_array_allocate (omapi_array_t **, omapi_array_ref_t, + omapi_array_deref_t, const char *, int); +isc_result_t omapi_array_free (omapi_array_t **, const char *, int); +isc_result_t omapi_array_extend (omapi_array_t *, char *, int *, + const char *, int); +isc_result_t omapi_array_set (omapi_array_t *, void *, int, const char *, int); +isc_result_t omapi_array_lookup (char **, + omapi_array_t *, int, const char *, int); +OMAPI_ARRAY_TYPE_DECL(omapi_object, omapi_object_t); +#endif /* _OMAPIP_H_ */ diff --git a/contrib/dhcp-3.0/includes/omapip/omapip_p.h b/contrib/dhcp-3.0/includes/omapip/omapip_p.h new file mode 100644 index 0000000000..d8adecd0d6 --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/omapip_p.h @@ -0,0 +1,293 @@ +/* omapip_p.h + + Private master include file for the OMAPI library. */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#ifndef __OMAPIP_OMAPIP_P_H__ +#define __OMAPIP_OMAPIP_P_H__ + +#ifndef __CYGWIN32__ +#include +#include +#include +#include +#include + +#include +#else +#define fd_set cygwin_fd_set +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdefs.h" +#include "osdep.h" + +#include +#include + +#include +#include +#include +#include + +/* OMAPI protocol header, version 1.00 */ +typedef struct { + u_int32_t authlen; /* Length of authenticator. */ + u_int32_t authid; /* Authenticator object ID. */ + u_int32_t op; /* Opcode. */ + omapi_handle_t handle; /* Handle of object being operated on, + or zero. */ + u_int32_t id; /* Transaction ID. */ + u_int32_t rid; /* ID of transaction to which this is a response. */ +} omapi_protocol_header_t; + +#define OMAPI_PROTOCOL_VERSION 100 + +#define OMAPI_OP_OPEN 1 +#define OMAPI_OP_REFRESH 2 +#define OMAPI_OP_UPDATE 3 +#define OMAPI_OP_NOTIFY 4 +#define OMAPI_OP_STATUS 5 +#define OMAPI_OP_DELETE 6 + +typedef enum { + omapi_connection_unconnected, + omapi_connection_connecting, + omapi_connection_connected, + omapi_connection_disconnecting, + omapi_connection_closed +} omapi_connection_state_t; + +typedef enum { + omapi_protocol_intro_wait, + omapi_protocol_header_wait, + omapi_protocol_signature_wait, + omapi_protocol_name_wait, + omapi_protocol_name_length_wait, + omapi_protocol_value_wait, + omapi_protocol_value_length_wait +} omapi_protocol_state_t; + +typedef struct __omapi_message_object { + OMAPI_OBJECT_PREAMBLE; + struct __omapi_message_object *next, *prev; + omapi_object_t *object; + omapi_object_t *notify_object; + struct __omapi_protocol_object *protocol_object; + u_int32_t authlen; + omapi_typed_data_t *authenticator; + u_int32_t authid; + omapi_object_t *id_object; + u_int32_t op; + u_int32_t h; + u_int32_t id; + u_int32_t rid; +} omapi_message_object_t; + +typedef struct __omapi_remote_auth { + struct __omapi_remote_auth *next; + omapi_handle_t remote_handle; + omapi_object_t *a; +} omapi_remote_auth_t; + +typedef struct __omapi_protocol_object { + OMAPI_OBJECT_PREAMBLE; + u_int32_t header_size; + u_int32_t protocol_version; + u_int32_t next_xid; + + omapi_protocol_state_t state; /* Input state. */ + int reading_message_values; /* True if reading message-specific + values. */ + omapi_message_object_t *message; /* Incoming message. */ + omapi_data_string_t *name; /* Incoming name. */ + omapi_typed_data_t *value; /* Incoming value. */ + isc_result_t verify_result; + omapi_remote_auth_t *default_auth; /* Default authinfo to use. */ + omapi_remote_auth_t *remote_auth_list; /* Authenticators active on + this connection. */ + + isc_boolean_t insecure; /* Set to allow unauthenticated + messages. */ + + isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *); +} omapi_protocol_object_t; + +typedef struct { + OMAPI_OBJECT_PREAMBLE; + + isc_boolean_t insecure; /* Set to allow unauthenticated + messages. */ + + isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *); +} omapi_protocol_listener_object_t; + +#include + +typedef struct __omapi_listener_object { + OMAPI_OBJECT_PREAMBLE; + int socket; /* Connection socket. */ + int index; + struct sockaddr_in address; + isc_result_t (*verify_addr) (omapi_object_t *, omapi_addr_t *); +} omapi_listener_object_t; + +typedef struct __omapi_connection_object { + OMAPI_OBJECT_PREAMBLE; + int socket; /* Connection socket. */ + int32_t index; + omapi_connection_state_t state; + struct sockaddr_in remote_addr; + struct sockaddr_in local_addr; + omapi_addr_list_t *connect_list; /* List of addresses to which + to connect. */ + int cptr; /* Current element we are connecting to. */ + u_int32_t bytes_needed; /* Bytes of input needed before wakeup. */ + u_int32_t in_bytes; /* Bytes of input already buffered. */ + omapi_buffer_t *inbufs; + u_int32_t out_bytes; /* Bytes of output in buffers. */ + omapi_buffer_t *outbufs; + omapi_listener_object_t *listener; /* Listener that accepted this + connection, if any. */ + DST_KEY *in_key; /* Authenticator signing incoming + data. */ + void *in_context; /* Input hash context. */ + DST_KEY *out_key; /* Authenticator signing outgoing + data. */ + void *out_context; /* Output hash context. */ +} omapi_connection_object_t; + +typedef struct __omapi_io_object { + OMAPI_OBJECT_PREAMBLE; + struct __omapi_io_object *next; + int (*readfd) (omapi_object_t *); + int (*writefd) (omapi_object_t *); + isc_result_t (*reader) (omapi_object_t *); + isc_result_t (*writer) (omapi_object_t *); + isc_result_t (*reaper) (omapi_object_t *); +} omapi_io_object_t; + +typedef struct __omapi_generic_object { + OMAPI_OBJECT_PREAMBLE; + omapi_value_t **values; + u_int8_t *changed; + int nvalues, va_max; +} omapi_generic_object_t; + +typedef struct __omapi_waiter_object { + OMAPI_OBJECT_PREAMBLE; + int ready; + isc_result_t waitstatus; + struct __omapi_waiter_object *next; +} omapi_waiter_object_t; + +#define OMAPI_HANDLE_TABLE_SIZE 120 + +typedef struct __omapi_handle_table { + omapi_handle_t first, limit; + omapi_handle_t next; + int leafp; + union { + omapi_object_t *object; + struct __omapi_handle_table *table; + } children [OMAPI_HANDLE_TABLE_SIZE]; +} omapi_handle_table_t; + +#include + +OMAPI_OBJECT_ALLOC_DECL (omapi_protocol, omapi_protocol_object_t, + omapi_type_protocol) +OMAPI_OBJECT_ALLOC_DECL (omapi_protocol_listener, + omapi_protocol_listener_object_t, + omapi_type_protocol_listener) +OMAPI_OBJECT_ALLOC_DECL (omapi_connection, + omapi_connection_object_t, omapi_type_connection) +OMAPI_OBJECT_ALLOC_DECL (omapi_listener, + omapi_listener_object_t, omapi_type_listener) +OMAPI_OBJECT_ALLOC_DECL (omapi_io, + omapi_io_object_t, omapi_type_io_object) +OMAPI_OBJECT_ALLOC_DECL (omapi_waiter, + omapi_waiter_object_t, omapi_type_waiter) +OMAPI_OBJECT_ALLOC_DECL (omapi_generic, + omapi_generic_object_t, omapi_type_generic) +OMAPI_OBJECT_ALLOC_DECL (omapi_message, + omapi_message_object_t, omapi_type_message) + +isc_result_t omapi_connection_sign_data (int mode, + DST_KEY *key, + void **context, + const unsigned char *data, + const unsigned len, + omapi_typed_data_t **result); +isc_result_t omapi_listener_connect (omapi_connection_object_t **obj, + omapi_listener_object_t *listener, + int socket, + struct sockaddr_in *remote_addr); +void omapi_listener_trace_setup (void); +void omapi_connection_trace_setup (void); +void omapi_buffer_trace_setup (void); +void omapi_connection_register (omapi_connection_object_t *, + const char *, int); +void trace_mr_init (void); + +OMAPI_ARRAY_TYPE_DECL(omapi_listener, omapi_listener_object_t); +OMAPI_ARRAY_TYPE_DECL(omapi_connection, omapi_connection_object_t); + +extern int log_priority; +extern int log_perror; +extern void (*log_cleanup) (void); + +void log_fatal (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +int log_error (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +int log_info (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +int log_debug (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +void do_percentm (char *obuf, const char *ibuf); + +isc_result_t uerr2isc (int); +isc_result_t ns_rcode_to_isc (int); + +extern omapi_message_object_t *omapi_registered_messages; + +#endif /* __OMAPIP_OMAPIP_P_H__ */ diff --git a/contrib/dhcp-3.0/includes/omapip/trace.h b/contrib/dhcp-3.0/includes/omapip/trace.h new file mode 100644 index 0000000000..d48c3700ea --- /dev/null +++ b/contrib/dhcp-3.0/includes/omapip/trace.h @@ -0,0 +1,115 @@ +/* trace.h + + Definitions for omapi tracing facility... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon, as part of a project for Nominum, Inc. To learn more + * about Internet Systems Consortium, see http://www.isc.org/. To + * learn more about Nominum, Inc., see ``http://www.nominum.com''. + */ + +#define TRACEFILE_MAGIC 0x64484370UL /* dHCp */ +#define TRACEFILE_VERSION 1 + +/* The first thing in a trace file is the header, which basically just + defines the version of the file. */ +typedef struct { + u_int32_t magic; /* Magic number for trace file. */ + u_int32_t version; /* Version of file. */ + int32_t hlen; /* Length of this header. */ + int32_t phlen; /* Length of packet headers. */ +} tracefile_header_t; + +/* The trace file is composed of a bunch of trace packets. Each such packet + has a type, followed by a length, followed by a timestamp, followed by + the actual contents of the packet. The type indexes are not fixed - + they are allocated either on readback or when writing a trace file. + One index type is reserved - type zero means that this record is a type + name to index mapping. */ +typedef struct { + u_int32_t type_index; /* Index to the type of handler that this + packet needs. */ + u_int32_t length; /* Length of the packet. This includes + everything except the fixed header. */ + u_int32_t when; /* When the packet was written. */ + u_int32_t pad; /* Round this out to a quad boundary. */ +} tracepacket_t; + +#define TRACE_INDEX_MAPPING_SIZE 4 /* trace_index_mapping_t less name. */ +typedef struct { + u_int32_t index; + char name [1]; +} trace_index_mapping_t; + +struct trace_type; /* forward */ +typedef struct trace_type trace_type_t; + +struct trace_type { + trace_type_t *next; + int index; + char *name; + void *baggage; + void (*have_packet) (trace_type_t *, unsigned, char *); + void (*stop_tracing) (trace_type_t *); +}; + +typedef struct trace_iov { + const char *buf; + unsigned len; +} trace_iov_t; + +typedef struct { + u_int16_t addrtype; + u_int16_t addrlen; + u_int8_t address [16]; + u_int16_t port; +} trace_addr_t; + +void trace_free_all (void); +int trace_playback (void); +int trace_record (void); +isc_result_t trace_init (void (*set_time) (u_int32_t), const char *, int); +isc_result_t trace_begin (const char *, const char *, int); +isc_result_t trace_write_packet (trace_type_t *, unsigned, const char *, + const char *, int); +isc_result_t trace_write_packet_iov (trace_type_t *, int, trace_iov_t *, + const char *, int); +void trace_type_stash (trace_type_t *); +trace_type_t *trace_type_register (const char *, void *, + void (*) (trace_type_t *, + unsigned, char *), + void (*) (trace_type_t *), + const char *, int); +void trace_stop (void); +void trace_index_map_input (trace_type_t *, unsigned, char *); +void trace_index_stop_tracing (trace_type_t *); +void trace_replay_init (void); +void trace_file_replay (const char *); +isc_result_t trace_get_next_packet (trace_type_t **, tracepacket_t *, + char **, unsigned *, unsigned *); +isc_result_t trace_get_file (trace_type_t *, + const char *, unsigned *, char **); +isc_result_t trace_get_packet (trace_type_t **, unsigned *, char **); +time_t trace_snoop_time (trace_type_t **); diff --git a/contrib/dhcp-3.0/includes/osdep.h b/contrib/dhcp-3.0/includes/osdep.h new file mode 100644 index 0000000000..5d1bc912cf --- /dev/null +++ b/contrib/dhcp-3.0/includes/osdep.h @@ -0,0 +1,319 @@ +/* osdep.h + + Operating system dependencies... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +#if !defined (__ISC_DHCP_OSDEP_H__) +#define __ISC_DHCP_OSDEP_H__ + +#include "site.h" + +/* Porting:: + + If you add a new network API, you must add a check for it below: */ + +#if !defined (USE_SOCKETS) && \ + !defined (USE_SOCKET_SEND) && \ + !defined (USE_SOCKET_RECEIVE) && \ + !defined (USE_RAW_SOCKETS) && \ + !defined (USE_RAW_SEND) && \ + !defined (USE_SOCKET_RECEIVE) && \ + !defined (USE_BPF) && \ + !defined (USE_BPF_SEND) && \ + !defined (USE_BPF_RECEIVE) && \ + !defined (USE_LPF) && \ + !defined (USE_LPF_SEND) && \ + !defined (USE_LPF_RECEIVE) && \ + !defined (USE_NIT) && \ + !defined (USE_NIT_SEND) && \ + !defined (USE_NIT_RECEIVE) && \ + !defined (USR_DLPI_SEND) && \ + !defined (USE_DLPI_RECEIVE) +# define USE_DEFAULT_NETWORK +#endif + +#if !defined (TIME_MAX) +# define TIME_MAX 2147483647 +#endif + +/* Porting:: + + If you add a new system configuration file, include it here: */ + +#if defined (sun) +# if defined (__svr4__) || defined (__SVR4) +# include "cf/sunos5-5.h" +# else +# include "cf/sunos4.h" +# endif +#endif + +#ifdef aix +# include "cf/aix.h" +#endif + +#ifdef bsdi +# include "cf/bsdos.h" +#endif + +#ifdef __NetBSD__ +# include "cf/netbsd.h" +#endif + +#ifdef __FreeBSD__ +# include "cf/freebsd.h" +#endif + +#ifdef OpenBSD +# include "cf/openbsd.h" +#endif + +#if defined (__osf__) && defined (__alpha) +# include "cf/alphaosf.h" +#endif + +#ifdef ultrix +# include "cf/ultrix.h" +#endif + +#ifdef linux +# include "cf/linux.h" +#endif + +#ifdef SCO +# include "cf/sco.h" +#endif + +#if defined (hpux) || defined (__hpux) +# include "cf/hpux.h" +#endif + +#ifdef __QNX__ +# include "cf/qnx.h" +#endif + +#ifdef __CYGWIN32__ +# include "cf/cygwin32.h" +#endif + +#ifdef __APPLE__ +# include "cf/rhapsody.h" +#else +# if defined (NeXT) +# include "cf/nextstep.h" +# endif +#endif + +/* snprintf/vsnprintf hacks. for systems with no libc versions only. */ +#ifdef NO_SNPRINTF + extern int isc_print_snprintf(char *, size_t, const char *, ...); + extern int isc_print_vsnprintf(char *, size_t, const char *, va_list ap); +# define snprintf isc_print_snprintf +# define vsnprintf isc_print_vsnprintf +#endif + +/* Porting:: + + If you add a new network API, and have it set up so that it can be + used for sending or receiving, but doesn't have to be used for both, + then set up an ifdef like the ones below: */ + +#ifdef USE_SOCKETS +# define USE_SOCKET_SEND +# define USE_SOCKET_RECEIVE +#endif + +#ifdef USE_RAW_SOCKETS +# define USE_RAW_SEND +# define USE_SOCKET_RECEIVE +#endif + +#ifdef USE_BPF +# define USE_BPF_SEND +# define USE_BPF_RECEIVE +#endif + +#ifdef USE_LPF +# define USE_LPF_SEND +# define USE_LPF_RECEIVE +#endif + +#ifdef USE_NIT +# define USE_NIT_SEND +# define USE_NIT_RECEIVE +#endif + +#ifdef USE_DLPI +# define USE_DLPI_SEND +# define USE_DLPI_RECEIVE +#endif + +#ifdef USE_UPF +# define USE_UPF_SEND +# define USE_UPF_RECEIVE +#endif + +/* Porting:: + + If you add support for sending packets directly out an interface, + and your support does not do ARP or routing, you must use a fallback + mechanism to deal with packets that need to be sent to routers. + Currently, all low-level packet interfaces use BSD sockets as a + fallback. */ + +#if defined (USE_BPF_SEND) || defined (USE_NIT_SEND) || \ + defined (USE_DLPI_SEND) || defined (USE_UPF_SEND) || \ + defined (USE_LPF_SEND) || \ + (defined (USE_SOCKET_SEND) && defined (HAVE_SO_BINDTODEVICE)) +# define USE_SOCKET_FALLBACK +# define USE_FALLBACK +#endif + +/* Porting:: + + If you add support for sending packets directly out an interface + and need to be able to assemble packets, add the USE_XXX_SEND + definition for your interface to the list tested below. */ + +#if defined (USE_RAW_SEND) || defined (USE_BPF_SEND) || \ + defined (USE_NIT_SEND) || defined (USE_UPF_SEND) || \ + defined (USE_DLPI_SEND) || defined (USE_LPF_SEND) +# define PACKET_ASSEMBLY +#endif + +/* Porting:: + + If you add support for receiving packets directly from an interface + and need to be able to decode raw packets, add the USE_XXX_RECEIVE + definition for your interface to the list tested below. */ + +#if defined (USE_RAW_RECEIVE) || defined (USE_BPF_SEND) || \ + defined (USE_NIT_RECEIVE) || defined (USE_UPF_RECEIVE) || \ + defined (USE_DLPI_RECEIVE) || defined (USE_LPF_RECEIVE) +# define PACKET_DECODING +#endif + +/* If we don't have a DLPI packet filter, we have to filter in userland. + Probably not worth doing, actually. */ +#if defined (USE_DLPI_RECEIVE) && !defined (USE_DLPI_PFMOD) +# define USERLAND_FILTER +#endif + +/* jmp_buf is assumed to be a struct unless otherwise defined in the + system header. */ +#ifndef jbp_decl +# define jbp_decl(x) jmp_buf *x +#endif +#ifndef jref +# define jref(x) (&(x)) +#endif +#ifndef jdref +# define jdref(x) (*(x)) +#endif +#ifndef jrefproto +# define jrefproto jmp_buf * +#endif + +#ifndef BPF_FORMAT +# define BPF_FORMAT "/dev/bpf%d" +#endif + +#if defined (F_SETFD) && !defined (HAVE_SETFD) +# define HAVE_SETFD +#endif + +#if defined (IFF_POINTOPOINT) && !defined (HAVE_IFF_POINTOPOINT) +# define HAVE_IFF_POINTOPOINT +#endif + +#if defined (AF_LINK) && !defined (HAVE_AF_LINK) +# define HAVE_AF_LINK +#endif + +#if defined (ARPHRD_TUNNEL) && !defined (HAVE_ARPHRD_TUNNEL) +# define HAVE_ARPHRD_TUNNEL +#endif + +#if defined (ARPHRD_LOOPBACK) && !defined (HAVE_ARPHRD_LOOPBACK) +# define HAVE_ARPHRD_LOOPBACK +#endif + +#if defined (ARPHRD_ROSE) && !defined (HAVE_ARPHRD_ROSE) +# define HAVE_ARPHRD_ROSE +#endif + +#if defined (ARPHRD_IEEE802) && !defined (HAVE_ARPHRD_IEEE802) +# define HAVE_ARPHRD_IEEE802 +#endif + +#if defined (ARPHRD_IEEE802_TR) && !defined (HAVE_ARPHRD_IEEE802_TR) +# define HAVE_ARPHRD_IEEE802_TR +#endif + +#if defined (ARPHRD_FDDI) && !defined (HAVE_ARPHRD_FDDI) +# define HAVE_ARPHRD_FDDI +#endif + +#if defined (ARPHRD_AX25) && !defined (HAVE_ARPHRD_AX25) +# define HAVE_ARPHRD_AX25 +#endif + +#if defined (ARPHRD_NETROM) && !defined (HAVE_ARPHRD_NETROM) +# define HAVE_ARPHRD_NETROM +#endif + +#if defined (ARPHRD_METRICOM) && !defined (HAVE_ARPHRD_METRICOM) +# define HAVE_ARPHRD_METRICOM +#endif + +#if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE) +# define HAVE_SO_BINDTODEVICE +#endif + +#if defined (AF_LINK) && !defined (HAVE_AF_LINK) +# define HAVE_AF_LINK +#endif + +/* Linux needs to define SHUT_* in /usr/include/sys/socket.h someday... */ +#if !defined (SHUT_RD) +# define SHUT_RD 0 +#endif + +#if !defined (SOCKLEN_T) +# define SOCKLEN_T socklen_t +#endif + +#if !defined (STDERR_FILENO) +# define STDERR_FILENO 2 +#endif + +#endif /* __ISC_DHCP_OSDEP_H__ */ diff --git a/contrib/dhcp-3.0/includes/site.h b/contrib/dhcp-3.0/includes/site.h new file mode 100644 index 0000000000..b4d910a0e4 --- /dev/null +++ b/contrib/dhcp-3.0/includes/site.h @@ -0,0 +1,179 @@ +/* Site-specific definitions. + + For supported systems, you shouldn't need to make any changes here. + However, you may want to, in order to deal with site-specific + differences. */ + +/* Add any site-specific definitions and inclusions here... */ + +/* #include */ +/* #define SITE_FOOBAR */ + +/* Define this if you don't want dhcpd to run as a daemon and do want + to see all its output printed to stdout instead of being logged via + syslog(). This also makes dhcpd use the dhcpd.conf in its working + directory and write the dhcpd.leases file there. */ + +/* #define DEBUG */ + +/* Define this to see what the parser is parsing. You probably don't + want to see this. */ + +/* #define DEBUG_TOKENS */ + +/* Define this to see dumps of incoming and outgoing packets. This + slows things down quite a bit... */ + +/* #define DEBUG_PACKET */ + +/* Define this if you want to see dumps of expression evaluation. */ + +/* #define DEBUG_EXPRESSIONS */ + +/* Define this if you want to see dumps of find_lease() in action. */ + +/* #define DEBUG_FIND_LEASE */ + +/* Define this if you want to see dumps of parsed expressions. */ + +/* #define DEBUG_EXPRESSION_PARSE */ + +/* Define this if you want to watch the class matching process. */ + +/* #define DEBUG_CLASS_MATCHING */ + +/* Define this if you want to track memory usage for the purpose of + noticing memory leaks quickly. */ + +/* #define DEBUG_MEMORY_LEAKAGE */ + +/* Define this if you want exhaustive (and very slow) checking of the + malloc pool for corruption. */ + +/* #define DEBUG_MALLOC_POOL */ + +/* Define this if you want to see a message every time a lease's state + changes. */ +/* #define DEBUG_LEASE_STATE_TRANSITIONS */ + +/* Define this if you want to maintain a history of the last N operations + that changed reference counts on objects. This can be used to debug + cases where an object is dereferenced too often, or not often enough. */ + +/* #define DEBUG_RC_HISTORY */ + +/* Define this if you want to see the history every cycle. */ + +/* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */ + +/* This is the number of history entries to maintain - by default, 256. */ + +/* #define RC_HISTORY_MAX 10240 */ + +/* Define this if you want dhcpd to dump core when a non-fatal memory + allocation error is detected (i.e., something that would cause a + memory leak rather than a memory smash). */ + +/* #define POINTER_DEBUG */ + +/* Define this if you want debugging output for DHCP failover protocol + messages. */ + +/* #define DEBUG_FAILOVER_MESSAGES */ + +/* Define this if you want debugging output for DHCP failover protocol + lease assignment timing. */ + +/* #define DEBUG_FAILOVER_TIMING */ + +/* Define this if you want all leases written to the lease file, even if + they are free leases that have never been used. */ + +/* #define DEBUG_DUMP_ALL_LEASES */ + +/* Define this if you want DHCP failover protocol support in the DHCP + server. */ + +#define FAILOVER_PROTOCOL + +/* Define this if you want DNS update functionality to be available. */ + +#define NSUPDATE + +/* Define this if you want the dhcpd.pid file to go somewhere other than + the default (which varies from system to system, but is usually either + /etc or /var/run. */ + +/* #define _PATH_DHCPD_PID "/var/run/dhcpd.pid" */ + +/* Define this if you want the dhcpd.leases file (the dynamic lease database) + to go somewhere other than the default location, which is normally + /etc/dhcpd.leases. */ + +/* #define _PATH_DHCPD_DB "/etc/dhcpd.leases" */ + +/* Define this if you want the dhcpd.conf file to go somewhere other than + the default location. By default, it goes in /etc/dhcpd.conf. */ + +/* #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" */ + +/* Network API definitions. You do not need to choose one of these - if + you don't choose, one will be chosen for you in your system's config + header. DON'T MESS WITH THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!! */ + +/* Define this to use the standard BSD socket API. + + On many systems, the BSD socket API does not provide the ability to + send packets to the 255.255.255.255 broadcast address, which can + prevent some clients (e.g., Win95) from seeing replies. This is + not a problem on Solaris. + + In addition, the BSD socket API will not work when more than one + network interface is configured on the server. + + However, the BSD socket API is about as efficient as you can get, so if + the aforementioned problems do not matter to you, or if no other + API is supported for your system, you may want to go with it. */ + +/* #define USE_SOCKETS */ + +/* Define this to use the Sun Streams NIT API. + + The Sun Streams NIT API is only supported on SunOS 4.x releases. */ + +/* #define USE_NIT */ + +/* Define this to use the Berkeley Packet Filter API. + + The BPF API is available on all 4.4-BSD derivatives, including + NetBSD, FreeBSD and BSDI's BSD/OS. It's also available on + DEC Alpha OSF/1 in a compatibility mode supported by the Alpha OSF/1 + packetfilter interface. */ + +/* #define USE_BPF */ + +/* Define this to use the raw socket API. + + The raw socket API is provided on many BSD derivatives, and provides + a way to send out raw IP packets. It is only supported for sending + packets - packets must be received with the regular socket API. + This code is experimental - I've never gotten it to actually transmit + a packet to the 255.255.255.255 broadcast address - so use it at your + own risk. */ + +/* #define USE_RAW_SOCKETS */ + +/* Define this to change the logging facility used by dhcpd. */ + +/* #define DHCPD_LOG_FACILITY LOG_DAEMON */ + +/* Define this if you aren't debugging and you want to save memory + (potentially a _lot_ of memory) by allocating leases in chunks rather + than one at a time. */ + +#define COMPACT_LEASES + +/* Define this if you want to be able to save and playback server operational + traces. */ + +#define TRACING diff --git a/contrib/dhcp-3.0/includes/statement.h b/contrib/dhcp-3.0/includes/statement.h new file mode 100644 index 0000000000..44fe8a8489 --- /dev/null +++ b/contrib/dhcp-3.0/includes/statement.h @@ -0,0 +1,104 @@ +/* statement.h + + Definitions for executable statements... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +struct executable_statement { + int refcnt; + struct executable_statement *next; + enum statement_op { + null_statement, + if_statement, + add_statement, + eval_statement, + break_statement, + default_option_statement, + supersede_option_statement, + append_option_statement, + prepend_option_statement, + send_option_statement, + statements_statement, + on_statement, + switch_statement, + case_statement, + default_statement, + set_statement, + unset_statement, + let_statement, + define_statement, + log_statement, + return_statement + } op; + union { + struct { + struct executable_statement *tc, *fc; + struct expression *expr; + } ie; + struct expression *eval; + struct expression *retval; + struct class *add; + struct option_cache *option; + struct option_cache *supersede; + struct option_cache *prepend; + struct option_cache *append; + struct executable_statement *statements; + struct { + int evtypes; +# define ON_COMMIT 1 +# define ON_EXPIRY 2 +# define ON_RELEASE 4 +# define ON_TRANSMISSION 8 + struct executable_statement *statements; + } on; + struct { + struct expression *expr; + struct executable_statement *statements; + } s_switch; + struct expression *c_case; + struct { + char *name; + struct expression *expr; + struct executable_statement *statements; + } set, let; + char *unset; + struct { + enum { + log_priority_fatal, + log_priority_error, + log_priority_debug, + log_priority_info + } priority; + struct expression *expr; + } log; + } data; +}; + diff --git a/contrib/dhcp-3.0/includes/tree.h b/contrib/dhcp-3.0/includes/tree.h new file mode 100644 index 0000000000..64f4397766 --- /dev/null +++ b/contrib/dhcp-3.0/includes/tree.h @@ -0,0 +1,337 @@ +/* tree.h + + Definitions for address trees... */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + * + * This software has been written for Internet Systems Consortium + * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. + * To learn more about Internet Systems Consortium, see + * ``http://www.isc.org/''. To learn more about Vixie Enterprises, + * see ``http://www.vix.com''. To learn more about Nominum, Inc., see + * ``http://www.nominum.com''. + */ + +/* A pair of pointers, suitable for making a linked list. */ +typedef struct _pair { + caddr_t car; + struct _pair *cdr; +} *pair; + +struct option_chain_head { + int refcnt; + pair first; +}; + +struct enumeration_value { + const char *name; + u_int8_t value; +}; + +struct enumeration { + struct enumeration *next; + const char *name; + struct enumeration_value *values; +}; + +/* Tree node types... */ +#define TREE_CONCAT 1 +#define TREE_HOST_LOOKUP 2 +#define TREE_CONST 3 +#define TREE_LIMIT 4 +#define TREE_DATA_EXPR 5 + +/* A data buffer with a reference count. */ +struct buffer { + int refcnt; + unsigned char data [1]; +}; + +/* XXX The mechanism by which data strings are returned is currently + XXX broken: rather than returning an ephemeral pointer, we create + XXX a reference to the data in the caller's space, which the caller + XXX then has to dereference - instead, the reference should be + XXX ephemeral by default and be made a persistent reference explicitly. */ +/* XXX on the other hand, it seems to work pretty nicely, so maybe the + XXX above comment is meshuggenah. */ + +/* A string of data bytes, possibly accompanied by a larger buffer. */ +struct data_string { + struct buffer *buffer; + const unsigned char *data; + unsigned len; /* Does not include NUL terminator, if any. */ + int terminated; +}; + +enum expression_context { + context_any, /* indefinite */ + context_boolean, + context_data, + context_numeric, + context_dns, + context_data_or_numeric, /* indefinite */ + context_function +}; + +struct fundef { + int refcnt; + struct string_list *args; + struct executable_statement *statements; +}; + +struct binding_value { + int refcnt; + enum { + binding_boolean, + binding_data, + binding_numeric, + binding_dns, + binding_function + } type; + union value { + struct data_string data; + unsigned long intval; + int boolean; +#if defined (NSUPDATE) + ns_updrec *dns; +#endif + struct fundef *fundef; + struct binding_value *bv; + } value; +}; + +struct binding { + struct binding *next; + char *name; + struct binding_value *value; +}; + +struct binding_scope { + int refcnt; + struct binding_scope *outer; + struct binding *bindings; +}; + +/* Expression tree structure. */ + +enum expr_op { + expr_none, + expr_match, + expr_check, + expr_equal, + expr_substring, + expr_suffix, + expr_concat, + expr_host_lookup, + expr_and, + expr_or, + expr_not, + expr_option, + expr_hardware, + expr_packet, + expr_const_data, + expr_extract_int8, + expr_extract_int16, + expr_extract_int32, + expr_encode_int8, + expr_encode_int16, + expr_encode_int32, + expr_const_int, + expr_exists, + expr_encapsulate, + expr_known, + expr_reverse, + expr_leased_address, + expr_binary_to_ascii, + expr_config_option, + expr_host_decl_name, + expr_pick_first_value, + expr_lease_time, + expr_dns_transaction, + expr_static, + expr_ns_add, + expr_ns_delete, + expr_ns_exists, + expr_ns_not_exists, + expr_not_equal, + expr_null, + expr_variable_exists, + expr_variable_reference, + expr_filename, + expr_sname, + expr_arg, + expr_funcall, + expr_function, + expr_add, + expr_subtract, + expr_multiply, + expr_divide, + expr_remainder, + expr_binary_and, + expr_binary_or, + expr_binary_xor, + expr_client_state +}; + +struct expression { + int refcnt; + enum expr_op op; + union { + struct { + struct expression *expr; + struct expression *offset; + struct expression *len; + } substring; + struct expression *equal [2]; + struct expression *and [2]; + struct expression *or [2]; + struct expression *not; + struct expression *add; + struct expression *subtract; + struct expression *multiply; + struct expression *divide; + struct expression *remainder; + struct collection *check; + struct { + struct expression *expr; + struct expression *len; + } suffix; + struct option *option; + struct option *config_option; + struct { + struct expression *offset; + struct expression *len; + } packet; + struct data_string const_data; + struct expression *extract_int; + struct expression *encode_int; + unsigned long const_int; + struct expression *concat [2]; + struct dns_host_entry *host_lookup; + struct option *exists; + struct data_string encapsulate; + struct { + struct expression *base; + struct expression *width; + struct expression *seperator; + struct expression *buffer; + } b2a; + struct { + struct expression *width; + struct expression *buffer; + } reverse; + struct { + struct expression *car; + struct expression *cdr; + } pick_first_value; + struct { + struct expression *car; + struct expression *cdr; + } dns_transaction; + struct { + unsigned rrclass; + unsigned rrtype; + struct expression *rrname; + struct expression *rrdata; + struct expression *ttl; + } ns_add; + struct { + unsigned rrclass; + unsigned rrtype; + struct expression *rrname; + struct expression *rrdata; + } ns_delete, ns_exists, ns_not_exists; + char *variable; + struct { + struct expression *val; + struct expression *next; + } arg; + struct { + char *name; + struct expression *arglist; + } funcall; + struct fundef *func; + } data; + int flags; +# define EXPR_EPHEMERAL 1 +}; + +/* DNS host entry structure... */ +struct dns_host_entry { + int refcnt; + TIME timeout; + struct data_string data; + char hostname [1]; +}; + +struct option_cache; /* forward */ +struct packet; /* forward */ +struct option_state; /* forward */ +struct decoded_option_state; /* forward */ +struct lease; /* forward */ +struct client_state; /* forward */ + +struct universe { + const char *name; + struct option_cache *(*lookup_func) (struct universe *, + struct option_state *, + unsigned); + void (*save_func) (struct universe *, struct option_state *, + struct option_cache *); + void (*foreach) (struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, struct universe *, void *, + void (*) (struct option_cache *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, + struct option_state *, + struct binding_scope **, + struct universe *, void *)); + void (*delete_func) (struct universe *universe, + struct option_state *, int); + int (*option_state_dereference) (struct universe *, + struct option_state *, + const char *, int); + int (*decode) (struct option_state *, + const unsigned char *, unsigned, struct universe *); + int (*encapsulate) (struct data_string *, struct packet *, + struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct binding_scope **, + struct universe *); + void (*store_tag) PROTO ((unsigned char *, u_int32_t)); + void (*store_length) PROTO ((unsigned char *, u_int32_t)); + int tag_size, length_size; + option_hash_t *hash; + struct option *options [256]; + struct option *enc_opt; + int index; +}; + +struct option { + const char *name; + const char *format; + struct universe *universe; + unsigned code; +}; diff --git a/contrib/dhcp-3.0/includes/version.h b/contrib/dhcp-3.0/includes/version.h new file mode 100644 index 0000000000..62136922d1 --- /dev/null +++ b/contrib/dhcp-3.0/includes/version.h @@ -0,0 +1,3 @@ +/* Current version of ISC DHCP Distribution. */ + +#define DHCP_VERSION "V3.0.2rc3" diff --git a/contrib/dhcp-3.0/minires/ns_date.c b/contrib/dhcp-3.0/minires/ns_date.c new file mode 100644 index 0000000000..ccd84d36e7 --- /dev/null +++ b/contrib/dhcp-3.0/minires/ns_date.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_date.c,v 1.2.2.1 2004/06/10 17:59:40 dhankins Exp $"; +#endif + +/* Import. */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static int datepart(const char *, int, int, int, int *); + +/* Public. */ + +/* Convert a date in ASCII into the number of seconds since + 1 January 1970 (GMT assumed). Format is yyyymmddhhmmss, all + digits required, no spaces allowed. */ + +u_int32_t +ns_datetosecs(const char *cp, int *errp) { + struct tm time; + u_int32_t result; + int mdays, i; + static const int days_per_month[12] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if (strlen(cp) != 14) { + *errp = 1; + return (0); + } + *errp = 0; + + memset(&time, 0, sizeof time); + time.tm_year = datepart(cp + 0, 4, 1990, 9999, errp) - 1900; + time.tm_mon = datepart(cp + 4, 2, 01, 12, errp) - 1; + time.tm_mday = datepart(cp + 6, 2, 01, 31, errp); + time.tm_hour = datepart(cp + 8, 2, 00, 23, errp); + time.tm_min = datepart(cp + 10, 2, 00, 59, errp); + time.tm_sec = datepart(cp + 12, 2, 00, 59, errp); + if (*errp) /* Any parse errors? */ + return (0); + + /* + * OK, now because timegm() is not available in all environments, + * we will do it by hand. Roll up sleeves, curse the gods, begin! + */ + +#define SECS_PER_DAY ((u_int32_t)24*60*60) +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + + result = time.tm_sec; /* Seconds */ + result += time.tm_min * 60; /* Minutes */ + result += time.tm_hour * (60*60); /* Hours */ + result += (time.tm_mday - 1) * SECS_PER_DAY; /* Days */ + + /* Months are trickier. Look without leaping, then leap */ + mdays = 0; + for (i = 0; i < time.tm_mon; i++) + mdays += days_per_month[i]; + result += mdays * SECS_PER_DAY; /* Months */ + if (time.tm_mon > 1 && isleap(1900+time.tm_year)) + result += SECS_PER_DAY; /* Add leapday for this year */ + + /* First figure years without leapdays, then add them in. */ + /* The loop is slow, FIXME, but simple and accurate. */ + result += (time.tm_year - 70) * (SECS_PER_DAY*365); /* Years */ + for (i = 70; i < time.tm_year; i++) + if (isleap(1900+i)) + result += SECS_PER_DAY; /* Add leapday for prev year */ + + return (result); +} + +/* Private. */ + +/* + * Parse part of a date. Set error flag if any error. + * Don't reset the flag if there is no error. + */ +static int +datepart(const char *buf, int size, int min, int max, int *errp) { + int result = 0; + int i; + + for (i = 0; i < size; i++) { + if (!isdigit(buf[i])) + *errp = 1; + result = (result * 10) + buf[i] - '0'; + } + if (result < min) + *errp = 1; + if (result > max) + *errp = 1; + return (result); +} diff --git a/contrib/dhcp-3.0/minires/ns_name.c b/contrib/dhcp-3.0/minires/ns_name.c new file mode 100644 index 0000000000..49ec296011 --- /dev/null +++ b/contrib/dhcp-3.0/minires/ns_name.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_name.c,v 1.1.2.5 2004/06/10 17:59:40 dhankins Exp $"; +#endif + +#include + +#include +#include + +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +/* Data. */ + +static const char digits[] = "0123456789"; + +/* Forward. */ + +static int special(int); +static int printable(int); +static int dn_find(const u_char *, const u_char *, + const u_char * const *, + const u_char * const *); + +/* Public. */ + +/* + * ns_name_ntop(src, dst, dstsiz) + * Convert an encoded domain name to printable ascii as per RFC1035. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * The root is returned as "." + * All other domains are returned in non absolute form + */ +int +ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) { + const u_char *cp; + char *dn, *eom; + u_char c; + u_int n; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) != 0) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + if (dn != dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if (dn + n >= eom) { + errno = EMSGSIZE; + return (-1); + } + for ((void)NULL; n > 0; n--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = (char)c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = digits[c / 100]; + *dn++ = digits[(c % 100) / 10]; + *dn++ = digits[c % 10]; + } else { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = (char)c; + } + } + } + if (dn == dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * ns_name_pton(src, dst, dstsiz) + * Convert a ascii string into an encoded domain name as per RFC1035. + * return: + * -1 if it fails + * 1 if string was fully qualified + * 0 is string was not fully qualified + * notes: + * Enforces label and domain length limits. + */ + +int +ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { + u_char *label, *bp, *eom; + int c, n, escaped; + char *cp; + + escaped = 0; + bp = dst; + eom = dst + dstsiz; + label = bp++; + + while ((c = *src++) != 0) { + if (escaped) { + if ((cp = strchr(digits, c)) != NULL) { + n = (cp - digits) * 100; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits) * 10; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits); + if (n > 255) { + errno = EMSGSIZE; + return (-1); + } + c = n; + } + escaped = 0; + } else if (c == '\\') { + escaped = 1; + continue; + } else if (c == '.') { + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + /* Fully qualified ? */ + if (*src == '\0') { + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = '\0'; + } + if ((bp - dst) > MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + return (1); + } + if (c == 0 || *src == '.') { + errno = EMSGSIZE; + return (-1); + } + label = bp++; + continue; + } + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = (u_char)c; + } + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = 0; + } + if ((bp - dst) > MAXCDNAME) { /* src too big */ + errno = EMSGSIZE; + return (-1); + } + return (0); +} + +/* + * ns_name_ntol(src, dst, dstsiz) + * Convert a network strings labels into all lowercase. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * Enforces label and domain length limits. + */ + +int +ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) { + const u_char *cp; + u_char *dn, *eom; + u_char c; + u_int n; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) != 0) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + *dn++ = n; + if (dn + n >= eom) { + errno = EMSGSIZE; + return (-1); + } + for ((void)NULL; n > 0; n--) { + c = *cp++; + if (isupper(c)) + *dn++ = tolower(c); + else + *dn++ = c; + } + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * ns_name_unpack(msg, eom, src, dst, dstsiz) + * Unpack a domain name from a message, source may be compressed. + * return: + * -1 if it fails, or consumed octets if it succeeds. + */ +int +ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, + u_char *dst, size_t dstsiz) +{ + const u_char *srcp, *dstlim; + u_char *dstp; + unsigned n; + int len; + int checked; + + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + /* Fetch next label in domain name. */ + while ((n = *srcp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: + /* Limit checks. */ + if (dstp + n + 1 >= dstlim || srcp + n >= eom) { + errno = EMSGSIZE; + return (-1); + } + checked += n + 1; + *dstp++ = n; + memcpy(dstp, srcp, n); + dstp += n; + srcp += n; + break; + + case NS_CMPRSFLGS: + if (srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); + if (srcp < msg || srcp >= eom) { /* Out of range. */ + errno = EMSGSIZE; + return (-1); + } + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eom - msg) { + errno = EMSGSIZE; + return (-1); + } + break; + + default: + errno = EMSGSIZE; + return (-1); /* flag error */ + } + } + *dstp = '\0'; + if (len < 0) + len = srcp - src; + return (len); +} + +/* + * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) + * Pack domain name 'domain' into 'comp_dn'. + * return: + * Size of the compressed name, or -1. + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. The array + * ends with NULL. + * 'lastdnptr' is a pointer to the end of the array pointed to + * by 'dnptrs'. + * Side effects: + * The list of pointers in dnptrs is updated for labels inserted into + * the message as we compress the name. If 'dnptr' is NULL, we don't + * try to compress names. If 'lastdnptr' is NULL, we don't update the + * list. + */ +int +ns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char *dstp; + const u_char **cpp, **lpp, *eob, *msg; + const u_char *srcp; + unsigned n; + int l; + + srcp = src; + dstp = dst; + eob = dstp + dstsiz; + lpp = cpp = NULL; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++) + (void)NULL; + lpp = cpp; /* end of list to search */ + } + } else + msg = NULL; + + /* make sure the domain we are about to add is legal */ + l = 0; + do { + n = *srcp; + if ((n & NS_CMPRSFLGS) != 0) { + errno = EMSGSIZE; + return (-1); + } + l += n + 1; + if (l > MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + srcp += n + 1; + } while (n != 0); + + /* from here on we need to reset compression pointer array on error */ + srcp = src; + do { + /* Look to see if we can use pointers. */ + n = *srcp; + if (n != 0 && msg != NULL) { + l = dn_find(srcp, msg, (const u_char * const *)dnptrs, + (const u_char * const *)lpp); + if (l >= 0) { + if (dstp + 1 >= eob) { + goto cleanup; + } + *dstp++ = (l >> 8) | NS_CMPRSFLGS; + *dstp++ = l % 256; + return (dstp - dst); + } + /* Not found, save it. */ + if (lastdnptr != NULL && cpp < lastdnptr - 1 && + (dstp - msg) < 0x4000) { + *cpp++ = dstp; + *cpp = NULL; + } + } + /* copy label to buffer */ + if (n & NS_CMPRSFLGS) { /* Should not happen. */ + goto cleanup; + } + if (dstp + 1 + n >= eob) { + goto cleanup; + } + memcpy(dstp, srcp, n + 1); + srcp += n + 1; + dstp += n + 1; + } while (n != 0); + + if (dstp > eob) { +cleanup: + if (msg != NULL) + *lpp = NULL; + errno = EMSGSIZE; + return (-1); + } + return (dstp - dst); +} + +/* + * ns_name_uncompress(msg, eom, src, dst, dstsiz) + * Expand compressed domain name to presentation format. + * return: + * Number of bytes read out of `src', or -1 (with errno set). + * note: + * Root domain returns as "." not "". + */ +int +ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, size_t dstsiz) +{ + u_char tmp[NS_MAXCDNAME]; + int n; + + if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) + return (-1); + if (ns_name_ntop(tmp, dst, dstsiz) == -1) + return (-1); + return (n); +} + +/* + * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) + * Compress a domain name into wire format, using compression pointers. + * return: + * Number of bytes consumed in `dst' or -1 (with errno set). + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. + * The list ends with NULL. 'lastdnptr' is a pointer to the end of the + * array pointed to by 'dnptrs'. Side effect is to update the list of + * pointers for labels inserted into the message as we compress the name. + * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' + * is NULL, we don't update the list. + */ +int +ns_name_compress(const char *src, u_char *dst, size_t dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char tmp[NS_MAXCDNAME]; + + if (ns_name_pton(src, tmp, sizeof tmp) == -1) + return (-1); + return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); +} + +/* + * ns_name_skip(ptrptr, eom) + * Advance *ptrptr to skip over the compressed name it points at. + * return: + * 0 on success, -1 (with errno set) on failure. + */ +int +ns_name_skip(const u_char **ptrptr, const u_char *eom) { + const u_char *cp; + u_int n; + + cp = *ptrptr; + while (cp < eom && (n = *cp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + cp += n; + continue; + case NS_CMPRSFLGS: /* indirection */ + cp++; + break; + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + break; + } + if (cp > eom) { + errno = EMSGSIZE; + return (-1); + } + *ptrptr = cp; + return (0); +} + +/* Private. */ + +/* + * special(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? + * return: + * boolean. + */ +static int +special(int ch) { + switch (ch) { + case 0x22: /* '"' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + return (1); + default: + return (0); + } +} + +/* + * printable(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? + * return: + * boolean. + */ +static int +printable(int ch) { + return (ch > 0x20 && ch < 0x7f); +} + +/* + * Thinking in noninternationalized USASCII (per the DNS spec), + * convert this character to lower case if it's upper case. + */ +static int +mklower(int ch) { + if (ch >= 0x41 && ch <= 0x5A) + return (ch + 0x20); + return (ch); +} + +/* + * dn_find(domain, msg, dnptrs, lastdnptr) + * Search for the counted-label name in an array of compressed names. + * return: + * offset from msg if found, or -1. + * notes: + * dnptrs is the pointer to the first name on the list, + * not the pointer to the start of the message. + */ +static int +dn_find(const u_char *domain, const u_char *msg, + const u_char * const *dnptrs, + const u_char * const *lastdnptr) +{ + const u_char *dn, *cp, *sp; + const u_char * const *cpp; + u_int n; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) { + dn = domain; + sp = cp = *cpp; + while ((n = *cp++) != 0) { + /* + * check for indirection + */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + if (n != *dn++) + goto next; + for ((void)NULL; n > 0; n--) + if (mklower(*dn++) != mklower(*cp++)) + goto next; + /* Is next root for both ? */ + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; + + case NS_CMPRSFLGS: /* indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + break; + + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + } + next: ; + } + errno = ENOENT; + return (-1); +} diff --git a/contrib/dhcp-3.0/minires/ns_parse.c b/contrib/dhcp-3.0/minires/ns_parse.c new file mode 100644 index 0000000000..24abc719a5 --- /dev/null +++ b/contrib/dhcp-3.0/minires/ns_parse.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_parse.c,v 1.2.2.2 2004/06/10 17:59:40 dhankins Exp $"; +#endif + +/* Import. */ + +#include + +#include +#include + +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +/* Forward. */ + +static void setsection(ns_msg *msg, ns_sect sect); + +/* Macros. */ + +/* Public. */ + +/* These need to be in the same order as the nres.h:ns_flag enum. */ +struct _ns_flagdata _ns_flagdata[16] = { + { 0x8000, 15 }, /* qr. */ + { 0x7800, 11 }, /* opcode. */ + { 0x0400, 10 }, /* aa. */ + { 0x0200, 9 }, /* tc. */ + { 0x0100, 8 }, /* rd. */ + { 0x0080, 7 }, /* ra. */ + { 0x0040, 6 }, /* z. */ + { 0x0020, 5 }, /* ad. */ + { 0x0010, 4 }, /* cd. */ + { 0x000f, 0 }, /* rcode. */ + { 0x0000, 0 }, /* expansion (1/6). */ + { 0x0000, 0 }, /* expansion (2/6). */ + { 0x0000, 0 }, /* expansion (3/6). */ + { 0x0000, 0 }, /* expansion (4/6). */ + { 0x0000, 0 }, /* expansion (5/6). */ + { 0x0000, 0 }, /* expansion (6/6). */ +}; + +isc_result_t +ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count, + int *rc) { + const u_char *optr = ptr; + + for ((void)NULL; count > 0; count--) { + int b, rdlength; + + b = dn_skipname(ptr, eom); + if (b < 0) + return ISC_R_INCOMPLETE; + ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/; + if (section != ns_s_qd) { + if (ptr + NS_INT32SZ + NS_INT16SZ > eom) + return ISC_R_INCOMPLETE; + ptr += NS_INT32SZ/*TTL*/; + rdlength = getUShort(ptr); + ptr += 2; + ptr += rdlength/*RData*/; + } + } + if (ptr > eom) + return ISC_R_INCOMPLETE; + if (rc) + *rc = ptr - optr; + return ISC_R_SUCCESS; +} + +isc_result_t +ns_initparse(const u_char *msg, unsigned msglen, ns_msg *handle) { + const u_char *eom = msg + msglen; + int i; + + memset(handle, 0x5e, sizeof *handle); + handle->_msg = msg; + handle->_eom = eom; + if (msg + NS_INT16SZ > eom) + return ISC_R_INCOMPLETE; + handle->_id = getUShort (msg); + msg += 2; + if (msg + NS_INT16SZ > eom) + return ISC_R_INCOMPLETE; + handle->_flags = getUShort (msg); + msg += 2; + for (i = 0; i < ns_s_max; i++) { + if (msg + NS_INT16SZ > eom) + return ISC_R_INCOMPLETE; + handle->_counts[i] = getUShort (msg); + msg += 2; + } + for (i = 0; i < ns_s_max; i++) + if (handle->_counts[i] == 0) + handle->_sections[i] = NULL; + else { + int b; + isc_result_t status = + ns_skiprr(msg, eom, (ns_sect)i, + handle->_counts[i], &b); + + if (status != ISC_R_SUCCESS) + return STATUS; + handle->_sections[i] = msg; + msg += b; + } + if (msg != eom) + return ISC_R_INCOMPLETE; + setsection(handle, ns_s_max); + return ISC_R_SUCCESS; +} + +isc_result_t +ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) { + int b; + isc_result_t status; + + /* Make section right. */ + if (section < 0 || section >= ns_s_max) + return ISC_R_NOTIMPLEMENTED; + if (section != handle->_sect) + setsection(handle, section); + + /* Make rrnum right. */ + if (rrnum == -1) + rrnum = handle->_rrnum; + if (rrnum < 0 || rrnum >= handle->_counts[(int)section]) + return ISC_R_UNKNOWNATTRIBUTE; + if (rrnum < handle->_rrnum) + setsection(handle, section); + if (rrnum > handle->_rrnum) { + status = ns_skiprr(handle->_ptr, handle->_eom, section, + rrnum - handle->_rrnum, &b); + + if (status != ISC_R_SUCCESS) + return status; + handle->_ptr += b; + handle->_rrnum = rrnum; + } + + /* Do the parse. */ + b = dn_expand(handle->_msg, handle->_eom, + handle->_ptr, rr->name, NS_MAXDNAME); + if (b < 0) + return ISC_R_FORMERR; + handle->_ptr += b; + if (handle->_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) + return ISC_R_INCOMPLETE; + rr->type = getUShort (handle->_ptr); + handle -> _ptr += 2; + rr->rr_class = getUShort (handle->_ptr); + handle -> _ptr += 2; + if (section == ns_s_qd) { + rr->ttl = 0; + rr->rdlength = 0; + rr->rdata = NULL; + } else { + if (handle->_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) + return ISC_R_INCOMPLETE; + rr->ttl = getULong (handle->_ptr); + handle -> _ptr += 4; + rr->rdlength = getUShort (handle->_ptr); + handle -> _ptr += 2; + if (handle->_ptr + rr->rdlength > handle->_eom) + return ISC_R_INCOMPLETE; + rr->rdata = handle->_ptr; + handle->_ptr += rr->rdlength; + } + if (++handle->_rrnum > handle->_counts[(int)section]) + setsection(handle, (ns_sect)((int)section + 1)); + + /* All done. */ + return ISC_R_SUCCESS; +} + +/* Private. */ + +static void +setsection(ns_msg *msg, ns_sect sect) { + msg->_sect = sect; + if (sect == ns_s_max) { + msg->_rrnum = -1; + msg->_ptr = NULL; + } else { + msg->_rrnum = 0; + msg->_ptr = msg->_sections[(int)sect]; + } +} diff --git a/contrib/dhcp-3.0/minires/ns_samedomain.c b/contrib/dhcp-3.0/minires/ns_samedomain.c new file mode 100644 index 0000000000..a12d172fea --- /dev/null +++ b/contrib/dhcp-3.0/minires/ns_samedomain.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_samedomain.c,v 1.3.2.4 2004/06/10 17:59:41 dhankins Exp $"; +#endif + +#include +#include +#include + +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +/* + * int + * ns_samedomain(a, b) + * Check whether a name belongs to a domain. + * Inputs: + * a - the domain whose ancestory is being verified + * b - the potential ancestor we're checking against + * Return: + * boolean - is a at or below b? + * Notes: + * Trailing dots are first removed from name and domain. + * Always compare complete subdomains, not only whether the + * domain name is the trailing string of the given name. + * + * "host.foobar.top" lies in "foobar.top" and in "top" and in "" + * but NOT in "bar.top" + */ + +int +ns_samedomain(const char *a, const char *b) { + size_t la, lb; + int diff, i, escaped; + const char *cp; + + la = strlen(a); + lb = strlen(b); + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ + if (la != 0 && a[la - 1] == '.') { + escaped = 0; + /* Note this loop doesn't get executed if la==1. */ + for (i = la - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + la--; + } + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ + if (lb != 0 && b[lb - 1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if lb==1 */ + for (i = lb - 2; i >= 0; i--) + if (b[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + lb--; + } + + /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ + if (lb == 0) + return (1); + + /* 'b' longer than 'a' means 'a' can't be in 'b'. */ + if (lb > la) + return (0); + + /* 'a' and 'b' being equal at this point indicates sameness. */ + if (lb == la) + return (strncasecmp(a, b, lb) == 0); + + /* Ok, we know la > lb. */ + + diff = la - lb; + + /* + * If 'a' is only 1 character longer than 'b', then it can't be + * a subdomain of 'b' (because of the need for the '.' label + * separator). + */ + if (diff < 2) + return (0); + + /* + * If the character before the last 'lb' characters of 'b' + * isn't '.', then it can't be a match (this lets us avoid + * having "foobar.com" match "bar.com"). + */ + if (a[diff - 1] != '.') + return (0); + + /* + * We're not sure about that '.', however. It could be escaped + * and thus not a really a label separator. + */ + escaped = 0; + for (i = diff - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (escaped) + return (0); + + /* Now compare aligned trailing substring. */ + cp = a + diff; + return (strncasecmp(cp, b, lb) == 0); +} + +/* + * int + * ns_subdomain(a, b) + * is "a" a subdomain of "b"? + */ +int +ns_subdomain(const char *a, const char *b) { + return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); +} + +/* + * int + * ns_makecanon(src, dst, dstsize) + * make a canonical copy of domain name "src" + * notes: + * foo -> foo. + * foo. -> foo. + * foo.. -> foo. + * foo\. -> foo\.. + * foo\\. -> foo\\. + */ + +isc_result_t +ns_makecanon(const char *src, char *dst, size_t dstsize) { + size_t n = strlen(src); + + if (n + sizeof "." > dstsize) { + return ISC_R_NOSPACE; + } + strcpy(dst, src); + while (n > 0 && dst[n - 1] == '.') /* Ends in "." */ + if (n > 1 && dst[n - 2] == '\\' && /* Ends in "\." */ + (n < 2 || dst[n - 3] != '\\')) /* But not "\\." */ + break; + else + dst[--n] = '\0'; + dst[n++] = '.'; + dst[n] = '\0'; + return ISC_R_SUCCESS; +} + +/* + * int + * ns_samename(a, b) + * determine whether domain name "a" is the same as domain name "b" + * return: + * -1 on error + * 0 if names differ + * 1 if names are the same + */ + +int +ns_samename(const char *a, const char *b) { + char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; + isc_result_t status; + + status = ns_makecanon(a, ta, sizeof ta); + if (status != ISC_R_SUCCESS) + return status; + status = ns_makecanon(b, tb, sizeof tb); + if (status != ISC_R_SUCCESS) + return (-1); + if (strcasecmp(ta, tb) == 0) + return (1); + else + return (0); +} diff --git a/contrib/dhcp-3.0/minires/ns_sign.c b/contrib/dhcp-3.0/minires/ns_sign.c new file mode 100644 index 0000000000..0061725728 --- /dev/null +++ b/contrib/dhcp-3.0/minires/ns_sign.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_sign.c,v 1.4.2.4 2004/06/10 17:59:42 dhankins Exp $"; +#endif + +#if defined (TRACING) +#define time(x) trace_mr_time (x) +time_t trace_mr_time (time_t *); +#endif + +/* Import. */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +#include + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eob) { \ + return ISC_R_NOSPACE; \ + } \ + } while (0) + +/* ns_sign + * Parameters: + * msg message to be sent + * msglen input - length of message + * output - length of signed message + * msgsize length of buffer containing message + * error value to put in the error field + * key tsig key used for signing + * querysig (response), the signature in the query + * querysiglen (response), the length of the signature in the query + * sig a buffer to hold the generated signature + * siglen input - length of signature buffer + * output - length of signature + * + * Errors: + * - bad input data (-1) + * - bad key / sign failed (-BADKEY) + * - not enough space (NS_TSIG_ERROR_NO_SPACE) + */ +isc_result_t +ns_sign(u_char *msg, unsigned *msglen, unsigned msgsize, int error, void *k, + const u_char *querysig, unsigned querysiglen, u_char *sig, + unsigned *siglen, time_t in_timesigned) +{ + HEADER *hp = (HEADER *)msg; + DST_KEY *key = (DST_KEY *)k; + u_char *cp = msg + *msglen, *eob = msg + msgsize; + u_char *lenp; + u_char *name, *alg; + unsigned n; + time_t timesigned; + + dst_init(); + if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL) + return ISC_R_INVALIDARG; + + /* Name. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) + n = dn_comp(key->dk_key_name, + cp, (unsigned)(eob - cp), NULL, NULL); + else + n = dn_comp("", cp, (unsigned)(eob - cp), NULL, NULL); + if (n < 0) + return ISC_R_NOSPACE; + name = cp; + cp += n; + + /* Type, class, ttl, length (not filled in yet). */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(ns_t_tsig, cp); + PUTSHORT(ns_c_any, cp); + PUTLONG(0, cp); /* TTL */ + lenp = cp; + cp += 2; + + /* Alg. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + if (key->dk_alg != KEY_HMAC_MD5) + return ISC_R_BADKEY; + n = dn_comp(NS_TSIG_ALG_HMAC_MD5, + cp, (unsigned)(eob - cp), NULL, NULL); + } + else + n = dn_comp("", cp, (unsigned)(eob - cp), NULL, NULL); + if (n < 0) + return ISC_R_NOSPACE; + alg = cp; + cp += n; + + /* Time. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(0, cp); + timesigned = time(NULL); + if (error != ns_r_badtime) + PUTLONG(timesigned, cp); + else + PUTLONG(in_timesigned, cp); + PUTSHORT(NS_TSIG_FUDGE, cp); + + /* Compute the signature. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + void *ctx; + u_char buf[MAXDNAME], *cp2; + unsigned n; + + dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); + + /* Digest the query signature, if this is a response. */ + if (querysiglen > 0 && querysig != NULL) { + u_int16_t len_n = htons(querysiglen); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + (u_char *)&len_n, INT16SZ, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + querysig, querysiglen, NULL, 0); + } + + /* Digest the message. */ + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen, + NULL, 0); + + /* Digest the key name. */ + n = ns_name_ntol(name, buf, sizeof(buf)); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the class and TTL. */ + cp2 = buf; + PUTSHORT(ns_c_any, cp2); + PUTLONG(0, cp2); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + buf, (unsigned)(cp2-buf), NULL, 0); + + /* Digest the algorithm. */ + n = ns_name_ntol(alg, buf, sizeof(buf)); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the time signed, fudge, error, and other data */ + cp2 = buf; + PUTSHORT(0, cp2); /* Top 16 bits of time */ + if (error != ns_r_badtime) + PUTLONG(timesigned, cp2); + else + PUTLONG(in_timesigned, cp2); + PUTSHORT(NS_TSIG_FUDGE, cp2); + PUTSHORT(error, cp2); /* Error */ + if (error != ns_r_badtime) + PUTSHORT(0, cp2); /* Other data length */ + else { + PUTSHORT(INT16SZ+INT32SZ, cp2); /* Other data length */ + PUTSHORT(0, cp2); /* Top 16 bits of time */ + PUTLONG(timesigned, cp2); + } + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + buf, (unsigned)(cp2-buf), NULL, 0); + + n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, + sig, *siglen); + if (n < 0) + return ISC_R_BADKEY; + *siglen = n; + } else + *siglen = 0; + + /* Add the signature. */ + BOUNDS_CHECK(cp, INT16SZ + (*siglen)); + PUTSHORT(*siglen, cp); + memcpy(cp, sig, *siglen); + cp += (*siglen); + + /* The original message ID & error. */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ); + PUTSHORT(ntohs(hp->id), cp); /* already in network order */ + PUTSHORT(error, cp); + + /* Other data. */ + BOUNDS_CHECK(cp, INT16SZ); + if (error != ns_r_badtime) + PUTSHORT(0, cp); /* Other data length */ + else { + PUTSHORT(INT16SZ+INT32SZ, cp); /* Other data length */ + BOUNDS_CHECK(cp, INT32SZ+INT16SZ); + PUTSHORT(0, cp); /* Top 16 bits of time */ + PUTLONG(timesigned, cp); + } + + /* Go back and fill in the length. */ + PUTSHORT(cp - lenp - INT16SZ, lenp); + + hp->arcount = htons(ntohs(hp->arcount) + 1); + *msglen = (cp - msg); + return ISC_R_SUCCESS; +} + +#if 0 +isc_result_t +ns_sign_tcp_init(void *k, const u_char *querysig, unsigned querysiglen, + ns_tcp_tsig_state *state) +{ + dst_init(); + if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) + return ISC_R_INVALIDARG; + state->counter = -1; + state->key = k; + if (state->key->dk_alg != KEY_HMAC_MD5) + return ISC_R_BADKEY; + if (querysiglen > sizeof(state->sig)) + return ISC_R_NOSPACE; + memcpy(state->sig, querysig, querysiglen); + state->siglen = querysiglen; + return ISC_R_SUCCESS; +} + +isc_result_t +ns_sign_tcp(u_char *msg, unsigned *msglen, unsigned msgsize, int error, + ns_tcp_tsig_state *state, int done) +{ + u_char *cp, *eob, *lenp; + u_char buf[MAXDNAME], *cp2; + HEADER *hp = (HEADER *)msg; + time_t timesigned; + int n; + + if (msg == NULL || msglen == NULL || state == NULL) + return ISC_R_INVALIDARG; + + state->counter++; + if (state->counter == 0) + return ns_sign(msg, msglen, msgsize, error, state->key, + state->sig, state->siglen, + state->sig, &state->siglen, 0); + + if (state->siglen > 0) { + u_int16_t siglen_n = htons(state->siglen); + dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx, + NULL, 0, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + (u_char *)&siglen_n, INT16SZ, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + state->sig, state->siglen, NULL, 0); + state->siglen = 0; + } + + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen, + NULL, 0); + + if (done == 0 && (state->counter % 100 != 0)) + return ISC_R_SUCCESS; + + cp = msg + *msglen; + eob = msg + msgsize; + + /* Name. */ + n = dn_comp(state->key->dk_key_name, + cp, (unsigned)(eob - cp), NULL, NULL); + if (n < 0) + return ISC_R_NOSPACE; + cp += n; + + /* Type, class, ttl, length (not filled in yet). */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(ns_t_tsig, cp); + PUTSHORT(ns_c_any, cp); + PUTLONG(0, cp); /* TTL */ + lenp = cp; + cp += 2; + + /* Alg. */ + n = dn_comp(NS_TSIG_ALG_HMAC_MD5, + cp, (unsigned)(eob - cp), NULL, NULL); + if (n < 0) + return ISC_R_NOSPACE; + cp += n; + + /* Time. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(0, cp); + timesigned = time(NULL); + PUTLONG(timesigned, cp); + PUTSHORT(NS_TSIG_FUDGE, cp); + + /* + * Compute the signature. + */ + + /* Digest the time signed and fudge. */ + cp2 = buf; + PUTSHORT(0, cp2); /* Top 16 bits of time */ + PUTLONG(timesigned, cp2); + PUTSHORT(NS_TSIG_FUDGE, cp2); + + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + buf, (unsigned)(cp2 - buf), NULL, 0); + + n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, + state->sig, sizeof(state->sig)); + if (n < 0) + return ISC_R_BADKEY; + state->siglen = n; + + /* Add the signature. */ + BOUNDS_CHECK(cp, INT16SZ + state->siglen); + PUTSHORT(state->siglen, cp); + memcpy(cp, state->sig, state->siglen); + cp += state->siglen; + + /* The original message ID & error. */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ); + PUTSHORT(ntohs(hp->id), cp); /* already in network order */ + PUTSHORT(error, cp); + + /* Other data. */ + BOUNDS_CHECK(cp, INT16SZ); + PUTSHORT(0, cp); + + /* Go back and fill in the length. */ + PUTSHORT(cp - lenp - INT16SZ, lenp); + + hp->arcount = htons(ntohs(hp->arcount) + 1); + *msglen = (cp - msg); + return ISC_R_SUCCESS; +} +#endif diff --git a/contrib/dhcp-3.0/minires/ns_verify.c b/contrib/dhcp-3.0/minires/ns_verify.c new file mode 100644 index 0000000000..0691db6f40 --- /dev/null +++ b/contrib/dhcp-3.0/minires/ns_verify.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_verify.c,v 1.5.2.3 2004/06/10 17:59:42 dhankins Exp $"; +#endif + +#define time(x) trace_mr_time (x) + +/* Import. */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" +#include + +time_t trace_mr_time (time_t *); + +/* Private. */ + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + return (NS_TSIG_ERROR_FORMERR); \ + } \ + } while (0) + +/* Public. */ + +u_char * +ns_find_tsig(u_char *msg, u_char *eom) { + HEADER *hp = (HEADER *)msg; + int n, type; + u_char *cp = msg, *start; + isc_result_t status; + + if (msg == NULL || eom == NULL || msg > eom) + return (NULL); + + if (cp + HFIXEDSZ >= eom) + return (NULL); + + if (hp->arcount == 0) + return (NULL); + + cp += HFIXEDSZ; + + status = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount), &n); + if (status != ISC_R_SUCCESS) + return (NULL); + cp += n; + + status = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount), &n); + if (status != ISC_R_SUCCESS) + return (NULL); + cp += n; + + status = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount), &n); + if (status != ISC_R_SUCCESS) + return (NULL); + cp += n; + + status = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1, &n); + if (status != ISC_R_SUCCESS) + return (NULL); + cp += n; + + start = cp; + n = dn_skipname(cp, eom); + if (n < 0) + return (NULL); + cp += n; + if (cp + INT16SZ >= eom) + return (NULL); + + GETSHORT(type, cp); + if (type != ns_t_tsig) + return (NULL); + return (start); +} + +/* ns_verify + * Parameters: + * statp res stuff + * msg received message + * msglen length of message + * key tsig key used for verifying. + * querysig (response), the signature in the query + * querysiglen (response), the length of the signature in the query + * sig (query), a buffer to hold the signature + * siglen (query), input - length of signature buffer + * output - length of signature + * + * Errors: + * - bad input (-1) + * - invalid dns message (NS_TSIG_ERROR_FORMERR) + * - TSIG is not present (NS_TSIG_ERROR_NO_TSIG) + * - key doesn't match (-ns_r_badkey) + * - TSIG verification fails with BADKEY (-ns_r_badkey) + * - TSIG verification fails with BADSIG (-ns_r_badsig) + * - TSIG verification fails with BADTIME (-ns_r_badtime) + * - TSIG verification succeeds, error set to BAKEY (ns_r_badkey) + * - TSIG verification succeeds, error set to BADSIG (ns_r_badsig) + * - TSIG verification succeeds, error set to BADTIME (ns_r_badtime) + */ +isc_result_t +ns_verify(u_char *msg, unsigned *msglen, void *k, + const u_char *querysig, unsigned querysiglen, + u_char *sig, unsigned *siglen, time_t *timesigned, int nostrip) +{ + HEADER *hp = (HEADER *)msg; + DST_KEY *key = (DST_KEY *)k; + u_char *cp = msg, *eom; + char name[MAXDNAME], alg[MAXDNAME]; + u_char *recstart, *rdatastart; + u_char *sigstart, *otherstart; + unsigned n; + int error; + u_int16_t type, length; + u_int16_t fudge, sigfieldlen, id, otherfieldlen; + + dst_init(); + if (msg == NULL || msglen == NULL || *msglen < 0) + return ISC_R_INVALIDARG; + + eom = msg + *msglen; + + recstart = ns_find_tsig(msg, eom); + if (recstart == NULL) + return ISC_R_NO_TSIG; + + cp = recstart; + + /* Read the key name. */ + n = dn_expand(msg, eom, cp, name, MAXDNAME); + if (n < 0) + return ISC_R_FORMERR; + cp += n; + + /* Read the type. */ + BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); + GETSHORT(type, cp); + if (type != ns_t_tsig) + return ISC_R_NO_TSIG; + + /* Skip the class and TTL, save the length. */ + cp += INT16SZ + INT32SZ; + GETSHORT(length, cp); + if (eom - cp != length) + return ISC_R_FORMERR; + + /* Read the algorithm name. */ + rdatastart = cp; + n = dn_expand(msg, eom, cp, alg, MAXDNAME); + if (n < 0) + return ISC_R_FORMERR; + if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) + return ISC_R_INVALIDKEY; + cp += n; + + /* Read the time signed and fudge. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + cp += INT16SZ; + GETLONG((*timesigned), cp); + GETSHORT(fudge, cp); + + /* Read the signature. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(sigfieldlen, cp); + BOUNDS_CHECK(cp, sigfieldlen); + sigstart = cp; + cp += sigfieldlen; + + /* Read the original id and error. */ + BOUNDS_CHECK(cp, 2*INT16SZ); + GETSHORT(id, cp); + GETSHORT(error, cp); + + /* Parse the other data. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(otherfieldlen, cp); + BOUNDS_CHECK(cp, otherfieldlen); + otherstart = cp; + cp += otherfieldlen; + + if (cp != eom) + return ISC_R_FORMERR; + + /* Verify that the key used is OK. */ + if (key != NULL) { + if (key->dk_alg != KEY_HMAC_MD5) + return ISC_R_INVALIDKEY; + if (error != ns_r_badsig && error != ns_r_badkey) { + if (ns_samename(key->dk_key_name, name) != 1) + return ISC_R_INVALIDKEY; + } + } + + hp->arcount = htons(ntohs(hp->arcount) - 1); + + /* + * Do the verification. + */ + + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + void *ctx; + u_char buf[MAXDNAME]; + + /* Digest the query signature, if this is a response. */ + dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); + if (querysiglen > 0 && querysig != NULL) { + u_int16_t len_n = htons(querysiglen); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + (u_char *)&len_n, INT16SZ, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + querysig, querysiglen, NULL, 0); + } + + /* Digest the message. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, + (unsigned)(recstart - msg), NULL, 0); + + /* Digest the key name. */ + n = ns_name_ntol(recstart, buf, sizeof(buf)); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the class and TTL. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + recstart + dn_skipname(recstart, eom) + INT16SZ, + INT16SZ + INT32SZ, NULL, 0); + + /* Digest the algorithm. */ + n = ns_name_ntol(rdatastart, buf, sizeof(buf)); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the time signed and fudge. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + rdatastart + dn_skipname(rdatastart, eom), + INT16SZ + INT32SZ + INT16SZ, NULL, 0); + + /* Digest the error and other data. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + otherstart - INT16SZ - INT16SZ, + (unsigned)otherfieldlen + INT16SZ + INT16SZ, + NULL, 0); + + n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, + sigstart, sigfieldlen); + + if (n < 0) + return ISC_R_BADSIG; + + if (sig != NULL && siglen != NULL) { + if (*siglen < sigfieldlen) + return ISC_R_NOSPACE; + memcpy(sig, sigstart, sigfieldlen); + *siglen = sigfieldlen; + } + } else { + if (sigfieldlen > 0) + return ISC_R_FORMERR; + if (sig != NULL && siglen != NULL) + *siglen = 0; + } + + /* Reset the counter, since we still need to check for badtime. */ + hp->arcount = htons(ntohs(hp->arcount) + 1); + + /* Verify the time. */ + if (abs((*timesigned) - time(NULL)) > fudge) + return ISC_R_BADTIME; + + if (nostrip == 0) { + *msglen = recstart - msg; + hp->arcount = htons(ntohs(hp->arcount) - 1); + } + + if (error != NOERROR) + return ns_rcode_to_isc (error); + + return ISC_R_SUCCESS; +} + +#if 0 +isc_result_t +ns_verify_tcp_init(void *k, const u_char *querysig, unsigned querysiglen, + ns_tcp_tsig_state *state) +{ + dst_init(); + if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) + return ISC_R_INVALIDARG; + state->counter = -1; + state->key = k; + if (state->key->dk_alg != KEY_HMAC_MD5) + return ISC_R_BADKEY; + if (querysiglen > sizeof(state->sig)) + return ISC_R_NOSPACE; + memcpy(state->sig, querysig, querysiglen); + state->siglen = querysiglen; + return ISC_R_SUCCESS; +} + +isc_result_t +ns_verify_tcp(u_char *msg, unsigned *msglen, ns_tcp_tsig_state *state, + int required) +{ + HEADER *hp = (HEADER *)msg; + u_char *recstart, *rdatastart, *sigstart; + unsigned sigfieldlen, otherfieldlen; + u_char *cp, *eom = msg + *msglen, *cp2; + char name[MAXDNAME], alg[MAXDNAME]; + u_char buf[MAXDNAME]; + int n, type, length, fudge, id, error; + time_t timesigned; + + if (msg == NULL || msglen == NULL || state == NULL) + return ISC_R_INVALIDARG; + + state->counter++; + if (state->counter == 0) + return (ns_verify(msg, msglen, state->key, + state->sig, state->siglen, + state->sig, &state->siglen, ×igned, 0)); + + if (state->siglen > 0) { + u_int16_t siglen_n = htons(state->siglen); + + dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx, + NULL, 0, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + (u_char *)&siglen_n, INT16SZ, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + state->sig, state->siglen, NULL, 0); + state->siglen = 0; + } + + cp = recstart = ns_find_tsig(msg, eom); + + if (recstart == NULL) { + if (required) + return ISC_R_NO_TSIG; + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + msg, *msglen, NULL, 0); + return ISC_R_SUCCESS; + } + + hp->arcount = htons(ntohs(hp->arcount) - 1); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + msg, (unsigned)(recstart - msg), NULL, 0); + + /* Read the key name. */ + n = dn_expand(msg, eom, cp, name, MAXDNAME); + if (n < 0) + return ISC_R_FORMERR; + cp += n; + + /* Read the type. */ + BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); + GETSHORT(type, cp); + if (type != ns_t_tsig) + return ISC_R_NO_TSIG; + + /* Skip the class and TTL, save the length. */ + cp += INT16SZ + INT32SZ; + GETSHORT(length, cp); + if (eom - cp != length) + return ISC_R_FORMERR; + + /* Read the algorithm name. */ + rdatastart = cp; + n = dn_expand(msg, eom, cp, alg, MAXDNAME); + if (n < 0) + return ISC_R_FORMERR; + if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) + return ISC_R_BADKEY; + cp += n; + + /* Verify that the key used is OK. */ + if ((ns_samename(state->key->dk_key_name, name) != 1 || + state->key->dk_alg != KEY_HMAC_MD5)) + return ISC_R_BADKEY; + + /* Read the time signed and fudge. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + cp += INT16SZ; + GETLONG(timesigned, cp); + GETSHORT(fudge, cp); + + /* Read the signature. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(sigfieldlen, cp); + BOUNDS_CHECK(cp, sigfieldlen); + sigstart = cp; + cp += sigfieldlen; + + /* Read the original id and error. */ + BOUNDS_CHECK(cp, 2*INT16SZ); + GETSHORT(id, cp); + GETSHORT(error, cp); + + /* Parse the other data. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(otherfieldlen, cp); + BOUNDS_CHECK(cp, otherfieldlen); + cp += otherfieldlen; + + if (cp != eom) + return ISC_R_FORMERR; + + /* + * Do the verification. + */ + + /* Digest the time signed and fudge. */ + cp2 = buf; + PUTSHORT(0, cp2); /* Top 16 bits of time. */ + PUTLONG(timesigned, cp2); + PUTSHORT(NS_TSIG_FUDGE, cp2); + + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + buf, (unsigned)(cp2 - buf), NULL, 0); + + n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, + sigstart, sigfieldlen); + if (n < 0) + return ISC_R_BADSIG; + + if (sigfieldlen > sizeof(state->sig)) + return ISC_R_BADSIG; + + if (sigfieldlen > sizeof(state->sig)) + return ISC_R_NOSPACE; + + memcpy(state->sig, sigstart, sigfieldlen); + state->siglen = sigfieldlen; + + /* Verify the time. */ + if (abs(timesigned - time(NULL)) > fudge) + return ISC_R_BADTIME; + + *msglen = recstart - msg; + + if (error != NOERROR) + return ns_rcode_to_isc (error); + + return ISC_R_SUCCESS; +} +#endif diff --git a/contrib/dhcp-3.0/minires/res_comp.c b/contrib/dhcp-3.0/minires/res_comp.c new file mode 100644 index 0000000000..7b7d1879c2 --- /dev/null +++ b/contrib/dhcp-3.0/minires/res_comp.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 1985, 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) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * + * http://www.isc.org/ + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_comp.c,v 1.2.2.1 2004/06/10 17:59:42 dhankins Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "minires/minires.h" +#include "arpa/nameser.h" + +/* + * Expand compressed domain name 'comp_dn' to full domain name. + * 'msg' is a pointer to the begining of the message, + * 'eomorig' points to the first location after the message, + * 'exp_dn' is a pointer to a buffer of size 'length' for the result. + * Return size of compressed name or -1 if there was an error. + */ +int +dn_expand(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, unsigned dstsiz) +{ + int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); + + if (n > 0 && dst[0] == '.') + dst[0] = '\0'; + return (n); +} + +/* + * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. + * Return the size of the compressed name or -1. + * 'length' is the size of the array pointed to by 'comp_dn'. + */ +int +dn_comp(const char *src, u_char *dst, unsigned dstsiz, + u_char **dnptrs, u_char **lastdnptr) +{ + return (ns_name_compress(src, dst, (size_t)dstsiz, + (const u_char **)dnptrs, + (const u_char **)lastdnptr)); +} + +/* + * Skip over a compressed domain name. Return the size or -1. + */ +int +dn_skipname(const u_char *ptr, const u_char *eom) { + const u_char *saveptr = ptr; + + if (ns_name_skip(&ptr, eom) == -1) + return (-1); + return (ptr - saveptr); +} + +/* + * Verify that a domain name uses an acceptable character set. + */ + +#if 0 +/* + * Note the conspicuous absence of ctype macros in these definitions. On + * non-ASCII hosts, we can't depend on string literals or ctype macros to + * tell us anything about network-format data. The rest of the BIND system + * is not careful about this, but for some reason, we're doing it right here. + */ +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ + || ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +int +res_hnok(const char *dn) { + int ppch = '\0', pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + (void)NULL; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + ppch = pch, pch = ch, ch = nch; + } + return (1); +} + +/* + * hostname-like (A, MX, WKS) owners can have "*" as their first label + * but must otherwise be as a host name. + */ +int +res_ownok(const char *dn) { + if (asterchar(dn[0])) { + if (periodchar(dn[1])) + return (res_hnok(dn+2)); + if (dn[1] == '\0') + return (1); + } + return (res_hnok(dn)); +} + +/* + * SOA RNAMEs and RP RNAMEs can have any printable character in their first + * label, but the rest of the name has to look like a host name. + */ +int +res_mailok(const char *dn) { + int ch, escaped = 0; + + /* "." is a valid missing representation */ + if (*dn == '\0') + return (1); + + /* otherwise