From f2c49875c1ce6e1d3c51968f7b058b1b0c55428c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:43:02 +0200 Subject: Adding upstream version 20080615. Signed-off-by: Daniel Baumann --- CHANGES | 299 +++ COPYRIGHT | 28 + Makefile.in | 134 + README | 43 + addrconf.c | 399 +++ addrconf.h | 35 + auth.c | 497 ++++ auth.h | 60 + base64.c | 172 ++ base64.h | 32 + cfparse.c | 3156 +++++++++++++++++++++++ cfparse.y | 1340 ++++++++++ cftoken.c | 2583 +++++++++++++++++++ cftoken.l | 448 ++++ common.c | 3388 ++++++++++++++++++++++++ common.h | 189 ++ config.c | 2233 ++++++++++++++++ config.h | 331 +++ configure | 6941 ++++++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 239 ++ control.h | 91 + dhcp6.h | 385 +++ dhcp6_ctl.c | 368 +++ dhcp6_ctl.h | 45 + dhcp6_ctlclient.c | 702 +++++ dhcp6c.8 | 208 ++ dhcp6c.c | 2148 ++++++++++++++++ dhcp6c.conf.5 | 670 +++++ dhcp6c.conf.sample | 24 + dhcp6c.h | 37 + dhcp6c_ia.c | 772 ++++++ dhcp6c_ia.h | 56 + dhcp6c_script.c | 437 ++++ dhcp6ctl.8 | 200 ++ dhcp6relay.8 | 127 + dhcp6relay.c | 1059 ++++++++ dhcp6relay_script.c | 304 +++ dhcp6s.8 | 168 ++ dhcp6s.c | 3681 ++++++++++++++++++++++++++ dhcp6s.conf.5 | 389 +++ dhcp6s.conf.sample | 25 + if.c | 184 ++ install-sh | 250 ++ lease.c | 328 +++ lease.h | 40 + missing/arc4random.c | 67 + missing/arc4random.h | 37 + missing/daemon.c | 50 + missing/err.h | 34 + missing/getifaddrs.c | 217 ++ missing/ifaddrs.h | 44 + missing/strlcat.c | 75 + missing/strlcpy.c | 72 + missing/sys/queue.h | 137 + missing/warnx.c | 59 + prefixconf.c | 517 ++++ prefixconf.h | 41 + timer.c | 212 ++ timer.h | 62 + y.tab.h | 193 ++ 60 files changed, 37062 insertions(+) create mode 100644 CHANGES create mode 100644 COPYRIGHT create mode 100644 Makefile.in create mode 100644 README create mode 100644 addrconf.c create mode 100644 addrconf.h create mode 100644 auth.c create mode 100644 auth.h create mode 100644 base64.c create mode 100644 base64.h create mode 100644 cfparse.c create mode 100644 cfparse.y create mode 100644 cftoken.c create mode 100644 cftoken.l create mode 100644 common.c create mode 100644 common.h create mode 100644 config.c create mode 100644 config.h create mode 100755 configure create mode 100644 configure.in create mode 100644 control.h create mode 100644 dhcp6.h create mode 100644 dhcp6_ctl.c create mode 100644 dhcp6_ctl.h create mode 100644 dhcp6_ctlclient.c create mode 100644 dhcp6c.8 create mode 100644 dhcp6c.c create mode 100644 dhcp6c.conf.5 create mode 100644 dhcp6c.conf.sample create mode 100644 dhcp6c.h create mode 100644 dhcp6c_ia.c create mode 100644 dhcp6c_ia.h create mode 100644 dhcp6c_script.c create mode 100644 dhcp6ctl.8 create mode 100644 dhcp6relay.8 create mode 100644 dhcp6relay.c create mode 100644 dhcp6relay_script.c create mode 100644 dhcp6s.8 create mode 100644 dhcp6s.c create mode 100644 dhcp6s.conf.5 create mode 100644 dhcp6s.conf.sample create mode 100644 if.c create mode 100755 install-sh create mode 100644 lease.c create mode 100644 lease.h create mode 100644 missing/arc4random.c create mode 100644 missing/arc4random.h create mode 100644 missing/daemon.c create mode 100644 missing/err.h create mode 100644 missing/getifaddrs.c create mode 100644 missing/ifaddrs.h create mode 100644 missing/strlcat.c create mode 100644 missing/strlcpy.c create mode 100644 missing/sys/queue.h create mode 100644 missing/warnx.c create mode 100644 prefixconf.c create mode 100644 prefixconf.h create mode 100644 timer.c create mode 100644 timer.h create mode 100644 y.tab.h diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..a1f62d8 --- /dev/null +++ b/CHANGES @@ -0,0 +1,299 @@ + --- 20080615 tar ball released --- +2008-05-01 SUZUKI, Shinsuke + * common.c: fixed a memory leak (Bug-ID: 1847587) + * dhcp6s.c: fixed a potential NULL pointer access (Bug-ID: 1848304). + used a val_statefuladdr, instead of val_prefix, to access + an address in IA in DHCP DECLINE handling. + * config.c: fixed a possible memory leak. (Bug-ID 1847588) + * dhcp6s.c: fixed a lifetime calculation failure in RENEW/REBIND + process for stateful-address. (Bug-ID: 1850457) + * common.c: fixed a improper handling of a domain-name ending with '.'. + (Bug-ID: 1852869) + +2007-12-06 SUZUKI, Shinsuke + * common.c: fixed a possible memory leak. (Bug-ID 1844676) + * common.c: fixed a possible double free. (Bug-ID 1844683) + * dhcp6s.c: fixed a bugthat dhcp6s cannot accept a relayed + request message with authentication option. (Bug-ID 1814287) + +2007-07-25 SUZUKI, Shinsuke + * config.c, lease.h, common.c: removed compilation warnings + +2007-07-24 SUZUKI, Shinsuke + * dhcp6s.c, dhcp6c.c, dhcp6relay.c, configure*: use sig_atomic_t for + a signal-handler variable (Bug-ID 1678874) + * common.c: fixed a buffer overrun in sprint_uint64() + (Bug-ID 1714648) + +2007-07-10 SUZUKI, Shinsuke + * common.c: fixed a singular-point detection failure in replay check. + (Bug-ID 1714644) + +2007-06-28 SUZUKI, Shinsuke + * dhcp6s.8: fixed a typo in a manual (program name "dhcp6sctl" should + be "dhcp6ctl") + + --- 20070507 tar ball released --- +2007-05-03 SUZUKI, Shinsuke + * dhcp6relay.c: changes the destination port of a relayed DHCP packet + from 546 to 547, when it is supposed to be bound for a relay, + instead of a client. (the situation occurs when DHCPv6-relays are + cascased in a row) + * dhcp6s.c: fixed a Solicitation message validation error (introduced + on Mar 21 2007) + +2007-03-23 SUZUKI, Shinsuke + * dhcp6relay.c,dhcp6.c: in signal-receiving procedure, not uses + a function that is not always designed to be signal-safe + (Bug-ID 1678874) + +2007-03-21 SUZUKI, Shinsuke + fixed several bugs found in IPv6-Ready-Logo DHCPv6 Self-Test script. + * dhcp6s.c, config.h, if.c: implemented DHCPv6 Confirm message processing + on DHCPv6 server. + * dhcp6s.c, lease.c: implemented DHCPv6 Decline message processing on DHCPv6 server. + * dhcp6s.c: fixed a Solicitation message validation as stated in RFC3315 15.2. + * dhcp6s.c: fixed a bug that Rebind does not work for IA-NA. + * config.c: supported an address pool that has only one entry. + +2007-03-20 SUZUKI, Shinsuke + * common.c: fixed a bug that IA-PD/IA-NA cannot coexist in one DHCP message + when they have the same IA-ID. + +2007-02-28 SUZUKI, Shinsuke + * config.c: fixed a typo. + +2007-02-27 SUZUKI, Shinsuke + * dhcp6relay.c, dhcp6relay_script.c, common.[ch], dhcp6c_script.c, + Makefile.in, dhcp6relay.8: + supported script execution for dhcp6relay (contributed by Bruno STEVANT) + + * Makefile.in, configure.in, configure, auth.h, common.c, control.h, dhcp6.h, + dhcp6s.c, missing/{arc4random.h, daemon.c, err.h, getifaddrs.c, ifaddrs.h, + sys/queue.h, warnx.c} + supported compilation on Solaris (contributed by James Carlson) + +2007-02-12 SUZUKI, Shinsuke + * cftoken.l: accepts interface names, such as eth0.100(Linux VLAN interface) + or foobar (named by "ifconfig name" command). (Bug-ID 1644637) + * configure.in, Makefile.in: allows a user to specify the owner/group for the + installed files. (suggested by Tsuyoshi MOMOSE and Tomofumi Hayashi) + +2007-01-11 SUZUKI, Shinsuke + * dhcp6s.conf.5: fixed an incorrect descrption regarding NTP option. +2006-12-04 SUZUKI, Shinsuke + * dhcp6c.8: clarified the behavior of the Info-req mode. + * addrconf.c, common.[ch], configure, configure.in, dhcp6relay.c, + prefixconf.c: introduced a HAVE_SA_LEN macro to support OSes + (other than linux) without length field in sockaddr_* + +2006-11-15 SUZUKI, Shinsuke + * dhcp6s.c: fixed an IA allocation failure from pool when it doubly + requested by the same client. (patch submitted by Hajimu UMEMOTO) + * Makefile.in: creates man/man[58] in "make install" + (suggested by Masahide NAKAMURA) + +2006-11-05 SUZUKI, Shinsuke + * dhcp6s.c: dhcp6s recognizes "-P" option. + (patch submitted by Masahide NAKAMURA) + + * dhcp6relay.c: accepts ULA (Bug-ID 1584704) + + * Makefile.in: fixed a compilation error on SuSE and Debian Linux. + (patch submitted by Masahide NAKAMURA) + + --- 20061016 tar ball released --- +2006-10-13 SUZUKI, Shinsuke + * Makefile.in: creates the target directory in "make install" + (suggested by Masahide NAKAMURA) + +2006-10-08 SUZUKI, Shinsuke + * dhcp6s.conf.sample: corrected a misleading comment (pointed out by + Hajimu UMEMOTO) + +2006-10-04 SUZUKI, Shinsuke + * config.c: fixed a improper memory access when there is no free + address in an address pool. (Bug-ID 1563232) + + fixed a bug that a pooled address will never be reused even when + it is released. + +2006-10-03 SUZUKI, Shinsuke + * config.c: dhcp6s does not include a 0 refresh-time option in + ADVERTISE/REPLY message, when "option refreshtime" configuration does + not exist in its configuration. (patch submitted by Jun-ya Kato) + + * cftoken.l, cfparse.y: fixed a compilation failure by flex 2.5.4 + (patch submitted by Jun-ya Kato) + +2006-10-01 SUZUKI, Shinsuke + * cftoken.l, dhcp6[cs].conf.5: accepted "nis-domain-name" and "nisp- + domain-name" as a configuration keyword for NIS/NIS+ domain name, + as well as nis/nisp-server-domain-name. (Bug-ID 1566494) + + * cftoken.l: supported a declaration name (hostname, poolname, + keyname, authname) including "-". (Bug-ID 1566476) + +2006-09-20 SUZUKI, Shinsuke + * common.c: fixed a bug that dhcp6s inserts SIP server addresses into + DNS server address option (Bug-ID 1561202). (degrade from 2006-07-30) + * common.c: fixed a bug that status-code option might include a unexpected + garbage value. (Bug-ID 1561202) + * dhcp6s.conf.5, dhcp6s.conf.sample: you need to provide a preferred-lifetime + for each address-prefix. (Bug-ID 1558811) + +2006-09-20 STEVANT, Bruno + * lease.c: Fix a memory violation in lease management. + + --- 20060902 tar ball released --- +2006-09-02 SUZUKI, Shinsuke + * added a configure check routine to automatically detect a + difference in TAILQ_FOREACH_REVERSE macros (Suggested by Brute + STEVANT) + * fixed a bug that dhcp6c cannot advertise an Option-Request-Option. + * fixed a compilation failure in freebsd4 + +2006-07-30 SUZUKI, Shinsuke + * supported the following options: + NIS server option, NIS domain option (RFC3898) + NIS+ server option, NIS+ domain option (RFC3898) + BCMCS server option, BCMCS domain option (RFC4280) + * changed the name of the enviromental variable for SIP server + address (new_sip_servers). + * removed a configure option to control the NTP option number. + (since one year has been passed since its official assignment) + +2006-07-29 SUZUKI, Shinsuke + * dhcp6s and dhcp6relay dumps process-id file, like dhcp6c + +2006-05-05 SUZUKI, Shinsuke + * fixed a compilation error on netbsd + * updated the list of the supported RFCs + * NTP option number has been already assigned by IANA + * added other official option numbers for future implementors + +2006-04-26 SUZUKI, Shinsuke + * supported 'include' statement + (contributed by Bruno STEVANT) + +2006-04-11 SUZUKI, Shinsuke + * removed 'ifdef USE_POOL' macros, since it is always ON + * rename some pool-related variables to prepare for the support of prefix-pool + + --- 20060322 tar ball released --- +2006-03-20 SUZUKI, Shinsuke + * fixed a bug that dhcp6c cannot receive a DHCPv6 packet on linux. + (Request ID=1435976, and a patch proposed by Yasuyuki KOZAKAI) + +2006-03-08 SUZUKI, Shinsuke + * added some missing queue-management macros to make it compilable on linux + + --- 20060214 tar ball released --- +2006-01-26 SUZUKI, Shinsuke + * supported IA-NA address-pool (contributed by: WIDE Project Secure6-WG) + ToDo: IA-PD prefix-pool, write a manual + +2006-01-19 SUZUKI, Shinsuke + * corrected the condition for detecting unwanted incoming messages + * missing initialization in dhcp6c + --- 20060114 tar ball released --- + --- 20051211 tar ball released --- +2005-12-11 SUZUKI, Shinsuke + * locate every files in /usr/local/xxx, instead of /usr/local/v6/xxx + * Assign an official option number for Information Refresh Timer + * Removed old prefix delegation options, since these options numbers + coinsides with Information Refresh Timer. + --- 20050723a tar ball released --- + +2005-07-22 JINMEI, Tatuya + * kame/kame/dhcp6: fixed memory leak for renew/rebind event data. + (KAME PR 872 reported by Meng Huan Hsieh) + +2005-05-03 JINMEI, Tatuya + * kame/kame/dhcp6/dhcp6c_ia.c (ia_timo): kept the latest server's + DUID even in the REBIND state in case of sending a Release message + in that state. + +2005-05-03 JINMEI, Tatuya + * kame/kame/dhcp6/cfparse.y: corrected "address parameters" so + that we don't have to specify the meaningless (but mandatory) + prefix length in the context of IA_NA. + +2005-05-03 JINMEI, Tatuya + * kame/kame/dhcp6/config.c (configure_commit): made sure that + interface parameters are initialized with the default values even + if the interface is not explicitly configured in the configuration + file. This particularly made sure that the preference option is + not included unless explicitly specified. A memory leakage for + the client when specifying a script file was also fixed. + +2005-04-29 JINMEI, Tatuya + * kame/kame/dhcp6/dhcp6c.c (client6_mainloop): corrected error + handling for select(2) in order to avoid unexpected blocking when + receiving a signal. + +2005-04-27 JINMEI, Tatuya + * kame/sys/netinet6/in6.c (in6_update_ifa): always updated + ia6_updatetime regardless of whether the address is created or + updated. This is necessary so that the expiration times will be + updated correctly when the lifetiems of an existing address are + modified by hand or by a process such as a DHCPv6 client. + +2005-04-26 JINMEI, Tatuya + * kame/kame/dhcp6/addrconf.c (na_ifaddrconf): corrected lifetime + arguments to ifaddrconf(). + +2005-03-30 JINMEI, Tatuya + * kame/kame/dhcp6/addrconf.c (update_address): make sure that + the lifetimes of addresses are updated when the client receives a + Reply in response to Renew or Rebind. + +2005-03-02 SUZUKI, Shinsuke + * kame/kame/dhcp6/dhcp6.c: fixed a DHCPv6-client + initialization failure when a node has no IPv6 global address. + +2005-03-02 SUZUKI, Shinsuke + * kame/kame/dhcp6/dhcp6relay.[c8]: supports multiple client-side + interfaces + +2005-01-12 suz@crl.hitachi.co.jp + * kame/kame/dhcp6: implemented stateful non-temporary address + assignment. + +2004-12-03 JINMEI, Tatuya + * kame/kame/dhcp6/common.c: silenced strict-aliasing warnings. + Otherwise dhcp6 could not be built with -O2 on FreeBSD 5.3. + (KAME-PR 851) + +2004-12-02 JINMEI, Tatuya + * kame/kame/dhcp6/config.c (configure_commit): corrected list + management operation. (mostly harmless, but it was incorrect.) + From: Nicolas Dichtel + +2004-11-28 JINMEI, Tatuya + * kame/kame/dhcp6: catch up draft-ietf-dhc-lifetime-02. + Specifically, + - renamed "lifetime" to "refreshtime". Note that configuration + compatibility was lost + - implemented the default and minimum timer values + - clarified that the refresh time option can only be available for + Information-request and reply exchanges + +2004-09-07 JINMEI, Tatuya + * kame/kame/dhcp6: provided a new command "dhcp6ctl", deprecating + dhcp6sctl. This new command controls the client as well as the + server. For the client, the "start" and "stop" (which can specify + a particular interface) commands are available. + +2004-09-04 JINMEI, Tatuya + * kame/kame/dhcp6: added the ability of dhcp6c to work on multiple + interfaces simultaneously. + + --- 20040903a tar ball released --- + + --- 20040816a tar ball released --- + +2004-08-01 SUZUKI, Shinsuke + * kame/freebsd5/usr.sbin/dhcp6: builds DHCPv6 in FreeBSD5 + + --- 20040731a tar ball released --- diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..167160e --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,28 @@ +$KAME: COPYRIGHT,v 1.2 2004/07/29 19:02:18 jinmei Exp $ + +Copyright (C) 1998-2004 WIDE Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..a39596f --- /dev/null +++ b/Makefile.in @@ -0,0 +1,134 @@ +# Copyright (c) 2004 WIDE Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# +# $Id: Makefile.in,v 1.16 2007/02/27 14:47:11 suzsuz Exp $ +# $KAME: Makefile.in,v 1.45 2005/10/16 16:25:38 suz Exp $ +# + +prefix= @prefix@ +srcdir= @srcdir@ +sysconfdir= @sysconfdir@ +localdbdir= @localdbdir@ +user= @user@ +group= @group@ + +CFLAGS= @CFLAGS@ @DEFS@ -DSYSCONFDIR=\"${sysconfdir}\" \ + -DLOCALDBDIR=\"${localdbdir}\" +LDFLAGS=@LDFLAGS@ +LIBOBJS=@LIBOBJS@ +LIBS= @LIBS@ @LEXLIB@ +CC= @CC@ +TARGET= dhcp6c dhcp6s dhcp6relay dhcp6ctl + +INSTALL=@INSTALL@ +INSTALL_PROGRAM=@INSTALL_PROGRAM@ +INSTALL_DATA=@INSTALL_DATA@ +prefix= @prefix@ +exec_prefix= @exec_prefix@ +bindir= @bindir@ +sbindir=@sbindir@ +mandir= @mandir@ + +GENSRCS=cfparse.c cftoken.c +CLIENTOBJS= dhcp6c.o common.o config.o prefixconf.o dhcp6c_ia.o timer.o \ + dhcp6c_script.o if.o base64.o auth.o dhcp6_ctl.o addrconf.o lease.o \ + $(GENSRCS:%.c=%.o) +SERVOBJS= dhcp6s.o common.o if.o config.o timer.o lease.o \ + base64.o auth.o dhcp6_ctl.o $(GENSRCS:%.c=%.o) +RELAYOBJS = dhcp6relay.o dhcp6relay_script.o common.o timer.o +CTLOBJS= dhcp6_ctlclient.o base64.o auth.o +CLEANFILES+= y.tab.h + +all: $(TARGET) +dhcp6c: $(CLIENTOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o dhcp6c $(CLIENTOBJS) $(LIBOBJS) $(LIBS) +dhcp6s: $(SERVOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o dhcp6s $(SERVOBJS) $(LIBOBJS) $(LIBS) +dhcp6relay: $(RELAYOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o $@ $(RELAYOBJS) $(LIBOBJS) $(LIBS) +dhcp6ctl: $(CTLOBJS) + $(CC) $(LDFLAGS) -o $@ $(CTLOBJS) $(LIBOBJS) $(LIBS) + +cfparse.c y.tab.h: cfparse.y + @YACC@ -d cfparse.y + mv y.tab.c cfparse.c + +cftoken.c: cftoken.l y.tab.h + @LEX@ cftoken.l + mv lex.yy.c $@ + +getaddrinfo.o: $(srcdir)/missing/getaddrinfo.c + $(CC) -c $(srcdir)/missing/$*.c +getnameinfo.o: $(srcdir)/missing/getnameinfo.c + $(CC) -c $(srcdir)/missing/$*.c +strlcat.o: $(srcdir)/missing/strlcat.c + $(CC) -c $(srcdir)/missing/$*.c +strlcpy.o: $(srcdir)/missing/strlcpy.c + $(CC) -c $(srcdir)/missing/$*.c +arc4random.o: $(srcdir)/missing/arc4random.c + $(CC) $(CFLAGS) -c $(srcdir)/missing/$*.c +getifaddrs.o: $(srcdir)/missing/getifaddrs.c + $(CC) -c $(srcdir)/missing/$*.c +daemon.o: $(srcdir)/missing/daemon.c + $(CC) -c $(srcdir)/missing/$*.c +warnx.o: $(srcdir)/missing/warnx.c + $(CC) -c $(srcdir)/missing/$*.c + +$(srcdir)/ianaopts.h: gentab.pl bootp-dhcp-parameters + expand bootp-dhcp-parameters | perl gentab.pl > ianaopts.h + +install:: + -mkdir -p $(sbindir) $(mandir)/man5 $(mandir)/man8 + $(INSTALL_PROGRAM) -s -o $(user) -g $(group) $(TARGET) $(sbindir) + $(INSTALL_DATA) -o $(user) -g $(group) dhcp6c.8 $(mandir)/man8 + $(INSTALL_DATA) -o $(user) -g $(group) dhcp6s.8 $(mandir)/man8 + $(INSTALL_DATA) -o $(user) -g $(group) dhcp6relay.8 $(mandir)/man8 + $(INSTALL_DATA) -o $(user) -g $(group) dhcp6ctl.8 $(mandir)/man8 + $(INSTALL_DATA) -o $(user) -g $(group) dhcp6c.conf.5 $(mandir)/man5 + $(INSTALL_DATA) -o $(user) -g $(group) dhcp6s.conf.5 $(mandir)/man5 + +includes:: + +clean:: + /bin/rm -f *.o $(TARGET) $(CLEANFILES) $(GENSRCS) + +distclean:: clean + /bin/rm -f Makefile config.cache config.log config.status .depend + +depend: + mkdep ${CFLAGS:M-[ID]*} $(srcdir)/*.c + +package: + tar -zcvf wide-dhcpv6.tar.gz $(srcdir)/*.[chyl1-8] $(srcdir)/Makefile.in \ + $(srcdir)/README $(srcdir)/COPYRIGHT $(srcdir)/CHANGES \ + $(srcdir)/configure $(srcdir)/configure.in \ + $(srcdir)/install-sh $(srcdir)/*.sample \ + $(srcdir)/missing/arc4random.? $(srcdir)/missing/strlcat.c \ + $(srcdir)/missing/strlcpy.c $(srcdir)/missing/daemon.c \ + $(srcdir)/missing/err.h $(srcdir)/missing/warnx.c \ + $(srcdir)/missing/ifaddrs.h $(srcdir)/missing/getifaddrs.c \ + $(srcdir)/missing/sys/queue.h diff --git a/README b/README new file mode 100644 index 0000000..a0d4fcf --- /dev/null +++ b/README @@ -0,0 +1,43 @@ +$KAME: README,v 1.3 2005/01/12 06:06:10 suz Exp $ + +WIDE-DHCPv6 + + WIDE-DHCPv6 is an open-source implementation of Dynamic Host + Configuration Protocol for IPv6 (DHCPv6) developed by the KAME + project. + + The implementation mainly conforms to the following standards: + + - RFC3315: Dynamic Host Configuration Protocol for IPv6 (DHCPv6) + - RFC3319: Dynamic Host Configuration Protocol (DHCPv6) Options + for Session Initiation Protocol (SIP) Servers + - RFC3633: IPv6 Prefix Options for Dynamic Host Configuration + Protocol (DHCP) + - RFC3646: DNS Configuration options for Dynamic Host + Configuration Protocol for IPv6 (DHCPv6) + - RFC4075: Simple Network Time Protocol (SNTP) Configuration + Option for DHCPv6 + - RFC4242: Information Refresh Time Option for Dynamic Host + Configuration Protocol for IPv6 (DHCPv6) + + All the components of the main protocol are provided, i.e., + DHCPv6 clients, servers, and relay agents. + + Supported message types are as follows: + Solicit, Advertise, Request, Renew, Rebind, Release, Reply, + Information-request, Relay-forward and Relay-reply. + + Note that the current implementation does not support IPv6 + temporary address allocation by DHCPv6, and there is no plan to + implement that feature at the moment. + +Bug Reports and Mailing Lists + + Bugs reports should be put to the Bug Tracking System at + sourceforge.net + http://sourceforge.net/projects/wide-dhcpv6/ + + Information on the mailing list, including how to subscribe + and browse archives, is available at the following URL: + + http://sourceforge.net/mail/?group_id=153668 diff --git a/addrconf.c b/addrconf.c new file mode 100644 index 0000000..d98373f --- /dev/null +++ b/addrconf.c @@ -0,0 +1,399 @@ +/* $KAME: addrconf.c,v 1.8 2005/09/16 11:30:13 suz Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include +#ifdef __FreeBSD__ +#include +#endif + +#include + +#ifdef __KAME__ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "timer.h" +#include "dhcp6c_ia.h" +#include "prefixconf.h" + +TAILQ_HEAD(statefuladdr_list, statefuladdr); +struct iactl_na { + struct iactl common; + struct statefuladdr_list statefuladdr_head; +}; +#define iacna_ia common.iactl_ia +#define iacna_callback common.callback +#define iacna_isvalid common.isvalid +#define iacna_duration common.duration +#define iacna_renew_data common.renew_data +#define iacna_rebind_data common.rebind_data +#define iacna_reestablish_data common.reestablish_data +#define iacna_release_data common.release_data +#define iacna_cleanup common.cleanup + +struct statefuladdr { + TAILQ_ENTRY (statefuladdr) link; + + struct dhcp6_statefuladdr addr; + time_t updatetime; + struct dhcp6_timer *timer; + struct iactl_na *ctl; + struct dhcp6_if *dhcpif; +}; + +static struct statefuladdr *find_addr __P((struct statefuladdr_list *, + struct dhcp6_statefuladdr *)); +static int remove_addr __P((struct statefuladdr *)); +static int isvalid_addr __P((struct iactl *)); +static u_int32_t duration_addr __P((struct iactl *)); +static void cleanup_addr __P((struct iactl *)); +static int renew_addr __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); +static void na_renew_data_free __P((struct dhcp6_eventdata *)); + +static struct dhcp6_timer *addr_timo __P((void *)); + +static int na_ifaddrconf __P((ifaddrconf_cmd_t, struct statefuladdr *)); + +extern struct dhcp6_timer *client6_timo __P((void *)); + +int +update_address(ia, addr, dhcpifp, ctlp, callback) + struct ia *ia; + struct dhcp6_statefuladdr *addr; + struct dhcp6_if *dhcpifp; + struct iactl **ctlp; + void (*callback)__P((struct ia *)); +{ + struct iactl_na *iac_na = (struct iactl_na *)*ctlp; + struct statefuladdr *sa; + int sacreate = 0; + struct timeval timo; + + /* + * A client discards any addresses for which the preferred + * lifetime is greater than the valid lifetime. + * [RFC3315 22.6] + */ + if (addr->vltime != DHCP6_DURATION_INFINITE && + (addr->pltime == DHCP6_DURATION_INFINITE || + addr->pltime > addr->vltime)) { + dprintf(LOG_INFO, FNAME, "invalid address %s: " + "pltime (%lu) is larger than vltime (%lu)", + in6addr2str(&addr->addr, 0), + addr->pltime, addr->vltime); + return (-1); + } + + if (iac_na == NULL) { + if ((iac_na = malloc(sizeof(*iac_na))) == NULL) { + dprintf(LOG_NOTICE, FNAME, "memory allocation failed"); + return (-1); + } + memset(iac_na, 0, sizeof(*iac_na)); + iac_na->iacna_ia = ia; + iac_na->iacna_callback = callback; + iac_na->iacna_isvalid = isvalid_addr; + iac_na->iacna_duration = duration_addr; + iac_na->iacna_cleanup = cleanup_addr; + iac_na->iacna_renew_data = + iac_na->iacna_rebind_data = + iac_na->iacna_release_data = + iac_na->iacna_reestablish_data = renew_addr; + + TAILQ_INIT(&iac_na->statefuladdr_head); + *ctlp = (struct iactl *)iac_na; + } + + /* search for the given address, and make a new one if it fails */ + if ((sa = find_addr(&iac_na->statefuladdr_head, addr)) == NULL) { + if ((sa = malloc(sizeof(*sa))) == NULL) { + dprintf(LOG_NOTICE, FNAME, "memory allocation failed"); + return (-1); + } + memset(sa, 0, sizeof(*sa)); + sa->addr.addr = addr->addr; + sa->ctl = iac_na; + TAILQ_INSERT_TAIL(&iac_na->statefuladdr_head, sa, link); + sacreate = 1; + } + + /* update the timestamp of update */ + sa->updatetime = time(NULL); + + /* update the prefix according to addr */ + sa->addr.pltime = addr->pltime; + sa->addr.vltime = addr->vltime; + sa->dhcpif = dhcpifp; + dprintf(LOG_DEBUG, FNAME, "%s an address %s pltime=%lu, vltime=%lu", + sacreate ? "create" : "update", + in6addr2str(&addr->addr, 0), addr->pltime, addr->vltime); + + if (sa->addr.vltime != 0) + if (na_ifaddrconf(IFADDRCONF_ADD, sa) < 0) + return (-1); + + /* + * If the new vltime is 0, this address immediately expires. + * Otherwise, set up or update the associated timer. + */ + switch (sa->addr.vltime) { + case 0: + if (remove_addr(sa) < 0) + return (-1); + break; + case DHCP6_DURATION_INFINITE: + if (sa->timer) + dhcp6_remove_timer(&sa->timer); + break; + default: + if (sa->timer == NULL) { + sa->timer = dhcp6_add_timer(addr_timo, sa); + if (sa->timer == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to add stateful addr timer"); + remove_addr(sa); /* XXX */ + return (-1); + } + } + /* update the timer */ + timo.tv_sec = sa->addr.vltime; + timo.tv_usec = 0; + + dhcp6_set_timer(&timo, sa->timer); + break; + } + + return (0); +} + +static struct statefuladdr * +find_addr(head, addr) + struct statefuladdr_list *head; + struct dhcp6_statefuladdr *addr; +{ + struct statefuladdr *sa; + + for (sa = TAILQ_FIRST(head); sa; sa = TAILQ_NEXT(sa, link)) { + if (!IN6_ARE_ADDR_EQUAL(&sa->addr.addr, &addr->addr)) + continue; + return (sa); + } + + return (NULL); +} + +static int +remove_addr(sa) + struct statefuladdr *sa; +{ + int ret; + + dprintf(LOG_DEBUG, FNAME, "remove an address %s", + in6addr2str(&sa->addr.addr, 0)); + + if (sa->timer) + dhcp6_remove_timer(&sa->timer); + + TAILQ_REMOVE(&sa->ctl->statefuladdr_head, sa, link); + ret = na_ifaddrconf(IFADDRCONF_REMOVE, sa); + free(sa); + + return (ret); +} + +static int +isvalid_addr(iac) + struct iactl *iac; +{ + struct iactl_na *iac_na = (struct iactl_na *)iac; + + if (TAILQ_EMPTY(&iac_na->statefuladdr_head)) + return (0); /* this IA is invalid */ + return (1); +} + +static u_int32_t +duration_addr(iac) + struct iactl *iac; +{ + struct iactl_na *iac_na = (struct iactl_na *)iac; + struct statefuladdr *sa; + u_int32_t base = DHCP6_DURATION_INFINITE, pltime, passed; + time_t now; + + /* Determine the smallest period until pltime expires. */ + now = time(NULL); + for (sa = TAILQ_FIRST(&iac_na->statefuladdr_head); sa; + sa = TAILQ_NEXT(sa, link)) { + passed = now > sa->updatetime ? + (u_int32_t)(now - sa->updatetime) : 0; + pltime = sa->addr.pltime > passed ? + sa->addr.pltime - passed : 0; + + if (base == DHCP6_DURATION_INFINITE || pltime < base) + base = pltime; + } + + return (base); +} + +static void +cleanup_addr(iac) + struct iactl *iac; +{ + struct iactl_na *iac_na = (struct iactl_na *)iac; + struct statefuladdr *sa; + + while ((sa = TAILQ_FIRST(&iac_na->statefuladdr_head)) != NULL) { + TAILQ_REMOVE(&iac_na->statefuladdr_head, sa, link); + remove_addr(sa); + } + + free(iac); +} + +static int +renew_addr(iac, iaparam, evdp, evd) + struct iactl *iac; + struct dhcp6_ia *iaparam; + struct dhcp6_eventdata **evdp, *evd; +{ + struct iactl_na *iac_na = (struct iactl_na *)iac; + struct statefuladdr *sa; + struct dhcp6_list *ial = NULL, pl; + + TAILQ_INIT(&pl); + for (sa = TAILQ_FIRST(&iac_na->statefuladdr_head); sa; + sa = TAILQ_NEXT(sa, link)) { + if (dhcp6_add_listval(&pl, DHCP6_LISTVAL_STATEFULADDR6, + &sa->addr, NULL) == NULL) + goto fail; + } + + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + TAILQ_INIT(ial); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IANA, iaparam, &pl) == NULL) + goto fail; + dhcp6_clear_list(&pl); + + evd->type = DHCP6_EVDATA_IANA; + evd->data = (void *)ial; + evd->privdata = (void *)evdp; + evd->destructor = na_renew_data_free; + + return (0); + + fail: + dhcp6_clear_list(&pl); + if (ial) + free(ial); + return (-1); +} + +static void +na_renew_data_free(evd) + struct dhcp6_eventdata *evd; +{ + struct dhcp6_list *ial; + + if (evd->type != DHCP6_EVDATA_IANA) { + dprintf(LOG_ERR, FNAME, "assumption failure"); + exit(1); + } + + if (evd->privdata) + *(struct dhcp6_eventdata **)evd->privdata = NULL; + ial = (struct dhcp6_list *)evd->data; + dhcp6_clear_list(ial); + free(ial); +} + +static struct dhcp6_timer * +addr_timo(arg) + void *arg; +{ + struct statefuladdr *sa = (struct statefuladdr *)arg; + struct ia *ia; + void (*callback)__P((struct ia *)); + + dprintf(LOG_DEBUG, FNAME, "address timeout for %s", + in6addr2str(&sa->addr.addr, 0)); + + ia = sa->ctl->iacna_ia; + callback = sa->ctl->iacna_callback; + + if (sa->timer) + dhcp6_remove_timer(&sa->timer); + + remove_addr(sa); + + (*callback)(ia); + + return (NULL); +} + +static int +na_ifaddrconf(cmd, sa) + ifaddrconf_cmd_t cmd; + struct statefuladdr *sa; +{ + struct dhcp6_statefuladdr *addr; + struct sockaddr_in6 sin6; + + addr = &sa->addr; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + sin6.sin6_len = sizeof(sin6); +#endif + sin6.sin6_addr = addr->addr; + + return (ifaddrconf(cmd, sa->dhcpif->ifname, &sin6, 128, + addr->pltime, addr->vltime)); +} diff --git a/addrconf.h b/addrconf.h new file mode 100644 index 0000000..a5d1f66 --- /dev/null +++ b/addrconf.h @@ -0,0 +1,35 @@ +/* $KAME: addrconf.h,v 1.1 2005/03/02 07:20:13 suz Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +typedef enum { ADDR6S_ACTIVE, ADDR6S_RENEW, ADDR6S_REBIND} addr6state_t; + +extern int update_address __P((struct ia *, struct dhcp6_statefuladdr *, + struct dhcp6_if *, struct iactl **, void (*)__P((struct ia *)))); diff --git a/auth.c b/auth.c new file mode 100644 index 0000000..6b9ff9b --- /dev/null +++ b/auth.c @@ -0,0 +1,497 @@ +/* $KAME: auth.c,v 1.4 2004/09/07 05:03:02 jinmei Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000, 2001 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. + */ +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#define PADLEN 64 +#define IPAD 0x36 +#define OPAD 0x5C + +#define HMACMD5_KEYLENGTH 64 + +typedef struct { + u_int32_t buf[4]; + u_int32_t bytes[2]; + u_int32_t in[16]; +} md5_t; + +typedef struct { + md5_t md5ctx; + unsigned char key[HMACMD5_KEYLENGTH]; +} hmacmd5_t; + +static void hmacmd5_init __P((hmacmd5_t *, const unsigned char *, + unsigned int)); +static void hmacmd5_invalidate __P((hmacmd5_t *)); +static void hmacmd5_update __P((hmacmd5_t *, const unsigned char *, + unsigned int)); +static void hmacmd5_sign __P((hmacmd5_t *, unsigned char *)); +static int hmacmd5_verify __P((hmacmd5_t *, unsigned char *)); + +static void md5_init __P((md5_t *)); +static void md5_invalidate __P((md5_t *)); +static void md5_final __P((md5_t *, unsigned char *)); +static void md5_update __P((md5_t *, const unsigned char *, unsigned int)); + +int +dhcp6_validate_key(key) + struct keyinfo *key; +{ + time_t now; + + if (key->expire == 0) /* never expire */ + return (0); + + if (time(&now) == -1) + return (-1); /* treat it as expiration (XXX) */ + + if (now > key->expire) + return (-1); + + return (0); +} + +int +dhcp6_calc_mac(buf, len, proto, alg, off, key) + char *buf; + size_t len, off; + int proto, alg; + struct keyinfo *key; +{ + hmacmd5_t ctx; + unsigned char digest[MD5_DIGESTLENGTH]; + + /* right now, we don't care about the protocol */ + + if (alg != DHCP6_AUTHALG_HMACMD5) + return (-1); + + if (off + MD5_DIGESTLENGTH > len) { + /* + * this should be assured by the caller, but check it here + * for safety. + */ + return (-1); + } + + hmacmd5_init(&ctx, key->secret, key->secretlen); + hmacmd5_update(&ctx, buf, len); + hmacmd5_sign(&ctx, digest); + + memcpy(buf + off, digest, MD5_DIGESTLENGTH); + + return (0); +} + +int +dhcp6_verify_mac(buf, len, proto, alg, off, key) + char *buf; + ssize_t len; + int proto, alg; + size_t off; + struct keyinfo *key; +{ + hmacmd5_t ctx; + unsigned char digest[MD5_DIGESTLENGTH]; + int result; + + /* right now, we don't care about the protocol */ + + if (alg != DHCP6_AUTHALG_HMACMD5) + return (-1); + + if (off + MD5_DIGESTLENGTH > len) + return (-1); + + /* + * Copy the MAC value and clear the field. + * XXX: should we make a local working copy? + */ + memcpy(digest, buf + off, sizeof(digest)); + memset(buf + off, 0, sizeof(digest)); + + hmacmd5_init(&ctx, key->secret, key->secretlen); + hmacmd5_update(&ctx, buf, len); + result = hmacmd5_verify(&ctx, digest); + + /* copy back the digest value (XXX) */ + memcpy(buf + off, digest, sizeof(digest)); + + return (result); +} + +/* + * This code implements the HMAC-MD5 keyed hash algorithm + * described in RFC 2104. + */ +/* + * Start HMAC-MD5 process. Initialize an md5 context and digest the key. + */ +static void +hmacmd5_init(hmacmd5_t *ctx, const unsigned char *key, unsigned int len) +{ + unsigned char ipad[PADLEN]; + int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + md5_t md5ctx; + md5_init(&md5ctx); + md5_update(&md5ctx, key, len); + md5_final(&md5ctx, ctx->key); + } else + memcpy(ctx->key, key, len); + + md5_init(&ctx->md5ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < PADLEN; i++) + ipad[i] ^= ctx->key[i]; + md5_update(&ctx->md5ctx, ipad, sizeof(ipad)); +} + +static void +hmacmd5_invalidate(hmacmd5_t *ctx) +{ + md5_invalidate(&ctx->md5ctx); + memset(ctx->key, 0, sizeof(ctx->key)); + memset(ctx, 0, sizeof(ctx)); +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void +hmacmd5_update(hmacmd5_t *ctx, const unsigned char *buf, unsigned int len) +{ + md5_update(&ctx->md5ctx, buf, len); +} + +/* + * Compute signature - finalize MD5 operation and reapply MD5. + */ +static void +hmacmd5_sign(hmacmd5_t *ctx, unsigned char *digest) +{ + unsigned char opad[PADLEN]; + int i; + + md5_final(&ctx->md5ctx, digest); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < PADLEN; i++) + opad[i] ^= ctx->key[i]; + + md5_init(&ctx->md5ctx); + md5_update(&ctx->md5ctx, opad, sizeof(opad)); + md5_update(&ctx->md5ctx, digest, MD5_DIGESTLENGTH); + md5_final(&ctx->md5ctx, digest); + hmacmd5_invalidate(ctx); +} + +/* + * Verify signature - finalize MD5 operation and reapply MD5, then + * compare to the supplied digest. + */ +static int +hmacmd5_verify(hmacmd5_t *ctx, unsigned char *digest) { + unsigned char newdigest[MD5_DIGESTLENGTH]; + + hmacmd5_sign(ctx, newdigest); + return (memcmp(digest, newdigest, MD5_DIGESTLENGTH)); +} + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +static void +byteSwap(u_int32_t *buf, unsigned words) +{ + unsigned char *p = (unsigned char *)buf; + + do { + *buf++ = (u_int32_t)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void +md5_init(md5_t *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +static void +md5_invalidate(md5_t *ctx) +{ + memset(ctx, 0, sizeof(md5_t)); +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +transform(u_int32_t buf[4], u_int32_t const in[16]) { + register u_int32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void +md5_update(md5_t *ctx, const unsigned char *buf, unsigned int len) +{ + u_int32_t t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((unsigned char *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((unsigned char *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void +md5_final(md5_t *ctx, unsigned char *digest) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + unsigned char *p = (unsigned char *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + transform(ctx->buf, ctx->in); + p = (unsigned char *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(md5_t)); /* In case it's sensitive */ +} diff --git a/auth.h b/auth.h new file mode 100644 index 0000000..84564d9 --- /dev/null +++ b/auth.h @@ -0,0 +1,60 @@ +/* $KAME: auth.h,v 1.3 2004/09/07 05:03:02 jinmei Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __sun__ +#define __P(x) x +#ifndef U_INT32_T_DEFINED +#define U_INT32_T_DEFINED +typedef uint32_t u_int32_t; +#endif +#endif + +#define MD5_DIGESTLENGTH 16 + +/* secret key information for delayed authentication */ +struct keyinfo { + struct keyinfo *next; + + char *name; /* key name */ + + char *realm; /* DHCP realm */ + size_t realmlen; /* length of realm */ + u_int32_t keyid; /* key ID */ + char *secret; /* binary key */ + size_t secretlen; /* length of the key */ + time_t expire; /* expiration time (0 means forever) */ +}; + +extern int dhcp6_validate_key __P((struct keyinfo *)); +extern int dhcp6_calc_mac __P((char *, size_t, int, int, size_t, + struct keyinfo *)); +extern int dhcp6_verify_mac __P((char *, ssize_t, int, int, size_t, + struct keyinfo *)); diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..7c9731d --- /dev/null +++ b/base64.c @@ -0,0 +1,172 @@ +/* $KAME: base64.c,v 1.1 2004/06/08 07:26:56 jinmei Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1998-2001, 2003 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. + */ + +#include + +typedef enum { FALSE = 0, TRUE = 1 } boolean_t; + +static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +/* + * State of a base64 decoding process in progress. + */ +typedef struct { + int length; /* Desired length of binary data or -1 */ + int digits; /* Number of buffered base64 digits */ + boolean_t seen_end; /* True if "=" end marker seen */ + int val[4]; + + char *dst; /* Head of the available space for resulting + * binary data */ + char *dstend; /* End of the buffer */ +} base64_decode_ctx_t; + +static int +mem_tobuffer(base64_decode_ctx_t *ctx, void *base, unsigned int length) +{ + if (ctx->dst + length >= ctx->dstend) + return (-1); + memcpy(ctx->dst, base, length); + ctx->dst += length; + return (0); +} + +static inline void +base64_decode_init(base64_decode_ctx_t *ctx, int length, + char *result, size_t resultlen) +{ + ctx->digits = 0; + ctx->seen_end = FALSE; + ctx->length = length; + ctx->dst = result; + ctx->dstend = result + resultlen; +} + +static inline int +base64_decode_char(base64_decode_ctx_t *ctx, int c) +{ + char *s; + + if (ctx->seen_end == TRUE) + return (-1); + if ((s = strchr(base64, c)) == NULL) + return (-1); + ctx->val[ctx->digits++] = s - base64; + if (ctx->digits == 4) { + int n; + unsigned char buf[3]; + if (ctx->val[0] == 64 || ctx->val[1] == 64) + return (-1); + if (ctx->val[2] == 64 && ctx->val[3] != 64) + return (-1); + /* + * Check that bits that should be zero are. + */ + if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) + return (-1); + /* + * We don't need to test for ctx->val[2] != 64 as + * the bottom two bits of 64 are zero. + */ + if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) + return (-1); + n = (ctx->val[2] == 64) ? 1 : + (ctx->val[3] == 64) ? 2 : 3; + if (n != 3) { + ctx->seen_end = TRUE; + if (ctx->val[2] == 64) + ctx->val[2] = 0; + if (ctx->val[3] == 64) + ctx->val[3] = 0; + } + buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); + buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); + buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); + if (mem_tobuffer(ctx, buf, n)) + return (-1); + if (ctx->length >= 0) { + if (n > ctx->length) + return (-1); + else + ctx->length -= n; + } + ctx->digits = 0; + } + return (0); +} + +static inline int +base64_decode_finish(base64_decode_ctx_t *ctx) +{ + if (ctx->length > 0) + return (-1); + if (ctx->digits != 0) + return (-1); + return (0); +} + +int +base64_decodestring(const char *cstr, char *result, size_t resultlen) +{ + base64_decode_ctx_t ctx; + + base64_decode_init(&ctx, -1, result, resultlen); + for (;;) { + int c = *cstr++; + if (c == '\0') + break; + if (c == ' ' || c == '\t' || c == '\n' || c== '\r') + continue; + if (base64_decode_char(&ctx, c)) + return (-1); + } + if (base64_decode_finish(&ctx)) + return (-1); + return (ctx.dst - result); +} diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..6fa0ada --- /dev/null +++ b/base64.h @@ -0,0 +1,32 @@ +/* $KAME: base64.h,v 1.1 2004/06/17 12:25:18 jinmei Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern int base64_decodestring __P((const char *, char *, size_t)); diff --git a/cfparse.c b/cfparse.c new file mode 100644 index 0000000..6979c5c --- /dev/null +++ b/cfparse.c @@ -0,0 +1,3156 @@ +/* A Bison parser, made by GNU Bison 2.1. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + INTERFACE = 258, + IFNAME = 259, + PREFIX_INTERFACE = 260, + SLA_ID = 261, + SLA_LEN = 262, + DUID_ID = 263, + ID_ASSOC = 264, + IA_PD = 265, + IAID = 266, + IA_NA = 267, + ADDRESS = 268, + REQUEST = 269, + SEND = 270, + ALLOW = 271, + PREFERENCE = 272, + HOST = 273, + HOSTNAME = 274, + DUID = 275, + OPTION = 276, + RAPID_COMMIT = 277, + DNS_SERVERS = 278, + DNS_NAME = 279, + NTP_SERVERS = 280, + REFRESHTIME = 281, + SIP_SERVERS = 282, + SIP_NAME = 283, + NIS_SERVERS = 284, + NIS_NAME = 285, + NISP_SERVERS = 286, + NISP_NAME = 287, + BCMCS_SERVERS = 288, + BCMCS_NAME = 289, + INFO_ONLY = 290, + SCRIPT = 291, + DELAYEDKEY = 292, + AUTHENTICATION = 293, + PROTOCOL = 294, + ALGORITHM = 295, + DELAYED = 296, + RECONFIG = 297, + HMACMD5 = 298, + MONOCOUNTER = 299, + AUTHNAME = 300, + RDM = 301, + KEY = 302, + KEYINFO = 303, + REALM = 304, + KEYID = 305, + SECRET = 306, + KEYNAME = 307, + EXPIRE = 308, + ADDRPOOL = 309, + POOLNAME = 310, + RANGE = 311, + TO = 312, + ADDRESS_POOL = 313, + INCLUDE = 314, + NUMBER = 315, + SLASH = 316, + EOS = 317, + BCL = 318, + ECL = 319, + STRING = 320, + QSTRING = 321, + PREFIX = 322, + INFINITY = 323, + COMMA = 324 + }; +#endif +/* Tokens. */ +#define INTERFACE 258 +#define IFNAME 259 +#define PREFIX_INTERFACE 260 +#define SLA_ID 261 +#define SLA_LEN 262 +#define DUID_ID 263 +#define ID_ASSOC 264 +#define IA_PD 265 +#define IAID 266 +#define IA_NA 267 +#define ADDRESS 268 +#define REQUEST 269 +#define SEND 270 +#define ALLOW 271 +#define PREFERENCE 272 +#define HOST 273 +#define HOSTNAME 274 +#define DUID 275 +#define OPTION 276 +#define RAPID_COMMIT 277 +#define DNS_SERVERS 278 +#define DNS_NAME 279 +#define NTP_SERVERS 280 +#define REFRESHTIME 281 +#define SIP_SERVERS 282 +#define SIP_NAME 283 +#define NIS_SERVERS 284 +#define NIS_NAME 285 +#define NISP_SERVERS 286 +#define NISP_NAME 287 +#define BCMCS_SERVERS 288 +#define BCMCS_NAME 289 +#define INFO_ONLY 290 +#define SCRIPT 291 +#define DELAYEDKEY 292 +#define AUTHENTICATION 293 +#define PROTOCOL 294 +#define ALGORITHM 295 +#define DELAYED 296 +#define RECONFIG 297 +#define HMACMD5 298 +#define MONOCOUNTER 299 +#define AUTHNAME 300 +#define RDM 301 +#define KEY 302 +#define KEYINFO 303 +#define REALM 304 +#define KEYID 305 +#define SECRET 306 +#define KEYNAME 307 +#define EXPIRE 308 +#define ADDRPOOL 309 +#define POOLNAME 310 +#define RANGE 311 +#define TO 312 +#define ADDRESS_POOL 313 +#define INCLUDE 314 +#define NUMBER 315 +#define SLASH 316 +#define EOS 317 +#define BCL 318 +#define ECL 319 +#define STRING 320 +#define QSTRING 321 +#define PREFIX 322 +#define INFINITY 323 +#define COMMA 324 + + + + +/* Copy the first part of user declarations. */ +#line 31 "cfparse.y" + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +extern int lineno; +extern int cfdebug; + +extern void yywarn __P((char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); +extern void yyerror __P((char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); + +#define MAKE_NAMELIST(l, n, p) do { \ + (l) = (struct cf_namelist *)malloc(sizeof(*(l))); \ + if ((l) == NULL) { \ + yywarn("can't allocate memory"); \ + if (p) cleanup_cflist(p); \ + return (-1); \ + } \ + memset((l), 0, sizeof(*(l))); \ + l->line = lineno; \ + l->name = (n); \ + l->params = (p); \ + } while (0) + +#define MAKE_CFLIST(l, t, pp, pl) do { \ + (l) = (struct cf_list *)malloc(sizeof(*(l))); \ + if ((l) == NULL) { \ + yywarn("can't allocate memory"); \ + if (pp) free(pp); \ + if (pl) cleanup_cflist(pl); \ + return (-1); \ + } \ + memset((l), 0, sizeof(*(l))); \ + l->line = lineno; \ + l->type = (t); \ + l->ptr = (pp); \ + l->list = (pl); \ + l->tail = (l); \ + } while (0) + +static struct cf_namelist *iflist_head, *hostlist_head, *iapdlist_head; +static struct cf_namelist *addrpoollist_head; +static struct cf_namelist *authinfolist_head, *keylist_head; +static struct cf_namelist *ianalist_head; +struct cf_list *cf_dns_list, *cf_dns_name_list, *cf_ntp_list; +struct cf_list *cf_sip_list, *cf_sip_name_list; +struct cf_list *cf_nis_list, *cf_nis_name_list; +struct cf_list *cf_nisp_list, *cf_nisp_name_list; +struct cf_list *cf_bcmcs_list, *cf_bcmcs_name_list; +long long cf_refreshtime = -1; + +extern int yylex __P((void)); +extern int cfswitch_buffer __P((char *)); +static int add_namelist __P((struct cf_namelist *, struct cf_namelist **)); +static void cleanup __P((void)); +static void cleanup_namelist __P((struct cf_namelist *)); +static void cleanup_cflist __P((struct cf_list *)); + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 126 "cfparse.y" +typedef union YYSTYPE { + long long num; + char* str; + struct cf_list *list; + struct dhcp6_prefix *prefix; + struct dhcp6_range *range; + struct dhcp6_poolspec *pool; +} YYSTYPE; +/* Line 196 of yacc.c. */ +#line 305 "y.tab.c" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 219 of yacc.c. */ +#line 317 "y.tab.c" + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) && (defined (__STDC__) || defined (__cplusplus)) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# else +# define YYSTACK_ALLOC alloca +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYINCLUDED_STDLIB_H +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2005 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM ((YYSIZE_T) -1) +# endif +# ifdef __cplusplus +extern "C" { +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if (! defined (malloc) && ! defined (YYINCLUDED_STDLIB_H) \ + && (defined (__STDC__) || defined (__cplusplus))) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if (! defined (free) && ! defined (YYINCLUDED_STDLIB_H) \ + && (defined (__STDC__) || defined (__cplusplus))) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifdef __cplusplus +} +# endif +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short int yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short int yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 227 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 70 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 36 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 105 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 231 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 324 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned short int yyprhs[] = +{ + 0, 0, 3, 4, 7, 9, 11, 13, 15, 17, + 19, 21, 23, 30, 37, 42, 47, 52, 57, 62, + 67, 72, 77, 82, 87, 92, 97, 105, 112, 120, + 127, 134, 141, 145, 152, 153, 156, 158, 159, 162, + 166, 170, 173, 177, 181, 185, 189, 193, 197, 201, + 205, 209, 211, 215, 217, 220, 223, 226, 228, 230, + 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, + 254, 257, 261, 266, 272, 275, 279, 281, 283, 284, + 287, 289, 293, 300, 301, 304, 308, 312, 313, 316, + 320, 321, 324, 328, 332, 336, 340, 342, 344, 346, + 348, 349, 352, 356, 360, 364 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 71, 0, -1, -1, 71, 72, -1, 73, -1, 74, + -1, 75, -1, 76, -1, 77, -1, 78, -1, 80, + -1, 79, -1, 3, 4, 63, 83, 64, 62, -1, + 18, 19, 63, 83, 64, 62, -1, 21, 23, 81, + 62, -1, 21, 24, 66, 62, -1, 21, 25, 81, + 62, -1, 21, 27, 81, 62, -1, 21, 28, 66, + 62, -1, 21, 29, 81, 62, -1, 21, 30, 66, + 62, -1, 21, 31, 81, 62, -1, 21, 32, 66, + 62, -1, 21, 33, 81, 62, -1, 21, 34, 66, + 62, -1, 21, 26, 60, 62, -1, 9, 10, 11, + 63, 92, 64, 62, -1, 9, 10, 63, 92, 64, + 62, -1, 9, 12, 11, 63, 97, 64, 62, -1, + 9, 12, 63, 97, 64, 62, -1, 38, 45, 63, + 99, 64, 62, -1, 48, 52, 63, 104, 64, 62, + -1, 59, 66, 62, -1, 54, 55, 63, 83, 64, + 62, -1, -1, 81, 82, -1, 65, -1, -1, 83, + 84, -1, 15, 85, 62, -1, 14, 85, 62, -1, + 35, 62, -1, 16, 86, 62, -1, 20, 8, 62, + -1, 13, 88, 62, -1, 67, 89, 62, -1, 17, + 60, 62, -1, 36, 66, 62, -1, 37, 65, 62, + -1, 56, 87, 62, -1, 58, 90, 62, -1, 86, + -1, 86, 69, 85, -1, 22, -1, 38, 45, -1, + 10, 60, -1, 12, 60, -1, 27, -1, 28, -1, + 23, -1, 24, -1, 25, -1, 26, -1, 29, -1, + 30, -1, 31, -1, 32, -1, 33, -1, 34, -1, + 65, 57, 65, -1, 65, 91, -1, 65, 91, 91, + -1, 65, 61, 60, 91, -1, 65, 61, 60, 91, + 91, -1, 65, 91, -1, 65, 91, 91, -1, 68, + -1, 60, -1, -1, 92, 93, -1, 94, -1, 67, + 89, 62, -1, 5, 4, 63, 95, 64, 62, -1, + -1, 95, 96, -1, 6, 60, 62, -1, 7, 60, + 62, -1, -1, 97, 98, -1, 13, 88, 62, -1, + -1, 99, 100, -1, 39, 101, 62, -1, 40, 102, + 62, -1, 46, 103, 62, -1, 47, 65, 62, -1, + 41, -1, 42, -1, 43, -1, 44, -1, -1, 104, + 105, -1, 49, 66, 62, -1, 50, 60, 62, -1, + 51, 66, 62, -1, 53, 66, 62, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short int yyrline[] = +{ + 0, 149, 149, 151, 155, 156, 157, 158, 159, 160, + 161, 162, 166, 178, 190, 199, 214, 223, 232, 247, + 256, 271, 280, 295, 304, 319, 348, 357, 371, 380, + 397, 409, 421, 432, 444, 445, 463, 486, 487, 505, + 513, 521, 529, 537, 545, 553, 561, 570, 578, 586, + 594, 605, 609, 619, 627, 635, 643, 651, 659, 667, + 675, 683, 691, 699, 707, 715, 723, 731, 739, 750, + 781, 808, 841, 868, 901, 926, 957, 961, 968, 969, + 987, 988, 999, 1009, 1010, 1028, 1036, 1047, 1048, 1066, + 1077, 1078, 1096, 1104, 1112, 1120, 1131, 1132, 1136, 1140, + 1144, 1145, 1163, 1171, 1179, 1187 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "INTERFACE", "IFNAME", + "PREFIX_INTERFACE", "SLA_ID", "SLA_LEN", "DUID_ID", "ID_ASSOC", "IA_PD", + "IAID", "IA_NA", "ADDRESS", "REQUEST", "SEND", "ALLOW", "PREFERENCE", + "HOST", "HOSTNAME", "DUID", "OPTION", "RAPID_COMMIT", "DNS_SERVERS", + "DNS_NAME", "NTP_SERVERS", "REFRESHTIME", "SIP_SERVERS", "SIP_NAME", + "NIS_SERVERS", "NIS_NAME", "NISP_SERVERS", "NISP_NAME", "BCMCS_SERVERS", + "BCMCS_NAME", "INFO_ONLY", "SCRIPT", "DELAYEDKEY", "AUTHENTICATION", + "PROTOCOL", "ALGORITHM", "DELAYED", "RECONFIG", "HMACMD5", "MONOCOUNTER", + "AUTHNAME", "RDM", "KEY", "KEYINFO", "REALM", "KEYID", "SECRET", + "KEYNAME", "EXPIRE", "ADDRPOOL", "POOLNAME", "RANGE", "TO", + "ADDRESS_POOL", "INCLUDE", "NUMBER", "SLASH", "EOS", "BCL", "ECL", + "STRING", "QSTRING", "PREFIX", "INFINITY", "COMMA", "$accept", + "statements", "statement", "interface_statement", "host_statement", + "option_statement", "ia_statement", "authentication_statement", + "key_statement", "include_statement", "addrpool_statement", + "address_list", "address_list_ent", "declarations", "declaration", + "dhcpoption_list", "dhcpoption", "rangeparam", "addressparam", + "prefixparam", "poolparam", "duration", "iapdconf_list", "iapdconf", + "prefix_interface", "ifparams", "ifparam", "ianaconf_list", "ianaconf", + "authparam_list", "authparam", "authproto", "authalg", "authrdm", + "keyparam_list", "keyparam", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short int yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 70, 71, 71, 72, 72, 72, 72, 72, 72, + 72, 72, 73, 74, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, + 77, 78, 79, 80, 81, 81, 82, 83, 83, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 85, 85, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, + 88, 88, 89, 89, 90, 90, 91, 91, 92, 92, + 93, 93, 94, 95, 95, 96, 96, 97, 97, 98, + 99, 99, 100, 100, 100, 100, 101, 101, 102, 103, + 104, 104, 105, 105, 105, 105 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 7, 6, 7, 6, + 6, 6, 3, 6, 0, 2, 1, 0, 2, 3, + 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 3, 1, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 2, 3, 4, 5, 2, 3, 1, 1, 0, 2, + 1, 3, 6, 0, 2, 3, 3, 0, 2, 3, + 0, 2, 3, 3, 3, 3, 1, 1, 1, 1, + 0, 2, 3, 3, 3, 3 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 4, 5, 6, 7, 8, 9, 11, 10, + 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, + 34, 0, 34, 0, 34, 0, 0, 0, 0, 0, + 37, 0, 78, 0, 87, 37, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 100, + 37, 32, 0, 78, 0, 87, 0, 0, 14, 36, + 35, 15, 16, 25, 17, 18, 19, 20, 21, 22, + 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, + 0, 0, 0, 79, 80, 0, 0, 0, 88, 0, + 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 53, 59, 60, + 61, 62, 57, 58, 63, 64, 65, 66, 67, 68, + 0, 0, 51, 0, 0, 0, 0, 41, 0, 0, + 0, 0, 0, 0, 12, 0, 0, 0, 0, 27, + 0, 0, 0, 29, 13, 96, 97, 0, 98, 0, + 99, 0, 0, 30, 0, 0, 0, 0, 31, 33, + 77, 76, 70, 44, 55, 56, 54, 40, 0, 39, + 42, 46, 43, 47, 48, 0, 49, 74, 50, 0, + 45, 26, 83, 81, 28, 89, 92, 93, 94, 95, + 102, 103, 104, 105, 71, 52, 69, 75, 0, 0, + 72, 0, 0, 0, 84, 73, 0, 0, 82, 85, + 86 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const short int yydefgoto[] = +{ + -1, 1, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 46, 70, 62, 98, 141, 142, 151, 124, 156, + 153, 182, 64, 103, 104, 219, 224, 66, 108, 82, + 115, 167, 169, 171, 83, 121 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -152 +static const short int yypact[] = +{ + -152, 4, -152, 8, 64, -4, 119, -28, -14, -26, + -22, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -15, -6, 5, 21, -152, -7, -152, 0, -152, 27, + -152, 32, -152, 34, -152, 36, 40, 41, 42, 37, + -152, 43, -152, 44, -152, -152, -51, 46, -25, 55, + -12, 56, 16, 58, 20, 59, 30, 60, -152, -152, + -152, -152, 19, -152, -3, -152, 6, 74, -152, -152, + -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, 33, -23, 99, 31, 146, 146, 146, 63, + 116, 65, 62, 61, 66, 68, 67, 72, -152, -2, + 121, 77, 72, -152, -152, 7, 31, 78, -152, 92, + -18, 117, 115, 96, 100, -152, 98, 105, 101, 120, + 123, -152, 125, -50, 126, 122, 129, -152, -152, -152, + -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + 136, 128, 114, 130, 131, 132, 133, -152, 134, 135, + 141, 137, -50, 138, -152, 140, 142, 143, 139, -152, + 144, 145, 147, -152, -152, -152, -152, 148, -152, 149, + -152, 150, 151, -152, 152, 153, 154, 155, -152, -152, + -152, -152, -50, -152, -152, -152, -152, -152, 146, -152, + -152, -152, -152, -152, -152, 156, -152, -50, -152, 158, + -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, -152, -152, -152, -152, -152, -152, -50, 2, + -50, 159, 160, 161, -152, -152, 162, 163, -152, -152, + -152 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const short int yypgoto[] = +{ + -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, 17, -152, -39, -152, -87, 103, -152, 97, 106, + -152, -151, 164, -152, -152, -152, -152, 157, -152, -152, + -152, -152, -152, -152, -152, -152 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const unsigned char yytable[] = +{ + 143, 197, 100, 100, 2, 41, 67, 3, 221, 222, + 180, 68, 20, 4, 69, 23, 43, 36, 181, 106, + 106, 84, 5, 165, 166, 6, 116, 117, 118, 38, + 119, 214, 85, 86, 87, 88, 89, 72, 37, 90, + 69, 120, 7, 48, 39, 50, 217, 52, 40, 54, + 74, 56, 8, 69, 91, 92, 93, 42, 9, 47, + 49, 101, 157, 10, 102, 102, 223, 220, 44, 225, + 107, 161, 110, 111, 21, 94, 22, 95, 76, 112, + 113, 69, 78, 96, 45, 69, 97, 85, 86, 87, + 88, 89, 80, 51, 90, 69, 123, 114, 53, 61, + 55, 215, 57, 58, 59, 60, 63, 65, 71, 91, + 92, 93, 85, 86, 87, 88, 89, 73, 75, 90, + 77, 79, 81, 145, 146, 158, 149, 147, 148, 154, + 94, 150, 95, 152, 91, 92, 93, 155, 109, 159, + 163, 97, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 164, 94, 125, 95, 126, 170, + 168, 172, 173, 122, 174, 175, 97, 176, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 186, 184, 188, 140, 178, 177, 179, 183, 185, + 187, 144, 189, 190, 191, 192, 193, 194, 195, 196, + 198, 199, 202, 162, 200, 201, 203, 204, 160, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 218, 226, + 227, 216, 105, 228, 229, 230, 0, 99 +}; + +static const short int yycheck[] = +{ + 87, 152, 5, 5, 0, 11, 45, 3, 6, 7, + 60, 62, 4, 9, 65, 19, 11, 45, 68, 13, + 13, 60, 18, 41, 42, 21, 49, 50, 51, 55, + 53, 182, 13, 14, 15, 16, 17, 62, 52, 20, + 65, 64, 38, 26, 66, 28, 197, 30, 63, 32, + 62, 34, 48, 65, 35, 36, 37, 63, 54, 66, + 60, 64, 64, 59, 67, 67, 64, 218, 63, 220, + 64, 64, 39, 40, 10, 56, 12, 58, 62, 46, + 47, 65, 62, 64, 63, 65, 67, 13, 14, 15, + 16, 17, 62, 66, 20, 65, 65, 64, 66, 62, + 66, 188, 66, 63, 63, 63, 63, 63, 62, 35, + 36, 37, 13, 14, 15, 16, 17, 62, 62, 20, + 62, 62, 62, 60, 8, 4, 65, 62, 66, 62, + 56, 65, 58, 65, 35, 36, 37, 65, 64, 62, + 62, 67, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 62, 56, 10, 58, 12, 44, + 43, 65, 62, 64, 66, 60, 67, 66, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 45, 60, 69, 38, 62, 66, 62, 62, 60, + 62, 88, 62, 62, 62, 62, 62, 62, 57, 62, + 62, 61, 63, 106, 62, 62, 62, 62, 102, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 60, 60, + 60, 65, 65, 62, 62, 62, -1, 63 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 71, 0, 3, 9, 18, 21, 38, 48, 54, + 59, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 4, 10, 12, 19, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 45, 52, 55, 66, + 63, 11, 63, 11, 63, 63, 81, 66, 81, 60, + 81, 66, 81, 66, 81, 66, 81, 66, 63, 63, + 63, 62, 83, 63, 92, 63, 97, 83, 62, 65, + 82, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 99, 104, 83, 13, 14, 15, 16, 17, + 20, 35, 36, 37, 56, 58, 64, 67, 84, 92, + 5, 64, 67, 93, 94, 97, 13, 64, 98, 64, + 39, 40, 46, 47, 64, 100, 49, 50, 51, 53, + 64, 105, 64, 65, 88, 10, 12, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 38, 85, 86, 85, 86, 60, 8, 62, 66, 65, + 65, 87, 65, 90, 62, 65, 89, 64, 4, 62, + 89, 64, 88, 62, 62, 41, 42, 101, 43, 102, + 44, 103, 65, 62, 66, 60, 66, 66, 62, 62, + 60, 68, 91, 62, 60, 60, 45, 62, 69, 62, + 62, 62, 62, 62, 62, 57, 62, 91, 62, 61, + 62, 62, 63, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 91, 85, 65, 91, 60, 95, + 91, 6, 7, 64, 96, 91, 60, 60, 62, 62, + 62 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short int *bottom, short int *top) +#else +static void +yy_stack_print (bottom, top) + short int *bottom; + short int *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname[yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + size_t yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +#endif /* YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + ; +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short int yyssa[YYINITDEPTH]; + short int *yyss = yyssa; + short int *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short int *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short int *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a look-ahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to look-ahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 12: +#line 167 "cfparse.y" + { + struct cf_namelist *ifl; + + MAKE_NAMELIST(ifl, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(ifl, &iflist_head)) + return (-1); + } + break; + + case 13: +#line 179 "cfparse.y" + { + struct cf_namelist *host; + + MAKE_NAMELIST(host, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(host, &hostlist_head)) + return (-1); + } + break; + + case 14: +#line 191 "cfparse.y" + { + if (cf_dns_list == NULL) + cf_dns_list = (yyvsp[-1].list); + else { + cf_dns_list->tail->next = (yyvsp[-1].list); + cf_dns_list->tail = (yyvsp[-1].list)->tail; + } + } + break; + + case 15: +#line 200 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, (yyvsp[-1].str), NULL); + + if (cf_dns_name_list == NULL) { + cf_dns_name_list = l; + cf_dns_name_list->tail = l; + cf_dns_name_list->next = NULL; + } else { + cf_dns_name_list->tail->next = l; + cf_dns_name_list->tail = l->tail; + } + } + break; + + case 16: +#line 215 "cfparse.y" + { + if (cf_ntp_list == NULL) + cf_ntp_list = (yyvsp[-1].list); + else { + cf_ntp_list->tail->next = (yyvsp[-1].list); + cf_ntp_list->tail = (yyvsp[-1].list)->tail; + } + } + break; + + case 17: +#line 224 "cfparse.y" + { + if (cf_sip_list == NULL) + cf_sip_list = (yyvsp[-1].list); + else { + cf_sip_list->tail->next = (yyvsp[-1].list); + cf_sip_list->tail = (yyvsp[-1].list)->tail; + } + } + break; + + case 18: +#line 233 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, (yyvsp[-1].str), NULL); + + if (cf_sip_name_list == NULL) { + cf_sip_name_list = l; + cf_sip_name_list->tail = l; + cf_sip_name_list->next = NULL; + } else { + cf_sip_name_list->tail->next = l; + cf_sip_name_list->tail = l->tail; + } + } + break; + + case 19: +#line 248 "cfparse.y" + { + if (cf_nis_list == NULL) + cf_nis_list = (yyvsp[-1].list); + else { + cf_nis_list->tail->next = (yyvsp[-1].list); + cf_nis_list->tail = (yyvsp[-1].list)->tail; + } + } + break; + + case 20: +#line 257 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, (yyvsp[-1].str), NULL); + + if (cf_nis_name_list == NULL) { + cf_nis_name_list = l; + cf_nis_name_list->tail = l; + cf_nis_name_list->next = NULL; + } else { + cf_nis_name_list->tail->next = l; + cf_nis_name_list->tail = l->tail; + } + } + break; + + case 21: +#line 272 "cfparse.y" + { + if (cf_nisp_list == NULL) + cf_nisp_list = (yyvsp[-1].list); + else { + cf_nisp_list->tail->next = (yyvsp[-1].list); + cf_nisp_list->tail = (yyvsp[-1].list)->tail; + } + } + break; + + case 22: +#line 281 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, (yyvsp[-1].str), NULL); + + if (cf_nisp_name_list == NULL) { + cf_nisp_name_list = l; + cf_nisp_name_list->tail = l; + cf_nisp_name_list->next = NULL; + } else { + cf_nisp_name_list->tail->next = l; + cf_nisp_name_list->tail = l->tail; + } + } + break; + + case 23: +#line 296 "cfparse.y" + { + if (cf_bcmcs_list == NULL) + cf_bcmcs_list = (yyvsp[-1].list); + else { + cf_bcmcs_list->tail->next = (yyvsp[-1].list); + cf_bcmcs_list->tail = (yyvsp[-1].list)->tail; + } + } + break; + + case 24: +#line 305 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, (yyvsp[-1].str), NULL); + + if (cf_bcmcs_name_list == NULL) { + cf_bcmcs_name_list = l; + cf_bcmcs_name_list->tail = l; + cf_bcmcs_name_list->next = NULL; + } else { + cf_bcmcs_name_list->tail->next = l; + cf_bcmcs_name_list->tail = l->tail; + } + } + break; + + case 25: +#line 320 "cfparse.y" + { + if (cf_refreshtime == -1) { + cf_refreshtime = (yyvsp[-1].num); + if (cf_refreshtime < -1 || + cf_refreshtime > 0xffffffff) { + /* + * refresh time should not be negative + * according to the lex definition, + * but check it for safety. + */ + yyerror("refresh time is out of range"); + } + if (cf_refreshtime < DHCP6_IRT_MINIMUM) { + /* + * the value MUST NOT be smaller than + * IRT_MINIMUM. + */ + yyerror("refresh time is too small " + "(must not be smaller than %d)", + DHCP6_IRT_MINIMUM); + } + } else { + yywarn("multiple refresh times (ignored)"); + } + } + break; + + case 26: +#line 349 "cfparse.y" + { + struct cf_namelist *iapd; + + MAKE_NAMELIST(iapd, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(iapd, &iapdlist_head)) + return (-1); + } + break; + + case 27: +#line 358 "cfparse.y" + { + struct cf_namelist *iapd; + char *zero; + + if ((zero = strdup("0")) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + MAKE_NAMELIST(iapd, zero, (yyvsp[-2].list)); + + if (add_namelist(iapd, &iapdlist_head)) + return (-1); + } + break; + + case 28: +#line 372 "cfparse.y" + { + struct cf_namelist *iana; + + MAKE_NAMELIST(iana, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(iana, &ianalist_head)) + return (-1); + } + break; + + case 29: +#line 381 "cfparse.y" + { + struct cf_namelist *iana; + char *zero; + + if ((zero = strdup("0")) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + MAKE_NAMELIST(iana, zero, (yyvsp[-2].list)); + + if (add_namelist(iana, &ianalist_head)) + return (-1); + } + break; + + case 30: +#line 398 "cfparse.y" + { + struct cf_namelist *authinfo; + + MAKE_NAMELIST(authinfo, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(authinfo, &authinfolist_head)) + return (-1); + } + break; + + case 31: +#line 410 "cfparse.y" + { + struct cf_namelist *key; + + MAKE_NAMELIST(key, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(key, &keylist_head)) + return (-1); + } + break; + + case 32: +#line 422 "cfparse.y" + { + if (cfswitch_buffer((yyvsp[-1].str))) { + free((yyvsp[-1].str)); + return (-1); + } + free((yyvsp[-1].str)); + } + break; + + case 33: +#line 433 "cfparse.y" + { + struct cf_namelist *pool; + + MAKE_NAMELIST(pool, (yyvsp[-4].str), (yyvsp[-2].list)); + + if (add_namelist(pool, &addrpoollist_head)) + return (-1); + } + break; + + case 34: +#line 444 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 35: +#line 446 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 36: +#line 464 "cfparse.y" + { + struct cf_list *l; + struct in6_addr a0, *a; + + if (inet_pton(AF_INET6, (yyvsp[0].str), &a0) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[0].str)); + free((yyvsp[0].str)); + return (-1); + } + if ((a = malloc(sizeof(*a))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *a = a0; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, a, NULL); + + (yyval.list) = l; + } + break; + + case 37: +#line 486 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 38: +#line 488 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 39: +#line 506 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_SEND, NULL, (yyvsp[-1].list)); + + (yyval.list) = l; + } + break; + + case 40: +#line 514 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_REQUEST, NULL, (yyvsp[-1].list)); + + (yyval.list) = l; + } + break; + + case 41: +#line 522 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_INFO_ONLY, NULL, NULL); + /* no value */ + (yyval.list) = l; + } + break; + + case 42: +#line 530 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ALLOW, NULL, (yyvsp[-1].list)); + + (yyval.list) = l; + } + break; + + case 43: +#line 538 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_DUID, (yyvsp[-1].str), NULL); + + (yyval.list) = l; + } + break; + + case 44: +#line 546 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ADDRESS, (yyvsp[-1].prefix),NULL); + + (yyval.list) = l; + } + break; + + case 45: +#line 554 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_PREFIX, (yyvsp[-1].prefix), NULL); + + (yyval.list) = l; + } + break; + + case 46: +#line 562 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_PREFERENCE, NULL, NULL); + l->num = (yyvsp[-1].num); + + (yyval.list) = l; + } + break; + + case 47: +#line 571 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_SCRIPT, (yyvsp[-1].str), NULL); + + (yyval.list) = l; + } + break; + + case 48: +#line 579 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_DELAYEDKEY, (yyvsp[-1].str), NULL); + + (yyval.list) = l; + } + break; + + case 49: +#line 587 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_RANGE, (yyvsp[-1].range), NULL); + + (yyval.list) = l; + } + break; + + case 50: +#line 595 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ADDRESSPOOL, (yyvsp[-1].pool), NULL); + + (yyval.list) = l; + } + break; + + case 51: +#line 606 "cfparse.y" + { + (yyval.list) = (yyvsp[0].list); + } + break; + + case 52: +#line 610 "cfparse.y" + { + (yyvsp[-2].list)->next = (yyvsp[0].list); + (yyvsp[-2].list)->tail = (yyvsp[0].list)->tail; + + (yyval.list) = (yyvsp[-2].list); + } + break; + + case 53: +#line 620 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_RAPID_COMMIT, NULL, NULL); + /* no value */ + (yyval.list) = l; + } + break; + + case 54: +#line 628 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_AUTHINFO, NULL, NULL); + l->ptr = (yyvsp[0].str); + (yyval.list) = l; + } + break; + + case 55: +#line 636 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_IA_PD, NULL, NULL); + l->num = (yyvsp[0].num); + (yyval.list) = l; + } + break; + + case 56: +#line 644 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_IA_NA, NULL, NULL); + l->num = (yyvsp[0].num); + (yyval.list) = l; + } + break; + + case 57: +#line 652 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_SIP, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 58: +#line 660 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_SIPNAME, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 59: +#line 668 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_DNS, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 60: +#line 676 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_DNSNAME, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 61: +#line 684 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NTP, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 62: +#line 692 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_REFRESHTIME, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 63: +#line 700 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NIS, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 64: +#line 708 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NISNAME, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 65: +#line 716 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NISP, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 66: +#line 724 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NISPNAME, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 67: +#line 732 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_BCMCS, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 68: +#line 740 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_BCMCSNAME, NULL, NULL); + /* currently no value */ + (yyval.list) = l; + } + break; + + case 69: +#line 751 "cfparse.y" + { + struct dhcp6_range range0, *range; + + memset(&range0, 0, sizeof(range0)); + if (inet_pton(AF_INET6, (yyvsp[-2].str), &range0.min) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[-2].str)); + free((yyvsp[-2].str)); + free((yyvsp[0].str)); + return (-1); + } + if (inet_pton(AF_INET6, (yyvsp[0].str), &range0.max) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[0].str)); + free((yyvsp[-2].str)); + free((yyvsp[0].str)); + return (-1); + } + free((yyvsp[-2].str)); + free((yyvsp[0].str)); + + if ((range = malloc(sizeof(*range))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *range = range0; + + (yyval.range) = range; + } + break; + + case 70: +#line 782 "cfparse.y" + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, (yyvsp[-1].str), &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[-1].str)); + free((yyvsp[-1].str)); + return (-1); + } + free((yyvsp[-1].str)); + /* validate other parameters later */ + pconf0.plen = 128; /* XXX this field is ignored */ + if ((yyvsp[0].num) < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)(yyvsp[0].num); + pconf0.vltime = pconf0.pltime; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + (yyval.prefix) = pconf; + } + break; + + case 71: +#line 809 "cfparse.y" + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, (yyvsp[-2].str), &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[-2].str)); + free((yyvsp[-2].str)); + return (-1); + } + free((yyvsp[-2].str)); + /* validate other parameters later */ + pconf0.plen = 128; /* XXX */ + if ((yyvsp[-1].num) < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)(yyvsp[-1].num); + if ((yyvsp[0].num) < 0) + pconf0.vltime = DHCP6_DURATION_INFINITE; + else + pconf0.vltime = (u_int32_t)(yyvsp[0].num); + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + (yyval.prefix) = pconf; + } + break; + + case 72: +#line 842 "cfparse.y" + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, (yyvsp[-3].str), &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[-3].str)); + free((yyvsp[-3].str)); + return (-1); + } + free((yyvsp[-3].str)); + /* validate other parameters later */ + pconf0.plen = (yyvsp[-1].num); + if ((yyvsp[0].num) < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)(yyvsp[0].num); + pconf0.vltime = pconf0.pltime; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + (yyval.prefix) = pconf; + } + break; + + case 73: +#line 869 "cfparse.y" + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, (yyvsp[-4].str), &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", (yyvsp[-4].str)); + free((yyvsp[-4].str)); + return (-1); + } + free((yyvsp[-4].str)); + /* validate other parameters later */ + pconf0.plen = (yyvsp[-2].num); + if ((yyvsp[-1].num) < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)(yyvsp[-1].num); + if ((yyvsp[0].num) < 0) + pconf0.vltime = DHCP6_DURATION_INFINITE; + else + pconf0.vltime = (u_int32_t)(yyvsp[0].num); + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + (yyval.prefix) = pconf; + } + break; + + case 74: +#line 902 "cfparse.y" + { + struct dhcp6_poolspec* pool; + + if ((pool = malloc(sizeof(*pool))) == NULL) { + yywarn("can't allocate memory"); + free((yyvsp[-1].str)); + return (-1); + } + if ((pool->name = strdup((yyvsp[-1].str))) == NULL) { + yywarn("can't allocate memory"); + free((yyvsp[-1].str)); + return (-1); + } + free((yyvsp[-1].str)); + + /* validate other parameters later */ + if ((yyvsp[0].num) < 0) + pool->pltime = DHCP6_DURATION_INFINITE; + else + pool->pltime = (u_int32_t)(yyvsp[0].num); + pool->vltime = pool->pltime; + + (yyval.pool) = pool; + } + break; + + case 75: +#line 927 "cfparse.y" + { + struct dhcp6_poolspec* pool; + + if ((pool = malloc(sizeof(*pool))) == NULL) { + yywarn("can't allocate memory"); + free((yyvsp[-2].str)); + return (-1); + } + if ((pool->name = strdup((yyvsp[-2].str))) == NULL) { + yywarn("can't allocate memory"); + free((yyvsp[-2].str)); + return (-1); + } + free((yyvsp[-2].str)); + + /* validate other parameters later */ + if ((yyvsp[-1].num) < 0) + pool->pltime = DHCP6_DURATION_INFINITE; + else + pool->pltime = (u_int32_t)(yyvsp[-1].num); + if ((yyvsp[0].num) < 0) + pool->vltime = DHCP6_DURATION_INFINITE; + else + pool->vltime = (u_int32_t)(yyvsp[0].num); + + (yyval.pool) = pool; + } + break; + + case 76: +#line 958 "cfparse.y" + { + (yyval.num) = -1; + } + break; + + case 77: +#line 962 "cfparse.y" + { + (yyval.num) = (yyvsp[0].num); + } + break; + + case 78: +#line 968 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 79: +#line 970 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 80: +#line 987 "cfparse.y" + { (yyval.list) = (yyvsp[0].list); } + break; + + case 81: +#line 989 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, IACONF_PREFIX, (yyvsp[-1].prefix), NULL); + + (yyval.list) = l; + } + break; + + case 82: +#line 1000 "cfparse.y" + { + struct cf_list *ifl; + + MAKE_CFLIST(ifl, IACONF_PIF, (yyvsp[-4].str), (yyvsp[-2].list)); + (yyval.list) = ifl; + } + break; + + case 83: +#line 1009 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 84: +#line 1011 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 85: +#line 1029 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, IFPARAM_SLA_ID, NULL, NULL); + l->num = (yyvsp[-1].num); + (yyval.list) = l; + } + break; + + case 86: +#line 1037 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, IFPARAM_SLA_LEN, NULL, NULL); + l->num = (yyvsp[-1].num); + (yyval.list) = l; + } + break; + + case 87: +#line 1047 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 88: +#line 1049 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 89: +#line 1067 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, IACONF_ADDR, (yyvsp[-1].prefix), NULL); + + (yyval.list) = l; + } + break; + + case 90: +#line 1077 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 91: +#line 1079 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 92: +#line 1097 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_PROTO, NULL, NULL); + l->num = (yyvsp[-1].num); + (yyval.list) = l; + } + break; + + case 93: +#line 1105 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_ALG, NULL, NULL); + l->num = (yyvsp[-1].num); + (yyval.list) = l; + } + break; + + case 94: +#line 1113 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_RDM, NULL, NULL); + l->num = (yyvsp[-1].num); + (yyval.list) = l; + } + break; + + case 95: +#line 1121 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_KEY, NULL, NULL); + l->ptr = (yyvsp[-1].str); + (yyval.list) = l; + } + break; + + case 96: +#line 1131 "cfparse.y" + { (yyval.num) = DHCP6_AUTHPROTO_DELAYED; } + break; + + case 97: +#line 1132 "cfparse.y" + { (yyval.num) = DHCP6_AUTHPROTO_RECONFIG; } + break; + + case 98: +#line 1136 "cfparse.y" + { (yyval.num) = DHCP6_AUTHALG_HMACMD5; } + break; + + case 99: +#line 1140 "cfparse.y" + { (yyval.num) = DHCP6_AUTHRDM_MONOCOUNTER; } + break; + + case 100: +#line 1144 "cfparse.y" + { (yyval.list) = NULL; } + break; + + case 101: +#line 1146 "cfparse.y" + { + struct cf_list *head; + + if ((head = (yyvsp[-1].list)) == NULL) { + (yyvsp[0].list)->next = NULL; + (yyvsp[0].list)->tail = (yyvsp[0].list); + head = (yyvsp[0].list); + } else { + head->tail->next = (yyvsp[0].list); + head->tail = (yyvsp[0].list)->tail; + } + + (yyval.list) = head; + } + break; + + case 102: +#line 1164 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_REALM, NULL, NULL); + l->ptr = (yyvsp[-1].str); + (yyval.list) = l; + } + break; + + case 103: +#line 1172 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_KEYID, NULL, NULL); + l->num = (yyvsp[-1].num); + (yyval.list) = l; + } + break; + + case 104: +#line 1180 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_SECRET, NULL, NULL); + l->ptr = (yyvsp[-1].str); + (yyval.list) = l; + } + break; + + case 105: +#line 1188 "cfparse.y" + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_EXPIRE, NULL, NULL); + l->ptr = (yyvsp[-1].str); + (yyval.list) = l; + } + break; + + + default: break; + } + +/* Line 1126 of yacc.c. */ +#line 2744 "y.tab.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + char *yymsg = 0; +# define YYERROR_VERBOSE_ARGS_MAXIMUM 5 + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +#if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +#endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= yysize1 < yysize; + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= yysize1 < yysize; + yysize = yysize1; + + if (!yysize_overflow && yysize <= YYSTACK_ALLOC_MAXIMUM) + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yymsg; + int yyi = 0; + while ((*yyp = *yyf)) + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + { + yyerror (YY_("syntax error")); + goto yyexhaustedlab; + } + } + else +#endif /* YYERROR_VERBOSE */ + yyerror (YY_("syntax error")); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (0) + goto yyerrorlab; + +yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK; + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 1197 "cfparse.y" + +/* supplement routines for configuration */ +static int +add_namelist(new, headp) + struct cf_namelist *new, **headp; +{ + struct cf_namelist *n; + + /* check for duplicated configuration */ + for (n = *headp; n; n = n->next) { + if (strcmp(n->name, new->name) == 0) { + yywarn("duplicated name: %s (ignored)", + new->name); + cleanup_namelist(new); + return (0); + } + } + + new->next = *headp; + *headp = new; + + return (0); +} + +/* free temporary resources */ +static void +cleanup() +{ + cleanup_namelist(iflist_head); + iflist_head = NULL; + cleanup_namelist(hostlist_head); + hostlist_head = NULL; + cleanup_namelist(iapdlist_head); + iapdlist_head = NULL; + cleanup_namelist(ianalist_head); + ianalist_head = NULL; + cleanup_namelist(authinfolist_head); + authinfolist_head = NULL; + cleanup_namelist(keylist_head); + keylist_head = NULL; + cleanup_namelist(addrpoollist_head); + addrpoollist_head = NULL; + + cleanup_cflist(cf_sip_list); + cf_sip_list = NULL; + cleanup_cflist(cf_sip_name_list); + cf_sip_name_list = NULL; + cleanup_cflist(cf_dns_list); + cf_dns_list = NULL; + cleanup_cflist(cf_dns_name_list); + cf_dns_name_list = NULL; + cleanup_cflist(cf_ntp_list); + cf_ntp_list = NULL; + cleanup_cflist(cf_nis_list); + cf_nis_list = NULL; + cleanup_cflist(cf_nis_name_list); + cf_nis_name_list = NULL; + cleanup_cflist(cf_nisp_list); + cf_nisp_list = NULL; + cleanup_cflist(cf_nisp_name_list); + cf_nisp_name_list = NULL; + cleanup_cflist(cf_bcmcs_list); + cf_bcmcs_list = NULL; + cleanup_cflist(cf_bcmcs_name_list); + cf_bcmcs_name_list = NULL; +} + +static void +cleanup_namelist(head) + struct cf_namelist *head; +{ + struct cf_namelist *ifp, *ifp_next; + + for (ifp = head; ifp; ifp = ifp_next) { + ifp_next = ifp->next; + cleanup_cflist(ifp->params); + free(ifp->name); + free(ifp); + } +} + +static void +cleanup_cflist(p) + struct cf_list *p; +{ + struct cf_list *n; + + if (p == NULL) + return; + + n = p->next; + if (p->type == DECL_ADDRESSPOOL) { + free(((struct dhcp6_poolspec *)p->ptr)->name); + } + if (p->ptr) + free(p->ptr); + if (p->list) + cleanup_cflist(p->list); + free(p); + + cleanup_cflist(n); +} + +#define config_fail() \ + do { cleanup(); configure_cleanup(); return (-1); } while(0) + +int +cf_post_config() +{ + if (configure_keys(keylist_head)) + config_fail(); + + if (configure_authinfo(authinfolist_head)) + config_fail(); + + if (configure_ia(iapdlist_head, IATYPE_PD)) + config_fail(); + + if (configure_ia(ianalist_head, IATYPE_NA)) + config_fail(); + + if (configure_pool(addrpoollist_head)) + config_fail(); + + if (configure_interface(iflist_head)) + config_fail(); + + if (configure_host(hostlist_head)) + config_fail(); + + if (configure_global_option()) + config_fail(); + + configure_commit(); + cleanup(); + return (0); +} +#undef config_fail + +void +cf_init() +{ + iflist_head = NULL; +} + diff --git a/cfparse.y b/cfparse.y new file mode 100644 index 0000000..dcac3d7 --- /dev/null +++ b/cfparse.y @@ -0,0 +1,1340 @@ +/* $KAME: cfparse.y,v 1.36 2005/05/03 06:46:00 jinmei Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +%{ +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +extern int lineno; +extern int cfdebug; + +extern void yywarn __P((char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); +extern void yyerror __P((char *, ...)) + __attribute__((__format__(__printf__, 1, 2))); + +#define MAKE_NAMELIST(l, n, p) do { \ + (l) = (struct cf_namelist *)malloc(sizeof(*(l))); \ + if ((l) == NULL) { \ + yywarn("can't allocate memory"); \ + if (p) cleanup_cflist(p); \ + return (-1); \ + } \ + memset((l), 0, sizeof(*(l))); \ + l->line = lineno; \ + l->name = (n); \ + l->params = (p); \ + } while (0) + +#define MAKE_CFLIST(l, t, pp, pl) do { \ + (l) = (struct cf_list *)malloc(sizeof(*(l))); \ + if ((l) == NULL) { \ + yywarn("can't allocate memory"); \ + if (pp) free(pp); \ + if (pl) cleanup_cflist(pl); \ + return (-1); \ + } \ + memset((l), 0, sizeof(*(l))); \ + l->line = lineno; \ + l->type = (t); \ + l->ptr = (pp); \ + l->list = (pl); \ + l->tail = (l); \ + } while (0) + +static struct cf_namelist *iflist_head, *hostlist_head, *iapdlist_head; +static struct cf_namelist *addrpoollist_head; +static struct cf_namelist *authinfolist_head, *keylist_head; +static struct cf_namelist *ianalist_head; +struct cf_list *cf_dns_list, *cf_dns_name_list, *cf_ntp_list; +struct cf_list *cf_sip_list, *cf_sip_name_list; +struct cf_list *cf_nis_list, *cf_nis_name_list; +struct cf_list *cf_nisp_list, *cf_nisp_name_list; +struct cf_list *cf_bcmcs_list, *cf_bcmcs_name_list; +long long cf_refreshtime = -1; + +extern int yylex __P((void)); +extern int cfswitch_buffer __P((char *)); +static int add_namelist __P((struct cf_namelist *, struct cf_namelist **)); +static void cleanup __P((void)); +static void cleanup_namelist __P((struct cf_namelist *)); +static void cleanup_cflist __P((struct cf_list *)); +%} + +%token INTERFACE IFNAME +%token PREFIX_INTERFACE SLA_ID SLA_LEN DUID_ID +%token ID_ASSOC IA_PD IAID IA_NA +%token ADDRESS +%token REQUEST SEND ALLOW PREFERENCE +%token HOST HOSTNAME DUID +%token OPTION RAPID_COMMIT DNS_SERVERS DNS_NAME NTP_SERVERS REFRESHTIME +%token SIP_SERVERS SIP_NAME +%token NIS_SERVERS NIS_NAME +%token NISP_SERVERS NISP_NAME +%token BCMCS_SERVERS BCMCS_NAME +%token INFO_ONLY +%token SCRIPT DELAYEDKEY +%token AUTHENTICATION PROTOCOL ALGORITHM DELAYED RECONFIG HMACMD5 MONOCOUNTER +%token AUTHNAME RDM KEY +%token KEYINFO REALM KEYID SECRET KEYNAME EXPIRE +%token ADDRPOOL POOLNAME RANGE TO ADDRESS_POOL +%token INCLUDE + +%token NUMBER SLASH EOS BCL ECL STRING QSTRING PREFIX INFINITY +%token COMMA + +%union { + long long num; + char* str; + struct cf_list *list; + struct dhcp6_prefix *prefix; + struct dhcp6_range *range; + struct dhcp6_poolspec *pool; +} + +%type IFNAME HOSTNAME AUTHNAME KEYNAME DUID_ID STRING QSTRING IAID +%type POOLNAME +%type NUMBER duration authproto authalg authrdm +%type declaration declarations dhcpoption ifparam ifparams +%type address_list address_list_ent dhcpoption_list +%type iapdconf_list iapdconf prefix_interface +%type ianaconf_list ianaconf +%type authparam_list authparam +%type keyparam_list keyparam +%type addressparam prefixparam +%type rangeparam +%type poolparam + +%% +statements: + /* empty */ + | statements statement + ; + +statement: + interface_statement + | host_statement + | option_statement + | ia_statement + | authentication_statement + | key_statement + | addrpool_statement + | include_statement + ; + +interface_statement: + INTERFACE IFNAME BCL declarations ECL EOS + { + struct cf_namelist *ifl; + + MAKE_NAMELIST(ifl, $2, $4); + + if (add_namelist(ifl, &iflist_head)) + return (-1); + } + ; + +host_statement: + HOST HOSTNAME BCL declarations ECL EOS + { + struct cf_namelist *host; + + MAKE_NAMELIST(host, $2, $4); + + if (add_namelist(host, &hostlist_head)) + return (-1); + } + ; + +option_statement: + OPTION DNS_SERVERS address_list EOS + { + if (cf_dns_list == NULL) + cf_dns_list = $3; + else { + cf_dns_list->tail->next = $3; + cf_dns_list->tail = $3->tail; + } + } + | OPTION DNS_NAME QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, $3, NULL); + + if (cf_dns_name_list == NULL) { + cf_dns_name_list = l; + cf_dns_name_list->tail = l; + cf_dns_name_list->next = NULL; + } else { + cf_dns_name_list->tail->next = l; + cf_dns_name_list->tail = l->tail; + } + } + | OPTION NTP_SERVERS address_list EOS + { + if (cf_ntp_list == NULL) + cf_ntp_list = $3; + else { + cf_ntp_list->tail->next = $3; + cf_ntp_list->tail = $3->tail; + } + } + | OPTION SIP_SERVERS address_list EOS + { + if (cf_sip_list == NULL) + cf_sip_list = $3; + else { + cf_sip_list->tail->next = $3; + cf_sip_list->tail = $3->tail; + } + } + | OPTION SIP_NAME QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, $3, NULL); + + if (cf_sip_name_list == NULL) { + cf_sip_name_list = l; + cf_sip_name_list->tail = l; + cf_sip_name_list->next = NULL; + } else { + cf_sip_name_list->tail->next = l; + cf_sip_name_list->tail = l->tail; + } + } + | OPTION NIS_SERVERS address_list EOS + { + if (cf_nis_list == NULL) + cf_nis_list = $3; + else { + cf_nis_list->tail->next = $3; + cf_nis_list->tail = $3->tail; + } + } + | OPTION NIS_NAME QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, $3, NULL); + + if (cf_nis_name_list == NULL) { + cf_nis_name_list = l; + cf_nis_name_list->tail = l; + cf_nis_name_list->next = NULL; + } else { + cf_nis_name_list->tail->next = l; + cf_nis_name_list->tail = l->tail; + } + } + | OPTION NISP_SERVERS address_list EOS + { + if (cf_nisp_list == NULL) + cf_nisp_list = $3; + else { + cf_nisp_list->tail->next = $3; + cf_nisp_list->tail = $3->tail; + } + } + | OPTION NISP_NAME QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, $3, NULL); + + if (cf_nisp_name_list == NULL) { + cf_nisp_name_list = l; + cf_nisp_name_list->tail = l; + cf_nisp_name_list->next = NULL; + } else { + cf_nisp_name_list->tail->next = l; + cf_nisp_name_list->tail = l->tail; + } + } + | OPTION BCMCS_SERVERS address_list EOS + { + if (cf_bcmcs_list == NULL) + cf_bcmcs_list = $3; + else { + cf_bcmcs_list->tail->next = $3; + cf_bcmcs_list->tail = $3->tail; + } + } + | OPTION BCMCS_NAME QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, $3, NULL); + + if (cf_bcmcs_name_list == NULL) { + cf_bcmcs_name_list = l; + cf_bcmcs_name_list->tail = l; + cf_bcmcs_name_list->next = NULL; + } else { + cf_bcmcs_name_list->tail->next = l; + cf_bcmcs_name_list->tail = l->tail; + } + } + | OPTION REFRESHTIME NUMBER EOS + { + if (cf_refreshtime == -1) { + cf_refreshtime = $3; + if (cf_refreshtime < -1 || + cf_refreshtime > 0xffffffff) { + /* + * refresh time should not be negative + * according to the lex definition, + * but check it for safety. + */ + yyerror("refresh time is out of range"); + } + if (cf_refreshtime < DHCP6_IRT_MINIMUM) { + /* + * the value MUST NOT be smaller than + * IRT_MINIMUM. + */ + yyerror("refresh time is too small " + "(must not be smaller than %d)", + DHCP6_IRT_MINIMUM); + } + } else { + yywarn("multiple refresh times (ignored)"); + } + } + ; + +ia_statement: + ID_ASSOC IA_PD IAID BCL iapdconf_list ECL EOS + { + struct cf_namelist *iapd; + + MAKE_NAMELIST(iapd, $3, $5); + + if (add_namelist(iapd, &iapdlist_head)) + return (-1); + } + | ID_ASSOC IA_PD BCL iapdconf_list ECL EOS + { + struct cf_namelist *iapd; + char *zero; + + if ((zero = strdup("0")) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + MAKE_NAMELIST(iapd, zero, $4); + + if (add_namelist(iapd, &iapdlist_head)) + return (-1); + } + | ID_ASSOC IA_NA IAID BCL ianaconf_list ECL EOS + { + struct cf_namelist *iana; + + MAKE_NAMELIST(iana, $3, $5); + + if (add_namelist(iana, &ianalist_head)) + return (-1); + } + | ID_ASSOC IA_NA BCL ianaconf_list ECL EOS + { + struct cf_namelist *iana; + char *zero; + + if ((zero = strdup("0")) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + MAKE_NAMELIST(iana, zero, $4); + + if (add_namelist(iana, &ianalist_head)) + return (-1); + } + ; + +authentication_statement: + AUTHENTICATION AUTHNAME BCL authparam_list ECL EOS + { + struct cf_namelist *authinfo; + + MAKE_NAMELIST(authinfo, $2, $4); + + if (add_namelist(authinfo, &authinfolist_head)) + return (-1); + } + ; + +key_statement: + KEYINFO KEYNAME BCL keyparam_list ECL EOS + { + struct cf_namelist *key; + + MAKE_NAMELIST(key, $2, $4); + + if (add_namelist(key, &keylist_head)) + return (-1); + } + ; + +include_statement: + INCLUDE QSTRING EOS + { + if (cfswitch_buffer($2)) { + free($2); + return (-1); + } + free($2); + } + ; + +addrpool_statement: + ADDRPOOL POOLNAME BCL declarations ECL EOS + { + struct cf_namelist *pool; + + MAKE_NAMELIST(pool, $2, $4); + + if (add_namelist(pool, &addrpoollist_head)) + return (-1); + } + ; + +address_list: + { $$ = NULL; } + | address_list address_list_ent + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +address_list_ent: + STRING + { + struct cf_list *l; + struct in6_addr a0, *a; + + if (inet_pton(AF_INET6, $1, &a0) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + if ((a = malloc(sizeof(*a))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *a = a0; + + MAKE_CFLIST(l, CFLISTENT_GENERIC, a, NULL); + + $$ = l; + } + ; + +declarations: + { $$ = NULL; } + | declarations declaration + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +declaration: + SEND dhcpoption_list EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_SEND, NULL, $2); + + $$ = l; + } + | REQUEST dhcpoption_list EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_REQUEST, NULL, $2); + + $$ = l; + } + | INFO_ONLY EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_INFO_ONLY, NULL, NULL); + /* no value */ + $$ = l; + } + | ALLOW dhcpoption EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ALLOW, NULL, $2); + + $$ = l; + } + | DUID DUID_ID EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_DUID, $2, NULL); + + $$ = l; + } + | ADDRESS addressparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ADDRESS, $2,NULL); + + $$ = l; + } + | PREFIX prefixparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_PREFIX, $2, NULL); + + $$ = l; + } + | PREFERENCE NUMBER EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_PREFERENCE, NULL, NULL); + l->num = $2; + + $$ = l; + } + | SCRIPT QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_SCRIPT, $2, NULL); + + $$ = l; + } + | DELAYEDKEY STRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_DELAYEDKEY, $2, NULL); + + $$ = l; + } + | RANGE rangeparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_RANGE, $2, NULL); + + $$ = l; + } + | ADDRESS_POOL poolparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, DECL_ADDRESSPOOL, $2, NULL); + + $$ = l; + } + ; + +dhcpoption_list: + dhcpoption + { + $$ = $1; + } + | dhcpoption COMMA dhcpoption_list + { + $1->next = $3; + $1->tail = $3->tail; + + $$ = $1; + } + ; + +dhcpoption: + RAPID_COMMIT + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_RAPID_COMMIT, NULL, NULL); + /* no value */ + $$ = l; + } + | AUTHENTICATION AUTHNAME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_AUTHINFO, NULL, NULL); + l->ptr = $2; + $$ = l; + } + | IA_PD NUMBER + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_IA_PD, NULL, NULL); + l->num = $2; + $$ = l; + } + | IA_NA NUMBER + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_IA_NA, NULL, NULL); + l->num = $2; + $$ = l; + } + | SIP_SERVERS + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_SIP, NULL, NULL); + /* currently no value */ + $$ = l; + } + | SIP_NAME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_SIPNAME, NULL, NULL); + /* currently no value */ + $$ = l; + } + | DNS_SERVERS + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_DNS, NULL, NULL); + /* currently no value */ + $$ = l; + } + | DNS_NAME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_DNSNAME, NULL, NULL); + /* currently no value */ + $$ = l; + } + | NTP_SERVERS + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NTP, NULL, NULL); + /* currently no value */ + $$ = l; + } + | REFRESHTIME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_REFRESHTIME, NULL, NULL); + /* currently no value */ + $$ = l; + } + | NIS_SERVERS + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NIS, NULL, NULL); + /* currently no value */ + $$ = l; + } + | NIS_NAME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NISNAME, NULL, NULL); + /* currently no value */ + $$ = l; + } + | NISP_SERVERS + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NISP, NULL, NULL); + /* currently no value */ + $$ = l; + } + | NISP_NAME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_NISPNAME, NULL, NULL); + /* currently no value */ + $$ = l; + } + | BCMCS_SERVERS + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_BCMCS, NULL, NULL); + /* currently no value */ + $$ = l; + } + | BCMCS_NAME + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_BCMCSNAME, NULL, NULL); + /* currently no value */ + $$ = l; + } + ; + +rangeparam: + STRING TO STRING + { + struct dhcp6_range range0, *range; + + memset(&range0, 0, sizeof(range0)); + if (inet_pton(AF_INET6, $1, &range0.min) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + free($3); + return (-1); + } + if (inet_pton(AF_INET6, $3, &range0.max) != 1) { + yywarn("invalid IPv6 address: %s", $3); + free($1); + free($3); + return (-1); + } + free($1); + free($3); + + if ((range = malloc(sizeof(*range))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *range = range0; + + $$ = range; + } + ; + +addressparam: + STRING duration + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + free($1); + /* validate other parameters later */ + pconf0.plen = 128; /* XXX this field is ignored */ + if ($2 < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)$2; + pconf0.vltime = pconf0.pltime; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + $$ = pconf; + } + | STRING duration duration + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + free($1); + /* validate other parameters later */ + pconf0.plen = 128; /* XXX */ + if ($2 < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)$2; + if ($3 < 0) + pconf0.vltime = DHCP6_DURATION_INFINITE; + else + pconf0.vltime = (u_int32_t)$3; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + $$ = pconf; + } + ; + +prefixparam: + STRING SLASH NUMBER duration + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + free($1); + /* validate other parameters later */ + pconf0.plen = $3; + if ($4 < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)$4; + pconf0.vltime = pconf0.pltime; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + $$ = pconf; + } + | STRING SLASH NUMBER duration duration + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + free($1); + /* validate other parameters later */ + pconf0.plen = $3; + if ($4 < 0) + pconf0.pltime = DHCP6_DURATION_INFINITE; + else + pconf0.pltime = (u_int32_t)$4; + if ($5 < 0) + pconf0.vltime = DHCP6_DURATION_INFINITE; + else + pconf0.vltime = (u_int32_t)$5; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + $$ = pconf; + } + ; + +poolparam: + STRING duration + { + struct dhcp6_poolspec* pool; + + if ((pool = malloc(sizeof(*pool))) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + if ((pool->name = strdup($1)) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + free($1); + + /* validate other parameters later */ + if ($2 < 0) + pool->pltime = DHCP6_DURATION_INFINITE; + else + pool->pltime = (u_int32_t)$2; + pool->vltime = pool->pltime; + + $$ = pool; + } + | STRING duration duration + { + struct dhcp6_poolspec* pool; + + if ((pool = malloc(sizeof(*pool))) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + if ((pool->name = strdup($1)) == NULL) { + yywarn("can't allocate memory"); + free($1); + return (-1); + } + free($1); + + /* validate other parameters later */ + if ($2 < 0) + pool->pltime = DHCP6_DURATION_INFINITE; + else + pool->pltime = (u_int32_t)$2; + if ($3 < 0) + pool->vltime = DHCP6_DURATION_INFINITE; + else + pool->vltime = (u_int32_t)$3; + + $$ = pool; + } + ; + +duration: + INFINITY + { + $$ = -1; + } + | NUMBER + { + $$ = $1; + } + ; + +iapdconf_list: + { $$ = NULL; } + | iapdconf_list iapdconf + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +iapdconf: + prefix_interface { $$ = $1; } + | PREFIX prefixparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, IACONF_PREFIX, $2, NULL); + + $$ = l; + } + ; + +prefix_interface: + PREFIX_INTERFACE IFNAME BCL ifparams ECL EOS + { + struct cf_list *ifl; + + MAKE_CFLIST(ifl, IACONF_PIF, $2, $4); + $$ = ifl; + } + ; + +ifparams: + { $$ = NULL; } + | ifparams ifparam + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +ifparam: + SLA_ID NUMBER EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, IFPARAM_SLA_ID, NULL, NULL); + l->num = $2; + $$ = l; + } + | SLA_LEN NUMBER EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, IFPARAM_SLA_LEN, NULL, NULL); + l->num = $2; + $$ = l; + } + ; + +ianaconf_list: + { $$ = NULL; } + | ianaconf_list ianaconf + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +ianaconf: + ADDRESS addressparam EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, IACONF_ADDR, $2, NULL); + + $$ = l; + } + ; + +authparam_list: + { $$ = NULL; } + | authparam_list authparam + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +authparam: + PROTOCOL authproto EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_PROTO, NULL, NULL); + l->num = $2; + $$ = l; + } + | ALGORITHM authalg EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_ALG, NULL, NULL); + l->num = $2; + $$ = l; + } + | RDM authrdm EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_RDM, NULL, NULL); + l->num = $2; + $$ = l; + } + | KEY STRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, AUTHPARAM_KEY, NULL, NULL); + l->ptr = $2; + $$ = l; + } + ; + +authproto: + DELAYED { $$ = DHCP6_AUTHPROTO_DELAYED; } + | RECONFIG { $$ = DHCP6_AUTHPROTO_RECONFIG; } + ; + +authalg: + HMACMD5 { $$ = DHCP6_AUTHALG_HMACMD5; } + ; + +authrdm: + MONOCOUNTER { $$ = DHCP6_AUTHRDM_MONOCOUNTER; } + ; + +keyparam_list: + { $$ = NULL; } + | keyparam_list keyparam + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2->tail; + } + + $$ = head; + } + ; + +keyparam: + REALM QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_REALM, NULL, NULL); + l->ptr = $2; + $$ = l; + } + | KEYID NUMBER EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_KEYID, NULL, NULL); + l->num = $2; + $$ = l; + } + | SECRET QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_SECRET, NULL, NULL); + l->ptr = $2; + $$ = l; + } + | EXPIRE QSTRING EOS + { + struct cf_list *l; + + MAKE_CFLIST(l, KEYPARAM_EXPIRE, NULL, NULL); + l->ptr = $2; + $$ = l; + } + ; + +%% +/* supplement routines for configuration */ +static int +add_namelist(new, headp) + struct cf_namelist *new, **headp; +{ + struct cf_namelist *n; + + /* check for duplicated configuration */ + for (n = *headp; n; n = n->next) { + if (strcmp(n->name, new->name) == 0) { + yywarn("duplicated name: %s (ignored)", + new->name); + cleanup_namelist(new); + return (0); + } + } + + new->next = *headp; + *headp = new; + + return (0); +} + +/* free temporary resources */ +static void +cleanup() +{ + cleanup_namelist(iflist_head); + iflist_head = NULL; + cleanup_namelist(hostlist_head); + hostlist_head = NULL; + cleanup_namelist(iapdlist_head); + iapdlist_head = NULL; + cleanup_namelist(ianalist_head); + ianalist_head = NULL; + cleanup_namelist(authinfolist_head); + authinfolist_head = NULL; + cleanup_namelist(keylist_head); + keylist_head = NULL; + cleanup_namelist(addrpoollist_head); + addrpoollist_head = NULL; + + cleanup_cflist(cf_sip_list); + cf_sip_list = NULL; + cleanup_cflist(cf_sip_name_list); + cf_sip_name_list = NULL; + cleanup_cflist(cf_dns_list); + cf_dns_list = NULL; + cleanup_cflist(cf_dns_name_list); + cf_dns_name_list = NULL; + cleanup_cflist(cf_ntp_list); + cf_ntp_list = NULL; + cleanup_cflist(cf_nis_list); + cf_nis_list = NULL; + cleanup_cflist(cf_nis_name_list); + cf_nis_name_list = NULL; + cleanup_cflist(cf_nisp_list); + cf_nisp_list = NULL; + cleanup_cflist(cf_nisp_name_list); + cf_nisp_name_list = NULL; + cleanup_cflist(cf_bcmcs_list); + cf_bcmcs_list = NULL; + cleanup_cflist(cf_bcmcs_name_list); + cf_bcmcs_name_list = NULL; +} + +static void +cleanup_namelist(head) + struct cf_namelist *head; +{ + struct cf_namelist *ifp, *ifp_next; + + for (ifp = head; ifp; ifp = ifp_next) { + ifp_next = ifp->next; + cleanup_cflist(ifp->params); + free(ifp->name); + free(ifp); + } +} + +static void +cleanup_cflist(p) + struct cf_list *p; +{ + struct cf_list *n; + + if (p == NULL) + return; + + n = p->next; + if (p->type == DECL_ADDRESSPOOL) { + free(((struct dhcp6_poolspec *)p->ptr)->name); + } + if (p->ptr) + free(p->ptr); + if (p->list) + cleanup_cflist(p->list); + free(p); + + cleanup_cflist(n); +} + +#define config_fail() \ + do { cleanup(); configure_cleanup(); return (-1); } while(0) + +int +cf_post_config() +{ + if (configure_keys(keylist_head)) + config_fail(); + + if (configure_authinfo(authinfolist_head)) + config_fail(); + + if (configure_ia(iapdlist_head, IATYPE_PD)) + config_fail(); + + if (configure_ia(ianalist_head, IATYPE_NA)) + config_fail(); + + if (configure_pool(addrpoollist_head)) + config_fail(); + + if (configure_interface(iflist_head)) + config_fail(); + + if (configure_host(hostlist_head)) + config_fail(); + + if (configure_global_option()) + config_fail(); + + configure_commit(); + cleanup(); + return (0); +} +#undef config_fail + +void +cf_init() +{ + iflist_head = NULL; +} diff --git a/cftoken.c b/cftoken.c new file mode 100644 index 0000000..fcccc5a --- /dev/null +++ b/cftoken.c @@ -0,0 +1,2583 @@ +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $ + * $FreeBSD: src/usr.bin/lex/flex.skl,v 1.8 2004/01/06 19:03:44 nectar Exp $ + */ + +#if defined(__FreeBSD__) +#include +#else +#define __unused +#endif + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )) __unused; +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 81 +#define YY_END_OF_BUFFER 82 +static yyconst short int yy_accept[517] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 81, 71, 72, 81, 73, + 76, 80, 80, 75, 74, 77, 78, 79, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 2, 2, 2, 4, 4, 4, 74, + 80, 80, 42, 80, 80, 43, 49, 49, 49, 62, + 62, 62, 81, 6, 6, 6, 81, 71, 0, 70, + 73, 80, 74, 80, 74, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 17, 2, 2, 2, 2, 4, 4, + 4, 4, 8, 8, 42, 80, 42, 41, 40, 49, + 49, 49, 49, 62, 62, 62, 62, 0, 66, 6, + 6, 6, 6, 0, 69, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 53, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 52, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 7, 80, 80, 3, 80, 80, 80, 80, 80, + + 80, 80, 80, 80, 80, 80, 80, 80, 5, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 15, + 80, 80, 80, 80, 80, 80, 80, 38, 80, 80, + 80, 80, 80, 80, 80, 22, 21, 80, 80, 80, + 80, 80, 64, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 16, 80, 63, 80, 80, 80, 80, + 80, 80, 80, 80, 8, 80, 80, 80, 80, 80, + 80, 80, 80, 67, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 19, 80, + 10, 80, 80, 80, 80, 80, 12, 65, 80, 45, + + 80, 80, 59, 9, 80, 80, 80, 54, 80, 80, + 58, 80, 68, 80, 80, 80, 61, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 14, + 80, 46, 57, 80, 80, 80, 80, 80, 80, 56, + 39, 47, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 50, 80, 55, 80, 80, 80, 51, 80, + 80, 80, 80, 80, 1, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 13, + 80, 80, 80, 80, 80, 80, 80, 80, 11, 80, + 80, 80, 80, 80, 80, 80, 24, 80, 60, 80, + + 80, 80, 80, 27, 80, 80, 34, 80, 18, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 20, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 48, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 29, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 37, 80, 80, 31, 80, + 80, 44, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 28, 80, 80, 80, + 25, 80, 80, 80, 23, 80, 30, 80, 80, 32, + + 80, 80, 80, 80, 80, 80, 80, 80, 80, 35, + 80, 26, 80, 36, 33, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 5, 1, 1, 1, 1, 1, + 1, 1, 1, 6, 7, 8, 9, 10, 10, 10, + 10, 10, 11, 10, 10, 10, 10, 12, 13, 1, + 1, 1, 1, 1, 14, 15, 16, 17, 15, 15, + 18, 19, 18, 18, 18, 18, 20, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 1, 1, 1, 1, 18, 1, 21, 22, 23, 24, + + 25, 26, 27, 28, 29, 18, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 18, 45, 1, 46, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[47] = + { 0, + 1, 1, 2, 1, 1, 1, 3, 3, 1, 3, + 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 1, 1 + } ; + +static yyconst short int yy_base[533] = + { 0, + 0, 46, 74, 0, 113, 159, 0, 0, 205, 251, + 288, 0, 8, 25, 314, 360, 406, 452, 767, 766, + 498, 544, 765, 764, 767, 772, 764, 772, 761, 0, + 772, 0, 4, 772, 12, 772, 772, 772, 744, 0, + 740, 4, 719, 0, 4, 736, 726, 32, 724, 5, + 41, 44, 724, 0, 16, 66, 0, 53, 70, 583, + 74, 76, 117, 736, 732, 772, 0, 78, 121, 0, + 90, 125, 751, 0, 127, 131, 750, 751, 748, 772, + 0, 0, 133, 135, 139, 737, 726, 41, 710, 716, + 716, 714, 716, 709, 722, 704, 734, 733, 71, 695, + + 705, 699, 701, 696, 700, 123, 118, 701, 150, 695, + 97, 696, 709, 0, 0, 144, 164, 169, 0, 171, + 173, 177, 181, 717, 184, 186, 190, 0, 0, 0, + 192, 209, 213, 0, 215, 217, 221, 724, 772, 0, + 223, 225, 229, 723, 772, 710, 688, 690, 689, 694, + 698, 699, 698, 694, 688, 693, 676, 208, 693, 682, + 183, 687, 682, 676, 223, 702, 679, 676, 680, 666, + 677, 674, 0, 671, 667, 663, 659, 669, 660, 672, + 688, 687, 600, 10, 668, 655, 649, 665, 651, 644, + 658, 0, 649, 215, 0, 664, 660, 645, 642, 648, + + 643, 642, 166, 655, 128, 670, 638, 641, 0, 54, + 640, 641, 613, 604, 602, 605, 604, 585, 594, 0, + 580, 213, 617, 593, 595, 565, 573, 0, 568, 589, + 570, 559, 563, 555, 562, 0, 0, 547, 560, 554, + 550, 555, 0, 554, 545, 544, 552, 225, 551, 542, + 537, 530, 549, 0, 564, 0, 544, 531, 530, 528, + 527, 540, 540, 538, 550, 544, 549, 521, 519, 517, + 513, 518, 534, 0, 516, 528, 504, 512, 497, 514, + 513, 499, 492, 499, 493, 495, 503, 490, 0, 501, + 518, 490, 500, 493, 493, 481, 0, 0, 482, 0, + + 485, 506, 0, 509, 487, 485, 488, 482, 477, 494, + 0, 473, 0, 451, 455, 470, 0, 459, 470, 449, + 457, 451, 446, 453, 456, 453, 449, 455, 442, 0, + 439, 0, 0, 444, 446, 454, 439, 450, 453, 0, + 0, 0, 444, 447, 432, 441, 444, 447, 426, 441, + 441, 426, 0, 418, 0, 420, 423, 413, 0, 425, + 404, 400, 411, 408, 0, 416, 407, 402, 409, 412, + 399, 410, 395, 401, 400, 394, 396, 390, 403, 0, + 402, 393, 388, 417, 416, 389, 384, 382, 0, 393, + 384, 379, 396, 371, 372, 363, 392, 391, 0, 364, + + 224, 389, 388, 0, 357, 354, 0, 244, 0, 358, + 384, 352, 355, 367, 363, 352, 352, 245, 358, 0, + 359, 348, 348, 246, 355, 346, 346, 353, 344, 354, + 350, 338, 346, 334, 325, 0, 332, 321, 317, 322, + 327, 314, 329, 317, 324, 315, 323, 308, 323, 319, + 310, 300, 296, 0, 314, 309, 312, 299, 314, 309, + 308, 303, 294, 309, 304, 0, 290, 293, 0, 296, + 279, 0, 269, 273, 276, 271, 258, 256, 286, 254, + 258, 252, 282, 250, 254, 248, 0, 252, 246, 276, + 0, 249, 243, 273, 0, 258, 0, 245, 256, 0, + + 243, 243, 253, 241, 251, 246, 214, 213, 146, 0, + 131, 0, 115, 0, 0, 772, 643, 646, 649, 652, + 655, 658, 661, 664, 131, 102, 87, 18, 9, 667, + 4, 670 + } ; + +static yyconst short int yy_def[533] = + { 0, + 517, 517, 2, 3, 518, 518, 2, 2, 519, 519, + 2, 11, 2, 2, 520, 520, 521, 521, 2, 2, + 522, 522, 2, 2, 516, 516, 516, 516, 523, 524, + 516, 525, 525, 516, 525, 516, 516, 516, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 526, 526, 526, 527, 527, 527, 525, + 60, 525, 525, 525, 525, 516, 528, 528, 528, 529, + 529, 529, 530, 531, 531, 531, 532, 516, 523, 516, + 524, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 526, 526, 526, 526, 527, 527, + 527, 527, 525, 525, 525, 525, 525, 525, 525, 528, + 528, 528, 528, 529, 529, 529, 529, 530, 516, 531, + 531, 531, 531, 532, 516, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 0, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516 + } ; + +static yyconst short int yy_nxt[819] = + { 0, + 26, 27, 28, 29, 30, 31, 140, 33, 34, 35, + 35, 134, 36, 83, 83, 62, 224, 63, 63, 84, + 130, 85, 85, 87, 97, 116, 116, 98, 91, 225, + 88, 95, 62, 96, 63, 63, 99, 92, 105, 89, + 64, 106, 65, 93, 37, 38, 26, 27, 28, 29, + 30, 31, 66, 33, 34, 35, 35, 64, 36, 65, + 102, 107, 120, 120, 108, 109, 110, 148, 111, 66, + 103, 149, 112, 117, 113, 118, 118, 121, 251, 122, + 122, 82, 252, 124, 124, 125, 125, 131, 131, 119, + 37, 38, 39, 160, 40, 41, 161, 42, 43, 135, + + 135, 44, 45, 46, 115, 47, 48, 49, 50, 162, + 51, 52, 53, 26, 27, 28, 29, 30, 31, 179, + 55, 34, 56, 56, 126, 36, 127, 127, 132, 180, + 133, 133, 136, 82, 137, 137, 141, 141, 142, 515, + 143, 143, 83, 83, 83, 83, 84, 169, 85, 85, + 171, 246, 172, 116, 116, 514, 170, 37, 38, 26, + 27, 28, 29, 30, 31, 247, 55, 34, 56, 56, + 174, 36, 175, 116, 116, 176, 117, 513, 118, 118, + 120, 120, 120, 120, 121, 177, 122, 122, 84, 243, + 85, 85, 183, 125, 125, 125, 125, 126, 244, 127, + + 127, 131, 131, 37, 38, 26, 27, 28, 29, 30, + 31, 200, 58, 34, 59, 59, 201, 36, 131, 131, + 132, 234, 133, 133, 135, 135, 135, 135, 136, 205, + 137, 137, 141, 141, 141, 141, 142, 512, 143, 143, + 196, 263, 197, 264, 415, 511, 235, 416, 286, 37, + 38, 26, 27, 28, 29, 30, 31, 206, 58, 34, + 59, 59, 287, 36, 421, 431, 437, 422, 432, 438, + 510, 509, 508, 507, 506, 505, 504, 503, 502, 501, + 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, + 490, 489, 488, 487, 486, 37, 38, 60, 60, 485, + + 484, 61, 61, 61, 61, 483, 482, 481, 61, 61, + 61, 61, 61, 61, 26, 27, 28, 29, 30, 31, + 480, 68, 34, 69, 69, 479, 36, 478, 477, 476, + 475, 474, 473, 472, 471, 470, 469, 468, 467, 466, + 465, 464, 463, 462, 461, 460, 459, 458, 457, 456, + 455, 454, 453, 452, 451, 450, 449, 448, 37, 38, + 26, 27, 28, 29, 30, 31, 447, 68, 34, 69, + 69, 446, 36, 445, 444, 443, 442, 441, 440, 439, + 436, 435, 434, 433, 430, 429, 428, 427, 426, 425, + 424, 423, 420, 419, 418, 417, 414, 413, 412, 411, + + 410, 409, 408, 407, 37, 38, 26, 27, 28, 29, + 30, 31, 406, 71, 34, 72, 72, 405, 36, 404, + 403, 402, 401, 400, 399, 398, 397, 396, 395, 394, + 393, 392, 391, 390, 389, 388, 387, 386, 385, 384, + 383, 382, 381, 380, 379, 378, 377, 376, 375, 374, + 37, 38, 26, 27, 28, 29, 30, 31, 373, 71, + 34, 72, 72, 372, 36, 371, 370, 369, 368, 367, + 366, 365, 364, 363, 362, 361, 360, 359, 358, 357, + 356, 355, 354, 353, 352, 351, 350, 349, 348, 347, + 346, 345, 344, 343, 342, 341, 37, 38, 26, 27, + + 28, 29, 30, 31, 340, 75, 34, 76, 76, 339, + 36, 338, 337, 336, 335, 334, 333, 332, 331, 330, + 329, 328, 327, 326, 325, 324, 323, 322, 321, 320, + 319, 318, 317, 316, 315, 314, 313, 312, 311, 310, + 309, 308, 37, 38, 26, 27, 28, 29, 30, 31, + 307, 75, 34, 76, 76, 306, 36, 305, 304, 303, + 302, 183, 301, 300, 299, 298, 297, 296, 295, 294, + 293, 292, 291, 290, 289, 288, 285, 284, 283, 282, + 281, 280, 279, 278, 277, 276, 275, 274, 37, 38, + 84, 273, 123, 123, 272, 271, 124, 124, 124, 124, + + 270, 269, 268, 124, 124, 124, 124, 124, 124, 223, + 223, 267, 266, 223, 223, 223, 223, 262, 261, 260, + 223, 223, 223, 223, 223, 223, 265, 265, 259, 258, + 265, 265, 265, 265, 257, 256, 255, 265, 265, 265, + 265, 265, 265, 32, 32, 32, 54, 54, 54, 57, + 57, 57, 67, 67, 67, 70, 70, 70, 74, 74, + 74, 79, 79, 79, 81, 254, 81, 138, 138, 138, + 144, 144, 144, 253, 250, 249, 248, 245, 242, 241, + 240, 239, 238, 237, 236, 233, 232, 231, 230, 229, + 228, 227, 226, 222, 221, 220, 219, 218, 217, 216, + + 215, 214, 213, 212, 211, 210, 209, 208, 207, 204, + 203, 202, 199, 198, 195, 194, 193, 192, 191, 190, + 189, 188, 187, 186, 185, 184, 145, 139, 183, 182, + 181, 178, 173, 168, 167, 166, 165, 164, 163, 159, + 158, 157, 156, 155, 154, 153, 152, 151, 150, 147, + 146, 80, 78, 145, 139, 129, 128, 114, 104, 101, + 100, 94, 90, 86, 80, 78, 516, 77, 77, 73, + 73, 25, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516 + } ; + +static yyconst short int yy_chk[819] = + { 0, + 1, 1, 1, 1, 1, 1, 531, 1, 1, 1, + 1, 529, 1, 33, 33, 13, 184, 13, 13, 35, + 528, 35, 35, 40, 45, 55, 55, 45, 42, 184, + 40, 44, 14, 44, 14, 14, 45, 42, 50, 40, + 13, 50, 13, 42, 1, 1, 2, 2, 2, 2, + 2, 2, 13, 2, 2, 2, 2, 14, 2, 14, + 48, 51, 58, 58, 51, 51, 52, 88, 52, 14, + 48, 88, 52, 56, 52, 56, 56, 59, 210, 59, + 59, 61, 210, 61, 61, 62, 62, 68, 68, 527, + 2, 2, 3, 99, 3, 3, 99, 3, 3, 71, + + 71, 3, 3, 3, 526, 3, 3, 3, 3, 99, + 3, 3, 3, 5, 5, 5, 5, 5, 5, 111, + 5, 5, 5, 5, 63, 5, 63, 63, 69, 111, + 69, 69, 72, 525, 72, 72, 75, 75, 76, 513, + 76, 76, 83, 83, 84, 84, 85, 106, 85, 85, + 107, 205, 107, 116, 116, 511, 106, 5, 5, 6, + 6, 6, 6, 6, 6, 205, 6, 6, 6, 6, + 109, 6, 109, 117, 117, 109, 118, 509, 118, 118, + 120, 120, 121, 121, 122, 109, 122, 122, 123, 203, + 123, 123, 123, 125, 125, 126, 126, 127, 203, 127, + + 127, 131, 131, 6, 6, 9, 9, 9, 9, 9, + 9, 161, 9, 9, 9, 9, 161, 9, 132, 132, + 133, 194, 133, 133, 135, 135, 136, 136, 137, 165, + 137, 137, 141, 141, 142, 142, 143, 508, 143, 143, + 158, 222, 158, 222, 401, 507, 194, 401, 248, 9, + 9, 10, 10, 10, 10, 10, 10, 165, 10, 10, + 10, 10, 248, 10, 408, 418, 424, 408, 418, 424, + 506, 505, 504, 503, 502, 501, 499, 498, 496, 494, + 493, 492, 490, 489, 488, 486, 485, 484, 483, 482, + 481, 480, 479, 478, 477, 10, 10, 11, 11, 476, + + 475, 11, 11, 11, 11, 474, 473, 471, 11, 11, + 11, 11, 11, 11, 15, 15, 15, 15, 15, 15, + 470, 15, 15, 15, 15, 468, 15, 467, 465, 464, + 463, 462, 461, 460, 459, 458, 457, 456, 455, 453, + 452, 451, 450, 449, 448, 447, 446, 445, 444, 443, + 442, 441, 440, 439, 438, 437, 435, 434, 15, 15, + 16, 16, 16, 16, 16, 16, 433, 16, 16, 16, + 16, 432, 16, 431, 430, 429, 428, 427, 426, 425, + 423, 422, 421, 419, 417, 416, 415, 414, 413, 412, + 411, 410, 406, 405, 403, 402, 400, 398, 397, 396, + + 395, 394, 393, 392, 16, 16, 17, 17, 17, 17, + 17, 17, 391, 17, 17, 17, 17, 390, 17, 388, + 387, 386, 385, 384, 383, 382, 381, 379, 378, 377, + 376, 375, 374, 373, 372, 371, 370, 369, 368, 367, + 366, 364, 363, 362, 361, 360, 358, 357, 356, 354, + 17, 17, 18, 18, 18, 18, 18, 18, 352, 18, + 18, 18, 18, 351, 18, 350, 349, 348, 347, 346, + 345, 344, 343, 339, 338, 337, 336, 335, 334, 331, + 329, 328, 327, 326, 325, 324, 323, 322, 321, 320, + 319, 318, 316, 315, 314, 312, 18, 18, 21, 21, + + 21, 21, 21, 21, 310, 21, 21, 21, 21, 309, + 21, 308, 307, 306, 305, 304, 302, 301, 299, 296, + 295, 294, 293, 292, 291, 290, 288, 287, 286, 285, + 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, + 273, 272, 21, 21, 22, 22, 22, 22, 22, 22, + 271, 22, 22, 22, 22, 270, 22, 269, 268, 267, + 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, + 255, 253, 252, 251, 250, 249, 247, 246, 245, 244, + 242, 241, 240, 239, 238, 235, 234, 233, 22, 22, + 60, 232, 60, 60, 231, 230, 60, 60, 60, 60, + + 229, 227, 226, 60, 60, 60, 60, 60, 60, 183, + 183, 225, 224, 183, 183, 183, 183, 221, 219, 218, + 183, 183, 183, 183, 183, 183, 223, 223, 217, 216, + 223, 223, 223, 223, 215, 214, 213, 223, 223, 223, + 223, 223, 223, 517, 517, 517, 518, 518, 518, 519, + 519, 519, 520, 520, 520, 521, 521, 521, 522, 522, + 522, 523, 523, 523, 524, 212, 524, 530, 530, 530, + 532, 532, 532, 211, 208, 207, 206, 204, 202, 201, + 200, 199, 198, 197, 196, 193, 191, 190, 189, 188, + 187, 186, 185, 182, 181, 180, 179, 178, 177, 176, + + 175, 174, 172, 171, 170, 169, 168, 167, 166, 164, + 163, 162, 160, 159, 157, 156, 155, 154, 153, 152, + 151, 150, 149, 148, 147, 146, 144, 138, 124, 113, + 112, 110, 108, 105, 104, 103, 102, 101, 100, 98, + 97, 96, 95, 94, 93, 92, 91, 90, 89, 87, + 86, 79, 78, 77, 73, 65, 64, 53, 49, 47, + 46, 43, 41, 39, 29, 27, 25, 24, 23, 20, + 19, 516, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + + 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 516, 516, 516, 516, 516, 516, 516 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "cftoken.l" +#define INITIAL 0 +/* $KAME: cftoken.l,v 1.35 2005/01/12 06:06:11 suz Exp $ */ +#line 4 "cftoken.l" +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#else +#include +#endif + +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "y.tab.h" + +#define YY_NO_UNPUT 1 + +char *configfilename; +int lineno = 1; + + +/* Recursion limit for includes */ +#define MAX_INCLUDE_DEPTH 10 + +static struct include_stack { + char *path; + YY_BUFFER_STATE state; + int lineno; +} incstack[MAX_INCLUDE_DEPTH]; +int incstackp = 0; + + +static int yy_first_time = 1; +static int yyerrorcount = 0; + +#ifndef NOCONFIG_DEBUG +#define YYDEBUG 1 + +int cfdebug = 1; +#else +int cfdebug = 0; +#endif + +extern int yyparse __P((void)); +extern int cf_post_config __P((void)); + +static void cfdebug_print __P((char *, char *, int)); + +#define DP(str) if (cfdebug) cfdebug_print(str, yytext, yyleng) +#define DECHO if (cfdebug) cfdebug_print(NULL, yytext, yyleng); +#define DNOECHO if (cfdebug) cfdebug_print(NULL, "****", yyleng); +/* abbreviations */ +#define S_CNF 1 + +#define S_IFACE 2 + +#define S_PREF 3 + +#define S_HOST 4 + +#define S_DUID 5 + +#define S_IA 6 + +#define S_AUTH 7 + +#define S_KEY 8 + +#define S_SECRET 9 + +#define S_ADDRPOOL 10 + +#define S_INCL 11 + +#line 835 "lex.yy.c" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 124 "cftoken.l" + + + if (yy_first_time) { + BEGIN S_CNF; + yy_first_time = 0; + } + + /* interface configuration */ +#line 995 "lex.yy.c" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 517 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 772 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 132 "cftoken.l" +{ DECHO; BEGIN S_IFACE; return (INTERFACE); } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 133 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (IFNAME); +} + YY_BREAK +/* host configuration */ +case 3: +YY_RULE_SETUP +#line 141 "cftoken.l" +{ DECHO; BEGIN S_HOST; return (HOST); } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 142 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (HOSTNAME); +} + YY_BREAK +/* address pool configuration */ +case 5: +YY_RULE_SETUP +#line 150 "cftoken.l" +{ DECHO; BEGIN S_ADDRPOOL; return (ADDRPOOL); } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 152 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (POOLNAME); +} + YY_BREAK +case 7: +YY_RULE_SETUP +#line 159 "cftoken.l" +{ DECHO; BEGIN S_DUID; return (DUID); } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 160 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (DUID_ID); +} + YY_BREAK +case 9: +YY_RULE_SETUP +#line 167 "cftoken.l" +{ DECHO; return (ADDRESS); } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 169 "cftoken.l" +{ DECHO; return (PREFIX); } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 171 "cftoken.l" +{ DECHO; return (PREFERENCE); } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 173 "cftoken.l" +{ DECHO; return (SCRIPT); } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 175 "cftoken.l" +{ DECHO; return (DELAYEDKEY); } + YY_BREAK +/* request */ +case 14: +YY_RULE_SETUP +#line 178 "cftoken.l" +{ DECHO; return (REQUEST); } + YY_BREAK +/* send */ +case 15: +YY_RULE_SETUP +#line 181 "cftoken.l" +{ DECHO; return (SEND); } + YY_BREAK +/* range */ +case 16: +YY_RULE_SETUP +#line 184 "cftoken.l" +{ DECHO; return (RANGE); } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 185 "cftoken.l" +{ DECHO; return (TO); } + YY_BREAK +/* address-pool */ +case 18: +YY_RULE_SETUP +#line 188 "cftoken.l" +{ DECHO; return (ADDRESS_POOL); } + YY_BREAK +/* DHCP options */ +case 19: +YY_RULE_SETUP +#line 191 "cftoken.l" +{ DECHO; return (OPTION); } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 193 "cftoken.l" +{ DECHO; return (RAPID_COMMIT); } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 194 "cftoken.l" +{ DECHO; return (IA_PD); } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 195 "cftoken.l" +{ DECHO; return (IA_NA); } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 196 "cftoken.l" +{ DECHO; return (DNS_SERVERS); } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 197 "cftoken.l" +{ DECHO; return (DNS_NAME); } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 198 "cftoken.l" +{ DECHO; return (SIP_SERVERS); } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 199 "cftoken.l" +{ DECHO; return (SIP_NAME); } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 200 "cftoken.l" +{ DECHO; return (NTP_SERVERS); } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 201 "cftoken.l" +{ DECHO; return (NIS_SERVERS); } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 202 "cftoken.l" +{ DECHO; return (NIS_NAME); } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 203 "cftoken.l" +{ DECHO; return (NISP_SERVERS); } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 204 "cftoken.l" +{ DECHO; return (NISP_NAME); } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 205 "cftoken.l" +{ DECHO; return (BCMCS_SERVERS); } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 206 "cftoken.l" +{ DECHO; return (BCMCS_NAME); } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 207 "cftoken.l" +{ DECHO; return (REFRESHTIME); } + YY_BREAK +/* provided for a backward compatibility to WIDE-DHCPv6 before Oct 1 2006 */ +case 35: +YY_RULE_SETUP +#line 210 "cftoken.l" +{ DECHO; return (NIS_NAME); } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 211 "cftoken.l" +{ DECHO; return (NISP_NAME); } + YY_BREAK +/* generic options */ +case 37: +YY_RULE_SETUP +#line 214 "cftoken.l" +{ DECHO; return (INFO_ONLY); } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 216 "cftoken.l" +{ DECHO; return (ALLOW); } + YY_BREAK +/* identity association */ +case 39: +YY_RULE_SETUP +#line 219 "cftoken.l" +{ DECHO; BEGIN S_IA; return(ID_ASSOC); } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 220 "cftoken.l" +{ DECHO; return(IA_PD); } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 221 "cftoken.l" +{ DECHO; return(IA_NA); } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 222 "cftoken.l" +{ DECHO; yylval.str = strdup(yytext); return(IAID); } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 223 "cftoken.l" +{ DP("begin of closure"); BEGIN S_CNF; return (BCL); } + YY_BREAK +/* + * interface parameters for delegated prefix configuration. + * when lex reads an interface name, the state will be back to + * S_CNF. + */ +case 44: +YY_RULE_SETUP +#line 230 "cftoken.l" +{ DECHO; BEGIN S_IFACE; return (PREFIX_INTERFACE); } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 231 "cftoken.l" +{ DECHO; return (SLA_ID); } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 232 "cftoken.l" +{ DECHO; return (SLA_LEN); } + YY_BREAK +/* duration */ +case 47: +YY_RULE_SETUP +#line 235 "cftoken.l" +{ DECHO; return (INFINITY); } + YY_BREAK +/* authentication option */ +case 48: +YY_RULE_SETUP +#line 238 "cftoken.l" +{ DECHO; BEGIN S_AUTH; return (AUTHENTICATION); } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 239 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (AUTHNAME); +} + YY_BREAK +/* authentication parameters */ +case 50: +YY_RULE_SETUP +#line 247 "cftoken.l" +{ DECHO; return (PROTOCOL); }; + YY_BREAK +case 51: +YY_RULE_SETUP +#line 248 "cftoken.l" +{ DECHO; return (ALGORITHM); }; + YY_BREAK +case 52: +YY_RULE_SETUP +#line 249 "cftoken.l" +{ DECHO; return (RDM); }; + YY_BREAK +case 53: +YY_RULE_SETUP +#line 250 "cftoken.l" +{ DECHO; return (KEY); }; + YY_BREAK +/* authentication protocols */ +case 54: +YY_RULE_SETUP +#line 253 "cftoken.l" +{ DECHO; return (DELAYED); }; + YY_BREAK +case 55: +YY_RULE_SETUP +#line 254 "cftoken.l" +{ DECHO; return (RECONFIG); }; + YY_BREAK +/* authentication algorithms */ +case 56: +YY_RULE_SETUP +#line 257 "cftoken.l" +{ DECHO; return (HMACMD5); }; + YY_BREAK +case 57: +YY_RULE_SETUP +#line 258 "cftoken.l" +{ DECHO; return (HMACMD5); }; + YY_BREAK +case 58: +YY_RULE_SETUP +#line 259 "cftoken.l" +{ DECHO; return (HMACMD5); }; + YY_BREAK +case 59: +YY_RULE_SETUP +#line 260 "cftoken.l" +{ DECHO; return (HMACMD5); }; + YY_BREAK +/* authentication RDM */ +case 60: +YY_RULE_SETUP +#line 263 "cftoken.l" +{ DECHO; return (MONOCOUNTER); }; + YY_BREAK +/* secret keys */ +case 61: +YY_RULE_SETUP +#line 266 "cftoken.l" +{ DECHO; BEGIN S_KEY; return (KEYINFO); } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 267 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (KEYNAME); +} + YY_BREAK +/* key parameters */ +case 63: +YY_RULE_SETUP +#line 275 "cftoken.l" +{ DECHO; return (REALM); } + YY_BREAK +case 64: +YY_RULE_SETUP +#line 276 "cftoken.l" +{ DECHO; return (KEYID); } + YY_BREAK +case 65: +YY_RULE_SETUP +#line 277 "cftoken.l" +{ DECHO; BEGIN S_SECRET; return (SECRET); } + YY_BREAK +case 66: +YY_RULE_SETUP +#line 278 "cftoken.l" +{ + DNOECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (QSTRING); +} + YY_BREAK +case 67: +YY_RULE_SETUP +#line 284 "cftoken.l" +{ DECHO; return (EXPIRE); } + YY_BREAK +/* include */ +case 68: +YY_RULE_SETUP +#line 287 "cftoken.l" +{ DECHO; BEGIN S_INCL; return (INCLUDE); } + YY_BREAK +case 69: +YY_RULE_SETUP +#line 288 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (QSTRING); +} + YY_BREAK +/* quoted string */ +case 70: +YY_RULE_SETUP +#line 296 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + return (QSTRING); + } + YY_BREAK +/* misc */ +case 71: +YY_RULE_SETUP +#line 303 "cftoken.l" +{ ; } + YY_BREAK +case 72: +YY_RULE_SETUP +#line 304 "cftoken.l" +{ lineno++; } + YY_BREAK +case 73: +YY_RULE_SETUP +#line 305 "cftoken.l" +{ DP("comment"); } + YY_BREAK +case 74: +YY_RULE_SETUP +#line 306 "cftoken.l" +{ + DECHO; + yylval.num = strtoll(yytext, NULL, 10); + return (NUMBER); +} + YY_BREAK +case 75: +YY_RULE_SETUP +#line 311 "cftoken.l" +{ DECHO; return (SLASH); } + YY_BREAK +case 76: +YY_RULE_SETUP +#line 312 "cftoken.l" +{ DECHO; return (COMMA); } + YY_BREAK +case 77: +YY_RULE_SETUP +#line 313 "cftoken.l" +{ DP("end of sentence"); return (EOS); } + YY_BREAK +case 78: +YY_RULE_SETUP +#line 314 "cftoken.l" +{ DP("begin of closure"); return (BCL); } + YY_BREAK +case 79: +YY_RULE_SETUP +#line 315 "cftoken.l" +{ DP("end of closure"); return (ECL); } + YY_BREAK +/* generic string */ +case 80: +YY_RULE_SETUP +#line 318 "cftoken.l" +{ + DECHO; + yylval.str = strdup(yytext); + return (STRING); + } + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(S_CNF): +case YY_STATE_EOF(S_IFACE): +case YY_STATE_EOF(S_PREF): +case YY_STATE_EOF(S_HOST): +case YY_STATE_EOF(S_DUID): +case YY_STATE_EOF(S_IA): +case YY_STATE_EOF(S_AUTH): +case YY_STATE_EOF(S_KEY): +case YY_STATE_EOF(S_SECRET): +case YY_STATE_EOF(S_ADDRPOOL): +case YY_STATE_EOF(S_INCL): +#line 324 "cftoken.l" +{ + if (--incstackp < 0) + yyterminate(); + else { + yy_delete_buffer(YY_CURRENT_BUFFER); + free(incstack[incstackp + 1].path); + configfilename = incstack[incstackp].path; + lineno = incstack[incstackp].lineno; + yy_switch_to_buffer(incstack[incstackp].state); + } + } + YY_BREAK +case 81: +YY_RULE_SETUP +#line 336 "cftoken.l" +ECHO; + YY_BREAK +#line 1587 "lex.yy.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 517 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 517 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 516); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 336 "cftoken.l" + +static void +cfdebug_print(w, t, l) + char *w, *t; + int l; +{ + if (w) { + dprintf(LOG_DEBUG, FNAME, + "<%d>%s [%s] (%d)", yy_start, w, t, l); + } else { + dprintf(LOG_DEBUG, FNAME, + "<%d>[%s] (%d)", yy_start, t, l); + } +} + +static void +yyerror0(int level, char *s, va_list ap) +{ + char ebuf[BUFSIZ], *bp, *ep; + + bp = ebuf; + ep = ebuf + sizeof(ebuf); + bp += snprintf(bp, ep - bp, "%s %d: ", configfilename, lineno); + if (bp < ep) + bp += vsnprintf(bp, ep - bp, s, ap); + + dprintf(level, FNAME, ebuf); +} + +void +yyerror(char *s, ...) +{ + va_list ap; +#ifdef HAVE_STDARG_H + va_start(ap, s); +#else + va_start(ap); +#endif + yyerror0(LOG_ERR, s, ap); + va_end(ap); + yyerrorcount++; +} + +void +yywarn(char *s, ...) +{ + va_list ap; +#ifdef HAVE_STDARG_H + va_start(ap, s); +#else + va_start(ap); +#endif + yyerror0(LOG_WARNING, s, ap); + va_end(ap); +} + +int +cfswitch_buffer(incl) + char *incl; +{ + char *path = qstrdup(incl); + FILE *fp; + + if (incstackp >= MAX_INCLUDE_DEPTH) { + dprintf(LOG_ERR, FNAME, "cfparse: includes nested too deeply"); + return (-1); + } + incstack[incstackp].path = configfilename; + incstack[incstackp].state = YY_CURRENT_BUFFER; + incstack[incstackp].lineno = lineno; + + fp = fopen(path, "r"); + if (fp == NULL) { + dprintf(LOG_ERR, FNAME, "cfparse: fopen(%s): %s", + path, strerror(errno)); + if (errno == ENOENT) + return (0); + return (-1); + } + incstackp++; + configfilename = path; + lineno = 1; + yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + + BEGIN(S_CNF); + + return (0); +} + +int +cfparse(conf) + char *conf; +{ + configfilename = conf; + if ((yyin = fopen(configfilename, "r")) == NULL) { + dprintf(LOG_ERR, FNAME, "cfparse: fopen(%s): %s", + configfilename, strerror(errno)); + if (errno == ENOENT) + return (0); + return (-1); + } + + if (yyparse() || yyerrorcount) { + if (yyerrorcount) { + yyerror("fatal parse failure: exiting (%d errors)", + yyerrorcount); + } else + yyerror("fatal parse failure: exiting"); + return (-1); + } + + return (cf_post_config()); +} diff --git a/cftoken.l b/cftoken.l new file mode 100644 index 0000000..ece7fbb --- /dev/null +++ b/cftoken.l @@ -0,0 +1,448 @@ +/* $KAME: cftoken.l,v 1.35 2005/01/12 06:06:11 suz Exp $ */ + +%{ +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#else +#include +#endif + +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "y.tab.h" + +#define YY_NO_UNPUT 1 + +char *configfilename; +int lineno = 1; + + +/* Recursion limit for includes */ +#define MAX_INCLUDE_DEPTH 10 + +static struct include_stack { + char *path; + YY_BUFFER_STATE state; + int lineno; +} incstack[MAX_INCLUDE_DEPTH]; +int incstackp = 0; + + +static int yy_first_time = 1; +static int yyerrorcount = 0; + +#ifndef NOCONFIG_DEBUG +#define YYDEBUG 1 + +int cfdebug = 1; +#else +int cfdebug = 0; +#endif + +extern int yyparse __P((void)); +extern int cf_post_config __P((void)); + +static void cfdebug_print __P((char *, char *, int)); + +#define DP(str) if (cfdebug) cfdebug_print(str, yytext, yyleng) +#define DECHO if (cfdebug) cfdebug_print(NULL, yytext, yyleng); +#define DNOECHO if (cfdebug) cfdebug_print(NULL, "****", yyleng); +%} + +/* abbreviations */ +nl \n +ws [ \t]+ +comma , +comment \#.* +semi \; +quotedstring \"[^\"]*\" +string [a-zA-Z0-9:\._\-][a-zA-Z0-9:\._\-]* +digit [0-9] +integer {digit}+ +number {integer}|({digit}*\.{integer}) +hexdigit [0-9A-Fa-f] +hexpair {hexdigit}{hexdigit} +hexstring 0[xX]{hexpair}+ +duid {hexpair}(:{hexpair})* +ifname {string} +slash \/ +bcl \{ +ecl \} + +%s S_CNF +%s S_IFACE +%s S_PREF +%s S_HOST +%s S_DUID +%s S_IA +%s S_AUTH +%s S_KEY +%s S_SECRET +%s S_ADDRPOOL +%s S_INCL + +%% +%{ + if (yy_first_time) { + BEGIN S_CNF; + yy_first_time = 0; + } +%} + /* interface configuration */ +interface { DECHO; BEGIN S_IFACE; return (INTERFACE); } +{ifname} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (IFNAME); +} + + /* host configuration */ +host { DECHO; BEGIN S_HOST; return (HOST); } +{string} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (HOSTNAME); +} + + /* address pool configuration */ +pool { DECHO; BEGIN S_ADDRPOOL; return (ADDRPOOL); } + +{string} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (POOLNAME); +} + +duid { DECHO; BEGIN S_DUID; return (DUID); } +{duid} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (DUID_ID); +} + +address { DECHO; return (ADDRESS); } + +prefix { DECHO; return (PREFIX); } + +preference { DECHO; return (PREFERENCE); } + +script { DECHO; return (SCRIPT); } + +delayedkey { DECHO; return (DELAYEDKEY); } + + /* request */ +request { DECHO; return (REQUEST); } + + /* send */ +send { DECHO; return (SEND); } + + /* range */ +range { DECHO; return (RANGE); } +to { DECHO; return (TO); } + + /* address-pool */ +address-pool { DECHO; return (ADDRESS_POOL); } + + /* DHCP options */ +option { DECHO; return (OPTION); } + +rapid-commit { DECHO; return (RAPID_COMMIT); } +ia-pd { DECHO; return (IA_PD); } +ia-na { DECHO; return (IA_NA); } +domain-name-servers { DECHO; return (DNS_SERVERS); } +domain-name { DECHO; return (DNS_NAME); } +sip-server-address { DECHO; return (SIP_SERVERS); } +sip-server-domain-name { DECHO; return (SIP_NAME); } +ntp-servers { DECHO; return (NTP_SERVERS); } +nis-server-address { DECHO; return (NIS_SERVERS); } +nis-domain-name { DECHO; return (NIS_NAME); } +nisp-server-address { DECHO; return (NISP_SERVERS); } +nisp-domain-name { DECHO; return (NISP_NAME); } +bcmcs-server-address { DECHO; return (BCMCS_SERVERS); } +bcmcs-server-domain-name { DECHO; return (BCMCS_NAME); } +refreshtime { DECHO; return (REFRESHTIME); } + + /* provided for a backward compatibility to WIDE-DHCPv6 before Oct 1 2006 */ +nis-server-domain-name { DECHO; return (NIS_NAME); } +nisp-server-domain-name { DECHO; return (NISP_NAME); } + + /* generic options */ +information-only { DECHO; return (INFO_ONLY); } + +allow { DECHO; return (ALLOW); } + + /* identity association */ +id-assoc { DECHO; BEGIN S_IA; return(ID_ASSOC); } +pd { DECHO; return(IA_PD); } +na { DECHO; return(IA_NA); } +{number} { DECHO; yylval.str = strdup(yytext); return(IAID); } +{bcl} { DP("begin of closure"); BEGIN S_CNF; return (BCL); } + + /* + * interface parameters for delegated prefix configuration. + * when lex reads an interface name, the state will be back to + * S_CNF. + */ +prefix-interface { DECHO; BEGIN S_IFACE; return (PREFIX_INTERFACE); } +sla-id { DECHO; return (SLA_ID); } +sla-len { DECHO; return (SLA_LEN); } + + /* duration */ +infinity { DECHO; return (INFINITY); } + + /* authentication option */ +authentication { DECHO; BEGIN S_AUTH; return (AUTHENTICATION); } +{string} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (AUTHNAME); +} + + /* authentication parameters */ +protocol { DECHO; return (PROTOCOL); }; +algorithm { DECHO; return (ALGORITHM); }; +rdm { DECHO; return (RDM); }; +key { DECHO; return (KEY); }; + + /* authentication protocols */ +delayed { DECHO; return (DELAYED); }; +reconfig { DECHO; return (RECONFIG); }; + + /* authentication algorithms */ +hmac-md5 { DECHO; return (HMACMD5); }; +HMAC-MD5 { DECHO; return (HMACMD5); }; +hmacmd5 { DECHO; return (HMACMD5); }; +HMACMD5 { DECHO; return (HMACMD5); }; + + /* authentication RDM */ +monocounter { DECHO; return (MONOCOUNTER); }; + + /* secret keys */ +keyinfo { DECHO; BEGIN S_KEY; return (KEYINFO); } +{string} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (KEYNAME); +} + + /* key parameters */ +realm { DECHO; return (REALM); } +keyid { DECHO; return (KEYID); } +secret { DECHO; BEGIN S_SECRET; return (SECRET); } +{quotedstring} { + DNOECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (QSTRING); +} +expire { DECHO; return (EXPIRE); } + + /* include */ +include { DECHO; BEGIN S_INCL; return (INCLUDE); } +{quotedstring} { + DECHO; + yylval.str = strdup(yytext); + BEGIN S_CNF; + return (QSTRING); +} + + /* quoted string */ +{quotedstring} { + DECHO; + yylval.str = strdup(yytext); + return (QSTRING); + } + + /* misc */ +{ws} { ; } +{nl} { lineno++; } +{comment} { DP("comment"); } +{number} { + DECHO; + yylval.num = strtoll(yytext, NULL, 10); + return (NUMBER); +} +{slash} { DECHO; return (SLASH); } +{comma} { DECHO; return (COMMA); } +{semi} { DP("end of sentence"); return (EOS); } +{bcl} { DP("begin of closure"); return (BCL); } +{ecl} { DP("end of closure"); return (ECL); } + + /* generic string */ +{string} { + DECHO; + yylval.str = strdup(yytext); + return (STRING); + } + +<> { + if (--incstackp < 0) + yyterminate(); + else { + yy_delete_buffer(YY_CURRENT_BUFFER); + free(incstack[incstackp + 1].path); + configfilename = incstack[incstackp].path; + lineno = incstack[incstackp].lineno; + yy_switch_to_buffer(incstack[incstackp].state); + } + } + +%% +static void +cfdebug_print(w, t, l) + char *w, *t; + int l; +{ + if (w) { + dprintf(LOG_DEBUG, FNAME, + "<%d>%s [%s] (%d)", yy_start, w, t, l); + } else { + dprintf(LOG_DEBUG, FNAME, + "<%d>[%s] (%d)", yy_start, t, l); + } +} + +static void +yyerror0(int level, char *s, va_list ap) +{ + char ebuf[BUFSIZ], *bp, *ep; + + bp = ebuf; + ep = ebuf + sizeof(ebuf); + bp += snprintf(bp, ep - bp, "%s %d: ", configfilename, lineno); + if (bp < ep) + bp += vsnprintf(bp, ep - bp, s, ap); + + dprintf(level, FNAME, ebuf); +} + +void +yyerror(char *s, ...) +{ + va_list ap; +#ifdef HAVE_STDARG_H + va_start(ap, s); +#else + va_start(ap); +#endif + yyerror0(LOG_ERR, s, ap); + va_end(ap); + yyerrorcount++; +} + +void +yywarn(char *s, ...) +{ + va_list ap; +#ifdef HAVE_STDARG_H + va_start(ap, s); +#else + va_start(ap); +#endif + yyerror0(LOG_WARNING, s, ap); + va_end(ap); +} + +int +cfswitch_buffer(incl) + char *incl; +{ + char *path = qstrdup(incl); + FILE *fp; + + if (incstackp >= MAX_INCLUDE_DEPTH) { + dprintf(LOG_ERR, FNAME, "cfparse: includes nested too deeply"); + return (-1); + } + incstack[incstackp].path = configfilename; + incstack[incstackp].state = YY_CURRENT_BUFFER; + incstack[incstackp].lineno = lineno; + + fp = fopen(path, "r"); + if (fp == NULL) { + dprintf(LOG_ERR, FNAME, "cfparse: fopen(%s): %s", + path, strerror(errno)); + if (errno == ENOENT) + return (0); + return (-1); + } + incstackp++; + configfilename = path; + lineno = 1; + yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + + BEGIN(S_CNF); + + return (0); +} + +int +cfparse(conf) + char *conf; +{ + configfilename = conf; + if ((yyin = fopen(configfilename, "r")) == NULL) { + dprintf(LOG_ERR, FNAME, "cfparse: fopen(%s): %s", + configfilename, strerror(errno)); + if (errno == ENOENT) + return (0); + return (-1); + } + + if (yyparse() || yyerrorcount) { + if (yyerrorcount) { + yyerror("fatal parse failure: exiting (%d errors)", + yyerrorcount); + } else + yyerror("fatal parse failure: exiting"); + return (-1); + } + + return (cf_post_config()); +} diff --git a/common.c b/common.c new file mode 100644 index 0000000..f35399c --- /dev/null +++ b/common.c @@ -0,0 +1,3388 @@ +/* $KAME: common.c,v 1.129 2005/09/16 11:30:13 suz Exp $ */ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include +#include +#ifdef __KAME__ +#include +#ifdef __FreeBSD__ +#include +#endif +#include +#endif +#ifdef __linux__ +#include +#endif +#include +#ifdef __sun__ +#include +#include +#include +#include +#include +#endif + +#ifdef __KAME__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __linux__ +/* from /usr/include/linux/ipv6.h */ + +struct in6_ifreq { + struct in6_addr ifr6_addr; + u_int32_t ifr6_prefixlen; + unsigned int ifr6_ifindex; +}; +#endif + +#define MAXDNAME 255 + +int foreground; +int debug_thresh; + +static int dhcp6_count_list __P((struct dhcp6_list *)); +static int in6_matchflags __P((struct sockaddr *, char *, int)); +static ssize_t dnsencode __P((const char *, char *, size_t)); +static char *dnsdecode __P((u_char **, u_char *, char *, size_t)); +static int copyout_option __P((char *, char *, struct dhcp6_listval *)); +static int copyin_option __P((int, struct dhcp6opt *, struct dhcp6opt *, + struct dhcp6_list *)); +static int copy_option __P((u_int16_t, u_int16_t, void *, struct dhcp6opt **, + struct dhcp6opt *, int *)); +static ssize_t gethwid __P((char *, int, const char *, u_int16_t *)); +static char *sprint_uint64 __P((char *, int, u_int64_t)); +static char *sprint_auth __P((struct dhcp6_optinfo *)); + +int +dhcp6_copy_list(dst, src) + struct dhcp6_list *dst, *src; +{ + struct dhcp6_listval *ent; + + for (ent = TAILQ_FIRST(src); ent; ent = TAILQ_NEXT(ent, link)) { + if (dhcp6_add_listval(dst, ent->type, + &ent->uv, &ent->sublist) == NULL) + goto fail; + } + + return (0); + + fail: + dhcp6_clear_list(dst); + return (-1); +} + +void +dhcp6_move_list(dst, src) + struct dhcp6_list *dst, *src; +{ + struct dhcp6_listval *v; + + while ((v = TAILQ_FIRST(src)) != NULL) { + TAILQ_REMOVE(src, v, link); + TAILQ_INSERT_TAIL(dst, v, link); + } +} + +void +dhcp6_clear_list(head) + struct dhcp6_list *head; +{ + struct dhcp6_listval *v; + + while ((v = TAILQ_FIRST(head)) != NULL) { + TAILQ_REMOVE(head, v, link); + dhcp6_clear_listval(v); + } + + return; +} + +static int +dhcp6_count_list(head) + struct dhcp6_list *head; +{ + struct dhcp6_listval *v; + int i; + + for (i = 0, v = TAILQ_FIRST(head); v; v = TAILQ_NEXT(v, link)) + i++; + + return (i); +} + +void +dhcp6_clear_listval(lv) + struct dhcp6_listval *lv; +{ + dhcp6_clear_list(&lv->sublist); + switch (lv->type) { + case DHCP6_LISTVAL_VBUF: + dhcp6_vbuf_free(&lv->val_vbuf); + break; + default: /* nothing to do */ + break; + } + free(lv); +} + +/* + * Note: this function only searches for the first entry that matches + * VAL. It also does not care about sublists. + */ +struct dhcp6_listval * +dhcp6_find_listval(head, type, val, option) + struct dhcp6_list *head; + dhcp6_listval_type_t type; + void *val; + int option; +{ + struct dhcp6_listval *lv; + + for (lv = TAILQ_FIRST(head); lv; lv = TAILQ_NEXT(lv, link)) { + if (lv->type != type) + continue; + + switch(type) { + case DHCP6_LISTVAL_NUM: + if (lv->val_num == *(int *)val) + return (lv); + break; + case DHCP6_LISTVAL_STCODE: + if (lv->val_num16 == *(u_int16_t *)val) + return (lv); + break; + case DHCP6_LISTVAL_ADDR6: + if (IN6_ARE_ADDR_EQUAL(&lv->val_addr6, + (struct in6_addr *)val)) { + return (lv); + } + break; + case DHCP6_LISTVAL_PREFIX6: + if ((option & MATCHLIST_PREFIXLEN) && + lv->val_prefix6.plen == + ((struct dhcp6_prefix *)val)->plen) { + return (lv); + } else if (IN6_ARE_ADDR_EQUAL(&lv->val_prefix6.addr, + &((struct dhcp6_prefix *)val)->addr) && + lv->val_prefix6.plen == + ((struct dhcp6_prefix *)val)->plen) { + return (lv); + } + break; + case DHCP6_LISTVAL_STATEFULADDR6: + if (IN6_ARE_ADDR_EQUAL(&lv->val_statefuladdr6.addr, + &((struct dhcp6_prefix *)val)->addr)) { + return (lv); + } + break; + case DHCP6_LISTVAL_IAPD: + case DHCP6_LISTVAL_IANA: + if (lv->val_ia.iaid == + ((struct dhcp6_ia *)val)->iaid) { + return (lv); + } + break; + case DHCP6_LISTVAL_VBUF: + if (dhcp6_vbuf_cmp(&lv->val_vbuf, + (struct dhcp6_vbuf *)val) == 0) { + return (lv); + } + break; + } + } + + return (NULL); +} + +struct dhcp6_listval * +dhcp6_add_listval(head, type, val, sublist) + struct dhcp6_list *head, *sublist; + dhcp6_listval_type_t type; + void *val; +{ + struct dhcp6_listval *lv = NULL; + + if ((lv = malloc(sizeof(*lv))) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to allocate memory for list entry"); + goto fail; + } + memset(lv, 0, sizeof(*lv)); + lv->type = type; + TAILQ_INIT(&lv->sublist); + + switch(type) { + case DHCP6_LISTVAL_NUM: + lv->val_num = *(int *)val; + break; + case DHCP6_LISTVAL_STCODE: + lv->val_num16 = *(u_int16_t *)val; + break; + case DHCP6_LISTVAL_ADDR6: + lv->val_addr6 = *(struct in6_addr *)val; + break; + case DHCP6_LISTVAL_PREFIX6: + lv->val_prefix6 = *(struct dhcp6_prefix *)val; + break; + case DHCP6_LISTVAL_STATEFULADDR6: + lv->val_statefuladdr6 = *(struct dhcp6_statefuladdr *)val; + break; + case DHCP6_LISTVAL_IAPD: + case DHCP6_LISTVAL_IANA: + lv->val_ia = *(struct dhcp6_ia *)val; + break; + case DHCP6_LISTVAL_VBUF: + if (dhcp6_vbuf_copy(&lv->val_vbuf, (struct dhcp6_vbuf *)val)) + goto fail; + break; + default: + dprintf(LOG_ERR, FNAME, + "unexpected list value type (%d)", type); + goto fail; + } + + if (sublist && dhcp6_copy_list(&lv->sublist, sublist)) + goto fail; + + TAILQ_INSERT_TAIL(head, lv, link); + + return (lv); + + fail: + if (lv) + free(lv); + + return (NULL); +} + +int +dhcp6_vbuf_copy(dst, src) + struct dhcp6_vbuf *dst, *src; +{ + dst->dv_buf = malloc(src->dv_len); + if (dst->dv_buf == NULL) + return (-1); + + dst->dv_len = src->dv_len; + memcpy(dst->dv_buf, src->dv_buf, dst->dv_len); + + return (0); +} + +void +dhcp6_vbuf_free(vbuf) + struct dhcp6_vbuf *vbuf; +{ + free(vbuf->dv_buf); + + vbuf->dv_len = 0; + vbuf->dv_buf = NULL; +} + +int +dhcp6_vbuf_cmp(vb1, vb2) + struct dhcp6_vbuf *vb1, *vb2; +{ + if (vb1->dv_len != vb2->dv_len) + return (vb1->dv_len - vb2->dv_len); + + return (memcmp(vb1->dv_buf, vb2->dv_buf, vb1->dv_len)); +} + +static int +dhcp6_get_addr(optlen, cp, type, list) + int optlen; + void *cp; + dhcp6_listval_type_t type; + struct dhcp6_list *list; +{ + void *val; + + if (optlen % sizeof(struct in6_addr) || optlen == 0) { + dprintf(LOG_INFO, FNAME, + "malformed DHCP option: type %d, len %d", type, optlen); + return -1; + } + for (val = cp; val < cp + optlen; val += sizeof(struct in6_addr)) { + struct in6_addr valaddr; + + memcpy(&valaddr, val, sizeof(valaddr)); + if (dhcp6_find_listval(list, + DHCP6_LISTVAL_ADDR6, &valaddr, 0)) { + dprintf(LOG_INFO, FNAME, "duplicated %s address (%s)", + dhcp6optstr(type), in6addr2str(&valaddr, 0)); + continue; + } + + if (dhcp6_add_listval(list, DHCP6_LISTVAL_ADDR6, + &valaddr, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to copy %s address", dhcp6optstr(type)); + return -1; + } + } + + return 0; +} + +static int +dhcp6_set_addr(type, list, p, optep, len) + dhcp6_listval_type_t type; + struct dhcp6_list *list; + struct dhcp6opt **p, *optep; + int *len; +{ + struct in6_addr *in6; + char *tmpbuf; + struct dhcp6_listval *d; + int optlen; + + if (TAILQ_EMPTY(list)) + return 0; + + tmpbuf = NULL; + optlen = dhcp6_count_list(list) * sizeof(struct in6_addr); + if ((tmpbuf = malloc(optlen)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation failed for %s options", + dhcp6optstr(type)); + return -1; + } + in6 = (struct in6_addr *)tmpbuf; + for (d = TAILQ_FIRST(list); d; d = TAILQ_NEXT(d, link), in6++) + memcpy(in6, &d->val_addr6, sizeof(*in6)); + if (copy_option(type, optlen, tmpbuf, p, optep, len) != 0) { + free(tmpbuf); + return -1; + } + + free(tmpbuf); + return 0; +} + +static int +dhcp6_get_domain(optlen, cp, type, list) + int optlen; + void *cp; + dhcp6_listval_type_t type; + struct dhcp6_list *list; +{ + void *val; + + val = cp; + while (val < cp + optlen) { + struct dhcp6_vbuf vb; + char name[MAXDNAME + 1]; + + if (dnsdecode((u_char **)(void *)&val, + (u_char *)(cp + optlen), name, sizeof(name)) == NULL) { + dprintf(LOG_INFO, FNAME, "failed to " + "decode a %s domain name", + dhcp6optstr(type)); + dprintf(LOG_INFO, FNAME, + "malformed DHCP option: type %d, len %d", + type, optlen); + return -1; + } + + vb.dv_len = strlen(name) + 1; + vb.dv_buf = name; + + if (dhcp6_add_listval(list, + DHCP6_LISTVAL_VBUF, &vb, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to " + "copy a %s domain name", dhcp6optstr(type)); + return -1; + } + } + + return 0; +} + +static int +dhcp6_set_domain(type, list, p, optep, len) + dhcp6_listval_type_t type; + struct dhcp6_list *list; + struct dhcp6opt **p, *optep; + int *len; +{ + int optlen = 0; + struct dhcp6_listval *d; + char *tmpbuf; + char name[MAXDNAME], *cp, *ep; + + if (TAILQ_EMPTY(list)) + return 0; + + for (d = TAILQ_FIRST(list); d; d = TAILQ_NEXT(d, link)) + optlen += (d->val_vbuf.dv_len + 1); + + if (optlen == 0) { + return 0; + } + + tmpbuf = NULL; + if ((tmpbuf = malloc(optlen)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed for " + "%s domain options", dhcp6optstr(type)); + return -1; + } + cp = tmpbuf; + ep = cp + optlen; + for (d = TAILQ_FIRST(list); d; d = TAILQ_NEXT(d, link)) { + int nlen; + + nlen = dnsencode((const char *)d->val_vbuf.dv_buf, + name, sizeof (name)); + if (nlen < 0) { + dprintf(LOG_ERR, FNAME, + "failed to encode a %s domain name", + dhcp6optstr(type)); + free(tmpbuf); + return -1; + } + if (ep - cp < nlen) { + dprintf(LOG_ERR, FNAME, + "buffer length for %s domain name is too short", + dhcp6optstr(type)); + free(tmpbuf); + return -1; + } + memcpy(cp, name, nlen); + cp += nlen; + } + if (copy_option(type, cp - tmpbuf, tmpbuf, p, optep, len) != 0) { + free(tmpbuf); + return -1; + } + free(tmpbuf); + + return 0; +} + +struct dhcp6_event * +dhcp6_create_event(ifp, state) + struct dhcp6_if *ifp; + int state; +{ + struct dhcp6_event *ev; + + if ((ev = malloc(sizeof(*ev))) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to allocate memory for an event"); + return (NULL); + } + memset(ev, 0, sizeof(*ev)); + ev->ifp = ifp; + ev->state = state; + TAILQ_INIT(&ev->data_list); + + return (ev); +} + +void +dhcp6_remove_event(ev) + struct dhcp6_event *ev; +{ + struct dhcp6_serverinfo *sp, *sp_next; + + dprintf(LOG_DEBUG, FNAME, "removing an event on %s, state=%s", + ev->ifp->ifname, dhcp6_event_statestr(ev)); + + dhcp6_remove_evdata(ev); + + duidfree(&ev->serverid); + + if (ev->timer) + dhcp6_remove_timer(&ev->timer); + TAILQ_REMOVE(&ev->ifp->event_list, ev, link); + + for (sp = ev->servers; sp; sp = sp_next) { + sp_next = sp->next; + + dprintf(LOG_DEBUG, FNAME, "removing server (ID: %s)", + duidstr(&sp->optinfo.serverID)); + dhcp6_clear_options(&sp->optinfo); + if (sp->authparam != NULL) + free(sp->authparam); + free(sp); + } + + if (ev->authparam != NULL) + free(ev->authparam); + + free(ev); +} + +void +dhcp6_remove_evdata(ev) + struct dhcp6_event *ev; +{ + struct dhcp6_eventdata *evd; + + while ((evd = TAILQ_FIRST(&ev->data_list)) != NULL) { + TAILQ_REMOVE(&ev->data_list, evd, link); + if (evd->destructor) + (*evd->destructor)(evd); + free(evd); + } +} + +struct authparam * +new_authparam(proto, alg, rdm) + int proto, alg, rdm; +{ + struct authparam *authparam; + + if ((authparam = malloc(sizeof(*authparam))) == NULL) + return (NULL); + + memset(authparam, 0, sizeof(*authparam)); + + authparam->authproto = proto; + authparam->authalgorithm = alg; + authparam->authrdm = rdm; + authparam->key = NULL; + authparam->flags |= AUTHPARAM_FLAGS_NOPREVRD; + authparam->prevrd = 0; + + return (authparam); +} + +struct authparam * +copy_authparam(authparam) + struct authparam *authparam; +{ + struct authparam *dst; + + if ((dst = malloc(sizeof(*dst))) == NULL) + return (NULL); + + memcpy(dst, authparam, sizeof(*dst)); + + return (dst); +} + +/* + * Home-brew function of a 64-bit version of ntohl. + * XXX: is there any standard for this? + */ +#if (BYTE_ORDER == LITTLE_ENDIAN) +static __inline u_int64_t +ntohq(u_int64_t x) +{ + return (u_int64_t)ntohl((u_int32_t)(x >> 32)) | + (int64_t)ntohl((u_int32_t)(x & 0xffffffff)) << 32; +} +#else /* (BYTE_ORDER == LITTLE_ENDIAN) */ +#define ntohq(x) (x) +#endif + +int +dhcp6_auth_replaycheck(method, prev, current) + int method; + u_int64_t prev, current; +{ + char bufprev[] = "ffff ffff ffff ffff"; + char bufcurrent[] = "ffff ffff ffff ffff"; + + if (method != DHCP6_AUTHRDM_MONOCOUNTER) { + dprintf(LOG_ERR, FNAME, "unsupported replay detection " + "method (%d)", method); + return (-1); + } + + (void)sprint_uint64(bufprev, sizeof(bufprev), prev); + (void)sprint_uint64(bufcurrent, sizeof(bufcurrent), current); + dprintf(LOG_DEBUG, FNAME, "previous: %s, current: %s", + bufprev, bufcurrent); + + prev = ntohq(prev); + current = ntohq(current); + + /* + * we call the singular point guilty, since we cannot guess + * whether the serial number is increasing or not. + */ + if (prev == (current ^ 0x8000000000000000ULL)) { + dprintf(LOG_INFO, FNAME, "detected a singular point"); + return (1); + } + + return (((int64_t)(current - prev) > 0) ? 0 : 1); +} + +int +getifaddr(addr, ifnam, prefix, plen, strong, ignoreflags) + struct in6_addr *addr; + char *ifnam; + struct in6_addr *prefix; + int plen; + int strong; /* if strong host model is required or not */ + int ignoreflags; +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in6 sin6; + int error = -1; + + if (getifaddrs(&ifap) != 0) { + dprintf(LOG_WARNING, FNAME, + "getifaddrs failed: %s", strerror(errno)); + return (-1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + int s1, s2; + + if (strong && strcmp(ifnam, ifa->ifa_name) != 0) + continue; + + /* in any case, ignore interfaces in different scope zones. */ + if ((s1 = in6_addrscopebyif(prefix, ifnam)) < 0 || + (s2 = in6_addrscopebyif(prefix, ifa->ifa_name)) < 0 || + s1 != s2) + continue; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; +#ifdef HAVE_SA_LEN + if (ifa->ifa_addr->sa_len > sizeof(sin6)) + continue; +#endif + + if (in6_matchflags(ifa->ifa_addr, ifa->ifa_name, ignoreflags)) + continue; + + memcpy(&sin6, ifa->ifa_addr, sysdep_sa_len(ifa->ifa_addr)); +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) { + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } +#endif + if (plen % 8 == 0) { + if (memcmp(&sin6.sin6_addr, prefix, plen / 8) != 0) + continue; + } else { + struct in6_addr a, m; + int i; + + memcpy(&a, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + memset(&m, 0, sizeof(m)); + memset(&m, 0xff, plen / 8); + m.s6_addr[plen / 8] = (0xff00 >> (plen % 8)) & 0xff; + for (i = 0; i < sizeof(a); i++) + a.s6_addr[i] &= m.s6_addr[i]; + + if (memcmp(&a, prefix, plen / 8) != 0 || + a.s6_addr[plen / 8] != + (prefix->s6_addr[plen / 8] & m.s6_addr[plen / 8])) + continue; + } + memcpy(addr, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(addr)) + addr->s6_addr[2] = addr->s6_addr[3] = 0; +#endif + error = 0; + break; + } + + freeifaddrs(ifap); + return (error); +} + +int +getifidfromaddr(addr, ifidp) + struct in6_addr *addr; + unsigned int *ifidp; +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in6 *sa6; + unsigned int ifid; + int retval = -1; + + if (getifaddrs(&ifap) != 0) { + dprintf(LOG_WARNING, FNAME, + "getifaddrs failed: %s", strerror(errno)); + return (-1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_ARE_ADDR_EQUAL(addr, &sa6->sin6_addr)) + break; + } + + if (ifa != NULL) { + if ((ifid = if_nametoindex(ifa->ifa_name)) == 0) { + dprintf(LOG_ERR, FNAME, + "if_nametoindex failed for %s", ifa->ifa_name); + goto end; + } + retval = 0; + *ifidp = ifid; + } + + end: + freeifaddrs(ifap); + return (retval); +} + +int +in6_addrscopebyif(addr, ifnam) + struct in6_addr *addr; + char *ifnam; +{ + u_int ifindex; + + if ((ifindex = if_nametoindex(ifnam)) == 0) + return (-1); + + if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr)) + return (ifindex); + + if (IN6_IS_ADDR_SITELOCAL(addr) || IN6_IS_ADDR_MC_SITELOCAL(addr)) + return (1); /* XXX */ + + if (IN6_IS_ADDR_MC_ORGLOCAL(addr)) + return (1); /* XXX */ + + return (1); /* treat it as global */ +} + +int +transmit_sa(s, sa, buf, len) + int s; + struct sockaddr *sa; + char *buf; + size_t len; +{ + int error; + + error = sendto(s, buf, len, 0, sa, sysdep_sa_len(sa)); + + return (error != len) ? -1 : 0; +} + +long +random_between(x, y) + long x; + long y; +{ + long ratio; + + ratio = 1 << 16; + while ((y - x) * ratio < (y - x)) + ratio = ratio / 2; + return (x + ((y - x) * (ratio - 1) / random() & (ratio - 1))); +} + +int +prefix6_mask(in6, plen) + struct in6_addr *in6; + int plen; +{ + struct sockaddr_in6 mask6; + int i; + + if (sa6_plen2mask(&mask6, plen)) + return (-1); + + for (i = 0; i < 16; i++) + in6->s6_addr[i] &= mask6.sin6_addr.s6_addr[i]; + + return (0); +} + +int +sa6_plen2mask(sa6, plen) + struct sockaddr_in6 *sa6; + int plen; +{ + u_char *cp; + + if (plen < 0 || plen > 128) + return (-1); + + memset(sa6, 0, sizeof(*sa6)); + sa6->sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + sa6->sin6_len = sizeof(*sa6); +#endif + + for (cp = (u_char *)&sa6->sin6_addr; plen > 7; plen -= 8) + *cp++ = 0xff; + *cp = 0xff << (8 - plen); + + return (0); +} + +char * +addr2str(sa) + struct sockaddr *sa; +{ + static char addrbuf[8][NI_MAXHOST]; + static int round = 0; + char *cp; + + round = (round + 1) & 7; + cp = addrbuf[round]; + + getnameinfo(sa, sysdep_sa_len(sa), cp, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST); + + return (cp); +} + +char * +in6addr2str(in6, scopeid) + struct in6_addr *in6; + int scopeid; +{ + struct sockaddr_in6 sa6; + + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + sa6.sin6_len = sizeof(sa6); +#endif + sa6.sin6_addr = *in6; + sa6.sin6_scope_id = scopeid; + + return (addr2str((struct sockaddr *)&sa6)); +} + +/* return IPv6 address scope type. caller assumes that smaller is narrower. */ +int +in6_scope(addr) + struct in6_addr *addr; +{ + int scope; + + if (addr->s6_addr[0] == 0xfe) { + scope = addr->s6_addr[1] & 0xc0; + + switch (scope) { + case 0x80: + return (2); /* link-local */ + break; + case 0xc0: + return (5); /* site-local */ + break; + default: + return (14); /* global: just in case */ + break; + } + } + + /* multicast scope. just return the scope field */ + if (addr->s6_addr[0] == 0xff) + return (addr->s6_addr[1] & 0x0f); + + if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { + if (addr->s6_addr[15] == 1) /* loopback */ + return (1); + if (addr->s6_addr[15] == 0) /* unspecified */ + return (0); /* XXX: good value? */ + } + + return (14); /* global */ +} + +static int +in6_matchflags(addr, ifnam, flags) + struct sockaddr *addr; + char *ifnam; + int flags; +{ +#ifdef __KAME__ + int s; + struct in6_ifreq ifr6; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + warn("in6_matchflags: socket(DGRAM6)"); + return (-1); + } + memset(&ifr6, 0, sizeof(ifr6)); + strncpy(ifr6.ifr_name, ifnam, sizeof(ifr6.ifr_name)); + ifr6.ifr_addr = *(struct sockaddr_in6 *)addr; + + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + warn("in6_matchflags: ioctl(SIOCGIFAFLAG_IN6, %s)", + addr2str(addr)); + close(s); + return (-1); + } + + close(s); + + return (ifr6.ifr_ifru.ifru_flags6 & flags); +#else + return (0); +#endif +} + +int +get_duid(idfile, duid) + char *idfile; + struct duid *duid; +{ + FILE *fp = NULL; + u_int16_t len = 0, hwtype; + struct dhcp6opt_duid_type1 *dp; /* we only support the type1 DUID */ + char tmpbuf[256]; /* DUID should be no more than 256 bytes */ + + if ((fp = fopen(idfile, "r")) == NULL && errno != ENOENT) + dprintf(LOG_NOTICE, FNAME, "failed to open DUID file: %s", + idfile); + + if (fp) { + /* decode length */ + if (fread(&len, sizeof(len), 1, fp) != 1) { + dprintf(LOG_ERR, FNAME, "DUID file corrupted"); + goto fail; + } + } else { + int l; + + if ((l = gethwid(tmpbuf, sizeof(tmpbuf), NULL, &hwtype)) < 0) { + dprintf(LOG_INFO, FNAME, + "failed to get a hardware address"); + goto fail; + } + len = l + sizeof(struct dhcp6opt_duid_type1); + } + + memset(duid, 0, sizeof(*duid)); + duid->duid_len = len; + if ((duid->duid_id = (char *)malloc(len)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to allocate memory"); + goto fail; + } + + /* copy (and fill) the ID */ + if (fp) { + if (fread(duid->duid_id, len, 1, fp) != 1) { + dprintf(LOG_ERR, FNAME, "DUID file corrupted"); + goto fail; + } + + dprintf(LOG_DEBUG, FNAME, + "extracted an existing DUID from %s: %s", + idfile, duidstr(duid)); + } else { + u_int64_t t64; + + dp = (struct dhcp6opt_duid_type1 *)duid->duid_id; + dp->dh6_duid1_type = htons(1); /* type 1 */ + dp->dh6_duid1_hwtype = htons(hwtype); + /* time is Jan 1, 2000 (UTC), modulo 2^32 */ + t64 = (u_int64_t)(time(NULL) - 946684800); + dp->dh6_duid1_time = htonl((u_long)(t64 & 0xffffffff)); + memcpy((void *)(dp + 1), tmpbuf, (len - sizeof(*dp))); + + dprintf(LOG_DEBUG, FNAME, "generated a new DUID: %s", + duidstr(duid)); + } + + /* save the (new) ID to the file for next time */ + if (!fp) { + if ((fp = fopen(idfile, "w+")) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to open DUID file for save"); + goto fail; + } + if ((fwrite(&len, sizeof(len), 1, fp)) != 1) { + dprintf(LOG_ERR, FNAME, "failed to save DUID"); + goto fail; + } + if ((fwrite(duid->duid_id, len, 1, fp)) != 1) { + dprintf(LOG_ERR, FNAME, "failed to save DUID"); + goto fail; + } + + dprintf(LOG_DEBUG, FNAME, "saved generated DUID to %s", + idfile); + } + + if (fp) + fclose(fp); + return (0); + + fail: + if (fp) + fclose(fp); + if (duid->duid_id) { + free(duid->duid_id); + duid->duid_id = NULL; /* for safety */ + } + return (-1); +} + +#ifdef __sun__ +struct hwparms { + char *buf; + u_int16_t *hwtypep; + ssize_t retval; +}; + +static ssize_t +getifhwaddr(const char *ifname, char *buf, u_int16_t *hwtypep, int ppa) +{ + int fd, flags; + char fname[MAXPATHLEN], *cp; + struct strbuf putctl; + struct strbuf getctl; + long getbuf[1024]; + dl_info_req_t dlir; + dl_phys_addr_req_t dlpar; + dl_phys_addr_ack_t *dlpaa; + + dprintf(LOG_DEBUG, FNAME, "trying %s ppa %d", ifname, ppa); + + if (ifname[0] == '\0') + return (-1); + if (ppa >= 0 && !isdigit(ifname[strlen(ifname) - 1])) + (void) snprintf(fname, sizeof (fname), "/dev/%s%d", ifname, + ppa); + else + (void) snprintf(fname, sizeof (fname), "/dev/%s", ifname); + getctl.maxlen = sizeof (getbuf); + getctl.buf = (char *)getbuf; + if ((fd = open(fname, O_RDWR)) == -1) { + dl_attach_req_t dlar; + + cp = fname + strlen(fname) - 1; + if (!isdigit(*cp)) + return (-1); + while (cp > fname) { + if (!isdigit(*cp)) + break; + cp--; + } + if (cp == fname) + return (-1); + cp++; + dlar.dl_ppa = atoi(cp); + *cp = '\0'; + if ((fd = open(fname, O_RDWR)) == -1) + return (-1); + dlar.dl_primitive = DL_ATTACH_REQ; + putctl.len = sizeof (dlar); + putctl.buf = (char *)&dlar; + if (putmsg(fd, &putctl, NULL, 0) == -1) { + (void) close(fd); + return (-1); + } + flags = 0; + if (getmsg(fd, &getctl, NULL, &flags) == -1) { + (void) close(fd); + return (-1); + } + if (getbuf[0] != DL_OK_ACK) { + (void) close(fd); + return (-1); + } + } + dlir.dl_primitive = DL_INFO_REQ; + putctl.len = sizeof (dlir); + putctl.buf = (char *)&dlir; + if (putmsg(fd, &putctl, NULL, 0) == -1) { + (void) close(fd); + return (-1); + } + flags = 0; + if (getmsg(fd, &getctl, NULL, &flags) == -1) { + (void) close(fd); + return (-1); + } + if (getbuf[0] != DL_INFO_ACK) { + (void) close(fd); + return (-1); + } + switch (((dl_info_ack_t *)getbuf)->dl_mac_type) { + case DL_CSMACD: + case DL_ETHER: + case DL_100VG: + case DL_ETH_CSMA: + case DL_100BT: + *hwtypep = ARPHRD_ETHER; + break; + default: + (void) close(fd); + return (-1); + } + dlpar.dl_primitive = DL_PHYS_ADDR_REQ; + dlpar.dl_addr_type = DL_CURR_PHYS_ADDR; + putctl.len = sizeof (dlpar); + putctl.buf = (char *)&dlpar; + if (putmsg(fd, &putctl, NULL, 0) == -1) { + (void) close(fd); + return (-1); + } + flags = 0; + if (getmsg(fd, &getctl, NULL, &flags) == -1) { + (void) close(fd); + return (-1); + } + if (getbuf[0] != DL_PHYS_ADDR_ACK) { + (void) close(fd); + return (-1); + } + dlpaa = (dl_phys_addr_ack_t *)getbuf; + if (dlpaa->dl_addr_length != 6) { + (void) close(fd); + return (-1); + } + (void) memcpy(buf, (char *)getbuf + dlpaa->dl_addr_offset, + dlpaa->dl_addr_length); + return (dlpaa->dl_addr_length); +} + +static int +devfs_handler(di_node_t node, di_minor_t minor, void *arg) +{ + struct hwparms *parms = arg; + + parms->retval = getifhwaddr(di_minor_name(minor), parms->buf, + parms->hwtypep, di_instance(node)); + return (parms->retval == -1 ? DI_WALK_CONTINUE : DI_WALK_TERMINATE); +} +#endif + +static ssize_t +gethwid(buf, len, ifname, hwtypep) + char *buf; + int len; + const char *ifname; + u_int16_t *hwtypep; +{ + struct ifaddrs *ifa, *ifap; +#ifdef __KAME__ + struct sockaddr_dl *sdl; +#endif +#ifdef __linux__ + struct sockaddr_ll *sll; +#endif + ssize_t l; + +#ifdef __sun__ + if (ifname == NULL) { + di_node_t root; + struct hwparms parms; + + if ((root = di_init("/", DINFOSUBTREE | DINFOMINOR | + DINFOPROP)) == DI_NODE_NIL) { + dprintf(LOG_INFO, FNAME, "di_init failed"); + return (-1); + } + parms.buf = buf; + parms.hwtypep = hwtypep; + parms.retval = -1; + (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &parms, + devfs_handler); + di_fini(root); + return (parms.retval); + } else { + return (getifhwaddr(ifname, buf, hwtypep, -1)); + } +#endif + + if (getifaddrs(&ifap) < 0) + return (-1); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifname && strcmp(ifa->ifa_name, ifname) != 0) + continue; + if (ifa->ifa_addr == NULL) + continue; +#ifdef __KAME__ + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (len < 2 + sdl->sdl_alen) + goto fail; + /* + * translate interface type to hardware type based on + * http://www.iana.org/assignments/arp-parameters + */ + switch(sdl->sdl_type) { + case IFT_ETHER: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif + *hwtypep = ARPHRD_ETHER; + break; + default: + continue; /* XXX */ + } + dprintf(LOG_DEBUG, FNAME, "found an interface %s for DUID", + ifa->ifa_name); + memcpy(buf, LLADDR(sdl), sdl->sdl_alen); + l = sdl->sdl_alen; /* sdl will soon be freed */ +#endif +#ifdef __linux__ + if (ifa->ifa_addr->sa_family != AF_PACKET) + continue; + + sll = (struct sockaddr_ll *)ifa->ifa_addr; + if (sll->sll_hatype != ARPHRD_ETHER) + continue; + *hwtypep = ARPHRD_ETHER; + dprintf(LOG_DEBUG, FNAME, "found an interface %s for DUID", + ifa->ifa_name); + memcpy(buf, sll->sll_addr, sll->sll_halen); + l = sll->sll_halen; /* sll will soon be freed */ +#endif + freeifaddrs(ifap); + return (l); + } + + fail: + freeifaddrs(ifap); + return (-1); +} + +void +dhcp6_init_options(optinfo) + struct dhcp6_optinfo *optinfo; +{ + memset(optinfo, 0, sizeof(*optinfo)); + + optinfo->pref = DH6OPT_PREF_UNDEF; + optinfo->elapsed_time = DH6OPT_ELAPSED_TIME_UNDEF; + optinfo->refreshtime = DH6OPT_REFRESHTIME_UNDEF; + + TAILQ_INIT(&optinfo->iapd_list); + TAILQ_INIT(&optinfo->iana_list); + TAILQ_INIT(&optinfo->reqopt_list); + TAILQ_INIT(&optinfo->stcode_list); + TAILQ_INIT(&optinfo->sip_list); + TAILQ_INIT(&optinfo->sipname_list); + TAILQ_INIT(&optinfo->dns_list); + TAILQ_INIT(&optinfo->dnsname_list); + TAILQ_INIT(&optinfo->ntp_list); + TAILQ_INIT(&optinfo->prefix_list); + TAILQ_INIT(&optinfo->nis_list); + TAILQ_INIT(&optinfo->nisname_list); + TAILQ_INIT(&optinfo->nisp_list); + TAILQ_INIT(&optinfo->nispname_list); + TAILQ_INIT(&optinfo->bcmcs_list); + TAILQ_INIT(&optinfo->bcmcsname_list); + + optinfo->authproto = DHCP6_AUTHPROTO_UNDEF; + optinfo->authalgorithm = DHCP6_AUTHALG_UNDEF; + optinfo->authrdm = DHCP6_AUTHRDM_UNDEF; +} + +void +dhcp6_clear_options(optinfo) + struct dhcp6_optinfo *optinfo; +{ + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + if (optinfo->delayedauth_realmval != NULL) { + free(optinfo->delayedauth_realmval); + } + break; + } + + duidfree(&optinfo->clientID); + duidfree(&optinfo->serverID); + + dhcp6_clear_list(&optinfo->iapd_list); + dhcp6_clear_list(&optinfo->iana_list); + dhcp6_clear_list(&optinfo->reqopt_list); + dhcp6_clear_list(&optinfo->stcode_list); + dhcp6_clear_list(&optinfo->sip_list); + dhcp6_clear_list(&optinfo->sipname_list); + dhcp6_clear_list(&optinfo->dns_list); + dhcp6_clear_list(&optinfo->dnsname_list); + dhcp6_clear_list(&optinfo->ntp_list); + dhcp6_clear_list(&optinfo->prefix_list); + dhcp6_clear_list(&optinfo->nis_list); + dhcp6_clear_list(&optinfo->nisname_list); + dhcp6_clear_list(&optinfo->nisp_list); + dhcp6_clear_list(&optinfo->nispname_list); + dhcp6_clear_list(&optinfo->bcmcs_list); + dhcp6_clear_list(&optinfo->bcmcsname_list); + + if (optinfo->relaymsg_msg != NULL) + free(optinfo->relaymsg_msg); + + if (optinfo->ifidopt_id != NULL) + free(optinfo->ifidopt_id); + + dhcp6_init_options(optinfo); +} + +int +dhcp6_copy_options(dst, src) + struct dhcp6_optinfo *dst, *src; +{ + if (duidcpy(&dst->clientID, &src->clientID)) + goto fail; + if (duidcpy(&dst->serverID, &src->serverID)) + goto fail; + dst->rapidcommit = src->rapidcommit; + + if (dhcp6_copy_list(&dst->iapd_list, &src->iapd_list)) + goto fail; + if (dhcp6_copy_list(&dst->iana_list, &src->iana_list)) + goto fail; + if (dhcp6_copy_list(&dst->reqopt_list, &src->reqopt_list)) + goto fail; + if (dhcp6_copy_list(&dst->stcode_list, &src->stcode_list)) + goto fail; + if (dhcp6_copy_list(&dst->sip_list, &src->sip_list)) + goto fail; + if (dhcp6_copy_list(&dst->sipname_list, &src->sipname_list)) + goto fail; + if (dhcp6_copy_list(&dst->dns_list, &src->dns_list)) + goto fail; + if (dhcp6_copy_list(&dst->dnsname_list, &src->dnsname_list)) + goto fail; + if (dhcp6_copy_list(&dst->ntp_list, &src->ntp_list)) + goto fail; + if (dhcp6_copy_list(&dst->prefix_list, &src->prefix_list)) + goto fail; + if (dhcp6_copy_list(&dst->nis_list, &src->nis_list)) + goto fail; + if (dhcp6_copy_list(&dst->nisname_list, &src->nisname_list)) + goto fail; + if (dhcp6_copy_list(&dst->nisp_list, &src->nisp_list)) + goto fail; + if (dhcp6_copy_list(&dst->nispname_list, &src->nispname_list)) + goto fail; + if (dhcp6_copy_list(&dst->bcmcs_list, &src->bcmcs_list)) + goto fail; + if (dhcp6_copy_list(&dst->bcmcsname_list, &src->bcmcsname_list)) + goto fail; + dst->elapsed_time = src->elapsed_time; + dst->refreshtime = src->refreshtime; + dst->pref = src->pref; + + if (src->relaymsg_msg != NULL) { + if ((dst->relaymsg_msg = malloc(src->relaymsg_len)) == NULL) + goto fail; + dst->relaymsg_len = src->relaymsg_len; + memcpy(dst->relaymsg_msg, src->relaymsg_msg, + src->relaymsg_len); + } + + if (src->ifidopt_id != NULL) { + if ((dst->ifidopt_id = malloc(src->ifidopt_len)) == NULL) + goto fail; + dst->ifidopt_len = src->ifidopt_len; + memcpy(dst->ifidopt_id, src->ifidopt_id, src->ifidopt_len); + } + + dst->authflags = src->authflags; + dst->authproto = src->authproto; + dst->authalgorithm = src->authalgorithm; + dst->authrdm = src->authrdm; + dst->authrd = src->authrd; + + switch (src->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + dst->delayedauth_keyid = src->delayedauth_keyid; + dst->delayedauth_offset = src->delayedauth_offset; + dst->delayedauth_realmlen = src->delayedauth_realmlen; + if (src->delayedauth_realmval != NULL) { + if ((dst->delayedauth_realmval = + malloc(src->delayedauth_realmlen)) == NULL) { + goto fail; + } + memcpy(dst->delayedauth_realmval, + src->delayedauth_realmval, + src->delayedauth_realmlen); + } + break; + case DHCP6_AUTHPROTO_RECONFIG: + dst->reconfigauth_type = src->reconfigauth_type; + dst->reconfigauth_offset = src->reconfigauth_offset; + memcpy(dst->reconfigauth_val, src->reconfigauth_val, + sizeof(dst->reconfigauth_val)); + break; + } + + return (0); + + fail: + /* cleanup temporary resources */ + dhcp6_clear_options(dst); + return (-1); +} + +int +dhcp6_get_options(p, ep, optinfo) + struct dhcp6opt *p, *ep; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6opt *np, opth; + int i, opt, optlen, reqopts, num; + u_int16_t num16; + char *bp, *cp, *val; + u_int16_t val16; + u_int32_t val32; + struct dhcp6opt_ia optia; + struct dhcp6_ia ia; + struct dhcp6_list sublist; + int authinfolen; + + bp = (char *)p; + for (; p + 1 <= ep; p = np) { + struct duid duid0; + + /* + * get the option header. XXX: since there is no guarantee + * about the header alignment, we need to make a local copy. + */ + memcpy(&opth, p, sizeof(opth)); + optlen = ntohs(opth.dh6opt_len); + opt = ntohs(opth.dh6opt_type); + + cp = (char *)(p + 1); + np = (struct dhcp6opt *)(cp + optlen); + + dprintf(LOG_DEBUG, FNAME, "get DHCP option %s, len %d", + dhcp6optstr(opt), optlen); + + /* option length field overrun */ + if (np > ep) { + dprintf(LOG_INFO, FNAME, "malformed DHCP options"); + goto fail; + } + + switch (opt) { + case DH6OPT_CLIENTID: + if (optlen == 0) + goto malformed; + duid0.duid_len = optlen; + duid0.duid_id = cp; + dprintf(LOG_DEBUG, "", + " DUID: %s", duidstr(&duid0)); + if (duidcpy(&optinfo->clientID, &duid0)) { + dprintf(LOG_ERR, FNAME, "failed to copy DUID"); + goto fail; + } + break; + case DH6OPT_SERVERID: + if (optlen == 0) + goto malformed; + duid0.duid_len = optlen; + duid0.duid_id = cp; + dprintf(LOG_DEBUG, "", " DUID: %s", duidstr(&duid0)); + if (duidcpy(&optinfo->serverID, &duid0)) { + dprintf(LOG_ERR, FNAME, "failed to copy DUID"); + goto fail; + } + break; + case DH6OPT_STATUS_CODE: + if (optlen < sizeof(u_int16_t)) + goto malformed; + memcpy(&val16, cp, sizeof(val16)); + num16 = ntohs(val16); + dprintf(LOG_DEBUG, "", " status code: %s", + dhcp6_stcodestr(num16)); + + /* need to check duplication? */ + + if (dhcp6_add_listval(&optinfo->stcode_list, + DHCP6_LISTVAL_STCODE, &num16, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy " + "status code"); + goto fail; + } + + break; + case DH6OPT_ORO: + if ((optlen % 2) != 0 || optlen == 0) + goto malformed; + reqopts = optlen / 2; + for (i = 0, val = cp; i < reqopts; + i++, val += sizeof(u_int16_t)) { + u_int16_t opttype; + + memcpy(&opttype, val, sizeof(u_int16_t)); + num = (int)ntohs(opttype); + + dprintf(LOG_DEBUG, "", + " requested option: %s", + dhcp6optstr(num)); + + if (dhcp6_find_listval(&optinfo->reqopt_list, + DHCP6_LISTVAL_NUM, &num, 0)) { + dprintf(LOG_INFO, FNAME, "duplicated " + "option type (%s)", + dhcp6optstr(opttype)); + goto nextoption; + } + + if (dhcp6_add_listval(&optinfo->reqopt_list, + DHCP6_LISTVAL_NUM, &num, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to copy requested option"); + goto fail; + } + nextoption: + ; + } + break; + case DH6OPT_PREFERENCE: + if (optlen != 1) + goto malformed; + dprintf(LOG_DEBUG, "", " preference: %d", + (int)*(u_char *)cp); + if (optinfo->pref != DH6OPT_PREF_UNDEF) { + dprintf(LOG_INFO, FNAME, + "duplicated preference option"); + } else + optinfo->pref = (int)*(u_char *)cp; + break; + case DH6OPT_ELAPSED_TIME: + if (optlen != 2) + goto malformed; + memcpy(&val16, cp, sizeof(val16)); + val16 = ntohs(val16); + dprintf(LOG_DEBUG, "", " elapsed time: %lu", + (u_int32_t)val16); + if (optinfo->elapsed_time != + DH6OPT_ELAPSED_TIME_UNDEF) { + dprintf(LOG_INFO, FNAME, + "duplicated elapsed time option"); + } else + optinfo->elapsed_time = val16; + break; + case DH6OPT_RELAY_MSG: + if ((optinfo->relaymsg_msg = malloc(optlen)) == NULL) + goto fail; + memcpy(optinfo->relaymsg_msg, cp, optlen); + optinfo->relaymsg_len = optlen; + break; + case DH6OPT_AUTH: + if (optlen < sizeof(struct dhcp6opt_auth) - 4) + goto malformed; + + /* + * Any DHCP message that includes more than one + * authentication option MUST be discarded. + * [RFC3315 Section 21.4.2] + */ + if (optinfo->authproto != DHCP6_AUTHPROTO_UNDEF) { + dprintf(LOG_INFO, FNAME, "found more than one " + "authentication option"); + goto fail; + } + + optinfo->authproto = *cp++; + optinfo->authalgorithm = *cp++; + optinfo->authrdm = *cp++; + memcpy(&optinfo->authrd, cp, sizeof(optinfo->authrd)); + cp += sizeof(optinfo->authrd); + + dprintf(LOG_DEBUG, "", " %s", sprint_auth(optinfo)); + + authinfolen = + optlen - (sizeof(struct dhcp6opt_auth) - 4); + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + if (authinfolen == 0) { + optinfo->authflags |= + DHCP6OPT_AUTHFLAG_NOINFO; + break; + } + /* XXX: should we reject an empty realm? */ + if (authinfolen < + sizeof(optinfo->delayedauth_keyid) + 16) { + goto malformed; + } + + optinfo->delayedauth_realmlen = authinfolen - + (sizeof(optinfo->delayedauth_keyid) + 16); + optinfo->delayedauth_realmval = + malloc(optinfo->delayedauth_realmlen); + if (optinfo->delayedauth_realmval == NULL) { + dprintf(LOG_WARNING, FNAME, "failed " + "allocate memory for auth realm"); + goto fail; + } + memcpy(optinfo->delayedauth_realmval, cp, + optinfo->delayedauth_realmlen); + cp += optinfo->delayedauth_realmlen; + + memcpy(&optinfo->delayedauth_keyid, cp, + sizeof(optinfo->delayedauth_keyid)); + optinfo->delayedauth_keyid = + ntohl(optinfo->delayedauth_keyid); + cp += sizeof(optinfo->delayedauth_keyid); + + optinfo->delayedauth_offset = cp - bp; + cp += 16; + + dprintf(LOG_DEBUG, "", " auth key ID: %x, " + "offset=%d, realmlen=%d", + optinfo->delayedauth_keyid, + optinfo->delayedauth_offset, + optinfo->delayedauth_realmlen); + break; +#ifdef notyet + case DHCP6_AUTHPROTO_RECONFIG: + break; +#endif + default: + dprintf(LOG_INFO, FNAME, + "unsupported authentication protocol: %d", + *cp); + goto fail; + } + break; + case DH6OPT_RAPID_COMMIT: + if (optlen != 0) + goto malformed; + optinfo->rapidcommit = 1; + break; + case DH6OPT_INTERFACE_ID: + if ((optinfo->ifidopt_id = malloc(optlen)) == NULL) + goto fail; + memcpy(optinfo->ifidopt_id, cp, optlen); + optinfo->ifidopt_len = optlen; + break; + case DH6OPT_SIP_SERVER_D: + if (dhcp6_get_domain(optlen, cp, opt, + &optinfo->sipname_list) == -1) + goto fail; + break; + case DH6OPT_DNSNAME: + if (dhcp6_get_domain(optlen, cp, opt, + &optinfo->dnsname_list) == -1) + goto fail; + break; + case DH6OPT_NIS_DOMAIN_NAME: + if (dhcp6_get_domain(optlen, cp, opt, + &optinfo->nisname_list) == -1) + goto fail; + break; + case DH6OPT_NISP_DOMAIN_NAME: + if (dhcp6_get_domain(optlen, cp, opt, + &optinfo->nispname_list) == -1) + goto fail; + break; + case DH6OPT_BCMCS_SERVER_D: + if (dhcp6_get_domain(optlen, cp, opt, + &optinfo->bcmcsname_list) == -1) + goto fail; + break; + case DH6OPT_SIP_SERVER_A: + if (dhcp6_get_addr(optlen, cp, opt, + &optinfo->sip_list) == -1) + goto fail; + break; + case DH6OPT_DNS: + if (dhcp6_get_addr(optlen, cp, opt, + &optinfo->dns_list) == -1) + goto fail; + break; + case DH6OPT_NIS_SERVERS: + if (dhcp6_get_addr(optlen, cp, opt, + &optinfo->nis_list) == -1) + goto fail; + break; + case DH6OPT_NISP_SERVERS: + if (dhcp6_get_addr(optlen, cp, opt, + &optinfo->nisp_list) == -1) + goto fail; + break; + case DH6OPT_BCMCS_SERVER_A: + if (dhcp6_get_addr(optlen, cp, opt, + &optinfo->bcmcs_list) == -1) + goto fail; + break; + case DH6OPT_NTP: + if (dhcp6_get_addr(optlen, cp, opt, + &optinfo->ntp_list) == -1) + goto fail; + break; + case DH6OPT_IA_PD: + if (optlen + sizeof(struct dhcp6opt) < + sizeof(optia)) + goto malformed; + memcpy(&optia, p, sizeof(optia)); + ia.iaid = ntohl(optia.dh6_ia_iaid); + ia.t1 = ntohl(optia.dh6_ia_t1); + ia.t2 = ntohl(optia.dh6_ia_t2); + + dprintf(LOG_DEBUG, "", + " IA_PD: ID=%lu, T1=%lu, T2=%lu", + ia.iaid, ia.t1, ia.t2); + + /* duplication check */ + if (dhcp6_find_listval(&optinfo->iapd_list, + DHCP6_LISTVAL_IAPD, &ia, 0)) { + dprintf(LOG_INFO, FNAME, + "duplicated IA_PD %lu", ia.iaid); + break; /* ignore this IA_PD */ + } + + /* take care of sub-options */ + TAILQ_INIT(&sublist); + if (copyin_option(opt, + (struct dhcp6opt *)((char *)p + sizeof(optia)), + (struct dhcp6opt *)(cp + optlen), &sublist)) { + goto fail; + } + + /* link this option set */ + if (dhcp6_add_listval(&optinfo->iapd_list, + DHCP6_LISTVAL_IAPD, &ia, &sublist) == NULL) { + dhcp6_clear_list(&sublist); + goto fail; + } + dhcp6_clear_list(&sublist); + + break; + case DH6OPT_REFRESHTIME: + if (optlen != 4) + goto malformed; + memcpy(&val32, cp, sizeof(val32)); + val32 = ntohl(val32); + dprintf(LOG_DEBUG, "", + " information refresh time: %lu", val32); + if (val32 < DHCP6_IRT_MINIMUM) { + /* + * A client MUST use the refresh time + * IRT_MINIMUM if it receives the option with a + * value less than IRT_MINIMUM. + * [draft-ietf-dhc-lifetime-02.txt, + * Section 3.2] + */ + dprintf(LOG_INFO, FNAME, + "refresh time is too small (%d), adjusted", + val32); + val32 = DHCP6_IRT_MINIMUM; + } + if (optinfo->refreshtime != DH6OPT_REFRESHTIME_UNDEF) { + dprintf(LOG_INFO, FNAME, + "duplicated refresh time option"); + } else + optinfo->refreshtime = (int64_t)val32; + break; + case DH6OPT_IA_NA: + if (optlen + sizeof(struct dhcp6opt) < + sizeof(optia)) + goto malformed; + memcpy(&optia, p, sizeof(optia)); + ia.iaid = ntohl(optia.dh6_ia_iaid); + ia.t1 = ntohl(optia.dh6_ia_t1); + ia.t2 = ntohl(optia.dh6_ia_t2); + + dprintf(LOG_DEBUG, "", + " IA_NA: ID=%lu, T1=%lu, T2=%lu", + ia.iaid, ia.t1, ia.t2); + + /* duplication check */ + if (dhcp6_find_listval(&optinfo->iana_list, + DHCP6_LISTVAL_IANA, &ia, 0)) { + dprintf(LOG_INFO, FNAME, + "duplicated IA_NA %lu", ia.iaid); + break; /* ignore this IA_NA */ + } + + /* take care of sub-options */ + TAILQ_INIT(&sublist); + if (copyin_option(opt, + (struct dhcp6opt *)((char *)p + sizeof(optia)), + (struct dhcp6opt *)(cp + optlen), &sublist)) { + goto fail; + } + + /* link this option set */ + if (dhcp6_add_listval(&optinfo->iana_list, + DHCP6_LISTVAL_IANA, &ia, &sublist) == NULL) { + dhcp6_clear_list(&sublist); + goto fail; + } + dhcp6_clear_list(&sublist); + + break; + default: + /* no option specific behavior */ + dprintf(LOG_INFO, FNAME, + "unknown or unexpected DHCP6 option %s, len %d", + dhcp6optstr(opt), optlen); + break; + } + } + + return (0); + + malformed: + dprintf(LOG_INFO, FNAME, "malformed DHCP option: type %d, len %d", + opt, optlen); + fail: + dhcp6_clear_options(optinfo); + return (-1); +} + +static char * +dnsdecode(sp, ep, buf, bufsiz) + u_char **sp; + u_char *ep; + char *buf; + size_t bufsiz; +{ + int i, l; + u_char *cp; + char tmpbuf[MAXDNAME + 1]; + + cp = *sp; + *buf = '\0'; + i = 0; /* XXX: appease gcc */ + + if (cp >= ep) + return (NULL); + while (cp < ep) { + i = *cp; + if (i == 0 || cp != *sp) { + if (strlcat((char *)buf, ".", bufsiz) >= bufsiz) + return (NULL); /* result overrun */ + } + if (i == 0) + break; + cp++; + + if (i > 0x3f) + return (NULL); /* invalid label */ + + if (i > ep - cp) + return (NULL); /* source overrun */ + while (i-- > 0 && cp < ep) { + if (!isprint(*cp)) /* we don't accept non-printables */ + return (NULL); + l = snprintf(tmpbuf, sizeof(tmpbuf), "%c" , *cp); + if (l >= sizeof(tmpbuf) || l < 0) + return (NULL); + if (strlcat(buf, tmpbuf, bufsiz) >= bufsiz) + return (NULL); /* result overrun */ + cp++; + } + } + if (i != 0) + return (NULL); /* not terminated */ + cp++; + *sp = cp; + return (buf); +} + +static int +copyin_option(type, p, ep, list) + int type; + struct dhcp6opt *p, *ep; + struct dhcp6_list *list; +{ + int opt, optlen; + char *cp; + struct dhcp6opt *np, opth; + struct dhcp6opt_stcode opt_stcode; + struct dhcp6opt_ia_pd_prefix opt_iapd_prefix; + struct dhcp6_prefix iapd_prefix; + struct dhcp6opt_ia_addr opt_ia_addr; + struct dhcp6_prefix ia_addr; + struct dhcp6_list sublist; + + TAILQ_INIT(&sublist); + + for (; p + 1 <= ep; p = np) { + memcpy(&opth, p, sizeof(opth)); + optlen = ntohs(opth.dh6opt_len); + opt = ntohs(opth.dh6opt_type); + + cp = (char *)(p + 1); + np = (struct dhcp6opt *)(cp + optlen); + + dprintf(LOG_DEBUG, FNAME, "get DHCP option %s, len %d", + dhcp6optstr(opt), optlen); + + if (np > ep) { + dprintf(LOG_INFO, FNAME, "malformed DHCP option"); + goto fail; + } + + switch (opt) { + case DH6OPT_IA_PD_PREFIX: + /* check option context */ + if (type != DH6OPT_IA_PD) { + dprintf(LOG_INFO, FNAME, + "%s is an invalid position for %s", + dhcp6optstr(type), dhcp6optstr(opt)); + goto fail; + } + /* check option length */ + if (optlen + sizeof(opth) < sizeof(opt_iapd_prefix)) + goto malformed; + + /* copy and convert option values */ + memcpy(&opt_iapd_prefix, p, sizeof(opt_iapd_prefix)); + if (opt_iapd_prefix.dh6_iapd_prefix_prefix_len > 128) { + dprintf(LOG_INFO, FNAME, + "invalid prefix length (%d)", + opt_iapd_prefix.dh6_iapd_prefix_prefix_len); + goto malformed; + } + iapd_prefix.pltime = ntohl(opt_iapd_prefix.dh6_iapd_prefix_preferred_time); + iapd_prefix.vltime = ntohl(opt_iapd_prefix.dh6_iapd_prefix_valid_time); + iapd_prefix.plen = + opt_iapd_prefix.dh6_iapd_prefix_prefix_len; + memcpy(&iapd_prefix.addr, + &opt_iapd_prefix.dh6_iapd_prefix_prefix_addr, + sizeof(iapd_prefix.addr)); + /* clear padding bits in the prefix address */ + prefix6_mask(&iapd_prefix.addr, iapd_prefix.plen); + + dprintf(LOG_DEBUG, FNAME, " IA_PD prefix: " + "%s/%d pltime=%lu vltime=%lu", + in6addr2str(&iapd_prefix.addr, 0), + iapd_prefix.plen, + iapd_prefix.pltime, iapd_prefix.vltime); + + if (dhcp6_find_listval(list, DHCP6_LISTVAL_PREFIX6, + &iapd_prefix, 0)) { + dprintf(LOG_INFO, FNAME, + "duplicated IA_PD prefix " + "%s/%d pltime=%lu vltime=%lu", + in6addr2str(&iapd_prefix.addr, 0), + iapd_prefix.plen, + iapd_prefix.pltime, iapd_prefix.vltime); + goto nextoption; + } + + /* take care of sub-options */ + TAILQ_INIT(&sublist); + if (copyin_option(opt, + (struct dhcp6opt *)((char *)p + + sizeof(opt_iapd_prefix)), np, &sublist)) { + goto fail; + } + + if (dhcp6_add_listval(list, DHCP6_LISTVAL_PREFIX6, + &iapd_prefix, &sublist) == NULL) { + dhcp6_clear_list(&sublist); + goto fail; + } + dhcp6_clear_list(&sublist); + break; + case DH6OPT_IAADDR: + /* check option context */ + if (type != DH6OPT_IA_NA) { + dprintf(LOG_INFO, FNAME, + "%s is an invalid position for %s", + dhcp6optstr(type), dhcp6optstr(opt)); + goto fail; + } + /* check option length */ + if (optlen + sizeof(opth) < sizeof(opt_ia_addr)) + goto malformed; + + /* copy and convert option values */ + memcpy(&opt_ia_addr, p, sizeof(opt_ia_addr)); + ia_addr.pltime = ntohl(opt_ia_addr.dh6_ia_addr_preferred_time); + ia_addr.vltime = ntohl(opt_ia_addr.dh6_ia_addr_valid_time); + memcpy(&ia_addr.addr, &opt_ia_addr.dh6_ia_addr_addr, + sizeof(ia_addr.addr)); + + dprintf(LOG_DEBUG, FNAME, " IA_NA address: " + "%s pltime=%lu vltime=%lu", + in6addr2str(&ia_addr.addr, 0), + ia_addr.pltime, ia_addr.vltime); + + if (dhcp6_find_listval(list, + DHCP6_LISTVAL_STATEFULADDR6, &ia_addr, 0)) { + dprintf(LOG_INFO, FNAME, + "duplicated IA_NA address" + "%s pltime=%lu vltime=%lu", + in6addr2str(&ia_addr.addr, 0), + ia_addr.pltime, ia_addr.vltime); + goto nextoption; + } + + /* take care of sub-options */ + TAILQ_INIT(&sublist); + if (copyin_option(opt, + (struct dhcp6opt *)((char *)p + + sizeof(opt_ia_addr)), np, &sublist)) { + goto fail; + } + + if (dhcp6_add_listval(list, DHCP6_LISTVAL_STATEFULADDR6, + &ia_addr, &sublist) == NULL) { + dhcp6_clear_list(&sublist); + goto fail; + } + dhcp6_clear_list(&sublist); + break; + case DH6OPT_STATUS_CODE: + /* check option context */ + if (type != DH6OPT_IA_PD && + type != DH6OPT_IA_PD_PREFIX && + type != DH6OPT_IA_NA && + type != DH6OPT_IAADDR) { + dprintf(LOG_INFO, FNAME, + "%s is an invalid position for %s", + dhcp6optstr(type), dhcp6optstr(opt)); + goto nextoption; /* or discard the message? */ + } + /* check option length */ + if (optlen + sizeof(opth) < sizeof(opt_stcode)) + goto malformed; + + /* copy and convert option values */ + memcpy(&opt_stcode, p, sizeof(opt_stcode)); + opt_stcode.dh6_stcode_code = + ntohs(opt_stcode.dh6_stcode_code); + + dprintf(LOG_DEBUG, "", " status code: %s", + dhcp6_stcodestr(opt_stcode.dh6_stcode_code)); + + /* duplication check */ + if (dhcp6_find_listval(list, DHCP6_LISTVAL_STCODE, + &opt_stcode.dh6_stcode_code, 0)) { + dprintf(LOG_INFO, FNAME, + "duplicated status code (%d)", + opt_stcode.dh6_stcode_code); + goto nextoption; + } + + /* copy-in the code value */ + if (dhcp6_add_listval(list, DHCP6_LISTVAL_STCODE, + &opt_stcode.dh6_stcode_code, NULL) == NULL) + goto fail; + + break; + } + nextoption: + ; + } + + return (0); + + malformed: + dprintf(LOG_INFO, "", " malformed DHCP option: type %d", opt); + + fail: + dhcp6_clear_list(&sublist); + return (-1); +} + +static char * +sprint_uint64(buf, buflen, i64) + char *buf; + int buflen; + u_int64_t i64; +{ + u_int16_t rd0, rd1, rd2, rd3; + u_int16_t *ptr = (u_int16_t *)(void *)&i64; + + rd0 = ntohs(*ptr++); + rd1 = ntohs(*ptr++); + rd2 = ntohs(*ptr++); + rd3 = ntohs(*ptr); + + snprintf(buf, buflen, "%04x %04x %04x %04x", rd0, rd1, rd2, rd3); + + return (buf); +} + +static char * +sprint_auth(optinfo) + struct dhcp6_optinfo *optinfo; +{ + static char ret[1024]; /* XXX: thread unsafe */ + char *proto, proto0[] = "unknown(255)"; + char *alg, alg0[] = "unknown(255)"; + char *rdm, rdm0[] = "unknown(255)"; + char rd[] = "ffff ffff ffff ffff"; + + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + proto = "delayed"; + break; + case DHCP6_AUTHPROTO_RECONFIG: + proto = "reconfig"; + break; + default: + snprintf(proto0, sizeof(proto0), "unknown(%d)", + optinfo->authproto & 0xff); + proto = proto0; + break; + } + + switch (optinfo->authalgorithm) { + case DHCP6_AUTHALG_HMACMD5: + alg = "HMAC-MD5"; + break; + default: + snprintf(alg0, sizeof(alg0), "unknown(%d)", + optinfo->authalgorithm & 0xff); + alg = alg0; + break; + } + + switch (optinfo->authrdm) { + case DHCP6_AUTHRDM_MONOCOUNTER: + rdm = "mono counter"; + break; + default: + snprintf(rdm0, sizeof(rdm0), "unknown(%d)", optinfo->authrdm); + rdm = rdm0; + } + + (void)sprint_uint64(rd, sizeof(rd), optinfo->authrd); + + snprintf(ret, sizeof(ret), "proto: %s, alg: %s, RDM: %s, RD: %s", + proto, alg, rdm, rd); + + return (ret); +} + +static int +copy_option(type, len, val, optp, ep, totallenp) + u_int16_t type, len; + void *val; + struct dhcp6opt **optp, *ep; + int *totallenp; +{ + struct dhcp6opt *opt = *optp, opth; + + if ((void *)ep - (void *)optp < len + sizeof(struct dhcp6opt)) { + dprintf(LOG_INFO, FNAME, + "option buffer short for %s", dhcp6optstr(type)); + return (-1); + } + opth.dh6opt_type = htons(type); + opth.dh6opt_len = htons(len); + memcpy(opt, &opth, sizeof(opth)); + if (len != 0) + memcpy(opt + 1, val, len); + + *optp = (struct dhcp6opt *)((char *)(opt + 1) + len); + *totallenp += sizeof(struct dhcp6opt) + len; + dprintf(LOG_DEBUG, FNAME, "set %s (len %d)", dhcp6optstr(type), len); + + return (0); +} + +int +dhcp6_set_options(type, optbp, optep, optinfo) + int type; + struct dhcp6opt *optbp, *optep; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6opt *p = optbp; + struct dhcp6_listval *stcode, *op; + int len = 0, optlen; + char *tmpbuf = NULL; + + if (optinfo->clientID.duid_len) { + if (copy_option(DH6OPT_CLIENTID, optinfo->clientID.duid_len, + optinfo->clientID.duid_id, &p, optep, &len) != 0) { + goto fail; + } + } + + if (optinfo->serverID.duid_len) { + if (copy_option(DH6OPT_SERVERID, optinfo->serverID.duid_len, + optinfo->serverID.duid_id, &p, optep, &len) != 0) { + goto fail; + } + } + + for (op = TAILQ_FIRST(&optinfo->iana_list); op; + op = TAILQ_NEXT(op, link)) { + int optlen; + + tmpbuf = NULL; + if ((optlen = copyout_option(NULL, NULL, op)) < 0) { + dprintf(LOG_INFO, FNAME, + "failed to count option length"); + goto fail; + } + if ((void *)optep - (void *)p < optlen) { + dprintf(LOG_INFO, FNAME, "short buffer"); + goto fail; + } + if ((tmpbuf = malloc(optlen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "memory allocation failed for IA_NA options"); + goto fail; + } + if (copyout_option(tmpbuf, tmpbuf + optlen, op) < 0) { + dprintf(LOG_ERR, FNAME, + "failed to construct an IA_NA option"); + goto fail; + } + memcpy(p, tmpbuf, optlen); + free(tmpbuf); + tmpbuf = NULL; + p = (struct dhcp6opt *)((char *)p + optlen); + len += optlen; + } + + if (optinfo->rapidcommit) { + if (copy_option(DH6OPT_RAPID_COMMIT, 0, NULL, &p, + optep, &len) != 0) { + goto fail; + } + } + + if (optinfo->pref != DH6OPT_PREF_UNDEF) { + u_int8_t p8 = (u_int8_t)optinfo->pref; + + if (copy_option(DH6OPT_PREFERENCE, sizeof(p8), &p8, &p, + optep, &len) != 0) { + goto fail; + } + } + + if (optinfo->elapsed_time != DH6OPT_ELAPSED_TIME_UNDEF) { + u_int16_t p16 = (u_int16_t)optinfo->elapsed_time; + + p16 = htons(p16); + if (copy_option(DH6OPT_ELAPSED_TIME, sizeof(p16), &p16, &p, + optep, &len) != 0) { + goto fail; + } + } + + for (stcode = TAILQ_FIRST(&optinfo->stcode_list); stcode; + stcode = TAILQ_NEXT(stcode, link)) { + u_int16_t code; + + code = htons(stcode->val_num16); + if (copy_option(DH6OPT_STATUS_CODE, sizeof(code), &code, &p, + optep, &len) != 0) { + goto fail; + } + } + + if (!TAILQ_EMPTY(&optinfo->reqopt_list)) { + struct dhcp6_listval *opt; + u_int16_t *valp; + int buflen; + + tmpbuf = NULL; + buflen = dhcp6_count_list(&optinfo->reqopt_list) * + sizeof(u_int16_t); + if ((tmpbuf = malloc(buflen)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation failed for options"); + goto fail; + } + optlen = 0; + valp = (u_int16_t *)tmpbuf; + for (opt = TAILQ_FIRST(&optinfo->reqopt_list); opt; + opt = TAILQ_NEXT(opt, link)) { + /* + * Information request option can only be specified + * in information-request messages. + * [draft-ietf-dhc-lifetime-02.txt, Section 3.2] + */ + if (opt->val_num == DH6OPT_REFRESHTIME && + type != DH6_INFORM_REQ) { + dprintf(LOG_DEBUG, FNAME, + "refresh time option is not requested " + "for %s", dhcp6msgstr(type)); + } + + *valp = htons((u_int16_t)opt->val_num); + valp++; + optlen += sizeof(u_int16_t); + } + if (optlen > 0 && + copy_option(DH6OPT_ORO, optlen, tmpbuf, &p, + optep, &len) != 0) { + goto fail; + } + free(tmpbuf); + tmpbuf = NULL; + } + + if (dhcp6_set_domain(DH6OPT_SIP_SERVER_D, &optinfo->sipname_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_addr(DH6OPT_SIP_SERVER_A, &optinfo->sip_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_addr(DH6OPT_DNS, &optinfo->dns_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_domain(DH6OPT_DNSNAME, &optinfo->dnsname_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_addr(DH6OPT_NIS_SERVERS, &optinfo->nis_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_addr(DH6OPT_NISP_SERVERS, &optinfo->nisp_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_domain(DH6OPT_NIS_DOMAIN_NAME, &optinfo->nisname_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_domain(DH6OPT_NISP_DOMAIN_NAME, &optinfo->nispname_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_addr(DH6OPT_NTP, &optinfo->ntp_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_domain(DH6OPT_BCMCS_SERVER_D, &optinfo->bcmcsname_list, + &p, optep, &len) != 0) + goto fail; + + if (dhcp6_set_addr(DH6OPT_BCMCS_SERVER_A, &optinfo->bcmcs_list, + &p, optep, &len) != 0) + goto fail; + + for (op = TAILQ_FIRST(&optinfo->iapd_list); op; + op = TAILQ_NEXT(op, link)) { + int optlen; + + tmpbuf = NULL; + if ((optlen = copyout_option(NULL, NULL, op)) < 0) { + dprintf(LOG_INFO, FNAME, + "failed to count option length"); + goto fail; + } + if ((void *)optep - (void *)p < optlen) { + dprintf(LOG_INFO, FNAME, "short buffer"); + goto fail; + } + if ((tmpbuf = malloc(optlen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "memory allocation failed for IA_PD options"); + goto fail; + } + if (copyout_option(tmpbuf, tmpbuf + optlen, op) < 0) { + dprintf(LOG_ERR, FNAME, + "failed to construct an IA_PD option"); + goto fail; + } + memcpy(p, tmpbuf, optlen); + free(tmpbuf); + tmpbuf = NULL; + p = (struct dhcp6opt *)((char *)p + optlen); + len += optlen; + } + + if (optinfo->relaymsg_len) { + if (copy_option(DH6OPT_RELAY_MSG, optinfo->relaymsg_len, + optinfo->relaymsg_msg, &p, optep, &len) != 0) { + goto fail; + } + } + + if (optinfo->ifidopt_id) { + if (copy_option(DH6OPT_INTERFACE_ID, optinfo->ifidopt_len, + optinfo->ifidopt_id, &p, optep, &len) != 0) { + goto fail; + } + } + + if (optinfo->refreshtime != DH6OPT_REFRESHTIME_UNDEF) { + u_int32_t p32 = (u_int32_t)optinfo->refreshtime; + + p32 = htonl(p32); + if (copy_option(DH6OPT_REFRESHTIME, sizeof(p32), &p32, &p, + optep, &len) != 0) { + goto fail; + } + } + + if (optinfo->authproto != DHCP6_AUTHPROTO_UNDEF) { + struct dhcp6opt_auth *auth; + int authlen; + char *authinfo; + + authlen = sizeof(*auth); + if (!(optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) { + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + /* Realm + key ID + HMAC-MD5 */ + authlen += optinfo->delayedauth_realmlen + + sizeof(optinfo->delayedauth_keyid) + 16; + break; +#ifdef notyet + case DHCP6_AUTHPROTO_RECONFIG: + /* type + key-or-HAMC */ + authlen += 17; + break; +#endif + default: + dprintf(LOG_ERR, FNAME, + "unexpected authentication protocol"); + goto fail; + } + } + if ((auth = malloc(authlen)) == NULL) { + dprintf(LOG_WARNING, FNAME, "failed to allocate " + "memory for authentication information"); + goto fail; + } + + memset(auth, 0, authlen); + /* copy_option will take care of type and len later */ + auth->dh6_auth_proto = (u_int8_t)optinfo->authproto; + auth->dh6_auth_alg = (u_int8_t)optinfo->authalgorithm; + auth->dh6_auth_rdm = (u_int8_t)optinfo->authrdm; + memcpy(auth->dh6_auth_rdinfo, &optinfo->authrd, + sizeof(auth->dh6_auth_rdinfo)); + + if (!(optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) { + u_int32_t p32; + + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + authinfo = (char *)(auth + 1); + + /* copy realm */ + memcpy(authinfo, optinfo->delayedauth_realmval, + optinfo->delayedauth_realmlen); + authinfo += optinfo->delayedauth_realmlen; + + /* copy key ID (need memcpy for alignment) */ + p32 = htonl(optinfo->delayedauth_keyid); + memcpy(authinfo, &p32, sizeof(p32)); + + /* + * Set the offset so that the caller can + * calculate the HMAC. + */ + optinfo->delayedauth_offset = + ((char *)p - (char *)optbp) + authlen - 16; + + dprintf(LOG_DEBUG, FNAME, + "key ID %x, offset %d", + optinfo->delayedauth_keyid, + optinfo->delayedauth_offset); + break; +#ifdef notyet + case DHCP6_AUTHPROTO_RECONFIG: +#endif + default: + dprintf(LOG_ERR, FNAME, + "unexpected authentication protocol"); + free(auth); + goto fail; + } + } + + if (copy_option(DH6OPT_AUTH, authlen - 4, + &auth->dh6_auth_proto, &p, optep, &len) != 0) { + goto fail; + } + free(auth); + } + + return (len); + + fail: + if (tmpbuf) + free(tmpbuf); + return (-1); +} +#undef COPY_OPTION + +static ssize_t +dnsencode(name, buf, buflen) + const char *name; + char *buf; + size_t buflen; +{ + char *cp, *ep; + const char *p, *q; + int i; + int namelen = strlen(name); + + cp = buf; + ep = cp + buflen; + + /* if not certain about my name, return an empty buffer */ + if (namelen == 0) + return (0); + + p = name; + while (cp < ep && p < name + namelen) { + i = 0; + for (q = p; q < name + namelen && *q && *q != '.'; q++) + i++; + /* result does not fit into buf */ + if (cp + i + 1 >= ep) + goto fail; + /* + * DNS label length restriction, RFC1035 page 8. + * "i == 0" case is included here to avoid returning + * 0-length label on "foo..bar". + */ + if (i <= 0 || i >= 64) + goto fail; + *cp++ = i; + if (!isalpha(p[0]) || !isalnum(p[i - 1])) + goto fail; + while (i > 0) { + if (!isalnum(*p) && *p != '-') + goto fail; + if (isupper(*p)) + *cp++ = tolower(*p++); + else + *cp++ = *p++; + i--; + } + p = q; + if (p < name + namelen && *p == '.') + p++; + } + /* termination */ + if (cp + 1 >= ep) + goto fail; + *cp++ = '\0'; + return (cp - buf); + + fail: + return (-1); +} + +/* + * Construct a DHCPv6 option along with sub-options in the wire format. + * If the packet buffer is NULL, just calculate the length of the option + * (and sub-options) so that the caller can allocate a buffer to store the + * option(s). + * This function basically assumes that the caller prepares enough buffer to + * store all the options. However, it also takes the buffer end and checks + * the possibility of overrun for safety. + */ +static int +copyout_option(p, ep, optval) + char *p, *ep; + struct dhcp6_listval *optval; +{ + struct dhcp6opt *opt; + struct dhcp6opt_stcode stcodeopt; + struct dhcp6opt_ia ia; + struct dhcp6opt_ia_pd_prefix pd_prefix; + struct dhcp6opt_ia_addr ia_addr; + char *subp; + struct dhcp6_listval *subov; + int optlen, headlen, sublen, opttype; + + /* check invariant for safety */ + if (p && ep <= p) + return (-1); + + /* first, detect the length of the option head */ + switch(optval->type) { + case DHCP6_LISTVAL_IAPD: + memset(&ia, 0, sizeof(ia)); + headlen = sizeof(ia); + opttype = DH6OPT_IA_PD; + opt = (struct dhcp6opt *)(void *)&ia; + break; + case DHCP6_LISTVAL_IANA: + memset(&ia, 0, sizeof(ia)); + headlen = sizeof(ia); + opttype = DH6OPT_IA_NA; + opt = (struct dhcp6opt *)(void *)&ia; + break; + case DHCP6_LISTVAL_ADDR6: + memset(&pd_prefix, 0, sizeof(pd_prefix)); + headlen = sizeof(pd_prefix); + opttype = DH6OPT_IA_PD_PREFIX; + opt = (struct dhcp6opt *)&pd_prefix; + break; + case DHCP6_LISTVAL_PREFIX6: + memset(&pd_prefix, 0, sizeof(pd_prefix)); + headlen = sizeof(pd_prefix); + opttype = DH6OPT_IA_PD_PREFIX; + opt = (struct dhcp6opt *)&pd_prefix; + break; + case DHCP6_LISTVAL_STATEFULADDR6: + memset(&ia_addr, 0, sizeof(ia_addr)); + headlen = sizeof(ia_addr); + opttype = DH6OPT_IAADDR; + opt = (struct dhcp6opt *)&ia_addr; + break; + case DHCP6_LISTVAL_STCODE: + memset(&stcodeopt, 0, sizeof(stcodeopt)); + headlen = sizeof(stcodeopt); + opttype = DH6OPT_STATUS_CODE; + opt = (struct dhcp6opt *)(void *)&stcodeopt; + break; + default: + /* + * we encounter an unknown option. this should be an internal + * error. + */ + dprintf(LOG_ERR, FNAME, "unknown option: code %d", + optval->type); + return (-1); + } + + /* then, calculate the length of and/or fill in the sub-options */ + subp = NULL; + sublen = 0; + if (p) + subp = p + headlen; + for (subov = TAILQ_FIRST(&optval->sublist); subov; + subov = TAILQ_NEXT(subov, link)) { + int s; + + if ((s = copyout_option(subp, ep, subov)) < 0) + return (-1); + if (p) + subp += s; + sublen += s; + } + + /* finally, deal with the head part again */ + optlen = headlen + sublen; + if (!p) + return(optlen); + + dprintf(LOG_DEBUG, FNAME, "set %s", dhcp6optstr(opttype)); + if (ep - p < headlen) /* check it just in case */ + return (-1); + + /* fill in the common part */ + opt->dh6opt_type = htons(opttype); + opt->dh6opt_len = htons(optlen - sizeof(struct dhcp6opt)); + + /* fill in type specific fields */ + switch(optval->type) { + case DHCP6_LISTVAL_IAPD: + ia.dh6_ia_iaid = htonl(optval->val_ia.iaid); + ia.dh6_ia_t1 = htonl(optval->val_ia.t1); + ia.dh6_ia_t2 = htonl(optval->val_ia.t2); + break; + case DHCP6_LISTVAL_IANA: + ia.dh6_ia_iaid = htonl(optval->val_ia.iaid); + ia.dh6_ia_t1 = htonl(optval->val_ia.t1); + ia.dh6_ia_t2 = htonl(optval->val_ia.t2); + break; + case DHCP6_LISTVAL_PREFIX6: + pd_prefix.dh6_iapd_prefix_preferred_time = + htonl(optval->val_prefix6.pltime); + pd_prefix.dh6_iapd_prefix_valid_time = + htonl(optval->val_prefix6.vltime); + pd_prefix.dh6_iapd_prefix_prefix_len = + optval->val_prefix6.plen; + /* XXX: prefix_addr is badly aligned, so we need memcpy */ + memcpy(&pd_prefix.dh6_iapd_prefix_prefix_addr, + &optval->val_prefix6.addr, sizeof(struct in6_addr)); + break; + case DHCP6_LISTVAL_STATEFULADDR6: + ia_addr.dh6_ia_addr_preferred_time = + htonl(optval->val_statefuladdr6.pltime); + ia_addr.dh6_ia_addr_valid_time = + htonl(optval->val_statefuladdr6.vltime); + ia_addr.dh6_ia_addr_addr = optval->val_statefuladdr6.addr; + break; + case DHCP6_LISTVAL_STCODE: + stcodeopt.dh6_stcode_code = htons(optval->val_num16); + break; + default: + /* + * XXX: this case should be rejected at the beginning of this + * function. + */ + return (-1); + } + + /* copyout the data (p must be non NULL at this point) */ + memcpy(p, opt, headlen); + return (optlen); +} + +void +dhcp6_set_timeoparam(ev) + struct dhcp6_event *ev; +{ + ev->retrans = 0; + ev->init_retrans = 0; + ev->max_retrans_cnt = 0; + ev->max_retrans_dur = 0; + ev->max_retrans_time = 0; + + switch(ev->state) { + case DHCP6S_SOLICIT: + ev->init_retrans = SOL_TIMEOUT; + ev->max_retrans_time = SOL_MAX_RT; + break; + case DHCP6S_INFOREQ: + ev->init_retrans = INF_TIMEOUT; + ev->max_retrans_time = INF_MAX_RT; + break; + case DHCP6S_REQUEST: + ev->init_retrans = REQ_TIMEOUT; + ev->max_retrans_time = REQ_MAX_RT; + ev->max_retrans_cnt = REQ_MAX_RC; + break; + case DHCP6S_RENEW: + ev->init_retrans = REN_TIMEOUT; + ev->max_retrans_time = REN_MAX_RT; + break; + case DHCP6S_REBIND: + ev->init_retrans = REB_TIMEOUT; + ev->max_retrans_time = REB_MAX_RT; + break; + case DHCP6S_RELEASE: + ev->init_retrans = REL_TIMEOUT; + ev->max_retrans_cnt = REL_MAX_RC; + break; + default: + dprintf(LOG_ERR, FNAME, "unexpected event state %d on %s", + ev->state, ev->ifp->ifname); + exit(1); + } +} + +void +dhcp6_reset_timer(ev) + struct dhcp6_event *ev; +{ + double n, r; + char *statestr; + struct timeval interval; + + switch(ev->state) { + case DHCP6S_INIT: + /* + * The first Solicit message from the client on the interface + * MUST be delayed by a random amount of time between + * 0 and SOL_MAX_DELAY. + * [RFC3315 17.1.2] + * XXX: a random delay is also necessary before the first + * information-request message. Fortunately, the parameters + * and the algorithm for these two cases are the same. + * [RFC3315 18.1.5] + */ + ev->retrans = (random() % (SOL_MAX_DELAY)); + break; + default: + if (ev->state == DHCP6S_SOLICIT && ev->timeouts == 0) { + /* + * The first RT MUST be selected to be strictly + * greater than IRT by choosing RAND to be strictly + * greater than 0. + * [RFC3315 17.1.2] + */ + r = (double)((random() % 1000) + 1) / 10000; + n = ev->init_retrans + r * ev->init_retrans; + } else { + r = (double)((random() % 2000) - 1000) / 10000; + + if (ev->timeouts == 0) { + n = ev->init_retrans + r * ev->init_retrans; + } else + n = 2 * ev->retrans + r * ev->retrans; + } + if (ev->max_retrans_time && n > ev->max_retrans_time) + n = ev->max_retrans_time + r * ev->max_retrans_time; + ev->retrans = (long)n; + break; + } + + interval.tv_sec = (ev->retrans * 1000) / 1000000; + interval.tv_usec = (ev->retrans * 1000) % 1000000; + dhcp6_set_timer(&interval, ev->timer); + + statestr = dhcp6_event_statestr(ev); + + dprintf(LOG_DEBUG, FNAME, "reset a timer on %s, " + "state=%s, timeo=%d, retrans=%d", + ev->ifp->ifname, statestr, ev->timeouts, ev->retrans); +} + +int +duidcpy(dd, ds) + struct duid *dd, *ds; +{ + dd->duid_len = ds->duid_len; + if ((dd->duid_id = malloc(dd->duid_len)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (-1); + } + memcpy(dd->duid_id, ds->duid_id, dd->duid_len); + + return (0); +} + +int +duidcmp(d1, d2) + struct duid *d1, *d2; +{ + if (d1->duid_len == d2->duid_len) { + return (memcmp(d1->duid_id, d2->duid_id, d1->duid_len)); + } else + return (-1); +} + +void +duidfree(duid) + struct duid *duid; +{ + if (duid->duid_id) + free(duid->duid_id); + duid->duid_id = NULL; + duid->duid_len = 0; +} + +/* + * Provide an NTP-format timestamp as a replay detection counter + * as mentioned in RFC3315. + */ +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ +int +get_rdvalue(rdm, rdvalue, rdsize) + int rdm; + void *rdvalue; + size_t rdsize; +{ +#if defined(HAVE_CLOCK_GETTIME) + struct timespec tp; + double nsec; +#else + struct timeval tv; +#endif + u_int32_t u32, l32; + + if (rdm != DHCP6_AUTHRDM_MONOCOUNTER) { + dprintf(LOG_INFO, FNAME, "unsupported RDM (%d)", rdm); + return (-1); + } + if (rdsize != sizeof(u_int64_t)) { + dprintf(LOG_INFO, FNAME, "unsupported RD size (%d)", rdsize); + return (-1); + } + +#if defined(HAVE_CLOCK_GETTIME) + if (clock_gettime(CLOCK_REALTIME, &tp)) { + dprintf(LOG_WARNING, FNAME, "clock_gettime failed: %s", + strerror(errno)); + return (-1); + } + + u32 = (u_int32_t)tp.tv_sec; + u32 += JAN_1970; + + nsec = (double)tp.tv_nsec / 1e9 * 0x100000000ULL; + /* nsec should be smaller than 2^32 */ + l32 = (u_int32_t)nsec; +#else + if (gettimeofday(&tv, NULL) != 0) { + dprintf(LOG_WARNING, FNAME, "gettimeofday failed: %s", + strerror(errno)); + return (-1); + } + u32 = (u_int32_t)tv.tv_sec; + u32 += JAN_1970; + l32 = (u_int32_t)tv.tv_usec; +#endif /* HAVE_CLOCK_GETTIME */ + + u32 = htonl(u32); + l32 = htonl(l32); + + memcpy(rdvalue, &u32, sizeof(u32)); + memcpy((char *)rdvalue + sizeof(u32), &l32, sizeof(l32)); + + return (0); +} + +char * +dhcp6optstr(type) + int type; +{ + static char genstr[sizeof("opt_65535") + 1]; /* XXX thread unsafe */ + + if (type > 65535) + return ("INVALID option"); + + switch(type) { + case DH6OPT_CLIENTID: + return ("client ID"); + case DH6OPT_SERVERID: + return ("server ID"); + case DH6OPT_IA_NA: + return ("identity association"); + case DH6OPT_IA_TA: + return ("IA for temporary"); + case DH6OPT_IAADDR: + return ("IA address"); + case DH6OPT_ORO: + return ("option request"); + case DH6OPT_PREFERENCE: + return ("preference"); + case DH6OPT_ELAPSED_TIME: + return ("elapsed time"); + case DH6OPT_RELAY_MSG: + return ("relay message"); + case DH6OPT_AUTH: + return ("authentication"); + case DH6OPT_UNICAST: + return ("server unicast"); + case DH6OPT_STATUS_CODE: + return ("status code"); + case DH6OPT_RAPID_COMMIT: + return ("rapid commit"); + case DH6OPT_USER_CLASS: + return ("user class"); + case DH6OPT_VENDOR_CLASS: + return ("vendor class"); + case DH6OPT_VENDOR_OPTS: + return ("vendor specific info"); + case DH6OPT_INTERFACE_ID: + return ("interface ID"); + case DH6OPT_RECONF_MSG: + return ("reconfigure message"); + case DH6OPT_SIP_SERVER_D: + return ("SIP domain name"); + case DH6OPT_SIP_SERVER_A: + return ("SIP server address"); + case DH6OPT_DNS: + return ("DNS"); + case DH6OPT_DNSNAME: + return ("domain search list"); + case DH6OPT_NTP: + return ("NTP server"); + case DH6OPT_IA_PD: + return ("IA_PD"); + case DH6OPT_IA_PD_PREFIX: + return ("IA_PD prefix"); + case DH6OPT_REFRESHTIME: + return ("information refresh time"); + case DH6OPT_NIS_SERVERS: + return ("NIS servers"); + case DH6OPT_NISP_SERVERS: + return ("NIS+ servers"); + case DH6OPT_NIS_DOMAIN_NAME: + return ("NIS domain name"); + case DH6OPT_NISP_DOMAIN_NAME: + return ("NIS+ domain name"); + case DH6OPT_BCMCS_SERVER_D: + return ("BCMCS domain name"); + case DH6OPT_BCMCS_SERVER_A: + return ("BCMCS server address"); + case DH6OPT_GEOCONF_CIVIC: + return ("Geoconf Civic"); + case DH6OPT_REMOTE_ID: + return ("remote ID"); + case DH6OPT_SUBSCRIBER_ID: + return ("subscriber ID"); + case DH6OPT_CLIENT_FQDN: + return ("client FQDN"); + default: + snprintf(genstr, sizeof(genstr), "opt_%d", type); + return (genstr); + } +} + +char * +dhcp6msgstr(type) + int type; +{ + static char genstr[sizeof("msg255") + 1]; /* XXX thread unsafe */ + + if (type > 255) + return ("INVALID msg"); + + switch(type) { + case DH6_SOLICIT: + return ("solicit"); + case DH6_ADVERTISE: + return ("advertise"); + case DH6_REQUEST: + return ("request"); + case DH6_CONFIRM: + return ("confirm"); + case DH6_RENEW: + return ("renew"); + case DH6_REBIND: + return ("rebind"); + case DH6_REPLY: + return ("reply"); + case DH6_RELEASE: + return ("release"); + case DH6_DECLINE: + return ("decline"); + case DH6_RECONFIGURE: + return ("reconfigure"); + case DH6_INFORM_REQ: + return ("information request"); + case DH6_RELAY_FORW: + return ("relay-forward"); + case DH6_RELAY_REPLY: + return ("relay-reply"); + default: + snprintf(genstr, sizeof(genstr), "msg%d", type); + return (genstr); + } +} + +char * +dhcp6_stcodestr(code) + u_int16_t code; +{ + static char genstr[sizeof("code255") + 1]; /* XXX thread unsafe */ + + if (code > 255) + return ("INVALID code"); + + switch(code) { + case DH6OPT_STCODE_SUCCESS: + return ("success"); + case DH6OPT_STCODE_UNSPECFAIL: + return ("unspec failure"); + case DH6OPT_STCODE_NOADDRSAVAIL: + return ("no addresses"); + case DH6OPT_STCODE_NOBINDING: + return ("no binding"); + case DH6OPT_STCODE_NOTONLINK: + return ("not on-link"); + case DH6OPT_STCODE_USEMULTICAST: + return ("use multicast"); + case DH6OPT_STCODE_NOPREFIXAVAIL: + return ("no prefixes"); + default: + snprintf(genstr, sizeof(genstr), "code%d", code); + return (genstr); + } +} + +char * +duidstr(duid) + struct duid *duid; +{ + int i, n; + char *cp, *ep; + static char duidstr[sizeof("xx:") * 128 + sizeof("...")]; + + cp = duidstr; + ep = duidstr + sizeof(duidstr); + for (i = 0; i < duid->duid_len && i <= 128; i++) { + n = snprintf(cp, ep - cp, "%s%02x", i == 0 ? "" : ":", + duid->duid_id[i] & 0xff); + if (n < 0) + return NULL; + cp += n; + } + if (i < duid->duid_len) + snprintf(cp, ep - cp, "%s", "..."); + + return (duidstr); +} + +char *dhcp6_event_statestr(ev) + struct dhcp6_event *ev; +{ + switch(ev->state) { + case DHCP6S_INIT: + return ("INIT"); + case DHCP6S_SOLICIT: + return ("SOLICIT"); + case DHCP6S_INFOREQ: + return ("INFOREQ"); + case DHCP6S_REQUEST: + return ("REQUEST"); + case DHCP6S_RENEW: + return ("RENEW"); + case DHCP6S_REBIND: + return ("REBIND"); + case DHCP6S_RELEASE: + return ("RELEASE"); + case DHCP6S_IDLE: + return ("IDLE"); + default: + return ("???"); /* XXX */ + } +} + +void +setloglevel(debuglevel) + int debuglevel; +{ + if (foreground) { + switch(debuglevel) { + case 0: + debug_thresh = LOG_ERR; + break; + case 1: + debug_thresh = LOG_INFO; + break; + default: + debug_thresh = LOG_DEBUG; + break; + } + } else { + switch(debuglevel) { + case 0: + setlogmask(LOG_UPTO(LOG_ERR)); + break; + case 1: + setlogmask(LOG_UPTO(LOG_INFO)); + break; + } + } +} + +void +dprintf(int level, const char *fname, const char *fmt, ...) +{ + va_list ap; + char logbuf[LINE_MAX]; + int printfname = 1; + + va_start(ap, fmt); + vsnprintf(logbuf, sizeof(logbuf), fmt, ap); + + if (*fname == '\0') + printfname = 0; + + if (foreground && debug_thresh >= level) { + time_t now; + struct tm *tm_now; + const char *month[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + + if ((now = time(NULL)) < 0) + exit(1); /* XXX */ + tm_now = localtime(&now); + fprintf(stderr, "%3s/%02d/%04d %02d:%02d:%02d: %s%s%s\n", + month[tm_now->tm_mon], tm_now->tm_mday, + tm_now->tm_year + 1900, + tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec, + fname, printfname ? ": " : "", + logbuf); + } else + syslog(level, "%s%s%s", fname, printfname ? ": " : "", logbuf); +} + +int +ifaddrconf(cmd, ifname, addr, plen, pltime, vltime) + ifaddrconf_cmd_t cmd; + char *ifname; + struct sockaddr_in6 *addr; + int plen; + int pltime; + int vltime; +{ +#ifdef __KAME__ + struct in6_aliasreq req; +#endif +#ifdef __linux__ + struct in6_ifreq req; + struct ifreq ifr; +#endif +#ifdef __sun__ + struct lifreq req; +#endif + unsigned long ioctl_cmd; + char *cmdstr; + int s; /* XXX overhead */ + + switch(cmd) { + case IFADDRCONF_ADD: + cmdstr = "add"; +#ifdef __KAME__ + ioctl_cmd = SIOCAIFADDR_IN6; +#endif +#ifdef __linux__ + ioctl_cmd = SIOCSIFADDR; +#endif +#ifdef __sun__ + ioctl_cmd = SIOCLIFADDIF; +#endif + break; + case IFADDRCONF_REMOVE: + cmdstr = "remove"; +#ifdef __KAME__ + ioctl_cmd = SIOCDIFADDR_IN6; +#endif +#ifdef __linux__ + ioctl_cmd = SIOCDIFADDR; +#endif +#ifdef __sun__ + ioctl_cmd = SIOCLIFREMOVEIF; +#endif + break; + default: + return (-1); + } + + if ((s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + dprintf(LOG_ERR, FNAME, "can't open a temporary socket: %s", + strerror(errno)); + return (-1); + } + + memset(&req, 0, sizeof(req)); +#ifdef __KAME__ + req.ifra_addr = *addr; + memcpy(req.ifra_name, ifname, sizeof(req.ifra_name)); + (void)sa6_plen2mask(&req.ifra_prefixmask, plen); + /* XXX: should lifetimes be calculated based on the lease duration? */ + req.ifra_lifetime.ia6t_vltime = vltime; + req.ifra_lifetime.ia6t_pltime = pltime; +#endif +#ifdef __linux__ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); + if (ioctl(s, SIOGIFINDEX, &ifr) < 0) { + dprintf(LOG_NOTICE, FNAME, "failed to get the index of %s: %s", + ifname, strerror(errno)); + close(s); + return (-1); + } + memcpy(&req.ifr6_addr, &addr->sin6_addr, sizeof(struct in6_addr)); + req.ifr6_prefixlen = plen; + req.ifr6_ifindex = ifr.ifr_ifindex; +#endif +#ifdef __sun__ + strncpy(req.lifr_name, ifname, sizeof (req.lifr_name)); +#endif + + if (ioctl(s, ioctl_cmd, &req)) { + dprintf(LOG_NOTICE, FNAME, "failed to %s an address on %s: %s", + cmdstr, ifname, strerror(errno)); + close(s); + return (-1); + } + +#ifdef __sun__ + memcpy(&req.lifr_addr, addr, sizeof (*addr)); + if (ioctl(s, SIOCSLIFADDR, &req) == -1) { + dprintf(LOG_NOTICE, FNAME, "failed to %s new address on %s: %s", + cmdstr, ifname, strerror(errno)); + close(s); + return (-1); + } +#endif + + dprintf(LOG_DEBUG, FNAME, "%s an address %s/%d on %s", cmdstr, + addr2str((struct sockaddr *)addr), plen, ifname); + + close(s); + return (0); +} + +int +safefile(path) + const char *path; +{ + struct stat s; + uid_t myuid; + + /* no setuid */ + if (getuid() != geteuid()) { + dprintf(LOG_NOTICE, FNAME, + "setuid'ed execution not allowed"); + return (-1); + } + + if (lstat(path, &s) != 0) { + dprintf(LOG_NOTICE, FNAME, "lstat failed: %s", + strerror(errno)); + return (-1); + } + + /* the file must be owned by the running uid */ + myuid = getuid(); + if (s.st_uid != myuid) { + dprintf(LOG_NOTICE, FNAME, "%s has invalid owner uid", path); + return (-1); + } + + switch (s.st_mode & S_IFMT) { + case S_IFREG: + break; + default: + dprintf(LOG_NOTICE, FNAME, "%s is an invalid file type 0x%o", + path, (s.st_mode & S_IFMT)); + return (-1); + } + + return (0); +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..7366c25 --- /dev/null +++ b/common.h @@ -0,0 +1,189 @@ +/* $KAME: common.h,v 1.42 2005/09/16 11:30:13 suz Exp $ */ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __KAME__ +#define IN6_IFF_INVALID (IN6_IFF_ANYCAST|IN6_IFF_TENTATIVE|\ + IN6_IFF_DUPLICATED|IN6_IFF_DETACHED) +#else +#define IN6_IFF_INVALID (0) +#endif + +#ifdef HAVE_ANSI_FUNC +#define FNAME __func__ +#elif defined (HAVE_GCC_FUNCTION) +#define FNAME __FUNCTION__ +#else +#define FNAME "" +#endif + +/* XXX: bsdi4 does not have TAILQ_EMPTY */ +#ifndef TAILQ_EMPTY +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#endif + +/* and linux *_FIRST and *_NEXT */ +#ifndef LIST_EMPTY +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#endif +#ifndef LIST_FIRST +#define LIST_FIRST(head) ((head)->lh_first) +#endif +#ifndef LIST_NEXT +#define LIST_NEXT(elm, field) ((elm)->field.le_next) +#endif +#ifndef LIST_FOREACH +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) +#endif +#ifndef TAILQ_FIRST +#define TAILQ_FIRST(head) ((head)->tqh_first) +#endif +#ifndef TAILQ_LAST +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#endif +#ifndef TAILQ_PREV +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#endif +#ifndef TAILQ_NEXT +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#endif +#ifndef TAILQ_FOREACH +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) +#endif +#ifdef HAVE_TAILQ_FOREACH_REVERSE_OLD +#undef TAILQ_FOREACH_REVERSE +#endif +#ifndef TAILQ_FOREACH_REVERSE +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) +#endif + + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +/* s*_len stuff */ +static __inline u_int8_t +sysdep_sa_len (const struct sockaddr *sa) +{ +#ifndef HAVE_SA_LEN + switch (sa->sa_family) + { + case AF_INET: + return sizeof (struct sockaddr_in); + case AF_INET6: + return sizeof (struct sockaddr_in6); + } + return sizeof (struct sockaddr_in); +#else + return sa->sa_len; +#endif +} + +extern int foreground; +extern int debug_thresh; +extern char *device; + +/* search option for dhcp6_find_listval() */ +#define MATCHLIST_PREFIXLEN 0x1 + +/* common.c */ +typedef enum { IFADDRCONF_ADD, IFADDRCONF_REMOVE } ifaddrconf_cmd_t; +extern int dhcp6_copy_list __P((struct dhcp6_list *, struct dhcp6_list *)); +extern void dhcp6_move_list __P((struct dhcp6_list *, struct dhcp6_list *)); +extern void dhcp6_clear_list __P((struct dhcp6_list *)); +extern void dhcp6_clear_listval __P((struct dhcp6_listval *)); +extern struct dhcp6_listval *dhcp6_find_listval __P((struct dhcp6_list *, + dhcp6_listval_type_t, void *, int)); +extern struct dhcp6_listval *dhcp6_add_listval __P((struct dhcp6_list *, + dhcp6_listval_type_t, void *, struct dhcp6_list *)); +extern int dhcp6_vbuf_copy __P((struct dhcp6_vbuf *, struct dhcp6_vbuf *)); +extern void dhcp6_vbuf_free __P((struct dhcp6_vbuf *)); +extern int dhcp6_vbuf_cmp __P((struct dhcp6_vbuf *, struct dhcp6_vbuf *)); +extern struct dhcp6_event *dhcp6_create_event __P((struct dhcp6_if *, int)); +extern void dhcp6_remove_event __P((struct dhcp6_event *)); +extern void dhcp6_remove_evdata __P((struct dhcp6_event *)); +extern struct authparam *new_authparam __P((int, int, int)); +extern struct authparam *copy_authparam __P((struct authparam *)); +extern int dhcp6_auth_replaycheck __P((int, u_int64_t, u_int64_t)); +extern int getifaddr __P((struct in6_addr *, char *, struct in6_addr *, + int, int, int)); +extern int getifidfromaddr __P((struct in6_addr *, unsigned int *)); +extern int transmit_sa __P((int, struct sockaddr *, char *, size_t)); +extern long random_between __P((long, long)); +extern int prefix6_mask __P((struct in6_addr *, int)); +extern int sa6_plen2mask __P((struct sockaddr_in6 *, int)); +extern char *addr2str __P((struct sockaddr *)); +extern char *in6addr2str __P((struct in6_addr *, int)); +extern int in6_addrscopebyif __P((struct in6_addr *, char *)); +extern int in6_scope __P((struct in6_addr *)); +extern void setloglevel __P((int)); +extern void dprintf __P((int, const char *, const char *, ...)); +extern int get_duid __P((char *, struct duid *)); +extern void dhcp6_init_options __P((struct dhcp6_optinfo *)); +extern void dhcp6_clear_options __P((struct dhcp6_optinfo *)); +extern int dhcp6_copy_options __P((struct dhcp6_optinfo *, + struct dhcp6_optinfo *)); +extern int dhcp6_get_options __P((struct dhcp6opt *, struct dhcp6opt *, + struct dhcp6_optinfo *)); +extern int dhcp6_set_options __P((int, struct dhcp6opt *, struct dhcp6opt *, + struct dhcp6_optinfo *)); +extern void dhcp6_set_timeoparam __P((struct dhcp6_event *)); +extern void dhcp6_reset_timer __P((struct dhcp6_event *)); +extern char *dhcp6optstr __P((int)); +extern char *dhcp6msgstr __P((int)); +extern char *dhcp6_stcodestr __P((u_int16_t)); +extern char *duidstr __P((struct duid *)); +extern char *dhcp6_event_statestr __P((struct dhcp6_event *)); +extern int get_rdvalue __P((int, void *, size_t)); +extern int duidcpy __P((struct duid *, struct duid *)); +extern int duidcmp __P((struct duid *, struct duid *)); +extern void duidfree __P((struct duid *)); +extern int ifaddrconf __P((ifaddrconf_cmd_t, char *, struct sockaddr_in6 *, + int, int, int)); +extern int safefile __P((const char *)); + +/* missing */ +#ifndef HAVE_STRLCAT +extern size_t strlcat __P((char *, const char *, size_t)); +#endif +#ifndef HAVE_STRLCPY +extern size_t strlcpy __P((char *, const char *, size_t)); +#endif diff --git a/config.c b/config.c new file mode 100644 index 0000000..383c12a --- /dev/null +++ b/config.c @@ -0,0 +1,2233 @@ +/* $KAME: config.c,v 1.53 2005/09/16 11:30:14 suz Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include + +#include +#include +#ifdef __KAME__ +#include +#endif +#ifdef __linux__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#define __USE_XOPEN +#include +#endif + +#include +#include +#include +#include +#include +#include + +extern int errno; + +struct prefix_ifconf *prefix_ifconflist; +struct dhcp6_list siplist, sipnamelist, dnslist, dnsnamelist, ntplist; +struct dhcp6_list nislist, nisnamelist; +struct dhcp6_list nisplist, nispnamelist; +struct dhcp6_list bcmcslist, bcmcsnamelist; +long long optrefreshtime; + +static struct dhcp6_ifconf *dhcp6_ifconflist; +struct ia_conflist ia_conflist0; +static struct host_conf *host_conflist0, *host_conflist; +static struct keyinfo *key_list, *key_list0; +static struct authinfo *auth_list, *auth_list0; +static struct dhcp6_list siplist0, sipnamelist0, dnslist0, dnsnamelist0, ntplist0; +static struct dhcp6_list nislist0, nisnamelist0; +static struct dhcp6_list nisplist0, nispnamelist0; +static struct dhcp6_list bcmcslist0, bcmcsnamelist0; +static long long optrefreshtime0 = -1; +#ifndef DHCP6_DYNAMIC_HOSTCONF_MAX +#define DHCP6_DYNAMIC_HOSTCONF_MAX 1024 +#endif +struct dynamic_hostconf { + TAILQ_ENTRY(dynamic_hostconf) link; + struct host_conf *host; +}; +static TAILQ_HEAD(dynamic_hostconf_listhead, dynamic_hostconf) + dynamic_hostconf_head; +static unsigned int dynamic_hostconf_count; +static struct pool_conf *pool_conflist, *pool_conflist0; + +enum { DHCPOPTCODE_SEND, DHCPOPTCODE_REQUEST, DHCPOPTCODE_ALLOW }; + +/* temporary configuration structure for DHCP interface */ +struct dhcp6_ifconf { + struct dhcp6_ifconf *next; + + char *ifname; + + /* configuration flags */ + u_long send_flags; + u_long allow_flags; + + int server_pref; /* server preference (server only) */ + + char *scriptpath; /* path to config script (client only) */ + + struct dhcp6_list reqopt_list; + struct ia_conflist iaconf_list; + + struct authinfo *authinfo; /* authentication information + * (no need to clear) */ + struct dhcp6_poolspec pool; +}; + +extern struct cf_list *cf_dns_list, *cf_dns_name_list, *cf_ntp_list; +extern struct cf_list *cf_sip_list, *cf_sip_name_list; +extern struct cf_list *cf_nis_list, *cf_nis_name_list; +extern struct cf_list *cf_nisp_list, *cf_nisp_name_list; +extern struct cf_list *cf_bcmcs_list, *cf_bcmcs_name_list; +extern long long cf_refreshtime; +extern char *configfilename; + +static struct keyinfo *find_keybyname __P((struct keyinfo *, char *)); +static int add_pd_pif __P((struct iapd_conf *, struct cf_list *)); +static int add_options __P((int, struct dhcp6_ifconf *, struct cf_list *)); +static int add_prefix __P((struct dhcp6_list *, char *, int, + struct dhcp6_prefix *)); +static void clear_pd_pif __P((struct iapd_conf *)); +static void clear_ifconf __P((struct dhcp6_ifconf *)); +static void clear_iaconf __P((struct ia_conflist *)); +static void clear_hostconf __P((struct host_conf *)); +static void clear_keys __P((struct keyinfo *)); +static void clear_authinfo __P((struct authinfo *)); +static int configure_duid __P((char *, struct duid *)); +static int configure_addr __P((struct cf_list *, struct dhcp6_list *, char *)); +static int configure_domain __P((struct cf_list *, struct dhcp6_list *, char *)); +static int get_default_ifid __P((struct prefix_ifconf *)); +static void clear_poolconf __P((struct pool_conf *)); +static struct pool_conf *create_pool __P((char *, struct dhcp6_range *)); +struct host_conf *find_dynamic_hostconf __P((struct duid *)); +static int in6_addr_cmp __P((struct in6_addr *, struct in6_addr *)); +static void in6_addr_inc __P((struct in6_addr *)); + +int +configure_interface(iflist) + struct cf_namelist *iflist; +{ + struct cf_namelist *ifp; + struct dhcp6_ifconf *ifc; + char *cp; + + for (ifp = iflist; ifp; ifp = ifp->next) { + struct cf_list *cfl; + + if (if_nametoindex(ifp->name) == 0) { + dprintf(LOG_ERR, FNAME, "invalid interface(%s): %s", + ifp->name, strerror(errno)); + goto bad; + } + + if ((ifc = malloc(sizeof(*ifc))) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation for %s failed", ifp->name); + goto bad; + } + memset(ifc, 0, sizeof(*ifc)); + ifc->next = dhcp6_ifconflist; + dhcp6_ifconflist = ifc; + + if ((ifc->ifname = strdup(ifp->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy ifname"); + goto bad; + } + + ifc->server_pref = DH6OPT_PREF_UNDEF; + TAILQ_INIT(&ifc->reqopt_list); + TAILQ_INIT(&ifc->iaconf_list); + + for (cfl = ifp->params; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DECL_REQUEST: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "client-only configuration", + configfilename, + cfl->line); + goto bad; + } + if (add_options(DHCPOPTCODE_REQUEST, + ifc, cfl->list)) { + goto bad; + } + break; + case DECL_SEND: + if (add_options(DHCPOPTCODE_SEND, + ifc, cfl->list)) { + goto bad; + } + break; + case DECL_ALLOW: + if (add_options(DHCPOPTCODE_ALLOW, + ifc, cfl->list)) { + goto bad; + } + break; + case DECL_INFO_ONLY: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "client-only configuration", + configfilename, cfl->line); + goto bad; + } + ifc->send_flags |= DHCIFF_INFO_ONLY; + break; + case DECL_PREFERENCE: + if (dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "server-only configuration", + configfilename, cfl->line); + goto bad; + } + ifc->server_pref = (int)cfl->num; + if (ifc->server_pref < 0 || + ifc->server_pref > 255) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "bad value: %d", + configfilename, cfl->line, + ifc->server_pref); + goto bad; + } + break; + case DECL_SCRIPT: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_INFO, FNAME, "%s:%d " + "client-only configuration", + configfilename, cfl->line); + goto bad; + } + if (ifc->scriptpath) { + dprintf(LOG_INFO, FNAME, + "%s:%d duplicated configuration", + configfilename, cfl->line); + goto bad; + } + cp = cfl->ptr; + ifc->scriptpath = strdup(cp + 1); + if (ifc->scriptpath == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to copy script path"); + goto bad; + } + cp = ifc->scriptpath; + if (*cp != '/') { + dprintf(LOG_INFO, FNAME, + "script must be an absolute path"); + goto bad; + } + cp += strlen(ifc->scriptpath) - 1; + *cp = '\0'; /* clear the terminating quote */ + break; + case DECL_ADDRESSPOOL: + { + struct dhcp6_poolspec* spec; + struct pool_conf* pool; + + spec = (struct dhcp6_poolspec *)cfl->ptr; + + for (pool = pool_conflist0; pool; pool = pool->next) + if (strcmp(spec->name, pool->name) == 0) + break; + if (pool == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool '%s' not found", + configfilename, cfl->line, + spec->name); + goto bad; + } + if (spec->vltime != DHCP6_DURATION_INFINITE && + (spec->pltime == DHCP6_DURATION_INFINITE || + spec->pltime > spec->vltime)) { + dprintf(LOG_ERR, FNAME, "%s:%d ", + configfilename, cfl->line, + "specified a larger preferred lifetime " + "than valid lifetime"); + goto bad; + } + ifc->pool = *spec; + if ((ifc->pool.name = strdup(spec->name)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation failed"); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "pool '%s' is specified to the interface '%s'", + ifc->pool.name, ifc->ifname); + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid interface configuration", + configfilename, cfl->line); + goto bad; + } + } + } + + return (0); + + bad: + clear_ifconf(dhcp6_ifconflist); + dhcp6_ifconflist = NULL; + return (-1); +} + +int +configure_ia(ialist, iatype) + struct cf_namelist *ialist; + iatype_t iatype; +{ + struct cf_namelist *iap; + struct ia_conf *iac = NULL; + size_t confsize; + static int init = 1; + + if (init) { + TAILQ_INIT(&ia_conflist0); + init = 0; + } + + switch(iatype) { + case IATYPE_PD: + confsize = sizeof(struct iapd_conf); + break; + case IATYPE_NA: + confsize = sizeof(struct iana_conf); + break; + default: + dprintf(LOG_ERR, FNAME, "internal error"); + goto bad; + } + + for (iap = ialist; iap; iap = iap->next) { + struct cf_list *cfl; + + if ((iac = malloc(confsize)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation for IA %s failed", + iap->name); + goto bad; + } + memset(iac, 0, confsize); + + /* common initialization */ + iac->type = iatype; + iac->iaid = (u_int32_t)atoi(iap->name); + TAILQ_INIT(&iac->iadata); + TAILQ_INSERT_TAIL(&ia_conflist0, iac, link); + + /* IA-type specific initialization */ + switch(iatype) { + case IATYPE_PD: + TAILQ_INIT(&((struct iapd_conf *)iac)->iapd_prefix_list); + TAILQ_INIT(&((struct iapd_conf *)iac)->iapd_pif_list); + break; + case IATYPE_NA: + TAILQ_INIT(&((struct iana_conf *)iac)->iana_address_list); + break; + } + + /* set up parameters for the IA */ + for (cfl = iap->params; cfl; cfl = cfl->next) { + struct iapd_conf *pdp = (struct iapd_conf *) iac; + struct iana_conf *nap = (struct iana_conf *) iac; + + switch (iatype) { + case IATYPE_PD: + switch(cfl->type) { + case IACONF_PIF: + if (add_pd_pif(pdp, cfl)) + goto bad; + break; + case IACONF_PREFIX: + if (add_prefix(&pdp->iapd_prefix_list, + "IAPD", DHCP6_LISTVAL_PREFIX6, + cfl->ptr)) { + dprintf(LOG_NOTICE, FNAME, "failed " + "to configure prefix"); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid configuration", + configfilename, cfl->line); + goto bad; + } + break; + case IATYPE_NA: + switch(cfl->type) { + case IACONF_ADDR: + if (add_prefix(&nap->iana_address_list, + "IANA", DHCP6_LISTVAL_STATEFULADDR6, + cfl->ptr)) { + dprintf(LOG_NOTICE, FNAME, "failed " + "to configure address"); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid configuration", + configfilename, cfl->line); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid iatype %d", + configfilename, cfl->line, iatype); + goto bad; + } + } + } + + return (0); + + bad: + return (-1); +} + +static int +add_pd_pif(iapdc, cfl0) + struct iapd_conf *iapdc; + struct cf_list *cfl0; +{ + struct cf_list *cfl; + struct prefix_ifconf *pif; + + /* duplication check */ + for (pif = TAILQ_FIRST(&iapdc->iapd_pif_list); pif; + pif = TAILQ_NEXT(pif, link)) { + if (strcmp(pif->ifname, cfl0->ptr) == 0) { + dprintf(LOG_NOTICE, FNAME, "%s:%d " + "duplicated prefix interface: %s", + configfilename, cfl0->line, cfl0->ptr); + return (0); /* ignore it */ + } + } + + if ((pif = malloc(sizeof(*pif))) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation for %s failed", cfl0->ptr); + goto bad; + } + memset(pif, 0, sizeof(*pif)); + + /* validate and copy ifname */ + if (if_nametoindex(cfl0->ptr) == 0) { + dprintf(LOG_ERR, FNAME, "%s:%d invalid interface (%s): %s", + configfilename, cfl0->line, + cfl0->ptr, strerror(errno)); + goto bad; + } + if ((pif->ifname = strdup(cfl0->ptr)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy ifname"); + goto bad; + } + + pif->ifid_len = IFID_LEN_DEFAULT; + pif->sla_len = SLA_LEN_DEFAULT; + if (get_default_ifid(pif)) { + dprintf(LOG_NOTICE, FNAME, + "failed to get default IF ID for %s", pif->ifname); + goto bad; + } + + for (cfl = cfl0->list; cfl; cfl = cfl->next) { + switch(cfl->type) { + case IFPARAM_SLA_ID: + pif->sla_id = (u_int32_t)cfl->num; + break; + case IFPARAM_SLA_LEN: + pif->sla_len = (int)cfl->num; + if (pif->sla_len < 0 || pif->sla_len > 128) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid SLA length: %d", + configfilename, cfl->line, pif->sla_len); + goto bad; + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d internal error: " + "invalid configuration", + configfilename, cfl->line); + goto bad; + } + } + + TAILQ_INSERT_TAIL(&iapdc->iapd_pif_list, pif, link); + return (0); + + bad: + if (pif->ifname) + free(pif->ifname); + free(pif); + return (-1); +} + +int +configure_host(hostlist) + struct cf_namelist *hostlist; +{ + struct cf_namelist *host; + struct host_conf *hconf; + + for (host = hostlist; host; host = host->next) { + struct cf_list *cfl; + + if ((hconf = malloc(sizeof(*hconf))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed " + "for host %s", host->name); + goto bad; + } + memset(hconf, 0, sizeof(*hconf)); + TAILQ_INIT(&hconf->prefix_list); + TAILQ_INIT(&hconf->addr_list); + hconf->next = host_conflist0; + host_conflist0 = hconf; + + if ((hconf->name = strdup(host->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy host name: %s", + host->name); + goto bad; + } + + for (cfl = host->params; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DECL_DUID: + if (hconf->duid.duid_id) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "duplicated DUID for %s", + configfilename, + cfl->line, host->name); + goto bad; + } + if ((configure_duid((char *)cfl->ptr, + &hconf->duid)) != 0) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "failed to configure " + "DUID for %s", + configfilename, cfl->line, + host->name); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "configure DUID for %s: %s", + host->name, duidstr(&hconf->duid)); + break; + case DECL_PREFIX: + if (add_prefix(&hconf->prefix_list, + hconf->name, DHCP6_LISTVAL_PREFIX6, + cfl->ptr)) { + dprintf(LOG_ERR, FNAME, "failed " + "to configure prefix for %s", + host->name); + goto bad; + } + break; + case DECL_ADDRESS: + if (add_prefix(&hconf->addr_list, + hconf->name, DHCP6_LISTVAL_STATEFULADDR6, + cfl->ptr)) { + dprintf(LOG_ERR, FNAME, "failed " + "to configure address for %s", + host->name); + goto bad; + } + break; + case DECL_DELAYEDKEY: + if (hconf->delayedkey != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d: duplicate key %s for %s" + " (ignored)", configfilename, + cfl->line, cfl->ptr, host->name); + continue; + } + if ((hconf->delayedkey = + find_keybyname(key_list0, cfl->ptr)) + == NULL) { + dprintf(LOG_ERR, FNAME, "failed to " + "find key information for %s", + cfl->ptr); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, "configure key for " + "delayed auth with %s (keyid=%08x)", + host->name, hconf->delayedkey->keyid); + break; + case DECL_ADDRESSPOOL: + { + struct dhcp6_poolspec* spec; + struct pool_conf *pool; + + spec = (struct dhcp6_poolspec *)cfl->ptr; + + for (pool = pool_conflist0; pool; pool = pool->next) + if (strcmp(spec->name, pool->name) == 0) + break; + if (pool == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool '%s' not found", + configfilename, cfl->line, + spec->name); + goto bad; + } + if (spec->vltime != DHCP6_DURATION_INFINITE && + (spec->pltime == DHCP6_DURATION_INFINITE || + spec->pltime > spec->vltime)) { + dprintf(LOG_ERR, FNAME, "%s:%d ", + configfilename, cfl->line, + "specified a larger preferred lifetime " + "than valid lifetime"); + goto bad; + } + hconf->pool = *spec; + if ((hconf->pool.name = strdup(spec->name)) == NULL) { + dprintf(LOG_ERR, FNAME, + "memory allocation failed"); + goto bad; + } + dprintf(LOG_DEBUG, FNAME, + "pool '%s' is specified to the host '%s'", + hconf->pool.name, hconf->name); + } + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid host configuration for %s", + configfilename, cfl->line, + host->name); + goto bad; + } + } + } + + return (0); + + bad: + /* there is currently nothing special to recover the error */ + return (-1); +} + +int +configure_keys(keylist) + struct cf_namelist *keylist; +{ + struct cf_namelist *key; + char *secretstr; + char secret[1024]; + int secretlen; + struct keyinfo *kinfo; + long long keyid; + char *expire = NULL; + + for (key = keylist; key; key = key->next) { + struct cf_list *cfl; + + if ((kinfo = malloc(sizeof(*kinfo))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed " + "for key %s", key->name); + goto bad; + } + memset(kinfo, 0, sizeof(*kinfo)); + kinfo->next = key_list0; + key_list0 = kinfo; + + if ((kinfo->name = strdup(key->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy key name: %s", + key->name); + goto bad; + } + + keyid = -1; + expire = NULL; + for (cfl = key->params; cfl; cfl = cfl->next) { + switch (cfl->type) { + case KEYPARAM_REALM: + if (kinfo->realm != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate realm for key %s " + "(ignored)", configfilename, + cfl->line, key->name); + continue; + } + kinfo->realm = qstrdup(cfl->ptr); + if (kinfo->realm == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate memory for " + "realm"); + goto bad; + } + kinfo->realmlen = strlen(kinfo->realm); + break; + case KEYPARAM_KEYID: + if (keyid != -1) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate realm for key %s " + "(ignored)", + configfilename, cfl->line); + continue; + } + keyid = cfl->num; + if (keyid < 0 || keyid > 0xffffffff) { + dprintf(LOG_WARNING, FNAME, + "%s:%d key ID overflow", + configfilename, cfl->line); + goto bad; + } + break; + case KEYPARAM_SECRET: + /* duplicate check */ + if (kinfo->secret != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate secret " + "for key %s (ignored)", + configfilename, cfl->line, + key->name); + continue; /* ignored */ + } + + /* convert base64 string to binary secret */ + if ((secretstr = qstrdup(cfl->ptr)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to make a copy of secret"); + goto bad; + } + memset(secret, 0, sizeof(secret)); + secretlen = base64_decodestring(secretstr, + secret, sizeof(secret)); + if (secretlen < 0) { + dprintf(LOG_ERR, FNAME, + "%s:%d failed to parse base64 key", + configfilename, cfl->line); + free(secretstr); + goto bad; + } + free(secretstr); + + /* set the binary secret */ + kinfo->secret = malloc(secretlen); + if (kinfo->secret == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate memory " + "for secret"); + goto bad; + } + memcpy(kinfo->secret, secret, secretlen); + kinfo->secretlen = secretlen; + break; + case KEYPARAM_EXPIRE: + if (expire != NULL) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate expire for key " + "%s (ignored)", configfilename, + cfl->line, key->name); + continue; + } + expire = qstrdup(cfl->ptr); + break; + default: + dprintf(LOG_ERR, FNAME, + "%s:%d invalid key parameter for %s", + configfilename, cfl->line, key->name); + goto bad; + } + } + + /* check for mandatory parameters or use default */ + if (kinfo->realm == NULL) { + dprintf(LOG_ERR, FNAME, + "realm not specified for key %s", key->name); + goto bad; + } + if (keyid == -1) { + dprintf(LOG_ERR, FNAME, + "key ID not specified for key %s", key->name); + goto bad; + } + kinfo->keyid = keyid; + if (kinfo->secret == NULL) { + dprintf(LOG_ERR, FNAME, + "secret not specified for key %s", key->name); + goto bad; + } + kinfo->expire = 0; + if (expire != NULL) { + if (strcmp(expire, "forever") != 0) { + time_t now, expire_time; + struct tm *lt; + + if (time(&now) == -1) { + dprintf(LOG_ERR, FNAME, "cannot get " + "current time: %s", + strerror(errno)); + goto bad; + } + lt = localtime(&now); + lt->tm_sec = 0; + + if (strptime(expire, "%Y-%m-%d %H:%M", lt) + == NULL && + strptime(expire, "%m-%d %H:%M", lt) + == NULL && + strptime(expire, "%H:%M", lt) == NULL) { + dprintf(LOG_ERR, FNAME, "invalid " + "expiration time: %s"); + goto bad; + } + + expire_time = mktime(lt); + if (expire_time < now) { + dprintf(LOG_ERR, FNAME, "past " + "expiration time specified: %s", + expire); + goto bad; + } + + kinfo->expire = expire_time; + } + } + } + + return (0); + + bad: + if (expire != NULL) + free(expire); + return (-1); +} + +static struct keyinfo * +find_keybyname(head, kname) + struct keyinfo *head; + char *kname; +{ + struct keyinfo *kinfo; + + for (kinfo = head; kinfo != NULL; kinfo = kinfo->next) { + if (strcmp(kname, kinfo->name) == 0) + return (kinfo); + } + + return (NULL); +} + +int +configure_authinfo(authlist) + struct cf_namelist *authlist; +{ + struct cf_namelist *auth; + struct authinfo *ainfo; + + for (auth = authlist; auth; auth = auth->next) { + struct cf_list *cfl; + + if ((ainfo = malloc(sizeof(*ainfo))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed " + "for auth info %s", auth->name); + goto bad; + } + memset(ainfo, 0, sizeof(*ainfo)); + ainfo->next = auth_list0; + auth_list0 = ainfo; + ainfo->protocol = DHCP6_AUTHPROTO_UNDEF; + ainfo->algorithm = DHCP6_AUTHALG_UNDEF; + ainfo->rdm = DHCP6_AUTHRDM_UNDEF; + + if ((ainfo->name = strdup(auth->name)) == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to copy auth info name: %s", auth->name); + goto bad; + } + + for (cfl = auth->params; cfl; cfl = cfl->next) { + switch (cfl->type) { + case AUTHPARAM_PROTO: + if (ainfo->protocol != DHCP6_AUTHPROTO_UNDEF) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate protocol " + "for auth info %s " + "(ignored)", + configfilename, cfl->line, + auth->name); + continue; /* ignored */ + } + ainfo->protocol = (int)cfl->num; + break; + case AUTHPARAM_ALG: + if (ainfo->algorithm != DHCP6_AUTHALG_UNDEF) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate algorithm " + "for auth info %s " + "(ignored)", + configfilename, cfl->line, + auth->name); + continue; /* ignored */ + } + ainfo->algorithm = (int)cfl->num; + break; + case AUTHPARAM_RDM: + if (ainfo->rdm != DHCP6_AUTHRDM_UNDEF) { + dprintf(LOG_WARNING, FNAME, + "%s:%d duplicate RDM " + "for auth info %s " + "(ignored)", + configfilename, cfl->line, + auth->name); + continue; /* ignored */ + } + ainfo->rdm = (int)cfl->num; + break; + case AUTHPARAM_KEY: + dprintf(LOG_WARNING, FNAME, + "%s:%d auth info specific keys " + "are not supported", + configfilename, cfl->line); + break; + default: + dprintf(LOG_ERR, FNAME, + "%s:%d invalid auth info parameter for %s", + configfilename, cfl->line, auth->name); + goto bad; + } + } + + /* check for mandatory parameters and consistency */ + switch (ainfo->protocol) { + case DHCP6_AUTHPROTO_UNDEF: + dprintf(LOG_ERR, FNAME, + "auth protocol is not specified for %s", + auth->name); + goto bad; + case DHCP6_AUTHPROTO_DELAYED: + if (dhcp6_mode != DHCP6_MODE_CLIENT) { + dprintf(LOG_ERR, FNAME, + "client-only auth protocol is specified"); + goto bad; + } + break; + case DHCP6_AUTHPROTO_RECONFIG: + if (dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_ERR, FNAME, + "server-only auth protocol is specified"); + goto bad; + } + break; + } + if (ainfo->algorithm == DHCP6_AUTHALG_UNDEF) + ainfo->algorithm = DHCP6_AUTHALG_HMACMD5; + if (ainfo->rdm == DHCP6_AUTHRDM_UNDEF) + ainfo->rdm = DHCP6_AUTHRDM_MONOCOUNTER; + } + + return (0); + + bad: + /* there is currently nothing special to recover the error */ + return (-1); +} + +int +configure_global_option() +{ + /* SIP Server address */ + if (configure_addr(cf_sip_list, &siplist0, "SIP") < 0) + goto bad; + + /* SIP domain name */ + if (configure_domain(cf_sip_name_list, &sipnamelist0, "SIP") < 0) + goto bad; + + /* DNS servers */ + if (configure_addr(cf_dns_list, &dnslist0, "DNS") < 0) + goto bad; + + /* DNS name */ + if (configure_domain(cf_dns_name_list, &dnsnamelist0, "DNS") < 0) + goto bad; + + /* NTP servers */ + if (configure_addr(cf_ntp_list, &ntplist0, "NTP") < 0) + goto bad; + + /* NIS Server address */ + if (configure_addr(cf_nis_list, &nislist0, "NIS") < 0) + goto bad; + + /* NIS domain name */ + if (configure_domain(cf_nis_name_list, &nisnamelist0, "NIS") < 0) + goto bad; + + /* NIS+ Server address */ + if (configure_addr(cf_nisp_list, &nisplist0, "NISP") < 0) + goto bad; + + /* NIS+ domain name */ + if (configure_domain(cf_nisp_name_list, &nispnamelist0, "NISP") < 0) + goto bad; + + /* BCMCS Server address */ + if (configure_addr(cf_bcmcs_list, &bcmcslist0, "BCMCS") < 0) + goto bad; + + /* BCMCS domain name */ + if (configure_domain(cf_bcmcs_name_list, &bcmcsnamelist0, "BCMCS") < 0) + goto bad; + + /* Lifetime for stateless options */ + if (cf_refreshtime >= 0) { + optrefreshtime0 = cf_refreshtime; + } + + return (0); + + bad: + return (-1); /* no need to free intermediate list */ +} + +static int +configure_addr(cf_addr_list, list0, optname) + struct cf_list *cf_addr_list; + struct dhcp6_list *list0; + char *optname; +{ + struct cf_list *cl; + + /* check against configuration restriction */ + if (cf_addr_list != NULL && dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_INFO, FNAME, "%s:%d server-only configuration", + configfilename, cf_addr_list->line); + return -1; + } + + TAILQ_INIT(list0); + for (cl = cf_addr_list; cl; cl = cl->next) { + /* duplication check */ + if (dhcp6_find_listval(list0, DHCP6_LISTVAL_ADDR6, + cl->ptr, 0)) { + dprintf(LOG_INFO, FNAME, + "%s:%d duplicated %s server: %s", + configfilename, cl->line, + optname, + in6addr2str((struct in6_addr *)cl->ptr, 0)); + return -1; + } + if (dhcp6_add_listval(list0, DHCP6_LISTVAL_ADDR6, + cl->ptr, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a %s server", + optname); + return -1; + } + } + + return 0; +} + +static int +configure_domain(cf_name_list, list0, optname) + struct cf_list *cf_name_list; + struct dhcp6_list *list0; + char *optname; +{ + struct cf_list *cl; + + /* check against configuration restriction */ + if (cf_name_list != NULL && dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_INFO, FNAME, "%s:%d server-only configuration", + configfilename, cf_name_list->line); + return -1; + } + + TAILQ_INIT(list0); + for (cl = cf_name_list; cl; cl = cl->next) { + char *name, *cp; + struct dhcp6_vbuf name_vbuf; + + name = strdup(cl->ptr + 1); + if (name == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to copy a %s domain name", + optname); + return -1; + } + cp = name + strlen(name) - 1; + *cp = '\0'; /* clear the terminating quote */ + + name_vbuf.dv_buf = name; + name_vbuf.dv_len = strlen(name) + 1; + + /* duplication check */ + if (dhcp6_find_listval(list0, DHCP6_LISTVAL_VBUF, + &name_vbuf, 0)) { + dprintf(LOG_INFO, FNAME, + "%s:%d duplicated %s name: %s", + configfilename, cl->line, optname, + name_vbuf.dv_buf); + dhcp6_vbuf_free(&name_vbuf); + return -1; + } + + /* add the name */ + if (dhcp6_add_listval(list0, DHCP6_LISTVAL_VBUF, + &name_vbuf, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a %s name", + optname); + dhcp6_vbuf_free(&name_vbuf); + return -1; + } + dhcp6_vbuf_free(&name_vbuf); + } + + return 0; +} + +static int +configure_duid(str, duid) + char *str; /* this is a valid DUID string */ + struct duid *duid; +{ + char *cp, *bp; + char *idbuf = NULL; + int duidlen, slen; + unsigned int x; + + /* calculate DUID len */ + slen = strlen(str); + if (slen < 2) + goto bad; + duidlen = 1; + slen -= 2; + if ((slen % 3) != 0) + goto bad; + duidlen += (slen / 3); + if (duidlen > 128) { + dprintf(LOG_ERR, FNAME, "too long DUID (%d)", duidlen); + return (-1); + } + + if ((idbuf = malloc(duidlen)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (-1); + } + + for (cp = str, bp = idbuf; *cp;) { + if (*cp == ':') { + cp++; + continue; + } + + if (sscanf(cp, "%02x", &x) != 1) + goto bad; + *bp = x; + cp += 2; + bp++; + } + + duid->duid_len = duidlen; + duid->duid_id = idbuf; + + return (0); + + bad: + if (idbuf) + free(idbuf); + dprintf(LOG_ERR, FNAME, "assumption failure (bad string)"); + return (-1); +} + +/* we currently only construct EUI-64 based interface ID */ +static int +get_default_ifid(pif) + struct prefix_ifconf *pif; +{ + struct ifaddrs *ifa, *ifap; +#ifdef __KAME__ + struct sockaddr_dl *sdl; +#endif +#ifdef __linux__ + struct sockaddr_ll *sll; +#endif + + if (pif->ifid_len < 64) { + dprintf(LOG_NOTICE, FNAME, "ID length too short"); + return (-1); + } + + if (getifaddrs(&ifap) < 0) { + dprintf(LOG_ERR, FNAME, "getifaddrs failed: %s", + strerror(errno)); + return (-1); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + char *cp; + + if (strcmp(ifa->ifa_name, pif->ifname) != 0) + continue; + + if (ifa->ifa_addr == NULL) + continue; + +#ifdef __KAME__ + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_alen < 6) { + dprintf(LOG_NOTICE, FNAME, + "link layer address is too short (%s)", + pif->ifname); + goto fail; + } + + memset(pif->ifid, 0, sizeof(pif->ifid)); + cp = (char *)(sdl->sdl_data + sdl->sdl_nlen); +#endif +#ifdef __linux__ + if (ifa->ifa_addr->sa_family != AF_PACKET) + continue; + + sll = (struct sockaddr_ll *)ifa->ifa_addr; + if (sll->sll_halen < 6) { + dprintf(LOG_NOTICE, FNAME, + "link layer address is too short (%s)", + pif->ifname); + goto fail; + } + + memset(pif->ifid, 0, sizeof(pif->ifid)); + cp = (char *)(sll->sll_addr); +#endif + pif->ifid[8] = cp[0]; + pif->ifid[8] ^= 0x02; /* reverse the u/l bit*/ + pif->ifid[9] = cp[1]; + pif->ifid[10] = cp[2]; + pif->ifid[11] = 0xff; + pif->ifid[12] = 0xfe; + pif->ifid[13] = cp[3]; + pif->ifid[14] = cp[4]; + pif->ifid[15] = cp[5]; + + break; + } + + if (ifa == NULL) { + dprintf(LOG_INFO, FNAME, + "cannot find interface information for %s", pif->ifname); + goto fail; + } + + freeifaddrs(ifap); + return (0); + + fail: + freeifaddrs(ifap); + return (-1); +} + +void +configure_cleanup() +{ + clear_iaconf(&ia_conflist0); + clear_ifconf(dhcp6_ifconflist); + dhcp6_ifconflist = NULL; + clear_hostconf(host_conflist0); + host_conflist0 = NULL; + clear_keys(key_list0); + key_list0 = NULL; + clear_authinfo(auth_list0); + auth_list0 = NULL; + + dhcp6_clear_list(&siplist0); + TAILQ_INIT(&siplist0); + dhcp6_clear_list(&sipnamelist0); + TAILQ_INIT(&sipnamelist0); + dhcp6_clear_list(&dnslist0); + TAILQ_INIT(&dnslist0); + dhcp6_clear_list(&dnsnamelist0); + TAILQ_INIT(&dnsnamelist0); + dhcp6_clear_list(&ntplist0); + TAILQ_INIT(&ntplist0); + optrefreshtime0 = -1; + clear_poolconf(pool_conflist0); +} + +void +configure_commit() +{ + struct dhcp6_ifconf *ifc; + struct dhcp6_if *ifp; + struct ia_conf *iac; + + /* commit interface configuration */ + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + /* re-initialization */ + ifp->send_flags = 0; + ifp->allow_flags = 0; + dhcp6_clear_list(&ifp->reqopt_list); + clear_iaconf(&ifp->iaconf_list); + ifp->server_pref = DH6OPT_PREF_UNDEF; + if (ifp->scriptpath != NULL) + free(ifp->scriptpath); + ifp->scriptpath = NULL; + ifp->authproto = DHCP6_AUTHPROTO_UNDEF; + ifp->authalgorithm = DHCP6_AUTHALG_UNDEF; + ifp->authrdm = DHCP6_AUTHRDM_UNDEF; + + for (ifc = dhcp6_ifconflist; ifc; ifc = ifc->next) { + if (strcmp(ifp->ifname, ifc->ifname) == 0) + break; + } + if (ifc == NULL) + continue; + + /* copy new configuration */ + ifp->send_flags = ifc->send_flags; + ifp->allow_flags = ifc->allow_flags; + dhcp6_copy_list(&ifp->reqopt_list, &ifc->reqopt_list); + while ((iac = TAILQ_FIRST(&ifc->iaconf_list)) != NULL) { + TAILQ_REMOVE(&ifc->iaconf_list, iac, link); + TAILQ_INSERT_TAIL(&ifp->iaconf_list, + iac, link); + } + ifp->server_pref = ifc->server_pref; + ifp->scriptpath = ifc->scriptpath; + ifc->scriptpath = NULL; + + if (ifc->authinfo != NULL) { + ifp->authproto = ifc->authinfo->protocol; + ifp->authalgorithm = ifc->authinfo->algorithm; + ifp->authrdm = ifc->authinfo->rdm; + } + ifp->pool = ifc->pool; + ifc->pool.name = NULL; + } + + clear_ifconf(dhcp6_ifconflist); + dhcp6_ifconflist = NULL; + + /* clear unused IA configuration */ + if (!TAILQ_EMPTY(&ia_conflist0)) { + dprintf(LOG_INFO, FNAME, + "some IA configuration defined but not used"); + } + clear_iaconf(&ia_conflist0); + + /* commit per-host configuration */ + clear_hostconf(host_conflist); + host_conflist = host_conflist0; + host_conflist0 = NULL; + + /* commit secret key information */ + clear_keys(key_list); + key_list = key_list0; + key_list0 = NULL; + + /* commit authentication information */ + clear_authinfo(auth_list); + auth_list = auth_list0; + auth_list0 = NULL; + + /* commit SIP server addresses */ + dhcp6_clear_list(&siplist); + dhcp6_move_list(&siplist, &siplist0); + + /* commit SIP domain names */ + dhcp6_clear_list(&sipnamelist); + dhcp6_move_list(&sipnamelist, &sipnamelist0); + + /* commit DNS addresses */ + dhcp6_clear_list(&dnslist); + dhcp6_move_list(&dnslist, &dnslist0); + + /* commit DNS names */ + dhcp6_clear_list(&dnsnamelist); + dhcp6_move_list(&dnsnamelist, &dnsnamelist0); + + /* commit NTP addresses */ + dhcp6_clear_list(&ntplist); + dhcp6_move_list(&ntplist, &ntplist0); + + /* commit NIS server addresses */ + dhcp6_clear_list(&nislist); + dhcp6_move_list(&nislist, &nislist0); + + /* commit NIS domain names */ + dhcp6_clear_list(&nisnamelist); + dhcp6_move_list(&nisnamelist, &nisnamelist0); + + /* commit NIS+ server addresses */ + dhcp6_clear_list(&nisplist); + dhcp6_move_list(&nisplist, &nisplist0); + + /* commit NIS+ domain names */ + dhcp6_clear_list(&nispnamelist); + dhcp6_move_list(&nispnamelist, &nispnamelist0); + + /* commit BCMCS server addresses */ + dhcp6_clear_list(&bcmcslist); + dhcp6_move_list(&bcmcslist, &bcmcslist0); + + /* commit BCMCS domain names */ + dhcp6_clear_list(&bcmcsnamelist); + dhcp6_move_list(&bcmcsnamelist, &bcmcsnamelist0); + + /* commit information refresh time */ + optrefreshtime = optrefreshtime0; + /* commit pool configuration */ + clear_poolconf(pool_conflist); + pool_conflist = pool_conflist0; + pool_conflist0 = NULL; +} + +static void +clear_ifconf(iflist) + struct dhcp6_ifconf *iflist; +{ + struct dhcp6_ifconf *ifc, *ifc_next; + + for (ifc = iflist; ifc; ifc = ifc_next) { + ifc_next = ifc->next; + + free(ifc->ifname); + dhcp6_clear_list(&ifc->reqopt_list); + + clear_iaconf(&ifc->iaconf_list); + + if (ifc->scriptpath) + free(ifc->scriptpath); + + if (ifc->pool.name) + free(ifc->pool.name); + free(ifc); + } +} + +static void +clear_pd_pif(iapdc) + struct iapd_conf *iapdc; +{ + struct prefix_ifconf *pif, *pif_next; + + for (pif = TAILQ_FIRST(&iapdc->iapd_pif_list); pif; pif = pif_next) { + pif_next = TAILQ_NEXT(pif, link); + + free(pif->ifname); + free(pif); + } + + dhcp6_clear_list(&iapdc->iapd_prefix_list); +} + +static void +clear_iaconf(ialist) + struct ia_conflist *ialist; +{ + struct ia_conf *iac; + + while ((iac = TAILQ_FIRST(ialist)) != NULL) { + TAILQ_REMOVE(ialist, iac, link); + + switch(iac->type) { + case IATYPE_PD: + if (!TAILQ_EMPTY(&iac->iadata)) { + dprintf(LOG_ERR, FNAME, "assumption failure"); + exit(1); + } + clear_pd_pif((struct iapd_conf *)iac); + break; + case IATYPE_NA: + break; + } + free(iac); + } +} + +static void +clear_hostconf(hlist) + struct host_conf *hlist; +{ + struct host_conf *host, *host_next; + + for (host = hlist; host; host = host_next) { + host_next = host->next; + + free(host->name); + dhcp6_clear_list(&host->prefix_list); + dhcp6_clear_list(&host->addr_list); + if (host->duid.duid_id) + free(host->duid.duid_id); + if (host->pool.name) + free(host->pool.name); + free(host); + } +} + +static void +clear_keys(klist) + struct keyinfo *klist; +{ + struct keyinfo *key, *key_next; + + for (key = klist; key; key = key_next) { + key_next = key->next; + + free(key->name); + free(key->realm); + free(key->secret); + free(key); + } +} + +static void +clear_authinfo(alist) + struct authinfo *alist; +{ + struct authinfo *auth, *auth_next; + + for (auth = alist; auth; auth = auth_next) { + auth_next = auth->next; + free(auth); + } +} + +static int +add_options(opcode, ifc, cfl0) + int opcode; + struct dhcp6_ifconf *ifc; + struct cf_list *cfl0; +{ + struct cf_list *cfl; + int opttype; + struct authinfo *ainfo; + struct ia_conf *iac; + + for (cfl = cfl0; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DHCPOPT_RAPID_COMMIT: + switch (opcode) { + case DHCPOPTCODE_SEND: + ifc->send_flags |= DHCIFF_RAPID_COMMIT; + break; + case DHCPOPTCODE_ALLOW: + ifc->allow_flags |= DHCIFF_RAPID_COMMIT; + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", + opcode, cfl->type); + return (-1); + } + break; + case DHCPOPT_AUTHINFO: + if (opcode != DHCPOPTCODE_SEND) { + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", + opcode, cfl->type); + return (-1); + } + ainfo = find_authinfo(auth_list0, cfl->ptr); + if (ainfo == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "auth info (%s) is not defined", + configfilename, cfl->line, + (char *)cfl->ptr); + return (-1); + } + if (ifc->authinfo != NULL) { + dprintf(LOG_ERR, FNAME, + "%s:%d authinfo is doubly specified on %s", + configfilename, cfl->line, ifc->ifname); + return (-1); + } + ifc->authinfo = ainfo; + break; + case DHCPOPT_IA_PD: + switch (opcode) { + case DHCPOPTCODE_SEND: + iac = find_iaconf(&ia_conflist0, IATYPE_PD, + (u_int32_t)cfl->num); + if (iac == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "IA_PD (%lu) is not defined", + configfilename, cfl->line, + (u_long)cfl->num); + return (-1); + } + + TAILQ_REMOVE(&ia_conflist0, iac, link); + TAILQ_INSERT_TAIL(&ifc->iaconf_list, + iac, link); + + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", opcode, cfl->type); + break; + } + break; + case DHCPOPT_IA_NA: + switch (opcode) { + case DHCPOPTCODE_SEND: + iac = find_iaconf(&ia_conflist0, IATYPE_NA, + (u_int32_t)cfl->num); + if (iac == NULL) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "IA_NA (%lu) is not defined", + configfilename, cfl->line, + (u_long)cfl->num); + return (-1); + } + + TAILQ_REMOVE(&ia_conflist0, iac, link); + TAILQ_INSERT_TAIL(&ifc->iaconf_list, + iac, link); + + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", opcode, cfl->type); + break; + } + break; + case DHCPOPT_SIP: + case DHCPOPT_SIPNAME: + case DHCPOPT_DNS: + case DHCPOPT_DNSNAME: + case DHCPOPT_NTP: + case DHCPOPT_NIS: + case DHCPOPT_NISNAME: + case DHCPOPT_NISP: + case DHCPOPT_NISPNAME: + case DHCPOPT_BCMCS: + case DHCPOPT_BCMCSNAME: + case DHCPOPT_REFRESHTIME: + switch (cfl->type) { + case DHCPOPT_SIP: + opttype = DH6OPT_SIP_SERVER_A; + break; + case DHCPOPT_SIPNAME: + opttype = DH6OPT_SIP_SERVER_D; + break; + case DHCPOPT_DNS: + opttype = DH6OPT_DNS; + break; + case DHCPOPT_DNSNAME: + opttype = DH6OPT_DNSNAME; + break; + case DHCPOPT_NTP: + opttype = DH6OPT_NTP; + break; + case DHCPOPT_NIS: + opttype = DH6OPT_NIS_SERVERS; + break; + case DHCPOPT_NISNAME: + opttype = DH6OPT_NIS_DOMAIN_NAME; + break; + case DHCPOPT_NISP: + opttype = DH6OPT_NISP_SERVERS; + break; + case DHCPOPT_NISPNAME: + opttype = DH6OPT_NISP_DOMAIN_NAME; + break; + case DHCPOPT_BCMCS: + opttype = DH6OPT_BCMCS_SERVER_A; + break; + case DHCPOPT_BCMCSNAME: + opttype = DH6OPT_BCMCS_SERVER_D; + break; + case DHCPOPT_REFRESHTIME: + opttype = DH6OPT_REFRESHTIME; + break; + } + switch(opcode) { + case DHCPOPTCODE_REQUEST: + if (dhcp6_find_listval(&ifc->reqopt_list, + DHCP6_LISTVAL_NUM, &opttype, 0) + != NULL) { + dprintf(LOG_INFO, FNAME, + "duplicated requested option: %s", + dhcp6optstr(opttype)); + goto next; /* ignore it */ + } + if (dhcp6_add_listval(&ifc->reqopt_list, + DHCP6_LISTVAL_NUM, &opttype, NULL) + == NULL) { + dprintf(LOG_ERR, FNAME, "failed to " + "configure an option"); + return (-1); + } + break; + default: + dprintf(LOG_ERR, FNAME, + "invalid operation (%d) " + "for option type (%d)", opcode, cfl->type); + break; + } + break; + default: + dprintf(LOG_ERR, FNAME, + "%s:%d unsupported option type: %d", + configfilename, cfl->line, cfl->type); + return (-1); + } + + next: + ; + } + + return (0); +} + +static int +add_prefix(head, name, type, prefix0) + struct dhcp6_list *head; + char *name; + int type; + struct dhcp6_prefix *prefix0; +{ + struct dhcp6_prefix oprefix; + + oprefix = *prefix0; + + /* additional validation of parameters */ + if (oprefix.plen < 0 || oprefix.plen > 128) { + dprintf(LOG_ERR, FNAME, "invalid prefix: %d", oprefix.plen); + return (-1); + } + /* clear trailing bits */ + prefix6_mask(&oprefix.addr, oprefix.plen); + if (!IN6_ARE_ADDR_EQUAL(&prefix0->addr, &oprefix.addr)) { + dprintf(LOG_WARNING, FNAME, "prefix %s/%d for %s " + "has a trailing garbage. It should be %s/%d", + in6addr2str(&prefix0->addr, 0), prefix0->plen, + name, in6addr2str(&oprefix.addr, 0), oprefix.plen); + /* ignore the error */ + } + + /* avoid invalid prefix addresses */ + if (IN6_IS_ADDR_MULTICAST(&oprefix.addr) || + IN6_IS_ADDR_LINKLOCAL(&oprefix.addr) || + IN6_IS_ADDR_SITELOCAL(&oprefix.addr)) { + dprintf(LOG_ERR, FNAME, "invalid prefix address: %s", + in6addr2str(&oprefix.addr, 0)); + return (-1); + } + + /* prefix duplication check */ + if (dhcp6_find_listval(head, type, &oprefix, 0)) { + if (type == DHCP6_LISTVAL_PREFIX6) { + dprintf(LOG_NOTICE, FNAME, + "duplicated prefix: %s/%d for %s", + in6addr2str(&oprefix.addr, 0), oprefix.plen, name); + } else { + dprintf(LOG_NOTICE, FNAME, + "duplicated address: %s for %s", + in6addr2str(&oprefix.addr, 0), name); + } + return (-1); + } + + /* validation about relationship of pltime and vltime */ + if (oprefix.vltime != DHCP6_DURATION_INFINITE && + (oprefix.pltime == DHCP6_DURATION_INFINITE || + oprefix.pltime > oprefix.vltime)) { + if (type == DHCP6_LISTVAL_PREFIX6) { + dprintf(LOG_NOTICE, FNAME, + "%s/%d has larger preferred lifetime " + "than valid lifetime", + in6addr2str(&oprefix.addr, 0), oprefix.plen); + } else { + dprintf(LOG_NOTICE, FNAME, + "%s has larger preferred lifetime " + "than valid lifetime", + in6addr2str(&oprefix.addr, 0)); + } + return (-1); + } + + /* insert the new prefix to the chain */ + if (dhcp6_add_listval(head, type, &oprefix, NULL) == NULL) { + return (-1); + } + + return (0); +} + +struct ia_conf * +find_iaconf(head, type, iaid) + struct ia_conflist *head; + int type; + u_int32_t iaid; +{ + struct ia_conf *iac; + + for (iac = TAILQ_FIRST(head); iac; iac = TAILQ_NEXT(iac, link)) { + if (iac->type == type && iac->iaid == iaid) + return (iac); + } + + return (NULL); +} + +struct host_conf * +find_hostconf(duid) + struct duid *duid; +{ + struct host_conf *host; + + if ((host = find_dynamic_hostconf(duid)) != NULL) { + return (host); + } + + for (host = host_conflist; host; host = host->next) { + if (host->duid.duid_len == duid->duid_len && + memcmp(host->duid.duid_id, duid->duid_id, + host->duid.duid_len) == 0) { + return (host); + } + } + + return (NULL); +} + +struct authinfo * +find_authinfo(head, name) + struct authinfo *head; + char *name; +{ + struct authinfo *ainfo; + + for (ainfo = head; ainfo; ainfo = ainfo->next) { + if (strcmp(ainfo->name, name) == 0) + return (ainfo); + } + + return (NULL); +} + +struct dhcp6_prefix * +find_prefix6(list, prefix) + struct dhcp6_list *list; + struct dhcp6_prefix *prefix; +{ + struct dhcp6_listval *v; + + for (v = TAILQ_FIRST(list); v; v = TAILQ_NEXT(v, link)) { + if (v->val_prefix6.plen == prefix->plen && + IN6_ARE_ADDR_EQUAL(&v->val_prefix6.addr, &prefix->addr)) { + return (&v->val_prefix6); + } + } + return (NULL); +} + +struct keyinfo * +find_key(realm, realmlen, id) + char *realm; + size_t realmlen; + u_int32_t id; +{ + struct keyinfo *key; + + for (key = key_list; key; key = key->next) { + if (key->realmlen == realmlen && + memcmp(key->realm, realm, realmlen) == 0 && + key->keyid == id) { + return (key); + } + } + + return (NULL); +} + +char * +qstrdup(qstr) + char *qstr; +{ + size_t len; + char *dup; + + len = strlen(qstr); + if (qstr[0] != '"' || len < 2 || qstr[len - 1] != '"') + return (NULL); + + if ((dup = malloc(len)) == NULL) + return (NULL); + + memcpy(dup, qstr + 1, len - 1); + dup[len - 2] = '\0'; + + return (dup); +} + +int +configure_pool(poollist) + struct cf_namelist *poollist; +{ + struct cf_namelist *plp; + + dprintf(LOG_DEBUG, FNAME, "called"); + + if (poollist && dhcp6_mode != DHCP6_MODE_SERVER) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool statement is server-only", + configfilename, poollist->line); + goto bad; + } + + for (plp = poollist; plp; plp = plp->next) { + struct pool_conf *pool = NULL; + struct dhcp6_range *range = NULL; + struct cf_list *cfl; + + for (cfl = plp->params; cfl; cfl = cfl->next) { + switch(cfl->type) { + case DECL_RANGE: + range = cfl->ptr; + break; + default: + dprintf(LOG_ERR, FNAME, "%s:%d " + "invalid pool configuration", + configfilename, cfl->line); + goto bad; + } + } + + if (!range) { + dprintf(LOG_ERR, FNAME, "%s:%d " + "pool '%s' has no range declaration", + configfilename, plp->line, + plp->name); + goto bad; + } + if ((pool = create_pool(plp->name, range)) == NULL) { + dprintf(LOG_ERR, FNAME, + "faled to craete pool '%s'", plp->name); + goto bad; + } + pool->next = pool_conflist0; + pool_conflist0 = pool; + } + + return (0); + + bad: + /* there is currently nothing special to recover the error */ + return (-1); +} + +static void +clear_poolconf(plist) + struct pool_conf *plist; +{ + struct pool_conf *pool, *pool_next; + + dprintf(LOG_DEBUG, FNAME, "called"); + + for (pool = plist; pool; pool = pool_next) { + pool_next = pool->next; + free(pool->name); + free(pool); + } +} + +struct host_conf * +create_dynamic_hostconf(duid, pool) + struct duid *duid; + struct dhcp6_poolspec *pool; +{ + struct dynamic_hostconf *dynconf = NULL; + struct host_conf *host; + char* strid = NULL; + static int init = 1; + + if (init) { + TAILQ_INIT(&dynamic_hostconf_head); + dynamic_hostconf_count = 0; + init = 0; + } + + if (dynamic_hostconf_count >= DHCP6_DYNAMIC_HOSTCONF_MAX) { + struct dynamic_hostconf_listhead *head = &dynamic_hostconf_head; + + dprintf(LOG_DEBUG, FNAME, "reached to the max count (count=%lu)", + dynamic_hostconf_count); + + /* Find the last entry that doesn't need authentication */ + TAILQ_FOREACH_REVERSE(dynconf, head, dynamic_hostconf_listhead, link) + if (dynconf->host->delayedkey == NULL) + break; + if (dynconf == NULL) + dynconf = TAILQ_LAST(head, dynamic_hostconf_listhead); + TAILQ_REMOVE(head, dynconf, link); + dynamic_hostconf_count--; + clear_hostconf(dynconf->host); + } else { + if ((dynconf = malloc(sizeof(*dynconf))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (NULL); + } + } + memset(dynconf, 0, sizeof(*dynconf)); + + if ((host = malloc(sizeof(*host))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + goto bad; + } + memset(host, 0, sizeof(*host)); + TAILQ_INIT(&host->prefix_list); + TAILQ_INIT(&host->addr_list); + + if ((strid = duidstr(duid)) == NULL) + strid = "???"; + if ((host->name = strdup(strid)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + goto bad; + } + if (duidcpy(&host->duid, duid) != 0) { + goto bad; + } + if (pool->name) { + if ((host->pool.name = strdup(pool->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + goto bad; + } + } + host->pool.pltime = pool->pltime; + host->pool.vltime = pool->vltime; + + dynconf->host = host; + TAILQ_INSERT_HEAD(&dynamic_hostconf_head, dynconf, link); + dynamic_hostconf_count++; + + dprintf(LOG_DEBUG, FNAME, "created host_conf (name=%s)", host->name); + + return (host); + +bad: + if (host) + clear_hostconf(host); /* host->next must be NULL */ + if (dynconf) + free(dynconf); + + return (NULL); +} + +struct host_conf * +find_dynamic_hostconf(duid) + struct duid *duid; +{ + struct dynamic_hostconf *dynconf = NULL; + + TAILQ_FOREACH(dynconf, &dynamic_hostconf_head, link) { + if (dynconf->host->duid.duid_len == duid->duid_len && + memcmp(dynconf->host->duid.duid_id, duid->duid_id, + duid->duid_len) == 0) + break; + } + + if (dynconf) { + /* relocation */ + TAILQ_REMOVE(&dynamic_hostconf_head, dynconf, link); + TAILQ_INSERT_HEAD(&dynamic_hostconf_head, dynconf, link); + + return (dynconf->host); + } + + return (NULL); +} + +struct pool_conf * +create_pool(name, range) + char *name; + struct dhcp6_range *range; +{ + struct pool_conf *pool = NULL; + + if (!name || !range) { + return (NULL); + } + + dprintf(LOG_DEBUG, FNAME, "name=%s, range=%s->%s", name, + in6addr2str(&range->min, 0), in6addr2str(&range->max, 0)); + + if (in6_addr_cmp(&range->min, &range->max) > 0) { + dprintf(LOG_ERR, FNAME, "invalid address range %s->%s", + in6addr2str(&range->min, 0), + in6addr2str(&range->max, 0)); + return (NULL); + } + + if ((pool = malloc(sizeof(struct pool_conf))) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + return (NULL); + } + if ((pool->name = strdup(name)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + free(pool); + return (NULL); + } + pool->min = range->min; + pool->max = range->max; + + return (pool); +} + +struct pool_conf * +find_pool(name) + const char *name; +{ + struct pool_conf *pool = NULL; + + if (!name) + return (NULL); + + dprintf(LOG_DEBUG, FNAME, "name=%s", name); + + for (pool = pool_conflist; pool; pool = pool->next) { + if (strcmp(name, pool->name) == 0) { + dprintf(LOG_DEBUG, FNAME, "found (name=%s)", name); + return (pool); + } + } + + dprintf(LOG_DEBUG, FNAME, "not found (name=%s)", name); + + return (NULL); +} + +int +get_free_address_from_pool(pool, addr) + struct pool_conf *pool; + struct in6_addr *addr; +{ + struct in6_addr cur; + if (!pool || !addr) + return (0); + + dprintf(LOG_DEBUG, FNAME, "called (pool=%s)", pool->name); + + for (cur = pool->min; in6_addr_cmp(&cur, &pool->max) <= 0; + in6_addr_inc(&cur)) { + if (!is_leased(&cur) && + !IN6_IS_ADDR_MULTICAST(&cur) && + !IN6_IS_ADDR_LINKLOCAL(&cur) && + !IN6_IS_ADDR_SITELOCAL(&cur)) { + dprintf(LOG_DEBUG, FNAME, "found %s", + in6addr2str(&cur, 0)); + *addr= cur; + return 1; + } + + dprintf(LOG_DEBUG, FNAME, "next address %s", + in6addr2str(&cur, 0)); + } + + dprintf(LOG_NOTICE, FNAME, "no available address"); + return 0; +} + +int +is_available_in_pool(pool, addr) + struct pool_conf *pool; + struct in6_addr *addr; +{ + if (!pool || !addr) + return (0); + + dprintf(LOG_DEBUG, FNAME, "pool=%s, addr=%s", + pool->name, in6addr2str(addr, 0)); + + if (in6_addr_cmp(addr, &pool->min) >= 0 && + in6_addr_cmp(addr, &pool->max) <= 0 && + !is_leased(addr) && + !IN6_IS_ADDR_MULTICAST(addr) && + !IN6_IS_ADDR_LINKLOCAL(addr) && + !IN6_IS_ADDR_SITELOCAL(addr)) { + return (1); + } + + dprintf(LOG_DEBUG, FNAME, "unavailable address (pool=%s, addr=%s)", + pool->name, in6addr2str(addr, 0)); + + return (0); +} + +static int +in6_addr_cmp(addr1, addr2) + struct in6_addr *addr1, *addr2; +{ + int i; + + for (i = 0; i < 16; i++) { + if (addr1->s6_addr[i] != addr2->s6_addr[i]) { + if (addr1->s6_addr[i] > addr2->s6_addr[i]) + return (1); + else + return (-1); + } + } + + return (0); +} + +static void +in6_addr_inc(addr) + struct in6_addr *addr; +{ + int i; + + for (i = 15; i >= 0; i--) { + if (++(addr->s6_addr[i]) != 0x00) + break; + } +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..bf6dae6 --- /dev/null +++ b/config.h @@ -0,0 +1,331 @@ +/* $KAME: config.h,v 1.39 2005/04/01 12:43:36 jinmei Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* definitions of tail-queue types */ +TAILQ_HEAD(ia_conflist, ia_conf); +TAILQ_HEAD(pifc_list, prefix_ifconf); + +struct dhcp6_poolspec { + char* name; + u_int32_t pltime; + u_int32_t vltime; +}; + +struct dhcp6_range { + struct in6_addr min; + struct in6_addr max; +}; + +struct pool_conf { + struct pool_conf *next; + + char* name; + + struct in6_addr min; + struct in6_addr max; +}; + +/* per-interface information */ +struct dhcp6_if { + struct dhcp6_if *next; + + /* timer for the interface */ + struct dhcp6_timer *timer; + + /* event queue */ + TAILQ_HEAD(, dhcp6_event) event_list; + + /* static parameters of the interface */ + char *ifname; + unsigned int ifid; + u_int32_t linkid; /* to send link-local packets */ + /* multiple global address configuration is not supported now */ + struct in6_addr addr; /* global address */ + + /* configuration parameters */ + u_long send_flags; + u_long allow_flags; +#define DHCIFF_INFO_ONLY 0x1 +#define DHCIFF_RAPID_COMMIT 0x2 + + int server_pref; /* server preference (server only) */ + struct dhcp6_poolspec pool; /* address pool (server only) */ + char *scriptpath; /* path to config script (client only) */ + + struct dhcp6_list reqopt_list; + struct ia_conflist iaconf_list; + + /* authentication information */ + int authproto; /* protocol */ + /* the followings are valid only if authproto is not UNDEF */ + int authalgorithm; /* algorithm */ + int authrdm; /* replay attack detection method */ +}; + +/* run-time authentication parameters */ +struct authparam { + int authproto; + int authalgorithm; + int authrdm; + struct keyinfo *key; + int flags; +#define AUTHPARAM_FLAGS_NOPREVRD 0x1 + + u_int64_t prevrd; /* previous RD value provided by the peer */ +}; + +struct dhcp6_event { + TAILQ_ENTRY(dhcp6_event) link; + + struct dhcp6_if *ifp; + struct dhcp6_timer *timer; + + struct duid serverid; + + struct timeval tv_start; /* timestamp when the 1st msg is sent */ + + /* internal timer parameters */ + long retrans; + long init_retrans; + long max_retrans_cnt; + long max_retrans_time; + long max_retrans_dur; + int timeouts; /* number of timeouts */ + + u_int32_t xid; /* current transaction ID */ + int state; + + /* list of known servers */ + struct dhcp6_serverinfo *current_server; + struct dhcp6_serverinfo *servers; + + /* authentication parameters */ + struct authparam *authparam; + + TAILQ_HEAD(, dhcp6_eventdata) data_list; +}; + +typedef enum { DHCP6_EVDATA_IAPD, DHCP6_EVDATA_IANA } dhcp6_eventdata_t; + +struct dhcp6_eventdata { + TAILQ_ENTRY(dhcp6_eventdata) link; + + struct dhcp6_event *event; + dhcp6_eventdata_t type; + void *data; + + void (*destructor) __P((struct dhcp6_eventdata *)); + void *privdata; +}; + +struct dhcp6_serverinfo { + struct dhcp6_serverinfo *next; + + /* option information provided in the advertisement */ + struct dhcp6_optinfo optinfo; + + int pref; /* preference */ + int active; /* bool; if this server is active or not */ + + struct authparam *authparam; /* authentication parameters */ + + /* TODO: remember available information from the server */ +}; + +/* client status code */ +enum {DHCP6S_INIT, DHCP6S_SOLICIT, DHCP6S_INFOREQ, DHCP6S_REQUEST, + DHCP6S_RENEW, DHCP6S_REBIND, DHCP6S_RELEASE, DHCP6S_IDLE}; + +struct prefix_ifconf { + TAILQ_ENTRY(prefix_ifconf) link; + + char *ifname; /* interface name such as ne0 */ + int sla_len; /* SLA ID length in bits */ + u_int32_t sla_id; /* need more than 32bits? */ + int ifid_len; /* interface ID length in bits */ + int ifid_type; /* EUI-64 and manual (unused?) */ + char ifid[16]; /* Interface ID, up to 128bits */ +}; +#define IFID_LEN_DEFAULT 64 +#define SLA_LEN_DEFAULT 16 + +typedef enum { IATYPE_PD, IATYPE_NA } iatype_t; +struct ia_conf { + TAILQ_ENTRY(ia_conf) link; + /*struct ia_conf *next;*/ + iatype_t type; + u_int32_t iaid; + + TAILQ_HEAD(, ia) iadata; /* struct ia is an opaque type */ + + /* type dependent values follow */ +}; + +struct iapd_conf { + struct ia_conf iapd_ia; + + /* type dependent values follow */ + struct dhcp6_list iapd_prefix_list; + struct pifc_list iapd_pif_list; +}; +#define iapd_next iapd_ia.next +#define iapd_type iapd_ia.type +#define iapd_id iapd_ia.iaid + +struct iana_conf { + struct ia_conf iana_ia; + + /* type dependent values follow */ + struct dhcp6_list iana_address_list; +}; +#define iana_next iana_ia.next + +/* per-host configuration */ +struct host_conf { + struct host_conf *next; + + char *name; /* host name to identify the host */ + struct duid duid; /* DUID for the host */ + + /* prefixes to be delegated to the host */ + struct dhcp6_list prefix_list; + /* address to be assigned for the host */ + struct dhcp6_list addr_list; + /* address pool from which addresses are assigned for the host */ + struct dhcp6_poolspec pool; + + /* secret key shared with the client for delayed authentication */ + struct keyinfo *delayedkey; + /* previous replay detection value from the client */ + int saw_previous_rd; /* if we remember the previous value */ + u_int64_t previous_rd; +}; + +/* DHCPv6 authentication information */ +struct authinfo { + struct authinfo *next; + + char *name; /* auth info name */ + + int protocol; /* authentication protocol */ + int algorithm; /* authentication algorithm */ + int rdm; /* random attack detection method */ + + /* keys specific to this info? */ +}; + +/* structures and definitions used in the config file parser */ +struct cf_namelist { + struct cf_namelist *next; + char *name; + int line; /* the line number of the config file */ + struct cf_list *params; +}; + +struct cf_list { + struct cf_list *next; + struct cf_list *tail; + int type; + int line; /* the line number of the config file */ + + /* type dependent values: */ + long long num; + struct cf_list *list; + void *ptr; +}; + +enum { DECL_SEND, DECL_ALLOW, DECL_INFO_ONLY, DECL_REQUEST, DECL_DUID, + DECL_PREFIX, DECL_PREFERENCE, DECL_SCRIPT, DECL_DELAYEDKEY, + DECL_ADDRESS, + DECL_RANGE, DECL_ADDRESSPOOL, + IFPARAM_SLA_ID, IFPARAM_SLA_LEN, + DHCPOPT_RAPID_COMMIT, DHCPOPT_AUTHINFO, + DHCPOPT_DNS, DHCPOPT_DNSNAME, + DHCPOPT_IA_PD, DHCPOPT_IA_NA, DHCPOPT_NTP, + DHCPOPT_REFRESHTIME, + DHCPOPT_NIS, DHCPOPT_NISNAME, + DHCPOPT_NISP, DHCPOPT_NISPNAME, + DHCPOPT_BCMCS, DHCPOPT_BCMCSNAME, + CFLISTENT_GENERIC, + IACONF_PIF, IACONF_PREFIX, IACONF_ADDR, + DHCPOPT_SIP, DHCPOPT_SIPNAME, + AUTHPARAM_PROTO, AUTHPARAM_ALG, AUTHPARAM_RDM, AUTHPARAM_KEY, + KEYPARAM_REALM, KEYPARAM_KEYID, KEYPARAM_SECRET, KEYPARAM_EXPIRE }; + +typedef enum {DHCP6_MODE_SERVER, DHCP6_MODE_CLIENT, DHCP6_MODE_RELAY } +dhcp6_mode_t; + +extern const dhcp6_mode_t dhcp6_mode; + +extern struct dhcp6_if *dhcp6_if; +extern struct dhcp6_ifconf *dhcp6_iflist; +extern struct prefix_ifconf *prefix_ifconflist; +extern struct dhcp6_list siplist; +extern struct dhcp6_list sipnamelist; +extern struct dhcp6_list dnslist; +extern struct dhcp6_list dnsnamelist; +extern struct dhcp6_list ntplist; +extern struct dhcp6_list nislist; +extern struct dhcp6_list nisnamelist; +extern struct dhcp6_list nisplist; +extern struct dhcp6_list nispnamelist; +extern struct dhcp6_list bcmcslist; +extern struct dhcp6_list bcmcsnamelist; +extern long long optrefreshtime; + +extern struct dhcp6_if *ifinit __P((char *)); +extern int ifreset __P((struct dhcp6_if *)); +extern int configure_interface __P((struct cf_namelist *)); +extern int configure_host __P((struct cf_namelist *)); +extern int configure_keys __P((struct cf_namelist *)); +extern int configure_authinfo __P((struct cf_namelist *)); +extern int configure_ia __P((struct cf_namelist *, iatype_t)); +extern int configure_global_option __P((void)); +extern void configure_cleanup __P((void)); +extern void configure_commit __P((void)); +extern int cfparse __P((char *)); +extern struct dhcp6_if *find_ifconfbyname __P((char *)); +extern struct dhcp6_if *find_ifconfbyid __P((unsigned int)); +extern struct prefix_ifconf *find_prefixifconf __P((char *)); +extern struct host_conf *find_hostconf __P((struct duid *)); +extern struct authinfo *find_authinfo __P((struct authinfo *, char *)); +extern struct dhcp6_prefix *find_prefix6 __P((struct dhcp6_list *, + struct dhcp6_prefix *)); +extern struct ia_conf *find_iaconf __P((struct ia_conflist *, int, u_int32_t)); +extern struct keyinfo *find_key __P((char *, size_t, u_int32_t)); +extern int configure_pool __P((struct cf_namelist *)); +extern struct pool_conf *find_pool __P((const char *)); +extern int is_available_in_pool __P((struct pool_conf *, struct in6_addr *)); +extern int get_free_address_from_pool __P((struct pool_conf *, + struct in6_addr *)); +struct host_conf *create_dynamic_hostconf __P((struct duid *, + struct dhcp6_poolspec *)); +extern char *qstrdup __P((char *)); diff --git a/configure b/configure new file mode 100755 index 0000000..6f50634 --- /dev/null +++ b/configure @@ -0,0 +1,6941 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="dhcp6c.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP YACC LEX LEXLIB LEX_OUTPUT_ROOT EGREP LIBOBJS localdbdir user group LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-pedant pedantic compilation + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-localdbdir=VALUE where to put local DB files + --with-user=USER set the owner of the programs + --with-group=GROUP set the group owner of the programs + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +for ac_prog in 'bison -y' byacc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_YACC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$YACC"; then + ac_cv_prog_YACC="$YACC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_YACC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +YACC=$ac_cv_prog_YACC +if test -n "$YACC"; then + echo "$as_me:$LINENO: result: $YACC" >&5 +echo "${ECHO_T}$YACC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$YACC" && break +done +test -n "$YACC" || YACC="yacc" + + +for ac_prog in flex lex +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_LEX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$LEX"; then + ac_cv_prog_LEX="$LEX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LEX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +LEX=$ac_cv_prog_LEX +if test -n "$LEX"; then + echo "$as_me:$LINENO: result: $LEX" >&5 +echo "${ECHO_T}$LEX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$LEX" && break +done +test -n "$LEX" || LEX=":" + +if test -z "$LEXLIB" +then + echo "$as_me:$LINENO: checking for yywrap in -lfl" >&5 +echo $ECHO_N "checking for yywrap in -lfl... $ECHO_C" >&6 +if test "${ac_cv_lib_fl_yywrap+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lfl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yywrap (); +int +main () +{ +yywrap (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_fl_yywrap=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_fl_yywrap=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_fl_yywrap" >&5 +echo "${ECHO_T}$ac_cv_lib_fl_yywrap" >&6 +if test $ac_cv_lib_fl_yywrap = yes; then + LEXLIB="-lfl" +else + echo "$as_me:$LINENO: checking for yywrap in -ll" >&5 +echo $ECHO_N "checking for yywrap in -ll... $ECHO_C" >&6 +if test "${ac_cv_lib_l_yywrap+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ll $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yywrap (); +int +main () +{ +yywrap (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_l_yywrap=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_l_yywrap=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_l_yywrap" >&5 +echo "${ECHO_T}$ac_cv_lib_l_yywrap" >&6 +if test $ac_cv_lib_l_yywrap = yes; then + LEXLIB="-ll" +fi + +fi + +fi + +if test "x$LEX" != "x:"; then + echo "$as_me:$LINENO: checking lex output file root" >&5 +echo $ECHO_N "checking lex output file root... $ECHO_C" >&6 +if test "${ac_cv_prog_lex_root+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # The minimal lex program is just a single line: %%. But some broken lexes +# (Solaris, I think it was) want two %% lines, so accommodate them. +cat >conftest.l <<_ACEOF +%% +%% +_ACEOF +{ (eval echo "$as_me:$LINENO: \"$LEX conftest.l\"") >&5 + (eval $LEX conftest.l) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +if test -f lex.yy.c; then + ac_cv_prog_lex_root=lex.yy +elif test -f lexyy.c; then + ac_cv_prog_lex_root=lexyy +else + { { echo "$as_me:$LINENO: error: cannot find output from $LEX; giving up" >&5 +echo "$as_me: error: cannot find output from $LEX; giving up" >&2;} + { (exit 1); exit 1; }; } +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_lex_root" >&5 +echo "${ECHO_T}$ac_cv_prog_lex_root" >&6 +rm -f conftest.l +LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root + +echo "$as_me:$LINENO: checking whether yytext is a pointer" >&5 +echo $ECHO_N "checking whether yytext is a pointer... $ECHO_C" >&6 +if test "${ac_cv_prog_lex_yytext_pointer+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # POSIX says lex can declare yytext either as a pointer or an array; the +# default is implementation-dependent. Figure out which it is, since +# not all implementations provide the %pointer and %array declarations. +ac_cv_prog_lex_yytext_pointer=no +echo 'extern char *yytext;' >>$LEX_OUTPUT_ROOT.c +ac_save_LIBS=$LIBS +LIBS="$LIBS $LEXLIB" +cat >conftest.$ac_ext <<_ACEOF +`cat $LEX_OUTPUT_ROOT.c` +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_lex_yytext_pointer=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_save_LIBS +rm -f "${LEX_OUTPUT_ROOT}.c" + +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_lex_yytext_pointer" >&5 +echo "${ECHO_T}$ac_cv_prog_lex_yytext_pointer" >&6 +if test $ac_cv_prog_lex_yytext_pointer = yes; then + +cat >>confdefs.h <<\_ACEOF +#define YYTEXT_POINTER 1 +_ACEOF + +fi + +fi + + + +CFLAGS="$CFLAGS -I\$(srcdir)" + +if test -x /usr/bin/sun && /usr/bin/sun; then + CFLAGS="$CFLAGS -D_XOPEN_SOURCE=500 -D__EXTENSIONS__" + LDFLAGS="-lxnet -ldevinfo -lnsl -lsocket -lrt" +fi + +echo "$as_me:$LINENO: checking for sys/queue.h" >&5 +echo $ECHO_N "checking for sys/queue.h... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +TAILQ_HEAD(test, none); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + CFLAGS="$CFLAGS -I\$(srcdir)/missing" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifdef __KAME__ + yes +#endif +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + result=kame +else + result=regular +fi +rm -f conftest* + +echo "$as_me:$LINENO: checking for getaddrinfo/getnameinfo library" >&5 +echo $ECHO_N "checking for getaddrinfo/getnameinfo library... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $result" >&5 +echo "${ECHO_T}$result" >&6 +case $result in +kame) cat >>confdefs.h <<\_ACEOF +#define INET6 1 +_ACEOF + + o_LIBS="$LIBS" + LIBS="$LIBS -L/usr/local/v6/lib" + +echo "$as_me:$LINENO: checking for getaddrinfo in -linet6" >&5 +echo $ECHO_N "checking for getaddrinfo in -linet6... $ECHO_C" >&6 +if test "${ac_cv_lib_inet6_getaddrinfo+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-linet6 $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char getaddrinfo (); +int +main () +{ +getaddrinfo (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_inet6_getaddrinfo=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_inet6_getaddrinfo=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_inet6_getaddrinfo" >&5 +echo "${ECHO_T}$ac_cv_lib_inet6_getaddrinfo" >&6 +if test $ac_cv_lib_inet6_getaddrinfo = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBINET6 1 +_ACEOF + + LIBS="-linet6 $LIBS" + +else + LIBS="$o_LIBS" +fi + + ;; +*) ;; +esac + + +for ac_func in getaddrinfo +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +for ac_func in getnameinfo +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +for ac_func in getifaddrs +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +for ac_func in if_nametoindex +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_func in strlcpy strlcat +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +for ac_func in daemon +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +for ac_func in warnx +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + +for ac_header in fcntl.h sys/ioctl.h sys/time.h syslog.h unistd.h ifaddrs.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking for socklen_t" >&5 +echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +int +main () +{ +socklen_t x; x = 1; return x; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking for socklen_t" >&5 +echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6 +if test "${ac_cv_type_socklen_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((socklen_t *) 0) + return 0; +if (sizeof (socklen_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_socklen_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_socklen_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_socklen_t" >&5 +echo "${ECHO_T}$ac_cv_type_socklen_t" >&6 +if test $ac_cv_type_socklen_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define socklen_t int +_ACEOF + +fi + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for struct tm.tm_zone" >&5 +echo $ECHO_N "checking for struct tm.tm_zone... $ECHO_C" >&6 +if test "${ac_cv_member_struct_tm_tm_zone+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_cv_struct_tm> + + +int +main () +{ +static struct tm ac_aggr; +if (ac_aggr.tm_zone) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_zone=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_cv_struct_tm> + + +int +main () +{ +static struct tm ac_aggr; +if (sizeof ac_aggr.tm_zone) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_member_struct_tm_tm_zone=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_member_struct_tm_tm_zone=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_member_struct_tm_tm_zone" >&5 +echo "${ECHO_T}$ac_cv_member_struct_tm_tm_zone" >&6 +if test $ac_cv_member_struct_tm_tm_zone = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_TM_TM_ZONE 1 +_ACEOF + + +fi + +if test "$ac_cv_member_struct_tm_tm_zone" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TM_ZONE 1 +_ACEOF + +else + echo "$as_me:$LINENO: checking for tzname" >&5 +echo $ECHO_N "checking for tzname... $ECHO_C" >&6 +if test "${ac_cv_var_tzname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#ifndef tzname /* For SGI. */ +extern char *tzname[]; /* RS6000 and others reject char **tzname. */ +#endif + +int +main () +{ +atoi(*tzname); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_var_tzname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_var_tzname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_var_tzname" >&5 +echo "${ECHO_T}$ac_cv_var_tzname" >&6 + if test $ac_cv_var_tzname = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TZNAME 1 +_ACEOF + + fi +fi + +echo "$as_me:$LINENO: checking for sig_atomic_t" >&5 +echo $ECHO_N "checking for sig_atomic_t... $ECHO_C" >&6 +if test "${ac_cv_type_sig_atomic_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + #include + +int +main () +{ +if ((sig_atomic_t *) 0) + return 0; +if (sizeof (sig_atomic_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_sig_atomic_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_sig_atomic_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_sig_atomic_t" >&5 +echo "${ECHO_T}$ac_cv_type_sig_atomic_t" >&6 +if test $ac_cv_type_sig_atomic_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_SIG_ATOMIC_T 1 +_ACEOF + + +else + cat >>confdefs.h <<\_ACEOF +#define sig_atomic_t u_long +_ACEOF + +fi + + + +echo "$as_me:$LINENO: checking whether getpgrp requires zero arguments" >&5 +echo $ECHO_N "checking whether getpgrp requires zero arguments... $ECHO_C" >&6 +if test "${ac_cv_func_getpgrp_void+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Use it with a single arg. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +getpgrp (0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_getpgrp_void=no +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_getpgrp_void=yes +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_getpgrp_void" >&5 +echo "${ECHO_T}$ac_cv_func_getpgrp_void" >&6 +if test $ac_cv_func_getpgrp_void = yes; then + +cat >>confdefs.h <<\_ACEOF +#define GETPGRP_VOID 1 +_ACEOF + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo "$as_me:$LINENO: checking whether setpgrp takes no argument" >&5 +echo $ECHO_N "checking whether setpgrp takes no argument... $ECHO_C" >&6 +if test "${ac_cv_func_setpgrp_void+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot check setpgrp when cross compiling" >&5 +echo "$as_me: error: cannot check setpgrp when cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if HAVE_UNISTD_H +# include +#endif + +int +main () +{ +/* If this system has a BSD-style setpgrp which takes arguments, + setpgrp(1, 1) will fail with ESRCH and return -1, in that case + exit successfully. */ + exit (setpgrp (1,1) == -1 ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_setpgrp_void=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_setpgrp_void=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_setpgrp_void" >&5 +echo "${ECHO_T}$ac_cv_func_setpgrp_void" >&6 +if test $ac_cv_func_setpgrp_void = yes; then + +cat >>confdefs.h <<\_ACEOF +#define SETPGRP_VOID 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + + + +for ac_func in mktime select socket +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in clock_gettime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +echo "$as_me:$LINENO: checking for local DB directory" >&5 +echo $ECHO_N "checking for local DB directory... $ECHO_C" >&6 + +# Check whether --with-localdbdir or --without-localdbdir was given. +if test "${with_localdbdir+set}" = set; then + withval="$with_localdbdir" + localdbdir="$withval" +else + localdbdir="/var/db" +fi; +echo "$as_me:$LINENO: result: $localdbdir" >&5 +echo "${ECHO_T}$localdbdir" >&6 + + + +for ac_func in arc4random +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + case $LIBOBJS in + "$ac_func.$ac_objext" | \ + *" $ac_func.$ac_objext" | \ + "$ac_func.$ac_objext "* | \ + *" $ac_func.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;; +esac + +fi +done + + + +echo "$as_me:$LINENO: checking if --enable-pedant option is specified" >&5 +echo $ECHO_N "checking if --enable-pedant option is specified... $ECHO_C" >&6 +# Check whether --enable-pedant or --disable-pedant was given. +if test "${enable_pedant+set}" = set; then + enableval="$enable_pedant" + if test "$enableval" = "yes"; then + CFLAGS="-Wall -Werror $CFLAGS" + fi +else + enableval=no +fi; +echo "$as_me:$LINENO: result: $enableval" >&5 +echo "${ECHO_T}$enableval" >&6; + +echo "$as_me:$LINENO: checking ANSI predeclared __func__" >&5 +echo $ECHO_N "checking ANSI predeclared __func__... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +void func(void) {printf("%s\n", __func__);} + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + pred_func_id=ansi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + pred_func_id=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +if test $pred_func_id = no; then +echo "$as_me:$LINENO: checking gcc predelcared __FUNCTION__" >&5 +echo $ECHO_N "checking gcc predelcared __FUNCTION__... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +void func(void) {printf("%s\n", __FUNCTION__);} + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + pred_func_id=gcc +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + pred_func_id=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +case $pred_func_id in + ansi) + cat >>confdefs.h <<\_ACEOF +#define HAVE_ANSI_FUNC 1 +_ACEOF + + ;; + gcc) + cat >>confdefs.h <<\_ACEOF +#define HAVE_GCC_FUNCTION 1 +_ACEOF + + ;; +esac + + +echo "$as_me:$LINENO: checking checking the existence TAILQ_FOREACH_REVERSE" >&5 +echo $ECHO_N "checking checking the existence TAILQ_FOREACH_REVERSE... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + #ifdef TAILQ_FOREACH_REVERSE + yes +#endif +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + result=yes +else + result=no +fi +rm -f conftest* + +echo "$as_me:$LINENO: result: $result" >&5 +echo "${ECHO_T}$result" >&6 +case $result in +yes) tailq_foreach=yes + ;; +*) tailq_foreach=no + ;; +esac + +if test $tailq_foreach = yes ; then + echo "$as_me:$LINENO: checking argument order of TAILQ_FOREACH_REVERSE" >&5 +echo $ECHO_N "checking argument order of TAILQ_FOREACH_REVERSE... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + #include + #include + + TAILQ_HEAD(tailhead, entry) head = TAILQ_HEAD_INITIALIZER(head); + struct entry { + TAILQ_ENTRY(entry) entries; + } *p; + + int i; + struct tailhead *headp; + + TAILQ_FOREACH_REVERSE(p, headp, tailhead, entries) { + } + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: new" >&5 +echo "${ECHO_T}new" >&6 + cat >>confdefs.h <<\_ACEOF +#define HAVE_TAILQ_FOREACH_REVERSE 1 +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: old" >&5 +echo "${ECHO_T}old" >&6 + cat >>confdefs.h <<\_ACEOF +#define HAVE_TAILQ_FOREACH_REVERSE_OLD 1 +_ACEOF + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext; +fi + +echo "$as_me:$LINENO: checking if you have a length field in struct sockaddr*" >&5 +echo $ECHO_N "checking if you have a length field in struct sockaddr*... $ECHO_C" >&6 +if test "${ts_cv_sa_len+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #include + #include + +int +main () +{ + + struct sockaddr a; + a.sa_len = 1; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ts_cv_sa_len="yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ts_cv_sa_len="no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +echo "$as_me:$LINENO: result: $ts_cv_sa_len" >&5 +echo "${ECHO_T}$ts_cv_sa_len" >&6; +if test "$ts_cv_sa_len" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SA_LEN 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking the owner of the programs" >&5 +echo $ECHO_N "checking the owner of the programs... $ECHO_C" >&6 + +# Check whether --with-user or --without-user was given. +if test "${with_user+set}" = set; then + withval="$with_user" + user="$withval" +else + user="bin" +fi; +echo "$as_me:$LINENO: result: $user" >&5 +echo "${ECHO_T}$user" >&6 + + +echo "$as_me:$LINENO: checking the group owner of the programs" >&5 +echo $ECHO_N "checking the group owner of the programs... $ECHO_C" >&6 + +# Check whether --with-group or --without-group was given. +if test "${with_group+set}" = set; then + withval="$with_group" + group="$withval" +else + group="bin" +fi; +echo "$as_me:$LINENO: result: $group" >&5 +echo "${ECHO_T}$group" >&6 + + + +for ac_header in stdarg.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + ac_config_files="$ac_config_files Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CPP@,$CPP,;t t +s,@YACC@,$YACC,;t t +s,@LEX@,$LEX,;t t +s,@LEXLIB@,$LEXLIB,;t t +s,@LEX_OUTPUT_ROOT@,$LEX_OUTPUT_ROOT,;t t +s,@EGREP@,$EGREP,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@localdbdir@,$localdbdir,;t t +s,@user@,$user,;t t +s,@group@,$group,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..59dd84e --- /dev/null +++ b/configure.in @@ -0,0 +1,239 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(dhcp6c.c) + +dnl Checks for programs. +AC_PROG_INSTALL +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_CPP +AC_PROG_YACC +AC_PROG_LEX + +dnl Checks for libraries. +dnl AC_CHECK_LIB(c, kvm_open, [], [AC_CHECK_LIB(kvm, kvm_open)]) +dnl AC_CHECK_LIB(mld, nlist) + +dnl pcap library +dnl CFLAGS="-I. $CFLAGS" +dnl AC_MSG_CHECKING(for pcap library/header) +dnl for dir in /usr/local/libpcap /usr/local/v6 /usr/local \ +dnl ../libpcap* ../../libpcap*; do +dnl ac_cv_pcap_lib=no +dnl ac_cv_pcap_include=no +dnl if test -d $dir -a -f $dir/libpcap.a; then +dnl ac_cv_pcap_lib="$dir" +dnl elif test -d $dir/lib -a -f $dir/lib/libpcap.a; then +dnl ac_cv_pcap_lib="$dir/lib" +dnl fi +dnl if test -d $dir -a -f $dir/pcap.h; then +dnl ac_cv_pcap_include="$dir" +dnl elif test -d $dir/include -a -f $dir/include/pcap.h; then +dnl ac_cv_pcap_include="$dir/include" +dnl fi +dnl if test "$ac_cv_pcap_lib" != "no" -a "$ac_cv_pcap_include" != "no"; then +dnl LIBS="-L$ac_cv_pcap_lib $LIBS" +dnl CFLAGS="-I$ac_cv_pcap_include $CFLAGS" +dnl break +dnl fi +dnl done +dnl if test "$ac_cv_pcap_lib" = "no" -a "$ac_cv_pcap_include" = "no"; then +dnl AC_MSG_RESULT(no) +dnl else +dnl AC_MSG_RESULT($ac_cv_pcap_lib and $ac_cv_pcap_include) +dnl fi +dnl AC_CHECK_LIB(pcap, pcap_lookupdev, [], +dnl [echo "Fatal: libpcap.a not found" +dnl exit 1]) +dnl AC_TRY_COMPILE([#include ], [], [], [dnl +dnl echo "Fatal: pcap.h not found" +dnl exit 1]) + +CFLAGS="$CFLAGS -I\$(srcdir)" + +dnl On Sun systems, we need to use the standards-compliant 3XNET functions +if test -x /usr/bin/sun && /usr/bin/sun; then + CFLAGS="$CFLAGS -D_XOPEN_SOURCE=500 -D__EXTENSIONS__" + LDFLAGS="-lxnet -ldevinfo -lnsl -lsocket -lrt" +fi + +AC_MSG_CHECKING(for sys/queue.h) +AC_TRY_COMPILE([#include ], + [TAILQ_HEAD(test, none);], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + CFLAGS="$CFLAGS -I\$(srcdir)/missing"]) + +AC_EGREP_CPP(yes, +[#include +#ifdef __KAME__ + yes +#endif], [result=kame], [result=regular]) +AC_MSG_CHECKING(for getaddrinfo/getnameinfo library) +AC_MSG_RESULT($result) +case $result in +kame) AC_DEFINE(INET6) + o_LIBS="$LIBS" + LIBS="$LIBS -L/usr/local/v6/lib" + AC_CHECK_LIB(inet6, getaddrinfo, [], [LIBS="$o_LIBS"]) + ;; +*) ;; +esac + +AC_REPLACE_FUNCS(getaddrinfo) +AC_REPLACE_FUNCS(getnameinfo) +AC_REPLACE_FUNCS(getifaddrs) +AC_CHECK_FUNCS(if_nametoindex) +AC_REPLACE_FUNCS(strlcpy strlcat) +AC_REPLACE_FUNCS(daemon) +AC_REPLACE_FUNCS(warnx) + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h sys/ioctl.h sys/time.h syslog.h unistd.h ifaddrs.h) +AC_MSG_CHECKING(for socklen_t) +AC_TRY_COMPILE([#include +#include ], + [socklen_t x; x = 1; return x;], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_CHECK_TYPE(socklen_t, int)]) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM +AC_STRUCT_TIMEZONE +AC_CHECK_TYPES([sig_atomic_t], , [AC_DEFINE(sig_atomic_t, u_long)], [#include + #include ]) + + +dnl Checks for library functions. +AC_FUNC_GETPGRP +AC_PROG_GCC_TRADITIONAL +AC_FUNC_SETPGRP +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(mktime select socket) +AC_CHECK_FUNCS(clock_gettime) + +dnl configure local DB directory +AC_MSG_CHECKING(for local DB directory) +AC_ARG_WITH(localdbdir, +[ --with-localdbdir=VALUE where to put local DB files], + localdbdir="$withval", localdbdir="/var/db") +AC_MSG_RESULT($localdbdir) +AC_SUBST(localdbdir) + +dnl Checks for arc4random +AC_REPLACE_FUNCS(arc4random) + +AC_MSG_CHECKING(if --enable-pedant option is specified) +AC_ARG_ENABLE(pedant, [ --enable-pedant pedantic compilation], + [if test "$enableval" = "yes"; then + CFLAGS="-Wall -Werror $CFLAGS" + fi], [enableval=no]) +AC_MSG_RESULT($enableval); + +dnl Checks predeclared identifers of function names +AC_MSG_CHECKING(ANSI predeclared __func__) +AC_TRY_COMPILE([], +[void func(void) {printf("%s\n", __func__);}], + [AC_MSG_RESULT(yes) + pred_func_id=ansi], + [AC_MSG_RESULT(no) + pred_func_id=no]) +if test $pred_func_id = no; then +AC_MSG_CHECKING(gcc predelcared __FUNCTION__) + AC_TRY_COMPILE([], + [void func(void) {printf("%s\n", __FUNCTION__);}], + [AC_MSG_RESULT(yes) + pred_func_id=gcc], + [AC_MSG_RESULT(no) + pred_func_id=no]) +fi +case $pred_func_id in + ansi) + AC_DEFINE(HAVE_ANSI_FUNC) + ;; + gcc) + AC_DEFINE(HAVE_GCC_FUNCTION) + ;; +esac + +dnl DHCP option type values not officially defined +dnl (no such option now) + +dnl Checks the existence of TAILQ_FOREACH_REVERSE +AC_MSG_CHECKING(checking the existence TAILQ_FOREACH_REVERSE) +AC_EGREP_CPP(yes, +[#include + #ifdef TAILQ_FOREACH_REVERSE + yes +#endif], [result=yes], [result=no]) +AC_MSG_RESULT($result) +case $result in +yes) tailq_foreach=yes + ;; +*) tailq_foreach=no + ;; +esac + +dnl Checks the argument order of TAILQ_FOREACH_REVERSE if it exists +if test $tailq_foreach = yes ; then + AC_MSG_CHECKING(argument order of TAILQ_FOREACH_REVERSE) + AC_TRY_COMPILE([], + [ + #include + #include + + TAILQ_HEAD(tailhead, entry) head = TAILQ_HEAD_INITIALIZER(head); + struct entry { + TAILQ_ENTRY(entry) entries; + } *p; + + int i; + struct tailhead *headp; + + TAILQ_FOREACH_REVERSE(p, headp, tailhead, entries) { + } + ], + [AC_MSG_RESULT(new) + AC_DEFINE(HAVE_TAILQ_FOREACH_REVERSE)], + [AC_MSG_RESULT(old) + AC_DEFINE(HAVE_TAILQ_FOREACH_REVERSE_OLD)]); +fi + +dnl Checks for other features +AC_MSG_CHECKING(if you have a length field in struct sockaddr*) +AC_CACHE_VAL(ts_cv_sa_len, [dnl + AC_TRY_COMPILE([ + #include + #include + ], [ + struct sockaddr a; + a.sa_len = 1; + ], [ts_cv_sa_len="yes"], [ts_cv_sa_len="no"])]) +AC_MSG_RESULT($ts_cv_sa_len); +if test "$ts_cv_sa_len" = yes; then + AC_DEFINE([HAVE_SA_LEN], 1, [Define to 1 if you have a length field in struct sockaddr]) +fi + +dnl configure the ownership of the programs +AC_MSG_CHECKING(the owner of the programs) +AC_ARG_WITH(user, +[ --with-user=USER set the owner of the programs], + user="$withval", user="bin") +AC_MSG_RESULT($user) +AC_SUBST(user) + +dnl configure the group ownership of the programs +AC_MSG_CHECKING(the group owner of the programs) +AC_ARG_WITH(group, +[ --with-group=GROUP set the group owner of the programs], + group="$withval", group="bin") +AC_MSG_RESULT($group) +AC_SUBST(group) + +AC_CHECK_HEADERS(stdarg.h) + +AC_OUTPUT(Makefile) diff --git a/control.h b/control.h new file mode 100644 index 0000000..de1214b --- /dev/null +++ b/control.h @@ -0,0 +1,91 @@ +/* $KAME: control.h,v 1.7 2005/01/12 06:06:11 suz Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __sun__ +#ifndef U_INT16_T_DEFINED +#define U_INT16_T_DEFINED +typedef uint16_t u_int16_t; +#endif +#ifndef U_INT32_T_DEFINED +#define U_INT32_T_DEFINED +typedef uint32_t u_int32_t; +#endif +#endif + +#define DEFAULT_SERVER_CONTROL_ADDR "::1" /* default IPv6 address for server + * control socket */ +#define DEFAULT_SERVER_CONTROL_PORT "5547" /* default TCP port for server + * control socket */ +#define DEFAULT_CLIENT_CONTROL_ADDR "::1" /* default IPv6 address for client + * control socket */ +#define DEFAULT_CLIENT_CONTROL_PORT "5546" /* default TCP port for client + * control socket */ + +#define DHCP6CTL_VERSION 0 + +/* control commands */ +#define DHCP6CTL_COMMAND_RELOAD 1 +#define DHCP6CTL_COMMAND_REMOVE 2 +#define DHCP6CTL_COMMAND_START 3 +#define DHCP6CTL_COMMAND_STOP 4 + +/* control objects */ +#define DHCP6CTL_BINDING 1 +#define DHCP6CTL_BINDING_IA 2 +#define DHCP6CTL_IA_PD 3 +#define DHCP6CTL_INTERFACE 4 +#define DHCP6CTL_IA_NA 5 + +/* + * Hash protocol/algorithm types. Use same values for DHCPv6 protocol + * authentication for code sharing. + */ +enum { DHCP6CTL_AUTHPROTO_UNDEF = -1 }; +enum { DHCP6CTL_AUTHALG_UNDEF = -1, DHCP6CTL_AUTHALG_HMACMD5 = 1 }; + +/* + * Packet formats of command protocol + */ +struct dhcp6ctl { + u_int16_t command; + u_int16_t len; + u_int16_t version; + u_int16_t reserved; + u_int32_t timestamp; +} __attribute__ ((__packed__)); + +struct dhcp6ctl_iaspec { + u_int32_t flags; + u_int32_t type; + u_int32_t id; + u_int32_t duidlen; + /* variable length of DUID follows */ +} __attribute__ ((__packed__)); diff --git a/dhcp6.h b/dhcp6.h new file mode 100644 index 0000000..6afb517 --- /dev/null +++ b/dhcp6.h @@ -0,0 +1,385 @@ +/* $KAME: dhcp6.h,v 1.56 2005/03/20 06:46:09 jinmei Exp $ */ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __DHCP6_H_DEFINED +#define __DHCP6_H_DEFINED + +#ifdef __sun__ +#define __P(x) x +typedef uint8_t u_int8_t; +#ifndef U_INT16_T_DEFINED +#define U_INT16_T_DEFINED +typedef uint16_t u_int16_t; +#endif +#ifndef U_INT32_T_DEFINED +#define U_INT32_T_DEFINED +typedef uint32_t u_int32_t; +#endif +typedef uint64_t u_int64_t; +#ifndef CMSG_SPACE +#define CMSG_SPACE(l) \ + ((unsigned int)_CMSG_HDR_ALIGN(sizeof (struct cmsghdr) + (l))) +#endif +#ifndef CMSG_LEN +#define CMSG_LEN(l) \ + ((unsigned int)_CMSG_DATA_ALIGN(sizeof (struct cmsghdr)) + (l)) +#endif +#endif + +/* Error Values */ +#define DH6ERR_FAILURE 16 +#define DH6ERR_AUTHFAIL 17 +#define DH6ERR_POORLYFORMED 18 +#define DH6ERR_UNAVAIL 19 +#define DH6ERR_OPTUNAVAIL 20 + +/* Message type */ +#define DH6_SOLICIT 1 +#define DH6_ADVERTISE 2 +#define DH6_REQUEST 3 +#define DH6_CONFIRM 4 +#define DH6_RENEW 5 +#define DH6_REBIND 6 +#define DH6_REPLY 7 +#define DH6_RELEASE 8 +#define DH6_DECLINE 9 +#define DH6_RECONFIGURE 10 +#define DH6_INFORM_REQ 11 +#define DH6_RELAY_FORW 12 +#define DH6_RELAY_REPLY 13 + +/* Predefined addresses */ +#define DH6ADDR_ALLAGENT "ff02::1:2" +#define DH6ADDR_ALLSERVER "ff05::1:3" +#define DH6PORT_DOWNSTREAM "546" +#define DH6PORT_UPSTREAM "547" + +/* Protocol constants */ + +/* timer parameters (msec, unless explicitly commented) */ +#define SOL_MAX_DELAY 1000 +#define SOL_TIMEOUT 1000 +#define SOL_MAX_RT 120000 +#define INF_TIMEOUT 1000 +#define INF_MAX_RT 120000 +#define REQ_TIMEOUT 1000 +#define REQ_MAX_RT 30000 +#define REQ_MAX_RC 10 /* Max Request retry attempts */ +#define REN_TIMEOUT 10000 /* 10secs */ +#define REN_MAX_RT 600000 /* 600secs */ +#define REB_TIMEOUT 10000 /* 10secs */ +#define REB_MAX_RT 600000 /* 600secs */ +#define REL_TIMEOUT 1000 /* 1 sec */ +#define REL_MAX_RC 5 + +#define DHCP6_DURATION_INFINITE 0xffffffff +#define DHCP6_DURATION_MIN 30 + +#define DHCP6_RELAY_MULTICAST_HOPS 32 +#define DHCP6_RELAY_HOP_COUNT_LIMIT 32 + +#define DHCP6_IRT_DEFAULT 86400 /* 1 day */ +#define DHCP6_IRT_MINIMUM 600 + +/* DUID: DHCP unique Identifier */ +struct duid { + size_t duid_len; /* length */ + char *duid_id; /* variable length ID value (must be opaque) */ +}; + +struct dhcp6_vbuf { /* generic variable length buffer */ + int dv_len; + caddr_t dv_buf; +}; + +/* option information */ +struct dhcp6_ia { /* identity association */ + u_int32_t iaid; + u_int32_t t1; + u_int32_t t2; +}; + +struct dhcp6_prefix { /* IA_PA */ + u_int32_t pltime; + u_int32_t vltime; + struct in6_addr addr; + int plen; +}; + +struct dhcp6_statefuladdr { /* IA_NA */ + u_int32_t pltime; + u_int32_t vltime; + struct in6_addr addr; +}; + +/* Internal data structure */ +typedef enum { DHCP6_LISTVAL_NUM = 1, + DHCP6_LISTVAL_STCODE, DHCP6_LISTVAL_ADDR6, + DHCP6_LISTVAL_IAPD, DHCP6_LISTVAL_PREFIX6, + DHCP6_LISTVAL_IANA, DHCP6_LISTVAL_STATEFULADDR6, + DHCP6_LISTVAL_VBUF +} dhcp6_listval_type_t; +TAILQ_HEAD(dhcp6_list, dhcp6_listval); +struct dhcp6_listval { + TAILQ_ENTRY(dhcp6_listval) link; + + dhcp6_listval_type_t type; + + union { + int uv_num; + u_int16_t uv_num16; + struct in6_addr uv_addr6; + struct dhcp6_prefix uv_prefix6; + struct dhcp6_statefuladdr uv_statefuladdr6; + struct dhcp6_ia uv_ia; + struct dhcp6_vbuf uv_vbuf; + } uv; + + struct dhcp6_list sublist; +}; +#define val_num uv.uv_num +#define val_num16 uv.uv_num16 +#define val_addr6 uv.uv_addr6 +#define val_ia uv.uv_ia +#define val_prefix6 uv.uv_prefix6 +#define val_statefuladdr6 uv.uv_statefuladdr6 +#define val_vbuf uv.uv_vbuf + +struct dhcp6_optinfo { + struct duid clientID; /* DUID */ + struct duid serverID; /* DUID */ + + int rapidcommit; /* bool */ + int pref; /* server preference */ + int32_t elapsed_time; /* elapsed time (from client to server only) */ + int64_t refreshtime; /* info refresh time for stateless options */ + + struct dhcp6_list iapd_list; /* list of IA_PD */ + struct dhcp6_list iana_list; /* list of IA_NA */ + struct dhcp6_list reqopt_list; /* options in option request */ + struct dhcp6_list stcode_list; /* status code */ + struct dhcp6_list sip_list; /* SIP server list */ + struct dhcp6_list sipname_list; /* SIP domain list */ + struct dhcp6_list dns_list; /* DNS server list */ + struct dhcp6_list dnsname_list; /* Domain Search list */ + struct dhcp6_list ntp_list; /* NTP server list */ + struct dhcp6_list prefix_list; /* prefix list */ + struct dhcp6_list nis_list; /* NIS server list */ + struct dhcp6_list nisname_list; /* NIS domain list */ + struct dhcp6_list nisp_list; /* NIS+ server list */ + struct dhcp6_list nispname_list; /* NIS+ domain list */ + struct dhcp6_list bcmcs_list; /* BCMC server list */ + struct dhcp6_list bcmcsname_list; /* BCMC domain list */ + + struct dhcp6_vbuf relay_msg; /* relay message */ +#define relaymsg_len relay_msg.dv_len +#define relaymsg_msg relay_msg.dv_buf + + struct dhcp6_vbuf ifidopt; /* Interface-id */ +#define ifidopt_len ifidopt.dv_len +#define ifidopt_id ifidopt.dv_buf + + u_int authflags; +#define DHCP6OPT_AUTHFLAG_NOINFO 0x1 + int authproto; + int authalgorithm; + int authrdm; + /* the followings are effective only when NOINFO is unset */ + u_int64_t authrd; + union { + struct { + u_int32_t keyid; + struct dhcp6_vbuf realm; + int offset; /* offset to the HMAC field */ + } aiu_delayed; + struct { + int type; + int offset; /* offset to the HMAC field */ + char val[16]; /* key value */ + } aiu_reconfig; + } authinfo; +#define delayedauth_keyid authinfo.aiu_delayed.keyid +#define delayedauth_realmlen authinfo.aiu_delayed.realm.dv_len +#define delayedauth_realmval authinfo.aiu_delayed.realm.dv_buf +#define delayedauth_offset authinfo.aiu_delayed.offset +#define reconfigauth_type authinfo.aiu_reconfig.type +#define reconfigauth_offset authinfo.aiu_reconfig.offset +#define reconfigauth_val authinfo.aiu_reconfig.val +}; + +/* DHCP6 base packet format */ +struct dhcp6 { + union { + u_int8_t m; + u_int32_t x; + } dh6_msgtypexid; + /* options follow */ +} __attribute__ ((__packed__)); +#define dh6_msgtype dh6_msgtypexid.m +#define dh6_xid dh6_msgtypexid.x +#define DH6_XIDMASK 0x00ffffff + +/* DHCPv6 relay messages */ +struct dhcp6_relay { + u_int8_t dh6relay_msgtype; + u_int8_t dh6relay_hcnt; + struct in6_addr dh6relay_linkaddr; /* XXX: badly aligned */ + struct in6_addr dh6relay_peeraddr; /* ditto */ + /* options follow */ +} __attribute__ ((__packed__)); + +/* options */ +#define DH6OPT_CLIENTID 1 +#define DH6OPT_SERVERID 2 +#define DH6OPT_IA_NA 3 +#define DH6OPT_IA_TA 4 +#define DH6OPT_IAADDR 5 +#define DH6OPT_ORO 6 +#define DH6OPT_PREFERENCE 7 +# define DH6OPT_PREF_UNDEF -1 +# define DH6OPT_PREF_MAX 255 +#define DH6OPT_ELAPSED_TIME 8 +# define DH6OPT_ELAPSED_TIME_UNDEF -1 +#define DH6OPT_RELAY_MSG 9 +/* #define DH6OPT_SERVER_MSG 10: deprecated */ +#define DH6OPT_AUTH 11 +# define DH6OPT_AUTH_PROTO_DELAYED 2 +# define DH6OPT_AUTH_RRECONFIGURE 3 +# define DH6OPT_AUTH_ALG_HMACMD5 1 +#define DH6OPT_UNICAST 12 +#define DH6OPT_STATUS_CODE 13 +# define DH6OPT_STCODE_SUCCESS 0 +# define DH6OPT_STCODE_UNSPECFAIL 1 +# define DH6OPT_STCODE_NOADDRSAVAIL 2 +# define DH6OPT_STCODE_NOBINDING 3 +# define DH6OPT_STCODE_NOTONLINK 4 +# define DH6OPT_STCODE_USEMULTICAST 5 +# define DH6OPT_STCODE_NOPREFIXAVAIL 6 + +#define DH6OPT_RAPID_COMMIT 14 +#define DH6OPT_USER_CLASS 15 +#define DH6OPT_VENDOR_CLASS 16 +#define DH6OPT_VENDOR_OPTS 17 +#define DH6OPT_INTERFACE_ID 18 +#define DH6OPT_RECONF_MSG 19 + +#define DH6OPT_SIP_SERVER_D 21 +#define DH6OPT_SIP_SERVER_A 22 +#define DH6OPT_DNS 23 +#define DH6OPT_DNSNAME 24 +#define DH6OPT_IA_PD 25 +#define DH6OPT_IA_PD_PREFIX 26 +#define DH6OPT_NIS_SERVERS 27 +#define DH6OPT_NISP_SERVERS 28 +#define DH6OPT_NIS_DOMAIN_NAME 29 +#define DH6OPT_NISP_DOMAIN_NAME 30 +#define DH6OPT_NTP 31 +#define DH6OPT_REFRESHTIME 32 + #define DH6OPT_REFRESHTIME_UNDEF -1 +#define DH6OPT_BCMCS_SERVER_D 33 +#define DH6OPT_BCMCS_SERVER_A 34 +#define DH6OPT_GEOCONF_CIVIC 36 +#define DH6OPT_REMOTE_ID 37 +#define DH6OPT_SUBSCRIBER_ID 38 +#define DH6OPT_CLIENT_FQDN 39 + +/* The followings are KAME specific. */ + +struct dhcp6opt { + u_int16_t dh6opt_type; + u_int16_t dh6opt_len; + /* type-dependent data follows */ +} __attribute__ ((__packed__)); + +/* DUID type 1 */ +struct dhcp6opt_duid_type1 { + u_int16_t dh6_duid1_type; + u_int16_t dh6_duid1_hwtype; + u_int32_t dh6_duid1_time; + /* link-layer address follows */ +} __attribute__ ((__packed__)); + +/* Status Code */ +struct dhcp6opt_stcode { + u_int16_t dh6_stcode_type; + u_int16_t dh6_stcode_len; + u_int16_t dh6_stcode_code; +} __attribute__ ((__packed__)); + +/* + * General format of Identity Association. + * This format applies to Prefix Delegation (IA_PD) and Non-temporary Addresses + * (IA_NA) + */ +struct dhcp6opt_ia { + u_int16_t dh6_ia_type; + u_int16_t dh6_ia_len; + u_int32_t dh6_ia_iaid; + u_int32_t dh6_ia_t1; + u_int32_t dh6_ia_t2; + /* sub options follow */ +} __attribute__ ((__packed__)); + +/* IA Addr */ +struct dhcp6opt_ia_addr { + u_int16_t dh6_ia_addr_type; + u_int16_t dh6_ia_addr_len; + struct in6_addr dh6_ia_addr_addr; + u_int32_t dh6_ia_addr_preferred_time; + u_int32_t dh6_ia_addr_valid_time; +} __attribute__ ((__packed__)); + +/* IA_PD Prefix */ +struct dhcp6opt_ia_pd_prefix { + u_int16_t dh6_iapd_prefix_type; + u_int16_t dh6_iapd_prefix_len; + u_int32_t dh6_iapd_prefix_preferred_time; + u_int32_t dh6_iapd_prefix_valid_time; + u_int8_t dh6_iapd_prefix_prefix_len; + struct in6_addr dh6_iapd_prefix_prefix_addr; +} __attribute__ ((__packed__)); + +/* Authentication */ +struct dhcp6opt_auth { + u_int16_t dh6_auth_type; + u_int16_t dh6_auth_len; + u_int8_t dh6_auth_proto; + u_int8_t dh6_auth_alg; + u_int8_t dh6_auth_rdm; + u_int8_t dh6_auth_rdinfo[8]; + /* authentication information follows */ +} __attribute__ ((__packed__)); + +enum { DHCP6_AUTHPROTO_UNDEF = -1, DHCP6_AUTHPROTO_DELAYED = 2, + DHCP6_AUTHPROTO_RECONFIG = 3 }; +enum { DHCP6_AUTHALG_UNDEF = -1, DHCP6_AUTHALG_HMACMD5 = 1 }; +enum { DHCP6_AUTHRDM_UNDEF = -1, DHCP6_AUTHRDM_MONOCOUNTER = 0 }; + +#endif /*__DHCP6_H_DEFINED*/ diff --git a/dhcp6_ctl.c b/dhcp6_ctl.c new file mode 100644 index 0000000..ab1f18d --- /dev/null +++ b/dhcp6_ctl.c @@ -0,0 +1,368 @@ +/* $KAME: dhcp6_ctl.c,v 1.4 2004/09/07 05:03:03 jinmei Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +TAILQ_HEAD(dhcp6_commandqueue, dhcp6_commandctx); + +static struct dhcp6_commandqueue commandqueue_head; +static int max_commands; +static int commands = 0; + +struct dhcp6_commandctx { + TAILQ_ENTRY(dhcp6_commandctx) link; + + int s; /* communication socket */ + char inputbuf[1024]; /* input buffer */ + ssize_t input_len; + ssize_t input_filled; + int (*callback) __P((char *, ssize_t)); +}; + +int +dhcp6_ctl_init(addr, port, max, sockp) + char *addr, *port; + int max, *sockp; +{ + struct addrinfo hints, *res = NULL; + int on; + int error; + int ctlsock = -1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(addr, port, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + return (-1); + } + ctlsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (ctlsock < 0) { + dprintf(LOG_ERR, FNAME, "socket(control sock): %s", + strerror(errno)); + goto fail; + } + on = 1; + if (setsockopt(ctlsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) + < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(control sock, SO_REUSEADDR: %s", + strerror(errno)); + goto fail; + } + if (bind(ctlsock, res->ai_addr, res->ai_addrlen) < 0) { + dprintf(LOG_ERR, FNAME, "bind(control sock): %s", + strerror(errno)); + goto fail; + } + freeaddrinfo(res); + if (listen(ctlsock, 1)) { + dprintf(LOG_ERR, FNAME, "listen(control sock): %s", + strerror(errno)); + goto fail; + } + + TAILQ_INIT(&commandqueue_head); + + if (max <= 0) { + dprintf(LOG_ERR, FNAME, + "invalid maximum number of commands (%d)", max_commands); + goto fail; + } + max_commands = max; + + *sockp = ctlsock; + return (0); + + fail: + if (res != NULL) + freeaddrinfo(res); + if (ctlsock >= 0) + close(ctlsock); + + return (-1); +} + +int +dhcp6_ctl_authinit(keyfile, keyinfop, digestlenp) + char *keyfile; + struct keyinfo **keyinfop; + int *digestlenp; +{ + FILE *fp = NULL; + struct keyinfo *ctlkey = NULL; + char line[1024], secret[1024]; + int secretlen; + + /* Currently, we only support HMAC-MD5 for authentication. */ + *digestlenp = MD5_DIGESTLENGTH; + + if ((fp = fopen(keyfile, "r")) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to open %s: %s", keyfile, + strerror(errno)); + return (-1); + } + if (fgets(line, sizeof(line), fp) == NULL && ferror(fp)) { + dprintf(LOG_ERR, FNAME, "failed to read key file: %s", + strerror(errno)); + goto fail; + } + if ((secretlen = base64_decodestring(line, secret, sizeof(secret))) + < 0) { + dprintf(LOG_ERR, FNAME, "failed to decode base64 string"); + goto fail; + } + if ((ctlkey = malloc(sizeof(*ctlkey))) == NULL) { + dprintf(LOG_WARNING, FNAME, "failed to allocate control key"); + goto fail; + } + memset(ctlkey, 0, sizeof(*ctlkey)); + if ((ctlkey->secret = malloc(secretlen)) == NULL) { + dprintf(LOG_WARNING, FNAME, "failed to allocate secret key"); + goto fail; + } + ctlkey->secretlen = (size_t)secretlen; + memcpy(ctlkey->secret, secret, secretlen); + + fclose(fp); + + *keyinfop = ctlkey; + return (0); + + fail: + if (fp != NULL) + fclose(fp); + if (ctlkey != NULL && ctlkey->secret != NULL) + free(ctlkey->secret); + if (ctlkey != NULL) + free(ctlkey); + + return (-1); +} + +int +dhcp6_ctl_acceptcommand(sl, callback) + int sl; + int (*callback) __P((char *, ssize_t)); +{ + int s; + struct sockaddr_storage from_ss; + struct sockaddr *from = (struct sockaddr *)&from_ss; + socklen_t fromlen; + struct dhcp6_commandctx *ctx, *new; + + fromlen = sizeof(from_ss); + if ((s = accept(sl, from, &fromlen)) < 0) { + dprintf(LOG_WARNING, FNAME, + "failed to accept control connection: %s", + strerror(errno)); + return (-1); + } + + dprintf(LOG_DEBUG, FNAME, "accept control connection from %s", + addr2str(from)); + + if (max_commands <= 0) { + dprintf(LOG_ERR, FNAME, "command queue is not initialized"); + close(s); + return (-1); + } + + new = malloc(sizeof(*new)); + if (new == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate new command context"); + goto fail; + } + + /* if the command queue is full, purge the oldest one */ + if (commands == max_commands) { + ctx = TAILQ_FIRST(&commandqueue_head); + + dprintf(LOG_INFO, FNAME, "command queue is full. " + "drop the oldest one (fd=%d)", ctx->s); + + TAILQ_REMOVE(&commandqueue_head, ctx, link); + dhcp6_ctl_closecommand(ctx); + } + + /* insert the next context to the queue */ + memset(new, 0, sizeof(*new)); + new->s = s; + new->callback = callback; + new->input_len = sizeof(struct dhcp6ctl); + TAILQ_INSERT_TAIL(&commandqueue_head, new, link); + commands++; + + return (0); + + fail: + close(s); + + return (-1); +} + +void +dhcp6_ctl_closecommand(ctx) + struct dhcp6_commandctx *ctx; +{ + close(ctx->s); + free(ctx); + + if (commands == 0) { + dprintf(LOG_ERR, FNAME, "assumption error: " + "command queue is empty?"); + exit(1); /* XXX */ + } + commands--; + + return; +} + +int +dhcp6_ctl_readcommand(read_fds) + fd_set *read_fds; +{ + struct dhcp6_commandctx *ctx, *ctx_next; + char *cp; + int cc, resid, result; + struct dhcp6ctl *ctlhead; + + for (ctx = TAILQ_FIRST(&commandqueue_head); ctx != NULL; + ctx = ctx_next) { + ctx_next = TAILQ_NEXT(ctx, link); + + if (FD_ISSET(ctx->s, read_fds)) { + cp = ctx->inputbuf + ctx->input_filled; + resid = ctx->input_len - ctx->input_filled; + + cc = read(ctx->s, cp, resid); + if (cc < 0) { + dprintf(LOG_WARNING, FNAME, "read failed: %s", + strerror(errno)); + goto closecommand; + } + if (cc == 0) { + dprintf(LOG_INFO, FNAME, + "control channel was reset by peer"); + goto closecommand; + } + + ctx->input_filled += cc; + if (ctx->input_filled < ctx->input_len) + continue; /* we need more data */ + else if (ctx->input_filled == sizeof(*ctlhead)) { + ctlhead = (struct dhcp6ctl *)ctx->inputbuf; + ctx->input_len += ntohs(ctlhead->len); + } + + if (ctx->input_filled == ctx->input_len) { + /* we're done. execute the command. */ + result = (ctx->callback)(ctx->inputbuf, + ctx->input_len); + + switch (result) { + case DHCP6CTL_R_DONE: + case DHCP6CTL_R_FAILURE: + goto closecommand; + default: + break; + } + } else if (ctx->input_len > sizeof(ctx->inputbuf)) { + dprintf(LOG_INFO, FNAME, + "too large command (%d bytes)", + ctx->input_len); + goto closecommand; + } + + continue; + + closecommand: + TAILQ_REMOVE(&commandqueue_head, ctx, link); + dhcp6_ctl_closecommand(ctx); + } + } + + return (0); +} + +int +dhcp6_ctl_setreadfds(read_fds, maxfdp) + fd_set *read_fds; + int *maxfdp; +{ + int maxfd = *maxfdp; + struct dhcp6_commandctx *ctx; + + for (ctx = TAILQ_FIRST(&commandqueue_head); ctx != NULL; + ctx = TAILQ_NEXT(ctx, link)) { + FD_SET(ctx->s, read_fds); + if (ctx->s > maxfd) + maxfd = ctx->s; + } + + *maxfdp = maxfd; + + return (0); +} diff --git a/dhcp6_ctl.h b/dhcp6_ctl.h new file mode 100644 index 0000000..064b38d --- /dev/null +++ b/dhcp6_ctl.h @@ -0,0 +1,45 @@ +/* $KAME: dhcp6_ctl.h,v 1.2 2004/09/07 05:03:03 jinmei Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define DHCP6CTL_DEF_COMMANDQUEUELEN 5 + +#define DHCP6CTL_R_FAILURE -1 +#define DHCP6CTL_R_DONE 0 +#define DHCP6CTL_R_CONT 1 + +struct dhcp6_commandctx; /* opaque type */ + +extern int dhcp6_ctl_init __P((char *, char *, int, int *)); +extern int dhcp6_ctl_authinit __P((char *, struct keyinfo **, int *)); +extern int dhcp6_ctl_acceptcommand __P((int, int (*)__P((char *, ssize_t)))); +extern void dhcp6_ctl_closecommand __P((struct dhcp6_commandctx *)); +extern int dhcp6_ctl_readcommand __P((fd_set *)); +extern int dhcp6_ctl_setreadfds __P((fd_set *, int *)); diff --git a/dhcp6_ctlclient.c b/dhcp6_ctlclient.c new file mode 100644 index 0000000..5597c9e --- /dev/null +++ b/dhcp6_ctlclient.c @@ -0,0 +1,702 @@ +/* $KAME: dhcp6_ctlclient.c,v 1.5 2005/01/12 06:06:11 suz Exp $ */ + +/* + * Copyright (C) 2004 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MD5_DIGESTLENGTH 16 +#define DEFAULT_SERVER_KEYFILE SYSCONFDIR "/dhcp6sctlkey" +#define DEFAULT_CLIENT_KEYFILE SYSCONFDIR "/dhcp6cctlkey" + +static char *ctladdr; +static char *ctlport; + +static enum { CTLCLIENT, CTLSERVER } ctltype = CTLCLIENT; + +static inline int put16 __P((char **, int *, u_int16_t)); +static inline int put32 __P((char **, int *, u_int32_t)); +static inline int putval __P((char **, int *, void *, size_t)); + +static int setup_auth __P((char *, struct keyinfo *, int *)); +static int make_command __P((int, char **, char **, size_t *, + struct keyinfo *, int)); +static int make_remove_command __P((int, char **, char **, int *)); +static int make_start_command __P((int, char **, char **, int *)); +static int make_stop_command __P((int, char **, char **, int *)); +static int make_binding_object __P((int, char **, char **, int *)); +static int make_interface_object __P((int, char **, char **, int *)); +static int make_ia_object __P((int, char **, char **, int *)); +static int parse_duid __P((char *, int *, char **, int *)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int cc, ch, s, error, passed; + int Cflag = 0, Sflag = 0; + char *cbuf; + size_t clen; + struct addrinfo hints, *res0, *res; + int digestlen; + char *keyfile = NULL; + struct keyinfo key; + + while ((ch = getopt(argc, argv, "CSa:k:p:")) != -1) { + switch (ch) { + case 'C': + if (Sflag) + errx(1, "-C and -S are exclusive"); + Cflag = 1; + ctltype = CTLCLIENT; + break; + case 'S': + if (Cflag) + errx(1, "-C and -S are exclusive"); + Sflag = 1; + ctltype = CTLSERVER; + break; + case 'a': + ctladdr = optarg; + break; + case 'k': + keyfile = optarg; + break; + case 'p': + ctlport = optarg; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + switch (ctltype) { + case CTLCLIENT: + if (ctladdr == NULL) + ctladdr = DEFAULT_CLIENT_CONTROL_ADDR; + if (ctlport == NULL) + ctlport = DEFAULT_CLIENT_CONTROL_PORT; + if (keyfile == NULL) + keyfile = DEFAULT_CLIENT_KEYFILE; + break; + case CTLSERVER: + if (ctladdr == NULL) + ctladdr = DEFAULT_SERVER_CONTROL_ADDR; + if (ctlport == NULL) + ctlport = DEFAULT_SERVER_CONTROL_PORT; + if (keyfile == NULL) + keyfile = DEFAULT_SERVER_KEYFILE; + break; + } + + memset(&key, 0, sizeof(key)); + digestlen = 0; + if (setup_auth(keyfile, &key, &digestlen) != 0) + errx(1, "failed to setup message authentication"); + + if ((passed = make_command(argc, argv, &cbuf, &clen, + &key, digestlen)) < 0) { + errx(1, "failed to make command buffer"); + } + argc -= passed; + argv += passed; + if (argc != 0) + warnx("redundant command argument after \"%s\"", argv[0]); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(ctladdr, ctlport, &hints, &res0); + if (error != 0) + errx(1, "getaddrinfo failed: %s", gai_strerror(error)); + + s = -1; + for (res = res0; res != NULL; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (s < 0) { + warn("socket"); + continue; + } + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + warn("connect"); + s = -1; + continue; + } + break; + } + freeaddrinfo(res0); + if (s < 0) { + warnx("failed to connect to the %s", + ctltype == CTLCLIENT ? "client" : "server"); + exit(1); + } + + cc = write(s, cbuf, clen); + if (cc < 0) + err(1, "write command"); + if (cc != clen) + errx(1, "failed to send complete command"); + + close(s); + free(cbuf); + + exit(0); +} + +static int +setup_auth(keyfile, key, digestlenp) + char *keyfile; + struct keyinfo *key; + int *digestlenp; +{ + FILE *fp = NULL; + char line[1024], secret[1024]; + int secretlen; + + key->secret = NULL; + + /* Currently, we only support HMAC-MD5 for authentication. */ + *digestlenp = MD5_DIGESTLENGTH; + + if ((fp = fopen(keyfile, "r")) == NULL) { + warn("fopen: %s", keyfile); + return (-1); + } + if (fgets(line, sizeof(line), fp) == NULL && ferror(fp)) { + warn("fgets failed"); + goto fail; + } + if ((secretlen = base64_decodestring(line, secret, sizeof(secret))) + < 0) { + warnx("failed to decode base64 string"); + goto fail; + } + if ((key->secret = malloc(secretlen)) == NULL) { + warn("setup_auth: malloc failed"); + goto fail; + } + key->secretlen = (size_t)secretlen; + memcpy(key->secret, secret, secretlen); + + fclose(fp); + + return (0); + + fail: + if (fp != NULL) + fclose(fp); + if (key->secret != NULL) + free(key->secret); + return (-1); +} + +static inline int +put16(bpp, lenp, val) + char **bpp; + int *lenp; + u_int16_t val; +{ + char *bp = *bpp; + int len = *lenp; + + if (len < sizeof(val)) + return (-1); + + val = htons(val); + memcpy(bp, &val, sizeof(val)); + bp += sizeof(val); + len -= sizeof(val); + + *bpp = bp; + *lenp = len; + + return (0); +} + +static inline int +put32(bpp, lenp, val) + char **bpp; + int *lenp; + u_int32_t val; +{ + char *bp = *bpp; + int len = *lenp; + + if (len < sizeof(val)) + return (-1); + + val = htonl(val); + memcpy(bp, &val, sizeof(val)); + bp += sizeof(val); + len -= sizeof(val); + + *bpp = bp; + *lenp = len; + + return (0); +} + +static inline int +putval(bpp, lenp, val, valsize) + char **bpp; + int *lenp; + void *val; + size_t valsize; +{ + char *bp = *bpp; + int len = *lenp; + + if (len < valsize) + return (-1); + + memcpy(bp, val, valsize); + bp += valsize; + len -= valsize; + + *bpp = bp; + *lenp = len; + + return (0); +} + +static int +make_command(argc, argv, bufp, lenp, key, authlen) + int argc; + char **argv, **bufp; + size_t *lenp; + struct keyinfo *key; + int authlen; +{ + struct dhcp6ctl ctl; + char commandbuf[4096]; /* XXX: ad-hoc value */ + char *bp, *buf, *mac; + int buflen, len; + int argc_passed = 0, passed; + time_t now; + + if (argc == 0) { + warnx("command is too short"); + return (-1); + } + + bp = commandbuf + sizeof(ctl) + authlen; + if (bp >= commandbuf + sizeof(commandbuf)) { + warnx("make_command: local buffer is too short"); + return (-1); + } + buflen = sizeof(commandbuf) - sizeof(ctl); + + memset(&ctl, 0, sizeof(ctl)); + ctl.version = htons(DHCP6CTL_VERSION); + + if (strcmp(argv[0], "reload") == 0) + ctl.command = htons(DHCP6CTL_COMMAND_RELOAD); + else if (strcmp(argv[0], "remove") == 0) { + if (ctltype != CTLSERVER) { + warnx("remove command is only for server"); + return (-1); + } + if ((passed = make_remove_command(argc - 1, argv + 1, + &bp, &buflen)) < 0) { + return (-1); + } + argc_passed += passed; + ctl.command = htons(DHCP6CTL_COMMAND_REMOVE); + } else if (strcmp(argv[0], "start") == 0) { + if ((passed = make_start_command(argc - 1, argv + 1, + &bp, &buflen)) < 0) { + return (-1); + } + argc_passed += passed; + ctl.command = htons(DHCP6CTL_COMMAND_START); + } else if (strcmp(argv[0], "stop") == 0) { + if ((passed = make_stop_command(argc - 1, argv + 1, + &bp, &buflen)) < 0) { + return (-1); + } + argc_passed += passed; + ctl.command = htons(DHCP6CTL_COMMAND_STOP); + } else { + warnx("unknown command: %s", argv[0]); + return (-1); + } + + len = bp - commandbuf; + ctl.len = htons(len - sizeof(ctl)); + + if ((now = time(NULL)) < 0) { + warn("failed to get current time"); + return (-1); + } + ctl.timestamp = htonl((u_int32_t)now); + + memcpy(commandbuf, &ctl, sizeof(ctl)); + + mac = commandbuf + sizeof(ctl); + memset(mac, 0, authlen); + if (dhcp6_calc_mac(commandbuf, len, DHCP6CTL_AUTHPROTO_UNDEF, + DHCP6CTL_AUTHALG_HMACMD5, sizeof(ctl), key) != 0) { + warnx("failed to calculate MAC"); + return (-1); + } + + if ((buf = malloc(len)) == NULL) { + warn("memory allocation failed"); + return (-1); + } + memcpy(buf, commandbuf, len); + + *lenp = len; + *bufp = buf; + + argc_passed++; + + return (argc_passed); +} + +static int +make_remove_command(argc, argv, bpp, lenp) + int argc, *lenp; + char **argv, **bpp; +{ + int argc_passed = 0, passed; + + if (argc == 0) { + warnx("make_remove_command: command is too short"); + return (-1); + } + + if (strcmp(argv[0], "binding") == 0) { + if (put32(bpp, lenp, DHCP6CTL_BINDING)) + goto fail; + if ((passed = make_binding_object(argc - 1, argv + 1, + bpp, lenp)) < 0) { + return (-1); + } + argc_passed += passed; + } else { + warnx("remove target not supported: %s", argv[0]); + return (-1); + } + + argc_passed++; + return (argc_passed); + + fail: + warnx("make_remove_command failed"); + return (-1); +} + +static int +make_start_command(argc, argv, bpp, lenp) + int argc, *lenp; + char **argv, **bpp; +{ + int argc_passed = 0, passed; + + if (argc == 0) { + warnx("make_remove_command: command is too short"); + return (-1); + } + + if (ctltype != CTLCLIENT) { + warnx("client-only command is specified for a server"); + return (-1); + } + + if (strcmp(argv[0], "interface") == 0) { + if (put32(bpp, lenp, DHCP6CTL_INTERFACE)) + goto fail; + if ((passed = make_interface_object(argc - 1, argv + 1, + bpp, lenp)) < 0) { + return (-1); + } + argc_passed += passed; + } else { + warnx("start target not supported: %s", argv[0]); + return (-1); + } + + argc_passed++; + return (argc_passed); + + fail: + warnx("make_start_command failed"); + return (-1); +} + +static int +make_stop_command(argc, argv, bpp, lenp) + int argc, *lenp; + char **argv, **bpp; +{ + int argc_passed = 0, passed; + + if (argc == 0) + return (0); + + if (ctltype != CTLCLIENT) { + warnx("client-only command is specified for a server"); + return (-1); + } + + if (strcmp(argv[0], "interface") == 0) { + if (put32(bpp, lenp, DHCP6CTL_INTERFACE)) + goto fail; + if ((passed = make_interface_object(argc - 1, argv + 1, + bpp, lenp)) < 0) { + return (-1); + } + argc_passed += passed; + } else { + warnx("stop target not supported: %s", argv[0]); + return (-1); + } + + argc_passed++; + return (argc_passed); + + fail: + warnx("make_stop_command failed"); + return (-1); +} + +static int +make_interface_object(argc, argv, bpp, lenp) + int argc, *lenp; + char **argv, **bpp; +{ + int iflen; + int argc_passed = 0; + + if (argc == 0) { + warnx("make_interface_object: interface not specified"); + return (-1); + } + argc_passed++; + + iflen = strlen(argv[0]) + 1; + if (put32(bpp, lenp, (u_int32_t)iflen)) + goto fail; + if (putval(bpp, lenp, argv[0], strlen(argv[0]) + 1)) + goto fail; + + return (argc_passed); + + fail: + warnx("make_interface_object: failed"); + return (-1); +} + +static int +make_binding_object(argc, argv, bpp, lenp) + int argc, *lenp; + char **argv, **bpp; +{ + int argc_passed = 0, passed; + + if (argc == 0) { + /* or allow this as "all"? */ + warnx("make_binding_object: command is too short"); + return (-1); + } + + if (strcmp(argv[0], "IA") == 0) { + if (put32(bpp, lenp, DHCP6CTL_BINDING_IA)) + goto fail; + if ((passed = make_ia_object(argc - 1, argv + 1, + bpp, lenp)) < 0) { + return (-1); + } + argc_passed += passed; + } else { + warn("unknown binding type: %s", argv[0]); + return (-1); + } + + argc_passed++; + return (argc_passed); + + fail: + warnx("make_binding_object: failed"); + return (-1); +} + +static int +make_ia_object(argc, argv, bpp, lenp) + int argc, *lenp; + char **argv, **bpp; +{ + struct dhcp6ctl_iaspec iaspec; + int duidlen, dummylen = 0; + int argc_passed = 0; + char *dummy = NULL; + + if (argc < 3) { + /* + * Right now, we require all three parameters of + * . This should be more flexible in + * the future. + */ + warnx("command is too short for an IA spec"); + return (-1); + } + argc_passed += 3; + + memset(&iaspec, 0, sizeof(iaspec)); + + if (strcmp(argv[0], "IA_PD") == 0) + iaspec.type = htonl(DHCP6CTL_IA_PD); + else if (strcmp(argv[0], "IA_NA") == 0) + iaspec.type = htonl(DHCP6CTL_IA_NA); + else { + warnx("IA type not supported: %s", argv[0]); + return (-1); + } + + iaspec.id = htonl((u_int32_t)strtol(argv[1], NULL, 10)); + + if (parse_duid(argv[2], &duidlen, &dummy, &dummylen)) + goto fail; + iaspec.duidlen = htonl(duidlen); + + if (putval(bpp, lenp, &iaspec, sizeof(iaspec))) + goto fail; + + if (parse_duid(argv[2], &duidlen, bpp, lenp)) + goto fail; + + return (argc_passed); + + fail: + warnx("make_ia_object: failed"); + return (-1); +} + +static int +parse_duid(str, lenp, bufp, buflenp) + char *str; + int *lenp; + char **bufp; + int *buflenp; +{ + char *buf = *bufp; + char *cp, *bp; + int duidlen, slen, buflen; + unsigned int x; + + /* calculate DUID len */ + slen = strlen(str); + if (slen < 2) + goto bad; + duidlen = 1; + slen -= 2; + if ((slen % 3) != 0) + goto bad; + duidlen += (slen / 3); + if (duidlen > 128) { + warn("too long DUID (%d bytes)", duidlen); + return (-1); + } + + *lenp = duidlen; + if (buf == NULL) + return (0); + + buflen = *buflenp; + if (buflen < duidlen) + goto bad; + + for (cp = str, bp = buf; *cp != '\0';) { + if (bp - buf >= buflen) + goto bad; + + if (sscanf(cp, "%02x", &x) != 1) + goto bad; + *bp++ = x; + cp += 2; + + switch (*cp) { + case ':': + cp++; + break; + case '\0': + goto done; + default: + goto bad; + } + } + done: + *bufp = bp; + return (0); + + bad: + return (-1); +} + +static void +usage() +{ + fprintf(stderr, "usage: dhcp6ctl [-C|-S] [-a ctladdr] [-k keyfile] " + "[-p ctlport] command...\n"); + + exit(1); +} diff --git a/dhcp6c.8 b/dhcp6c.8 new file mode 100644 index 0000000..6824ae0 --- /dev/null +++ b/dhcp6c.8 @@ -0,0 +1,208 @@ +.\" $KAME: dhcp6c.8,v 1.36 2005/03/17 05:27:01 suz Exp $ +.\" +.\" Copyright (C) 1998 and 1999 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd Sep 12, 1999 +.Dt DHCP6C 8 +.Os KAME +.Sh NAME +.Nm dhcp6c +.Nd DHCPv6 client daemon +.\" +.Sh SYNOPSIS +.Nm +.Op Fl c Ar configfile +.Op Fl Ddfi +.Op Fl p Ar pid-file +.Ar interface +.Op Ar interfaces... +.\" +.Sh DESCRIPTION +.Nm +works as a DHCPv6 client and gets information from DHCPv6 servers +to configure the specified +.Ar interface . +Multiple +.Ar interfaces +can be specified separated by spaces or tabs, +in which case +.Nm +will work on all the interfaces simultaneously. +.Pp +The command line options are: +.Bl -tag -width indent +.\" +.It Fl c Ar configfile +Use +.Ar configfile +as the configuration file. +.It Fl d +Print debugging messages. +.It Fl D +Even more debugging information is printed. +.It Fl f +Foreground mode (useful when debugging). +Although +.Nm +usually prints warning, debugging, or error messages to +.Xr syslog 8 , +it prints the messages to standard error if this option is +specified. +.It Fl i +Info-req mode. In this mode, stateless DHCPv6 is executed with the folllowing configuration, and the obtained info is written to stdout. After this output, +.Nm +is terminated. (suits for a use in shell-script etc). +.Bd -literal -offset + interface (interface given in the argument) { + information-only; + script (a script which displays the received information to stdout); + }; +.Ed +.Pp +Since the configuration is internally generated, you cannot provide a configuration in this mode. If you want to have different actions for the stateless DHCPv6 information, you should write an appropriate configuration and invoke +.Nm +without this option. +.It Fl p Ar pid-file +Use +.Ar pid-file +to dump the process ID of +.Nm . +.El +.Pp +The program will daemonize itself on invocation unless the +.Fl f +or +.Fl i +option is specified. +.Pp +Upon receipt of the +.Dv SIGHUP +or +.Dv SIGTERM +signals, +.Nm +will remove all stateful resources from the system. +In the former case the daemon will then reinvoke itself, +while it will stop running in the latter case. +In either case, +.Nm +will send DHCPv6 Release messages to release resources assigned from servers. +.\" +.Sh FILES +.Bl -tag -width /usr/local/etc/dhcp6c.conf -compact +.It Pa /var/run/dhcp6c.pid +is the default file that contains pid of the currently running +.Nm dhcp6c . +.It Pa /usr/local/etc/dhcp6c.conf +is the default configuration file. +.It Pa /var/db/dhcp6c_duid +is the file to store the client's DUID. +.El +.Sh Configuration Script +When +.Nm +receives a reply message, +it will invoke a supplementary configuration script specified in the +.Xr dhcp6c.conf 5 +file. +The daemon will provide the script with configuration parameters as +environment variables, +which include: +.Pp +.Bl -tag -width Ds -compact +.It Ic REASON +The reason why the script is invoked. +As of this writing, +the value is always +\(dqNBI\(dq +and thus meaningless. +.It Ic new_domain_name_servers +A list of available DNS servers, +each of which is an IPv6 numeric address and is separated by a white +space character. +.It Ic new_domain_name +A list of DNS names, +which provides DNS name search path. +.It Ic new_ntp_servers +A list of available NTP servers, +each of which is an IPv6 numeric address and is separated by a white +space character. +.It Ic new_sip_servers +A list of available SIP server addresses, +each of which is an IPv6 numeric address and is separated by a white +space character. +.It Ic new_sip_name +A list of SIP server domain names. +.It Ic new_nis_servers +A list of available NIS server addresses, +each of which is an IPv6 numeric address and is separated by a white +space character. +.It Ic new_nis_name +A list of NIS domain names. +.It Ic new_nisp_servers +A list of available NIS+ server addresses, +each of which is an IPv6 numeric address and is separated by a white +space character. +.It Ic new_nisp_name +A list of NIS+ domain names. +.It Ic new_bcmcs_servers +A list of available BCMCS server addresses, +each of which is an IPv6 numeric address and is separated by a white +space character. +.It Ic new_bcmcs_name +A list of BCMCS server domain names. +.El +.Pp +Note that the daemon does not always provide all the parameters. +It sets an environment variable only when the corresponding +configuration parameter is provided by the DHCPv6 server. +.\" +.Sh SEE ALSO +.Xr daemon 3 , +.Xr dhcp6c.conf 5 , +.Xr dhcp6s 8 +.Rs +.%A Ralph Droms, Editor +.%D 2003 +.%T Dynamic Host Configuration Protocol for IPv6 (DHCPv6) +.%R RFC 3315 +.Re +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE/KAME IPv6 protocol stack kit. +.\" +.Sh BUGS +.Nm +is incomplete and violates DHCPv6 protocol spec, in several aspects. +In particular, temporary address assignment is intentionally omitted. +.Pp +Information Refresh Time Option is not recognied in Info-req mode, since +.Nm +terminates after it receives a REPLY message. diff --git a/dhcp6c.c b/dhcp6c.c new file mode 100644 index 0000000..1caaaa5 --- /dev/null +++ b/dhcp6c.c @@ -0,0 +1,2148 @@ +/* $KAME: dhcp6c.c,v 1.164 2006/01/10 02:46:09 jinmei Exp $ */ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include +#ifdef __FreeBSD__ +#include +#endif + +#include +#ifdef __KAME__ +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug = 0; +static int exit_ok = 0; +static sig_atomic_t sig_flags = 0; +#define SIGF_TERM 0x1 +#define SIGF_HUP 0x2 + +const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_CLIENT; + +int sock; /* inbound/outbound udp port */ +int rtsock; /* routing socket */ +int ctlsock = -1; /* control TCP port */ +char *ctladdr = DEFAULT_CLIENT_CONTROL_ADDR; +char *ctlport = DEFAULT_CLIENT_CONTROL_PORT; + +#define DEFAULT_KEYFILE SYSCONFDIR "/dhcp6cctlkey" +#define CTLSKEW 300 + +static char *conffile = DHCP6C_CONF; + +static const struct sockaddr_in6 *sa6_allagent; +static struct duid client_duid; +static char *pid_file = DHCP6C_PIDFILE; + +static char *ctlkeyfile = DEFAULT_KEYFILE; +static struct keyinfo *ctlkey = NULL; +static int ctldigestlen; + +static int infreq_mode = 0; + +static inline int get_val32 __P((char **, int *, u_int32_t *)); +static inline int get_ifname __P((char **, int *, char *, int)); + +static void usage __P((void)); +static void client6_init __P((void)); +static void client6_startall __P((int)); +static void free_resources __P((struct dhcp6_if *)); +static void client6_mainloop __P((void)); +static int client6_do_ctlcommand __P((char *, ssize_t)); +static void client6_reload __P((void)); +static int client6_ifctl __P((char *ifname, u_int16_t)); +static void check_exit __P((void)); +static void process_signals __P((void)); +static struct dhcp6_serverinfo *find_server __P((struct dhcp6_event *, + struct duid *)); +static struct dhcp6_serverinfo *select_server __P((struct dhcp6_event *)); +static void client6_recv __P((void)); +static int client6_recvadvert __P((struct dhcp6_if *, struct dhcp6 *, + ssize_t, struct dhcp6_optinfo *)); +static int client6_recvreply __P((struct dhcp6_if *, struct dhcp6 *, + ssize_t, struct dhcp6_optinfo *)); +static void client6_signal __P((int)); +static struct dhcp6_event *find_event_withid __P((struct dhcp6_if *, + u_int32_t)); +static int construct_confdata __P((struct dhcp6_if *, struct dhcp6_event *)); +static int construct_reqdata __P((struct dhcp6_if *, struct dhcp6_optinfo *, + struct dhcp6_event *)); +static void destruct_iadata __P((struct dhcp6_eventdata *)); +static void tv_sub __P((struct timeval *, struct timeval *, struct timeval *)); +static struct dhcp6_timer *client6_expire_refreshtime __P((void *)); +static int process_auth __P((struct authparam *, struct dhcp6 *dh6, ssize_t, + struct dhcp6_optinfo *)); +static int set_auth __P((struct dhcp6_event *, struct dhcp6_optinfo *)); + +struct dhcp6_timer *client6_timo __P((void *)); +int client6_start __P((struct dhcp6_if *)); +static void info_printf __P((const char *, ...)); + +extern int client6_script __P((char *, int, struct dhcp6_optinfo *)); + +#define MAX_ELAPSED_TIME 0xffff + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch, pid; + char *progname; + FILE *pidfp; + struct dhcp6_if *ifp; + +#ifndef HAVE_ARC4RANDOM + srandom(time(NULL) & getpid()); +#endif + + if ((progname = strrchr(*argv, '/')) == NULL) + progname = *argv; + else + progname++; + + while ((ch = getopt(argc, argv, "c:dDfik:p:")) != -1) { + switch (ch) { + case 'c': + conffile = optarg; + break; + case 'd': + debug = 1; + break; + case 'D': + debug = 2; + break; + case 'f': + foreground++; + break; + case 'i': + infreq_mode = 1; + break; + case 'k': + ctlkeyfile = optarg; + break; + case 'p': + pid_file = optarg; + break; + default: + usage(); + exit(0); + } + } + argc -= optind; + argv += optind; + + if (argc == 0) { + usage(); + exit(0); + } + + if (foreground == 0) + openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); + + setloglevel(debug); + + client6_init(); + while (argc-- > 0) { + if ((ifp = ifinit(argv[0])) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to initialize %s", + argv[0]); + exit(1); + } + argv++; + } + + if (infreq_mode == 0 && (cfparse(conffile)) != 0) { + dprintf(LOG_ERR, FNAME, "failed to parse configuration file"); + exit(1); + } + + if (foreground == 0 && infreq_mode == 0) { + if (daemon(0, 0) < 0) + err(1, "daemon"); + } + + /* dump current PID */ + pid = getpid(); + if ((pidfp = fopen(pid_file, "w")) != NULL) { + fprintf(pidfp, "%d\n", pid); + fclose(pidfp); + } + + client6_startall(0); + client6_mainloop(); + exit(0); +} + +static void +usage() +{ + + fprintf(stderr, "usage: dhcp6c [-c configfile] [-dDfi] " + "[-p pid-file] interface [interfaces...]\n"); +} + +/*------------------------------------------------------------*/ + +void +client6_init() +{ + struct addrinfo hints, *res; + static struct sockaddr_in6 sa6_allagent_storage; + int error, on = 1; + + /* get our DUID */ + if (get_duid(DUID_FILE, &client_duid)) { + dprintf(LOG_ERR, FNAME, "failed to get a DUID"); + exit(1); + } + + if (dhcp6_ctl_authinit(ctlkeyfile, &ctlkey, &ctldigestlen) != 0) { + dprintf(LOG_NOTICE, FNAME, + "failed initialize control message authentication"); + /* run the server anyway */ + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(NULL, DH6PORT_DOWNSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) { + dprintf(LOG_ERR, FNAME, "socket"); + exit(1); + } + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + &on, sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(SO_REUSEPORT): %s", strerror(errno)); + exit(1); + } +#ifdef IPV6_RECVPKTINFO + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(IPV6_RECVPKTINFO): %s", + strerror(errno)); + exit(1); + } +#else + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(IPV6_PKTINFO): %s", + strerror(errno)); + exit(1); + } +#endif + if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(sock, IPV6_MULTICAST_LOOP): %s", + strerror(errno)); + exit(1); + } +#ifdef IPV6_V6ONLY + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(IPV6_V6ONLY): %s", + strerror(errno)); + exit(1); + } +#endif + + /* + * According RFC3315 2.2, only the incoming port should be bound to UDP + * port 546. However, to have an interoperability with some servers, + * the outgoing port is also bound to the DH6PORT_DOWNSTREAM. + */ + if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { + dprintf(LOG_ERR, FNAME, "bind: %s", strerror(errno)); + exit(1); + } + freeaddrinfo(res); + + /* open a routing socket to watch the routing table */ + if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { + dprintf(LOG_ERR, FNAME, "open a routing socket: %s", + strerror(errno)); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + memcpy(&sa6_allagent_storage, res->ai_addr, res->ai_addrlen); + sa6_allagent = (const struct sockaddr_in6 *)&sa6_allagent_storage; + freeaddrinfo(res); + + /* set up control socket */ + if (ctlkey == NULL) + dprintf(LOG_NOTICE, FNAME, "skip opening control port"); + else if (dhcp6_ctl_init(ctladdr, ctlport, + DHCP6CTL_DEF_COMMANDQUEUELEN, &ctlsock)) { + dprintf(LOG_ERR, FNAME, + "failed to initialize control channel"); + exit(1); + } + + if (signal(SIGHUP, client6_signal) == SIG_ERR) { + dprintf(LOG_WARNING, FNAME, "failed to set signal: %s", + strerror(errno)); + exit(1); + } + if (signal(SIGTERM, client6_signal) == SIG_ERR) { + dprintf(LOG_WARNING, FNAME, "failed to set signal: %s", + strerror(errno)); + exit(1); + } +} + +int +client6_start(ifp) + struct dhcp6_if *ifp; +{ + struct dhcp6_event *ev; + + /* make sure that the interface does not have a timer */ + if (ifp->timer != NULL) { + dprintf(LOG_DEBUG, FNAME, + "removed existing timer on %s", ifp->ifname); + dhcp6_remove_timer(&ifp->timer); + } + + /* create an event for the initial delay */ + if ((ev = dhcp6_create_event(ifp, DHCP6S_INIT)) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to create an event"); + return (-1); + } + TAILQ_INSERT_TAIL(&ifp->event_list, ev, link); + + if ((ev->authparam = new_authparam(ifp->authproto, + ifp->authalgorithm, ifp->authrdm)) == NULL) { + dprintf(LOG_WARNING, FNAME, "failed to allocate " + "authentication parameters"); + dhcp6_remove_event(ev); + return (-1); + } + + if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to add a timer for %s", + ifp->ifname); + dhcp6_remove_event(ev); + return (-1); + } + dhcp6_reset_timer(ev); + + return (0); +} + +static void +client6_startall(isrestart) + int isrestart; +{ + struct dhcp6_if *ifp; + + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + if (isrestart &&ifreset(ifp)) { + dprintf(LOG_NOTICE, FNAME, "failed to reset %s", + ifp->ifname); + continue; /* XXX: try to recover? */ + } + if (client6_start(ifp)) + exit(1); /* initialization failure. we give up. */ + } +} + +static void +free_resources(freeifp) + struct dhcp6_if *freeifp; +{ + struct dhcp6_if *ifp; + + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + struct dhcp6_event *ev, *ev_next; + + if (freeifp != NULL && freeifp != ifp) + continue; + + /* release all IAs as well as send RELEASE message(s) */ + release_all_ia(ifp); + + /* + * Cancel all outstanding events for each interface except + * ones being released. + */ + for (ev = TAILQ_FIRST(&ifp->event_list); ev; ev = ev_next) { + ev_next = TAILQ_NEXT(ev, link); + + if (ev->state == DHCP6S_RELEASE) + continue; /* keep it for now */ + + dhcp6_remove_event(ev); + } + } +} + +static void +check_exit() +{ + struct dhcp6_if *ifp; + + if (!exit_ok) + return; + + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + /* + * Check if we have an outstanding event. If we do, we cannot + * exit for now. + */ + if (!TAILQ_EMPTY(&ifp->event_list)) + return; + } + + /* We have no existing event. Do exit. */ + dprintf(LOG_INFO, FNAME, "exiting"); + + exit(0); +} + +static void +process_signals() +{ + if ((sig_flags & SIGF_TERM)) { + exit_ok = 1; + free_resources(NULL); + unlink(pid_file); + check_exit(); + } + if ((sig_flags & SIGF_HUP)) { + dprintf(LOG_INFO, FNAME, "restarting"); + free_resources(NULL); + client6_startall(1); + } + + sig_flags = 0; +} + +static void +client6_mainloop() +{ + struct timeval *w; + int ret, maxsock; + fd_set r; + + while(1) { + if (sig_flags) + process_signals(); + + w = dhcp6_check_timer(); + + FD_ZERO(&r); + FD_SET(sock, &r); + maxsock = sock; + if (ctlsock >= 0) { + FD_SET(ctlsock, &r); + maxsock = (sock > ctlsock) ? sock : ctlsock; + (void)dhcp6_ctl_setreadfds(&r, &maxsock); + } + + ret = select(maxsock + 1, &r, NULL, NULL, w); + + switch (ret) { + case -1: + if (errno != EINTR) { + dprintf(LOG_ERR, FNAME, "select: %s", + strerror(errno)); + exit(1); + } + continue; + case 0: /* timeout */ + break; /* dhcp6_check_timer() will treat the case */ + default: + break; + } + if (FD_ISSET(sock, &r)) + client6_recv(); + if (ctlsock >= 0) { + if (FD_ISSET(ctlsock, &r)) { + (void)dhcp6_ctl_acceptcommand(ctlsock, + client6_do_ctlcommand); + } + (void)dhcp6_ctl_readcommand(&r); + } + } +} + +static inline int +get_val32(bpp, lenp, valp) + char **bpp; + int *lenp; + u_int32_t *valp; +{ + char *bp = *bpp; + int len = *lenp; + u_int32_t i32; + + if (len < sizeof(*valp)) + return (-1); + + memcpy(&i32, bp, sizeof(i32)); + *valp = ntohl(i32); + + *bpp = bp + sizeof(*valp); + *lenp = len - sizeof(*valp); + + return (0); +} + +static inline int +get_ifname(bpp, lenp, ifbuf, ifbuflen) + char **bpp; + int *lenp; + char *ifbuf; + int ifbuflen; +{ + char *bp = *bpp; + int len = *lenp, ifnamelen; + u_int32_t i32; + + if (get_val32(bpp, lenp, &i32)) + return (-1); + ifnamelen = (int)i32; + + if (*lenp < ifnamelen || ifnamelen > ifbuflen) + return (-1); + + memset(ifbuf, 0, sizeof(ifbuf)); + memcpy(ifbuf, *bpp, ifnamelen); + if (ifbuf[ifbuflen - 1] != '\0') + return (-1); /* not null terminated */ + + *bpp = bp + sizeof(i32) + ifnamelen; + *lenp = len - (sizeof(i32) + ifnamelen); + + return (0); +} + +static int +client6_do_ctlcommand(buf, len) + char *buf; + ssize_t len; +{ + struct dhcp6ctl *ctlhead; + u_int16_t command, version; + u_int32_t p32, ts, ts0; + int commandlen; + char *bp; + char ifname[IFNAMSIZ]; + time_t now; + + memset(ifname, 0, sizeof(ifname)); + + ctlhead = (struct dhcp6ctl *)buf; + + command = ntohs(ctlhead->command); + commandlen = (int)(ntohs(ctlhead->len)); + version = ntohs(ctlhead->version); + if (len != sizeof(struct dhcp6ctl) + commandlen) { + dprintf(LOG_ERR, FNAME, + "assumption failure: command length mismatch"); + return (DHCP6CTL_R_FAILURE); + } + + /* replay protection and message authentication */ + if ((now = time(NULL)) < 0) { + dprintf(LOG_ERR, FNAME, "failed to get current time: %s", + strerror(errno)); + return (DHCP6CTL_R_FAILURE); + } + ts0 = (u_int32_t)now; + ts = ntohl(ctlhead->timestamp); + if (ts + CTLSKEW < ts0 || (ts - CTLSKEW) > ts0) { + dprintf(LOG_INFO, FNAME, "timestamp is out of range"); + return (DHCP6CTL_R_FAILURE); + } + + if (ctlkey == NULL) { /* should not happen!! */ + dprintf(LOG_ERR, FNAME, "no secret key for control channel"); + return (DHCP6CTL_R_FAILURE); + } + if (dhcp6_verify_mac(buf, len, DHCP6CTL_AUTHPROTO_UNDEF, + DHCP6CTL_AUTHALG_HMACMD5, sizeof(*ctlhead), ctlkey) != 0) { + dprintf(LOG_INFO, FNAME, "authentication failure"); + return (DHCP6CTL_R_FAILURE); + } + + bp = buf + sizeof(*ctlhead) + ctldigestlen; + commandlen -= ctldigestlen; + + if (version > DHCP6CTL_VERSION) { + dprintf(LOG_INFO, FNAME, "unsupported version: %d", version); + return (DHCP6CTL_R_FAILURE); + } + + switch (command) { + case DHCP6CTL_COMMAND_RELOAD: + if (commandlen != 0) { + dprintf(LOG_INFO, FNAME, "invalid command length " + "for reload: %d", commandlen); + return (DHCP6CTL_R_DONE); + } + client6_reload(); + break; + case DHCP6CTL_COMMAND_START: + if (get_val32(&bp, &commandlen, &p32)) + return (DHCP6CTL_R_FAILURE); + switch (p32) { + case DHCP6CTL_INTERFACE: + if (get_ifname(&bp, &commandlen, ifname, + sizeof(ifname))) { + return (DHCP6CTL_R_FAILURE); + } + if (client6_ifctl(ifname, DHCP6CTL_COMMAND_START)) + return (DHCP6CTL_R_FAILURE); + break; + default: + dprintf(LOG_INFO, FNAME, + "unknown start target: %ul", p32); + return (DHCP6CTL_R_FAILURE); + } + break; + case DHCP6CTL_COMMAND_STOP: + if (commandlen == 0) { + exit_ok = 1; + free_resources(NULL); + unlink(pid_file); + check_exit(); + } else { + if (get_val32(&bp, &commandlen, &p32)) + return (DHCP6CTL_R_FAILURE); + + switch (p32) { + case DHCP6CTL_INTERFACE: + if (get_ifname(&bp, &commandlen, ifname, + sizeof(ifname))) { + return (DHCP6CTL_R_FAILURE); + } + if (client6_ifctl(ifname, + DHCP6CTL_COMMAND_STOP)) { + return (DHCP6CTL_R_FAILURE); + } + break; + default: + dprintf(LOG_INFO, FNAME, + "unknown start target: %ul", p32); + return (DHCP6CTL_R_FAILURE); + } + } + break; + default: + dprintf(LOG_INFO, FNAME, + "unknown control command: %d (len=%d)", + (int)command, commandlen); + return (DHCP6CTL_R_FAILURE); + } + + return (DHCP6CTL_R_DONE); +} + +static void +client6_reload() +{ + /* reload the configuration file */ + if (cfparse(conffile) != 0) { + dprintf(LOG_WARNING, FNAME, + "failed to reload configuration file"); + return; + } + + dprintf(LOG_NOTICE, FNAME, "client reloaded"); + + return; +} + +static int +client6_ifctl(ifname, command) + char *ifname; + u_int16_t command; +{ + struct dhcp6_if *ifp; + + if ((ifp = find_ifconfbyname(ifname)) == NULL) { + dprintf(LOG_INFO, FNAME, + "failed to find interface configuration for %s", + ifname); + return (-1); + } + + dprintf(LOG_DEBUG, FNAME, "%s interface %s", + command == DHCP6CTL_COMMAND_START ? "start" : "stop", ifname); + + switch(command) { + case DHCP6CTL_COMMAND_START: + free_resources(ifp); + if (client6_start(ifp)) { + dprintf(LOG_NOTICE, FNAME, "failed to restart %s", + ifname); + return (-1); + } + break; + case DHCP6CTL_COMMAND_STOP: + free_resources(ifp); + if (ifp->timer != NULL) { + dprintf(LOG_DEBUG, FNAME, + "removed existing timer on %s", ifp->ifname); + dhcp6_remove_timer(&ifp->timer); + } + break; + default: /* impossible case, should be a bug */ + dprintf(LOG_ERR, FNAME, "unknown command: %d", (int)command); + break; + } + + return (0); +} + +static struct dhcp6_timer * +client6_expire_refreshtime(arg) + void *arg; +{ + struct dhcp6_if *ifp = arg; + + dprintf(LOG_DEBUG, FNAME, + "information refresh time on %s expired", ifp->ifname); + + dhcp6_remove_timer(&ifp->timer); + client6_start(ifp); + + return (NULL); +} + +struct dhcp6_timer * +client6_timo(arg) + void *arg; +{ + struct dhcp6_event *ev = (struct dhcp6_event *)arg; + struct dhcp6_if *ifp; + int state = ev->state; + + ifp = ev->ifp; + ev->timeouts++; + + /* + * Unless MRC is zero, the message exchange fails once the client has + * transmitted the message MRC times. + * [RFC3315 14.] + */ + if (ev->max_retrans_cnt && ev->timeouts >= ev->max_retrans_cnt) { + dprintf(LOG_INFO, FNAME, "no responses were received"); + dhcp6_remove_event(ev); + + if (state == DHCP6S_RELEASE) + check_exit(); + + return (NULL); + } + + switch(ev->state) { + case DHCP6S_INIT: + ev->timeouts = 0; /* indicate to generate a new XID. */ + if ((ifp->send_flags & DHCIFF_INFO_ONLY) || infreq_mode) + ev->state = DHCP6S_INFOREQ; + else { + ev->state = DHCP6S_SOLICIT; + if (construct_confdata(ifp, ev)) { + dprintf(LOG_ERR, FNAME, "can't send solicit"); + exit(1); /* XXX */ + } + } + dhcp6_set_timeoparam(ev); /* XXX */ + /* fall through */ + case DHCP6S_REQUEST: + case DHCP6S_RELEASE: + case DHCP6S_INFOREQ: + client6_send(ev); + break; + case DHCP6S_RENEW: + case DHCP6S_REBIND: + if (!TAILQ_EMPTY(&ev->data_list)) + client6_send(ev); + else { + dprintf(LOG_INFO, FNAME, + "all information to be updated was canceled"); + dhcp6_remove_event(ev); + return (NULL); + } + break; + case DHCP6S_SOLICIT: + if (ev->servers) { + /* + * Send a Request to the best server. + * Note that when we set Rapid-commit in Solicit, + * but a direct Reply has been delayed (very much), + * the transition to DHCP6S_REQUEST (and the change of + * transaction ID) will invalidate the reply even if it + * ever arrives. + */ + ev->current_server = select_server(ev); + if (ev->current_server == NULL) { + /* this should not happen! */ + dprintf(LOG_NOTICE, FNAME, + "can't find a server"); + exit(1); /* XXX */ + } + if (duidcpy(&ev->serverid, + &ev->current_server->optinfo.serverID)) { + dprintf(LOG_NOTICE, FNAME, + "can't copy server ID"); + return (NULL); /* XXX: better recovery? */ + } + ev->timeouts = 0; + ev->state = DHCP6S_REQUEST; + dhcp6_set_timeoparam(ev); + + if (ev->authparam != NULL) + free(ev->authparam); + ev->authparam = ev->current_server->authparam; + ev->current_server->authparam = NULL; + + if (construct_reqdata(ifp, + &ev->current_server->optinfo, ev)) { + dprintf(LOG_NOTICE, FNAME, + "failed to construct request data"); + break; + } + } + client6_send(ev); + break; + } + + dhcp6_reset_timer(ev); + + return (ev->timer); +} + +static int +construct_confdata(ifp, ev) + struct dhcp6_if *ifp; + struct dhcp6_event *ev; +{ + struct ia_conf *iac; + struct dhcp6_eventdata *evd = NULL; + struct dhcp6_list *ial = NULL, pl; + struct dhcp6_ia iaparam; + + TAILQ_INIT(&pl); /* for safety */ + + for (iac = TAILQ_FIRST(&ifp->iaconf_list); iac; + iac = TAILQ_NEXT(iac, link)) { + /* ignore IA config currently used */ + if (!TAILQ_EMPTY(&iac->iadata)) + continue; + + evd = NULL; + if ((evd = malloc(sizeof(*evd))) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event data"); + goto fail; + } + memset(evd, 0, sizeof(evd)); + + memset(&iaparam, 0, sizeof(iaparam)); + iaparam.iaid = iac->iaid; + switch (iac->type) { + case IATYPE_PD: + ial = NULL; + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + TAILQ_INIT(ial); + + TAILQ_INIT(&pl); + dhcp6_copy_list(&pl, + &((struct iapd_conf *)iac)->iapd_prefix_list); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IAPD, + &iaparam, &pl) == NULL) { + goto fail; + } + dhcp6_clear_list(&pl); + + evd->type = DHCP6_EVDATA_IAPD; + evd->data = ial; + evd->event = ev; + evd->destructor = destruct_iadata; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + break; + case IATYPE_NA: + ial = NULL; + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + TAILQ_INIT(ial); + + TAILQ_INIT(&pl); + dhcp6_copy_list(&pl, + &((struct iana_conf *)iac)->iana_address_list); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IANA, + &iaparam, &pl) == NULL) { + goto fail; + } + dhcp6_clear_list(&pl); + + evd->type = DHCP6_EVDATA_IANA; + evd->data = ial; + evd->event = ev; + evd->destructor = destruct_iadata; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + break; + default: + dprintf(LOG_ERR, FNAME, "internal error"); + exit(1); + } + } + + return (0); + + fail: + if (evd) + free(evd); + if (ial) + free(ial); + dhcp6_remove_event(ev); /* XXX */ + + return (-1); +} + +static int +construct_reqdata(ifp, optinfo, ev) + struct dhcp6_if *ifp; + struct dhcp6_optinfo *optinfo; + struct dhcp6_event *ev; +{ + struct ia_conf *iac; + struct dhcp6_eventdata *evd = NULL; + struct dhcp6_list *ial = NULL; + struct dhcp6_ia iaparam; + + /* discard previous event data */ + dhcp6_remove_evdata(ev); + + if (optinfo == NULL) + return (0); + + for (iac = TAILQ_FIRST(&ifp->iaconf_list); iac; + iac = TAILQ_NEXT(iac, link)) { + struct dhcp6_listval *v; + + /* ignore IA config currently used */ + if (!TAILQ_EMPTY(&iac->iadata)) + continue; + + memset(&iaparam, 0, sizeof(iaparam)); + iaparam.iaid = iac->iaid; + + ial = NULL; + evd = NULL; + + switch (iac->type) { + case IATYPE_PD: + if ((v = dhcp6_find_listval(&optinfo->iapd_list, + DHCP6_LISTVAL_IAPD, &iaparam, 0)) == NULL) + continue; + + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + + TAILQ_INIT(ial); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IAPD, + &iaparam, &v->sublist) == NULL) { + goto fail; + } + + if ((evd = malloc(sizeof(*evd))) == NULL) + goto fail; + memset(evd, 0, sizeof(*evd)); + evd->type = DHCP6_EVDATA_IAPD; + evd->data = ial; + evd->event = ev; + evd->destructor = destruct_iadata; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + break; + case IATYPE_NA: + if ((v = dhcp6_find_listval(&optinfo->iana_list, + DHCP6_LISTVAL_IANA, &iaparam, 0)) == NULL) + continue; + + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + + TAILQ_INIT(ial); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IANA, + &iaparam, &v->sublist) == NULL) { + goto fail; + } + + if ((evd = malloc(sizeof(*evd))) == NULL) + goto fail; + memset(evd, 0, sizeof(*evd)); + evd->type = DHCP6_EVDATA_IANA; + evd->data = ial; + evd->event = ev; + evd->destructor = destruct_iadata; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + break; + default: + dprintf(LOG_ERR, FNAME, "internal error"); + exit(1); + } + } + + return (0); + + fail: + if (evd) + free(evd); + if (ial) + free(ial); + dhcp6_remove_event(ev); /* XXX */ + + return (-1); +} + +static void +destruct_iadata(evd) + struct dhcp6_eventdata *evd; +{ + struct dhcp6_list *ial; + + if (evd->type != DHCP6_EVDATA_IAPD && evd->type != DHCP6_EVDATA_IANA) { + dprintf(LOG_ERR, FNAME, "assumption failure %d", evd->type); + exit(1); + } + + ial = (struct dhcp6_list *)evd->data; + dhcp6_clear_list(ial); + free(ial); +} + +static struct dhcp6_serverinfo * +select_server(ev) + struct dhcp6_event *ev; +{ + struct dhcp6_serverinfo *s; + + /* + * pick the best server according to RFC3315 Section 17.1.3. + * XXX: we currently just choose the one that is active and has the + * highest preference. + */ + for (s = ev->servers; s; s = s->next) { + if (s->active) { + dprintf(LOG_DEBUG, FNAME, "picked a server (ID: %s)", + duidstr(&s->optinfo.serverID)); + return (s); + } + } + + return (NULL); +} + +static void +client6_signal(sig) + int sig; +{ + + switch (sig) { + case SIGTERM: + sig_flags |= SIGF_TERM; + break; + case SIGHUP: + sig_flags |= SIGF_HUP; + break; + } +} + +void +client6_send(ev) + struct dhcp6_event *ev; +{ + struct dhcp6_if *ifp; + char buf[BUFSIZ]; + struct sockaddr_in6 dst; + struct dhcp6 *dh6; + struct dhcp6_optinfo optinfo; + ssize_t optlen, len; + struct dhcp6_eventdata *evd; + + ifp = ev->ifp; + + dh6 = (struct dhcp6 *)buf; + memset(dh6, 0, sizeof(*dh6)); + + switch(ev->state) { + case DHCP6S_SOLICIT: + dh6->dh6_msgtype = DH6_SOLICIT; + break; + case DHCP6S_REQUEST: + dh6->dh6_msgtype = DH6_REQUEST; + break; + case DHCP6S_RENEW: + dh6->dh6_msgtype = DH6_RENEW; + break; + case DHCP6S_REBIND: + dh6->dh6_msgtype = DH6_REBIND; + break; + case DHCP6S_RELEASE: + dh6->dh6_msgtype = DH6_RELEASE; + break; + case DHCP6S_INFOREQ: + dh6->dh6_msgtype = DH6_INFORM_REQ; + break; + default: + dprintf(LOG_ERR, FNAME, "unexpected state"); + exit(1); /* XXX */ + } + + if (ev->timeouts == 0) { + /* + * A client SHOULD generate a random number that cannot easily + * be guessed or predicted to use as the transaction ID for + * each new message it sends. + * + * A client MUST leave the transaction-ID unchanged in + * retransmissions of a message. [RFC3315 15.1] + */ +#ifdef HAVE_ARC4RANDOM + ev->xid = arc4random() & DH6_XIDMASK; +#else + ev->xid = random() & DH6_XIDMASK; +#endif + dprintf(LOG_DEBUG, FNAME, "a new XID (%x) is generated", + ev->xid); + } + dh6->dh6_xid &= ~ntohl(DH6_XIDMASK); + dh6->dh6_xid |= htonl(ev->xid); + len = sizeof(*dh6); + + /* + * construct options + */ + dhcp6_init_options(&optinfo); + + /* server ID */ + switch (ev->state) { + case DHCP6S_REQUEST: + case DHCP6S_RENEW: + case DHCP6S_RELEASE: + if (duidcpy(&optinfo.serverID, &ev->serverid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto end; + } + break; + } + + /* client ID */ + if (duidcpy(&optinfo.clientID, &client_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto end; + } + + /* rapid commit (in Solicit only) */ + if (ev->state == DHCP6S_SOLICIT && + (ifp->send_flags & DHCIFF_RAPID_COMMIT)) { + optinfo.rapidcommit = 1; + } + + /* elapsed time */ + if (ev->timeouts == 0) { + gettimeofday(&ev->tv_start, NULL); + optinfo.elapsed_time = 0; + } else { + struct timeval now, tv_diff; + long et; + + gettimeofday(&now, NULL); + tv_sub(&now, &ev->tv_start, &tv_diff); + + /* + * The client uses the value 0xffff to represent any elapsed + * time values greater than the largest time value that can be + * represented in the Elapsed Time option. + * [RFC3315 22.9.] + */ + if (tv_diff.tv_sec >= (MAX_ELAPSED_TIME / 100) + 1) { + /* + * Perhaps we are nervous too much, but without this + * additional check, we would see an overflow in 248 + * days (of no responses). + */ + et = MAX_ELAPSED_TIME; + } else { + et = tv_diff.tv_sec * 100 + tv_diff.tv_usec / 10000; + if (et >= MAX_ELAPSED_TIME) + et = MAX_ELAPSED_TIME; + } + optinfo.elapsed_time = (int32_t)et; + } + + /* option request options */ + if (ev->state != DHCP6S_RELEASE && + dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) { + dprintf(LOG_ERR, FNAME, "failed to copy requested options"); + goto end; + } + + /* configuration information specified as event data */ + for (evd = TAILQ_FIRST(&ev->data_list); evd; + evd = TAILQ_NEXT(evd, link)) { + switch(evd->type) { + case DHCP6_EVDATA_IAPD: + if (dhcp6_copy_list(&optinfo.iapd_list, + (struct dhcp6_list *)evd->data)) { + dprintf(LOG_NOTICE, FNAME, + "failed to add an IAPD"); + goto end; + } + break; + case DHCP6_EVDATA_IANA: + if (dhcp6_copy_list(&optinfo.iana_list, + (struct dhcp6_list *)evd->data)) { + dprintf(LOG_NOTICE, FNAME, + "failed to add an IAPD"); + goto end; + } + break; + default: + dprintf(LOG_ERR, FNAME, "unexpected event data (%d)", + evd->type); + exit(1); + } + } + + /* authentication information */ + if (set_auth(ev, &optinfo)) { + dprintf(LOG_INFO, FNAME, + "failed to set authentication option"); + goto end; + } + + /* set options in the message */ + if ((optlen = dhcp6_set_options(dh6->dh6_msgtype, + (struct dhcp6opt *)(dh6 + 1), + (struct dhcp6opt *)(buf + sizeof(buf)), &optinfo)) < 0) { + dprintf(LOG_INFO, FNAME, "failed to construct options"); + goto end; + } + len += optlen; + + /* calculate MAC if necessary, and put it to the message */ + if (ev->authparam != NULL) { + switch (ev->authparam->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + if (ev->authparam->key == NULL) + break; + + if (dhcp6_calc_mac((char *)dh6, len, + optinfo.authproto, optinfo.authalgorithm, + optinfo.delayedauth_offset + sizeof(*dh6), + ev->authparam->key)) { + dprintf(LOG_WARNING, FNAME, + "failed to calculate MAC"); + goto end; + } + break; + default: + break; /* do nothing */ + } + } + + /* + * Unless otherwise specified in this document or in a document that + * describes how IPv6 is carried over a specific type of link (for link + * types that do not support multicast), a client sends DHCP messages + * to the All_DHCP_Relay_Agents_and_Servers. + * [RFC3315 Section 13.] + */ + dst = *sa6_allagent; + dst.sin6_scope_id = ifp->linkid; + + if (sendto(sock, buf, len, 0, (struct sockaddr *)&dst, + sysdep_sa_len((struct sockaddr *)&dst)) == -1) { + dprintf(LOG_ERR, FNAME, + "transmit failed: %s", strerror(errno)); + goto end; + } + + dprintf(LOG_DEBUG, FNAME, "send %s to %s", + dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&dst)); + + end: + dhcp6_clear_options(&optinfo); + return; +} + +/* result will be a - b */ +static void +tv_sub(a, b, result) + struct timeval *a, *b, *result; +{ + if (a->tv_sec < b->tv_sec || + (a->tv_sec == b->tv_sec && a->tv_usec < b->tv_usec)) { + result->tv_sec = 0; + result->tv_usec = 0; + + return; + } + + result->tv_sec = a->tv_sec - b->tv_sec; + if (a->tv_usec < b->tv_usec) { + result->tv_usec = a->tv_usec + 1000000 - b->tv_usec; + result->tv_sec -= 1; + } else + result->tv_usec = a->tv_usec - b->tv_usec; + + return; +} + +static void +client6_recv() +{ + char rbuf[BUFSIZ], cmsgbuf[BUFSIZ]; + struct msghdr mhdr; + struct iovec iov; + struct sockaddr_storage from; + struct dhcp6_if *ifp; + struct dhcp6opt *p, *ep; + struct dhcp6_optinfo optinfo; + ssize_t len; + struct dhcp6 *dh6; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + + memset(&iov, 0, sizeof(iov)); + memset(&mhdr, 0, sizeof(mhdr)); + + iov.iov_base = (caddr_t)rbuf; + iov.iov_len = sizeof(rbuf); + mhdr.msg_name = (caddr_t)&from; + mhdr.msg_namelen = sizeof(from); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = (caddr_t)cmsgbuf; + mhdr.msg_controllen = sizeof(cmsgbuf); + if ((len = recvmsg(sock, &mhdr, 0)) < 0) { + dprintf(LOG_ERR, FNAME, "recvmsg: %s", strerror(errno)); + return; + } + + /* detect receiving interface */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + } + } + if (pi == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to get packet info"); + return; + } + + if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) { + dprintf(LOG_INFO, FNAME, "unexpected interface (%d)", + (unsigned int)pi->ipi6_ifindex); + return; + } + + if (len < sizeof(*dh6)) { + dprintf(LOG_INFO, FNAME, "short packet (%d bytes)", len); + return; + } + + dh6 = (struct dhcp6 *)rbuf; + + dprintf(LOG_DEBUG, FNAME, "receive %s from %s on %s", + dhcp6msgstr(dh6->dh6_msgtype), + addr2str((struct sockaddr *)&from), ifp->ifname); + + /* get options */ + dhcp6_init_options(&optinfo); + p = (struct dhcp6opt *)(dh6 + 1); + ep = (struct dhcp6opt *)((char *)dh6 + len); + if (dhcp6_get_options(p, ep, &optinfo) < 0) { + dprintf(LOG_INFO, FNAME, "failed to parse options"); + return; + } + + switch(dh6->dh6_msgtype) { + case DH6_ADVERTISE: + (void)client6_recvadvert(ifp, dh6, len, &optinfo); + break; + case DH6_REPLY: + (void)client6_recvreply(ifp, dh6, len, &optinfo); + break; + default: + dprintf(LOG_INFO, FNAME, "received an unexpected message (%s) " + "from %s", dhcp6msgstr(dh6->dh6_msgtype), + addr2str((struct sockaddr *)&from)); + break; + } + + dhcp6_clear_options(&optinfo); + return; +} + +static int +client6_recvadvert(ifp, dh6, len, optinfo) + struct dhcp6_if *ifp; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_serverinfo *newserver, **sp; + struct dhcp6_event *ev; + struct dhcp6_eventdata *evd; + struct authparam *authparam = NULL, authparam0; + + /* find the corresponding event based on the received xid */ + ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); + if (ev == NULL) { + dprintf(LOG_INFO, FNAME, "XID mismatch"); + return (-1); + } + + /* packet validation based on Section 15.3 of RFC3315. */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } else { + dprintf(LOG_DEBUG, FNAME, "server ID: %s, pref=%d", + duidstr(&optinfo->serverID), + optinfo->pref); + } + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + if (duidcmp(&optinfo->clientID, &client_duid)) { + dprintf(LOG_INFO, FNAME, "client DUID mismatch"); + return (-1); + } + + /* validate authentication */ + authparam0 = *ev->authparam; + if (process_auth(&authparam0, dh6, len, optinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication"); + return (-1); + } + + /* + * The requesting router MUST ignore any Advertise message that + * includes a Status Code option containing the value NoPrefixAvail + * [RFC3633 Section 11.1]. + * Likewise, the client MUST ignore any Advertise message that includes + * a Status Code option containing the value NoAddrsAvail. + * [RFC3315 Section 17.1.3]. + * We only apply this when we are going to request an address or + * a prefix. + */ + for (evd = TAILQ_FIRST(&ev->data_list); evd; + evd = TAILQ_NEXT(evd, link)) { + u_int16_t stcode; + char *stcodestr; + + switch (evd->type) { + case DHCP6_EVDATA_IAPD: + stcode = DH6OPT_STCODE_NOPREFIXAVAIL; + stcodestr = "NoPrefixAvail"; + break; + case DHCP6_EVDATA_IANA: + stcode = DH6OPT_STCODE_NOADDRSAVAIL; + stcodestr = "NoAddrsAvail"; + break; + default: + continue; + } + if (dhcp6_find_listval(&optinfo->stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, 0)) { + dprintf(LOG_INFO, FNAME, + "advertise contains %s status", stcodestr); + return (-1); + } + } + + if (ev->state != DHCP6S_SOLICIT || + (ifp->send_flags & DHCIFF_RAPID_COMMIT) || infreq_mode) { + /* + * We expected a reply message, but do actually receive an + * Advertise message. The server should be configured not to + * allow the Rapid Commit option. + * We process the message as if we expected the Advertise. + * [RFC3315 Section 17.1.4] + */ + dprintf(LOG_INFO, FNAME, "unexpected advertise"); + /* proceed anyway */ + } + + /* ignore the server if it is known */ + if (find_server(ev, &optinfo->serverID)) { + dprintf(LOG_INFO, FNAME, "duplicated server (ID: %s)", + duidstr(&optinfo->serverID)); + return (-1); + } + + /* keep the server */ + if ((newserver = malloc(sizeof(*newserver))) == NULL) { + dprintf(LOG_WARNING, FNAME, + "memory allocation failed for server"); + return (-1); + } + memset(newserver, 0, sizeof(*newserver)); + + /* remember authentication parameters */ + newserver->authparam = ev->authparam; + newserver->authparam->flags = authparam0.flags; + newserver->authparam->prevrd = authparam0.prevrd; + newserver->authparam->key = authparam0.key; + + /* allocate new authentication parameter for the soliciting event */ + if ((authparam = new_authparam(ev->authparam->authproto, + ev->authparam->authalgorithm, ev->authparam->authrdm)) == NULL) { + dprintf(LOG_WARNING, FNAME, "memory allocation failed " + "for authentication parameters"); + free(newserver); + return (-1); + } + ev->authparam = authparam; + + /* copy options */ + dhcp6_init_options(&newserver->optinfo); + if (dhcp6_copy_options(&newserver->optinfo, optinfo)) { + dprintf(LOG_ERR, FNAME, "failed to copy options"); + if (newserver->authparam != NULL) + free(newserver->authparam); + free(newserver); + return (-1); + } + if (optinfo->pref != DH6OPT_PREF_UNDEF) + newserver->pref = optinfo->pref; + newserver->active = 1; + for (sp = &ev->servers; *sp; sp = &(*sp)->next) { + if ((*sp)->pref != DH6OPT_PREF_MAX && + (*sp)->pref < newserver->pref) { + break; + } + } + newserver->next = *sp; + *sp = newserver; + + if (newserver->pref == DH6OPT_PREF_MAX) { + /* + * If the client receives an Advertise message that includes a + * Preference option with a preference value of 255, the client + * immediately begins a client-initiated message exchange. + * [RFC3315 Section 17.1.2] + */ + ev->current_server = newserver; + if (duidcpy(&ev->serverid, + &ev->current_server->optinfo.serverID)) { + dprintf(LOG_NOTICE, FNAME, "can't copy server ID"); + return (-1); /* XXX: better recovery? */ + } + if (construct_reqdata(ifp, &ev->current_server->optinfo, ev)) { + dprintf(LOG_NOTICE, FNAME, + "failed to construct request data"); + return (-1); /* XXX */ + } + + ev->timeouts = 0; + ev->state = DHCP6S_REQUEST; + + free(ev->authparam); + ev->authparam = newserver->authparam; + newserver->authparam = NULL; + + client6_send(ev); + + dhcp6_set_timeoparam(ev); + dhcp6_reset_timer(ev); + } else if (ev->servers->next == NULL) { + struct timeval *rest, elapsed, tv_rt, tv_irt, timo; + + /* + * If this is the first advertise, adjust the timer so that + * the client can collect other servers until IRT elapses. + * XXX: we did not want to do such "low level" timer + * calculation here. + */ + rest = dhcp6_timer_rest(ev->timer); + tv_rt.tv_sec = (ev->retrans * 1000) / 1000000; + tv_rt.tv_usec = (ev->retrans * 1000) % 1000000; + tv_irt.tv_sec = (ev->init_retrans * 1000) / 1000000; + tv_irt.tv_usec = (ev->init_retrans * 1000) % 1000000; + timeval_sub(&tv_rt, rest, &elapsed); + if (TIMEVAL_LEQ(elapsed, tv_irt)) + timeval_sub(&tv_irt, &elapsed, &timo); + else + timo.tv_sec = timo.tv_usec = 0; + + dprintf(LOG_DEBUG, FNAME, "reset timer for %s to %d.%06d", + ifp->ifname, (int)timo.tv_sec, (int)timo.tv_usec); + + dhcp6_set_timer(&timo, ev->timer); + } + + return (0); +} + +static struct dhcp6_serverinfo * +find_server(ev, duid) + struct dhcp6_event *ev; + struct duid *duid; +{ + struct dhcp6_serverinfo *s; + + for (s = ev->servers; s; s = s->next) { + if (duidcmp(&s->optinfo.serverID, duid) == 0) + return (s); + } + + return (NULL); +} + +static int +client6_recvreply(ifp, dh6, len, optinfo) + struct dhcp6_if *ifp; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_listval *lv; + struct dhcp6_event *ev; + int state; + + /* find the corresponding event based on the received xid */ + ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); + if (ev == NULL) { + dprintf(LOG_INFO, FNAME, "XID mismatch"); + return (-1); + } + + state = ev->state; + if (state != DHCP6S_INFOREQ && + state != DHCP6S_REQUEST && + state != DHCP6S_RENEW && + state != DHCP6S_REBIND && + state != DHCP6S_RELEASE && + (state != DHCP6S_SOLICIT || + !(ifp->send_flags & DHCIFF_RAPID_COMMIT))) { + dprintf(LOG_INFO, FNAME, "unexpected reply"); + return (-1); + } + + /* A Reply message must contain a Server ID option */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + + /* + * DUID in the Client ID option (which must be contained for our + * client implementation) must match ours. + */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + if (duidcmp(&optinfo->clientID, &client_duid)) { + dprintf(LOG_INFO, FNAME, "client DUID mismatch"); + return (-1); + } + + /* validate authentication */ + if (process_auth(ev->authparam, dh6, len, optinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication"); + return (-1); + } + + /* + * If the client included a Rapid Commit option in the Solicit message, + * the client discards any Reply messages it receives that do not + * include a Rapid Commit option. + * (should we keep the server otherwise?) + * [RFC3315 Section 17.1.4] + */ + if (state == DHCP6S_SOLICIT && + (ifp->send_flags & DHCIFF_RAPID_COMMIT) && + !optinfo->rapidcommit) { + dprintf(LOG_INFO, FNAME, "no rapid commit"); + return (-1); + } + + /* + * The client MAY choose to report any status code or message from the + * status code option in the Reply message. + * [RFC3315 Section 18.1.8] + */ + for (lv = TAILQ_FIRST(&optinfo->stcode_list); lv; + lv = TAILQ_NEXT(lv, link)) { + dprintf(LOG_INFO, FNAME, "status code: %s", + dhcp6_stcodestr(lv->val_num16)); + } + + if (!TAILQ_EMPTY(&optinfo->dns_list)) { + struct dhcp6_listval *d; + int i = 0; + + for (d = TAILQ_FIRST(&optinfo->dns_list); d; + d = TAILQ_NEXT(d, link), i++) { + info_printf("nameserver[%d] %s", + i, in6addr2str(&d->val_addr6, 0)); + } + } + + if (!TAILQ_EMPTY(&optinfo->dnsname_list)) { + struct dhcp6_listval *d; + int i = 0; + + for (d = TAILQ_FIRST(&optinfo->dnsname_list); d; + d = TAILQ_NEXT(d, link), i++) { + info_printf("Domain search list[%d] %s", + i, d->val_vbuf.dv_buf); + } + } + + if (!TAILQ_EMPTY(&optinfo->ntp_list)) { + struct dhcp6_listval *d; + int i = 0; + + for (d = TAILQ_FIRST(&optinfo->ntp_list); d; + d = TAILQ_NEXT(d, link), i++) { + info_printf("NTP server[%d] %s", + i, in6addr2str(&d->val_addr6, 0)); + } + } + + if (!TAILQ_EMPTY(&optinfo->sip_list)) { + struct dhcp6_listval *d; + int i = 0; + + for (d = TAILQ_FIRST(&optinfo->sip_list); d; + d = TAILQ_NEXT(d, link), i++) { + info_printf("SIP server address[%d] %s", + i, in6addr2str(&d->val_addr6, 0)); + } + } + + if (!TAILQ_EMPTY(&optinfo->sipname_list)) { + struct dhcp6_listval *d; + int i = 0; + + for (d = TAILQ_FIRST(&optinfo->sipname_list); d; + d = TAILQ_NEXT(d, link), i++) { + info_printf("SIP domain name[%d] %s", + i, d->val_vbuf.dv_buf); + } + } + + /* + * Call the configuration script, if specified, to handle various + * configuration parameters. + */ + if (ifp->scriptpath != NULL && strlen(ifp->scriptpath) != 0) { + dprintf(LOG_DEBUG, FNAME, "executes %s", ifp->scriptpath); + client6_script(ifp->scriptpath, state, optinfo); + } + + /* + * Set refresh timer for configuration information specified in + * information-request. If the timer value is specified by the server + * in an information refresh time option, use it; use the protocol + * default otherwise. + */ + if (state == DHCP6S_INFOREQ) { + int64_t refreshtime = DHCP6_IRT_DEFAULT; + + if (optinfo->refreshtime != DH6OPT_REFRESHTIME_UNDEF) + refreshtime = optinfo->refreshtime; + + ifp->timer = dhcp6_add_timer(client6_expire_refreshtime, ifp); + if (ifp->timer == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to add timer for refresh time"); + } else { + struct timeval tv; + + tv.tv_sec = (long)refreshtime; + tv.tv_usec = 0; + + if (tv.tv_sec < 0) { + /* + * XXX: tv_sec can overflow for an + * unsigned 32bit value. + */ + dprintf(LOG_WARNING, FNAME, + "refresh time is too large: %lu", + (u_int32_t)refreshtime); + tv.tv_sec = 0x7fffffff; /* XXX */ + } + + dhcp6_set_timer(&tv, ifp->timer); + } + } else if (optinfo->refreshtime != DH6OPT_REFRESHTIME_UNDEF) { + /* + * draft-ietf-dhc-lifetime-02 clarifies that refresh time + * is only used for information-request and reply exchanges. + */ + dprintf(LOG_INFO, FNAME, + "unexpected information refresh time option (ignored)"); + } + + /* update stateful configuration information */ + if (state != DHCP6S_RELEASE) { + update_ia(IATYPE_PD, &optinfo->iapd_list, ifp, + &optinfo->serverID, ev->authparam); + update_ia(IATYPE_NA, &optinfo->iana_list, ifp, + &optinfo->serverID, ev->authparam); + } + + dhcp6_remove_event(ev); + + if (state == DHCP6S_RELEASE) { + /* + * When the client receives a valid Reply message in response + * to a Release message, the client considers the Release event + * completed, regardless of the Status Code option(s) returned + * by the server. + * [RFC3315 Section 18.1.8] + */ + check_exit(); + } + + dprintf(LOG_DEBUG, FNAME, "got an expected reply, sleeping."); + + if (infreq_mode) { + exit_ok = 1; + free_resources(NULL); + unlink(pid_file); + check_exit(); + } + return (0); +} + +static struct dhcp6_event * +find_event_withid(ifp, xid) + struct dhcp6_if *ifp; + u_int32_t xid; +{ + struct dhcp6_event *ev; + + for (ev = TAILQ_FIRST(&ifp->event_list); ev; + ev = TAILQ_NEXT(ev, link)) { + if (ev->xid == xid) + return (ev); + } + + return (NULL); +} + +static int +process_auth(authparam, dh6, len, optinfo) + struct authparam *authparam; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; +{ + struct keyinfo *key = NULL; + int authenticated = 0; + + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_UNDEF: + /* server did not provide authentication option */ + break; + case DHCP6_AUTHPROTO_DELAYED: + if ((optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) { + dprintf(LOG_INFO, FNAME, "server did not include " + "authentication information"); + break; + } + + if (optinfo->authalgorithm != DHCP6_AUTHALG_HMACMD5) { + dprintf(LOG_INFO, FNAME, "unknown authentication " + "algorithm (%d)", optinfo->authalgorithm); + break; + } + + if (optinfo->authrdm != DHCP6_AUTHRDM_MONOCOUNTER) { + dprintf(LOG_INFO, FNAME,"unknown RDM (%d)", + optinfo->authrdm); + break; + } + + /* + * Replay protection. If we do not know the previous RD value, + * we accept the message anyway (XXX). + */ + if ((authparam->flags & AUTHPARAM_FLAGS_NOPREVRD)) { + dprintf(LOG_WARNING, FNAME, "previous RD value is " + "unknown (accept it)"); + } else { + if (dhcp6_auth_replaycheck(optinfo->authrdm, + authparam->prevrd, optinfo->authrd)) { + dprintf(LOG_INFO, FNAME, + "possible replay attack detected"); + break; + } + } + + /* identify the secret key */ + if ((key = authparam->key) != NULL) { + /* + * If we already know a key, its identification should + * match that contained in the received option. + * (from Section 21.4.5.1 of RFC3315) + */ + if (optinfo->delayedauth_keyid != key->keyid || + optinfo->delayedauth_realmlen != key->realmlen || + memcmp(optinfo->delayedauth_realmval, key->realm, + key->realmlen) != 0) { + dprintf(LOG_INFO, FNAME, + "authentication key mismatch"); + break; + } + } else { + key = find_key(optinfo->delayedauth_realmval, + optinfo->delayedauth_realmlen, + optinfo->delayedauth_keyid); + if (key == NULL) { + dprintf(LOG_INFO, FNAME, "failed to find key " + "provided by the server (ID: %x)", + optinfo->delayedauth_keyid); + break; + } else { + dprintf(LOG_DEBUG, FNAME, "found key for " + "authentication: %s", key->name); + } + authparam->key = key; + } + + /* check for the key lifetime */ + if (dhcp6_validate_key(key)) { + dprintf(LOG_INFO, FNAME, "key %s has expired", + key->name); + break; + } + + /* validate MAC */ + if (dhcp6_verify_mac((char *)dh6, len, optinfo->authproto, + optinfo->authalgorithm, + optinfo->delayedauth_offset + sizeof(*dh6), key) == 0) { + dprintf(LOG_DEBUG, FNAME, "message authentication " + "validated"); + authenticated = 1; + } else { + dprintf(LOG_INFO, FNAME, "invalid message " + "authentication"); + } + + break; + default: + dprintf(LOG_INFO, FNAME, "server sent unsupported " + "authentication protocol (%d)", optinfo->authproto); + break; + } + + if (authenticated == 0) { + if (authparam->authproto != DHCP6_AUTHPROTO_UNDEF) { + dprintf(LOG_INFO, FNAME, "message not authenticated " + "while authentication required"); + + /* + * Right now, we simply discard unauthenticated + * messages. + */ + return (-1); + } + } else { + /* if authenticated, update the "previous" RD value */ + authparam->prevrd = optinfo->authrd; + authparam->flags &= ~AUTHPARAM_FLAGS_NOPREVRD; + } + + return (0); +} + +static int +set_auth(ev, optinfo) + struct dhcp6_event *ev; + struct dhcp6_optinfo *optinfo; +{ + struct authparam *authparam = ev->authparam; + + if (authparam == NULL) + return (0); + + optinfo->authproto = authparam->authproto; + optinfo->authalgorithm = authparam->authalgorithm; + optinfo->authrdm = authparam->authrdm; + + switch (authparam->authproto) { + case DHCP6_AUTHPROTO_UNDEF: /* we simply do not need authentication */ + return (0); + case DHCP6_AUTHPROTO_DELAYED: + if (ev->state == DHCP6S_INFOREQ) { + /* + * In the current implementation, delayed + * authentication for Information-request and Reply + * exchanges doesn't work. Specification is also + * unclear on this usage. + */ + dprintf(LOG_WARNING, FNAME, "delayed authentication " + "cannot be used for Information-request yet"); + return (-1); + } + + if (ev->state == DHCP6S_SOLICIT) { + optinfo->authflags |= DHCP6OPT_AUTHFLAG_NOINFO; + return (0); /* no auth information is needed */ + } + + if (authparam->key == NULL) { + dprintf(LOG_INFO, FNAME, + "no authentication key for %s", + dhcp6_event_statestr(ev)); + return (-1); + } + + if (dhcp6_validate_key(authparam->key)) { + dprintf(LOG_INFO, FNAME, "key %s is invalid", + authparam->key->name); + return (-1); + } + + if (get_rdvalue(optinfo->authrdm, &optinfo->authrd, + sizeof(optinfo->authrd))) { + dprintf(LOG_ERR, FNAME, "failed to get a replay " + "detection value"); + return (-1); + } + + optinfo->delayedauth_keyid = authparam->key->keyid; + optinfo->delayedauth_realmlen = authparam->key->realmlen; + optinfo->delayedauth_realmval = + malloc(optinfo->delayedauth_realmlen); + if (optinfo->delayedauth_realmval == NULL) { + dprintf(LOG_ERR, FNAME, "failed to allocate memory " + "for authentication realm"); + return (-1); + } + memcpy(optinfo->delayedauth_realmval, authparam->key->realm, + optinfo->delayedauth_realmlen); + + break; + default: + dprintf(LOG_ERR, FNAME, "unsupported authentication protocol " + "%d", authparam->authproto); + return (-1); + } + + return (0); +} + +static void +info_printf(const char *fmt, ...) +{ + va_list ap; + char logbuf[LINE_MAX]; + + va_start(ap, fmt); + vsnprintf(logbuf, sizeof(logbuf), fmt, ap); + + dprintf(LOG_DEBUG, FNAME, "%s", logbuf); + if (infreq_mode) + printf("%s\n", logbuf); + + return; +} diff --git a/dhcp6c.conf.5 b/dhcp6c.conf.5 new file mode 100644 index 0000000..611ab4f --- /dev/null +++ b/dhcp6c.conf.5 @@ -0,0 +1,670 @@ +.\" $KAME: dhcp6c.conf.5,v 1.30 2005/05/03 06:54:26 jinmei Exp $ +.\" +.\" Copyright (C) 2002 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd July 29, 2004 +.Dt DHCP6C.CONF 5 +.Os KAME +.\" +.Sh NAME +.Nm dhcp6c.conf +.Nd DHCPv6 client configuration file +.\" +.Sh SYNOPSIS +.Pa /usr/local/etc/dhcp6c.conf +.\" +.Sh DESCRIPTION +The +.Nm +file contains configuration information for KAME's DHCPv6 client, +.Nm dhcp6c . +The configuration file consists of a sequence of statements terminated +by a semi-colon (`;'). +Statements are composed of tokens separated by white space, +which can be any combination of blanks, +tabs and newlines. +In some cases a set of statements is combined with a pair of brackets, +which is regarded as a single token. +Lines beginning with +.Ql # +are comments. +.Sh Interface specification +There are some statements that may or have to specify interface. +Interfaces are specified in the form of "name unit", such as +.Ar fxp0 +and +.Ar gif1. +.\" +.Sh DHCPv6 options +Some configuration statements take the description of a DHCPv6 option +as an argument. +The followings are the format and description of available DHCPv6 +options. +.Bl -tag -width Ds -compact +.It Xo +.Ic domain-name-servers +.Xc +means a Domain Name Server option. +.It Xo +.Ic domain-name +.Xc +means a domain name option. +.It Xo +.Ic ntp-servers +.Xc +means an NTP server option. +As of this writing, the option type for this option is not officially +assigned. +.Nm dhcp6c +will reject this option unless it is explicitly built to accept the option. +.It Xo +.Ic sip-server-address +.Xc +means a SIP Server address option. +.It Xo +.Ic sip-server-domain-name +.Xc +means a SIP server domain name option. +.It Xo +.Ic nis-server-address +.Xc +means a NIS Server address option. +.It Xo +.Ic nis-domain-name +.Xc +means a NIS domain name option. +.It Xo +.Ic nisp-server-address +.Xc +means a NIS+ Server address option. +.It Xo +.Ic nisp-domain-name +.Xc +means a NIS+ domain name option. +.It Xo +.Ic bcmcs-server-address +.Xc +means a BCMCS Server address option. +.It Xo +.Ic bcmcs-server-domain-name +.Xc +means a BCMCS server domain name option. +.It Ic ia-pd Ar ID +means an IA_PD +.Pq Identity Association for Prefix Delegation +option. +.Ar ID +is a decimal number of the IAID +.Pq see below about identity associations . +.It Ic ia-na Ar ID +means an IA_PD +.Pq Identity Association for Non-temporary Addresses +option. +.Ar ID +is a decimal number of the IAID +.Pq see below about identity associations . +.It Ic rapid-commit +means a rapid-commit option. +.It Ic authentication Ar authname +means an authentication option. +.Ar authname +is a string specifying parameters of the authentication protocol. +An +.Ic authentication +statement for +.Ar authname +must be provided. +.El +.\" +.Sh Interface statement +An interface statement specifies configuration parameters on the +interface. +The generic format of an interface statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic interface Ar interface +{ +.Ar substatements +}; +.Xc +The followings are possible +.Ar substatements +in an interface statement. +.Bl -tag -width Ds -compact +.It Xo +.Ic send Ar send-options +; +.Xc +This statement specifies DHCPv6 options to be sent to the server(s). +Some options can only appear in particular messages according to the +specification, +in which case the appearance of the options is limited to be compliant +with the specification. +.Pp +.Ar send-options +is a comma-separated list of options, +each of which should be specified as described above. +Multiple +.Ic send +statements can also be specified, +in which case all the specified options will be sent. +.Pp +When +.Ic rapid-commit +is specified, +.Nm dhcp6c +will include a rapid-commit option in solicit messages and wait for +an immediate reply instead of advertisements. +.Pp +When +.Ic ia-pd +is specified, +.Nm dhcp6c +will initiate prefix delegation as a requesting router by +including an IA_PD option with the specified +.Ar ID +in solicit messages. +.Pp +When +.Ic ia-na +is specified, +.Nm dhcp6c +will initiate stateful address assignment by +including an IA_NA option with the specified +.Ar ID +in solicit messages. +.Pp +In either case, a corresponding identity association statement +must exist with the same +.Ar ID . +.It Ic request Ar request-options ; +This statement specifies DHCPv6 options to be included in an +option-request option. +.Ar request-options +is a comma-separated list of options, +which can consist of the following options. +.Bl -tag -width Ds -compact +.It Xo +.Ic domain-name-servers +.Xc +requests a list of Domain Name Server addresses. +.It Xo +.Ic domain-name +.Xc +requests a DNS search path. +.It Xo +.Ic ntp-servers +.Xc +requests a list of NTP server addresses. +As of this writing, the option type for this option is not officially +assigned. +.Nm dhcp6c +will reject this option unless it is explicitly built to accept the option. +.It Xo +.Ic sip-server-address +.Xc +requests a list of SIP server addresses. +.It Xo +.Ic sip-domain-name +.Xc +requests a SIP server domain name. +.It Xo +.Ic nis-server-address +.Xc +requests a list of NIS server addresses. +.It Xo +.Ic nis-domain-name +.Xc +requests a NIS domain name. +.It Xo +.Ic nisp-server-address +.Xc +requests a list of NIS+ server addresses. +.It Xo +.Ic nisp-domain-name +.Xc +requests a NIS+ domain name. +.It Xo +.Ic bcmcs-server-address +.Xc +requests a list of BCMCS server addresses. +.It Xo +.Ic bcmcs-domain-name +.Xc +requests a BCMCS domain name. +.It Xo +.Ic refreshtime +.Xc +means an information refresh time option. +This can only be specified when sent with information-request +messages; +.Nm dhcp6c +will ignore this option for other messages. +.El +Multiple +.Ic request +statements can also be specified, +in which case all the specified options will be requested. +.It Ic information-only ; +This statement specifies +.Nm dhcp6c +to only exchange informational configuration parameters with servers. +A list of DNS server addresses is an example of such parameters. +This statement is useful when the client does not need stateful +configuration parameters such as IPv6 addresses or prefixes. +.It Ic script Ar \(dqscript-name\(dq ; +This statement specifies a path to script invoked by +.Nm dhcp6c +on a certain condition including when the daemon receives a reply +message. +.Ar script-name +must be the absolute path from root to the script file, be a regular +file, and be created by the same owner who runs the daemon. +.El +.El +.\" +.Sh Identity association statement +Identity association +.Pq IA +is a key notion of DHCPv6. +An IA is uniquely identified in a client by a pair of IA type and +IA identifier +.Pq IAID . +An IA is associated with configuration information dependent on the IA type. +.Pp +An identity association statement defines a single IA with some +client-side configuration parameters. +Its format is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic id-assoc Ar type Op Ar ID +{ +.Ar substatements +}; +.Xc +.Ar type +is a string for the type of this IA. +The current implementation supports +.Ql Ic na +(non-temporary address allocation) +.Ql Ic pd +(prefix delegation) for the IA type. +.Ar ID +is a decimal number of IAID. +If omitted, the value 0 will be used by default. +.Ar substatements +is a sequence of statements that specifies configuration parameters +for this IA. +Each statement may or may not be specific to the type of IA. +.Pp +The followings are possible +.Ar substatements +for an IA of type +.Ic na . +.Bl -tag -width Ds -compact +.It Xo +.Ic address Ar ipv6-address pltime Op Ar vltime ; +.Xc +specifies an address and related parameters that the client wants to be +allocated. +Multiple addresses can be specified, each of which is described as a +separate +.Ic address +substatement. +.Nm dhcp6c +will include all the addresses +.Pq and related parameters +in Solicit messages, +as an IA_NA prefix option encapsulated in the corresponding IA_NA +option. +Note, however, that the server may or may not respect the specified +prefix parameters. +For parameters of the +.Ic address +substatement, +see +.Xr dhcp6s.conf 5 . +.El +.Pp +The followings are possible +.Ar substatements +for an IA of type +.Ic pd . +.Bl -tag -width Ds -compact +.It Xo +.Ar prefix_interface_statement +.Xc +specifies the client's local configuration of how delegated prefixes +should be used +.Pq see below . +.It Ic prefix Ar ipv6-prefix pltime Op Ar vltime ; +specifies a prefix and related parameters that the client wants to be +delegated. +Multiple prefixes can be specified, each of which is described as a +separate +.Ic prefix +substatement. +.Nm dhcp6c +will include all the prefixes +.Pq and related parameters +in Solicit messages, +as an IA_PD prefix option encapsulated in the corresponding IA_PD +option. +Note, however, that the server may or may not respect the specified +prefix parameters. +For parameters of the +.Ic prefix +substatement, +see +.Xr dhcp6s.conf 5 . +.El +.El +.\" +.Sh Prefix interface statement +A prefix interface statement specifies configuration parameters of +prefixes on local interfaces that are derived from delegated prefixes. +A prefix interface statement can only appear as a substatement of +an identity association statement with the type +.Ic pd . +The generic format of an interface statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic prefix-interface Ar interface +{ +.Ar substatements +}; +.Xc +When an IPv6 prefix is delegated from a DHCPv6 server, +.Nm dhcp6c +will assign a prefix on the +.Ar interface +unless the interface receives the DHCPv6 message that contains the prefix +with the delegated prefix and the parameters provided in +.Ar substatements . +Possible substatements are as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic sla-id Ar ID +; +.Xc +This statement specifies the identifier value of the site-level aggregator +.Pq SLA +on the interface. +.Ar ID +must be a decimal integer which fits in the length of SLA IDs +.Pq see below . +For example, +if +.Ar ID +is 1 and the client is delegated an IPv6 prefix 2001:db8:ffff::/48, +.Nm dhcp6c +will combine the two values into a single IPv6 prefix, +2001:db8:ffff:1::/64, +and will configure the prefix on the specified +.Ar interface . +.It Xo +.Ic sla-len Ar length +; +.Xc +This statement specifies the length of the SLA ID in bits. +.Ar length +must be a decimal number between 0 and 128. +If the length is not specified by this statement, +the default value 16 will be used. +.El +.El +.\" +.Sh Authentication statement +An authentication statement defines a set of authentication parameters +used in DHCPv6 exchanges with the server(s). +The format of an authentication statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic authentication Ar authname +{ +.Ar substatements +}; +.Xc +.Ar authname +is a string which is unique among all authentication statements in the +configuration file. +It will specify a particular set of authentication parameters when +.Ic authentication +option is specified in the +.Ic interface +statement. +Possible substatements of the +.Ic authentication +statement are as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic protocol Ar authprotocol +; +.Xc +specifies the authentication protocol. +Currently, the only available protocol as +.Ar authprotocol +is +.Ic delayed , +which means the DHCPv6 delayed authentication protocol. +.It Xo +.Ic algorithm Ar authalgorithm +; +.Xc +specifies the algorithm for this authentication. +Currently, the only available algorithm is HMAC-MD5, +which can be specified as one of the followings: +.Ic hmac-md5 , +.Ic HMAC-MD5 , +.Ic hmacmd5 , +or +.Ic HMACMD5 . +This substatement can be omitted. +In this case, +HMAC-MD5 will be used as the algorithm. +.It Xo +.Ic rdm Ar replay-detection-method +; +.Xc +specifies the replay protection method for this authentication. +Currently, the only available method is +.Ic monocounter , +which means the use of a monotonically increasing counter. +If this method is specified, +.Ic dhcp6c +will use an NTP-format timestamp when it authenticates the message. +This substatement can be omitted, +in which case +.Ic monocounter +will be used as the method. +.El +.El +.\" +.Sh Keyinfo statement +A keyinfo statement defines a secret key shared with the server(s) +to authenticate DHCPv6 messages. +The format of a keyinfo statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic keyinfo Ar keyname +{ +.Ar substatements +}; +.Xc +.Ar keyname +is an arbitrary string. +It does not affect client's behavior but is provided for readability +of log messages. +Possible substatements of the +.Ic keyinfo +statement are as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic realm Ar \(dqrealmname\(dq +; +.Xc +specifies the DHCP realm. +.Ar realmname +is an arbitrary string, +but is typically expected to be a domain name like \(dqkame.net\(dq . +.It Xo +.Ic keyid Ar ID +; +.Xc +specifies the key identifier, +.Ar ID , +as a decimal number. +A secret key is uniquely identified within the client by the DHCP +realm and the key identifier. +.It Xo +.Ic secret Ar \(dqsecret-value\(dq +; +.Xc +specifies the shared secret of this key. +.Ar \(dqsecret-value\(dq +is a base-64 encoded string of the secret. +.It Xo +.Ic expire Ar \(dqexpiration-time\(dq +; +.Xc +specifies the expiration time of this key. +.Ar \(dqexpiration-time\(dq +should be formatted in one of the followings: +.Ar yyyy-mm-dd HH:MM , +.Ar mm-dd HH:MM , +or +.Ar HH:MM , +where +.Ar yyyy +is the year with century (e.g., 2004), +.Ar mm +is the month, +.Ar dd +is the day of the month, +.Ar HH +is the hour of 24-hour clock, +and +.Ar MM +is the minute, +each of which is given as a decimal number. +Additionally, +a special keyword +.Ic forever +can be specified as +.Ar expiration-time , +which means the key has an infinite lifetime and never expires. +This substatement can be omitted, +in which case +.Ic forever +will be used by default. +.El +.El +.\" +.Sh Examples +The followings are a sample configuration to be delegated an IPv6 +prefix from an upstream service provider. +With this configuration +.Nm dhcp6c +will send solicit messages containing an IA_PD option, +with an IAID 0, +on to an upstream PPP link, +.Ar ppp0 . +After receiving some prefixes from a server, +.Nm dhcp6c +will then configure derived IPv6 prefixes with the SLA ID 1 on a +local ethernet interface, +.Ar ne0 . +Note that the IAID for the +.Ic id-assoc +statement is 0 according to the default. +.Bd -literal -offset +interface ppp0 { + send ia-pd 0; +}; + +id-assoc pd { + prefix-interface ne0 { + sla-id 1; + }; +}; +.Ed +.Pp +If a shared secret should be configured in both the client and the +server for DHCPv6 authentication, +it would be specified in the configuration file as follows: +.Bd -literal -offset +keyinfo kame-key { + realm "kame.net"; + keyid 1; + secret "5pvW2g48OHPvkYMJSw0vZA=="; +}; +.Ed +.Pp +One easy way of generating a new secret in the base64 format is to +execute the +.Xr openssl 1 +command (when available) as follows, +.Bd -literal -offset +% openssl rand -base64 16 +.Ed +.Pp +and copy the output to the +.Nm dhcp6c.conf +file. +.Pp +To include an authentication option for DHCPv6 authentication, +the +.Ic interface +statement should be modified and an +.Ic authentication +statement should be added as follows: +.Bd -literal -offset +interface ppp0 { + send ia-pd 0; + send authentication kame; +}; + +authentication kame { + protocol delayed; +}; +.Ed +.Pp +.Bd -literal -offset +interface fxp0 { + send ia-na 0; +}; +.Ed +.Sh SEE ALSO +.Xr dhcp6s.conf 5 +.Xr dhcp6c 8 +.\" +.Sh HISTORY +The +.Nm +configuration file first appeared in the WIDE/KAME IPv6 protocol +stack kit. diff --git a/dhcp6c.conf.sample b/dhcp6c.conf.sample new file mode 100644 index 0000000..3aeede1 --- /dev/null +++ b/dhcp6c.conf.sample @@ -0,0 +1,24 @@ +# The followings are a sample configuration for requiring the "stateless" +# DHCPv6 service. +interface ne0 { + information-only; +}; + + +# The followings are a sample configuration to be delegated an IPv6 prefix +# from an upstream service provider. With this configuration dhcp6c will +# send solicit messages containing an IA_PD option, with an IAID 0, on to +# an upstream PPP link, ppp0. After receiving some prefixes from a server, +# dhcp6c will then configure derived IPv6 prefixes with the SLA ID 1 on a +# local ethernet interface, ne0. Note that the IAID for the id-assoc +# statement is 0 according to the default. + +interface ppp0 { + send ia-pd 0; +}; + +id-assoc pd { + prefix-interface ne0 { + sla-id 1; + }; +}; diff --git a/dhcp6c.h b/dhcp6c.h new file mode 100644 index 0000000..a90ce2e --- /dev/null +++ b/dhcp6c.h @@ -0,0 +1,37 @@ +/* $KAME: dhcp6c.h,v 1.6 2004/09/04 09:26:38 jinmei Exp $ */ + +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#define DHCP6C_CONF SYSCONFDIR "/dhcp6c.conf" +#define DHCP6C_PIDFILE "/var/run/dhcp6c.pid" +#define DUID_FILE LOCALDBDIR "/dhcp6c_duid" + +extern struct dhcp6_timer *client6_timo __P((void *)); +extern int client6_start __P((struct dhcp6_if *)); +extern void client6_send __P((struct dhcp6_event *)); diff --git a/dhcp6c_ia.c b/dhcp6c_ia.c new file mode 100644 index 0000000..6877c20 --- /dev/null +++ b/dhcp6c_ia.c @@ -0,0 +1,772 @@ +/* $KAME: dhcp6c_ia.c,v 1.33 2005/07/22 08:50:05 jinmei Exp $ */ + +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "timer.h" +#include "dhcp6c.h" +#include "dhcp6c_ia.h" +#include "prefixconf.h" +#include "addrconf.h" + +typedef enum {IAS_ACTIVE, IAS_RENEW, IAS_REBIND} iastate_t; + +struct ia { + TAILQ_ENTRY(ia) link; + + /* back pointer to configuration */ + struct ia_conf *conf; + + /* common parameters of IA */ + u_int32_t t1; /* duration for renewal */ + u_int32_t t2; /* duration for rebind */ + + /* internal parameters for renewal/rebinding */ + iastate_t state; + struct dhcp6_timer *timer; + struct dhcp6_eventdata *evdata; + + /* DHCP related parameters */ + struct dhcp6_if *ifp; /* DHCP interface */ + struct duid serverid; /* the server ID that provided this IA */ + + /* control information shared with each particular config routine */ + struct iactl *ctl; + + /* authentication parameters for transaction with servers on this IA */ + struct authparam *authparam; +}; + +static int update_authparam __P((struct ia *, struct authparam *)); +static void reestablish_ia __P((struct ia *)); +static void callback __P((struct ia *)); +static int release_ia __P((struct ia *)); +static void remove_ia __P((struct ia *)); +static struct ia *get_ia __P((iatype_t, struct dhcp6_if *, struct ia_conf *, + struct dhcp6_listval *, struct duid *)); +static struct ia *find_ia __P((struct ia_conf *, iatype_t, u_int32_t)); +static struct dhcp6_timer *ia_timo __P((void *)); + +static char *iastr __P((iatype_t)); +static char *statestr __P((iastate_t)); + +void +update_ia(iatype, ialist, ifp, serverid, authparam) + iatype_t iatype; + struct dhcp6_list *ialist; + struct dhcp6_if *ifp; + struct duid *serverid; + struct authparam *authparam; +{ + struct ia *ia; + struct ia_conf *iac; + struct iapd_conf *iapdc; + struct iana_conf *ianac; + struct dhcp6_listval *iav, *siav; + struct timeval timo; + + for (iav = TAILQ_FIRST(ialist); iav; iav = TAILQ_NEXT(iav, link)) { + /* if we're not interested in this IA, ignore it. */ + if ((iac = find_iaconf(&ifp->iaconf_list, iatype, + iav->val_ia.iaid)) == NULL) { + continue; + } + + /* validate parameters */ + /* + * If a client receives an IA_NA with T1 greater than T2, and + * both T1 and T2 are greater than 0, the client discards the + * IA_NA option and processes the remainder of the message as + * though the server had not included the invalid IA_NA option. + * [RFC3315 22.4] + * We apply the same rule to IA_PD as well. + */ + if (iav->val_ia.t2 != 0 && iav->val_ia.t1 > iav->val_ia.t2) { + dprintf(LOG_INFO, FNAME, + "invalid IA: T1(%lu) > T2(%lu)", + iav->val_ia.t1, iav->val_ia.t2); + continue; + } + + /* locate the local IA or make a new one */ + ia = get_ia(iatype, ifp, iac, iav, serverid); + if (ia == NULL) { + dprintf(LOG_WARNING, FNAME, "failed to get an IA " + "type: %s, ID: %u", iastr(iac->type), iac->iaid); + continue; + } + + /* update authentication parameters */ + if (update_authparam(ia, authparam)) { + dprintf(LOG_WARNING, FNAME, "failed to update " + "authentication param for IA " + "type: %s, ID: %u", iastr(iac->type), iac->iaid); + remove_ia(ia); + continue; + } + + /* update IA configuration information */ + for (siav = TAILQ_FIRST(&iav->sublist); siav; + siav = TAILQ_NEXT(siav, link)) { + switch (siav->type) { + case DHCP6_LISTVAL_PREFIX6: + /* add or update the prefix */ + iapdc = (struct iapd_conf *)iac; + if (update_prefix(ia, &siav->val_prefix6, + &iapdc->iapd_pif_list, ifp, &ia->ctl, + callback)) { + dprintf(LOG_NOTICE, FNAME, + "failed to update a prefix %s/%d", + in6addr2str(&siav->val_prefix6.addr, 0), + siav->val_prefix6.plen); + } + break; + case DHCP6_LISTVAL_STATEFULADDR6: + ianac = (struct iana_conf *)iac; + if (update_address(ia, &siav->val_statefuladdr6, + ifp, &ia->ctl, callback)) { + dprintf(LOG_NOTICE, FNAME, + "failed to update an address %s", + in6addr2str(&siav->val_statefuladdr6.addr, 0)); + } + break; + case DHCP6_LISTVAL_STCODE: + dprintf(LOG_INFO, FNAME, + "status code for %s-%lu: %s", + iastr(iatype), iav->val_ia.iaid, + dhcp6_stcodestr(siav->val_num16)); + if ((ia->state == IAS_RENEW || + ia->state == IAS_REBIND) && + siav->val_num16 == DH6OPT_STCODE_NOBINDING) { + /* + * For each IA in the original Renew or + * Rebind message, the client + * sends a Request message if the IA + * contained a Status Code option + * with the NoBinding status. + * [RFC3315 18.1.8] + * XXX: what about the PD case? + */ + dprintf(LOG_INFO, FNAME, + "receive NoBinding against " + "renew/rebind for %s-%lu", + iastr(ia->conf->type), + ia->conf->iaid); + reestablish_ia(ia); + goto nextia; + } + break; + default: + dprintf(LOG_ERR, FNAME, "impossible case"); + goto nextia; + } + } + + /* see if this IA is still valid. if not, remove it. */ + if (ia->ctl == NULL || !(*ia->ctl->isvalid)(ia->ctl)) { + dprintf(LOG_DEBUG, FNAME, "IA %s-%lu is invalidated", + iastr(ia->conf->type), ia->conf->iaid); + remove_ia(ia); + continue; + } + + /* if T1 or T2 is 0, determine appropriate values locally. */ + if (ia->t1 == 0 || ia->t2 == 0) { + u_int32_t duration; + + if (ia->ctl && ia->ctl->duration) + duration = (*ia->ctl->duration)(ia->ctl); + else + duration = 1800; /* 30min. XXX: no rationale */ + + if (ia->t1 == 0) { + if (duration == DHCP6_DURATION_INFINITE) + ia->t1 = DHCP6_DURATION_INFINITE; + else + ia->t1 = duration / 2; + } + if (ia->t2 == 0) { + if (duration == DHCP6_DURATION_INFINITE) + ia->t2 = DHCP6_DURATION_INFINITE; + else + ia->t2 = duration * 4 / 5; + } + + /* make sure T1 <= T2 */ + if (ia->t1 > ia->t2) + ia->t1 = ia->t2 * 5 / 8; + + dprintf(LOG_INFO, FNAME, "T1(%lu) and/or T2(%lu) " + "is locally determined", ia->t1, ia->t2); + } + + /* + * Be proactive for too-small timeout values. Note that + * the adjusted values may make some information expire + * without renewal. + */ + if (ia->t2 < DHCP6_DURATION_MIN) { + dprintf(LOG_INFO, FNAME, "T1 (%lu) or T2 (%lu) " + "is too small", ia->t1, ia->t2); + ia->t2 = DHCP6_DURATION_MIN; + ia->t1 = ia->t2 * 5 / 8; + dprintf(LOG_INFO, "", " adjusted to %lu and %lu", + ia->t1, ia->t2); + } + + /* set up a timer for this IA. */ + if (ia->t1 == DHCP6_DURATION_INFINITE) { + if (ia->timer) + dhcp6_remove_timer(&ia->timer); + } else { + if (ia->timer == NULL) + ia->timer = dhcp6_add_timer(ia_timo, ia); + if (ia->timer == NULL) { + dprintf(LOG_ERR, FNAME, + "failed to add IA timer"); + remove_ia(ia); /* XXX */ + continue; + } + timo.tv_sec = ia->t1; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, ia->timer); + } + + ia->state = IAS_ACTIVE; + + nextia: + ; + } +} + +static int +update_authparam(ia, authparam) + struct ia *ia; + struct authparam *authparam; +{ + if (authparam == NULL) + return (0); + + if (ia->authparam == NULL) { + if ((ia->authparam = copy_authparam(authparam)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to copy authparam"); + return (-1); + } + return (0); + } + + /* update the previous RD value and flags */ + ia->authparam->prevrd = authparam->prevrd; + ia->authparam->flags = authparam->flags; + + return (0); +} + +static void +reestablish_ia(ia) + struct ia *ia; +{ + struct dhcp6_ia iaparam; + struct dhcp6_event *ev; + struct dhcp6_eventdata *evd; + + dprintf(LOG_DEBUG, FNAME, "re-establishing IA: %s-%lu", + iastr(ia->conf->type), ia->conf->iaid); + + if (ia->state != IAS_RENEW && ia->state != IAS_REBIND) { + dprintf(LOG_ERR, FNAME, "internal error (invalid IA status)"); + exit(1); /* XXX */ + } + + /* cancel the current event for the prefix. */ + if (ia->evdata) { + TAILQ_REMOVE(&ia->evdata->event->data_list, ia->evdata, link); + if (ia->evdata->destructor) + ia->evdata->destructor(ia->evdata); + free(ia->evdata); + ia->evdata = NULL; + } + + /* we don't need a timer for the IA (see comments in ia_timo()) */ + if (ia->timer) + dhcp6_remove_timer(&ia->timer); + + if ((ev = dhcp6_create_event(ia->ifp, DHCP6S_REQUEST)) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to create a new event"); + goto fail; + } + TAILQ_INSERT_TAIL(&ia->ifp->event_list, ev, link); + + if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event timer"); + goto fail; + } + + if ((evd = malloc(sizeof(*evd))) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event data"); + goto fail; + } + memset(evd, 0, sizeof(*evd)); + evd->event = ev; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + + if (duidcpy(&ev->serverid, &ia->serverid)) { + dprintf(LOG_NOTICE, FNAME, "failed to copy server ID"); + goto fail; + } + + iaparam.iaid = ia->conf->iaid; + iaparam.t1 = ia->t1; + iaparam.t2 = ia->t2; + + if (ia->ctl && ia->ctl->reestablish_data) { + if ((*ia->ctl->reestablish_data)(ia->ctl, &iaparam, + &ia->evdata, evd)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make reestablish data"); + goto fail; + } + } + + if (ia->authparam != NULL) { + if ((ev->authparam = copy_authparam(ia->authparam)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to copy authparam"); + goto fail; + } + } + + ev->timeouts = 0; + dhcp6_set_timeoparam(ev); + dhcp6_reset_timer(ev); + + ia->evdata = evd; + + client6_send(ev); + + return; + + fail: + if (ev) + dhcp6_remove_event(ev); + + return; +} + +static void +callback(ia) + struct ia *ia; +{ + /* see if this IA is still valid. if not, remove it. */ + if (ia->ctl == NULL || !(*ia->ctl->isvalid)(ia->ctl)) { + dprintf(LOG_DEBUG, FNAME, "IA %s-%lu is invalidated", + iastr(ia->conf->type), ia->conf->iaid); + remove_ia(ia); + } +} + +void +release_all_ia(ifp) + struct dhcp6_if *ifp; +{ + struct ia_conf *iac; + struct ia *ia, *ia_next; + + for (iac = TAILQ_FIRST(&ifp->iaconf_list); iac; + iac = TAILQ_NEXT(iac, link)) { + for (ia = TAILQ_FIRST(&iac->iadata); ia; ia = ia_next) { + ia_next = TAILQ_NEXT(ia, link); + + (void)release_ia(ia); + + /* + * The client MUST stop using all of the addresses + * being released as soon as the client begins the + * Release message exchange process. + * [RFC3315 Section 18.1.6] + */ + remove_ia(ia); + } + } +} + +static int +release_ia(ia) + struct ia *ia; +{ + struct dhcp6_ia iaparam; + struct dhcp6_event *ev; + struct dhcp6_eventdata *evd; + + dprintf(LOG_DEBUG, FNAME, "release an IA: %s-%lu", + iastr(ia->conf->type), ia->conf->iaid); + + if ((ev = dhcp6_create_event(ia->ifp, DHCP6S_RELEASE)) + == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to create a new event"); + goto fail; + } + TAILQ_INSERT_TAIL(&ia->ifp->event_list, ev, link); + + + if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event timer"); + goto fail; + } + + if (duidcpy(&ev->serverid, &ia->serverid)) { + dprintf(LOG_NOTICE, FNAME, "failed to copy server ID"); + goto fail; + } + + if ((evd = malloc(sizeof(*evd))) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event data"); + goto fail; + } + memset(evd, 0, sizeof(*evd)); + iaparam.iaid = ia->conf->iaid; + /* XXX: should we set T1/T2 to 0? spec is silent on this. */ + iaparam.t1 = ia->t1; + iaparam.t2 = ia->t2; + + if (ia->ctl && ia->ctl->release_data) { + if ((*ia->ctl->release_data)(ia->ctl, &iaparam, NULL, evd)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make release data"); + goto fail; + } + } + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + + ev->timeouts = 0; + dhcp6_set_timeoparam(ev); + dhcp6_reset_timer(ev); + + if (ia->authparam != NULL) { + if ((ev->authparam = copy_authparam(ia->authparam)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to copy authparam"); + goto fail; + } + } + + client6_send(ev); + + return (0); + + fail: + if (ev) + dhcp6_remove_event(ev); + + return (-1); +} + +static void +remove_ia(ia) + struct ia *ia; +{ + struct ia_conf *iac = ia->conf; + struct dhcp6_if *ifp = ia->ifp; + + dprintf(LOG_DEBUG, FNAME, "remove an IA: %s-%lu", + iastr(ia->conf->type), ia->conf->iaid); + + TAILQ_REMOVE(&iac->iadata, ia, link); + + duidfree(&ia->serverid); + + if (ia->timer) + dhcp6_remove_timer(&ia->timer); + + if (ia->evdata) { + TAILQ_REMOVE(&ia->evdata->event->data_list, ia->evdata, link); + if (ia->evdata->destructor) + ia->evdata->destructor(ia->evdata); + free(ia->evdata); + ia->evdata = NULL; + } + + if (ia->ctl && ia->ctl->cleanup) + (*ia->ctl->cleanup)(ia->ctl); + + if (ia->authparam != NULL) + free(ia->authparam); + + free(ia); + + (void)client6_start(ifp); +} + +static struct dhcp6_timer * +ia_timo(arg) + void *arg; +{ + struct ia *ia = (struct ia *)arg; + struct dhcp6_ia iaparam; + struct dhcp6_event *ev; + struct dhcp6_eventdata *evd; + struct timeval timo; + int dhcpstate; + + dprintf(LOG_DEBUG, FNAME, "IA timeout for %s-%lu, state=%s", + iastr(ia->conf->type), ia->conf->iaid, statestr(ia->state)); + + /* cancel the current event for the prefix. */ + if (ia->evdata) { + TAILQ_REMOVE(&ia->evdata->event->data_list, ia->evdata, link); + if (ia->evdata->destructor) + ia->evdata->destructor(ia->evdata); + free(ia->evdata); + ia->evdata = NULL; + } + + switch (ia->state) { + case IAS_ACTIVE: + ia->state = IAS_RENEW; + dhcpstate = DHCP6S_RENEW; + timo.tv_sec = ia->t1 < ia->t2 ? ia->t2 - ia->t1 : 0; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, ia->timer); + break; + case IAS_RENEW: + ia->state = IAS_REBIND; + dhcpstate = DHCP6S_REBIND; + + /* + * We need keep DUID for sending Release in this state. + * But we don't need a timer for the IA. We'll just wait for a + * reply for the REBIND until all associated configuration + * parameters for this IA expire. + */ + dhcp6_remove_timer(&ia->timer); + break; + default: + dprintf(LOG_ERR, FNAME, "invalid IA state (%d)", + (int)ia->state); + return (NULL); /* XXX */ + } + + if ((ev = dhcp6_create_event(ia->ifp, dhcpstate)) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to create a new event"); + goto fail; + } + TAILQ_INSERT_TAIL(&ia->ifp->event_list, ev, link); + + if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event timer"); + goto fail; + } + + if ((evd = malloc(sizeof(*evd))) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to create a new event data"); + goto fail; + } + memset(evd, 0, sizeof(*evd)); + evd->event = ev; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + + if (ia->state == IAS_RENEW) { + if (duidcpy(&ev->serverid, &ia->serverid)) { + dprintf(LOG_NOTICE, FNAME, "failed to copy server ID"); + goto fail; + } + } + + iaparam.iaid = ia->conf->iaid; + iaparam.t1 = ia->t1; + iaparam.t2 = ia->t2; + switch(ia->state) { + case IAS_RENEW: + if (ia->ctl && ia->ctl->renew_data) { + if ((*ia->ctl->renew_data)(ia->ctl, &iaparam, + &ia->evdata, evd)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make renew data"); + goto fail; + } + } + break; + case IAS_REBIND: + if (ia->ctl && ia->ctl->rebind_data) { + if ((*ia->ctl->rebind_data)(ia->ctl, &iaparam, + &ia->evdata, evd)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make rebind data"); + goto fail; + } + } + break; + default: + break; + } + + ev->timeouts = 0; + dhcp6_set_timeoparam(ev); + dhcp6_reset_timer(ev); + + if (ia->authparam != NULL) { + if ((ev->authparam = copy_authparam(ia->authparam)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to copy authparam"); + goto fail; + } + } + + ia->evdata = evd; + + switch(ia->state) { + case IAS_RENEW: + case IAS_REBIND: + client6_send(ev); + break; + case IAS_ACTIVE: + /* what to do? */ + break; + } + + return (ia->timer); + + fail: + if (ev) + dhcp6_remove_event(ev); + + return (NULL); +} + +static struct ia * +get_ia(type, ifp, iac, iaparam, serverid) + iatype_t type; + struct dhcp6_if *ifp; + struct ia_conf *iac; + struct dhcp6_listval *iaparam; + struct duid *serverid; +{ + struct ia *ia; + struct duid newserver; + int create = 0; + + if (duidcpy(&newserver, serverid)) { + dprintf(LOG_NOTICE, FNAME, "failed to copy server ID"); + return (NULL); + } + + if ((ia = find_ia(iac, type, iaparam->val_ia.iaid)) == NULL) { + if ((ia = malloc(sizeof(*ia))) == NULL) { + dprintf(LOG_NOTICE, FNAME, "memory allocation failed"); + duidfree(&newserver); /* XXX */ + return (NULL); + } + memset(ia, 0, sizeof(*ia)); + ia->state = IAS_ACTIVE; + + TAILQ_INSERT_TAIL(&iac->iadata, ia, link); + ia->conf = iac; + + create = 1; + } else + duidfree(&ia->serverid); + + ia->t1 = iaparam->val_ia.t1; + ia->t2 = iaparam->val_ia.t2; + ia->ifp = ifp; + ia->serverid = newserver; + + dprintf(LOG_DEBUG, FNAME, "%s an IA: %s-%lu", + create ? "make" : "update", iastr(type), ia->conf->iaid); + + return (ia); +} + +static struct ia * +find_ia(iac, type, iaid) + struct ia_conf *iac; + iatype_t type; + u_int32_t iaid; +{ + struct ia *ia; + + for (ia = TAILQ_FIRST(&iac->iadata); ia; + ia = TAILQ_NEXT(ia, link)) { + if (ia->conf->type == type && ia->conf->iaid == iaid) + return (ia); + } + + return (NULL); +} + +static char * +iastr(type) + iatype_t type; +{ + switch (type) { + case IATYPE_PD: + return ("PD"); + case IATYPE_NA: + return ("NA"); + default: + return ("???"); /* should be a bug */ + } +} + +static char * +statestr(state) + iastate_t state; +{ + switch (state) { + case IAS_ACTIVE: + return "ACTIVE"; + case IAS_RENEW: + return "RENEW"; + case IAS_REBIND: + return "REBIND"; + default: + return "???"; /* should be a bug */ + } +} diff --git a/dhcp6c_ia.h b/dhcp6c_ia.h new file mode 100644 index 0000000..af328db --- /dev/null +++ b/dhcp6c_ia.h @@ -0,0 +1,56 @@ +/* $KAME: dhcp6c_ia.h,v 1.6 2004/06/10 07:28:29 jinmei Exp $ */ + +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +struct ia; /* this is an opaque type */ + +struct iactl { + struct ia *iactl_ia; /* back pointer to IA */ + + /* callback function called when something may happen on the IA */ + void (*callback) __P((struct ia *)); + + /* common methods: */ + int (*isvalid) __P((struct iactl *)); + u_int32_t (*duration) __P((struct iactl *)); + int (*renew_data) __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); + int (*rebind_data) __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); + int (*release_data) __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); + int (*reestablish_data) __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); + void (*cleanup) __P((struct iactl *)); +}; + +extern void update_ia __P((iatype_t, struct dhcp6_list *, + struct dhcp6_if *, struct duid *, struct authparam *)); +extern void release_all_ia __P((struct dhcp6_if *)); diff --git a/dhcp6c_script.c b/dhcp6c_script.c new file mode 100644 index 0000000..05ccf8c --- /dev/null +++ b/dhcp6c_script.c @@ -0,0 +1,437 @@ +/* $KAME: dhcp6c_script.c,v 1.11 2004/11/28 10:48:38 jinmei Exp $ */ + +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +static char sipserver_str[] = "new_sip_servers"; +static char sipname_str[] = "new_sip_name"; +static char dnsserver_str[] = "new_domain_name_servers"; +static char dnsname_str[] = "new_domain_name"; +static char ntpserver_str[] = "new_ntp_servers"; +static char nisserver_str[] = "new_nis_servers"; +static char nisname_str[] = "new_nis_name"; +static char nispserver_str[] = "new_nisp_servers"; +static char nispname_str[] = "new_nisp_name"; +static char bcmcsserver_str[] = "new_bcmcs_servers"; +static char bcmcsname_str[] = "new_bcmcs_name"; + +int +client6_script(scriptpath, state, optinfo) + char *scriptpath; + int state; + struct dhcp6_optinfo *optinfo; +{ + int i, dnsservers, ntpservers, dnsnamelen, envc, elen, ret = 0; + int sipservers, sipnamelen; + int nisservers, nisnamelen; + int nispservers, nispnamelen; + int bcmcsservers, bcmcsnamelen; + char **envp, *s; + char reason[] = "REASON=NBI"; + struct dhcp6_listval *v; + pid_t pid, wpid; + + /* if a script is not specified, do nothing */ + if (scriptpath == NULL || strlen(scriptpath) == 0) + return -1; + + /* initialize counters */ + dnsservers = 0; + ntpservers = 0; + dnsnamelen = 0; + sipservers = 0; + sipnamelen = 0; + nisservers = 0; + nisnamelen = 0; + nispservers = 0; + nispnamelen = 0; + bcmcsservers = 0; + bcmcsnamelen = 0; + envc = 2; /* we at least include the reason and the terminator */ + + /* count the number of variables */ + for (v = TAILQ_FIRST(&optinfo->dns_list); v; v = TAILQ_NEXT(v, link)) + dnsservers++; + envc += dnsservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->dnsname_list); v; + v = TAILQ_NEXT(v, link)) { + dnsnamelen += v->val_vbuf.dv_len; + } + envc += dnsnamelen ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->ntp_list); v; v = TAILQ_NEXT(v, link)) + ntpservers++; + envc += ntpservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->sip_list); v; v = TAILQ_NEXT(v, link)) + sipservers++; + envc += sipservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->sipname_list); v; + v = TAILQ_NEXT(v, link)) { + sipnamelen += v->val_vbuf.dv_len; + } + envc += sipnamelen ? 1 : 0; + + for (v = TAILQ_FIRST(&optinfo->nis_list); v; v = TAILQ_NEXT(v, link)) + nisservers++; + envc += nisservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->nisname_list); v; + v = TAILQ_NEXT(v, link)) { + nisnamelen += v->val_vbuf.dv_len; + } + envc += nisnamelen ? 1 : 0; + + for (v = TAILQ_FIRST(&optinfo->nisp_list); v; v = TAILQ_NEXT(v, link)) + nispservers++; + envc += nispservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->nispname_list); v; + v = TAILQ_NEXT(v, link)) { + nispnamelen += v->val_vbuf.dv_len; + } + envc += nispnamelen ? 1 : 0; + + for (v = TAILQ_FIRST(&optinfo->bcmcs_list); v; v = TAILQ_NEXT(v, link)) + bcmcsservers++; + envc += bcmcsservers ? 1 : 0; + for (v = TAILQ_FIRST(&optinfo->bcmcsname_list); v; + v = TAILQ_NEXT(v, link)) { + bcmcsnamelen += v->val_vbuf.dv_len; + } + envc += bcmcsnamelen ? 1 : 0; + + /* allocate an environments array */ + if ((envp = malloc(sizeof (char *) * envc)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate environment buffer"); + return -1; + } + memset(envp, 0, sizeof (char *) * envc); + + /* + * Copy the parameters as environment variables + */ + i = 0; + /* reason */ + if ((envp[i++] = strdup(reason)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate reason strings"); + ret = -1; + goto clean; + } + /* "var=addr1 addr2 ... addrN" + null char for termination */ + if (dnsservers) { + elen = sizeof (dnsserver_str) + + (INET6_ADDRSTRLEN + 1) * dnsservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for DNS servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", dnsserver_str); + for (v = TAILQ_FIRST(&optinfo->dns_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (ntpservers) { + elen = sizeof (ntpserver_str) + + (INET6_ADDRSTRLEN + 1) * ntpservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for NTP servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", ntpserver_str); + for (v = TAILQ_FIRST(&optinfo->ntp_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + + if (dnsnamelen) { + elen = sizeof (dnsname_str) + dnsnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for DNS name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", dnsname_str); + for (v = TAILQ_FIRST(&optinfo->dnsname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + + if (sipservers) { + elen = sizeof (sipserver_str) + + (INET6_ADDRSTRLEN + 1) * sipservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for SIP servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", sipserver_str); + for (v = TAILQ_FIRST(&optinfo->sip_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (sipnamelen) { + elen = sizeof (sipname_str) + sipnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for SIP domain name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", sipname_str); + for (v = TAILQ_FIRST(&optinfo->sipname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + + if (nisservers) { + elen = sizeof (nisserver_str) + + (INET6_ADDRSTRLEN + 1) * nisservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for NIS servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", nisserver_str); + for (v = TAILQ_FIRST(&optinfo->nis_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (nisnamelen) { + elen = sizeof (nisname_str) + nisnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for NIS domain name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", nisname_str); + for (v = TAILQ_FIRST(&optinfo->nisname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + + if (nispservers) { + elen = sizeof (nispserver_str) + + (INET6_ADDRSTRLEN + 1) * nispservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for NIS+ servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", nispserver_str); + for (v = TAILQ_FIRST(&optinfo->nisp_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (nispnamelen) { + elen = sizeof (nispname_str) + nispnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for NIS+ domain name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", nispname_str); + for (v = TAILQ_FIRST(&optinfo->nispname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + + if (bcmcsservers) { + elen = sizeof (bcmcsserver_str) + + (INET6_ADDRSTRLEN + 1) * bcmcsservers + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for BCMC servers"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", bcmcsserver_str); + for (v = TAILQ_FIRST(&optinfo->bcmcs_list); v; + v = TAILQ_NEXT(v, link)) { + char *addr; + + addr = in6addr2str(&v->val_addr6, 0); + strlcat(s, addr, elen); + strlcat(s, " ", elen); + } + } + if (bcmcsnamelen) { + elen = sizeof (bcmcsname_str) + bcmcsnamelen + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate strings for BCMC domain name"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=", bcmcsname_str); + for (v = TAILQ_FIRST(&optinfo->bcmcsname_list); v; + v = TAILQ_NEXT(v, link)) { + strlcat(s, v->val_vbuf.dv_buf, elen); + strlcat(s, " ", elen); + } + } + + /* launch the script */ + pid = fork(); + if (pid < 0) { + dprintf(LOG_ERR, FNAME, "failed to fork: %s", strerror(errno)); + ret = -1; + goto clean; + } else if (pid) { + int wstatus; + + do { + wpid = wait(&wstatus); + } while (wpid != pid && wpid > 0); + + if (wpid < 0) + dprintf(LOG_ERR, FNAME, "wait: %s", strerror(errno)); + else { + dprintf(LOG_DEBUG, FNAME, + "script \"%s\" terminated", scriptpath); + } + } else { + char *argv[2]; + int fd; + + argv[0] = scriptpath; + argv[1] = NULL; + + if (safefile(scriptpath)) { + dprintf(LOG_ERR, FNAME, + "script \"%s\" cannot be executed safely", + scriptpath); + exit(1); + } + + if (foreground == 0 && (fd = open("/dev/null", O_RDWR)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + + execve(scriptpath, argv, envp); + + dprintf(LOG_ERR, FNAME, "child: exec failed: %s", + strerror(errno)); + exit(0); + } + + clean: + for (i = 0; i < envc; i++) + free(envp[i]); + free(envp); + + return ret; +} diff --git a/dhcp6ctl.8 b/dhcp6ctl.8 new file mode 100644 index 0000000..86ea0e1 --- /dev/null +++ b/dhcp6ctl.8 @@ -0,0 +1,200 @@ +.\" $KAME: dhcp6ctl.8,v 1.4 2005/05/03 06:25:48 jinmei Exp $ +.\" +.\" Copyright (C) 2004 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd September 7, 2004 +.Dt DHCP6CTL 8 +.Os KAME +.Sh NAME +.Nm dhcp6ctl +.Nd DHCPv6 client and server control utility +.\" +.Sh SYNOPSIS +.Nm +.Op Fl C \(ba Fl S +.Op Fl k Ar keyfile +.Op Fl p Ar port +.Op Fl s Ar address +.Ic command +.\" +.Sh DESCRIPTION +.Nm +controls the operation of a DHCPv6 process, +which is either +.Nm dhcp6c +or +.Nm dhcp6s , +a DHCPv6 client or a server. +By default, +.Nm +controls a client. +The type of the process can also be specified explicitly by the +.Fl C +or +.Fl S +options. +.Pp +.Nm +communicates with the DHCPv6 process over a TCP connection, +sending commands authenticated with digital signatures. +Currently, +the only supported authentication algorithm is +HMAC-MD5, +which uses a shared secret on each end of the connection. +.Pp +Command line options are as below: +.Bl -tag -width indent +.\" +.It Fl C +Control a DHCPv6 client. +This option is exclusive with the +.Fl S +option. +.It Fl S +Control a DHCPv6 server. +This option is exclusive with the +.Fl C +option. +.It Fl k Ar keyfile +Use +.Ar keyfile +to provide the shared secret to communicate with the process. +The default file name used when unspecified is +.Pa /usr/local/etc/dhcp6cctlkey +with a client, +and +.Pa /usr/local/etc/dhcp6sctlkey +with a server. + +.It Fl p Ar port +Specify +.Ar port +as the listening port of the process. +The default port number used when unspecified is 5546 for a client, +and 5547 for a server. +.It Fl s Ar address +Specify +.A address +as the listening address of the process. +The default address used when unspecified is ::1. +.El +.\" +.Sh KEY FILE +Since the operation available with the +.Nm +command is powerful, +the communication between the command and +.Nm dhcp6c +or +.Nm dhcp6s +must be authenticated. +The supported algorithm for authentication is HMAC-MD5, +which requires a shared secret, +and the secret is stored in the key file. +The key file must consist of a single line, in which the secret value +is written in the form of BASE-64 encoding. +.\" +.Sh COMMANDS +Each +.Ic command +specifies a single control operation. +Supported commands are as follows: +.Pp +.Bl -tag -width Ds -compact +.It Xo +.Ic reload +.Xc +This command specifies the process to reload the configuration file. +Existing bindings, if any, are intact. +.It Xo +.Ic remove Ar arguments +.Xc +This command is only applicable to a server. +This specifies the server to remove a run-time object +specified by +.Ar arguments +from the server. +Currently, the only possible object is one particular IA_NA or IA_PD +binding, which is specified as +.Ql Ic binding IA Ic IA_NA Ar IAID Ar DUID +or +.Ql Ic binding IA Ic IA_PD Ar IAID Ar DUID +where +.Ar IAID +is a decimal number specifying the IAID of the IA, +and +.Ar DUID +is a DHCP Unique Identifier of the binding. +The format of +.Ar DUID +is the same as that specified in +.Xr dhcp6s.conf 5 . +.It Xo +.Ic start Ic interface Ar ifname +.Xc +This command is only applicable to a client. +It tells the client to release the current configuration information +(if any) on the interface +.Ar ifname +and restart the DHCPv6 configuration process on the interface. +.It Xo +.Ic stop Ic interface Ar ifname +.Xc +This command is only applicable to a client. +It tells the client to release the current configuration information +(if any) on the interface +.Ar ifname . +Any timer running for the interface will be stopped, +and no more DHCPv6 messages will be sent on the interface. +The configuration process can later be restarted by the +.Ic start +command. +.It Xo +.Ic stop +.Xc +This command stops the specified process. +If the process is a client, it will release all configuration +information (if any) and exits. +.El +.\" +.Sh FILES +.Bl -tag -width /usr/local/etc/dhcp6cctlkey -compact +.It Pa /usr/local/etc/dhcp6cctlkey +is the default key file to communicate with a client. +.It Pa /usr/local/etc/dhcp6sctlkey +is the default key file to communicate with a server. +.El +.\" +.Sh SEE ALSO +.Xr dhcp6s.conf 5 +.Xr dhcp6s 8 +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE/KAME IPv6 protocol stack kit. diff --git a/dhcp6relay.8 b/dhcp6relay.8 new file mode 100644 index 0000000..4f761b0 --- /dev/null +++ b/dhcp6relay.8 @@ -0,0 +1,127 @@ +.\" $KAME: dhcp6relay.8,v 1.19 2005/03/02 04:56:49 suz Exp $ +.\" +.\" Copyright (C) 2000 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd Apr 18, 2000 +.Dt DHCP6RELAY 8 +.Os KAME +.Sh NAME +.Nm dhcp6relay +.Nd DHCPv6 relay agent +.\" +.Sh SYNOPSIS +.Nm +.Op Fl Ddf +.Op Fl b Ar boundaddr +.Op Fl H Ar hoplim +.Op Fl r Ar relay-IF +.Op Fl s Ar serveraddr +.Op Fl S Ar script-file +.Op Fl p Ar pid-file +.Ar interface ... +.\" +.Sh DESCRIPTION +.Nm +acts as an intermediary to deliver DHCPv6 messages between clients and +servers, and is on the same link as a client. +.\" +.Nm +needs command line arguments +.Ar interface ... , +which specifies the list of links accommodating clients. +.Pp +Options supported by +.Nm +are: +.Bl -tag -width Ds +.It Fl d +Print debugging messages. +.It Fl D +Even more debugging information is printed. +.It Fl f +Foreground mode (useful when debugging). +Although +.Nm +usually prints warning, debugging, or error messages to +.Xr syslog 8 , +it prints the messages to standard error if this option is +specified. +.It Fl b Ar boundaddr +Specifies the source address to relay packets to servers (or other +agents). +.It Fl H Ar hoplim +Specifies the hop limit of DHCPv6 Solicit messages forwarded to +servers. +.It Fl r Ar relay-IF +Specifies the interface on which messages to servers are sent. +When omitted, the same interface as +.Ar interface +will be used. +When multiple +.Ar interface +are specified, this option cannot be omitted. +.It Fl s Ar serveraddr +Specifies the DHCPv6 server address to relay packets to. +If not specified, packets are relayed to ff05::1:3 (All DHCPv6 servers). +.It Fl S Ar script-file +Specifies the script file to be executed when +.Nm +receives a RELAY-REPLY message from a DHCPv6 server. Further detail of the +script file syntax is available in +.Xr dhcp6c 8 +.It Fl p Ar pid-file +Use +.Ar pid-file +to dump the process ID of +.Nm . +.El +.\" +.Sh FILES +.Bl -tag -width /var/run/dhcp6relay.pid -compact +.It Pa /var/run/dhcp6relay.pid +is the default file that contains pid of the currently running +.Nm . +.El + +.Sh SEE ALSO +.Xr dhcp6c 8 , +.Xr dhcp6s 8 +.Rs +.%A Ralph Droms, Editor +.%D 2003 +.%T Dynamic Host Configuration Protocol for IPv6 (DHCPv6) +.%R RFC 3315 +.Re +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE/KAME IPv6 protocol stack kit. +.\" +.Bl -enum +.El diff --git a/dhcp6relay.c b/dhcp6relay.c new file mode 100644 index 0000000..fb84ce3 --- /dev/null +++ b/dhcp6relay.c @@ -0,0 +1,1059 @@ +/* $KAME: dhcp6relay.c,v 1.62 2006/01/19 04:25:16 jinmei Exp $ */ +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#ifdef __FreeBSD__ +#include +#endif + +#include + +#ifdef __KAME__ +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include /* XXX: freebsd2 needs this for opt{arg,ind} */ +#include +#include +#include + +#include +#include +#include + +#define DHCP6RELAY_PIDFILE "/var/run/dhcp6relay.pid" +static char *pid_file = DHCP6RELAY_PIDFILE; + +static int ssock; /* socket for relaying to servers */ +static int csock; /* socket for clients */ +static int maxfd; /* maxi file descriptor for select(2) */ + +static int debug = 0; +static sig_atomic_t sig_flags = 0; +#define SIGF_TERM 0x1 + +static char *relaydevice; +static char *boundaddr; +static char *serveraddr = DH6ADDR_ALLSERVER; +static char *scriptpath; + +static char *rmsgctlbuf; +static socklen_t rmsgctllen; +static struct msghdr rmh; +static char rdatabuf[BUFSIZ]; +static int relayifid; + +static int mhops = DHCP6_RELAY_MULTICAST_HOPS; + +static struct sockaddr_in6 sa6_server, sa6_client; + +struct ifid_list { + TAILQ_ENTRY(ifid_list) ilink; + unsigned int ifid; +}; +TAILQ_HEAD(, ifid_list) ifid_list; +struct prefix_list { + TAILQ_ENTRY(prefix_list) plink; + struct sockaddr_in6 paddr; /* contains meaningless but enough members */ + int plen; +}; +TAILQ_HEAD(, prefix_list) global_prefixes; /* list of non-link-local prefixes */ +static char *global_strings[] = { + /* "fec0::/10", site-local unicast addresses were deprecated */ + "2000::/3", + "FC00::/7", /* Unique Local Address (RFC4193) */ + NULL +}; + +static void usage __P((void)); +static struct prefix_list *make_prefix __P((char *)); +static void relay6_init __P((int, char *[])); +static void relay6_loop __P((void)); +static void relay6_recv __P((int, int)); +static void process_signals __P((void)); +static void relay6_signal __P((int)); +static int make_msgcontrol __P((struct msghdr *, void *, socklen_t, + struct in6_pktinfo *, int)); +static void relay_to_server __P((struct dhcp6 *, ssize_t, + struct sockaddr_in6 *, char *, unsigned int)); +static void relay_to_client __P((struct dhcp6_relay *, ssize_t, + struct sockaddr *)); +extern int relay6_script __P((char *, struct sockaddr_in6 *, + struct dhcp6 *, int)); + + +static void +usage() +{ + fprintf(stderr, + "usage: dhcp6relay [-dDf] [-b boundaddr] [-H hoplim] " + "[-r relay-IF] [-s serveraddr] [-p pidfile] [-S script] IF ...\n"); + exit(0); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, pid; + char *progname; + char *p; + FILE *pidfp; + + if ((progname = strrchr(*argv, '/')) == NULL) + progname = *argv; + else + progname++; + + while((ch = getopt(argc, argv, "b:dDfH:r:s:S:p:")) != -1) { + switch(ch) { + case 'b': + boundaddr = optarg; + break; + case 'd': + debug = 1; + break; + case 'D': + debug = 2; + break; + case 'f': + foreground++; + break; + case 'H': + p = NULL; + mhops = (int)strtoul(optarg, &p, 10); + if (!*optarg || *p) { + errx(1, "illegal hop limit: %s", optarg); + /* NOTREACHED */ + } + if (mhops <= 0 || mhops > 255) { + errx(1, "illegal hop limit: %d", mhops); + /* NOTREACHED */ + } + break; + case 'r': + relaydevice = optarg; + break; + case 's': + serveraddr = optarg; + break; + case 'S': + scriptpath = optarg; + break; + case 'p': + pid_file = optarg; + break; + default: + usage(); + exit(0); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + /* NOTREACHED */ + } + if (relaydevice == NULL) { + if (argc != 1) { + fprintf(stderr, "you should explicitly specify a " + "relaying interface, when you are to " + "listen on multiple interfaces"); + exit(0); + } + relaydevice = argv[0]; + } + + if (foreground == 0) { + if (daemon(0, 0) < 0) + err(1, "daemon"); + openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); + } + setloglevel(debug); + + /* dump current PID */ + pid = getpid(); + if ((pidfp = fopen(pid_file, "w")) != NULL) { + fprintf(pidfp, "%d\n", pid); + fclose(pidfp); + } + + relay6_init(argc, argv); + + dprintf(LOG_INFO, FNAME, "dhcp6relay started"); + relay6_loop(); + + exit(0); +} + +static struct prefix_list * +make_prefix(pstr0) + char *pstr0; +{ + struct prefix_list *pent; + char *p, *ep; + int plen; + char pstr[BUFSIZ]; + struct in6_addr paddr; + + /* make a local copy for safety */ + if (strlcpy(pstr, pstr0, sizeof (pstr)) >= sizeof (pstr)) { + dprintf(LOG_WARNING, FNAME, + "prefix string too long (maybe bogus): %s", pstr0); + return (NULL); + } + + /* parse the string */ + if ((p = strchr(pstr, '/')) == NULL) + plen = 128; /* assumes it as a host prefix */ + else { + if (p[1] == '\0') { + dprintf(LOG_WARNING, FNAME, + "no prefix length (ignored): %s", p + 1); + return (NULL); + } + plen = (int)strtoul(p + 1, &ep, 10); + if (*ep != '\0') { + dprintf(LOG_WARNING, FNAME, + "illegal prefix length (ignored): %s", p + 1); + return (NULL); + } + *p = '\0'; + } + if (inet_pton(AF_INET6, pstr, &paddr) != 1) { + dprintf(LOG_ERR, FNAME, + "inet_pton failed for %s", pstr); + return (NULL); + } + + /* allocate a new entry */ + if ((pent = (struct prefix_list *)malloc(sizeof (*pent))) == NULL) { + dprintf(LOG_WARNING, FNAME, "memory allocation failed"); + return (NULL); /* or abort? */ + } + + /* fill in each member of the entry */ + memset(pent, 0, sizeof (*pent)); + pent->paddr.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + pent->paddr.sin6_len = sizeof (struct sockaddr_in6); +#endif + pent->paddr.sin6_addr = paddr; + pent->plen = plen; + + return (pent); +} + +static void +relay6_init(int ifnum, char *iflist[]) +{ + struct addrinfo hints; + struct addrinfo *res, *res2; + int i, error, on; + struct ipv6_mreq mreq6; + static struct iovec iov[2]; + + /* initialize non-link-local prefixes list */ + TAILQ_INIT(&global_prefixes); + for (i = 0; global_strings[i]; i++) { + struct prefix_list *p; + + if ((p = make_prefix(global_strings[i])) != NULL) + TAILQ_INSERT_TAIL(&global_prefixes, p, plink); + } + + /* initialize special socket addresses */ + memset(&hints, 0, sizeof (hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(serveraddr, DH6PORT_UPSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + goto failexit; + } + if (res->ai_family != PF_INET6 || + res->ai_addrlen < sizeof (sa6_server)) { + /* this should be impossible, but check for safety */ + dprintf(LOG_ERR, FNAME, + "getaddrinfo returned a bogus address: %s", + strerror(errno)); + goto failexit; + } + /* XXX: assume only one DHCPv6 server address */ + memcpy(&sa6_server, res->ai_addr, sizeof (sa6_server)); + freeaddrinfo(res); + + /* initialize send/receive buffer */ + iov[0].iov_base = (caddr_t)rdatabuf; + iov[0].iov_len = sizeof (rdatabuf); + rmh.msg_iov = iov; + rmh.msg_iovlen = 1; + rmsgctllen = CMSG_SPACE(sizeof (struct in6_pktinfo)); + if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + goto failexit; + } + + /* + * Setup a socket to communicate with clients. + */ + memset(&hints, 0, sizeof (hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(NULL, DH6PORT_UPSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + goto failexit; + } + csock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (csock < 0) { + dprintf(LOG_ERR, FNAME, "socket(csock): %s", strerror(errno)); + goto failexit; + } + if (csock > maxfd) + maxfd = csock; + on = 1; + if (setsockopt(csock, SOL_SOCKET, SO_REUSEPORT, + &on, sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(csock, SO_REUSEPORT): %s", + strerror(errno)); + goto failexit; + } +#ifdef IPV6_V6ONLY + if (setsockopt(csock, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(csock, IPV6_V6ONLY): %s", + strerror(errno)); + goto failexit; + } +#endif + if (bind(csock, res->ai_addr, res->ai_addrlen) < 0) { + dprintf(LOG_ERR, FNAME, "bind(csock): %s", strerror(errno)); + goto failexit; + } + freeaddrinfo(res); + on = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt(csock, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(IPV6_RECVPKTINFO): %s", + strerror(errno)); + goto failexit; + } +#else + if (setsockopt(csock, IPPROTO_IPV6, IPV6_PKTINFO, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(IPV6_PKTINFO): %s", + strerror(errno)); + goto failexit; + } +#endif + + hints.ai_flags = 0; + error = getaddrinfo(DH6ADDR_ALLAGENT, 0, &hints, &res2); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + goto failexit; + } + memset(&mreq6, 0, sizeof (mreq6)); + memcpy(&mreq6.ipv6mr_multiaddr, + &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, + sizeof (mreq6.ipv6mr_multiaddr)); + + TAILQ_INIT(&ifid_list); + while (ifnum-- > 0) { + char *ifp = iflist[0]; + struct ifid_list *ifd; + + ifd = (struct ifid_list *)malloc(sizeof (*ifd)); + if (ifd == NULL) { + dprintf(LOG_WARNING, FNAME, + "memory allocation failed"); + goto failexit; + } + memset(ifd, 0, sizeof (*ifd)); + ifd->ifid = if_nametoindex(ifp); + if (ifd->ifid == 0) { + dprintf(LOG_ERR, FNAME, "invalid interface %s", ifp); + goto failexit; + } + mreq6.ipv6mr_interface = ifd->ifid; + + if (setsockopt(csock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq6, sizeof (mreq6))) { + dprintf(LOG_ERR, FNAME, + "setsockopt(csock, IPV6_JOIN_GROUP): %s", + strerror(errno)); + goto failexit; + } + TAILQ_INSERT_TAIL(&ifid_list, ifd, ilink); + iflist++; + } + freeaddrinfo(res2); + + /* + * Setup a socket to relay to servers. + */ + relayifid = if_nametoindex(relaydevice); + if (relayifid == 0) + dprintf(LOG_ERR, FNAME, "invalid interface %s", relaydevice); + /* + * We are not really sure if we need to listen on the downstream + * port to receive packets from servers. We'll need to clarify the + * specification, but we do for now. + */ + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(boundaddr, DH6PORT_DOWNSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + goto failexit; + } + memcpy(&sa6_client, res->ai_addr, sizeof (sa6_client)); + ssock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (ssock < 0) { + dprintf(LOG_ERR, FNAME, "socket(outsock): %s", + strerror(error)); + goto failexit; + } + if (ssock > maxfd) + maxfd = ssock; + on = 1; + /* + * Both a relay and a client may run on a single node. If we need to + * listen on the downstream port, we need REUSEPORT to avoid conflict. + */ + if (setsockopt(ssock, SOL_SOCKET, SO_REUSEPORT, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(ssock, SO_REUSEPORT): %s", + strerror(errno)); + goto failexit; + } + on = 1; +#ifdef IPV6_V6ONLY + if (setsockopt(ssock, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(ssock, IPV6_V6ONLY): %s", + strerror(errno)); + goto failexit; + } +#endif + if (bind(ssock, res->ai_addr, res->ai_addrlen) < 0) { + dprintf(LOG_ERR, FNAME, "bind(ssock): %s", strerror(errno)); + goto failexit; + } + freeaddrinfo(res); + + on = 1; +#ifdef IPV6_RECVPKTINFO + if (setsockopt(ssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(IPV6_RECVPKTINFO): %s", + strerror(errno)); + goto failexit; + } +#else + if (setsockopt(ssock, IPPROTO_IPV6, IPV6_PKTINFO, + &on, sizeof (on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(IPV6_PKTINFO): %s", + strerror(errno)); + goto failexit; + } +#endif + + if (signal(SIGTERM, relay6_signal) == SIG_ERR) { + dprintf(LOG_WARNING, FNAME, "failed to set signal: %s", + strerror(errno)); + exit(1); + } + return; + + failexit: + exit(1); +} + +static void +relay6_signal(sig) + int sig; +{ + + switch (sig) { + case SIGTERM: + sig_flags |= SIGF_TERM; + break; + } +} + +static void +process_signals() +{ + if ((sig_flags & SIGF_TERM)) { + unlink(pid_file); + exit(0); + } +} + +static void +relay6_loop() +{ + fd_set readfds; + int e; + + while(1) { + if (sig_flags) + process_signals(); + + /* we'd rather use FD_COPY here, but it's not POSIX friendly */ + FD_ZERO(&readfds); + FD_SET(csock, &readfds); + FD_SET(ssock, &readfds); + + e = select(maxfd + 1, &readfds, NULL, NULL, NULL); + switch(e) { + case 0: /* impossible in our situation */ + errx(1, "select returned 0"); + /* NOTREACHED */ + case -1: + if (errno != EINTR) { + err(1, "select"); + /* NOTREACHED */ + } + continue; + default: + break; + } + + if (FD_ISSET(csock, &readfds)) + relay6_recv(csock, 1); + + if (FD_ISSET(ssock, &readfds)) + relay6_recv(ssock, 0); + } +} + +static void +relay6_recv(s, fromclient) + int s, fromclient; +{ + ssize_t len; + struct sockaddr_storage from; + struct in6_pktinfo *pi = NULL; + struct cmsghdr *cm; + struct dhcp6 *dh6; + struct ifid_list *ifd; + char ifname[IF_NAMESIZE]; + + rmh.msg_control = (caddr_t)rmsgctlbuf; + rmh.msg_controllen = rmsgctllen; + + rmh.msg_name = &from; + rmh.msg_namelen = sizeof (from); + + if ((len = recvmsg(s, &rmh, 0)) < 0) { + dprintf(LOG_WARNING, FNAME, "recvmsg: %s", strerror(errno)); + return; + } + + dprintf(LOG_DEBUG, FNAME, "from %s, size %d", + addr2str((struct sockaddr *)&from), len); + + if (((struct sockaddr *)&from)->sa_family != AF_INET6) { + dprintf(LOG_WARNING, FNAME, + "non-IPv6 packet is received (AF %d) ", + ((struct sockaddr *)&from)->sa_family); + return; + } + + /* get optional information as ancillary data (if available) */ + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rmh); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&rmh, cm)) { + if (cm->cmsg_level != IPPROTO_IPV6) + continue; + + switch(cm->cmsg_type) { + case IPV6_PKTINFO: + pi = (struct in6_pktinfo *)CMSG_DATA(cm); + break; + } + } + if (pi == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to get the arrival interface"); + return; + } + for (ifd = TAILQ_FIRST(&ifid_list); ifd; + ifd = TAILQ_NEXT(ifd, ilink)) { + if (pi->ipi6_ifindex == ifd->ifid) + break; + } + /* + * DHCPv6 relay may receive a DHCPv6 packet from a non-listening + * interface, when a DHCPv6 server is running on that interface. + * This check prevents such reception. + */ + if (ifd == NULL && pi->ipi6_ifindex != relayifid) + return; + if (if_indextoname(pi->ipi6_ifindex, ifname) == NULL) { + dprintf(LOG_WARNING, FNAME, + "if_indextoname(id = %d): %s", + pi->ipi6_ifindex, strerror(errno)); + return; + } + + /* packet validation */ + if (len < sizeof (*dh6)) { + dprintf(LOG_INFO, FNAME, "short packet (%d bytes)", len); + return; + } + + dh6 = (struct dhcp6 *)rdatabuf; + dprintf(LOG_DEBUG, FNAME, "received %s from %s", + dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&from)); + + /* + * Relay the packet according to the type. A client message or + * a relay forward message is forwarded to servers (or other relays), + * and a relay reply message is forwarded to the intended client. + */ + if (fromclient) { + switch (dh6->dh6_msgtype) { + case DH6_SOLICIT: + case DH6_REQUEST: + case DH6_CONFIRM: + case DH6_RENEW: + case DH6_REBIND: + case DH6_RELEASE: + case DH6_DECLINE: + case DH6_INFORM_REQ: + case DH6_RELAY_FORW: + relay_to_server(dh6, len, (struct sockaddr_in6 *)&from, + ifname, htonl(pi->ipi6_ifindex)); + break; + case DH6_RELAY_REPLY: + /* + * The server may send a relay reply to the client + * port. + * XXX: need to clarify the port issue + */ + relay_to_client((struct dhcp6_relay *)dh6, len, + (struct sockaddr *)&from); + break; + default: + dprintf(LOG_INFO, FNAME, + "unexpected message (%s) on the client side " + "from %s", dhcp6msgstr(dh6->dh6_msgtype), + addr2str((struct sockaddr *)&from)); + break; + } + } else { + if (dh6->dh6_msgtype != DH6_RELAY_REPLY) { + dprintf(LOG_INFO, FNAME, + "unexpected message (%s) on the server side" + "from %s", dhcp6msgstr(dh6->dh6_msgtype), + addr2str((struct sockaddr *)&from)); + return; + } + relay_to_client((struct dhcp6_relay *)dh6, len, + (struct sockaddr *)&from); + } +} + +static int +make_msgcontrol(mh, ctlbuf, buflen, pktinfo, hlim) + struct msghdr *mh; + void *ctlbuf; + socklen_t buflen; + struct in6_pktinfo *pktinfo; + int hlim; +{ + struct cmsghdr *cm; + socklen_t controllen; + + controllen = 0; + if (pktinfo) + controllen += CMSG_SPACE(sizeof (*pktinfo)); + if (hlim > 0) + controllen += CMSG_SPACE(sizeof (hlim)); + if (buflen < controllen) + return (-1); + + memset(ctlbuf, 0, buflen); + mh->msg_controllen = controllen; + mh->msg_control = ctlbuf; + + cm = (struct cmsghdr *)CMSG_FIRSTHDR(mh); + if (pktinfo) { + cm->cmsg_len = CMSG_LEN(sizeof (*pktinfo)); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + memcpy(CMSG_DATA((struct cmsghdr *)cm), pktinfo, + sizeof (*pktinfo)); + + cm = CMSG_NXTHDR(mh, cm); + } + + if (hlim > 0) { + cm->cmsg_len = CMSG_LEN(sizeof (hlim)); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_HOPLIMIT; + *(int *)CMSG_DATA((struct cmsghdr *)cm) = hlim; + + cm = CMSG_NXTHDR(mh, cm); /* just in case */ + } + + return (0); +} + +static void +relay_to_server(dh6, len, from, ifname, ifid) + struct dhcp6 *dh6; + ssize_t len; + struct sockaddr_in6 *from; + char *ifname; + unsigned int ifid; +{ + struct dhcp6_optinfo optinfo; + struct dhcp6_relay *dh6relay; + struct in6_addr linkaddr; + struct prefix_list *p; + int optlen, relaylen; + int cc; + struct msghdr mh; + static struct iovec iov[2]; + u_char relaybuf[sizeof (*dh6relay) + BUFSIZ]; + struct in6_pktinfo pktinfo; + char ctlbuf[CMSG_SPACE(sizeof (struct in6_pktinfo)) + + CMSG_SPACE(sizeof (int))]; + + /* + * Prepare a relay forward option. + */ + dhcp6_init_options(&optinfo); + + /* Relay message */ + if ((optinfo.relaymsg_msg = malloc(len)) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate memory to copy the original packet: " + "%s", strerror(errno)); + goto out; + } + optinfo.relaymsg_len = len; + memcpy(optinfo.relaymsg_msg, dh6, len); + + /* Interface-id. We always use this option. */ + if ((optinfo.ifidopt_id = malloc(sizeof (ifid))) == NULL) { + dprintf(LOG_WARNING, FNAME, + "failed to allocate memory for IFID: %s", strerror(errno)); + goto out; + } + optinfo.ifidopt_len = sizeof (ifid); + memcpy(optinfo.ifidopt_id, &ifid, sizeof (ifid)); + + /* + * Construct a relay forward message. + */ + memset(relaybuf, 0, sizeof (relaybuf)); + + dh6relay = (struct dhcp6_relay *)relaybuf; + memset(dh6relay, 0, sizeof (*dh6relay)); + dh6relay->dh6relay_msgtype = DH6_RELAY_FORW; + memcpy(&dh6relay->dh6relay_peeraddr, &from->sin6_addr, + sizeof (dh6relay->dh6relay_peeraddr)); + + /* find a global address to fill in the link address field */ + memset(&linkaddr, 0, sizeof (linkaddr)); + for (p = TAILQ_FIRST(&global_prefixes); p; p = TAILQ_NEXT(p, plink)) { + if (getifaddr(&linkaddr, ifname, &p->paddr.sin6_addr, + p->plen, 1, IN6_IFF_INVALID) == 0) /* found */ + break; + } + if (p == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to find a global address on %s", ifname); + + /* + * When relaying a message from a client, we need a global + * link address. + * XXX: this may be too strong for the stateless case, but + * the DHCPv6 specification seems to require the behavior. + */ + if (dh6->dh6_msgtype != DH6_RELAY_FORW) + goto out; + } + + if (dh6->dh6_msgtype == DH6_RELAY_FORW) { + struct dhcp6_relay *dh6relay0 = (struct dhcp6_relay *)dh6; + + /* Relaying a Message from a Relay Agent */ + + /* + * If the hop-count in the message is greater than or equal to + * HOP_COUNT_LIMIT, the relay agent discards the received + * message. + * [RFC3315 Section 20.1.2] + */ + if (dh6relay0->dh6relay_hcnt >= DHCP6_RELAY_HOP_COUNT_LIMIT) { + dprintf(LOG_INFO, FNAME, "too many relay forwardings"); + goto out; + } + + dh6relay->dh6relay_hcnt = dh6relay0->dh6relay_hcnt + 1; + + /* + * We can keep the link-address field 0, regardless of the + * scope of the source address, since we always include + * interface-ID option. + */ + } else { + /* Relaying a Message from a Client */ + memcpy(&dh6relay->dh6relay_linkaddr, &linkaddr, + sizeof (dh6relay->dh6relay_linkaddr)); + dh6relay->dh6relay_hcnt = 0; + } + + relaylen = sizeof (*dh6relay); + if ((optlen = dhcp6_set_options(DH6_RELAY_FORW, + (struct dhcp6opt *)(dh6relay + 1), + (struct dhcp6opt *)(relaybuf + sizeof (relaybuf)), + &optinfo)) < 0) { + dprintf(LOG_INFO, FNAME, + "failed to construct relay options"); + goto out; + } + relaylen += optlen; + + /* + * Forward the message. + */ + memset(&mh, 0, sizeof (mh)); + iov[0].iov_base = relaybuf; + iov[0].iov_len = relaylen; + mh.msg_iov = iov; + mh.msg_iovlen = 1; + mh.msg_name = &sa6_server; + mh.msg_namelen = sizeof (sa6_server); + if (IN6_IS_ADDR_MULTICAST(&sa6_server.sin6_addr)) { + memset(&pktinfo, 0, sizeof (pktinfo)); + pktinfo.ipi6_ifindex = relayifid; + if (make_msgcontrol(&mh, ctlbuf, sizeof (ctlbuf), + &pktinfo, mhops)) { + dprintf(LOG_WARNING, FNAME, + "failed to make message control data"); + goto out; + } + } + + if ((cc = sendmsg(ssock, &mh, 0)) < 0) { + dprintf(LOG_WARNING, FNAME, + "sendmsg %s failed: %s", + addr2str((struct sockaddr *)&sa6_server), strerror(errno)); + } else if (cc != relaylen) { + dprintf(LOG_WARNING, FNAME, + "failed to send a complete packet to %s", + addr2str((struct sockaddr *)&sa6_server)); + } else { + dprintf(LOG_DEBUG, FNAME, + "relay a message to a server %s", + addr2str((struct sockaddr *)&sa6_server)); + } + + out: + dhcp6_clear_options(&optinfo); +} + +static void +relay_to_client(dh6relay, len, from) + struct dhcp6_relay *dh6relay; + ssize_t len; + struct sockaddr *from; +{ + struct dhcp6_optinfo optinfo; + struct sockaddr_in6 peer; + unsigned int ifid; + char ifnamebuf[IFNAMSIZ]; + int cc; + int relayed = 0; + struct dhcp6 *dh6; + struct msghdr mh; + struct in6_pktinfo pktinfo; + static struct iovec iov[2]; + char ctlbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))]; + + dprintf(LOG_DEBUG, FNAME, + "dhcp6 relay reply: hop=%d, linkaddr=%s, peeraddr=%s", + dh6relay->dh6relay_hcnt, + in6addr2str(&dh6relay->dh6relay_linkaddr, 0), + in6addr2str(&dh6relay->dh6relay_peeraddr, 0)); + + /* + * parse and validate options in the relay reply message. + */ + dhcp6_init_options(&optinfo); + if (dhcp6_get_options((struct dhcp6opt *)(dh6relay + 1), + (struct dhcp6opt *)((char *)dh6relay + len), &optinfo) < 0) { + dprintf(LOG_INFO, FNAME, "failed to parse options"); + return; + } + + /* A relay reply message must include a relay message option */ + if (optinfo.relaymsg_msg == NULL) { + dprintf(LOG_INFO, FNAME, "relay reply message from %s " + "without a relay message", addr2str(from)); + goto out; + } + + /* minimum validation for the inner message */ + if (optinfo.relaymsg_len < sizeof (struct dhcp6)) { + dprintf(LOG_INFO, FNAME, "short relay message from %s", + addr2str(from)); + goto out; + } + + /* + * Extract interface ID which should be included in relay reply + * messages to us. + */ + ifid = 0; + if (optinfo.ifidopt_id) { + if (optinfo.ifidopt_len != sizeof (ifid)) { + dprintf(LOG_INFO, FNAME, + "unexpected length (%d) for Interface ID from %s", + optinfo.ifidopt_len, addr2str(from)); + goto out; + } else { + memcpy(&ifid, optinfo.ifidopt_id, sizeof (ifid)); + ifid = ntohl(ifid); + + /* validation for ID */ + if ((if_indextoname(ifid, ifnamebuf)) == NULL) { + dprintf(LOG_INFO, FNAME, + "invalid interface ID: %x", ifid); + goto out; + } + } + } else { + dprintf(LOG_INFO, FNAME, + "Interface ID is not included from %s", addr2str(from)); + /* + * the responding server should be buggy, but we deal with it. + */ + } + + /* + * If we fail, try to get the interface from the link address. + */ + if (ifid == 0 && + !IN6_IS_ADDR_UNSPECIFIED(&dh6relay->dh6relay_linkaddr) && + !IN6_IS_ADDR_LINKLOCAL(&dh6relay->dh6relay_linkaddr)) { + if (getifidfromaddr(&dh6relay->dh6relay_linkaddr, &ifid)) + ifid = 0; + } + + if (ifid == 0) { + dprintf(LOG_INFO, FNAME, "failed to determine relay link"); + goto out; + } + + peer = sa6_client; + dh6 = (struct dhcp6 *) optinfo.relaymsg_msg; + if (dh6->dh6_msgtype != DH6_RELAY_REPLY) { + relayed++; + } else { + /* + * change dst port to server/relay port, since it's a + * reply to relay, not to a client + */ + peer.sin6_port = htons(547); /* DH6PORT_UPSTREAM */ + } + memcpy(&peer.sin6_addr, &dh6relay->dh6relay_peeraddr, + sizeof (peer.sin6_addr)); + if (IN6_IS_ADDR_LINKLOCAL(&peer.sin6_addr)) + peer.sin6_scope_id = ifid; /* XXX: we assume a 1to1 map */ + + /* construct a message structure specifying the outgoing interface */ + memset(&mh, 0, sizeof (mh)); + iov[0].iov_base = optinfo.relaymsg_msg; + iov[0].iov_len = optinfo.relaymsg_len; + mh.msg_iov = iov; + mh.msg_iovlen = 1; + mh.msg_name = &peer; + mh.msg_namelen = sizeof (peer); + memset(&pktinfo, 0, sizeof (pktinfo)); + pktinfo.ipi6_ifindex = ifid; + if (make_msgcontrol(&mh, ctlbuf, sizeof (ctlbuf), &pktinfo, 0)) { + dprintf(LOG_WARNING, FNAME, + "failed to make message control data"); + goto out; + } + + /* send packet */ + if ((cc = sendmsg(csock, &mh, 0)) < 0) { + dprintf(LOG_WARNING, FNAME, + "sendmsg to %s failed: %s", + addr2str((struct sockaddr *)&peer), strerror(errno)); + } else if (cc != optinfo.relaymsg_len) { + dprintf(LOG_WARNING, FNAME, + "failed to send a complete packet to %s", + addr2str((struct sockaddr *)&peer)); + } else { + dprintf(LOG_DEBUG, FNAME, + "relay a message to a client %s", + addr2str((struct sockaddr *)&peer)); + } + + if (relayed && scriptpath != NULL) + relay6_script(scriptpath, &peer, dh6, optinfo.relaymsg_len); + + out: + dhcp6_clear_options(&optinfo); + return; +} diff --git a/dhcp6relay_script.c b/dhcp6relay_script.c new file mode 100644 index 0000000..82f5eab --- /dev/null +++ b/dhcp6relay_script.c @@ -0,0 +1,304 @@ +/*from $KAME: dhcp6c_script.c,v 1.11 2004/11/28 10:48:38 jinmei Exp $ */ + +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +static char client_str[] = "client"; +static char buf[BUFSIZ]; + +static char *iapd2str __P((int, struct dhcp6_listval *)); +static char *iana2str __P((int, struct dhcp6_listval *)); + +int +relay6_script(scriptpath, client, dh6, len) + char *scriptpath; + struct sockaddr_in6 *client; + struct dhcp6 *dh6; + int len; +{ + struct dhcp6_optinfo optinfo; + struct dhcp6opt *optend; + int i, j, iapds, ianas, envc, elen, ret = 0; + char **envp, *s, *t; + struct dhcp6_listval *v; + pid_t pid, wpid; + + /* if a script is not specified, do nothing */ + if (scriptpath == NULL || strlen(scriptpath) == 0) + return -1; + + /* only replies are interesting */ + if (dh6->dh6_msgtype != DH6_REPLY) { + if (dh6->dh6_msgtype != DH6_ADVERTISE) { + dprintf(LOG_INFO, FNAME, "forward msg#%d to client?", + dh6->dh6_msgtype); + return -1; + } + return 0; + } + + /* parse options */ + optend = (struct dhcp6opt *)((caddr_t) dh6 + len); + dhcp6_init_options(&optinfo); + if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), optend, + &optinfo) < 0) { + dprintf(LOG_INFO, FNAME, "failed to parse options"); + return -1; + } + + /* initialize counters */ + iapds = 0; + ianas = 0; + envc = 2; /* we at least include the address and the terminator */ + + /* count the number of variables */ + for (v = TAILQ_FIRST(&optinfo.iapd_list); v; v = TAILQ_NEXT(v, link)) + iapds++; + envc += iapds; + for (v = TAILQ_FIRST(&optinfo.iana_list); v; v = TAILQ_NEXT(v, link)) + ianas++; + envc += ianas; + + /* allocate an environments array */ + if ((envp = malloc(sizeof (char *) * envc)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate environment buffer"); + dhcp6_clear_options(&optinfo); + return -1; + } + memset(envp, 0, sizeof (char *) * envc); + + /* + * Copy the parameters as environment variables + */ + i = 0; + /* address */ + t = addr2str((struct sockaddr *) client); + if (t == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to get address of client"); + ret = -1; + goto clean; + } + elen = sizeof (client_str) + 1 + strlen(t) + 1; + if ((s = envp[i++] = malloc(elen)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate string for client"); + ret = -1; + goto clean; + } + memset(s, 0, elen); + snprintf(s, elen, "%s=%s", client_str, t); + /* IAs */ + j = 0; + for (v = TAILQ_FIRST(&optinfo.iapd_list); v; + v = TAILQ_NEXT(v, link)) { + if ((s = envp[i++] = iapd2str(j++, v)) == NULL) { + ret = -1; + goto clean; + } + } + j = 0; + for (v = TAILQ_FIRST(&optinfo.iana_list); v; + v = TAILQ_NEXT(v, link)) { + if ((s = envp[i++] = iana2str(j++, v)) == NULL) { + ret = -1; + goto clean; + } + } + + /* launch the script */ + pid = fork(); + if (pid < 0) { + dprintf(LOG_ERR, FNAME, "failed to fork: %s", strerror(errno)); + ret = -1; + goto clean; + } else if (pid) { + int wstatus; + + do { + wpid = wait(&wstatus); + } while (wpid != pid && wpid > 0); + + if (wpid < 0) + dprintf(LOG_ERR, FNAME, "wait: %s", strerror(errno)); + else { + dprintf(LOG_DEBUG, FNAME, + "script \"%s\" terminated", scriptpath); + } + } else { + char *argv[2]; + int fd; + + argv[0] = scriptpath; + argv[1] = NULL; + + if (safefile(scriptpath)) { + dprintf(LOG_ERR, FNAME, + "script \"%s\" cannot be executed safely", + scriptpath); + exit(1); + } + + if (foreground == 0 && + (fd = open("/dev/null", O_RDWR)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + + execve(scriptpath, argv, envp); + + dprintf(LOG_ERR, FNAME, "child: exec failed: %s", + strerror(errno)); + exit(0); + } + + clean: + for (i = 0; i < envc; i++) + free(envp[i]); + free(envp); + dhcp6_clear_options(&optinfo); + + return ret; +} + +static char * +iapd2str(num, iav) + int num; + struct dhcp6_listval *iav; +{ + struct dhcp6_listval *siav; + char *s, *r, *comma; + + s = buf; + memset(s, 0, BUFSIZ); + + snprintf(s, BUFSIZ, "iapd_%d=", num); + comma = ""; + + for (siav = TAILQ_FIRST(&iav->sublist); siav; + siav = TAILQ_NEXT(siav, link)) { + switch (siav->type) { + case DHCP6_LISTVAL_PREFIX6: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s%s/%d", comma, + in6addr2str(&siav->val_prefix6.addr, 0), + siav->val_prefix6.plen); + comma = ","; + break; + + case DHCP6_LISTVAL_STCODE: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s#%d", comma, siav->val_num16); + comma = ","; + break; + + default: + dprintf(LOG_ERR, FNAME, "impossible subopt"); + } + } + + if ((r = strdup(s)) == NULL) + dprintf(LOG_ERR, FNAME, "failed to allocate iapd_%d", num); + return r; +} + +static char * +iana2str(num, iav) + int num; + struct dhcp6_listval *iav; +{ + struct dhcp6_listval *siav; + char *s, *r, *comma; + + s = buf; + memset(s, 0, BUFSIZ); + + snprintf(s, BUFSIZ, "iana_%d=", num); + comma = ""; + + for (siav = TAILQ_FIRST(&iav->sublist); siav; + siav = TAILQ_NEXT(siav, link)) { + switch (siav->type) { + case DHCP6_LISTVAL_STATEFULADDR6: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s%s", comma, + in6addr2str(&siav->val_statefuladdr6.addr, 0)); + comma = ","; + break; + + case DHCP6_LISTVAL_STCODE: + snprintf(s + strlen(s), BUFSIZ - strlen(s), + "%s#%d", comma, siav->val_num16); + comma = ","; + break; + + default: + dprintf(LOG_ERR, FNAME, "impossible subopt"); + } + } + + if ((r = strdup(s)) == NULL) + dprintf(LOG_ERR, FNAME, "failed to allocate iana_%d", num); + return r; +} diff --git a/dhcp6s.8 b/dhcp6s.8 new file mode 100644 index 0000000..888117a --- /dev/null +++ b/dhcp6s.8 @@ -0,0 +1,168 @@ +.\" $KAME: dhcp6s.8,v 1.34 2005/03/17 05:27:01 suz Exp $ +.\" +.\" Copyright (C) 1998 and 1999 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd Sep 12, 1999 +.Dt DHCP6S 8 +.Os KAME +.Sh NAME +.Nm dhcp6s +.Nd DHCPv6 server +.\" +.Sh SYNOPSIS +.Nm +.Op Fl c Ar configfile +.Op Fl Ddf +.Op Fl k Ar ctlkeyfile +.Op Fl p Ar ctlport +.Op Fl P Ar pid-file +.Ar interface +.\" +.Sh DESCRIPTION +.Nm +replies to DHCPv6 client as DHCPv6 server. +.Nm +can give the following information to clients. +.Bl -bullet -compact +.It +IPv6 prefixes for the requesting site +.It +Non-temporary IPv6 addresses +.It +A list of the DNS server addresses +.It +DNS Domain Search List +.It +A list of the NTP server addresses +.It +A list of the SIP server addresses +.It +SIP Server domain name +.It +A list of the NIS server addresses +.It +NIS domain name +.It +A list of the NIS+ server addresses +.It +NIS+ domain name +.It +A list of the BCMCS server addresses +.It +BCMCS Server domain name +.El +.Pp +Command line options are as below: +.Bl -tag -width indent +.\" +.It Fl c Ar configfile +Use +.Ar configfile +as the configuration file. +.It Fl d +Print debugging messages. +.It Fl D +Even more debugging information is printed. +.It Fl f +Foreground mode (useful when debugging). +Although +.Nm +usually prints warning, debugging, or error messages to +.Xr syslog 8 , +it prints the messages to standard error if this option is +specified. +.It Fl k Ar ctlkeyfile +Use +.Ar ctlkeyfile +to store the shared secret to authenticate the communication with +.Nm dhcp6ctl . +The default file name used when unspecified is +.Pa /usr/local/etc/dhcp6sctlkey . +The default name is intentionally same as that for +.Nm dhcp6ctl +so that the server and the control command can share the file when +.Nm dhcp6ctl +controls the server on the same node, +which should be the typical case. +.It Fl p Ar ctlport +Use +.Ar ctlport +as the port number listening on to communicate with +.Nm dhcp6ctl . +.It Fl P Ar pid-file +Use +.Ar pid-file +to dump the process ID of +.Nm . +.El +.\" +.Sh FILES +.Bl -tag -width /usr/local/etc/dhcp6s.conf -compact +.It Pa /usr/local/etc/dhcp6s.conf +is the default configuration file. +.It Pa /var/db/dhcp6s_duid +is the default file to store the server's DUID. +.It Pa /usr/local/etc/dhcp6sctlkey +is the default key file to communicate with the control command. +See +.Xr dhcp6ctl 8 +for the file format. +.It Pa /var/run/dhcp6s.pid +is the default file that contains pid of the currently running +.Nm . +.El +.\" +.Sh SEE ALSO +.Xr dhcp6s.conf 5 , +.Xr dhcp6ctl 8 , +.Xr dhcp6c 8 +.Rs +.%A Ralph Droms, Editor +.%D 2003 +.%T Dynamic Host Configuration Protocol for IPv6 (DHCPv6) +.%R RFC 3315 +.Re +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE/KAME IPv6 protocol stack kit. +.Pp +The +.Fl n Ar dnsserv +command-line option was obsoleted by introducing a configuration file. +.\" +.Sh BUGS +.Nm +is incomplete and violates DHCPv6 protocol spec, in several aspects. +To name a few: +.Bl -enum +.It +it does not assign temporary IPv6 addresses +.Pq nor will . +.El diff --git a/dhcp6s.c b/dhcp6s.c new file mode 100644 index 0000000..544afff --- /dev/null +++ b/dhcp6s.c @@ -0,0 +1,3681 @@ +/* $KAME: dhcp6s.c,v 1.162 2005/10/04 11:53:32 suz Exp $ */ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +#include +#ifdef __FreeBSD__ +#include +#endif + +#include +#ifdef __KAME__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DUID_FILE LOCALDBDIR "/dhcp6s_duid" +#define DHCP6S_CONF SYSCONFDIR "/dhcp6s.conf" +#define DEFAULT_KEYFILE SYSCONFDIR "/dhcp6sctlkey" +#define DHCP6S_PIDFILE "/var/run/dhcp6s.pid" + +#define CTLSKEW 300 + +typedef enum { DHCP6_BINDING_IA } dhcp6_bindingtype_t; + +struct dhcp6_binding { + TAILQ_ENTRY(dhcp6_binding) link; + + dhcp6_bindingtype_t type; + + /* identifier of the binding */ + struct duid clientid; + /* additional identifiers for IA-based bindings */ + int iatype; + u_int32_t iaid; + + /* + * configuration information of this binding, + * which is type-dependent. + */ + union { + struct dhcp6_list uv_list; + } val; +#define val_list val.uv_list + + u_int32_t duration; + time_t updatetime; + struct dhcp6_timer *timer; +}; +static TAILQ_HEAD(, dhcp6_binding) dhcp6_binding_head; + +struct relayinfo { + TAILQ_ENTRY(relayinfo) link; + + u_int hcnt; /* hop count */ + struct in6_addr linkaddr; /* link address */ + struct in6_addr peeraddr; /* peer address */ + struct dhcp6_vbuf relay_ifid; /* Interface ID (if provided) */ + struct dhcp6_vbuf relay_msg; /* relay message */ +}; +TAILQ_HEAD(relayinfolist, relayinfo); + +static int debug = 0; +static sig_atomic_t sig_flags = 0; +#define SIGF_TERM 0x1 + +const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_SERVER; +char *device = NULL; +int ifidx; +int insock; /* inbound UDP port */ +int outsock; /* outbound UDP port */ +int ctlsock = -1; /* control TCP port */ +char *ctladdr = DEFAULT_SERVER_CONTROL_ADDR; +char *ctlport = DEFAULT_SERVER_CONTROL_PORT; + +static const struct sockaddr_in6 *sa6_any_downstream, *sa6_any_relay; +static struct msghdr rmh; +static char rdatabuf[BUFSIZ]; +static int rmsgctllen; +static char *conffile = DHCP6S_CONF; +static char *rmsgctlbuf; +static struct duid server_duid; +static struct dhcp6_list arg_dnslist; +static char *ctlkeyfile = DEFAULT_KEYFILE; +static struct keyinfo *ctlkey = NULL; +static int ctldigestlen; +static char *pid_file = DHCP6S_PIDFILE; + +static inline int get_val32 __P((char **, int *, u_int32_t *)); +static inline int get_val __P((char **, int *, void *, size_t)); + +static void usage __P((void)); +static void server6_init __P((void)); +static void server6_mainloop __P((void)); +static int server6_do_ctlcommand __P((char *, ssize_t)); +static void server6_reload __P((void)); +static void server6_stop __P((void)); +static void server6_recv __P((int)); +static void process_signals __P((void)); +static void server6_signal __P((int)); +static void free_relayinfo __P((struct relayinfo *)); +static int process_relayforw __P((struct dhcp6 **, struct dhcp6opt **, + struct relayinfolist *, struct sockaddr *)); +static int set_statelessinfo __P((int, struct dhcp6_optinfo *)); +static int react_solicit __P((struct dhcp6_if *, struct dhcp6 *, ssize_t, + struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *)); +static int react_request __P((struct dhcp6_if *, struct in6_pktinfo *, + struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, + struct relayinfolist *)); +static int react_renew __P((struct dhcp6_if *, struct in6_pktinfo *, + struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, + struct relayinfolist *)); +static int react_rebind __P((struct dhcp6_if *, struct dhcp6 *, ssize_t, + struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *)); +static int react_release __P((struct dhcp6_if *, struct in6_pktinfo *, + struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, + struct relayinfolist *)); +static int react_decline __P((struct dhcp6_if *, struct in6_pktinfo *, + struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int, + struct relayinfolist *)); +static int react_confirm __P((struct dhcp6_if *, struct in6_pktinfo *, + struct dhcp6 *, ssize_t, + struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *)); +static int react_informreq __P((struct dhcp6_if *, struct dhcp6 *, ssize_t, + struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *)); +static int server6_send __P((int, struct dhcp6_if *, struct dhcp6 *, + struct dhcp6_optinfo *, struct sockaddr *, int, struct dhcp6_optinfo *, + struct relayinfolist *, struct host_conf *)); +static int make_ia_stcode __P((int, u_int32_t, u_int16_t, + struct dhcp6_list *)); +static int update_ia __P((int, struct dhcp6_listval *, + struct dhcp6_list *, struct dhcp6_optinfo *)); +static int release_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_optinfo *)); +static int decline_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_optinfo *)); +static int make_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_list *, struct host_conf *, int)); +static int make_match_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_list *)); +static int make_iana_from_pool __P((struct dhcp6_poolspec *, + struct dhcp6_listval *, struct dhcp6_list *)); +static void calc_ia_timo __P((struct dhcp6_ia *, struct dhcp6_list *, + struct host_conf *)); +static void update_binding_duration __P((struct dhcp6_binding *)); +static struct dhcp6_binding *add_binding __P((struct duid *, + dhcp6_bindingtype_t, int, u_int32_t, void *)); +static struct dhcp6_binding *find_binding __P((struct duid *, + dhcp6_bindingtype_t, int, u_int32_t)); +static void update_binding __P((struct dhcp6_binding *)); +static void remove_binding __P((struct dhcp6_binding *)); +static void free_binding __P((struct dhcp6_binding *)); +static struct dhcp6_timer *binding_timo __P((void *)); +static struct dhcp6_listval *find_binding_ia __P((struct dhcp6_listval *, + struct dhcp6_binding *)); +static char *bindingstr __P((struct dhcp6_binding *)); +static int process_auth __P((struct dhcp6 *, ssize_t, struct host_conf *, + struct dhcp6_optinfo *, struct dhcp6_optinfo *)); +static inline char *clientstr __P((struct host_conf *, struct duid *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch, pid; + struct in6_addr a; + struct dhcp6_listval *dlv; + char *progname; + FILE *pidfp; + + if ((progname = strrchr(*argv, '/')) == NULL) + progname = *argv; + else + progname++; + + TAILQ_INIT(&arg_dnslist); + TAILQ_INIT(&dnslist); + TAILQ_INIT(&dnsnamelist); + TAILQ_INIT(&siplist); + TAILQ_INIT(&sipnamelist); + TAILQ_INIT(&ntplist); + TAILQ_INIT(&nislist); + TAILQ_INIT(&nisnamelist); + TAILQ_INIT(&nisplist); + TAILQ_INIT(&nispnamelist); + TAILQ_INIT(&bcmcslist); + TAILQ_INIT(&bcmcsnamelist); + + srandom(time(NULL) & getpid()); + while ((ch = getopt(argc, argv, "c:dDfk:n:p:P:")) != -1) { + switch (ch) { + case 'c': + conffile = optarg; + break; + case 'd': + debug = 1; + break; + case 'D': + debug = 2; + break; + case 'f': + foreground++; + break; + case 'k': + ctlkeyfile = optarg; + break; + case 'n': + warnx("-n dnsserv option was obsoleted. " + "use configuration file."); + if (inet_pton(AF_INET6, optarg, &a) != 1) { + errx(1, "invalid DNS server %s", optarg); + /* NOTREACHED */ + } + if ((dlv = malloc(sizeof *dlv)) == NULL) { + errx(1, "malloc failed for a DNS server"); + /* NOTREACHED */ + } + dlv->val_addr6 = a; + TAILQ_INSERT_TAIL(&arg_dnslist, dlv, link); + break; + case 'p': + ctlport = optarg; + break; + case 'P': + pid_file = optarg; + break; + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (argc != 1) { + usage(); + /* NOTREACHED */ + } + device = argv[0]; + + if (foreground == 0) + openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); + + setloglevel(debug); + + if (ifinit(device) == NULL) + exit(1); + + if ((cfparse(conffile)) != 0) { + dprintf(LOG_ERR, FNAME, "failed to parse configuration file"); + exit(1); + } + + if (foreground == 0) { + if (daemon(0, 0) < 0) + err(1, "daemon"); + } + + /* dump current PID */ + pid = getpid(); + if ((pidfp = fopen(pid_file, "w")) != NULL) { + fprintf(pidfp, "%d\n", pid); + fclose(pidfp); + } + + /* prohibit a mixture of old and new style of DNS server config */ + if (!TAILQ_EMPTY(&arg_dnslist)) { + if (!TAILQ_EMPTY(&dnslist)) { + dprintf(LOG_INFO, FNAME, "do not specify DNS servers " + "both by command line and by configuration file."); + exit(1); + } + dhcp6_move_list(&dnslist, &arg_dnslist); + TAILQ_INIT(&arg_dnslist); + } + + server6_init(); + + server6_mainloop(); + exit(0); +} + +static void +usage() +{ + fprintf(stderr, + "usage: dhcp6s [-c configfile] [-dDf] [-k ctlkeyfile] " + "[-p ctlport] [-P pidfile] intface\n"); + exit(0); +} + +/*------------------------------------------------------------*/ + +void +server6_init() +{ + struct addrinfo hints; + struct addrinfo *res, *res2; + int error; + int on = 1; + struct ipv6_mreq mreq6; + static struct iovec iov; + static struct sockaddr_in6 sa6_any_downstream_storage; + static struct sockaddr_in6 sa6_any_relay_storage; + + TAILQ_INIT(&dhcp6_binding_head); + if (lease_init() != 0) { + dprintf(LOG_ERR, FNAME, "failed to initialize the lease table"); + exit(1); + } + + ifidx = if_nametoindex(device); + if (ifidx == 0) { + dprintf(LOG_ERR, FNAME, "invalid interface %s", device); + exit(1); + } + + /* get our DUID */ + if (get_duid(DUID_FILE, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to get a DUID"); + exit(1); + } + + if (dhcp6_ctl_authinit(ctlkeyfile, &ctlkey, &ctldigestlen) != 0) { + dprintf(LOG_NOTICE, FNAME, + "failed to initialize control message authentication"); + /* run the server anyway */ + } + + /* initialize send/receive buffer */ + iov.iov_base = (caddr_t)rdatabuf; + iov.iov_len = sizeof(rdatabuf); + rmh.msg_iov = &iov; + rmh.msg_iovlen = 1; + rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) { + dprintf(LOG_ERR, FNAME, "memory allocation failed"); + exit(1); + } + + /* initialize socket */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(NULL, DH6PORT_UPSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (insock < 0) { + dprintf(LOG_ERR, FNAME, "socket(insock): %s", + strerror(errno)); + exit(1); + } + if (setsockopt(insock, SOL_SOCKET, SO_REUSEPORT, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(insock, SO_REUSEPORT): %s", + strerror(errno)); + exit(1); + } + if (setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, "setsockopt(insock, SO_REUSEADDR): %s", + strerror(errno)); + exit(1); + } +#ifdef IPV6_RECVPKTINFO + if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(inbound, IPV6_RECVPKTINFO): %s", + strerror(errno)); + exit(1); + } +#else + if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on, + sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(inbound, IPV6_PKTINFO): %s", + strerror(errno)); + exit(1); + } +#endif +#ifdef IPV6_V6ONLY + if (setsockopt(insock, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof(on)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(inbound, IPV6_V6ONLY): %s", strerror(errno)); + exit(1); + } +#endif + if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) { + dprintf(LOG_ERR, FNAME, "bind(insock): %s", strerror(errno)); + exit(1); + } + freeaddrinfo(res); + + hints.ai_flags = 0; + error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + memset(&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_interface = ifidx; + memcpy(&mreq6.ipv6mr_multiaddr, + &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, + sizeof(mreq6.ipv6mr_multiaddr)); + if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq6, sizeof(mreq6))) { + dprintf(LOG_ERR, FNAME, + "setsockopt(insock, IPV6_JOIN_GROUP): %s", + strerror(errno)); + exit(1); + } + freeaddrinfo(res2); + + hints.ai_flags = 0; + error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM, + &hints, &res2); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + memset(&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_interface = ifidx; + memcpy(&mreq6.ipv6mr_multiaddr, + &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, + sizeof(mreq6.ipv6mr_multiaddr)); + if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq6, sizeof(mreq6))) { + dprintf(LOG_ERR, FNAME, + "setsockopt(insock, IPV6_JOIN_GROUP): %s", + strerror(errno)); + exit(1); + } + freeaddrinfo(res2); + + hints.ai_flags = 0; + error = getaddrinfo(NULL, DH6PORT_DOWNSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (outsock < 0) { + dprintf(LOG_ERR, FNAME, "socket(outsock): %s", + strerror(errno)); + exit(1); + } + /* set outgoing interface of multicast packets for DHCP reconfig */ + if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &ifidx, sizeof(ifidx)) < 0) { + dprintf(LOG_ERR, FNAME, + "setsockopt(outsock, IPV6_MULTICAST_IF): %s", + strerror(errno)); + exit(1); + } +#if !defined(__linux__) && !defined(__sun__) + /* make the socket write-only */ + if (shutdown(outsock, 0)) { + dprintf(LOG_ERR, FNAME, "shutdown(outbound, 0): %s", + strerror(errno)); + exit(1); + } +#endif + freeaddrinfo(res); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + error = getaddrinfo("::", DH6PORT_DOWNSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen); + sa6_any_downstream = + (const struct sockaddr_in6*)&sa6_any_downstream_storage; + freeaddrinfo(res); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + error = getaddrinfo("::", DH6PORT_UPSTREAM, &hints, &res); + if (error) { + dprintf(LOG_ERR, FNAME, "getaddrinfo: %s", + gai_strerror(error)); + exit(1); + } + memcpy(&sa6_any_relay_storage, res->ai_addr, res->ai_addrlen); + sa6_any_relay = + (const struct sockaddr_in6*)&sa6_any_relay_storage; + freeaddrinfo(res); + + /* set up control socket */ + if (ctlkey == NULL) + dprintf(LOG_NOTICE, FNAME, "skip opening control port"); + else if (dhcp6_ctl_init(ctladdr, ctlport, + DHCP6CTL_DEF_COMMANDQUEUELEN, &ctlsock)) { + dprintf(LOG_ERR, FNAME, + "failed to initialize control channel"); + exit(1); + } + + if (signal(SIGTERM, server6_signal) == SIG_ERR) { + dprintf(LOG_WARNING, FNAME, "failed to set signal: %s", + strerror(errno)); + exit(1); + } + return; +} + +static void +process_signals() +{ + if ((sig_flags & SIGF_TERM)) { + unlink(pid_file); + exit(0); + } +} + +static void +server6_mainloop() +{ + struct timeval *w; + int ret; + fd_set r; + int maxsock; + + + while (1) { + if (sig_flags) + process_signals(); + + w = dhcp6_check_timer(); + + FD_ZERO(&r); + FD_SET(insock, &r); + maxsock = insock; + if (ctlsock >= 0) { + FD_SET(ctlsock, &r); + maxsock = (insock > ctlsock) ? insock : ctlsock; + (void)dhcp6_ctl_setreadfds(&r, &maxsock); + } + + ret = select(maxsock + 1, &r, NULL, NULL, w); + switch (ret) { + case -1: + if (errno != EINTR) { + dprintf(LOG_ERR, FNAME, "select: %s", + strerror(errno)); + exit(1); + } + continue; + case 0: /* timeout */ + break; + default: + break; + } + + if (FD_ISSET(insock, &r)) + server6_recv(insock); + if (ctlsock >= 0) { + if (FD_ISSET(ctlsock, &r)) { + (void)dhcp6_ctl_acceptcommand(ctlsock, + server6_do_ctlcommand); + } + (void)dhcp6_ctl_readcommand(&r); + } + } +} + +static inline int +get_val32(bpp, lenp, valp) + char **bpp; + int *lenp; + u_int32_t *valp; +{ + char *bp = *bpp; + int len = *lenp; + u_int32_t i32; + + if (len < sizeof(*valp)) + return (-1); + + memcpy(&i32, bp, sizeof(i32)); + *valp = ntohl(i32); + + *bpp = bp + sizeof(*valp); + *lenp = len - sizeof(*valp); + + return (0); +} + +static inline int +get_val(bpp, lenp, valp, vallen) + char **bpp; + int *lenp; + void *valp; + size_t vallen; +{ + char *bp = *bpp; + int len = *lenp; + + if (len < vallen) + return (-1); + + memcpy(valp, bp, vallen); + + *bpp = bp + vallen; + *lenp = len - vallen; + + return (0); +} + +static int +server6_do_ctlcommand(buf, len) + char *buf; + ssize_t len; +{ + struct dhcp6ctl *ctlhead; + struct dhcp6ctl_iaspec iaspec; + u_int16_t command, version; + u_int32_t p32, iaid, duidlen, ts, ts0; + struct duid duid; + struct dhcp6_binding *binding; + int commandlen; + char *bp; + time_t now; + + ctlhead = (struct dhcp6ctl *)buf; + + command = ntohs(ctlhead->command); + commandlen = (int)(ntohs(ctlhead->len)); + version = ntohs(ctlhead->version); + if (len != sizeof(struct dhcp6ctl) + commandlen) { + dprintf(LOG_ERR, FNAME, + "assumption failure: command length mismatch"); + return (DHCP6CTL_R_FAILURE); + } + + /* replay protection and message authentication */ + if ((now = time(NULL)) < 0) { + dprintf(LOG_ERR, FNAME, "failed to get current time: %s", + strerror(errno)); + return (DHCP6CTL_R_FAILURE); + } + ts0 = (u_int32_t)now; + ts = ntohl(ctlhead->timestamp); + if (ts + CTLSKEW < ts0 || (ts - CTLSKEW) > ts0) { + dprintf(LOG_INFO, FNAME, "timestamp is out of range"); + return (DHCP6CTL_R_FAILURE); + } + + if (ctlkey == NULL) { /* should not happen!! */ + dprintf(LOG_ERR, FNAME, "no secret key for control channel"); + return (DHCP6CTL_R_FAILURE); + } + if (dhcp6_verify_mac(buf, len, DHCP6CTL_AUTHPROTO_UNDEF, + DHCP6CTL_AUTHALG_HMACMD5, sizeof(*ctlhead), ctlkey) != 0) { + dprintf(LOG_INFO, FNAME, "authentication failure"); + return (DHCP6CTL_R_FAILURE); + } + + bp = buf + sizeof(*ctlhead) + ctldigestlen; + commandlen -= ctldigestlen; + + if (version > DHCP6CTL_VERSION) { + dprintf(LOG_INFO, FNAME, "unsupported version: %d", version); + return (DHCP6CTL_R_FAILURE); + } + + switch (command) { + case DHCP6CTL_COMMAND_RELOAD: + if (commandlen != 0) { + dprintf(LOG_INFO, FNAME, "invalid command length " + "for reload: %d", commandlen); + return (DHCP6CTL_R_DONE); + } + server6_reload(); + break; + case DHCP6CTL_COMMAND_STOP: + if (commandlen != 0) { + dprintf(LOG_INFO, FNAME, "invalid command length " + "for stop: %d", commandlen); + return (DHCP6CTL_R_DONE); + } + server6_stop(); + break; + case DHCP6CTL_COMMAND_REMOVE: + if (get_val32(&bp, &commandlen, &p32)) + return (DHCP6CTL_R_FAILURE); + if (p32 != DHCP6CTL_BINDING) { + dprintf(LOG_INFO, FNAME, + "unknown remove target: %ul", p32); + return (DHCP6CTL_R_FAILURE); + } + + if (get_val32(&bp, &commandlen, &p32)) + return (DHCP6CTL_R_FAILURE); + if (p32 != DHCP6CTL_BINDING_IA) { + dprintf(LOG_INFO, FNAME, "unknown binding type: %ul", + p32); + return (DHCP6CTL_R_FAILURE); + } + + if (get_val(&bp, &commandlen, &iaspec, sizeof(iaspec))) + return (DHCP6CTL_R_FAILURE); + if (ntohl(iaspec.type) != DHCP6CTL_IA_PD && + ntohl(iaspec.type) != DHCP6CTL_IA_NA) { + dprintf(LOG_INFO, FNAME, "unknown IA type: %ul", + ntohl(iaspec.type)); + return (DHCP6CTL_R_FAILURE); + } + iaid = ntohl(iaspec.id); + duidlen = ntohl(iaspec.duidlen); + + if (duidlen > commandlen) { + dprintf(LOG_INFO, FNAME, "DUID length mismatch"); + return (DHCP6CTL_R_FAILURE); + } + + duid.duid_len = (size_t)duidlen; + duid.duid_id = bp; + + binding = find_binding(&duid, DHCP6_BINDING_IA, + DHCP6_LISTVAL_IAPD, iaid); + if (binding == NULL) { + binding = find_binding(&duid, DHCP6_BINDING_IA, + DHCP6_LISTVAL_IANA, iaid); + if (binding == NULL) { + dprintf(LOG_INFO, FNAME, "no such binding"); + return (DHCP6CTL_R_FAILURE); + } + } + remove_binding(binding); + + break; + default: + dprintf(LOG_INFO, FNAME, + "unknown control command: %d (len=%d)", + (int)command, commandlen); + return (DHCP6CTL_R_FAILURE); + } + + return (DHCP6CTL_R_DONE); +} + +static void +server6_reload() +{ + /* reload the configuration file */ + if (cfparse(conffile) != 0) { + dprintf(LOG_WARNING, FNAME, + "failed to reload configuration file"); + return; + } + + dprintf(LOG_NOTICE, FNAME, "server reloaded"); + + return; +} + +static void +server6_stop() +{ + /* Right now, we simply stop running */ + + dprintf(LOG_NOTICE, FNAME, "exiting"); + + exit (0); +} + +static void +server6_recv(s) + int s; +{ + ssize_t len; + struct sockaddr_storage from; + int fromlen; + struct msghdr mhdr; + struct iovec iov; + char cmsgbuf[BUFSIZ]; + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + struct dhcp6_if *ifp; + struct dhcp6 *dh6; + struct dhcp6_optinfo optinfo; + struct dhcp6opt *optend; + struct relayinfolist relayinfohead; + struct relayinfo *relayinfo; + + TAILQ_INIT(&relayinfohead); + + memset(&iov, 0, sizeof(iov)); + memset(&mhdr, 0, sizeof(mhdr)); + + iov.iov_base = rdatabuf; + iov.iov_len = sizeof(rdatabuf); + mhdr.msg_name = &from; + mhdr.msg_namelen = sizeof(from); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = (caddr_t)cmsgbuf; + mhdr.msg_controllen = sizeof(cmsgbuf); + + if ((len = recvmsg(insock, &mhdr, 0)) < 0) { + dprintf(LOG_ERR, FNAME, "recvmsg: %s", strerror(errno)); + return; + } + fromlen = mhdr.msg_namelen; + + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) { + if (cm->cmsg_level == IPPROTO_IPV6 && + cm->cmsg_type == IPV6_PKTINFO && + cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); + } + } + if (pi == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to get packet info"); + return; + } + /* + * DHCPv6 server may receive a DHCPv6 packet from a non-listening + * interface, when a DHCPv6 relay agent is running on that interface. + * This check prevents such reception. + */ + if (pi->ipi6_ifindex != ifidx) + return; + if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) { + dprintf(LOG_INFO, FNAME, "unexpected interface (%d)", + (unsigned int)pi->ipi6_ifindex); + return; + } + + dh6 = (struct dhcp6 *)rdatabuf; + + if (len < sizeof(*dh6)) { + dprintf(LOG_INFO, FNAME, "short packet (%d bytes)", len); + return; + } + + dprintf(LOG_DEBUG, FNAME, "received %s from %s", + dhcp6msgstr(dh6->dh6_msgtype), + addr2str((struct sockaddr *)&from)); + + /* + * A server MUST discard any Solicit, Confirm, Rebind or + * Information-request messages it receives with a unicast + * destination address. + * [RFC3315 Section 15.] + */ + if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + (dh6->dh6_msgtype == DH6_SOLICIT || + dh6->dh6_msgtype == DH6_CONFIRM || + dh6->dh6_msgtype == DH6_REBIND || + dh6->dh6_msgtype == DH6_INFORM_REQ)) { + dprintf(LOG_INFO, FNAME, "invalid unicast message"); + return; + } + + /* + * A server never receives a relay reply message. Since relay + * replay messages will annoy option parser below, we explicitly + * reject them here. + */ + if (dh6->dh6_msgtype == DH6_RELAY_REPLY) { + dprintf(LOG_INFO, FNAME, "relay reply message from %s", + addr2str((struct sockaddr *)&from)); + return; + + } + + optend = (struct dhcp6opt *)(rdatabuf + len); + if (dh6->dh6_msgtype == DH6_RELAY_FORW) { + if (process_relayforw(&dh6, &optend, &relayinfohead, + (struct sockaddr *)&from)) { + goto end; + } + /* dh6 and optend should have been updated. */ + len = (ssize_t)((char *)optend - (char *)dh6); + } + + /* + * parse and validate options in the message + */ + dhcp6_init_options(&optinfo); + if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), + optend, &optinfo) < 0) { + dprintf(LOG_INFO, FNAME, "failed to parse options"); + goto end; + } + + switch (dh6->dh6_msgtype) { + case DH6_SOLICIT: + (void)react_solicit(ifp, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_REQUEST: + (void)react_request(ifp, pi, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_RENEW: + (void)react_renew(ifp, pi, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_REBIND: + (void)react_rebind(ifp, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_RELEASE: + (void)react_release(ifp, pi, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_DECLINE: + (void)react_decline(ifp, pi, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_CONFIRM: + (void)react_confirm(ifp, pi, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + case DH6_INFORM_REQ: + (void)react_informreq(ifp, dh6, len, &optinfo, + (struct sockaddr *)&from, fromlen, &relayinfohead); + break; + default: + dprintf(LOG_INFO, FNAME, "unknown or unsupported msgtype (%s)", + dhcp6msgstr(dh6->dh6_msgtype)); + break; + } + + dhcp6_clear_options(&optinfo); + + end: + while ((relayinfo = TAILQ_FIRST(&relayinfohead)) != NULL) { + TAILQ_REMOVE(&relayinfohead, relayinfo, link); + free_relayinfo(relayinfo); + } + + return; +} + +static void +free_relayinfo(relayinfo) + struct relayinfo *relayinfo; +{ + if (relayinfo->relay_ifid.dv_buf) + dhcp6_vbuf_free(&relayinfo->relay_ifid); + + if (relayinfo->relay_msg.dv_buf) + dhcp6_vbuf_free(&relayinfo->relay_msg); + + free(relayinfo); +} + +static int +process_relayforw(dh6p, optendp, relayinfohead, from) + struct dhcp6 **dh6p; + struct dhcp6opt **optendp; + struct relayinfolist *relayinfohead; + struct sockaddr *from; +{ + struct dhcp6_relay *dh6relay = (struct dhcp6_relay *)*dh6p; + struct dhcp6opt *optend = *optendp; + struct relayinfo *relayinfo; + struct dhcp6_optinfo optinfo; + int len; + + again: + len = (void *)optend - (void *)dh6relay; + if (len < sizeof (*dh6relay)) { + dprintf(LOG_INFO, FNAME, "short relay message from %s", + addr2str(from)); + return (-1); + } + dprintf(LOG_DEBUG, FNAME, + "dhcp6 relay: hop=%d, linkaddr=%s, peeraddr=%s", + dh6relay->dh6relay_hcnt, + in6addr2str(&dh6relay->dh6relay_linkaddr, 0), + in6addr2str(&dh6relay->dh6relay_peeraddr, 0)); + + /* + * parse and validate options in the relay forward message. + */ + dhcp6_init_options(&optinfo); + if (dhcp6_get_options((struct dhcp6opt *)(dh6relay + 1), + optend, &optinfo) < 0) { + dprintf(LOG_INFO, FNAME, "failed to parse options"); + return (-1); + } + + /* A relay forward message must include a relay message option */ + if (optinfo.relaymsg_msg == NULL) { + dprintf(LOG_INFO, FNAME, "relay forward from %s " + "without a relay message", addr2str(from)); + return (-1); + } + + /* relay message must contain a DHCPv6 message. */ + len = optinfo.relaymsg_len; + if (len < sizeof (struct dhcp6)) { + dprintf(LOG_INFO, FNAME, + "short packet (%d bytes) in relay message", len); + return (-1); + } + + if ((relayinfo = malloc(sizeof (*relayinfo))) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to allocate relay info"); + return (-1); + } + memset(relayinfo, 0, sizeof (*relayinfo)); + + relayinfo->hcnt = dh6relay->dh6relay_hcnt; + memcpy(&relayinfo->linkaddr, &dh6relay->dh6relay_linkaddr, + sizeof (relayinfo->linkaddr)); + memcpy(&relayinfo->peeraddr, &dh6relay->dh6relay_peeraddr, + sizeof (relayinfo->peeraddr)); + + if (dhcp6_vbuf_copy(&relayinfo->relay_msg, &optinfo.relay_msg)) + goto fail; + if (optinfo.ifidopt_id && + dhcp6_vbuf_copy(&relayinfo->relay_ifid, &optinfo.ifidopt)) { + goto fail; + } + + TAILQ_INSERT_HEAD(relayinfohead, relayinfo, link); + + dhcp6_clear_options(&optinfo); + + optend = (struct dhcp6opt *)(relayinfo->relay_msg.dv_buf + len); + dh6relay = (struct dhcp6_relay *)relayinfo->relay_msg.dv_buf; + + if (dh6relay->dh6relay_msgtype != DH6_RELAY_FORW) { + *dh6p = (struct dhcp6 *)dh6relay; + *optendp = optend; + return (0); + } + + goto again; + + fail: + free_relayinfo(relayinfo); + dhcp6_clear_options(&optinfo); + + return (-1); +} + +/* + * Set stateless configuration information to a option structure. + * It is the caller's responsibility to deal with error cases. + */ +static int +set_statelessinfo(type, optinfo) + int type; + struct dhcp6_optinfo *optinfo; +{ + /* SIP domain name */ + if (dhcp6_copy_list(&optinfo->sipname_list, &sipnamelist)) { + dprintf(LOG_ERR, FNAME, + "failed to copy SIP domain list"); + return (-1); + } + + /* SIP server */ + if (dhcp6_copy_list(&optinfo->sip_list, &siplist)) { + dprintf(LOG_ERR, FNAME, "failed to copy SIP servers"); + return (-1); + } + + /* DNS server */ + if (dhcp6_copy_list(&optinfo->dns_list, &dnslist)) { + dprintf(LOG_ERR, FNAME, "failed to copy DNS servers"); + return (-1); + } + + /* DNS search list */ + if (dhcp6_copy_list(&optinfo->dnsname_list, &dnsnamelist)) { + dprintf(LOG_ERR, FNAME, "failed to copy DNS search list"); + return (-1); + } + + /* NTP server */ + if (dhcp6_copy_list(&optinfo->ntp_list, &ntplist)) { + dprintf(LOG_ERR, FNAME, "failed to copy NTP servers"); + return (-1); + } + + /* NIS domain name */ + if (dhcp6_copy_list(&optinfo->nisname_list, &nisnamelist)) { + dprintf(LOG_ERR, FNAME, + "failed to copy NIS domain list"); + return (-1); + } + + /* NIS server */ + if (dhcp6_copy_list(&optinfo->nis_list, &nislist)) { + dprintf(LOG_ERR, FNAME, "failed to copy NIS servers"); + return (-1); + } + + /* NIS+ domain name */ + if (dhcp6_copy_list(&optinfo->nispname_list, &nispnamelist)) { + dprintf(LOG_ERR, FNAME, + "failed to copy NIS+ domain list"); + return (-1); + } + + /* NIS+ server */ + if (dhcp6_copy_list(&optinfo->nisp_list, &nisplist)) { + dprintf(LOG_ERR, FNAME, "failed to copy NIS+ servers"); + return (-1); + } + + /* BCMCS domain name */ + if (dhcp6_copy_list(&optinfo->bcmcsname_list, &bcmcsnamelist)) { + dprintf(LOG_ERR, FNAME, + "failed to copy BCMCS domain list"); + return (-1); + } + + /* BCMCS server */ + if (dhcp6_copy_list(&optinfo->bcmcs_list, &bcmcslist)) { + dprintf(LOG_ERR, FNAME, "failed to copy BCMCS servers"); + return (-1); + } + + /* + * Information refresh time. Only include in a response to + * an Information-request message. + */ + if (type == DH6_INFORM_REQ && + optrefreshtime != DH6OPT_REFRESHTIME_UNDEF) { + optinfo->refreshtime = (int64_t)optrefreshtime; + } + + return (0); +} + +static int +react_solicit(ifp, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct host_conf *client_conf; + int resptype, do_binding = 0, error; + + /* + * Servers MUST discard any Solicit messages that do not include a + * Client Identifier option. + * [RFC3315 Section 15.2] + */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } else { + dprintf(LOG_DEBUG, FNAME, "client ID %s", + duidstr(&optinfo->clientID)); + } + + /* + * Servers MUST discard any Solicit messages that do include a + * Server Identifier option. + * [RFC3315 Section 15.2] + */ + if (optinfo->serverID.duid_len) { + dprintf(LOG_INFO, FNAME, "server ID option found"); + return (-1); + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, "found a host configuration for %s", + client_conf->name); + } + + /* + * configure necessary options based on the options in solicit. + */ + dhcp6_init_options(&roptinfo); + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* preference (if configured) */ + if (ifp->server_pref != DH6OPT_PREF_UNDEF) + roptinfo.pref = ifp->server_pref; + + /* add other configuration information */ + if (set_statelessinfo(DH6_SOLICIT, &roptinfo)) { + dprintf(LOG_ERR, FNAME, + "failed to set other stateless information"); + goto fail; + } + + /* + * see if we have information for requested options, and if so, + * configure corresponding options. + */ + if (optinfo->rapidcommit && (ifp->allow_flags & DHCIFF_RAPID_COMMIT)) + do_binding = 1; + + /* + * The delegating router MUST include an IA_PD option, identifying any + * prefix(es) that the delegating router will delegate to the + * requesting router. [RFC3633 Section 11.2] + */ + if (!TAILQ_EMPTY(&optinfo->iapd_list)) { + int found = 0; + struct dhcp6_list conflist; + struct dhcp6_listval *iapd; + + TAILQ_INIT(&conflist); + + /* make a local copy of the configured prefixes */ + if (client_conf && + dhcp6_copy_list(&conflist, &client_conf->prefix_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make local data"); + goto fail; + } + + for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + /* + * find an appropriate prefix for each IA_PD, + * removing the adopted prefixes from the list. + * (dhcp6s cannot create IAs without client config) + */ + if (client_conf && + make_ia(iapd, &conflist, &roptinfo.iapd_list, + client_conf, do_binding) > 0) + found = 1; + } + + dhcp6_clear_list(&conflist); + + if (!found) { + /* + * If the delegating router will not assign any + * prefixes to any IA_PDs in a subsequent Request from + * the requesting router, the delegating router MUST + * send an Advertise message to the requesting router + * that includes a Status Code option with code + * NoPrefixAvail. + * [dhcpv6-opt-prefix-delegation-01 Section 10.2] + */ + u_int16_t stcode = DH6OPT_STCODE_NOPREFIXAVAIL; + + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) + goto fail; + } + } + + if (!TAILQ_EMPTY(&optinfo->iana_list)) { + int found = 0; + struct dhcp6_list conflist; + struct dhcp6_listval *iana; + + if (client_conf == NULL && ifp->pool.name) { + if ((client_conf = create_dynamic_hostconf(&optinfo->clientID, + &ifp->pool)) == NULL) + dprintf(LOG_NOTICE, FNAME, + "failed to make host configuration"); + } + TAILQ_INIT(&conflist); + + /* make a local copy of the configured addresses */ + if (client_conf && + dhcp6_copy_list(&conflist, &client_conf->addr_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make local data"); + goto fail; + } + + for (iana = TAILQ_FIRST(&optinfo->iana_list); iana; + iana = TAILQ_NEXT(iana, link)) { + /* + * find an appropriate address for each IA_NA, + * removing the adopted addresses from the list. + * (dhcp6s cannot create IAs without client config) + */ + if (client_conf && + make_ia(iana, &conflist, &roptinfo.iana_list, + client_conf, do_binding) > 0) + found = 1; + } + + dhcp6_clear_list(&conflist); + + if (!found) { + u_int16_t stcode = DH6OPT_STCODE_NOADDRSAVAIL; + + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) + goto fail; + } + } + + if (optinfo->rapidcommit && (ifp->allow_flags & DHCIFF_RAPID_COMMIT)) { + /* + * If the client has included a Rapid Commit option and the + * server has been configured to respond with committed address + * assignments and other resources, responds to the Solicit + * with a Reply message. + * [RFC3315 Section 17.2.1] + */ + roptinfo.rapidcommit = 1; + resptype = DH6_REPLY; + } else + resptype = DH6_ADVERTISE; + + error = server6_send(resptype, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + dhcp6_clear_options(&roptinfo); + return (error); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +react_request(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct in6_pktinfo *pi; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct host_conf *client_conf; + + /* message validation according to Section 15.4 of RFC3315 */ + + /* the message must include a Server Identifier option */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + /* the contents of the Server Identifier option must match ours */ + if (duidcmp(&optinfo->serverID, &server_duid)) { + dprintf(LOG_INFO, FNAME, "server ID mismatch"); + return (-1); + } + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* + * When the server receives a Request message via unicast from a + * client to which the server has not sent a unicast option, the server + * discards the Request message and responds with a Reply message + * containing a Status Code option with value UseMulticast, a Server + * Identifier option containing the server's DUID, the Client + * Identifier option from the client message and no other options. + * [RFC3315 18.2.1] + * (Our current implementation never sends a unicast option.) + * Note: a request message encapsulated in a relay server option can be + * unicasted. + */ + if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + TAILQ_EMPTY(relayinfohead)) { + u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST; + + dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s", + addr2str(from)); + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a status code"); + goto fail; + } + server6_send(DH6_REPLY, ifp, dh6, optinfo, from, + fromlen, &roptinfo, relayinfohead, client_conf); + goto end; + } + + /* + * See if we have to make a binding of some configuration information + * for the client. + */ + + /* + * When a delegating router receives a Request message from a + * requesting router that contains an IA_PD option, and the delegating + * router is authorized to delegate prefix(es) to the requesting + * router, the delegating router selects the prefix(es) to be delegated + * to the requesting router. + * [RFC3633 Section 12.2] + */ + if (!TAILQ_EMPTY(&optinfo->iapd_list)) { + struct dhcp6_list conflist; + struct dhcp6_listval *iapd; + + TAILQ_INIT(&conflist); + + /* make a local copy of the configured prefixes */ + if (client_conf && + dhcp6_copy_list(&conflist, &client_conf->prefix_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make local data"); + goto fail; + } + + for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + /* + * Find an appropriate prefix for each IA_PD, + * removing the adopted prefixes from the list. + * The prefixes will be bound to the client. + */ + if (make_ia(iapd, &conflist, &roptinfo.iapd_list, + client_conf, 1) == 0) { + /* + * We could not find any prefixes for the IA. + * RFC3315 specifies to include NoAddrsAvail + * for the IA in the address configuration + * case (Section 18.2.1). We follow the same + * logic for prefix delegation as well. + */ + if (make_ia_stcode(DHCP6_LISTVAL_IAPD, + iapd->val_ia.iaid, + DH6OPT_STCODE_NOPREFIXAVAIL, + &roptinfo.iapd_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make an option list"); + dhcp6_clear_list(&conflist); + goto fail; + } + } + } + + dhcp6_clear_list(&conflist); + } + + if (!TAILQ_EMPTY(&optinfo->iana_list)) { + struct dhcp6_list conflist; + struct dhcp6_listval *iana; + + if (client_conf == NULL && ifp->pool.name) { + if ((client_conf = create_dynamic_hostconf(&optinfo->clientID, + &ifp->pool)) == NULL) + dprintf(LOG_NOTICE, FNAME, + "failed to make host configuration"); + } + TAILQ_INIT(&conflist); + + /* make a local copy of the configured prefixes */ + if (client_conf && + dhcp6_copy_list(&conflist, &client_conf->addr_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make local data"); + goto fail; + } + + for (iana = TAILQ_FIRST(&optinfo->iana_list); iana; + iana = TAILQ_NEXT(iana, link)) { + /* + * Find an appropriate address for each IA_NA, + * removing the adopted addresses from the list. + * The addresses will be bound to the client. + */ + if (make_ia(iana, &conflist, &roptinfo.iana_list, + client_conf, 1) == 0) { + if (make_ia_stcode(DHCP6_LISTVAL_IANA, + iana->val_ia.iaid, + DH6OPT_STCODE_NOADDRSAVAIL, + &roptinfo.iana_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make an option list"); + dhcp6_clear_list(&conflist); + goto fail; + } + } + } + + dhcp6_clear_list(&conflist); + } + + /* + * If the Request message contained an Option Request option, the + * server MUST include options in the Reply message for any options in + * the Option Request option the server is configured to return to the + * client. + * [RFC3315 18.2.1] + * Note: our current implementation always includes all information + * that we can provide. So we do not have to check the option request + * options. + */ +#if 0 + for (opt = TAILQ_FIRST(&optinfo->reqopt_list); opt; + opt = TAILQ_NEXT(opt, link)) { + ; + } +#endif + + /* + * Add options to the Reply message for any other configuration + * information to be assigned to the client. + */ + if (set_statelessinfo(DH6_REQUEST, &roptinfo)) { + dprintf(LOG_ERR, FNAME, + "failed to set other stateless information"); + goto fail; + } + + /* send a reply message. */ + (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + end: + dhcp6_clear_options(&roptinfo); + return (0); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +react_renew(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct in6_pktinfo *pi; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct dhcp6_listval *ia; + struct host_conf *client_conf; + + /* message validation according to Section 15.6 of RFC3315 */ + + /* the message must include a Server Identifier option */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + /* the contents of the Server Identifier option must match ours */ + if (duidcmp(&optinfo->serverID, &server_duid)) { + dprintf(LOG_INFO, FNAME, "server ID mismatch"); + return (-1); + } + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* + * When the server receives a Renew message via unicast from a + * client to which the server has not sent a unicast option, the server + * discards the Request message and responds with a Reply message + * containing a status code option with value UseMulticast, a Server + * Identifier option containing the server's DUID, the Client + * Identifier option from the client message and no other options. + * [RFC3315 18.2.3] + * (Our current implementation never sends a unicast option.) + */ + if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + TAILQ_EMPTY(relayinfohead)) { + u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST; + + dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s", + addr2str(from)); + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a status code"); + goto fail; + } + server6_send(DH6_REPLY, ifp, dh6, optinfo, from, + fromlen, &roptinfo, relayinfohead, client_conf); + goto end; + } + + /* + * Locates the client's binding and verifies that the information + * from the client matches the information stored for that client. + */ + for (ia = TAILQ_FIRST(&optinfo->iapd_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (update_ia(DH6_RENEW, ia, &roptinfo.iapd_list, optinfo)) + goto fail; + } + for (ia = TAILQ_FIRST(&optinfo->iana_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (update_ia(DH6_RENEW, ia, &roptinfo.iana_list, optinfo)) + goto fail; + } + + /* add other configuration information */ + if (set_statelessinfo(DH6_RENEW, &roptinfo)) { + dprintf(LOG_ERR, FNAME, + "failed to set other stateless information"); + goto fail; + } + + (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + end: + dhcp6_clear_options(&roptinfo); + return (0); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +react_rebind(ifp, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct dhcp6_listval *ia; + struct host_conf *client_conf; + + /* message validation according to Section 15.7 of RFC3315 */ + + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + + /* the message must not include a server Identifier option */ + if (optinfo->serverID.duid_len) { + dprintf(LOG_INFO, FNAME, "server ID option is included in " + "a rebind message"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* + * Locates the client's binding and verifies that the information + * from the client matches the information stored for that client. + */ + for (ia = TAILQ_FIRST(&optinfo->iapd_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (update_ia(DH6_REBIND, ia, &roptinfo.iapd_list, optinfo)) + goto fail; + } + for (ia = TAILQ_FIRST(&optinfo->iana_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (update_ia(DH6_REBIND, ia, &roptinfo.iana_list, optinfo)) + goto fail; + } + + /* + * If the returned iana/pd_list is empty, we do not have an explicit + * knowledge about validity nor invalidity for any IA_NA/PD information + * in the Rebind message. In this case, we should rather ignore the + * message than to send a Reply with empty information back to the + * client, which may annoy the recipient. However, if we have at least + * one useful information, either positive or negative, based on some + * explicit knowledge, we should reply with the responsible part. + */ + if (TAILQ_EMPTY(&roptinfo.iapd_list) && + TAILQ_EMPTY(&roptinfo.iana_list)) { + dprintf(LOG_INFO, FNAME, "no useful information for a rebind"); + goto fail; /* discard the rebind */ + } + + /* add other configuration information */ + if (set_statelessinfo(DH6_REBIND, &roptinfo)) { + dprintf(LOG_ERR, FNAME, + "failed to set other stateless information"); + goto fail; + } + + (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + dhcp6_clear_options(&roptinfo); + return (0); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +react_release(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct in6_pktinfo *pi; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct dhcp6_listval *ia; + struct host_conf *client_conf; + u_int16_t stcode; + + /* message validation according to Section 15.9 of RFC3315 */ + + /* the message must include a Server Identifier option */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + /* the contents of the Server Identifier option must match ours */ + if (duidcmp(&optinfo->serverID, &server_duid)) { + dprintf(LOG_INFO, FNAME, "server ID mismatch"); + return (-1); + } + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* + * When the server receives a Release message via unicast from a + * client to which the server has not sent a unicast option, the server + * discards the Release message and responds with a Reply message + * containing a Status Code option with value UseMulticast, a Server + * Identifier option containing the server's DUID, the Client + * Identifier option from the client message and no other options. + * [RFC3315 18.2.6] + * (Our current implementation never sends a unicast option.) + */ + if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + TAILQ_EMPTY(relayinfohead)) { + u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST; + + dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s", + addr2str(from)); + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a status code"); + goto fail; + } + server6_send(DH6_REPLY, ifp, dh6, optinfo, from, + fromlen, &roptinfo, relayinfohead, client_conf); + goto end; + } + + /* + * Locates the client's binding and verifies that the information + * from the client matches the information stored for that client. + */ + for (ia = TAILQ_FIRST(&optinfo->iapd_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (release_binding_ia(ia, &roptinfo.iapd_list, optinfo)) + goto fail; + } + for (ia = TAILQ_FIRST(&optinfo->iana_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (release_binding_ia(ia, &roptinfo.iana_list, optinfo)) + goto fail; + } + + /* + * After all the addresses have been processed, the server generates a + * Reply message and includes a Status Code option with value Success. + * [RFC3315 Section 18.2.6] + */ + stcode = DH6OPT_STCODE_SUCCESS; + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to add a status code"); + goto fail; + } + + (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + end: + dhcp6_clear_options(&roptinfo); + return (0); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +react_decline(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct in6_pktinfo *pi; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct dhcp6_listval *ia; + struct host_conf *client_conf; + u_int16_t stcode; + + /* message validation according to Section 15.8 of RFC3315 */ + + /* the message must include a Server Identifier option */ + if (optinfo->serverID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no server ID option"); + return (-1); + } + /* the contents of the Server Identifier option must match ours */ + if (duidcmp(&optinfo->serverID, &server_duid)) { + dprintf(LOG_INFO, FNAME, "server ID mismatch"); + return (-1); + } + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + /* + * When the server receives a Decline message via unicast from a + * client to which the server has not sent a unicast option, the server + * discards the Decline message and responds with a Reply message + * containing a Status Code option with value UseMulticast, a Server + * Identifier option containing the server's DUID, the Client + * Identifier option from the client message and no other options. + * [RFC3315 18.2.6] + * (Our current implementation never sends a unicast option.) + */ + if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && + TAILQ_EMPTY(relayinfohead)) { + stcode = DH6OPT_STCODE_USEMULTICAST; + + dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s", + addr2str(from)); + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to add a status code"); + goto fail; + } + server6_send(DH6_REPLY, ifp, dh6, optinfo, from, + fromlen, &roptinfo, relayinfohead, client_conf); + goto end; + } + + /* + * Locates the client's binding on IA-NA and verifies that the + * information from the client matches the information stored + * for that client. (IA-PD is just ignored [RFC3633 12.1]) + */ + for (ia = TAILQ_FIRST(&optinfo->iana_list); ia; + ia = TAILQ_NEXT(ia, link)) { + if (decline_binding_ia(ia, &roptinfo.iana_list, optinfo)) + goto fail; + } + + /* + * After all the addresses have been processed, the server generates a + * Reply message and includes a Status Code option with value Success. + * [RFC3315 Section 18.2.7] + */ + stcode = DH6OPT_STCODE_SUCCESS; + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to add a status code"); + goto fail; + } + + (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + end: + dhcp6_clear_options(&roptinfo); + return (0); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +react_confirm(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct in6_pktinfo *pi; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + struct dhcp6_list conflist; + struct dhcp6_listval *iana, *iaaddr; + struct host_conf *client_conf; + u_int16_t stcode = DH6OPT_STCODE_SUCCESS; + int error; + + /* message validation according to Section 15.5 of RFC3315 */ + + /* the message may not include a Server Identifier option */ + if (optinfo->serverID.duid_len) { + dprintf(LOG_INFO, FNAME, "server ID option found"); + return (-1); + } + /* the message must include a Client Identifier option */ + if (optinfo->clientID.duid_len == 0) { + dprintf(LOG_INFO, FNAME, "no client ID option"); + return (-1); + } + + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + /* copy client information back */ + if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + /* process authentication */ + if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) { + dprintf(LOG_INFO, FNAME, "failed to process authentication " + "information for %s", + clientstr(client_conf, &optinfo->clientID)); + goto fail; + } + + if (client_conf == NULL && ifp->pool.name) { + if ((client_conf = create_dynamic_hostconf(&optinfo->clientID, + &ifp->pool)) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to make host configuration"); + goto fail; + } + } + TAILQ_INIT(&conflist); + /* make a local copy of the configured addresses */ + if (dhcp6_copy_list(&conflist, &client_conf->addr_list)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make local data"); + goto fail; + } + + /* + * the message must include an IPv6 address to be confirmed + * [RFC3315 18.2]. (IA-PD is just ignored [RFC3633 12.1]) + */ + if (TAILQ_EMPTY(&optinfo->iana_list)) { + dprintf(LOG_INFO, FNAME, "no IA-NA option found"); + goto fail; + } + for (iana = TAILQ_FIRST(&optinfo->iana_list); iana; + iana = TAILQ_NEXT(iana, link)) { + if (TAILQ_EMPTY(&iana->sublist)) { + dprintf(LOG_INFO, FNAME, + "no IA-ADDR option found in IA-NA %d", + iana->val_ia.iaid); + goto fail; + } + + /* + * check whether the confirmed prefix matches + * the prefix from where the message originates. + * XXX: prefix length is assumed to be 64 + */ + for (iaaddr = TAILQ_FIRST(&iana->sublist); iaaddr; + iaaddr = TAILQ_NEXT(iaaddr, link)) { + + struct in6_addr *confaddr = &iaaddr->val_statefuladdr6.addr; + struct in6_addr *linkaddr; + struct sockaddr_in6 *src = (struct sockaddr_in6 *)from; + + if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr)) { + /* CONFIRM is relayed via a DHCP-relay */ + struct relayinfo *relayinfo; + + if (relayinfohead == NULL) { + dprintf(LOG_INFO, FNAME, + "no link-addr found"); + goto fail; + } + relayinfo = TAILQ_LAST(relayinfohead, relayinfolist); + + /* XXX: link-addr is supposed to be a global address */ + linkaddr = &relayinfo->linkaddr; + } else { + /* CONFIRM is directly arrived */ + linkaddr = &ifp->addr; + } + + if (memcmp(linkaddr, confaddr, 8) != 0) { + dprintf(LOG_INFO, FNAME, + "%s does not seem to belong to %s's link", + in6addr2str(confaddr, 0), + in6addr2str(linkaddr, 0)); + stcode = DH6OPT_STCODE_NOTONLINK; + goto send_reply; + } + } + } + + /* + * even when the given address seems to be on the appropriate link, + * the confirm should be ignore if there's no corrensponding IA-NA + * configuration. + */ + for (iana = TAILQ_FIRST(&optinfo->iana_list); iana; + iana = TAILQ_NEXT(iana, link)) { + if (make_ia(iana, &conflist, &roptinfo.iana_list, + client_conf, 1) == 0) { + dprintf(LOG_DEBUG, FNAME, + "IA-NA configuration not found"); + goto fail; + } + } + +send_reply: + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) + goto fail; + error = server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, client_conf); + + dhcp6_clear_options(&roptinfo); + dhcp6_clear_list(&conflist); + + return (error); + + fail: + dhcp6_clear_options(&roptinfo); + dhcp6_clear_list(&conflist); + return (-1); +} + +static int +react_informreq(ifp, dh6, len, optinfo, from, fromlen, relayinfohead) + struct dhcp6_if *ifp; + struct dhcp6 *dh6; + ssize_t len; + struct dhcp6_optinfo *optinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; +{ + struct dhcp6_optinfo roptinfo; + int error; + + /* + * An IA option is not allowed to appear in an Information-request + * message. Such a message SHOULD be discarded. + * [RFC3315 Section 15] + */ + if (!TAILQ_EMPTY(&optinfo->iapd_list)) { + dprintf(LOG_INFO, FNAME, + "information request contains an IA_PD option"); + return (-1); + } + if (!TAILQ_EMPTY(&optinfo->iana_list)) { + dprintf(LOG_INFO, FNAME, + "information request contains an IA_NA option"); + return (-1); + } + + /* if a server identifier is included, it must match ours. */ + if (optinfo->serverID.duid_len && + duidcmp(&optinfo->serverID, &server_duid)) { + dprintf(LOG_INFO, FNAME, "server DUID mismatch"); + return (-1); + } + + /* + * configure necessary options based on the options in request. + */ + dhcp6_init_options(&roptinfo); + + /* server identifier option */ + if (duidcpy(&roptinfo.serverID, &server_duid)) { + dprintf(LOG_ERR, FNAME, "failed to copy server ID"); + goto fail; + } + + /* copy client information back (if provided) */ + if (optinfo->clientID.duid_id && + duidcpy(&roptinfo.clientID, &optinfo->clientID)) { + dprintf(LOG_ERR, FNAME, "failed to copy client ID"); + goto fail; + } + + /* set stateless information */ + if (set_statelessinfo(DH6_INFORM_REQ, &roptinfo)) { + dprintf(LOG_ERR, FNAME, + "failed to set other stateless information"); + goto fail; + } + + error = server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, + &roptinfo, relayinfohead, NULL); + + dhcp6_clear_options(&roptinfo); + return (error); + + fail: + dhcp6_clear_options(&roptinfo); + return (-1); +} + +static int +update_ia(msgtype, iap, retlist, optinfo) + int msgtype; + struct dhcp6_listval *iap; + struct dhcp6_list *retlist; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_binding *binding; + struct host_conf *client_conf; + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, FNAME, + "found a host configuration named %s", client_conf->name); + } + + if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA, + iap->type, iap->val_ia.iaid)) == NULL) { + /* + * Behavior in the case where the delegating router cannot + * find a binding for the requesting router's IA_PD as + * described in RFC3633 Section 12.2. It is derived from + * Sections 18.2.3 and 18.2.4 of RFC3315, and the two sets + * of behavior are identical. + */ + dprintf(LOG_INFO, FNAME, "no binding found for %s", + duidstr(&optinfo->clientID)); + + switch (msgtype) { + case DH6_RENEW: + /* + * If the delegating router cannot find a binding for + * the requesting router's IA_PD the delegating router + * returns the IA_PD containing no prefixes with a + * Status Code option set to NoBinding in the Reply + * message. + */ + if (make_ia_stcode(iap->type, iap->val_ia.iaid, + DH6OPT_STCODE_NOBINDING, retlist)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make an option list"); + return (-1); + } + break; + case DH6_REBIND: + /* + * If it can be determined the prefixes are not + * appropriate from the delegating router's explicit + * configuration, it MAY send a Reply message to + * the requesting router containing the IA_PD with the + * lifetimes of the prefixes in the IA_PD set to zero. + * + * If unable to determine, the Rebind message is + * discarded. + * + * XXX: it is not very clear what the explicit + * configuration means. Thus, we always discard the + * message. + */ + return (-1); + default: /* XXX: should be a bug */ + dprintf(LOG_ERR, FNAME, "impossible message type %s", + dhcp6msgstr(msgtype)); + return (-1); + } + } else { /* we found a binding */ + struct dhcp6_list ialist; + struct dhcp6_listval *lv; + struct dhcp6_prefix prefix; + struct dhcp6_statefuladdr saddr; + struct dhcp6_ia ia; + + TAILQ_INIT(&ialist); + update_binding(binding); + + /* see if each information to be renewed is still valid. */ + for (lv = TAILQ_FIRST(&iap->sublist); lv; + lv = TAILQ_NEXT(lv, link)) { + struct dhcp6_listval *blv; + + switch (iap->type) { + case DHCP6_LISTVAL_IAPD: + if (lv->type != DHCP6_LISTVAL_PREFIX6) + continue; + + prefix = lv->val_prefix6; + blv = dhcp6_find_listval(&binding->val_list, + DHCP6_LISTVAL_PREFIX6, &prefix, 0); + if (blv == NULL) { + dprintf(LOG_DEBUG, FNAME, + "%s/%d is not found in %s", + in6addr2str(&prefix.addr, 0), + prefix.plen, bindingstr(binding)); + prefix.pltime = 0; + prefix.vltime = 0; + } else { + prefix.pltime = + blv->val_prefix6.pltime; + prefix.vltime = + blv->val_prefix6.vltime; + } + + if (dhcp6_add_listval(&ialist, + DHCP6_LISTVAL_PREFIX6, &prefix, NULL) + == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to copy binding info"); + dhcp6_clear_list(&ialist); + return (-1); + } + break; + case DHCP6_LISTVAL_IANA: + if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) + continue; + + saddr = lv->val_statefuladdr6; + blv = dhcp6_find_listval(&binding->val_list, + DHCP6_LISTVAL_STATEFULADDR6, &saddr, 0); + if (blv == NULL) { + dprintf(LOG_DEBUG, FNAME, + "%s is not found in %s", + in6addr2str(&saddr.addr, 0), + bindingstr(binding)); + saddr.pltime = 0; + saddr.vltime = 0; + } else { + saddr.pltime = + blv->val_statefuladdr6.pltime; + saddr.vltime = + blv->val_statefuladdr6.vltime; + } + + if (dhcp6_add_listval(&ialist, + DHCP6_LISTVAL_STATEFULADDR6, &saddr, NULL) + == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to copy binding info"); + dhcp6_clear_list(&ialist); + return (-1); + } + break; + default: + dprintf(LOG_ERR, FNAME, "unsupported IA type"); + return (-1); /* XXX */ + } + } + + memset(&ia, 0, sizeof(ia)); + ia.iaid = binding->iaid; + /* determine appropriate T1 and T2 */ + calc_ia_timo(&ia, &ialist, client_conf); + + if (dhcp6_add_listval(retlist, iap->type, + &ia, &ialist) == NULL) { + dhcp6_clear_list(&ialist); + return (-1); + } + dhcp6_clear_list(&ialist); + } + + return (0); +} + +static int +release_binding_ia(iap, retlist, optinfo) + struct dhcp6_listval *iap; + struct dhcp6_list *retlist; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_binding *binding; + + if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA, + iap->type, iap->val_ia.iaid)) == NULL) { + /* + * For each IA in the Release message for which the server has + * no binding information, the server adds an IA option using + * the IAID from the Release message and includes a Status Code + * option with the value NoBinding in the IA option. + */ + if (make_ia_stcode(iap->type, iap->val_ia.iaid, + DH6OPT_STCODE_NOBINDING, retlist)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make an option list"); + return (-1); + } + } else { + struct dhcp6_listval *lv, *lvia; + + /* + * If the IAs in the message are in a binding for the client + * and the addresses in the IAs have been assigned by the + * server to those IAs, the server deletes the addresses from + * the IAs and makes the addresses available for assignment to + * other clients. + * [RFC3315 Section 18.2.6] + * RFC3633 is not very clear about the similar case for IA_PD, + * but we apply the same logic. + */ + for (lv = TAILQ_FIRST(&iap->sublist); lv; + lv = TAILQ_NEXT(lv, link)) { + if ((lvia = find_binding_ia(lv, binding)) != NULL) { + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + dprintf(LOG_DEBUG, FNAME, + "bound prefix %s/%d " + "has been released", + in6addr2str(&lvia->val_prefix6.addr, + 0), + lvia->val_prefix6.plen); + break; + case DHCP6_LISTVAL_IANA: + release_address(&lvia->val_prefix6.addr); + dprintf(LOG_DEBUG, FNAME, + "bound address %s " + "has been released", + in6addr2str(&lvia->val_prefix6.addr, + 0)); + break; + } + + TAILQ_REMOVE(&binding->val_list, lvia, link); + dhcp6_clear_listval(lvia); + if (TAILQ_EMPTY(&binding->val_list)) { + /* + * if the binding has become empty, + * stop procedure. + */ + remove_binding(binding); + return (0); + } + } + } + } + + return (0); +} + +static int +decline_binding_ia(iap, retlist, optinfo) + struct dhcp6_listval *iap; + struct dhcp6_list *retlist; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_binding *binding; + struct dhcp6_listval *lv, *lvia; + + if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA, + iap->type, iap->val_ia.iaid)) == NULL) { + /* + * For each IA in the Decline message for which the server has + * no binding information, the server adds an IA option using + * the IAID from the Release message and includes a Status Code + * option with the value NoBinding in the IA option. + */ + if (make_ia_stcode(iap->type, iap->val_ia.iaid, + DH6OPT_STCODE_NOBINDING, retlist)) { + dprintf(LOG_NOTICE, FNAME, + "failed to make an option list"); + return (-1); + } + + return (0); + } + + /* + * If the IAs in the message are in a binding for the client and the + * addresses in the IAs have been assigned by the server to those IAs, + * the server deletes the addresses from the IAs and makes the addresses + * available for assignment to other clients. [RFC3315 Section 18.2.7] + */ + for (lv = TAILQ_FIRST(&iap->sublist); lv; + lv = TAILQ_NEXT(lv, link)) { + if (binding->iatype != DHCP6_LISTVAL_IANA) { + /* should never reach here */ + continue; + } + + if ((lvia = find_binding_ia(lv, binding)) == NULL) { + dprintf(LOG_DEBUG, FNAME, "no binding found " + "for address %s", + in6addr2str(&lv->val_statefuladdr6.addr, 0)); + continue; + } + + dprintf(LOG_DEBUG, FNAME, + "bound address %s has been marked as declined", + in6addr2str(&lvia->val_statefuladdr6.addr, 0)); + decline_address(&lvia->val_statefuladdr6.addr); + + TAILQ_REMOVE(&binding->val_list, lvia, link); + dhcp6_clear_listval(lvia); + if (TAILQ_EMPTY(&binding->val_list)) { + /* + * if the binding has become empty, + * stop procedure. + */ + remove_binding(binding); + return (0); + } + } + + return (0); +} + +static void +server6_signal(sig) + int sig; +{ + + dprintf(LOG_INFO, FNAME, "received a signal (%d)", sig); + + switch (sig) { + case SIGTERM: + sig_flags |= SIGF_TERM; + break; + } +} + +static int +server6_send(type, ifp, origmsg, optinfo, from, fromlen, + roptinfo, relayinfohead, client_conf) + int type; + struct dhcp6_if *ifp; + struct dhcp6 *origmsg; + struct dhcp6_optinfo *optinfo, *roptinfo; + struct sockaddr *from; + int fromlen; + struct relayinfolist *relayinfohead; + struct host_conf *client_conf; +{ + char replybuf[BUFSIZ]; + struct sockaddr_in6 dst; + int len, optlen; + int relayed = 0; + struct dhcp6 *dh6; + struct relayinfo *relayinfo; + + if (sizeof(struct dhcp6) > sizeof(replybuf)) { + dprintf(LOG_ERR, FNAME, "buffer size assumption failed"); + return (-1); + } + + dh6 = (struct dhcp6 *)replybuf; + len = sizeof(*dh6); + memset(dh6, 0, sizeof(*dh6)); + dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid; + dh6->dh6_msgtype = (u_int8_t)type; + + /* set options in the reply message */ + if ((optlen = dhcp6_set_options(type, (struct dhcp6opt *)(dh6 + 1), + (struct dhcp6opt *)(replybuf + sizeof(replybuf)), roptinfo)) < 0) { + dprintf(LOG_INFO, FNAME, "failed to construct reply options"); + return (-1); + } + len += optlen; + + /* calculate MAC if necessary, and put it to the message */ + switch (roptinfo->authproto) { + case DHCP6_AUTHPROTO_DELAYED: + if (client_conf == NULL || client_conf->delayedkey == NULL) { + /* This case should have been caught earlier */ + dprintf(LOG_ERR, FNAME, "authentication required " + "but not key provided"); + break; + } + if (dhcp6_calc_mac((char *)dh6, len, roptinfo->authproto, + roptinfo->authalgorithm, + roptinfo->delayedauth_offset + sizeof(*dh6), + client_conf->delayedkey)) { + dprintf(LOG_WARNING, FNAME, "failed to calculate MAC"); + return (-1); + } + break; + default: + break; /* do nothing */ + } + + /* construct a relay chain, if necessary */ + for (relayinfo = TAILQ_FIRST(relayinfohead); relayinfo; + relayinfo = TAILQ_NEXT(relayinfo, link)) { + struct dhcp6_optinfo relayopt; + struct dhcp6_vbuf relaymsgbuf; + struct dhcp6_relay *dh6relay; + + relayed = 1; + dhcp6_init_options(&relayopt); + + relaymsgbuf.dv_len = len; + relaymsgbuf.dv_buf = replybuf; + if (dhcp6_vbuf_copy(&relayopt.relay_msg, &relaymsgbuf)) + return (-1); + if (relayinfo->relay_ifid.dv_buf && + dhcp6_vbuf_copy(&relayopt.ifidopt, + &relayinfo->relay_ifid)) { + dhcp6_vbuf_free(&relayopt.relay_msg); + return (-1); + } + + /* we can safely reuse replybuf here */ + dh6relay = (struct dhcp6_relay *)replybuf; + memset(dh6relay, 0, sizeof (*dh6relay)); + dh6relay->dh6relay_msgtype = DH6_RELAY_REPLY; + dh6relay->dh6relay_hcnt = relayinfo->hcnt; + memcpy(&dh6relay->dh6relay_linkaddr, &relayinfo->linkaddr, + sizeof (dh6relay->dh6relay_linkaddr)); + memcpy(&dh6relay->dh6relay_peeraddr, &relayinfo->peeraddr, + sizeof (dh6relay->dh6relay_peeraddr)); + + len = sizeof(*dh6relay); + if ((optlen = dhcp6_set_options(DH6_RELAY_REPLY, + (struct dhcp6opt *)(dh6relay + 1), + (struct dhcp6opt *)(replybuf + sizeof(replybuf)), + &relayopt)) < 0) { + dprintf(LOG_INFO, FNAME, + "failed to construct relay message"); + dhcp6_clear_options(&relayopt); + return (-1); + } + len += optlen; + + dhcp6_clear_options(&relayopt); + } + + /* specify the destination and send the reply */ + dst = relayed ? *sa6_any_relay : *sa6_any_downstream; + dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr; + dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id; + if (transmit_sa(outsock, (struct sockaddr *)&dst, + replybuf, len) != 0) { + dprintf(LOG_ERR, FNAME, "transmit %s to %s failed", + dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); + return (-1); + } + + dprintf(LOG_DEBUG, FNAME, "transmit %s to %s", + dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); + + return (0); +} + +static int +make_ia_stcode(iatype, iaid, stcode, retlist) + int iatype; + u_int16_t stcode; + u_int32_t iaid; + struct dhcp6_list *retlist; +{ + struct dhcp6_list stcode_list; + struct dhcp6_ia ia_empty; + + memset(&ia_empty, 0, sizeof(ia_empty)); + ia_empty.iaid = iaid; + + TAILQ_INIT(&stcode_list); + if (dhcp6_add_listval(&stcode_list, DHCP6_LISTVAL_STCODE, + &stcode, NULL) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to make an option list"); + return (-1); + } + + if (dhcp6_add_listval(retlist, iatype, + &ia_empty, &stcode_list) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to make an option list"); + dhcp6_clear_list(&stcode_list); + return (-1); + } + dhcp6_clear_list(&stcode_list); + + return (0); +} + +static int +make_ia(spec, conflist, retlist, client_conf, do_binding) + struct dhcp6_listval *spec; + struct dhcp6_list *conflist, *retlist; + struct host_conf *client_conf; + int do_binding; +{ + struct dhcp6_binding *binding; + struct dhcp6_list ialist; + struct dhcp6_listval *specia; + struct dhcp6_ia ia; + int found = 0; + + /* + * If we happen to have a binding already, update the binding and + * return it. Perhaps the request is being retransmitted. + */ + if ((binding = find_binding(&client_conf->duid, DHCP6_BINDING_IA, + spec->type, spec->val_ia.iaid)) != NULL) { + struct dhcp6_list *blist = &binding->val_list; + struct dhcp6_listval *bia, *v; + + dprintf(LOG_DEBUG, FNAME, "we have a binding already: %s", + bindingstr(binding)); + + update_binding(binding); + + memset(&ia, 0, sizeof(ia)); + ia.iaid = spec->val_ia.iaid; + /* determine appropriate T1 and T2 */ + calc_ia_timo(&ia, blist, client_conf); + if (dhcp6_add_listval(retlist, spec->type, &ia, blist) + == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to copy binding info"); + return (0); + } + + /* remove bound values from the configuration */ + for (bia = TAILQ_FIRST(blist); bia; + bia = TAILQ_NEXT(bia, link)) { + if ((v = dhcp6_find_listval(conflist, + bia->type, &bia->uv, 0)) != NULL) { + TAILQ_REMOVE(conflist, v, link); + dhcp6_clear_listval(v); + } + } + + return (1); + } + + /* + * trivial case: + * if the configuration is empty, we cannot make any IA. + */ + if (TAILQ_EMPTY(conflist)) { + if (spec->type != DHCP6_LISTVAL_IANA || + client_conf->pool.name == NULL) { + return (0); + } + } + + TAILQ_INIT(&ialist); + + /* First, check if we can meet the client's requirement */ + for (specia = TAILQ_FIRST(&spec->sublist); specia; + specia = TAILQ_NEXT(specia, link)) { + /* try to find an IA that matches the spec best. */ + if (!TAILQ_EMPTY(conflist)) { + if (make_match_ia(specia, conflist, &ialist)) + found++; + } else if (spec->type == DHCP6_LISTVAL_IANA && + client_conf->pool.name != NULL) { + if (make_iana_from_pool(&client_conf->pool, specia, &ialist)) + found++; + } + } + if (found == 0) { + if (!TAILQ_EMPTY(conflist)) { + struct dhcp6_listval *v; + + /* use the first IA in the configuration list */ + for (v = TAILQ_FIRST(conflist); v; v = TAILQ_NEXT(v, link)) { + if (spec->type != DHCP6_LISTVAL_IANA) + break; /* always use the first IA for non-IANA */ + if (!is_leased(&v->val_statefuladdr6.addr)) + break; + } + if (v && dhcp6_add_listval(&ialist, v->type, &v->uv, NULL)) { + found = 1; + TAILQ_REMOVE(conflist, v, link); + dhcp6_clear_listval(v); + } + } else if (spec->type == DHCP6_LISTVAL_IANA && + client_conf->pool.name != NULL) { + if (make_iana_from_pool(&client_conf->pool, NULL, &ialist)) + found = 1; + } + } + if (found) { + memset(&ia, 0, sizeof(ia)); + ia.iaid = spec->val_ia.iaid; + /* determine appropriate T1 and T2 */ + calc_ia_timo(&ia, &ialist, client_conf); + + /* make a binding for the set if necessary */ + if (do_binding) { + if (add_binding(&client_conf->duid, DHCP6_BINDING_IA, + spec->type, spec->val_ia.iaid, &ialist) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to make a binding"); + found = 0; + } + } + if (found) { + /* make an IA for the set */ + if (dhcp6_add_listval(retlist, spec->type, + &ia, &ialist) == NULL) + found = 0; + } + dhcp6_clear_list(&ialist); + } + + return (found); +} + +static int +make_match_ia(spec, conflist, retlist) + struct dhcp6_listval *spec; + struct dhcp6_list *conflist, *retlist; +{ + struct dhcp6_listval *match; + int matched = 0; + + /* do we have the exact value specified? */ + match = dhcp6_find_listval(conflist, spec->type, &spec->uv, 0); + + /* if not, make further search specific to the IA type. */ + if (!match) { + switch (spec->type) { + case DHCP6_LISTVAL_PREFIX6: + match = dhcp6_find_listval(conflist, spec->type, + &spec->uv, MATCHLIST_PREFIXLEN); + break; + case DHCP6_LISTVAL_STATEFULADDR6: + /* No "partial match" for addresses */ + if (is_leased(&spec->val_statefuladdr6.addr)) + match = 0; + break; + default: + dprintf(LOG_ERR, FNAME, "unsupported IA type"); + return (0); /* XXX */ + } + } + + /* + * if found, remove the matched entry from the configuration list + * and copy the value in the returned list. + */ + if (match) { + if (dhcp6_add_listval(retlist, match->type, + &match->uv, NULL)) { + matched = 1; + TAILQ_REMOVE(conflist, match, link); + dhcp6_clear_listval(match); + } + } + + return (matched); +} + +/* making sublist of iana */ +static int +make_iana_from_pool(poolspec, spec, retlist) + struct dhcp6_poolspec *poolspec; + struct dhcp6_listval *spec; + struct dhcp6_list *retlist; +{ + struct dhcp6_statefuladdr saddr; + struct pool_conf *pool; + int found = 0; + + dprintf(LOG_DEBUG, FNAME, "called"); + + if ((pool = find_pool(poolspec->name)) == NULL) { + dprintf(LOG_ERR, FNAME, "pool '%s' not found", poolspec->name); + return (0); + } + + if (spec) { + memcpy(&saddr.addr, &spec->val_statefuladdr6.addr, sizeof(saddr.addr)); + if (is_available_in_pool(pool, &saddr.addr)) { + found = 1; + } + } else { + if (get_free_address_from_pool(pool, &saddr.addr)) { + found = 1; + } + } + + if (found) { + saddr.pltime = poolspec->pltime; + saddr.vltime = poolspec->vltime; + + if (!dhcp6_add_listval(retlist, DHCP6_LISTVAL_STATEFULADDR6, + &saddr, NULL)) { + return (0); + } + } + + dprintf(LOG_DEBUG, FNAME, "returns (found=%d)", found); + + return (found); +} + +static void +calc_ia_timo(ia, ialist, client_conf) + struct dhcp6_ia *ia; + struct dhcp6_list *ialist; /* this should not be empty */ + struct host_conf *client_conf; /* unused yet */ +{ + struct dhcp6_listval *iav; + u_int32_t base = DHCP6_DURATION_INFINITE; + int iatype; + + iatype = TAILQ_FIRST(ialist)->type; + for (iav = TAILQ_FIRST(ialist); iav; iav = TAILQ_NEXT(iav, link)) { + if (iav->type != iatype) { + dprintf(LOG_ERR, FNAME, + "assumption failure: IA list is not consistent"); + exit (1); /* XXX */ + } + switch (iatype) { + case DHCP6_LISTVAL_PREFIX6: + case DHCP6_LISTVAL_STATEFULADDR6: + if (base == DHCP6_DURATION_INFINITE || + iav->val_prefix6.pltime < base) + base = iav->val_prefix6.pltime; + break; + } + } + + switch (iatype) { + case DHCP6_LISTVAL_PREFIX6: + case DHCP6_LISTVAL_STATEFULADDR6: + /* + * Configure the timeout parameters as recommended in + * Section 22.4 of RFC3315 and Section 9 of RFC3633. + * We could also set the parameters to 0 if we let the client + * decide the renew timing (not implemented yet). + */ + if (base == DHCP6_DURATION_INFINITE) { + ia->t1 = DHCP6_DURATION_INFINITE; + ia->t2 = DHCP6_DURATION_INFINITE; + } else { + ia->t1 = base / 2; + ia->t2 = (base * 4) / 5; + } + break; + } +} + +static void +update_binding_duration(binding) + struct dhcp6_binding *binding; +{ + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *iav; + int duration = DHCP6_DURATION_INFINITE; + u_int32_t past, min_lifetime; + time_t now = time(NULL); + + min_lifetime = 0; + past = (u_int32_t)(now >= binding->updatetime ? + now - binding->updatetime : 0); + + switch (binding->type) { + case DHCP6_BINDING_IA: + /* + * Binding configuration is a list of IA parameters. + * Determine the minimum valid lifetime. + */ + for (iav = TAILQ_FIRST(ia_list); iav; + iav = TAILQ_NEXT(iav, link)) { + u_int32_t lifetime; + + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + lifetime = iav->val_prefix6.vltime; + break; + case DHCP6_LISTVAL_IANA: + lifetime = iav->val_statefuladdr6.vltime; + break; + default: + dprintf(LOG_ERR, FNAME, "unsupported IA type"); + return; /* XXX */ + } + + if (min_lifetime == 0 || + (lifetime != DHCP6_DURATION_INFINITE && + lifetime < min_lifetime)) + min_lifetime = lifetime; + } + + if (past < min_lifetime) + duration = min_lifetime - past; + else + duration = 0; + + break; + default: + /* should be internal error. */ + dprintf(LOG_ERR, FNAME, "unknown binding type (%d)", + binding->type); + return; + } + + binding->duration = duration; +} + +static struct dhcp6_binding * +add_binding(clientid, btype, iatype, iaid, val0) + struct duid *clientid; + dhcp6_bindingtype_t btype; + int iatype; + u_int32_t iaid; + void *val0; +{ + struct dhcp6_binding *binding = NULL; + u_int32_t duration = DHCP6_DURATION_INFINITE; + + if ((binding = malloc(sizeof(*binding))) == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to allocate memory"); + return (NULL); + } + memset(binding, 0, sizeof(*binding)); + binding->type = btype; + if (duidcpy(&binding->clientid, clientid)) { + dprintf(LOG_NOTICE, FNAME, "failed to copy DUID"); + goto fail; + } + binding->iatype = iatype; + binding->iaid = iaid; + + /* construct configuration information for this binding */ + switch (btype) { + case DHCP6_BINDING_IA: + TAILQ_INIT(&binding->val_list); + if (dhcp6_copy_list(&binding->val_list, + (struct dhcp6_list *)val0)) { + dprintf(LOG_NOTICE, FNAME, + "failed to copy binding data"); + goto fail; + } + /* lease address */ + if (iatype == DHCP6_LISTVAL_IANA) { + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *lv, *lv_next; + + for (lv = TAILQ_FIRST(ia_list); lv; lv = lv_next) { + lv_next = TAILQ_NEXT(lv, link); + + if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) { + dprintf(LOG_ERR, FNAME, + "unexpected binding value type(%d)", lv->type); + continue; + } + + if (!lease_address(&lv->val_statefuladdr6.addr)) { + dprintf(LOG_NOTICE, FNAME, + "cannot lease address %s", + in6addr2str(&lv->val_statefuladdr6.addr, 0)); + TAILQ_REMOVE(ia_list, lv, link); + dhcp6_clear_listval(lv); + } + } + if (TAILQ_EMPTY(ia_list)) { + dprintf(LOG_NOTICE, FNAME, "cannot lease any address"); + goto fail; + } + } + break; + default: + dprintf(LOG_ERR, FNAME, "unexpected binding type(%d)", btype); + goto fail; + } + + /* calculate duration and start timer accordingly */ + binding->updatetime = time(NULL); + update_binding_duration(binding); + if (binding->duration != DHCP6_DURATION_INFINITE) { + struct timeval timo; + + binding->timer = dhcp6_add_timer(binding_timo, binding); + if (binding->timer == NULL) { + dprintf(LOG_NOTICE, FNAME, "failed to add timer"); + goto fail; + } + timo.tv_sec = (long)duration; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, binding->timer); + } + + TAILQ_INSERT_TAIL(&dhcp6_binding_head, binding, link); + + dprintf(LOG_DEBUG, FNAME, "add a new binding %s", bindingstr(binding)); + + return (binding); + + fail: + if (binding) + free_binding(binding); + return (NULL); +} + +static struct dhcp6_binding * +find_binding(clientid, btype, iatype, iaid) + struct duid *clientid; + dhcp6_bindingtype_t btype; + int iatype; + u_int32_t iaid; +{ + struct dhcp6_binding *bp; + + for (bp = TAILQ_FIRST(&dhcp6_binding_head); bp; + bp = TAILQ_NEXT(bp, link)) { + if (bp->type != btype || duidcmp(&bp->clientid, clientid)) + continue; + + if (btype == DHCP6_BINDING_IA && + (bp->iatype != iatype || bp->iaid != iaid)) + continue; + + return (bp); + } + + return (NULL); +} + +static void +update_binding(binding) + struct dhcp6_binding *binding; +{ + struct timeval timo; + + dprintf(LOG_DEBUG, FNAME, "update binding %s for %s", + bindingstr(binding), duidstr(&binding->clientid)); + + /* update timestamp and calculate new duration */ + binding->updatetime = time(NULL); + update_binding_duration(binding); + + /* if the lease duration is infinite, there's nothing to do. */ + if (binding->duration == DHCP6_DURATION_INFINITE) + return; + + /* reset the timer with the duration */ + timo.tv_sec = (long)binding->duration; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, binding->timer); +} + +static void +remove_binding(binding) + struct dhcp6_binding *binding; +{ + dprintf(LOG_DEBUG, FNAME, "remove a binding %s", + bindingstr(binding)); + + if (binding->timer) + dhcp6_remove_timer(&binding->timer); + + TAILQ_REMOVE(&dhcp6_binding_head, binding, link); + + free_binding(binding); +} + +static void +free_binding(binding) + struct dhcp6_binding *binding; +{ + duidfree(&binding->clientid); + + /* free configuration info in a type dependent manner. */ + switch (binding->type) { + case DHCP6_BINDING_IA: + /* releaes address */ + if (binding->iatype == DHCP6_LISTVAL_IANA) { + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *lv; + + for (lv = TAILQ_FIRST(ia_list); lv; lv = TAILQ_NEXT(lv, link)) { + if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) { + dprintf(LOG_ERR, FNAME, + "unexpected binding value type(%d)", lv->type); + continue; + } + release_address(&lv->val_statefuladdr6.addr); + } + } + dhcp6_clear_list(&binding->val_list); + break; + default: + dprintf(LOG_ERR, FNAME, "unknown binding type %d", + binding->type); + break; + } + + free(binding); +} + +static struct dhcp6_timer * +binding_timo(arg) + void *arg; +{ + struct dhcp6_binding *binding = (struct dhcp6_binding *)arg; + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *iav, *iav_next; + time_t now = time(NULL); + u_int32_t past, lifetime; + struct timeval timo; + + past = (u_int32_t)(now >= binding->updatetime ? + now - binding->updatetime : 0); + + switch (binding->type) { + case DHCP6_BINDING_IA: + for (iav = TAILQ_FIRST(ia_list); iav; iav = iav_next) { + iav_next = TAILQ_NEXT(iav, link); + + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + case DHCP6_LISTVAL_IANA: + lifetime = iav->val_prefix6.vltime; + break; + default: + dprintf(LOG_ERR, FNAME, "internal error: " + "unknown binding type (%d)", + binding->iatype); + return (NULL); /* XXX */ + } + + if (lifetime != DHCP6_DURATION_INFINITE && + lifetime <= past) { + dprintf(LOG_DEBUG, FNAME, "bound prefix %s/%d" + " in %s has expired", + in6addr2str(&iav->val_prefix6.addr, 0), + iav->val_prefix6.plen, + bindingstr(binding)); + if (binding->iatype == DHCP6_LISTVAL_IANA) + release_address(&iav->val_prefix6.addr); + TAILQ_REMOVE(ia_list, iav, link); + dhcp6_clear_listval(iav); + } + } + + /* If all IA parameters have expired, remove the binding. */ + if (TAILQ_EMPTY(ia_list)) { + remove_binding(binding); + return (NULL); + } + + break; + default: + dprintf(LOG_ERR, FNAME, "unknown binding type %d", + binding->type); + return (NULL); /* XXX */ + } + + update_binding_duration(binding); + + /* if the lease duration is infinite, there's nothing to do. */ + if (binding->duration == DHCP6_DURATION_INFINITE) + return (NULL); + + /* reset the timer with the duration */ + timo.tv_sec = (long)binding->duration; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, binding->timer); + + return (binding->timer); +} + +static struct dhcp6_listval * +find_binding_ia(key, binding) + struct dhcp6_listval *key; + struct dhcp6_binding *binding; +{ + struct dhcp6_list *ia_list = &binding->val_list; + + switch (binding->type) { + case DHCP6_BINDING_IA: + return (dhcp6_find_listval(ia_list, key->type, &key->uv, 0)); + default: + dprintf(LOG_ERR, FNAME, "unknown binding type %d", + binding->type); + return (NULL); /* XXX */ + } +} + +static char * +bindingstr(binding) + struct dhcp6_binding *binding; +{ + static char strbuf[LINE_MAX]; /* XXX: thread unsafe */ + char *iatype = NULL; + + switch (binding->type) { + case DHCP6_BINDING_IA: + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + iatype = "PD"; + break; + case DHCP6_LISTVAL_IANA: + iatype = "NA"; + break; + } + + snprintf(strbuf, sizeof(strbuf), + "[IA: duid=%s, type=%s, iaid=%lu, duration=%lu]", + duidstr(&binding->clientid), iatype, (u_long)binding->iaid, + (u_long)binding->duration); + break; + default: + dprintf(LOG_ERR, FNAME, "unexpected binding type(%d)", + binding->type); + return ("???"); + } + + return (strbuf); +} + +static int +process_auth(dh6, len, client_conf, optinfo, roptinfo) + struct dhcp6 *dh6; + ssize_t len; + struct host_conf *client_conf; + struct dhcp6_optinfo *optinfo, *roptinfo; +{ + u_int8_t msgtype = dh6->dh6_msgtype; + int authenticated = 0; + struct keyinfo *key; + + /* + * if the client wanted DHCPv6 authentication, check if a secret + * key is available for the client. + */ + switch (optinfo->authproto) { + case DHCP6_AUTHPROTO_UNDEF: + /* + * The client did not include authentication option. What if + * we had sent authentication information? The specification + * is not clear, but we should probably accept it, since the + * client MAY ignore the information in advertise messages. + */ + return (0); + case DHCP6_AUTHPROTO_DELAYED: + if (optinfo->authalgorithm != DHCP6_AUTHALG_HMACMD5) { + dprintf(LOG_INFO, FNAME, "unknown authentication " + "algorithm (%d) required by %s", + optinfo->authalgorithm, + clientstr(client_conf, &optinfo->clientID)); + break; /* give up with this authentication */ + } + + if (optinfo->authrdm != DHCP6_AUTHRDM_MONOCOUNTER) { + dprintf(LOG_INFO, FNAME, + "unknown RDM (%d) required by %s", + optinfo->authrdm, + clientstr(client_conf, &optinfo->clientID)); + break; /* give up with this authentication */ + } + + /* see if we have a key for the client */ + if (client_conf == NULL || client_conf->delayedkey == NULL) { + dprintf(LOG_INFO, FNAME, "client %s wanted " + "authentication, but no key found", + clientstr(client_conf, &optinfo->clientID)); + break; + } + key = client_conf->delayedkey; + dprintf(LOG_DEBUG, FNAME, "found key %s for client %s", + key->name, clientstr(client_conf, &optinfo->clientID)); + + if (msgtype == DH6_SOLICIT) { + if (!(optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) { + /* + * A solicit message should not contain + * authentication information. + */ + dprintf(LOG_INFO, FNAME, + "authentication information " + "provided in solicit from %s", + clientstr(client_conf, + &optinfo->clientID)); + /* accept it anyway. (or discard?) */ + } + } else { + /* replay protection */ + if (!client_conf->saw_previous_rd) { + dprintf(LOG_WARNING, FNAME, + "previous RD value for %s is unknown " + "(accept it)", clientstr(client_conf, + &optinfo->clientID)); + } else { + if (dhcp6_auth_replaycheck(optinfo->authrdm, + client_conf->previous_rd, + optinfo->authrd)) { + dprintf(LOG_INFO, FNAME, + "possible replay attack detected " + "for client %s", + clientstr(client_conf, + &optinfo->clientID)); + break; + } + } + + if ((optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) { + dprintf(LOG_INFO, FNAME, + "client %s did not provide authentication " + "information in %s", + clientstr(client_conf, &optinfo->clientID), + dhcp6msgstr(msgtype)); + break; + } + + /* + * The client MUST use the same key used by the server + * to generate the authentication information. + * [RFC3315 Section 21.4.4.3] + * The RFC does not say what the server should do if + * the client breaks this rule, but it should be + * natural to interpret this as authentication failure. + */ + if (optinfo->delayedauth_keyid != key->keyid || + optinfo->delayedauth_realmlen != key->realmlen || + memcmp(optinfo->delayedauth_realmval, key->realm, + key->realmlen) != 0) { + dprintf(LOG_INFO, FNAME, "authentication key " + "mismatch with client %s", + clientstr(client_conf, + &optinfo->clientID)); + break; + } + + /* check for the key lifetime */ + if (dhcp6_validate_key(key)) { + dprintf(LOG_INFO, FNAME, "key %s has expired", + key->name); + break; + } + + /* validate MAC */ + if (dhcp6_verify_mac((char *)dh6, len, + optinfo->authproto, optinfo->authalgorithm, + optinfo->delayedauth_offset + sizeof(*dh6), key) + == 0) { + dprintf(LOG_DEBUG, FNAME, + "message authentication validated for " + "client %s", clientstr(client_conf, + &optinfo->clientID)); + } else { + dprintf(LOG_INFO, FNAME, "invalid message " + "authentication"); + break; + } + } + + roptinfo->authproto = optinfo->authproto; + roptinfo->authalgorithm = optinfo->authalgorithm; + roptinfo->authrdm = optinfo->authrdm; + + if (get_rdvalue(roptinfo->authrdm, &roptinfo->authrd, + sizeof(roptinfo->authrd))) { + dprintf(LOG_ERR, FNAME, "failed to get a replay " + "detection value for %s", + clientstr(client_conf, &optinfo->clientID)); + break; /* XXX: try to recover? */ + } + + roptinfo->delayedauth_keyid = key->keyid; + roptinfo->delayedauth_realmlen = key->realmlen; + roptinfo->delayedauth_realmval = + malloc(roptinfo->delayedauth_realmlen); + if (roptinfo->delayedauth_realmval == NULL) { + dprintf(LOG_ERR, FNAME, "failed to allocate memory " + "for authentication realm for %s", + clientstr(client_conf, &optinfo->clientID)); + break; + } + memcpy(roptinfo->delayedauth_realmval, key->realm, + roptinfo->delayedauth_realmlen); + + authenticated = 1; + + break; + default: + dprintf(LOG_INFO, FNAME, "client %s wanted authentication " + "with unsupported protocol (%d)", + clientstr(client_conf, &optinfo->clientID), + optinfo->authproto); + return (-1); /* or simply ignore it? */ + } + + if (authenticated == 0) { + if (msgtype != DH6_SOLICIT) { + /* + * If the message fails to pass the validation test, + * the server MUST discard the message. + * [RFC3315 Section 21.4.5.2] + */ + return (-1); + } + } else { + /* Message authenticated. Update RD counter. */ + if (msgtype != DH6_SOLICIT && client_conf != NULL) { + client_conf->previous_rd = optinfo->authrd; + client_conf->saw_previous_rd = 1; + } + } + + return (0); +} + +static inline char * +clientstr(conf, duid) + struct host_conf *conf; + struct duid *duid; +{ + if (conf != NULL) + return (conf->name); + + return (duidstr(duid)); +} diff --git a/dhcp6s.conf.5 b/dhcp6s.conf.5 new file mode 100644 index 0000000..1206764 --- /dev/null +++ b/dhcp6s.conf.5 @@ -0,0 +1,389 @@ +.\" $KAME: dhcp6s.conf.5,v 1.18 2005/01/12 06:06:12 suz Exp $ +.\" +.\" Copyright (C) 2002 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd July 29, 2004 +.Dt DHCP6S.CONF 5 +.Os KAME +.\" +.Sh NAME +.Nm dhcp6s.conf +.Nd DHCPv6 server configuration file +.\" +.Sh SYNOPSIS +.Pa /usr/local/etc/dhcp6s.conf +.\" +.Sh DESCRIPTION +The +.Nm +file contains configuration information for KAME's DHCPv6 server, +.Nm dhcp6s . +The configuration file consists of a sequence of statements terminated +by a semi-colon (`;'). +Statements are composed of tokens separated by white space, +which can be any combination of blanks, +tabs and newlines. +In some cases a set of statements is combined with a pair of brackets, +which is regarded as a single token. +Lines beginning with +.Ql # +are comments. +.Sh Interface specification +There are some statements that may or have to specify interface. +Interfaces are specified in the form of "name unit", such as +.Ar fxp0 +and +.Ar gif1. +.\" +.Sh Include statement +An include statement specifies another configuration file to be included. +The format of an include statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic include Ar \(dqfilename\(dq ; +.Xc +Where +.Ar \(dqfilename\(dq +is the name (full path) of the file to be included. +.El +.\" +.Sh Option statement +An option statement specifies configuration parameters provided for +every client. +The format of the statement is as follows. +.Bl -tag -width Ds -compact +.It Xo +.Ic option Ar option-name Op Ar option-value +; +.Xc +The following options can be specified in an option statement. +.Bl -tag -width Ds -compact +.It Xo +.Ic domain-name-servers Ar dns-address Op Ar dns-addresses... ; +.Xc +provides DNS server address(es). +Each +.Ar dns-address +must be a numeric IPv6 address. +Multiple server addresses can also be specified by a sequence of +these statements. +.It Xo +.Ic domain-name Ar \(dqdns-name\(dq ; +.Xc +provides a domain name of a DNS search path. +Multiple names in the path can be specified by a sequence of these +statements. +.It Xo +.Ic ntp-servers Ar ntp-address Op Ar ntp-addresses... ; +.Xc +provides NTP server address(es). +Each +.Ar ntp-address +must be a numeric IPv6 address. +Multiple server addresses can also be specified by a sequence of these +statements. +.It Xo +.Ic sip-server-address Ar sip-server-address Op Ar sip-server-addresses... ; +.Xc +provides SIP server address(es). +Each +.Ar sip-server-address +must be a numeric IPv6 address. +Multiple server addresses can also be specified by a sequence of +these statements. +.It Xo +.Ic sip-server-domain-name Ar \(dqsip-server-domain-name\(dq ; +.Xc +provides a domain name of a SIP server. +Multiple names in the path can be specified by a sequence of these +statements. +.It Xo +.Ic nis-server-address Ar nis-server-address Op Ar nis-server-addresses... ; +.Xc +provides NIS server address(es). +Each +.Ar nis-server-address +must be a numeric IPv6 address. +Multiple server addresses can also be specified by a sequence of +these statements. +.It Xo +.Ic nis-domain-name Ar \(dqnis-domain-name\(dq ; +.Xc +provides a NIS domain name. +Multiple names in the path can be specified by a sequence of these +statements. +.It Xo +.Ic nisp-server-address Ar nisp-server-address Op Ar nisp-server-addresses... ; +.Xc +provides NIS+ server address(es). +Each +.Ar nisp-server-address +must be a numeric IPv6 address. +Multiple server addresses can also be specified by a sequence of +these statements. +.It Xo +.Ic nisp-domain-name Ar \(dqnisp-domain-name\(dq ; +.Xc +provides a NIS+ domain name. +Multiple names in the path can be specified by a sequence of these +statements. +.It Xo +.Ic bcmcs-server-address Ar bcmcs-server-address Op Ar bcmcs-server-addresses... ; +.Xc +provides BCMCS server address(es). +Each +.Ar bcmcs-server-address +must be a numeric IPv6 address. +Multiple server addresses can also be specified by a sequence of +these statements. +.It Xo +.Ic bcmcs-server-domain-name Ar \(dqbcmcs-server-domain-name\(dq ; +.Xc +provides a domain name of a BCMCS server. +Multiple names in the path can be specified by a sequence of these +statements. +.It Xo +.Ic refreshtime Ar interval ; +.Xc +specifies the refresh time of stateless information that does not have +particular lease duration in seconds. +This option is only applicable to stateless configuration by +information-request and reply exchanges. +.El +.El +.\" +.Sh Interface statement +An interface statement specifies configuration parameters on the +interface. +The generic format of an interface statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic interface Ar interface +{ +.Ar substatements +}; +.Xc +The followings are possible +.Ar substatements +in an interface statement. +.Bl -tag -width Ds -compact +.It Xo +.Ic allow Ar allow-options +; +.Xc +This statement specifies DHCPv6 options accepted by the server. +Currently only +.Ar rapid-commit +can be specified in an +.Ic allow +statement, which specifies the server to +accept a rapid-commit option in solicit messages. +.It Ic preference Ar pref ; +This statement sets the server's preference value on the +interface to the value +.Ar pref . +The specified value will be contained in a preference option of +advertise messages. +The preference value must be a decimal integer and be between 0 and +255 (inclusive.) +.It Ic address-pool Ar pool Ar pltime Op Ar vltime ; +This statement assigns an address pool +.Ar pool +to the interface. When +.Nm +receives a allocation request for an IA-NA, it assigns one IPv6 address from this pool. +The specified pool name will be defined in a pool statement. +Regarding the +.Ar pltime +and +.Ar vltime +, please see the explanation in the +.Ar prefix +substatement in host statement section. +.El +.El +.\" +.Sh Host statement +A host statement specifies configuration parameters for a particular +client. +The generic format of a host statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic host Ar name +{ +.Ar substatements +}; +.Xc +.Ar name +is an arbitrary string. +It does not affect server's behavior but is provided for +readability of log messages. +Possible substatements are as follows. +.Bl -tag -width Ds -compact +.It Ic duid Ar ID ; +This statement defines the client's DHCP unique identifier +.Pq DUID . +.Ar ID +is a colon-separated hexadecimal sequence where each separated part +must be composed of two hexadecimal values. +This statement is used to identify a particular host by the server +and must be included in a host statement. +.It Ic prefix Ar ipv6-prefix pltime Op Ar vltime ; +This statement specifies an IPv6 prefix to be delegated to the client. +.Ar ipv6-prefix +is a string representing a valid IPv6 prefix +.Pq see the example below . +.Ar pltime +and +.Ar vltime +are preferred and valid lifetimes of the prefix, respectively. +When the latter is omitted, it will be set to the same value of +.Ar pltime. +A positive decimal number or a special string +.Ic infinity +can be specified as a lifetime. +A decimal number provides the lifetime in seconds, +while +.Ic infinity +means the corresponding lifetime never expires. +When both lifetimes are specified, +.Ar pltime +must not be larger than +.Ar vltime . +Multiple prefixes can be specified, +each of which is given by a single +.Ic prefix +statement. +In that case, +all or some of the specified prefixes will be delegated to the client, +based on required parameters by the client. +.It Ic address Ar ipv6-address pltime Op Ar vltime ; +This statement specifies an IPv6 address to be assigned to the client. +Everything is same as +.Ar prefix +option, except that you do not need specify prefix length. +.It Ic delayedkey Ar keyname ; +This statement specifies a secret key shared with the client for the DHCPv6 +delayed authentication protocol. +.Ar keyname +is a string that identifies a particular set of key parameters. +A separate +.Ic keyinfo +statement for +.Ar keyname +must be provided in the configuration file. +When this statement is specified and the client includes an +authentication option for the delayed authentication protocol in a +Solicit message, +.Ic dhcp6s +will perform the authentication protocol for succeeding message +exchanges. +.El +.El +.\" +.Sh Pool statement +A pool statement specifies an address pool for a particular interface. +The generic format of a pool statement is as follows: +.Bl -tag -width Ds -compact +.It Xo +.Ic pool Ar name +{ +.Ar substatements ; +}; +.Xc +.Ar name +is an arbitrary string. +It does not affect server's behavior but is provided for +readability of log messages. +Possible substatements are as follows. +.Bl -tag -width Ds -compact +.It Ic range Ar min-addr Ic to Ar max-addr +This substatement defines the range of addresses allocated for the pool, +i.e. from +.Ar min-addr +to +.Ar max-addr. +.El +.El +.\" +.Sh Keyinfo statement +This statement defines a secret key shared with a client to +authenticate DHCPv6 messages. +The format and the description of this statement is provided in +.Xr dhcp6c.conf 5 . +One important difference in the server configuration is, +however, +the +.Ar keyname +is referred from a +.Ic host +statement as described above. +.\" +.Sh Examples +The followings are a sample configuration to provide a DNS server +address for every client as well as to delegate a permanent IPv6 +prefix 2001:db8:1111::/48 to a client whose DUID is 00:01:00:01:aa:bb. +.Bd -literal -offset +option domain-name-servers 2001:db8::35; + +host kame { + duid 00:01:00:01:aa:bb; + prefix 2001:db8:1111::/48 infinity; +}; +.Ed +.Pp +If a shared secret should be configured in both the server and the +client for DHCPv6 authentication, +it would be specified in the configuration file as follows: +.Bd -literal -offset +keyinfo kame { + realm "kame.net"; + keyid 1; + secret "5pvW2g48OHPvkYMJSw0vZA=="; +}; +.Ed +.Pp +And the +.Ic host +statement would be modified as follows: +.Bd -literal -offset +host kame { + duid 00:01:00:01:aa:bb; + prefix 2001:db8:1111::/48 infinity; + delayedkey kame; +}; +.Ed +.Sh SEE ALSO +.Xr dhcp6c.conf 5 +.Xr dhcp6s 8 +.\" +.Sh HISTORY +The +.Nm +configuration file first appeared in the WIDE/KAME IPv6 protocol +stack kit. diff --git a/dhcp6s.conf.sample b/dhcp6s.conf.sample new file mode 100644 index 0000000..a3f5f27 --- /dev/null +++ b/dhcp6s.conf.sample @@ -0,0 +1,25 @@ +# The followings are a sample configuration to provide a DNS server address +# for every client as well as to delegate a permanent IPv6 prefix +# 2001:db8:1111::/48 to a client whose DUID is 00:01:00:01:aa:bb. + +option domain-name-servers 2001:db8::35; + +host kame { + duid 00:01:00:01:aa:bb; + prefix 2001:db8:1111::/48 infinity; +}; + +# The followings are a sample configuration to provide an IPv6 address +# from an address pool 2001:db8:1:2::1000-2000 for 3600[s]. +# Note. You have to send an RA to fxp0; otherwise a client cannot be sure +# about the prefix-length and the default router. If you want to prevent +# stateless address configuration via RA, please set the autonomous-flag to +# OFF in your RA configuration. + +interface fxp0 { + address-pool pool1 3600; +}; + +pool pool1 { + range 2001:db8:1:2::1000 to 2001:db8:1:2::2000 ; +}; diff --git a/if.c b/if.c new file mode 100644 index 0000000..61a7fd6 --- /dev/null +++ b/if.c @@ -0,0 +1,184 @@ +/* $KAME: if.c,v 1.6 2005/09/16 11:30:15 suz Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include + +#include +#include +#ifdef __KAME__ +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern int errno; + +struct dhcp6_if *dhcp6_if; + +struct dhcp6_if * +ifinit(ifname) + char *ifname; +{ + struct dhcp6_if *ifp; + + if ((ifp = find_ifconfbyname(ifname)) != NULL) { + dprintf(LOG_NOTICE, FNAME, "duplicated interface: %s", ifname); + return (NULL); + } + + if ((ifp = malloc(sizeof(*ifp))) == NULL) { + dprintf(LOG_ERR, FNAME, "malloc failed"); + goto fail; + } + memset(ifp, 0, sizeof(*ifp)); + + TAILQ_INIT(&ifp->event_list); + + if ((ifp->ifname = strdup(ifname)) == NULL) { + dprintf(LOG_ERR, FNAME, "failed to copy ifname"); + goto fail; + } + + if (ifreset(ifp)) + goto fail; + + TAILQ_INIT(&ifp->reqopt_list); + TAILQ_INIT(&ifp->iaconf_list); + + ifp->authproto = DHCP6_AUTHPROTO_UNDEF; + ifp->authalgorithm = DHCP6_AUTHALG_UNDEF; + ifp->authrdm = DHCP6_AUTHRDM_UNDEF; + + { + struct ifaddrs *ifa, *ifap; + struct sockaddr_in6 *sin6; + + if (getifaddrs(&ifap) < 0) { + dprintf(LOG_ERR, FNAME, "getifaddrs failed: %s", + strerror(errno)); + goto fail; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, ifname) != 0) + continue; + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + continue; + + ifp->addr = sin6->sin6_addr; + } + + freeifaddrs(ifap); + } + + ifp->next = dhcp6_if; + dhcp6_if = ifp; + return (ifp); + + fail: + if (ifp->ifname != NULL) + free(ifp->ifname); + free(ifp); + return (NULL); +} + +int +ifreset(ifp) + struct dhcp6_if *ifp; +{ + unsigned int ifid; + u_int32_t linkid; + + if ((ifid = if_nametoindex(ifp->ifname)) == 0) { + dprintf(LOG_ERR, FNAME, "invalid interface(%s): %s", + ifp->ifname, strerror(errno)); + return (-1); + } + +#ifdef HAVE_SCOPELIB + if (inet_zoneid(AF_INET6, 2, ifname, &linkid)) { + dprintf(LOG_ERR, FNAME, "failed to get link ID for %s", + ifname); + return (-1); + } +#else + linkid = ifid; /* XXX: assume 1to1 mapping IFs and links */ +#endif + + ifp->ifid = ifid; + ifp->linkid = linkid; + + return (0); +} + +struct dhcp6_if * +find_ifconfbyname(ifname) + char *ifname; +{ + struct dhcp6_if *ifp; + + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + if (strcmp(ifp->ifname, ifname) == 0) + return (ifp); + } + + return (NULL); +} + +struct dhcp6_if * +find_ifconfbyid(id) + unsigned int id; +{ + struct dhcp6_if *ifp; + + for (ifp = dhcp6_if; ifp; ifp = ifp->next) { + if (ifp->ifid == id) + return (ifp); + } + + return (NULL); +} diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ebc6691 --- /dev/null +++ b/install-sh @@ -0,0 +1,250 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/lease.c b/lease.c new file mode 100644 index 0000000..d46ddfd --- /dev/null +++ b/lease.c @@ -0,0 +1,328 @@ +#include +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include +#include "dhcp6.h" +#include "config.h" +#include "common.h" + +#ifndef FALSE +#define FALSE 0 +#define TRUE !FALSE +#endif + +struct hash_entry { + LIST_ENTRY(hash_entry) list; + char *val; + char flag; /* 0x01: DHCP6_LEASE_DECLINED */ +}; + +/* marked as declined (e.g. someone has been using the same address) */ +#define DHCP6_LEASE_DECLINED 0x01 + +LIST_HEAD(hash_head, hash_entry); + +typedef unsigned int (*pfn_hash_t)(void *val) ; +typedef int (*pfh_hash_match_t)(void *val1, void *val2); + +struct hash_table { + struct hash_head *table; + unsigned int size; + pfn_hash_t hash; + pfh_hash_match_t match; +}; + +#ifndef DHCP6_LEASE_TABLE_SIZE +#define DHCP6_LEASE_TABLE_SIZE 256 +#endif + +static struct hash_table dhcp6_lease_table; + +static unsigned int in6_addr_hash __P((void *)); +static int in6_addr_match __P((void *, void *)); + +static int hash_table_init __P((struct hash_table *, unsigned int, + pfn_hash_t, pfh_hash_match_t)); +static void hash_table_cleanup __P((struct hash_table *)); +static int hash_table_add __P((struct hash_table *, void *, unsigned int)); +static int hash_table_remove __P((struct hash_table *, void *)); +static struct hash_entry * hash_table_find __P((struct hash_table *, void *)); + +int +lease_init(void) +{ + dprintf(LOG_DEBUG, FNAME, "called"); + + if (hash_table_init(&dhcp6_lease_table, DHCP6_LEASE_TABLE_SIZE, + in6_addr_hash, in6_addr_match) != 0) { + return (-1); + } + + return (0); +} + +void +lease_cleanup(void) +{ + hash_table_cleanup(&dhcp6_lease_table); +} + +int +lease_address(addr) + struct in6_addr *addr; +{ + if (!addr) + return (FALSE); + + dprintf(LOG_DEBUG, FNAME, "addr=%s", in6addr2str(addr, 0)); + + if (hash_table_find(&dhcp6_lease_table, addr)) { + dprintf(LOG_WARNING, FNAME, "already leased: %s", + in6addr2str(addr, 0)); + return (FALSE); + } + + if (hash_table_add(&dhcp6_lease_table, addr, sizeof(*addr)) != 0) { + return (FALSE); + } + + return (TRUE); +} + +void +release_address(addr) + struct in6_addr *addr; +{ + if (!addr) + return; + + dprintf(LOG_DEBUG, FNAME, "addr=%s", in6addr2str(addr, 0)); + + if (hash_table_remove(&dhcp6_lease_table, addr) != 0) { + dprintf(LOG_WARNING, FNAME, "not found: %s", in6addr2str(addr, 0)); + } +} + +void +decline_address(addr) + struct in6_addr *addr; +{ + struct hash_entry *entry; + + if (!addr) + return; + + dprintf(LOG_DEBUG, FNAME, "addr=%s", in6addr2str(addr, 0)); + + entry = hash_table_find(&dhcp6_lease_table, addr); + if (entry == NULL) { + dprintf(LOG_WARNING, FNAME, "not found: %s", + in6addr2str(addr, 0)); + return; + } + + entry->flag |= DHCP6_LEASE_DECLINED; +} + +int +is_leased(addr) + struct in6_addr *addr; +{ + return (hash_table_find(&dhcp6_lease_table, addr) != NULL); +} + +static unsigned int +in6_addr_hash(val) + void *val; +{ + u_int8_t *addr = ((struct in6_addr *)val)->s6_addr; + unsigned int hash = 0; + int i; + + for (i = 0; i < 16; i++) { + hash += addr[i]; + } + + return (hash); +} + +static int +in6_addr_match(val1, val2) + void *val1, *val2; +{ + struct in6_addr * addr1 = val1; + struct in6_addr * addr2 = val2; + + return (memcmp(addr1->s6_addr, addr2->s6_addr, 16) == 0); +} + +/* + * hash table + */ +static int +hash_table_init(table, size, hash, match) + struct hash_table *table; + unsigned int size; + pfn_hash_t hash; + pfh_hash_match_t match; +{ + int i; + + if (!table || !hash || !match) { + return (-1); + } + + if ((table->table = malloc(sizeof(*table->table) * size)) == NULL) { + return (-1); + } + + for (i = 0; i < size; i++) + LIST_INIT(&table->table[i]); + + table->size = size; + table->hash = hash; + table->match = match; + + return (0); +} + +static void +hash_table_cleanup(table) + struct hash_table *table; +{ + int i; + + if (!table) { + return; + } + + for (i = 0; i < table->size; i++) { + while (!LIST_EMPTY(&table->table[i])) { + struct hash_entry *entry = LIST_FIRST(&table->table[i]); + LIST_REMOVE(entry, list); + if (entry->val) + free(entry->val); + free(entry); + } + } + free(table->table); + memset(table, 0, sizeof(*table)); +} + +static int +hash_table_add(table, val, size) + struct hash_table *table; + void *val; + unsigned int size; +{ + struct hash_entry *entry = NULL; + int i = 0; + + if (!table || !val) { + return (-1); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + return (-1); + } + memset(entry, 0, sizeof(*entry)); + + if ((entry->val = malloc(size)) == NULL) { + return (-1); + } + memcpy(entry->val, val, size); + + i = table->hash(val) % table->size; + LIST_INSERT_HEAD(&table->table[i], entry, list); + + return (0); +} + +static int +hash_table_remove(table, val) + struct hash_table *table; + void *val; +{ + struct hash_entry *entry; + + if (!table || !val) { + return (-1); + } + + if ((entry = hash_table_find(table, val)) == NULL) { + return (-1); + } + + LIST_REMOVE(entry, list); + if (entry->val) + free(entry->val); + free(entry); + + return (0); +} + +static struct hash_entry * +hash_table_find(table, val) + struct hash_table *table; + void *val; +{ + struct hash_entry *entry; + int i; + + if (!table || !val) { + return (NULL); + } + + i = table->hash(val) % table->size; + LIST_FOREACH(entry, &table->table[i], list) + { + if (table->match(val, entry->val)) { + return (entry); + } + } + + return (NULL); +} + diff --git a/lease.h b/lease.h new file mode 100644 index 0000000..fec7867 --- /dev/null +++ b/lease.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1998 and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __LEASE_H_DEFINED +#define __LEASE_H_DEFINED + +extern int lease_init __P((void)); +extern void lease_cleanup __P((void)); +extern int lease_address __P((struct in6_addr *)); +extern void release_address __P((struct in6_addr *)); +extern void decline_address __P((struct in6_addr *)); +extern int is_leased __P((struct in6_addr *)); + +#endif diff --git a/missing/arc4random.c b/missing/arc4random.c new file mode 100644 index 0000000..8d1e050 --- /dev/null +++ b/missing/arc4random.c @@ -0,0 +1,67 @@ +/* $KAME: arc4random.c,v 1.1 2003/01/22 01:30:36 jinmei Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * a stub function to make random() to return good random numbers. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "arc4random.h" + +static int fd = -1; + +static void +arc4random_init() +{ + + fd = open("/dev/urandom", O_RDONLY, 0600); + if (fd < 0) { + err(1, "/dev/urandom"); + /*NOTREACHED*/ + } +} + +u_int32_t +arc4random() +{ + u_int32_t v; + + if (fd < 0) + arc4random_init(); + read(fd, &v, sizeof(v)); + return v; +} diff --git a/missing/arc4random.h b/missing/arc4random.h new file mode 100644 index 0000000..074c100 --- /dev/null +++ b/missing/arc4random.h @@ -0,0 +1,37 @@ +/* $KAME: arc4random.h,v 1.1 2003/01/22 01:30:36 jinmei Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __sun__ +#define __P(x) x +typedef uint32_t u_int32_t; +#endif + +extern u_int32_t arc4random __P((void)); diff --git a/missing/daemon.c b/missing/daemon.c new file mode 100644 index 0000000..9bf6416 --- /dev/null +++ b/missing/daemon.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +int +daemon(int nochdir, int noclose) +{ + if (fork() != 0) + _exit(0); + if (nochdir == 0) + (void) chdir("/"); + if (noclose == 0) { + (void) close(0); + (void) open("/dev/null", O_RDWR); + (void) dup2(0, 1); + (void) dup2(0, 2); + } + (void) setsid(); + if (fork() != 0) + _exit(0); + return (0); +} diff --git a/missing/err.h b/missing/err.h new file mode 100644 index 0000000..dfc41d5 --- /dev/null +++ b/missing/err.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void err(int, const char *); +/* PRINTFLIKE2 */ +extern void errx(int, const char *, ...); +/* PRINTFLIKE1 */ +extern void warnx(const char *, ...); +#define warn warnx diff --git a/missing/getifaddrs.c b/missing/getifaddrs.c new file mode 100644 index 0000000..4320c43 --- /dev/null +++ b/missing/getifaddrs.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2006 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ifaddrs.h" + +static int +get_lifreq(int fd, struct lifreq **ifr_ret) +{ + struct lifnum lifn; + struct lifconf lifc; + struct lifreq *lifrp; + + lifn.lifn_family = AF_UNSPEC; + lifn.lifn_flags = 0; + if (ioctl(fd, SIOCGLIFNUM, &lifn) == -1) + lifn.lifn_count = 16; + else + lifn.lifn_count += 16; + + for (;;) { + lifc.lifc_len = lifn.lifn_count * sizeof (*lifrp); + lifrp = malloc(lifc.lifc_len); + if (lifrp == NULL) + return (-1); + + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_flags = 0; + lifc.lifc_buf = (char *)lifrp; + if (ioctl(fd, SIOCGLIFCONF, &lifc) == -1) { + free(lifrp); + if (errno == EINVAL) { + lifn.lifn_count <<= 1; + continue; + } + (void) close(fd); + return (-1); + } + if (lifc.lifc_len < (lifn.lifn_count - 1) * sizeof (*lifrp)) + break; + free(lifrp); + lifn.lifn_count <<= 1; + } + (void) close(fd); + + *ifr_ret = lifrp; + + return (lifc.lifc_len / sizeof (*lifrp)); +} + +static size_t +nbytes(const struct lifreq *lifrp, int nlif, size_t socklen) +{ + size_t len = 0; + size_t slen; + + while (nlif > 0) { + slen = strlen(lifrp->lifr_name) + 1; + len += sizeof (struct ifaddrs) + ((slen + 3) & ~3); + len += 3 * socklen; + lifrp++; + nlif--; + } + return (len); +} + +static struct sockaddr * +addrcpy(struct sockaddr_storage *addr, char **bufp) +{ + char *buf = *bufp; + size_t len; + + len = addr->ss_family == AF_INET ? sizeof (struct sockaddr_in) : + sizeof (struct sockaddr_in6); + (void) memcpy(buf, addr, len); + *bufp = buf + len; + return ((struct sockaddr *)buf); +} + +static int +populate(struct ifaddrs *ifa, int fd, struct lifreq *lifrp, int nlif, int af, + char **bufp) +{ + char *buf = *bufp; + size_t slen; + + while (nlif > 0) { + ifa->ifa_next = (nlif > 1) ? ifa + 1 : NULL; + (void) strcpy(ifa->ifa_name = buf, lifrp->lifr_name); + slen = strlen(lifrp->lifr_name) + 1; + buf += (slen + 3) & ~3; + if (ioctl(fd, SIOCGLIFFLAGS, lifrp) == -1) + ifa->ifa_flags = 0; + else + ifa->ifa_flags = lifrp->lifr_flags; + if (ioctl(fd, SIOCGLIFADDR, lifrp) == -1) + ifa->ifa_addr = NULL; + else + ifa->ifa_addr = addrcpy(&lifrp->lifr_addr, &buf); + if (ioctl(fd, SIOCGLIFNETMASK, lifrp) == -1) + ifa->ifa_netmask = NULL; + else + ifa->ifa_netmask = addrcpy(&lifrp->lifr_addr, &buf); + if (ifa->ifa_flags & IFF_POINTOPOINT) { + if (ioctl(fd, SIOCGLIFDSTADDR, lifrp) == -1) + ifa->ifa_dstaddr = NULL; + else + ifa->ifa_dstaddr = + addrcpy(&lifrp->lifr_dstaddr, &buf); + } else if (ifa->ifa_flags & IFF_BROADCAST) { + if (ioctl(fd, SIOCGLIFBRDADDR, lifrp) == -1) + ifa->ifa_broadaddr = NULL; + else + ifa->ifa_broadaddr = + addrcpy(&lifrp->lifr_broadaddr, &buf); + } else { + ifa->ifa_dstaddr = NULL; + } + + ifa++; + nlif--; + lifrp++; + } + *bufp = buf; + return (0); +} + +int +getifaddrs(struct ifaddrs **ifap) +{ + int fd4, fd6; + int nif4, nif6 = 0; + struct lifreq *ifr4 = NULL; + struct lifreq *ifr6 = NULL; + struct ifaddrs *ifa = NULL; + char *buf; + + if ((fd4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return (-1); + if ((fd6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1 && + errno != EAFNOSUPPORT) { + (void) close(fd4); + return (-1); + } + + if ((nif4 = get_lifreq(fd4, &ifr4)) == -1 || + (fd6 != -1 && (nif6 = get_lifreq(fd6, &ifr6)) == -1)) + goto failure; + + if (nif4 == 0 && nif6 == 0) { + *ifap = NULL; + return (0); + } + + ifa = malloc(nbytes(ifr4, nif4, sizeof (struct sockaddr_in)) + + nbytes(ifr6, nif6, sizeof (struct sockaddr_in6))); + if (ifa == NULL) + goto failure; + + buf = (char *)(ifa + nif4 + nif6); + + if (populate(ifa, fd4, ifr4, nif4, AF_INET, &buf) == -1) + goto failure; + if (nif4 > 0 && nif6 > 0) + ifa[nif4 - 1].ifa_next = ifa + nif4; + if (populate(ifa + nif4, fd6, ifr6, nif6, AF_INET6, &buf) == -1) + goto failure; + + return (0); + +failure: + free(ifa); + (void) close(fd4); + if (fd6 != -1) + (void) close(fd6); + free(ifr4); + free(ifr6); + return (-1); +} + +void +freeifaddrs(struct ifaddrs *ifa) +{ + free(ifa); +} diff --git a/missing/ifaddrs.h b/missing/ifaddrs.h new file mode 100644 index 0000000..0bf5f92 --- /dev/null +++ b/missing/ifaddrs.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2006 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#undef ifa_broadaddr +#undef ifa_dstaddr +struct ifaddrs { + struct ifaddrs *ifa_next; /* Pointer to next struct */ + char *ifa_name; /* Interface name */ + uint64_t ifa_flags; /* Interface flags */ + struct sockaddr *ifa_addr; /* Interface address */ + struct sockaddr *ifa_netmask; /* Interface netmask */ + struct sockaddr *ifa_dstaddr; /* P2P interface destination */ +}; +#define ifa_broadaddr ifa_dstaddr + +extern int getifaddrs(struct ifaddrs **); +extern void freeifaddrs(struct ifaddrs *); diff --git a/missing/strlcat.c b/missing/strlcat.c new file mode 100644 index 0000000..28503e5 --- /dev/null +++ b/missing/strlcat.c @@ -0,0 +1,75 @@ +/* $NetBSD: strlcat.c,v 1.5 1999/09/20 04:39:47 lukem Exp $ */ +/* from OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: strlcat.c,v 1.5 1999/09/20 04:39:47 lukem Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (*d != '\0' && n-- != 0) + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/missing/strlcpy.c b/missing/strlcpy.c new file mode 100644 index 0000000..c6b0d3c --- /dev/null +++ b/missing/strlcpy.c @@ -0,0 +1,72 @@ +/* $NetBSD: strlcpy.c,v 1.5 1999/09/20 04:39:47 lukem Exp $ */ +/* from OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: strlcpy.c,v 1.5 1999/09/20 04:39:47 lukem Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/missing/sys/queue.h b/missing/sys/queue.h new file mode 100644 index 0000000..027f495 --- /dev/null +++ b/missing/sys/queue.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1991, 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. + * + * Adapted from FreeBSD sys/queue.h by James Carlson + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.32.2.6 2001/12/18 10:09:02 ru Exp $ + */ + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ +} while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) diff --git a/missing/warnx.c b/missing/warnx.c new file mode 100644 index 0000000..75e1a96 --- /dev/null +++ b/missing/warnx.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +void +err(int retv, const char *str) +{ + (void) fprintf(stderr, "%s\n", str); + exit(retv); +} + +void +errx(int retv, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + (void) vfprintf(stderr, fmt, args); + va_end(args); + exit(retv); +} + +void +warnx(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + (void) vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/prefixconf.c b/prefixconf.c new file mode 100644 index 0000000..ace6ade --- /dev/null +++ b/prefixconf.c @@ -0,0 +1,517 @@ +/* $KAME: prefixconf.c,v 1.33 2005/09/16 11:30:15 suz Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include +#ifdef __FreeBSD__ +#include +#endif + +#include + +#ifdef __KAME__ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "timer.h" +#include "dhcp6c_ia.h" +#include "prefixconf.h" + +TAILQ_HEAD(siteprefix_list, siteprefix); +struct iactl_pd { + struct iactl common; + struct pifc_list *pifc_head; + struct siteprefix_list siteprefix_head; +}; +#define iacpd_ia common.iactl_ia +#define iacpd_callback common.callback +#define iacpd_isvalid common.isvalid +#define iacpd_duration common.duration +#define iacpd_renew_data common.renew_data +#define iacpd_rebind_data common.rebind_data +#define iacpd_reestablish_data common.reestablish_data +#define iacpd_release_data common.release_data +#define iacpd_cleanup common.cleanup + +struct siteprefix { + TAILQ_ENTRY (siteprefix) link; + + struct dhcp6_prefix prefix; + time_t updatetime; + struct dhcp6_timer *timer; + struct iactl_pd *ctl; + TAILQ_HEAD(, dhcp6_ifprefix) ifprefix_list; /* interface prefixes */ +}; + +struct dhcp6_ifprefix { + TAILQ_ENTRY(dhcp6_ifprefix) plink; + + /* interface configuration */ + struct prefix_ifconf *ifconf; + + /* interface prefix parameters */ + struct sockaddr_in6 paddr; + int plen; + + /* address assigned on the interface based on the prefix */ + struct sockaddr_in6 ifaddr; +}; + +static struct siteprefix *find_siteprefix __P((struct siteprefix_list *, + struct dhcp6_prefix *, int)); +static void remove_siteprefix __P((struct siteprefix *)); +static int isvalid __P((struct iactl *)); +static u_int32_t duration __P((struct iactl *)); +static void cleanup __P((struct iactl *)); +static int renew_prefix __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); +static void renew_data_free __P((struct dhcp6_eventdata *)); + +static struct dhcp6_timer *siteprefix_timo __P((void *)); + +static int add_ifprefix __P((struct siteprefix *, + struct dhcp6_prefix *, struct prefix_ifconf *)); + +extern struct dhcp6_timer *client6_timo __P((void *)); +static int pd_ifaddrconf __P((ifaddrconf_cmd_t, struct dhcp6_ifprefix *ifpfx)); + +int +update_prefix(ia, pinfo, pifc, dhcpifp, ctlp, callback) + struct ia *ia; + struct dhcp6_prefix *pinfo; + struct pifc_list *pifc; + struct dhcp6_if *dhcpifp; + struct iactl **ctlp; + void (*callback)__P((struct ia *)); +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)*ctlp; + struct siteprefix *sp; + struct prefix_ifconf *pif; + int spcreate = 0; + struct timeval timo; + + /* + * A client discards any addresses for which the preferred + * lifetime is greater than the valid lifetime. + * [RFC3315 22.6] + */ + if (pinfo->vltime != DHCP6_DURATION_INFINITE && + (pinfo->pltime == DHCP6_DURATION_INFINITE || + pinfo->pltime > pinfo->vltime)) { + dprintf(LOG_INFO, FNAME, "invalid prefix %s/%d: " + "pltime (%lu) is larger than vltime (%lu)", + in6addr2str(&pinfo->addr, 0), pinfo->plen, + pinfo->pltime, pinfo->vltime); + return (-1); + } + + if (iac_pd == NULL) { + if ((iac_pd = malloc(sizeof(*iac_pd))) == NULL) { + dprintf(LOG_NOTICE, FNAME, "memory allocation failed"); + return (-1); + } + memset(iac_pd, 0, sizeof(*iac_pd)); + iac_pd->iacpd_ia = ia; + iac_pd->iacpd_callback = callback; + iac_pd->iacpd_isvalid = isvalid; + iac_pd->iacpd_duration = duration; + iac_pd->iacpd_cleanup = cleanup; + iac_pd->iacpd_renew_data = + iac_pd->iacpd_rebind_data = + iac_pd->iacpd_release_data = + iac_pd->iacpd_reestablish_data = renew_prefix; + + iac_pd->pifc_head = pifc; + TAILQ_INIT(&iac_pd->siteprefix_head); + *ctlp = (struct iactl *)iac_pd; + } + + /* search for the given prefix, and make a new one if it fails */ + if ((sp = find_siteprefix(&iac_pd->siteprefix_head, pinfo, 1)) == NULL) { + if ((sp = malloc(sizeof(*sp))) == NULL) { + dprintf(LOG_NOTICE, FNAME, "memory allocation failed"); + return (-1); + } + memset(sp, 0, sizeof(*sp)); + sp->prefix.addr = pinfo->addr; + sp->prefix.plen = pinfo->plen; + sp->ctl = iac_pd; + TAILQ_INIT(&sp->ifprefix_list); + + TAILQ_INSERT_TAIL(&iac_pd->siteprefix_head, sp, link); + + spcreate = 1; + } + + /* update the timestamp of update */ + sp->updatetime = time(NULL); + + /* update the prefix according to pinfo */ + sp->prefix.pltime = pinfo->pltime; + sp->prefix.vltime = pinfo->vltime; + dprintf(LOG_DEBUG, FNAME, "%s a prefix %s/%d pltime=%lu, vltime=%lu", + spcreate ? "create" : "update", + in6addr2str(&pinfo->addr, 0), pinfo->plen, + pinfo->pltime, pinfo->vltime); + + /* update prefix interfaces if necessary */ + if (sp->prefix.vltime != 0 && spcreate) { + for (pif = TAILQ_FIRST(iac_pd->pifc_head); pif; + pif = TAILQ_NEXT(pif, link)) { + /* + * The requesting router MUST NOT assign any delegated + * prefixes or subnets from the delegated prefix(es) to + * the link through which it received the DHCP message + * from the delegating router. + * [RFC3633 Section 12.1] + */ + if (strcmp(pif->ifname, dhcpifp->ifname) == 0) { + dprintf(LOG_INFO, FNAME, + "skip %s as a prefix interface", + dhcpifp->ifname); + continue; + } + + add_ifprefix(sp, pinfo, pif); + } + } + + /* + * If the new vltime is 0, this prefix immediately expires. + * Otherwise, set up or update the associated timer. + */ + switch (sp->prefix.vltime) { + case 0: + remove_siteprefix(sp); + break; + case DHCP6_DURATION_INFINITE: + if (sp->timer) + dhcp6_remove_timer(&sp->timer); + break; + default: + if (sp->timer == NULL) { + sp->timer = dhcp6_add_timer(siteprefix_timo, sp); + if (sp->timer == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to add prefix timer"); + remove_siteprefix(sp); /* XXX */ + return (-1); + } + } + /* update the timer */ + timo.tv_sec = sp->prefix.vltime; + timo.tv_usec = 0; + + dhcp6_set_timer(&timo, sp->timer); + break; + } + + return (0); +} + +static struct siteprefix * +find_siteprefix(head, prefix, match_plen) + struct siteprefix_list *head; + struct dhcp6_prefix *prefix; + int match_plen; +{ + struct siteprefix *sp; + + for (sp = TAILQ_FIRST(head); sp; sp = TAILQ_NEXT(sp, link)) { + if (!IN6_ARE_ADDR_EQUAL(&sp->prefix.addr, &prefix->addr)) + continue; + if (match_plen == 0 || sp->prefix.plen == prefix->plen) + return (sp); + } + + return (NULL); +} + +static void +remove_siteprefix(sp) + struct siteprefix *sp; +{ + struct dhcp6_ifprefix *ip; + + dprintf(LOG_DEBUG, FNAME, "remove a site prefix %s/%d", + in6addr2str(&sp->prefix.addr, 0), sp->prefix.plen); + + if (sp->timer) + dhcp6_remove_timer(&sp->timer); + + /* remove all interface prefixes */ + while ((ip = TAILQ_FIRST(&sp->ifprefix_list)) != NULL) { + TAILQ_REMOVE(&sp->ifprefix_list, ip, plink); + pd_ifaddrconf(IFADDRCONF_REMOVE, ip); + free(ip); + } + + TAILQ_REMOVE(&sp->ctl->siteprefix_head, sp, link); + free(sp); +} + +static int +isvalid(iac) + struct iactl *iac; +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + + if (TAILQ_EMPTY(&iac_pd->siteprefix_head)) + return (0); /* this IA is invalid */ + return (1); +} + +static u_int32_t +duration(iac) + struct iactl *iac; +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + struct siteprefix *sp; + u_int32_t base = DHCP6_DURATION_INFINITE, pltime, passed; + time_t now; + + /* Determine the smallest period until pltime expires. */ + now = time(NULL); + for (sp = TAILQ_FIRST(&iac_pd->siteprefix_head); sp; + sp = TAILQ_NEXT(sp, link)) { + passed = now > sp->updatetime ? + (u_int32_t)(now - sp->updatetime) : 0; + pltime = sp->prefix.pltime > passed ? + sp->prefix.pltime - passed : 0; + + if (base == DHCP6_DURATION_INFINITE || pltime < base) + base = pltime; + } + + return (base); +} + +static void +cleanup(iac) + struct iactl *iac; +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + struct siteprefix *sp; + + while ((sp = TAILQ_FIRST(&iac_pd->siteprefix_head)) != NULL) { + TAILQ_REMOVE(&iac_pd->siteprefix_head, sp, link); + remove_siteprefix(sp); + } + + free(iac); +} + +static int +renew_prefix(iac, iaparam, evdp, evd) + struct iactl *iac; + struct dhcp6_ia *iaparam; + struct dhcp6_eventdata **evdp, *evd; +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + struct siteprefix *sp; + struct dhcp6_list *ial = NULL, pl; + + TAILQ_INIT(&pl); + for (sp = TAILQ_FIRST(&iac_pd->siteprefix_head); sp; + sp = TAILQ_NEXT(sp, link)) { + if (dhcp6_add_listval(&pl, DHCP6_LISTVAL_PREFIX6, + &sp->prefix, NULL) == NULL) + goto fail; + } + + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + TAILQ_INIT(ial); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IAPD, iaparam, &pl) == NULL) + goto fail; + dhcp6_clear_list(&pl); + + evd->type = DHCP6_EVDATA_IAPD; + evd->data = (void *)ial; + evd->privdata = (void *)evdp; + evd->destructor = renew_data_free; + + return (0); + + fail: + dhcp6_clear_list(&pl); + if (ial) + free(ial); + return (-1); +} + +static void +renew_data_free(evd) + struct dhcp6_eventdata *evd; +{ + struct dhcp6_list *ial; + + if (evd->type != DHCP6_EVDATA_IAPD) { + dprintf(LOG_ERR, FNAME, "assumption failure"); + exit(1); + } + + if (evd->privdata) + *(struct dhcp6_eventdata **)evd->privdata = NULL; + ial = (struct dhcp6_list *)evd->data; + dhcp6_clear_list(ial); + free(ial); +} + +static struct dhcp6_timer * +siteprefix_timo(arg) + void *arg; +{ + struct siteprefix *sp = (struct siteprefix *)arg; + struct ia *ia; + void (*callback)__P((struct ia *)); + + dprintf(LOG_DEBUG, FNAME, "prefix timeout for %s/%d", + in6addr2str(&sp->prefix.addr, 0), sp->prefix.plen); + + ia = sp->ctl->iacpd_ia; + callback = sp->ctl->iacpd_callback; + + if (sp->timer) + dhcp6_remove_timer(&sp->timer); + + remove_siteprefix(sp); + + (*callback)(ia); + + return (NULL); +} + +static int +add_ifprefix(siteprefix, prefix, pconf) + struct siteprefix *siteprefix; + struct dhcp6_prefix *prefix; + struct prefix_ifconf *pconf; +{ + struct dhcp6_ifprefix *ifpfx = NULL; + struct in6_addr *a; + u_long sla_id; + char *sp; + int b, i; + + if ((ifpfx = malloc(sizeof(*ifpfx))) == NULL) { + dprintf(LOG_NOTICE, FNAME, + "failed to allocate memory for ifprefix"); + return (-1); + } + memset(ifpfx, 0, sizeof(*ifpfx)); + + ifpfx->ifconf = pconf; + + ifpfx->paddr.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + ifpfx->paddr.sin6_len = sizeof(struct sockaddr_in6); +#endif + ifpfx->paddr.sin6_addr = prefix->addr; + ifpfx->plen = prefix->plen + pconf->sla_len; + /* + * XXX: our current implementation assumes ifid len is a multiple of 8 + */ + if ((pconf->ifid_len % 8) != 0) { + dprintf(LOG_ERR, FNAME, + "assumption failure on the length of interface ID"); + goto bad; + } + if (ifpfx->plen + pconf->ifid_len < 0 || + ifpfx->plen + pconf->ifid_len > 128) { + dprintf(LOG_INFO, FNAME, + "invalid prefix length %d + %d + %d", + prefix->plen, pconf->sla_len, pconf->ifid_len); + goto bad; + } + + /* copy prefix and SLA ID */ + a = &ifpfx->paddr.sin6_addr; + b = prefix->plen; + for (i = 0, b = prefix->plen; b > 0; b -= 8, i++) + a->s6_addr[i] = prefix->addr.s6_addr[i]; + sla_id = htonl(pconf->sla_id); + sp = ((char *)&sla_id + 3); + i = (128 - pconf->ifid_len) / 8; + for (b = pconf->sla_len; b > 7; b -= 8, sp--) + a->s6_addr[--i] = *sp; + if (b) + a->s6_addr[--i] |= *sp; + + /* configure the corresponding address */ + ifpfx->ifaddr = ifpfx->paddr; + for (i = 15; i >= pconf->ifid_len / 8; i--) + ifpfx->ifaddr.sin6_addr.s6_addr[i] = pconf->ifid[i]; + if (pd_ifaddrconf(IFADDRCONF_ADD, ifpfx)) + goto bad; + + /* TODO: send a control message for other processes */ + + TAILQ_INSERT_TAIL(&siteprefix->ifprefix_list, ifpfx, plink); + + return (0); + + bad: + if (ifpfx) + free(ifpfx); + return (-1); +} + +#ifndef ND6_INFINITE_LIFETIME +#define ND6_INFINITE_LIFETIME 0xffffffff +#endif + +static int +pd_ifaddrconf(cmd, ifpfx) + ifaddrconf_cmd_t cmd; + struct dhcp6_ifprefix *ifpfx; +{ + struct prefix_ifconf *pconf; + + pconf = ifpfx->ifconf; + return (ifaddrconf(cmd, pconf->ifname, &ifpfx->ifaddr, ifpfx->plen, + ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME)); +} diff --git a/prefixconf.h b/prefixconf.h new file mode 100644 index 0000000..dcff695 --- /dev/null +++ b/prefixconf.h @@ -0,0 +1,41 @@ +/* $KAME: prefixconf.h,v 1.6 2005/03/02 07:20:14 suz Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +typedef enum { PREFIX6S_ACTIVE, PREFIX6S_RENEW, + PREFIX6S_REBIND} prefix6state_t; + +extern int update_prefix __P((struct ia *, struct dhcp6_prefix *, + struct pifc_list *, struct dhcp6_if *, struct iactl **, + void (*)__P((struct ia *)))); +extern int prefix6_add __P((struct dhcp6_if *, struct dhcp6_prefix *, + struct duid *)); +extern int prefix6_update __P((struct dhcp6_event *, struct dhcp6_list *, + struct duid *)); diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..c74aa1f --- /dev/null +++ b/timer.c @@ -0,0 +1,212 @@ +/* $KAME: timer.c,v 1.6 2003/07/31 23:25:59 jinmei Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#endif +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "timer.h" + +#define MILLION 1000000 + +LIST_HEAD(, dhcp6_timer) timer_head; +static struct timeval tm_sentinel; +static struct timeval tm_max = {0x7fffffff, 0x7fffffff}; + +static void timeval_add __P((struct timeval *, struct timeval *, + struct timeval *)); + +void +dhcp6_timer_init() +{ + LIST_INIT(&timer_head); + tm_sentinel = tm_max; +} + +struct dhcp6_timer * +dhcp6_add_timer(timeout, timeodata) + struct dhcp6_timer *(*timeout) __P((void *)); + void *timeodata; +{ + struct dhcp6_timer *newtimer; + + if ((newtimer = malloc(sizeof(*newtimer))) == NULL) { + dprintf(LOG_ERR, FNAME, "can't allocate memory"); + return (NULL); + } + + memset(newtimer, 0, sizeof(*newtimer)); + + if (timeout == NULL) { + dprintf(LOG_ERR, FNAME, "timeout function unspecified"); + exit(1); + } + newtimer->expire = timeout; + newtimer->expire_data = timeodata; + newtimer->tm = tm_max; + + LIST_INSERT_HEAD(&timer_head, newtimer, link); + + return (newtimer); +} + +void +dhcp6_remove_timer(timer) + struct dhcp6_timer **timer; +{ + LIST_REMOVE(*timer, link); + free(*timer); + *timer = NULL; +} + +void +dhcp6_set_timer(tm, timer) + struct timeval *tm; + struct dhcp6_timer *timer; +{ + struct timeval now; + + /* reset the timer */ + gettimeofday(&now, NULL); + + timeval_add(&now, tm, &timer->tm); + + /* update the next expiration time */ + if (TIMEVAL_LT(timer->tm, tm_sentinel)) + tm_sentinel = timer->tm; + + return; +} + +/* + * Check expiration for each timer. If a timer is expired, + * call the expire function for the timer and update the timer. + * Return the next interval for select() call. + */ +struct timeval * +dhcp6_check_timer() +{ + static struct timeval returnval; + struct timeval now; + struct dhcp6_timer *tm, *tm_next; + + gettimeofday(&now, NULL); + + tm_sentinel = tm_max; + for (tm = LIST_FIRST(&timer_head); tm; tm = tm_next) { + tm_next = LIST_NEXT(tm, link); + + if (TIMEVAL_LEQ(tm->tm, now)) { + if ((*tm->expire)(tm->expire_data) == NULL) + continue; /* timer has been freed */ + } + + if (TIMEVAL_LT(tm->tm, tm_sentinel)) + tm_sentinel = tm->tm; + } + + if (TIMEVAL_EQUAL(tm_max, tm_sentinel)) { + /* no need to timeout */ + return (NULL); + } else if (TIMEVAL_LT(tm_sentinel, now)) { + /* this may occur when the interval is too small */ + returnval.tv_sec = returnval.tv_usec = 0; + } else + timeval_sub(&tm_sentinel, &now, &returnval); + return (&returnval); +} + +struct timeval * +dhcp6_timer_rest(timer) + struct dhcp6_timer *timer; +{ + struct timeval now; + static struct timeval returnval; /* XXX */ + + gettimeofday(&now, NULL); + if (TIMEVAL_LEQ(timer->tm, now)) { + dprintf(LOG_DEBUG, FNAME, + "a timer must be expired, but not yet"); + returnval.tv_sec = returnval.tv_usec = 0; + } else + timeval_sub(&timer->tm, &now, &returnval); + + return (&returnval); +} + +/* result = a + b */ +static void +timeval_add(a, b, result) + struct timeval *a, *b, *result; +{ + long l; + + if ((l = a->tv_usec + b->tv_usec) < MILLION) { + result->tv_usec = l; + result->tv_sec = a->tv_sec + b->tv_sec; + } + else { + result->tv_usec = l - MILLION; + result->tv_sec = a->tv_sec + b->tv_sec + 1; + } +} + +/* + * result = a - b + * XXX: this function assumes that a >= b. + */ +void +timeval_sub(a, b, result) + struct timeval *a, *b, *result; +{ + long l; + + if ((l = a->tv_usec - b->tv_usec) >= 0) { + result->tv_usec = l; + result->tv_sec = a->tv_sec - b->tv_sec; + } + else { + result->tv_usec = MILLION + l; + result->tv_sec = a->tv_sec - b->tv_sec - 1; + } +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..78e3492 --- /dev/null +++ b/timer.h @@ -0,0 +1,62 @@ +/* $KAME: timer.h,v 1.1 2002/05/16 06:04:08 jinmei Exp $ */ + +/* + * Copyright (C) 2002 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* a < b */ +#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) && \ + ((a).tv_usec < (b).tv_usec))) +/* a <= b */ +#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ + (((a).tv_sec == (b).tv_sec) &&\ + ((a).tv_usec <= (b).tv_usec))) +/* a == b */ +#define TIMEVAL_EQUAL(a, b) ((a).tv_sec == (b).tv_sec &&\ + (a).tv_usec == (b).tv_usec) + +struct dhcp6_timer { + LIST_ENTRY(dhcp6_timer) link; + + struct timeval tm; + + struct dhcp6_timer *(*expire) __P((void *)); + void *expire_data; +}; + +void dhcp6_timer_init __P((void)); +struct dhcp6_timer *dhcp6_add_timer __P((struct dhcp6_timer *(*) __P((void *)), + void *)); +void dhcp6_set_timer __P((struct timeval *, struct dhcp6_timer *)); +void dhcp6_remove_timer __P((struct dhcp6_timer **)); +struct timeval * dhcp6_check_timer __P((void)); +struct timeval * dhcp6_timer_rest __P((struct dhcp6_timer *)); + +void timeval_sub __P((struct timeval *, struct timeval *, + struct timeval *)); diff --git a/y.tab.h b/y.tab.h new file mode 100644 index 0000000..e8501f2 --- /dev/null +++ b/y.tab.h @@ -0,0 +1,193 @@ +/* A Bison parser, made by GNU Bison 2.1. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + INTERFACE = 258, + IFNAME = 259, + PREFIX_INTERFACE = 260, + SLA_ID = 261, + SLA_LEN = 262, + DUID_ID = 263, + ID_ASSOC = 264, + IA_PD = 265, + IAID = 266, + IA_NA = 267, + ADDRESS = 268, + REQUEST = 269, + SEND = 270, + ALLOW = 271, + PREFERENCE = 272, + HOST = 273, + HOSTNAME = 274, + DUID = 275, + OPTION = 276, + RAPID_COMMIT = 277, + DNS_SERVERS = 278, + DNS_NAME = 279, + NTP_SERVERS = 280, + REFRESHTIME = 281, + SIP_SERVERS = 282, + SIP_NAME = 283, + NIS_SERVERS = 284, + NIS_NAME = 285, + NISP_SERVERS = 286, + NISP_NAME = 287, + BCMCS_SERVERS = 288, + BCMCS_NAME = 289, + INFO_ONLY = 290, + SCRIPT = 291, + DELAYEDKEY = 292, + AUTHENTICATION = 293, + PROTOCOL = 294, + ALGORITHM = 295, + DELAYED = 296, + RECONFIG = 297, + HMACMD5 = 298, + MONOCOUNTER = 299, + AUTHNAME = 300, + RDM = 301, + KEY = 302, + KEYINFO = 303, + REALM = 304, + KEYID = 305, + SECRET = 306, + KEYNAME = 307, + EXPIRE = 308, + ADDRPOOL = 309, + POOLNAME = 310, + RANGE = 311, + TO = 312, + ADDRESS_POOL = 313, + INCLUDE = 314, + NUMBER = 315, + SLASH = 316, + EOS = 317, + BCL = 318, + ECL = 319, + STRING = 320, + QSTRING = 321, + PREFIX = 322, + INFINITY = 323, + COMMA = 324 + }; +#endif +/* Tokens. */ +#define INTERFACE 258 +#define IFNAME 259 +#define PREFIX_INTERFACE 260 +#define SLA_ID 261 +#define SLA_LEN 262 +#define DUID_ID 263 +#define ID_ASSOC 264 +#define IA_PD 265 +#define IAID 266 +#define IA_NA 267 +#define ADDRESS 268 +#define REQUEST 269 +#define SEND 270 +#define ALLOW 271 +#define PREFERENCE 272 +#define HOST 273 +#define HOSTNAME 274 +#define DUID 275 +#define OPTION 276 +#define RAPID_COMMIT 277 +#define DNS_SERVERS 278 +#define DNS_NAME 279 +#define NTP_SERVERS 280 +#define REFRESHTIME 281 +#define SIP_SERVERS 282 +#define SIP_NAME 283 +#define NIS_SERVERS 284 +#define NIS_NAME 285 +#define NISP_SERVERS 286 +#define NISP_NAME 287 +#define BCMCS_SERVERS 288 +#define BCMCS_NAME 289 +#define INFO_ONLY 290 +#define SCRIPT 291 +#define DELAYEDKEY 292 +#define AUTHENTICATION 293 +#define PROTOCOL 294 +#define ALGORITHM 295 +#define DELAYED 296 +#define RECONFIG 297 +#define HMACMD5 298 +#define MONOCOUNTER 299 +#define AUTHNAME 300 +#define RDM 301 +#define KEY 302 +#define KEYINFO 303 +#define REALM 304 +#define KEYID 305 +#define SECRET 306 +#define KEYNAME 307 +#define EXPIRE 308 +#define ADDRPOOL 309 +#define POOLNAME 310 +#define RANGE 311 +#define TO 312 +#define ADDRESS_POOL 313 +#define INCLUDE 314 +#define NUMBER 315 +#define SLASH 316 +#define EOS 317 +#define BCL 318 +#define ECL 319 +#define STRING 320 +#define QSTRING 321 +#define PREFIX 322 +#define INFINITY 323 +#define COMMA 324 + + + + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 126 "cfparse.y" +typedef union YYSTYPE { + long long num; + char* str; + struct cf_list *list; + struct dhcp6_prefix *prefix; + struct dhcp6_range *range; + struct dhcp6_poolspec *pool; +} YYSTYPE; +/* Line 1447 of yacc.c. */ +#line 185 "y.tab.h" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + + + -- cgit v1.2.3