summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Markwalder <tmark@isc.org>2019-11-22 13:39:45 -0500
committerThomas Markwalder <tmark@isc.org>2019-11-22 13:39:45 -0500
commit0cd94b5ef4a078097fc2bd1dc72f5e80c2cf1844 (patch)
treee4dfe9a92d676a49b18ab547d781fb3fb10c8e6a
parent97c155273c0df0c8518f226e2b5e338e3ad63e87 (diff)
downloadisc-dhcp-0cd94b5ef4a078097fc2bd1dc72f5e80c2cf1844.tar.gz
[#64,!35] Restored work
Restored cummulative work.
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTING.md190
-rw-r--r--Makefile.am2
-rw-r--r--Makefile.in21
-rw-r--r--RELNOTES64
-rw-r--r--aclocal.m4193
-rw-r--r--bind/Makefile.in120
-rw-r--r--bind/bind.tar.gzbin0 -> 7950927 bytes
-rw-r--r--bind/version.tmp11
-rw-r--r--client/Makefile.in75
-rw-r--r--client/clparse.c28
-rw-r--r--client/dhc6.c19
-rw-r--r--client/dhclient.c124
-rw-r--r--client/tests/Makefile.in69
-rw-r--r--common/Makefile.in187
-rw-r--r--common/dns.c7
-rw-r--r--common/execute.c11
-rw-r--r--common/ns_name.c35
-rw-r--r--common/options.c101
-rw-r--r--common/parse.c52
-rw-r--r--common/socket.c8
-rw-r--r--common/tests/Kyuafile1
-rw-r--r--common/tests/Makefile.am11
-rw-r--r--common/tests/Makefile.in113
-rw-r--r--common/tests/domain_name_test.c100
-rwxr-xr-xconfigure214
-rw-r--r--configure.ac1
-rw-r--r--configure.ac+lt3
-rw-r--r--configure.ac-base3
-rw-r--r--configure.ac-lt3
-rw-r--r--contrib/dhcp-lease-list.pl4
-rw-r--r--dhcpctl/Makefile.in130
-rw-r--r--includes/Makefile.in13
-rw-r--r--includes/cf/aix.h130
-rw-r--r--includes/cf/alphaosf.h147
-rw-r--r--includes/cf/bsdos.h129
-rw-r--r--includes/cf/cygwin32.h128
-rw-r--r--includes/cf/freebsd.h148
-rw-r--r--includes/cf/hpux.h134
-rw-r--r--includes/cf/irix.h127
-rw-r--r--includes/cf/linux.h194
-rw-r--r--includes/cf/netbsd.h145
-rw-r--r--includes/cf/nextstep.h159
-rw-r--r--includes/cf/openbsd.h141
-rw-r--r--includes/cf/qnx.h172
-rw-r--r--includes/cf/rhapsody.h129
-rw-r--r--includes/cf/sample.h299
-rw-r--r--includes/cf/sco.h169
-rw-r--r--includes/cf/sunos4.h176
-rw-r--r--includes/cf/sunos5-5.h205
-rw-r--r--includes/cf/ultrix.h148
-rw-r--r--includes/dhcpd.h2
-rw-r--r--includes/ns_name.h3
-rw-r--r--includes/omapip/isclib.h3
-rw-r--r--includes/osdep.h8
-rw-r--r--keama/.gitignore1
-rw-r--r--keama/ChangeLog.md18
-rw-r--r--keama/Makefile.am5
-rw-r--r--keama/Makefile.in721
-rw-r--r--keama/README.md53
-rw-r--r--keama/REAME.md53
-rw-r--r--keama/conflex.c1550
-rw-r--r--keama/confparse.c4992
-rw-r--r--keama/data.c1258
-rw-r--r--keama/data.h304
-rw-r--r--keama/dhctoken.h389
-rw-r--r--keama/doc.txt516
-rw-r--r--keama/eval.c2252
-rw-r--r--keama/json.c182
-rw-r--r--keama/keama.8104
-rw-r--r--keama/keama.c225
-rw-r--r--keama/keama.h470
-rw-r--r--keama/options.c1154
-rw-r--r--keama/parse.c6140
-rw-r--r--keama/print.c1490
-rw-r--r--keama/reduce.c1016
-rw-r--r--keama/tests/README42
-rw-r--r--keama/tests/badcasexsc.err7
-rw-r--r--keama/tests/badcasexsc.msg1
-rw-r--r--keama/tests/badclass.err12
-rw-r--r--keama/tests/badclass.msg1
-rw-r--r--keama/tests/badclass2.err12
-rw-r--r--keama/tests/badclass2.msg1
-rw-r--r--keama/tests/baddecl2array.err4
-rw-r--r--keama/tests/baddecl2array.msg1
-rw-r--r--keama/tests/baddecl2record.err4
-rw-r--r--keama/tests/baddecl2record.msg1
-rw-r--r--keama/tests/baddeclBt.err4
-rw-r--r--keama/tests/baddeclBt.msg1
-rw-r--r--keama/tests/baddefaultxsc.err7
-rw-r--r--keama/tests/baddefaultxsc.msg1
-rw-r--r--keama/tests/baddomain.notyet6
-rw-r--r--keama/tests/badduid.err4
-rw-r--r--keama/tests/badduid.msg1
-rw-r--r--keama/tests/badinclude.err3
-rw-r--r--keama/tests/badinclude.msg1
-rw-r--r--keama/tests/badoption66.err63
-rw-r--r--keama/tests/badoption66.msg1
-rw-r--r--keama/tests/badoptionD6.notyet4
-rw-r--r--keama/tests/badoptionDc4.notyet3
-rw-r--r--keama/tests/badoptionI4.err43
-rw-r--r--keama/tests/badoptionI4.msg1
-rw-r--r--keama/tests/badoptiond4.err45
-rw-r--r--keama/tests/badoptiond4.msg1
-rw-r--r--keama/tests/badstatusdir.err7
-rw-r--r--keama/tests/badstatusdir.msg1
-rw-r--r--keama/tests/badsubclass.err11
-rw-r--r--keama/tests/badsubclass.msg1
-rw-r--r--keama/tests/bintadx6.in69
-rw-r--r--keama/tests/bintadx6.out15
-rw-r--r--keama/tests/bootfilename4.in44
-rw-r--r--keama/tests/bootfilename4.out13
-rw-r--r--keama/tests/charcasedx4.in49
-rw-r--r--keama/tests/charcasedx4.out15
-rw-r--r--keama/tests/checkall.sh16
-rw-r--r--keama/tests/checkone.sh54
-rw-r--r--keama/tests/class4.in411
-rw-r--r--keama/tests/class4.out36
-rw-r--r--keama/tests/class4empty.in45
-rw-r--r--keama/tests/class4empty.out11
-rw-r--r--keama/tests/class6.in611
-rw-r--r--keama/tests/class6.out36
-rw-r--r--keama/tests/class6empty.in65
-rw-r--r--keama/tests/class6empty.out11
-rw-r--r--keama/tests/classbadmatch.err7
-rw-r--r--keama/tests/classbadmatch.msg1
-rw-r--r--keama/tests/classbadmatchif.err7
-rw-r--r--keama/tests/classbadmatchif.msg1
-rw-r--r--keama/tests/classinclass.err10
-rw-r--r--keama/tests/classinclass.msg1
-rw-r--r--keama/tests/concatdx4.in419
-rw-r--r--keama/tests/concatdx4.out48
-rw-r--r--keama/tests/concatnulldx4.in418
-rw-r--r--keama/tests/concatnulldx4.out33
-rw-r--r--keama/tests/configdata4.in426
-rw-r--r--keama/tests/configdata4.out74
-rw-r--r--keama/tests/dbtimeformat4.in46
-rw-r--r--keama/tests/dbtimeformat4.out10
-rw-r--r--keama/tests/dbtimeformat6.in66
-rw-r--r--keama/tests/dbtimeformat6.out10
-rw-r--r--keama/tests/ddnsupdstyle6.in612
-rw-r--r--keama/tests/ddnsupdstyle6.out29
-rw-r--r--keama/tests/defaultexpr6.in610
-rw-r--r--keama/tests/defaultexpr6.out25
-rw-r--r--keama/tests/denyunknown6.in615
-rw-r--r--keama/tests/denyunknown6.out32
-rw-r--r--keama/tests/docsis4.dir11
-rw-r--r--keama/tests/docsis6.dir27
-rw-r--r--keama/tests/duid2.err7
-rw-r--r--keama/tests/duid2.msg1
-rw-r--r--keama/tests/duiden6.in64
-rw-r--r--keama/tests/duiden6.out11
-rw-r--r--keama/tests/duidennoid.err5
-rw-r--r--keama/tests/duidennoid.msg1
-rw-r--r--keama/tests/duidennonum.err4
-rw-r--r--keama/tests/duidennonum.msg1
-rw-r--r--keama/tests/duidll6.in65
-rw-r--r--keama/tests/duidll6.out9
-rw-r--r--keama/tests/duidllbadtype.err4
-rw-r--r--keama/tests/duidllbadtype.msg1
-rw-r--r--keama/tests/duidllhw6.in66
-rw-r--r--keama/tests/duidllhw6.out11
-rw-r--r--keama/tests/duidllnohw.err4
-rw-r--r--keama/tests/duidllnohw.msg1
-rw-r--r--keama/tests/duidllt6.in65
-rw-r--r--keama/tests/duidllt6.out9
-rw-r--r--keama/tests/duidlltbadtype.err4
-rw-r--r--keama/tests/duidlltbadtype.msg1
-rw-r--r--keama/tests/duidlltnohw.err4
-rw-r--r--keama/tests/duidlltnohw.msg1
-rw-r--r--keama/tests/duidlltnotime.err4
-rw-r--r--keama/tests/duidlltnotime.msg1
-rw-r--r--keama/tests/duidlltthw4.err44
-rw-r--r--keama/tests/duidlltthw4.msg1
-rw-r--r--keama/tests/duidlltthw6.in64
-rw-r--r--keama/tests/duidlltthw6.out12
-rw-r--r--keama/tests/duidnoid.err4
-rw-r--r--keama/tests/duidnoid.msg1
-rw-r--r--keama/tests/enableupdates6.in68
-rw-r--r--keama/tests/enableupdates6.out20
-rw-r--r--keama/tests/encodedx6.in69
-rw-r--r--keama/tests/encodedx6.out15
-rw-r--r--keama/tests/env3
-rw-r--r--keama/tests/escapestring4.in46
-rw-r--r--keama/tests/escapestring4.out23
-rw-r--r--keama/tests/execstatement4.in47
-rw-r--r--keama/tests/execstatement4.out11
-rw-r--r--keama/tests/execstatement6.in67
-rw-r--r--keama/tests/execstatement6.out11
-rw-r--r--keama/tests/existsbx4.in415
-rw-r--r--keama/tests/existsbx4.out48
-rw-r--r--keama/tests/filename4.in410
-rw-r--r--keama/tests/filename4.out17
-rw-r--r--keama/tests/filenamedx4.notyet20
-rw-r--r--keama/tests/fixedaddressinroot4.err44
-rw-r--r--keama/tests/fixedaddressinroot4.msg1
-rw-r--r--keama/tests/fixedaddressinroot6.err64
-rw-r--r--keama/tests/fixedaddressinroot6.msg1
-rw-r--r--keama/tests/fixedprefixinroot.err64
-rw-r--r--keama/tests/fixedprefixinroot.msg1
-rw-r--r--keama/tests/fqdncompressed.err67
-rw-r--r--keama/tests/fqdncompressed.msg1
-rw-r--r--keama/tests/gethostdx4.notyet13
-rw-r--r--keama/tests/global4.in410
-rw-r--r--keama/tests/global4.out28
-rw-r--r--keama/tests/global6.in610
-rw-r--r--keama/tests/global6.out26
-rw-r--r--keama/tests/groupclass4.in432
-rw-r--r--keama/tests/groupclass4.out102
-rw-r--r--keama/tests/groupclass6.in635
-rw-r--r--keama/tests/groupclass6.out123
-rw-r--r--keama/tests/groupgroup4.in445
-rw-r--r--keama/tests/groupgroup4.out138
-rw-r--r--keama/tests/grouphost4.inn35
-rw-r--r--keama/tests/grouphost4.out78
-rw-r--r--keama/tests/groupinclass.err10
-rw-r--r--keama/tests/groupinclass.msg1
-rw-r--r--keama/tests/groupsubnet4.in424
-rw-r--r--keama/tests/groupsubnet4.out38
-rw-r--r--keama/tests/groupsubnet6.in624
-rw-r--r--keama/tests/groupsubnet6.out44
-rw-r--r--keama/tests/groupsubnetif.err419
-rw-r--r--keama/tests/groupsubnetif.msg1
-rw-r--r--keama/tests/hardware2dx4.in413
-rw-r--r--keama/tests/hardware2dx4.out25
-rw-r--r--keama/tests/hardwaredx4.in416
-rw-r--r--keama/tests/hardwaredx4.out55
-rw-r--r--keama/tests/hardwareinroot.err5
-rw-r--r--keama/tests/hardwareinroot.msg1
-rw-r--r--keama/tests/host6.notyet20
-rw-r--r--keama/tests/hostidentifier4.inl30
-rw-r--r--keama/tests/hostidentifier4.outl78
-rw-r--r--keama/tests/hostinclass.err10
-rw-r--r--keama/tests/hostinclass.msg1
-rw-r--r--keama/tests/hostinhost.err11
-rw-r--r--keama/tests/hostinhost.msg1
-rw-r--r--keama/tests/hostname4.in418
-rw-r--r--keama/tests/hostname4.out37
-rw-r--r--keama/tests/hostnum.errF7
-rw-r--r--keama/tests/hostnum.msg1
-rw-r--r--keama/tests/hostuid4.inn29
-rw-r--r--keama/tests/hostuid4.out62
-rw-r--r--keama/tests/ifxsc4.in417
-rw-r--r--keama/tests/ifxsc4.out79
-rw-r--r--keama/tests/ipaddr6.in63
-rw-r--r--keama/tests/ipaddr6.out14
-rw-r--r--keama/tests/ipaddrhost4.in48
-rw-r--r--keama/tests/ipaddrhost4.out24
-rw-r--r--keama/tests/ipaddrs4.notyet411
-rw-r--r--keama/tests/lifetime4.ind5
-rw-r--r--keama/tests/lifetime4.out8
-rw-r--r--keama/tests/lifetime6.inD5
-rw-r--r--keama/tests/lifetime6.out8
-rw-r--r--keama/tests/lifetimedef4.ind1
-rw-r--r--keama/tests/lifetimedef4.out11
-rw-r--r--keama/tests/lifetimedef6.inD1
-rw-r--r--keama/tests/lifetimedef6.out11
-rw-r--r--keama/tests/listarray.err7
-rw-r--r--keama/tests/listarray.msg1
-rw-r--r--keama/tests/minimal4.in44
-rw-r--r--keama/tests/minimal4.out7
-rw-r--r--keama/tests/minimal6.in64
-rw-r--r--keama/tests/minimal6.out7
-rw-r--r--keama/tests/mixedarray.err7
-rw-r--r--keama/tests/mixedarray.msg1
-rw-r--r--keama/tests/nestarray.err7
-rw-r--r--keama/tests/nestarray.msg1
-rw-r--r--keama/tests/noauth4.in47
-rw-r--r--keama/tests/noauth4.out15
-rw-r--r--keama/tests/noauth6.in66
-rw-r--r--keama/tests/noauth6.out15
-rw-r--r--keama/tests/noclass.err7
-rw-r--r--keama/tests/noclass.msg1
-rw-r--r--keama/tests/noinclude.err3
-rw-r--r--keama/tests/noinclude.msg1
-rw-r--r--keama/tests/nosubclass.err11
-rw-r--r--keama/tests/nosubclass.msg1
-rw-r--r--keama/tests/nosuperclass.err11
-rw-r--r--keama/tests/nosuperclass.msg1
-rw-r--r--keama/tests/notbx4.in412
-rw-r--r--keama/tests/notbx4.out30
-rw-r--r--keama/tests/notnotbx4.in415
-rw-r--r--keama/tests/notnotbx4.out21
-rw-r--r--keama/tests/nxdomainnx6.in612
-rw-r--r--keama/tests/nxdomainnx6.out28
-rw-r--r--keama/tests/onxsc4.in412
-rw-r--r--keama/tests/onxsc4.out34
-rw-r--r--keama/tests/optdatagrouppool4.in418
-rw-r--r--keama/tests/optdatagrouppool4.out23
-rw-r--r--keama/tests/optiondata4.in448
-rw-r--r--keama/tests/optiondata4.out173
-rw-r--r--keama/tests/optiondata6.in649
-rw-r--r--keama/tests/optiondata6.out178
-rw-r--r--keama/tests/optiondatapool4.in412
-rw-r--r--keama/tests/optiondatapool4.out31
-rw-r--r--keama/tests/optiondatapool6.in613
-rw-r--r--keama/tests/optiondatapool6.out32
-rw-r--r--keama/tests/optiondecl4.in427
-rw-r--r--keama/tests/optiondecl4.out143
-rw-r--r--keama/tests/optiondecl6.in627
-rw-r--r--keama/tests/optiondecl6.out137
-rw-r--r--keama/tests/optiondeclBat4.in46
-rw-r--r--keama/tests/optiondeclBat4.out18
-rw-r--r--keama/tests/optionencap4.in47
-rw-r--r--keama/tests/optionencap4.out15
-rw-r--r--keama/tests/optionencap6.in67
-rw-r--r--keama/tests/optionencap6.out15
-rw-r--r--keama/tests/optionexpr4.in416
-rw-r--r--keama/tests/optionexpr4.out81
-rw-r--r--keama/tests/optionspace4.in415
-rw-r--r--keama/tests/optionspace4.out34
-rw-r--r--keama/tests/optionspace6.in615
-rw-r--r--keama/tests/optionspace6.out34
-rw-r--r--keama/tests/optionvendor4.in48
-rw-r--r--keama/tests/optionvendor4.out28
-rw-r--r--keama/tests/optionvendor6.in68
-rw-r--r--keama/tests/optionvendor6.out28
-rw-r--r--keama/tests/orphan4.inn10
-rw-r--r--keama/tests/orphan4.out30
-rw-r--r--keama/tests/orphan6.inN10
-rw-r--r--keama/tests/orphan6.out28
-rw-r--r--keama/tests/packetdx4.notyet20
-rw-r--r--keama/tests/permitauth4.in415
-rw-r--r--keama/tests/permitauth4.out33
-rw-r--r--keama/tests/permitauth6.in615
-rw-r--r--keama/tests/permitauth6.out33
-rw-r--r--keama/tests/permitknown4.in415
-rw-r--r--keama/tests/permitknown4.out26
-rw-r--r--keama/tests/pickdx6.in615
-rw-r--r--keama/tests/pickdx6.out21
-rw-r--r--keama/tests/pool4.in411
-rw-r--r--keama/tests/pool4.out31
-rw-r--r--keama/tests/pool42.in415
-rw-r--r--keama/tests/pool42.out49
-rw-r--r--keama/tests/pool6.in611
-rw-r--r--keama/tests/pool6.out31
-rw-r--r--keama/tests/pool6in4.err413
-rw-r--r--keama/tests/pool6in4.msg1
-rw-r--r--keama/tests/poolinroot4.err47
-rw-r--r--keama/tests/poolinroot4.msg1
-rw-r--r--keama/tests/poolinroot6.err67
-rw-r--r--keama/tests/poolinroot6.msg1
-rw-r--r--keama/tests/preferred6.in614
-rw-r--r--keama/tests/preferred6.out30
-rw-r--r--keama/tests/prefix0.err69
-rw-r--r--keama/tests/prefix0.msg1
-rw-r--r--keama/tests/prefix128.err69
-rw-r--r--keama/tests/prefix128.msg1
-rw-r--r--keama/tests/prefix6.in69
-rw-r--r--keama/tests/prefix6.out32
-rw-r--r--keama/tests/prefix62.in610
-rw-r--r--keama/tests/prefix62.out36
-rw-r--r--keama/tests/prefixinroot6.err64
-rw-r--r--keama/tests/prefixinroot6.msg1
-rw-r--r--keama/tests/qualifyingsuffix4.in49
-rw-r--r--keama/tests/qualifyingsuffix4.out17
-rw-r--r--keama/tests/qualifyingsuffix6.in68
-rw-r--r--keama/tests/qualifyingsuffix6.out17
-rw-r--r--keama/tests/range4.in49
-rw-r--r--keama/tests/range4.out30
-rw-r--r--keama/tests/range6.in611
-rw-r--r--keama/tests/range6.out37
-rw-r--r--keama/tests/range6in4.err45
-rw-r--r--keama/tests/range6in4.msg1
-rw-r--r--keama/tests/rangeinroot4.err44
-rw-r--r--keama/tests/rangeinroot4.msg1
-rw-r--r--keama/tests/rangeinroot6.err65
-rw-r--r--keama/tests/rangeinroot6.msg1
-rw-r--r--keama/tests/reversedx6.in69
-rw-r--r--keama/tests/reversedx6.out15
-rw-r--r--keama/tests/runall.sh15
-rw-r--r--keama/tests/runone.sh119
-rw-r--r--keama/tests/samples/example.conf111
-rw-r--r--keama/tests/samples/example.conf.orig104
-rw-r--r--keama/tests/samples/example.json223
-rw-r--r--keama/tests/samples/runall.sh11
-rw-r--r--keama/tests/samples/runone.sh39
-rw-r--r--keama/tests/samples/simple.conf33
-rw-r--r--keama/tests/samples/simple.json53
-rw-r--r--keama/tests/samples/test-a6.conf75
-rw-r--r--keama/tests/samples/test-a6.conf.orig67
-rw-r--r--keama/tests/samples/test-a6.json144
-rw-r--r--keama/tests/samples/vmnet8.conf43
-rw-r--r--keama/tests/samples/vmnet8.conf.orig43
-rw-r--r--keama/tests/samples/vmnet8.json105
-rw-r--r--keama/tests/share0.err6
-rw-r--r--keama/tests/share0.msg1
-rw-r--r--keama/tests/share2if.err7
-rw-r--r--keama/tests/share2if.msg1
-rw-r--r--keama/tests/shareempty.err6
-rw-r--r--keama/tests/shareempty.msg1
-rw-r--r--keama/tests/shareinclass.err11
-rw-r--r--keama/tests/shareinclass.msg1
-rw-r--r--keama/tests/shareinhost.err11
-rw-r--r--keama/tests/shareinhost.msg1
-rw-r--r--keama/tests/shareinshare.err10
-rw-r--r--keama/tests/shareinshare.msg1
-rw-r--r--keama/tests/shareinsubnet4.err410
-rw-r--r--keama/tests/shareinsubnet4.msg1
-rw-r--r--keama/tests/shareinsubnet6.err610
-rw-r--r--keama/tests/shareinsubnet6.msg1
-rw-r--r--keama/tests/sharenoname.err6
-rw-r--r--keama/tests/sharenoname.msg1
-rw-r--r--keama/tests/shareone4.in422
-rw-r--r--keama/tests/shareone4.out44
-rw-r--r--keama/tests/shareone6.in619
-rw-r--r--keama/tests/shareone6.out37
-rw-r--r--keama/tests/sharepools4.in427
-rw-r--r--keama/tests/sharepools4.out63
-rw-r--r--keama/tests/sharetwo4.in429
-rw-r--r--keama/tests/sharetwo4.out64
-rw-r--r--keama/tests/sharetwo6.in624
-rw-r--r--keama/tests/sharetwo6.out56
-rw-r--r--keama/tests/sname4.notyet20
-rw-r--r--keama/tests/spawning6.in611
-rw-r--r--keama/tests/spawning6.out37
-rw-r--r--keama/tests/subclass4.in423
-rw-r--r--keama/tests/subclass4.out84
-rw-r--r--keama/tests/subclass6.in623
-rw-r--r--keama/tests/subclass6.out89
-rw-r--r--keama/tests/subclassbinsel4.in416
-rw-r--r--keama/tests/subclassbinsel4.out61
-rw-r--r--keama/tests/subclassbinsel6.in625
-rw-r--r--keama/tests/subclassbinsel6.out88
-rw-r--r--keama/tests/subclassguard4.in424
-rw-r--r--keama/tests/subclassguard4.out79
-rw-r--r--keama/tests/subclassguard6.in624
-rw-r--r--keama/tests/subclassguard6.out84
-rw-r--r--keama/tests/subnet4.in416
-rw-r--r--keama/tests/subnet4.out33
-rw-r--r--keama/tests/subnet42if.err48
-rw-r--r--keama/tests/subnet42if.msg1
-rw-r--r--keama/tests/subnet4auth.in418
-rw-r--r--keama/tests/subnet4auth.out35
-rw-r--r--keama/tests/subnet4badmask.err47
-rw-r--r--keama/tests/subnet4badmask.msg1
-rw-r--r--keama/tests/subnet4inclass.err410
-rw-r--r--keama/tests/subnet4inclass.msg1
-rw-r--r--keama/tests/subnet4inhost.err411
-rw-r--r--keama/tests/subnet4inhost.msg1
-rw-r--r--keama/tests/subnet4nomask.err47
-rw-r--r--keama/tests/subnet4nomask.msg1
-rw-r--r--keama/tests/subnet6.in618
-rw-r--r--keama/tests/subnet6.out39
-rw-r--r--keama/tests/subnet62if.err68
-rw-r--r--keama/tests/subnet62if.msg1
-rw-r--r--keama/tests/subnet6auth.in621
-rw-r--r--keama/tests/subnet6auth.out44
-rw-r--r--keama/tests/subnet6inclass.err610
-rw-r--r--keama/tests/subnet6inclass.msg1
-rw-r--r--keama/tests/subnet6inhost.err611
-rw-r--r--keama/tests/subnet6inhost.msg1
-rw-r--r--keama/tests/subnet6multi.in619
-rw-r--r--keama/tests/subnet6multi.out49
-rw-r--r--keama/tests/subnet6nolen.err67
-rw-r--r--keama/tests/subnet6nolen.msg1
-rw-r--r--keama/tests/subnet6noslash.err67
-rw-r--r--keama/tests/subnet6noslash.msg1
-rw-r--r--keama/tests/subnet6one.in618
-rw-r--r--keama/tests/subnet6one.out45
-rw-r--r--keama/tests/subnetinsubnet4.err410
-rw-r--r--keama/tests/subnetinsubnet4.msg1
-rw-r--r--keama/tests/subnetinsubnet6.err610
-rw-r--r--keama/tests/subnetinsubnet6.msg1
-rw-r--r--keama/tests/substringdx4.in421
-rw-r--r--keama/tests/substringdx4.out49
-rw-r--r--keama/tests/suffixdx4.in426
-rw-r--r--keama/tests/suffixdx4.out74
-rw-r--r--keama/tests/switchxsc4.in418
-rw-r--r--keama/tests/switchxsc4.out79
-rw-r--r--keama/tests/switchxsc6.in618
-rw-r--r--keama/tests/switchxsc6.out82
-rw-r--r--keama/tests/tautology.err9
-rw-r--r--keama/tests/tautology.msg1
-rw-r--r--keama/tests/tautologyhexa.err9
-rw-r--r--keama/tests/tautologyhexa.msg1
-rw-r--r--keama/tests/tautologysub.err9
-rw-r--r--keama/tests/tautologysub.msg1
-rw-r--r--keama/tests/temporary6.in610
-rw-r--r--keama/tests/temporary6.out33
-rw-r--r--keama/tests/textarray.err7
-rw-r--r--keama/tests/textarray.msg1
-rw-r--r--keama/tests/unknownoption.err4
-rw-r--r--keama/tests/unknownoption.msg1
-rw-r--r--keama/tests/unknownspace.err4
-rw-r--r--keama/tests/unknownspace.msg1
-rw-r--r--keama/tests/userclass.err6
-rw-r--r--keama/tests/userclass.msg1
-rw-r--r--keama/tests/vendorclass.err6
-rw-r--r--keama/tests/vendorclass.msg1
-rw-r--r--keama/tests/vendorspace4.in411
-rw-r--r--keama/tests/vendorspace4.out41
-rw-r--r--keama/tests/zone4.in424
-rw-r--r--keama/tests/zone4.out47
-rw-r--r--omapip/Makefile.in160
-rw-r--r--omapip/isclib.c57
-rw-r--r--relay/Makefile.in46
-rw-r--r--relay/dhcrelay.c5
-rw-r--r--server/Makefile.in143
-rw-r--r--server/class.c3
-rw-r--r--server/confpars.c35
-rw-r--r--server/ddns.c5
-rw-r--r--server/dhcp.c8
-rw-r--r--server/dhcpleasequery.c12
-rw-r--r--server/dhcpv6.c20
-rw-r--r--server/mdb.c116
-rw-r--r--server/mdb6.c2
-rw-r--r--server/tests/Makefile.in154
-rw-r--r--tests/Makefile.in34
509 files changed, 33212 insertions, 3721 deletions
diff --git a/.gitignore b/.gitignore
index 42751758..59eb7dd9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
*.lo
*.o
-bind
doc/html
.deps
Makefile
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 33c29c02..1da7b4d0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,52 +3,114 @@
So you found a bug in ISC DHCP or plan to develop an extension and want to send us a patch? Great!
This page will explain how to contribute your changes smoothly.
-## Writing a patch
+We do not require a contributors agreement. By submitting a patch or merge request to this project,
+you are agreeing that your code will be covered by the primary license for the project.
+ISC DHCP is currently licensed under the MPL2.0 license.
+
+Here's are the steps in contributing a patch:
+
+1. **create account** on [gitlab](https://gitlab.isc.org)
+2. **open an issue** in [this project](https://gitlab.isc.org/isc-projects/dhcp/issues/new), make sure
+ it describes what you want to fix and **why**. ISC DHCP is very mature code, with a large installed base.
+ We are fairly conservative about making changes unless there is a very good reason.
+3. **ask someone from the ISC team to give you a 'project allocation' so you can to fork ISC DHCP in our repo** (ask on the issue - mention @tomek, @vicky, @ondrej
+ or @godfryd if it seems we haven't noticed your request)
+4. **fork the DHCP master branch**: go to the DHCP project page, click the [Fork button](https://gitlab.isc.org/isc-projects/dhcp/forks/new).
+ If you can't, you didn't complete step 3. It helps to include the issue number and subject in the branch name.
+5. **Implement your fix or feature, in your branch**. Make sure it compiles, has unit-tests,
+ is documented and does what it's supposed to do.
+6. **Open Merge Request**: go to the DHCP project [merge requests page](https://gitlab.isc.org/isc-projects/dhcp/merge_requests), and
+ click [New merge request](https://gitlab.isc.org/isc-projects/dhcp/merge_requests/new). If you
+ don't see the button, you didn't complete step 3.
+7. **Participate in the code review**: Once you submit the MR, someone from ISC will eventually get
+ to the issue and will review your code. Please make sure you respond to comments. It's likely
+ you'll be asked to update the code.
+
+See the text below for more details.
+
+
+## Create an issue
+
+The first step in contributing to ISC DHCP is to [create an issue](https://gitlab.isc.org/isc-projects/dhcp/issues/new), describing the problem, deficiency
+or missing feature you want to address. It is important to make it very clear why the specific change
+you are proposing should be made. ISC DHCP is very mature code, with a large and somewhat inert installed base.
+We are very cautious about introducing changes that could break existing functionalty. If you want to fix
+multiple problems, or make multiple changes, please make separate issues for each.
+
+## Plan your changes
Before you start working on a patch or a new feature, it is a good idea to discuss it first with
-DHCP developers. You can post your questions to the [dhcp-workers](https://lists.isc.org/mailman/listinfo/dhcp-workers)
-or [dhcp-users](https://lists.isc.org/mailman/listinfo/dhcp-users) mailing lists. The dhcp-users is
-intended for users who are not interested in the internal workings or development details: it is
-OK to ask for feedback regarding new design or the best proposed solution to a certain problem.
-This is the best place to get user's feedback. The internal details, questions about the code and
-its internals are better asked on dhcp-workers. The dhcp-workers is a very low traffic list.
-
-OK, so you have written a patch? Great! Before you submit it, make sure that your code compiles.
-This may seem obvious, but there's more to it. You have surely checked that it compiles on your
-system, but ISC DHCP is a portable software. Besides Linux, it is compiled and used on relatively
-uncommon systems like OpenBSD. Will your code compile and work there? What about endianness? It is
-likely that you used a regular x86 architecture machine to write your patch, but the software is
-expected to run on many other architectures. For a complete list of systems we build on, you may
-take a look at the [Jenkins build farm report](https://jenkins.isc.org/view/isc-dhcp/).
-
-## Running unit-tests
+DHCP developers. You may benefit from reading the [ISC DHCP Developer's Survival Guide](https://gitlab.isc.org/isc-projects/dhcp/wikis/home)
+posted on the wiki page for this repo.
+
+You can post questions about development on the [dhcp-workers](https://lists.isc.org/mailman/listinfo/dhcp-workers)
+or [dhcp-users](https://lists.isc.org/mailman/listinfo/dhcp-users) mailing lists. Dhcp-users is
+intended for users who are not interested in development details: it is appropriate to ask for
+feedback regarding the best proposed solution to a certain problem. The internal details,
+questions about the code and its internals are better asked on dhcp-workers. The dhcp-workers
+list is a very low traffic list.
+
+
+## Create a branch for your work
+
+These instructions assume you will be making your changes on a branch in the ISC DHCP Gitlab
+repository. This is by far the easiest way for us to collaborate with you. While we also maintain a presence
+on [Github](https://github.com/isc-projects/dhcp), ISC developers rarely look at Github, which is
+just a mirror of our Gitlab system.
+
+ISC's Gitlab has been a target for spammers, so it is set up defensively. New users need permission
+from ISC to create new projects. We gladly do this for anyone who asks and provides a good reason.
+"I'd like to fix bug X or develop feature Y" is an excellent reason. To request a project
+allocation in ISC's Gitlab, just ask for it in a comment in your issue. Make sure
+you tag someone at ISC (@tomek, @godfryd, @vicky or @ondrej). When you write a comment in an issue or
+merge request and add a name tag on it, the user is automatically notified.
+
+Once you are given a 'project allocation' in our Gitlab, you can fork ISC DHCP and create a branch.
+This is your copy of ISC DHCP and is where you will make your changes. Go to the DHCP project page,
+click the [Fork button](https://gitlab.isc.org/isc-projects/dhcp/forks/new) and you will be prompted
+to name your branch. It helps to include the issue number and subject in the branch name. You can make
+changes to this branch without worrying that you will impact the master branch - commit priviliges
+are restricted so you cannot accidentally alter the master branch.
+
+Please read the [Gitlab How-To](https://gitlab.isc.org/isc-projects/dhcp/wikis/processes/gitlab-howto) for ISC DHCP.
+
+
+## Implement your change
+
+Please try to conform to the project's coding standards. ISC DHCP uses the same [coding standards](https://gitlab.isc.org/isc-projects/bind9/blob/master/doc/dev/style.md) as the BIND 9 project. https://gitlab.isc.org/isc-projects/bind9/blob/master/doc/dev/style.md
+
+
+## Compile your code
+
+We don't yet have continuous integration set up for ISC DHCP, so you have to check the compilation manually.
+ISC DHCP is used on a wide array of UNIX and Linux operating systems. Will your code compile and work there?
+What about endianness? It is likely that you used a regular x86 architecture machine to write your
+patch, but the software is expected to run on many other architectures. .
+
+## Run unit-tests
One of the ground rules in all ISC projects is that every piece of code has to be tested. For newer
-projects, such as Kea, we require unit-test for almost every line of code. For older code, such as
+projects, we require a unit-test for almost every line of code. For older code, such as
ISC DHCP, that was not developed with testability in mind, it's unfortunately impractical to require
extensive unit-tests. Having said that, please think thoroughly if there is any way to develop
unit-tests. The long term goal is to improve the situation.
+Where unit tests are not practical, supplying us with things like configuration file(s), lease file(s),
+PCAPS, and step-by-step on how you tested the changes would be a big help. This will aid us in
+creating and adding system tests to the build farm.
+
You should have also conducted some sort of system testing to verify that your changes do what you
want. It would be extremely helpful if you can attach any configuration files (dhcpd and or
dhclient), along with a step-by-step procedure to carry out the test(s). This will help us verify
your changes as extend our own system tests.
-Building ISC DHCP code from the repository is slightly different than the release tarballs. One
-major difference is that it does not have BIND source bundled inside and those have to be
-downloaded separately. Fortunately, there's an easy to use script for that:
-
-```bash
-sh util/bind.sh v4_4
-./configure --with-atf
-make
-```
-
Make sure you have ATF (Automated Test Framework) installed in your system. For more information
about ATF, please refer to <dhcp source tree>/doc/devel/atf.dox. Note, running "make devel" in this
directory will generate the documentation. To run the unit-tests, simply run:
```bash
+./configure --with-atf
+make
make check
```
@@ -66,37 +128,19 @@ can be obtained with the command:
./configure --help
```
-## Create an issue
+## Create a Merge Request
-Since you want to change something in ISC DHCP, there's a problem, deficiency or a missing feature.
-Quite often it is not clear why specific change is being made. The best way to explain it is to
-[create an issue here](https://gitlab.isc.org/isc-projects/dhcp/issues/new). We prefer the original
-submitter fill them as he or she has the best understanding of the purpose of the change and may
-have any extra information, e.g. "this patch fixes compilation issue on FreeBSD 10.1". If there there
-is no MR and no gitlab issue, we will create one. Depending on the subjective importance and urgency
-as perceived by the ISC engineer, the issue and/or MR will be assigned to one of the milestones.
-
-## Merge Request (also known as sending your patch the right way)
-
-The first step in writing the patch or new feature should be to get the source code from our Git
-repository. The procedure is very easy and is [explained here](https://gitlab.isc.org/isc-projects/dhcp/wikis/gitlab-howto).
-While it is possible to provide a patch against the latest stable release, it makes the review
-process much easier if it is for latest code from the Git master branch.
-
-Since you won't get write access to the ISC DHCP repository, you should fork it and then commit
-your changes to your own repo. How you organize the work depends entirely on you, but it seems
-reasonable to create a branch rather than working on your master. Once you feel that your patch
-is ready, please commit your changes and push it to your copy of ISC DHCP repo. Then go to DHCP project
+Once you feel that your patch is ready, go to the DHCP project
and [submit a Merge Request](https://gitlab.isc.org/isc-projects/dhcp/merge_requests/new).
-TODO: I don't think this is necessary. If you can't access this link or don't see New Merge Request
-button on the [merge requests page](https://gitlab.isc.org/isc-projects/dhcp/merge_requests)
-or the link gives you 404 error, please ask on dhcp-users and someone will help you out.
+If you can't access this link or don't see New Merge Request button on the [merge requests
+page](https://gitlab.isc.org/isc-projects/dhcp/merge_requests), please ask on dhcp-workers and someone
+will help you out.
Once you submit it, someone from the DHCP development team will look at it and will get back to you.
The dev team is very small, so it may take a while...
-## If you really can't do MR on gitlab...
+## If you really can't do a merge request on ISC's Gitlab...
Well, you are out of luck. There are other ways, but those are really awkward and the chances of
your patch being ignored are really high. Anyway, here they are:
@@ -116,23 +160,23 @@ your patch being ignored are really high. Anyway, here they are:
## Going through a review
-Once the MR is in the system, the action is on one of the ISC (and possibly other trusted) engineers.
+Once the merge request (MR) is in the system, the action is on one of the core developers.
+
+Sooner or later, one of these engineers will do the review. Unfortunately, we have a small team
+and we have a lot of users to support, so it may take a while for us to get to your patch.
+Having said that, we value external contributions very much and will do whatever we
+can to review patches in a timely manner.
-Sooner or later, one of ISC engineers will do the review. Here's the tricky part. One of ISC DHCP
-developers will review your patch, but it may not happen immediately. Unfortunately, developers
-are usually working under a tight schedule, so any extra unplanned review work may take a while
-sometimes. Having said that, we value external contributions very much and will do whatever we
-can to review patches in a timely manner. Don't get discouraged if your patch is not accepted
-after first review. To keep the code quality high, we use the same review processes for external
-patches as we do for internal code. It may take some cycles of review/updated patch submissions
-before the code is finally accepted. The nature of the review process is that it emphasizes areas
-that need improvement. If you are not used to the review process, you may get the impression that
-the feedback is negative. It is not: even the ISC developers seldom see reviews that say "All OK
-please merge".
+Don't get discouraged if your patch is not accepted on the first review. To keep the code
+quality high, we use the same review processes for external patches as we do for internal code.
+It may take some cycles of review/updated patch submissions before the code is finally accepted.
+The nature of the review process is that it emphasizes areas that need improvement. If you are
+not used to the review process, you may get the impression that the feedback is negative. It
+is not: even the core developers seldom see reviews that say "All OK please merge".
If we happen to have any comments that you as submitter are expected to address (and in the
-overwhelming majority of cases, we have), you will be asked to update your MR. It is not
-uncommon to see several rounds of such reviews, so this can get very complicated very quickly.
+overwhelming majority of cases, we have), you will be asked to update your MR. It is common
+to see several rounds of such reviews.
Once the process is almost complete, the developer will likely ask you how you would like to be
credited. The typical answers are by first and last name, by nickname, by company name or
@@ -143,18 +187,10 @@ notes.
Sadly, we sometimes see patches that are submitted and then the submitter never responds to our
comments or requests for an updated patch. Depending on the nature of the patch, we may either fix
-the outstanding issues on our own and get another ISC engineer to review them or the ticket may end
+the outstanding issues on our own and get another engineer to review them or the ticket may end
up in our Outstanding milestone. When a new release is started, we go through the tickets in
Outstanding, select a small number of them and move them to whatever the current milestone is. Keep
that in mind if you plan to submit a patch and forget about it. We may accept it eventually, but
-it's much, much faster process if you participate in it.
-
-## Extra steps
+it's a much, much faster process if you participate in it.
-If you are interested in knowing the results of more in-depth testing, you are welcome to visit the
-ISC Jenkins page: https://jenkins.isc.org This is a live result page with all tests being run on
-various systems. Besides basic unit-tests, we also have reports from valgrind (memory debugger),
-cppcheck and clang-analyzer (static code analyzers), Lettuce system tests and more. Although it
-is not possible for non ISC employees to run tests on that farm, it is possible that your
-contributed patch will end up there sooner or later. We also have ISC Forge tests running and other
-additional tests, but currently those test results are not publicly available.
+#### Thank you for contributing your time and expertise to the ISC DHCP Project.
diff --git a/Makefile.am b/Makefile.am
index e814399d..2cc1dd90 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,6 +40,8 @@ endif
# to fool automake when the bind directory does not exist.
SUBDIRS = @BINDSUBDIR@ includes tests common omapip client dhcpctl relay server
+DIST_SUBDIRS = $(SUBDIRS) keama
+
nobase_include_HEADERS = dhcpctl/dhcpctl.h
#
diff --git a/Makefile.in b/Makefile.in
index 182e9511..9d14ab4b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -166,7 +166,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- cscope distdir dist dist-all distcheck
+ cscope distdir distdir-am dist dist-all distcheck
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -187,7 +187,6 @@ am__define_uniq_tagged_files = \
ETAGS = etags
CTAGS = ctags
CSCOPE = cscope
-DIST_SUBDIRS = $(SUBDIRS)
am__DIST_COMMON = $(srcdir)/Makefile.in \
$(top_srcdir)/doc/devel/doxyfile.in README compile \
config.guess config.sub depcomp install-sh missing
@@ -390,6 +389,7 @@ EXTRA_DIST = RELNOTES LICENSE configure.ac+lt config+lt \
# Use an autoconf substitution vs an automake conditional here
# to fool automake when the bind directory does not exist.
SUBDIRS = @BINDSUBDIR@ includes tests common omapip client dhcpctl relay server
+DIST_SUBDIRS = $(SUBDIRS) keama
nobase_include_HEADERS = dhcpctl/dhcpctl.h
#
@@ -558,7 +558,10 @@ distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
$(am__remove_distdir)
test -d "$(distdir)" || mkdir "$(distdir)"
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -623,7 +626,7 @@ distdir: $(DISTFILES)
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r "$(distdir)"
dist-gzip: distdir
- tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
$(am__post_remove_distdir)
dist-bzip2: distdir
@@ -649,7 +652,7 @@ dist-shar: distdir
@echo WARNING: "Support for shar distribution archives is" \
"deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
- shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
$(am__post_remove_distdir)
dist-zip: distdir
@@ -667,7 +670,7 @@ dist dist-all:
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
- GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
*.tar.lz*) \
@@ -677,7 +680,7 @@ distcheck: dist
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
- GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
esac
diff --git a/RELNOTES b/RELNOTES
index 7e9e7e4b..4883820b 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -92,7 +92,7 @@ by Eric Young (eay@cryptsoft.com).
- A new configuration parameter, ping-cltt-secs (v4 operation only), has
been added to allow the user to specify the number of seconds that must
elapse since CLTT before a ping check is conducted. Prior to this, the
- value was hard coded at 60 seconds. Please see the server man pages for
+ value was hard coded at 60 seconds. Please see the server man pages for
a more detailed discussion.
[ISC-Bugs #36283]
@@ -102,7 +102,7 @@ by Eric Young (eay@cryptsoft.com).
than in seconds (via ping-timeout). When greater than zero, the value
of ping-timeout-ms will override the value of ping-timeout. Thanks
to Jay Doran from Bluecat Networks for suggesting this feature.
- [ISC-Bugs #10,!6 git ebe4f7ae427fa91f561a0b6e5f242de08d319a16]
+ [Gitlab #10]
Changes since 4.4.1 (Bug Fixes)
@@ -115,7 +115,7 @@ by Eric Young (eay@cryptsoft.com).
- Bind9 now defaults to requiring python to build. The Makefile for
building Bind9 when bundled with ISC DHCP was modified to turn off
this dependency.
- [ISC-Bugs #3,!1 git cc35f84943df44dac2499f3e16e8aaba7d54191d]
+ [Gitlab #3]
- Corrected a dual-stack mixed-mode issue that occurs when both
ddns-guard-id-must-match and ddns-other-guard-is-dynamic
@@ -123,21 +123,71 @@ by Eric Young (eay@cryptsoft.com).
the presence of a guard record belonging to another client as
a case of no guard record at all. Thanks to Fernando Soto
from BlueCat Networks for reporting this issue.
- [ISC-Bugs #1,!2 git 9ef78585440f568da2a2a0093a8c40c49118e292]
+ [Gitlab #1]
- Corrected a compilation issue that occurred when building without DNS
update ability (e.g. by undefining NSUPDATE).
- [ISC-Bugs #16,!9 git ddb508ac083dae4ff83279dd240bad7f73a97b7d]
+ [Gitlab #16]
- Corrected an issue that was causing the server, when running in
DHPCv4 mode, to segfault when class lease limits are reached.
Thanks to Peter Nagy at Porion-Digital for reporting the matter
and submitting a patch.
- [ISC-Bugs #13,!7 git dfcbe359ab278cad70015994ca73ef50d626b23a]
+ [Gitlab #13]
- Made minor changes to eliminate warnings when compiled with GCC 9.
Thanks to Brett Neumeier for bringing the matter to our attention.
- [ISC-Bugs #15,!10 git c138f38bd00ceca4e1e51a4db7542a15ef79babd]
+ [Gitlab #15]
+
+- Fixed potential memory leaks in parser error message generation
+ spotted by Coverity, CIDs: 1448191, 1448193, 1448194, 1448195
+ [Gitlab #30]
+
+- Updated URL of IEEE oui.txt in contrib/dhcp-lease-list.pl. Thanks
+ to Tommy Smith for contributing the patch.
+ [Gitlab #26]
+
+- Fixed define flags when using SO_BINDTODEVICE. Thanks to Joe LeVeque for
+ reporting the issue.
+ [GitLab #19]
+
+- Applied a patch from OpenBSD to always set the scope id of outbound
+ DHPCv6 packets. Note this change only applies when compiling under
+ OpenBSD. Thanks to Brad Smith at OpenBSD from bringing it to our
+ attention.
+ [Gitlab #33]
+
+- Modified dhclient to not discard config file leases that are
+ duplicates of server-provided leases and to retain such leases
+ after they have been used as the fallback active lease and
+ DHCP service has been restored. This allows them to be used
+ more than once during the lifetime of a dhclient instance.
+ This applies to DHCPv4 operation only.
+ [Gitlab #9]
+
+- Corrected a number of reference counter and zero-length buffer leaks.
+ [Gitlab #57]
+
+- Closed a small window of time between the installation of graceful
+ shutdown signal handlers and application context startup, during which
+ the receipt of shutdown signal would cause a REQUIRE() assertion to
+ occur. Note this issue is only visible when compiling with
+ ENABLE_GENTLE_SHUTDOWN defined.
+ [Gitlab #53]
+
+- Corrected a buffer overflow that can occur when retrieving zone
+ names that are more than 255 characters in length.
+ [Gitlab #20]
+
+- The "d" domain name option format was incorrectly handled as text
+ instead of RFC 1035 wire format. Thanks to Jay Doran at BlueCat Networks
+ for reporting this issue.
+ [Gitlab #2]
+
+- Improved the error message issued when a host declaration has both
+ a uid and a dhcp-client-identifier. Server configuration parsing will
+ now fail if a host declaration specifies more than one uid.
+ [Gitlab #7]
Changes since 4.4.0 (New Features)
- none
diff --git a/aclocal.m4 b/aclocal.m4
index 0e300551..3508cb7f 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.15 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -32,10 +32,10 @@ To do so, use the procedure documented by the package, typically 'autoreconf'.])
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.15'
+[am__api_version='1.16'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.15], [],
+m4_if([$1], [1.16.1], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.15])dnl
+[AM_AUTOMAKE_VERSION([1.16.1])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -110,7 +110,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
# AM_COND_IF -*- Autoconf -*-
-# Copyright (C) 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2008-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -147,7 +147,7 @@ fi[]dnl
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -178,7 +178,7 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -369,13 +369,12 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
@@ -383,49 +382,41 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
- case $CONFIG_FILES in
- *\'*) eval set x "$CONFIG_FILES" ;;
- *) set x $CONFIG_FILES ;;
- esac
+ # TODO: see whether this extra hack can be removed once we start
+ # requiring Autoconf 2.70 or later.
+ AS_CASE([$CONFIG_FILES],
+ [*\'*], [eval set x "$CONFIG_FILES"],
+ [*], [set x $CONFIG_FILES])
shift
- for mf
+ # Used to flag and report bootstrapping failures.
+ am_rc=0
+ for am_mf
do
# Strip MF so we end up with the name of the file.
- mf=`echo "$mf" | sed -e 's/:.*$//'`
- # Check whether this is an Automake generated Makefile or not.
- # We used to match only the files named 'Makefile.in', but
- # some people rename them; so instead we look at the file content.
- # Grep'ing the first line is not enough: some people post-process
- # each Makefile.in and add a new line on top of each file to say so.
- # Grep'ing the whole file is not good either: AIX grep has a line
+ am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile which includes
+ # dependency-tracking related rules and includes.
+ # Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
- if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
- dirpart=`AS_DIRNAME("$mf")`
- else
- continue
- fi
- # Extract the definition of DEPDIR, am__include, and am__quote
- # from the Makefile without running 'make'.
- DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
- test -z "$DEPDIR" && continue
- am__include=`sed -n 's/^am__include = //p' < "$mf"`
- test -z "$am__include" && continue
- am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
- # Find all dependency output files, they are included files with
- # $(DEPDIR) in their names. We invoke sed twice because it is the
- # simplest approach to changing $(DEPDIR) to its actual value in the
- # expansion.
- for file in `sed -n "
- s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
- # Make sure the directory exists.
- test -f "$dirpart/$file" && continue
- fdir=`AS_DIRNAME(["$file"])`
- AS_MKDIR_P([$dirpart/$fdir])
- # echo "creating $dirpart/$file"
- echo '# dummy' > "$dirpart/$file"
- done
+ sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+ || continue
+ am_dirpart=`AS_DIRNAME(["$am_mf"])`
+ am_filepart=`AS_BASENAME(["$am_mf"])`
+ AM_RUN_LOG([cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles]) || am_rc=$?
done
+ if test $am_rc -ne 0; then
+ AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
+ for automatic dependency tracking. Try re-running configure with the
+ '--disable-dependency-tracking' option to at least be able to build
+ the package (albeit without support for automatic dependency tracking).])
+ fi
+ AS_UNSET([am_dirpart])
+ AS_UNSET([am_filepart])
+ AS_UNSET([am_mf])
+ AS_UNSET([am_rc])
+ rm -f conftest-deps.mk
}
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
@@ -434,18 +425,17 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
-# This code is only required when automatic dependency tracking
-# is enabled. FIXME. This creates each '.P' file that we will
-# need in order to bootstrap the dependency handling code.
+# This code is only required when automatic dependency tracking is enabled.
+# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
+# order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
- [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
-])
+ [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -532,8 +522,8 @@ AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
# We need awk for the "check" target (and possibly the TAP driver). The
# system "awk" is bad on some platforms.
@@ -600,7 +590,7 @@ END
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
@@ -642,7 +632,7 @@ for _am_header in $config_headers :; do
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -663,7 +653,7 @@ if test x"${install_sh+set}" != xset; then
fi
AC_SUBST([install_sh])])
-# Copyright (C) 2003-2014 Free Software Foundation, Inc.
+# Copyright (C) 2003-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -685,7 +675,7 @@ AC_SUBST([am__leading_dot])])
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
# From Jim Meyering
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -720,7 +710,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -728,49 +718,42 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
# AM_MAKE_INCLUDE()
# -----------------
-# Check to see how make treats includes.
+# Check whether make has an 'include' directive that can support all
+# the idioms we need for our automatic dependency tracking code.
AC_DEFUN([AM_MAKE_INCLUDE],
-[am_make=${MAKE-make}
-cat > confinc << 'END'
+[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
+cat > confinc.mk << 'END'
am__doit:
- @echo this is the am__doit target
+ @echo this is the am__doit target >confinc.out
.PHONY: am__doit
END
-# If we don't find an include directive, just comment out the code.
-AC_MSG_CHECKING([for style of include used by $am_make])
am__include="#"
am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# Ignore all kinds of additional output from 'make'.
-case `$am_make -s -f confmf 2> /dev/null` in #(
-*the\ am__doit\ target*)
- am__include=include
- am__quote=
- _am_result=GNU
- ;;
-esac
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
- echo '.include "confinc"' > confmf
- case `$am_make -s -f confmf 2> /dev/null` in #(
- *the\ am__doit\ target*)
- am__include=.include
- am__quote="\""
- _am_result=BSD
- ;;
- esac
-fi
-AC_SUBST([am__include])
-AC_SUBST([am__quote])
-AC_MSG_RESULT([$_am_result])
-rm -f confinc confmf
-])
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+ AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
+ AS_CASE([$?:`cat confinc.out 2>/dev/null`],
+ ['0:this is the am__doit target'],
+ [AS_CASE([$s],
+ [BSD], [am__include='.include' am__quote='"'],
+ [am__include='include' am__quote=''])])
+ if test "$am__include" != "#"; then
+ _am_result="yes ($s style)"
+ break
+ fi
+done
+rm -f confinc.* confmf.*
+AC_MSG_RESULT([${_am_result}])
+AC_SUBST([am__include])])
+AC_SUBST([am__quote])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -809,7 +792,7 @@ fi
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -838,7 +821,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -885,7 +868,7 @@ AC_LANG_POP([C])])
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -904,7 +887,7 @@ AC_DEFUN([AM_RUN_LOG],
# Check to make sure that the build environment is sane. -*- Autoconf -*-
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -985,7 +968,7 @@ AC_CONFIG_COMMANDS_PRE(
rm -f conftest.file
])
-# Copyright (C) 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2009-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1045,7 +1028,7 @@ AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1073,7 +1056,7 @@ fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2006-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1092,7 +1075,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004-2014 Free Software Foundation, Inc.
+# Copyright (C) 2004-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/bind/Makefile.in b/bind/Makefile.in
new file mode 100644
index 00000000..107ce159
--- /dev/null
+++ b/bind/Makefile.in
@@ -0,0 +1,120 @@
+#
+# Copyright (C) 2009-2018 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or 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.
+
+# Configure and build the bind libraries for use by DHCP
+
+binddir=@BINDDIR@
+bindsrcdir=@BINDSRCDIR@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindconfig = --without-openssl --without-libxml2 --without-libjson \
+ --without-gssapi --disable-threads --without-lmdb \
+ --includedir=@includedir@ --libdir=@libdir@ --without-python\
+ @BINDLT@ @BINDIOMUX@ @BINDCONFIG@ --enable-full-report
+
+@BIND_ATF_FALSE@cleandirs = ./lib ./include
+@BIND_ATF_TRUE@cleandirs = ./lib ./include ./atf
+cleanfiles = ./configure.log ./build.log ./install.log
+
+bindlibs = isc dns isccfg irs
+installdirs = includedir=${binddir}/include libdir=${binddir}/lib
+
+@BIND_ATF_FALSE@all: bind1 bind2
+@BIND_ATF_TRUE@all: bind1 atf bind2
+
+bind1:
+# Extract the source from the tarball, if it hasn't been already.
+ @if test -d ${bindsrcdir} ; then \
+ echo ${bindsrcdir} already unpacked... ; \
+ else \
+ gunzip -c bind.tar.gz | tar xf - ; \
+ fi
+
+# Configure the libraries
+# Currently disable the epoll, devpoll and kqueue options as they
+# don't interact well with the DHCP code.
+# If the top-level Bind Makefile exists we skip the configuration step
+# as we assume it's done and won't change. Doing a make clean will
+# reset things if necessary.
+ @if test -f ${bindsrcdir}/Makefile ; then \
+ echo Bind libraries already configured ; \
+ else \
+ echo Configuring BIND libraries for DHCP. ; \
+ rm -rf ${cleandirs} ${cleanfiles} ; \
+ (cd ${bindsrcdir} && \
+ ./configure ${bindconfig} > ${binddir}/configure.log); \
+ fi
+
+atf:
+# Build and copy the ATF support if not yet installed.
+ @if test -d ./atf ; then \
+ echo ATF support already installed ; \
+ else \
+ echo Building ATF support ; \
+ (cd ${bindsrcdir}/unit; \
+ $(MAKE) atf > ${binddir}/build.log ; \
+ cp -rp atf ${binddir}) ; \
+ fi
+
+bind2:
+# Build and install the libraries
+# No need to do anything if we already have something installed.
+ @if test -d ${binddir}/lib ; then \
+ echo Bind libraries already installed ; \
+ else \
+ echo Building BIND libraries - this takes some time. ; \
+ for libdir in ${bindlibs} ; do \
+ (cd ${bindsrcdir}/lib/$$libdir ; \
+ echo Building $$libdir library in `pwd` ; \
+ $(MAKE) all >> ${binddir}/build.log) ; \
+ done ; \
+ \
+ echo Installing BIND libraries to ${binddir}. ; \
+ for libdir in ${bindlibs} ; do \
+ (cd ${bindsrcdir}/lib/$$libdir ; \
+ MAKEDEFS="${installdirs}"; export MAKEDEFS; \
+ $(MAKE) ${installdirs} LIBTOOL_MODE_INSTALL= install >> \
+ ${binddir}/install.log) ; \
+ done ; \
+ fi
+
+clean:
+ @echo Cleaning BIND library.
+ rm -rf ${bindsrcdir} ${cleandirs} ${cleanfiles}
+
+@INSTALL_BIND_FALSE@install:
+@INSTALL_BIND_TRUE@install: install-bind
+
+install-bind: all
+ @for libdir in ${bindlibs} ; do \
+ (cd ${bindsrcdir}/lib/$$libdir ; \
+ $(MAKE) install) ; \
+ done
+
+@INSTALL_BIND_FALSE@uninstall:
+@INSTALL_BIND_TRUE@uninstall: uninstall-bind
+
+uninstall-bind: all
+ @for libdir in ${bindlibs} ; do \
+ (cd ${bindsrcdir}/lib/$$libdir ; \
+ $(MAKE) uninstall) ; \
+ done
+
+# Include the following so that this Makefile is happy when the parent
+# tries to use them.
+
+check distdir distclean dvi installcheck:
diff --git a/bind/bind.tar.gz b/bind/bind.tar.gz
new file mode 100644
index 00000000..c15be8e5
--- /dev/null
+++ b/bind/bind.tar.gz
Binary files differ
diff --git a/bind/version.tmp b/bind/version.tmp
new file mode 100644
index 00000000..ceed606c
--- /dev/null
+++ b/bind/version.tmp
@@ -0,0 +1,11 @@
+# This file must follow /bin/sh rules. It is imported directly via
+# configure.
+#
+PRODUCT=BIND
+DESCRIPTION="(Extended Support Version)"
+MAJORVER=9
+MINORVER=11
+PATCHVER=8
+RELEASETYPE=
+RELEASEVER=
+EXTENSIONS=
diff --git a/client/Makefile.in b/client/Makefile.in
index a016bbe9..dc473f35 100644
--- a/client/Makefile.in
+++ b/client/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -122,7 +122,10 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/client_tables.Po \
+ ./$(DEPDIR)/clparse.Po ./$(DEPDIR)/dhc6.Po \
+ ./$(DEPDIR)/dhclient.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -190,7 +193,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
+ distdir distdir-am
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -404,8 +407,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -469,10 +472,16 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client_tables.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clparse.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhc6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhclient.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client_tables.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clparse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhc6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhclient.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -694,7 +703,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -792,7 +804,10 @@ clean: clean-recursive
clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/client_tables.Po
+ -rm -f ./$(DEPDIR)/clparse.Po
+ -rm -f ./$(DEPDIR)/dhc6.Po
+ -rm -f ./$(DEPDIR)/dhclient.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -838,7 +853,10 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/client_tables.Po
+ -rm -f ./$(DEPDIR)/clparse.Po
+ -rm -f ./$(DEPDIR)/dhc6.Po
+ -rm -f ./$(DEPDIR)/dhclient.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -861,21 +879,22 @@ uninstall-man: uninstall-man5 uninstall-man8
.MAKE: $(am__recursive_targets) install-am install-strip
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-generic clean-sbinPROGRAMS cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-dist_sysconfDATA install-dvi install-dvi-am \
- install-exec install-exec-am install-html install-html-am \
- install-info install-info-am install-man install-man5 \
- install-man8 install-pdf install-pdf-am install-ps \
- install-ps-am install-sbinPROGRAMS install-strip installcheck \
- installcheck-am installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am uninstall-dist_sysconfDATA uninstall-man \
- uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dist_sysconfDATA \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-man install-man5 install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-dist_sysconfDATA uninstall-man uninstall-man5 \
+ uninstall-man8 uninstall-sbinPROGRAMS
.PRECIOUS: Makefile
diff --git a/client/clparse.c b/client/clparse.c
index eaf48a8f..35e2d164 100644
--- a/client/clparse.c
+++ b/client/clparse.c
@@ -3,7 +3,7 @@
Parser for dhclient config and lease files... */
/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -47,6 +47,9 @@ static struct dhc6_addr *parse_client6_iaprefix_statement(struct parse *cfile);
static void parse_lease_id_format (struct parse *cfile);
+extern void discard_duplicate (struct client_lease** lease_list,
+ struct client_lease* lease);
+
/* client-conf-file :== client-declarations END_OF_FILE
client-declarations :== <nil>
| client-declaration
@@ -1096,7 +1099,7 @@ void parse_client_lease_statement (cfile, is_static)
struct parse *cfile;
int is_static;
{
- struct client_lease *lease, *lp, *pl, *next;
+ struct client_lease *lease;
struct interface_info *ip = (struct interface_info *)0;
int token;
const char *val;
@@ -1154,22 +1157,11 @@ void parse_client_lease_statement (cfile, is_static)
/* The new lease may supersede a lease that's not the
active lease but is still on the lease list, so scan the
lease list looking for a lease with the same address, and
- if we find it, toss it. */
- pl = (struct client_lease *)0;
- for (lp = client -> leases; lp; lp = next) {
- next = lp -> next;
- if (lp -> address.len == lease -> address.len &&
- !memcmp (lp -> address.iabuf, lease -> address.iabuf,
- lease -> address.len)) {
- if (pl)
- pl -> next = next;
- else
- client -> leases = next;
- destroy_client_lease (lp);
- break;
- } else
- pl = lp;
- }
+ if we find it, toss it. We only allow supercession if
+ the leases originated from the same source. In other words,
+ either both are from the config file or both are from the lease
+ file. This keeps us from discarding fallback leases */
+ discard_duplicate (&client->leases, lease);
/* If this is a preloaded lease, just put it on the list of recorded
leases - don't make it the active lease. */
diff --git a/client/dhc6.c b/client/dhc6.c
index 16a08380..55ecab80 100644
--- a/client/dhc6.c
+++ b/client/dhc6.c
@@ -816,8 +816,7 @@ dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
} else {
log_error("Invalid IA_NA option cache.");
dfree(ia, MDL);
- if (ds.len != 0)
- data_string_forget(&ds, MDL);
+ data_string_forget(&ds, MDL);
return ISC_R_UNEXPECTED;
}
}
@@ -919,8 +918,7 @@ dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet,
} else {
log_error("Invalid IA_TA option cache.");
dfree(ia, MDL);
- if (ds.len != 0)
- data_string_forget(&ds, MDL);
+ data_string_forget(&ds, MDL);
return ISC_R_UNEXPECTED;
}
}
@@ -1042,8 +1040,7 @@ dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet,
} else {
log_error("Invalid IA_PD option cache.");
dfree(ia, MDL);
- if (ds.len != 0)
- data_string_forget(&ds, MDL);
+ data_string_forget(&ds, MDL);
return ISC_R_UNEXPECTED;
}
}
@@ -1158,8 +1155,7 @@ dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet,
} else {
log_error("Invalid IAADDR option cache.");
dfree(addr, MDL);
- if (ds.len != 0)
- data_string_forget(&ds, MDL);
+ data_string_forget(&ds, MDL);
return ISC_R_UNEXPECTED;
}
}
@@ -1283,8 +1279,7 @@ dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet,
} else {
log_error("Invalid IAPREFIX option cache.");
dfree(pfx, MDL);
- if (ds.len != 0)
- data_string_forget(&ds, MDL);
+ data_string_forget(&ds, MDL);
return ISC_R_UNEXPECTED;
}
}
@@ -1306,9 +1301,7 @@ dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line)
}
lease = *src;
- if (lease->server_id.len != 0)
- data_string_forget(&lease->server_id, file, line);
-
+ data_string_forget(&lease->server_id, file, line);
for (ia = lease->bindings ; ia != NULL ; ia = nia) {
nia = ia->next;
diff --git a/client/dhclient.c b/client/dhclient.c
index 6828c676..4a22803a 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -51,6 +51,8 @@ static char path_dhclient_script_array[] = _PATH_DHCLIENT_SCRIPT;
char *path_dhclient_script = path_dhclient_script_array;
const char *path_dhclient_duid = NULL;
+static void add_to_tail(struct client_lease** lease_list, struct client_lease* lease);
+
/* False (default) => we write and use a pid file */
isc_boolean_t no_pid_file = ISC_FALSE;
@@ -1556,8 +1558,16 @@ void bind_lease (client)
write_client_lease(client, client->new, 0, 1);
/* Replace the old active lease with the new one. */
- if (client->active)
- destroy_client_lease(client->active);
+ if (client->active) {
+ if (client->active->is_static) {
+ // We need to preserve the fallback lease in case
+ // we lose DHCP service again.
+ add_to_tail(&client->leases, client->active);
+ } else {
+ destroy_client_lease(client->active);
+ }
+ }
+
client->active = client->new;
client->new = NULL;
@@ -2458,6 +2468,99 @@ void send_discover (cpp)
add_timeout(&tv, send_discover, client, 0, 0);
}
+
+/*
+ * \brief Remove leases from a list of leases which duplicate a given lease
+ *
+ * Searches through a linked-list of leases, remove the first one matches the
+ * given lease's address and value of is_static. The latter test is done
+ * so we only remove leases that are from the same source (i.e server/lease file
+ * vs config file). This ensures we do not discard "fallback" config file leases
+ * that happen to match non-config file leases.
+ *
+ * \param lease_list list of leases to clean
+ * \param lease lease for which duplicates should be removed
+ */
+void discard_duplicate (struct client_lease** lease_list, struct client_lease* lease) {
+ struct client_lease *cur, *prev, *next;
+
+ if (!lease_list || !lease) {
+ return;
+ }
+
+ prev = (struct client_lease *)0;
+ for (cur = *lease_list; cur; cur = next) {
+ next = cur->next;
+ if ((cur->is_static == lease->is_static) &&
+ (cur->address.len == lease->address.len &&
+ !memcmp (cur->address.iabuf, lease->address.iabuf,
+ lease->address.len))) {
+ if (prev)
+ prev->next = next;
+ else
+ *lease_list = next;
+
+ destroy_client_lease (cur);
+ break;
+ } else {
+ prev = cur;
+ }
+ }
+}
+
+/*
+ * \brief Add a given lease to the end of list of leases
+ *
+ * Searches through a linked-list of leases, removing any that match the
+ * given lease's address and value of is_static. The latter test is done
+ * so we only remove leases that are from the same source (i.e server/lease file
+ * vs config file). This ensures we do not discard "fallback" config file leases
+ * that happen to match non-config file leases.
+ *
+ * \param lease_list list of leases to clean
+ * \param lease lease for which duplicates should be removed
+ */
+void add_to_tail(struct client_lease** lease_list,
+ struct client_lease* lease)
+{
+ if (!lease_list || !lease) {
+ return;
+ }
+
+ /* If there is already a lease for this address and
+ * is_static value, toss discard it. This ensures
+ * we only keep one dynamic and/or one static lease
+ * for a given address. */
+ discard_duplicate(lease_list, lease);
+
+ /* Find the tail */
+ struct client_lease* tail;
+ for (tail = *lease_list; tail && tail->next; tail = tail->next){};
+
+ /* Ensure the tail points nowhere. */
+ lease->next = NULL;
+
+ /* Add to the tail. */
+ if (!tail) {
+ *lease_list = lease;
+ } else {
+ tail->next = lease;
+ }
+}
+
+#if 0
+void dbg_print_lease(char *text, struct client_lease* lease) {
+ if (!lease) {
+ log_debug("%s, lease is null", text);
+ } else {
+ log_debug ("%s: %p addr:%s expires:%ld :is_static? %d",
+ text, lease, piaddr (lease->address),
+ (lease->expiry - cur_time),
+ lease->is_static);
+ }
+}
+#endif
+
/* state_panic gets called if we haven't received any offers in a preset
amount of time. When this happens, we try to use existing leases that
haven't yet expired, and failing that, we call the client script and
@@ -2483,8 +2586,10 @@ void state_panic (cpp)
/* Run through the list of leases and see if one can be used. */
while (client -> active) {
if (client -> active -> expiry > cur_time) {
- log_info ("Trying recorded lease %s",
- piaddr (client -> active -> address));
+ log_info ("Trying %s lease %s",
+ (client -> active -> is_static
+ ? "fallback" : "recorded"),
+ piaddr (client -> active -> address));
/* Run the client script with the existing
parameters. */
script_init(client, "TIMEOUT",
@@ -2531,12 +2636,8 @@ void state_panic (cpp)
activate_next:
/* Otherwise, put the active lease at the end of the
lease list, and try another lease.. */
- for (lp = client -> leases; lp -> next; lp = lp -> next)
- ;
- lp -> next = client -> active;
- if (lp -> next) {
- lp -> next -> next = (struct client_lease *)0;
- }
+ add_to_tail(&client->leases, client->active);
+
client -> active = client -> leases;
client -> leases = client -> leases -> next;
@@ -4078,9 +4179,10 @@ void client_option_envadd (struct option_cache *oc,
"option - discarded",
name);
}
- data_string_forget (&data, MDL);
}
}
+
+ data_string_forget (&data, MDL);
}
}
diff --git a/client/tests/Makefile.in b/client/tests/Makefile.in
index a7cfb89a..a68ce884 100644
--- a/client/tests/Makefile.in
+++ b/client/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -127,7 +127,9 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/clparse.Po ./$(DEPDIR)/dhc6.Po \
+ ./$(DEPDIR)/dhclient.Po ./$(DEPDIR)/duid_unittest.Po
am__mv = mv -f
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -167,7 +169,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
+ distdir distdir-am
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -377,8 +379,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -403,10 +405,16 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clparse.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhc6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhclient.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/duid_unittest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clparse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhc6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhclient.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/duid_unittest.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -563,7 +571,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -660,7 +671,10 @@ clean: clean-recursive
clean-am: clean-checkPROGRAMS clean-generic mostlyclean-am
distclean: distclean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/clparse.Po
+ -rm -f ./$(DEPDIR)/dhc6.Po
+ -rm -f ./$(DEPDIR)/dhclient.Po
+ -rm -f ./$(DEPDIR)/duid_unittest.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-local distclean-tags
@@ -704,7 +718,10 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/clparse.Po
+ -rm -f ./$(DEPDIR)/dhc6.Po
+ -rm -f ./$(DEPDIR)/dhclient.Po
+ -rm -f ./$(DEPDIR)/duid_unittest.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -724,19 +741,19 @@ uninstall-am:
.MAKE: $(am__recursive_targets) check-am install-am install-strip
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-checkPROGRAMS clean-generic cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-local distclean-tags distdir dvi dvi-am html html-am \
- info info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-checkPROGRAMS \
+ clean-generic cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-local \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
diff --git a/common/Makefile.in b/common/Makefile.in
index 630de997..ca801bdc 100644
--- a/common/Makefile.in
+++ b/common/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -159,7 +159,20 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/alloc.Po ./$(DEPDIR)/bpf.Po \
+ ./$(DEPDIR)/comapi.Po ./$(DEPDIR)/conflex.Po \
+ ./$(DEPDIR)/ctrace.Po ./$(DEPDIR)/dhcp4o6.Po \
+ ./$(DEPDIR)/discover.Po ./$(DEPDIR)/dispatch.Po \
+ ./$(DEPDIR)/dlpi.Po ./$(DEPDIR)/dns.Po ./$(DEPDIR)/ethernet.Po \
+ ./$(DEPDIR)/execute.Po ./$(DEPDIR)/fddi.Po ./$(DEPDIR)/icmp.Po \
+ ./$(DEPDIR)/inet.Po ./$(DEPDIR)/lpf.Po ./$(DEPDIR)/memory.Po \
+ ./$(DEPDIR)/nit.Po ./$(DEPDIR)/ns_name.Po \
+ ./$(DEPDIR)/options.Po ./$(DEPDIR)/packet.Po \
+ ./$(DEPDIR)/parse.Po ./$(DEPDIR)/print.Po ./$(DEPDIR)/raw.Po \
+ ./$(DEPDIR)/resolv.Po ./$(DEPDIR)/socket.Po \
+ ./$(DEPDIR)/tables.Po ./$(DEPDIR)/tr.Po ./$(DEPDIR)/tree.Po \
+ ./$(DEPDIR)/upf.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -198,7 +211,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
+ distdir distdir-am
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -408,8 +421,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -465,36 +478,42 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bpf.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comapi.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conflex.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctrace.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp4o6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/discover.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlpi.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ethernet.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execute.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fddi.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icmp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpf.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nit.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_name.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolv.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tables.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tr.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bpf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comapi.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conflex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctrace.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp4o6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/discover.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlpi.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ethernet.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execute.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fddi.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icmp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_name.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolv.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tables.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upf.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -652,7 +671,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -750,7 +772,36 @@ clean: clean-recursive
clean-am: clean-generic clean-libLIBRARIES mostlyclean-am
distclean: distclean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/alloc.Po
+ -rm -f ./$(DEPDIR)/bpf.Po
+ -rm -f ./$(DEPDIR)/comapi.Po
+ -rm -f ./$(DEPDIR)/conflex.Po
+ -rm -f ./$(DEPDIR)/ctrace.Po
+ -rm -f ./$(DEPDIR)/dhcp4o6.Po
+ -rm -f ./$(DEPDIR)/discover.Po
+ -rm -f ./$(DEPDIR)/dispatch.Po
+ -rm -f ./$(DEPDIR)/dlpi.Po
+ -rm -f ./$(DEPDIR)/dns.Po
+ -rm -f ./$(DEPDIR)/ethernet.Po
+ -rm -f ./$(DEPDIR)/execute.Po
+ -rm -f ./$(DEPDIR)/fddi.Po
+ -rm -f ./$(DEPDIR)/icmp.Po
+ -rm -f ./$(DEPDIR)/inet.Po
+ -rm -f ./$(DEPDIR)/lpf.Po
+ -rm -f ./$(DEPDIR)/memory.Po
+ -rm -f ./$(DEPDIR)/nit.Po
+ -rm -f ./$(DEPDIR)/ns_name.Po
+ -rm -f ./$(DEPDIR)/options.Po
+ -rm -f ./$(DEPDIR)/packet.Po
+ -rm -f ./$(DEPDIR)/parse.Po
+ -rm -f ./$(DEPDIR)/print.Po
+ -rm -f ./$(DEPDIR)/raw.Po
+ -rm -f ./$(DEPDIR)/resolv.Po
+ -rm -f ./$(DEPDIR)/socket.Po
+ -rm -f ./$(DEPDIR)/tables.Po
+ -rm -f ./$(DEPDIR)/tr.Po
+ -rm -f ./$(DEPDIR)/tree.Po
+ -rm -f ./$(DEPDIR)/upf.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -796,7 +847,36 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/alloc.Po
+ -rm -f ./$(DEPDIR)/bpf.Po
+ -rm -f ./$(DEPDIR)/comapi.Po
+ -rm -f ./$(DEPDIR)/conflex.Po
+ -rm -f ./$(DEPDIR)/ctrace.Po
+ -rm -f ./$(DEPDIR)/dhcp4o6.Po
+ -rm -f ./$(DEPDIR)/discover.Po
+ -rm -f ./$(DEPDIR)/dispatch.Po
+ -rm -f ./$(DEPDIR)/dlpi.Po
+ -rm -f ./$(DEPDIR)/dns.Po
+ -rm -f ./$(DEPDIR)/ethernet.Po
+ -rm -f ./$(DEPDIR)/execute.Po
+ -rm -f ./$(DEPDIR)/fddi.Po
+ -rm -f ./$(DEPDIR)/icmp.Po
+ -rm -f ./$(DEPDIR)/inet.Po
+ -rm -f ./$(DEPDIR)/lpf.Po
+ -rm -f ./$(DEPDIR)/memory.Po
+ -rm -f ./$(DEPDIR)/nit.Po
+ -rm -f ./$(DEPDIR)/ns_name.Po
+ -rm -f ./$(DEPDIR)/options.Po
+ -rm -f ./$(DEPDIR)/packet.Po
+ -rm -f ./$(DEPDIR)/parse.Po
+ -rm -f ./$(DEPDIR)/print.Po
+ -rm -f ./$(DEPDIR)/raw.Po
+ -rm -f ./$(DEPDIR)/resolv.Po
+ -rm -f ./$(DEPDIR)/socket.Po
+ -rm -f ./$(DEPDIR)/tables.Po
+ -rm -f ./$(DEPDIR)/tr.Po
+ -rm -f ./$(DEPDIR)/tree.Po
+ -rm -f ./$(DEPDIR)/upf.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -818,20 +898,21 @@ uninstall-man: uninstall-man5
.MAKE: $(am__recursive_targets) install-am install-strip
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-generic clean-libLIBRARIES cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am install-dvi \
- install-dvi-am install-exec install-exec-am install-html \
- install-html-am install-info install-info-am \
- install-libLIBRARIES install-man install-man5 install-pdf \
- install-pdf-am install-ps install-ps-am install-strip \
- installcheck installcheck-am installdirs installdirs-am \
- maintainer-clean maintainer-clean-generic mostlyclean \
- mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
- tags tags-am uninstall uninstall-am uninstall-libLIBRARIES \
- uninstall-man uninstall-man5
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLIBRARIES install-man \
+ install-man5 install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libLIBRARIES uninstall-man \
+ uninstall-man5
.PRECIOUS: Makefile
diff --git a/common/dns.c b/common/dns.c
index 5b097b69..bdd8af46 100644
--- a/common/dns.c
+++ b/common/dns.c
@@ -1246,7 +1246,7 @@ find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
}
/* Make sure the zone name will fit. */
- if (strlen(zone->name) > sizeof(ddns_cb->zone_name)) {
+ if (strlen(zone->name) >= sizeof(ddns_cb->zone_name)) {
dns_zone_dereference(&zone, MDL);
return (ISC_R_NOSPACE);
}
@@ -1374,8 +1374,9 @@ void cache_found_zone(dhcp_ddns_ns_t *ns_cb)
/* See if there's already such a zone. */
if (dns_zone_lookup(&zone, ns_cb->zname) == ISC_R_SUCCESS) {
/* If it's not a dynamic zone, leave it alone. */
- if (zone->timeout == 0)
- return;
+ if (zone->timeout == 0) {
+ goto cleanup;
+ }
/* Remove any old addresses in case they've changed */
if (zone->primary)
diff --git a/common/execute.c b/common/execute.c
index a33cec85..aad8067d 100644
--- a/common/execute.c
+++ b/common/execute.c
@@ -75,8 +75,10 @@ int execute_statements (result, packet, lease, client_state,
#if defined (DEBUG_EXPRESSIONS)
log_debug ("exec: statements returns %d", status);
#endif
- if (!status)
+ if (!status) {
+ executable_statement_dereference (&r, MDL);
return 0;
+ }
break;
case on_statement:
@@ -147,6 +149,8 @@ int execute_statements (result, packet, lease, client_state,
on_star))) {
executable_statement_dereference
(&e, MDL);
+ executable_statement_dereference
+ (&r, MDL);
return 0;
}
executable_statement_dereference (&e, MDL);
@@ -176,8 +180,10 @@ int execute_statements (result, packet, lease, client_state,
(result, packet, lease, client_state,
in_options, out_options, scope,
rc ? r->data.ie.tc : r->data.ie.fc,
- on_star))
+ on_star)) {
+ executable_statement_dereference (&r, MDL);
return 0;
+ }
break;
case eval_statement:
@@ -298,6 +304,7 @@ int execute_statements (result, packet, lease, client_state,
#if defined (DEBUG_EXPRESSIONS)
log_debug ("exec: break");
#endif
+ executable_statement_dereference (&r, MDL);
return 1;
case supersede_option_statement:
diff --git a/common/ns_name.c b/common/ns_name.c
index be035f6f..f5198219 100644
--- a/common/ns_name.c
+++ b/common/ns_name.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -48,6 +48,39 @@ static int dn_find(const u_char *, const u_char *,
/* Public. */
/*
+ * MRns_name_len(eom, src)
+ * Compute the length of encoded uncompressed domain name.
+ * return:
+ * -1 if it fails, or to be consumed octets if it succeeds.
+ */
+int
+MRns_name_len(const u_char *eom, const u_char *src)
+{
+ const u_char *srcp;
+ unsigned n;
+ int len;
+
+ len = -1;
+ srcp = src;
+ if (srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ /* Fetch next label in domain name. */
+ while ((n = *srcp++) != 0) {
+ /* Limit checks. */
+ if (srcp + n >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ srcp += n;
+ }
+ if (len < 0)
+ len = srcp - src;
+ return (len);
+}
+
+/*
* MRns_name_ntop(src, dst, dstsiz)
* Convert an encoded domain name to printable ascii as per RFC1035.
* return:
diff --git a/common/options.c b/common/options.c
index fc0e0889..405f7049 100644
--- a/common/options.c
+++ b/common/options.c
@@ -35,6 +35,8 @@ struct option *vendor_cfg_option;
static int pretty_text(char **, char *, const unsigned char **,
const unsigned char *, int);
+static int pretty_dname(char **, char *, const unsigned char *,
+ const unsigned char *);
static int pretty_domain(char **, char *, const unsigned char **,
const unsigned char *);
static int prepare_option_buffer(struct universe *universe, struct buffer *bp,
@@ -223,6 +225,7 @@ int parse_option_buffer (options, buffer, length, universe)
log_error("parse_option_buffer: "
"save_option_buffer failed");
buffer_dereference(&bp, MDL);
+ option_dereference(&option, MDL);
return (0);
}
} else if (universe->concat_duplicates) {
@@ -234,6 +237,7 @@ int parse_option_buffer (options, buffer, length, universe)
MDL)) {
log_error("parse_option_buffer: No memory.");
buffer_dereference(&bp, MDL);
+ option_dereference(&option, MDL);
return (0);
}
/* Copy old option to new data object. */
@@ -258,6 +262,7 @@ int parse_option_buffer (options, buffer, length, universe)
if (!option_cache_allocate(&nop, MDL)) {
log_error("parse_option_buffer: No memory.");
buffer_dereference(&bp, MDL);
+ option_dereference(&option, MDL);
return (0);
}
@@ -1403,8 +1408,9 @@ store_options(int *ocount,
(option_space_encapsulate
(&encapsulation, packet, lease, client_state,
in_options, cfg_options, scope, &name));
- data_string_forget (&name, MDL);
}
+
+ data_string_forget (&name, MDL);
}
}
@@ -1624,7 +1630,6 @@ format_has_text(format)
p = format;
while (*p != '\0') {
switch (*p++) {
- case 'd':
case 't':
return 1;
@@ -1638,6 +1643,7 @@ format_has_text(format)
case 'X':
case 'x':
case 'D':
+ case 'd':
return 0;
case 'c':
@@ -1880,8 +1886,23 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
numhunk = -2;
break;
case 'd':
- fmtbuf[l] = 't';
- /* Fall Through ! */
+ /* Should not be optional, array or compressed */
+ if ((option->format[i+1] == 'o') ||
+ (option->format[i+1] == 'a') ||
+ (option->format[i+1] == 'A') ||
+ (option->format[i+1] == 'c')) {
+ log_error("%s: Illegal use of domain name: %s",
+ option->name,
+ &(option->format[i-1]));
+ fmtbuf[l + 1] = 0;
+ }
+ k = MRns_name_len(data + len, data + hunksize);
+ if (k == -1) {
+ log_error("Invalid domain name.");
+ return "<error>";
+ }
+ hunksize += k;
+ break;
case 't':
fmtbuf[l + 1] = 0;
numhunk = -2;
@@ -2035,6 +2056,18 @@ const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
}
*op = 0;
break;
+ case 'd': /* RFC1035 format name */
+ k = MRns_name_len(data + len, dp);
+ /* Already tested... */
+ if (k == -1) {
+ log_error("invalid domain name.");
+ return "<error>";
+ }
+ pretty_dname(&op, endbuf-1, dp, data + len);
+ /* pretty_dname does not add the nul */
+ *op = '\0';
+ dp += k;
+ break;
case 'D': /* RFC1035 format name list */
for( ; dp < (data + len) ; dp += k) {
unsigned char nbuff[NS_MAXCDNAME];
@@ -3434,8 +3467,7 @@ int fqdn_option_space_encapsulate (result, packet, lease, client_state,
}
exit:
for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
- if (results [i].len)
- data_string_forget (&results [i], MDL);
+ data_string_forget (&results[i], MDL);
}
buffer_dereference (&bp, MDL);
if (!status)
@@ -3594,8 +3626,7 @@ fqdn6_option_space_encapsulate(struct data_string *result,
exit:
for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) {
- if (results[i].len)
- data_string_forget(&results[i], MDL);
+ data_string_forget(&results[i], MDL);
}
return rval;
@@ -4284,6 +4315,56 @@ pretty_text(char **dst, char *dend, const unsigned char **src,
}
static int
+pretty_dname(char **dst, char *dend, const unsigned char *src,
+ const unsigned char *send)
+{
+ const unsigned char *tend;
+ const unsigned char *srcp = src;
+ int count = 0;
+ int tsiz, status;
+
+ if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
+ *dst == NULL || ((*dst + 1) > dend) || (src >= send))
+ return -1;
+
+ do {
+ /* Continue loop until end of src buffer. */
+ if (srcp >= send)
+ break;
+
+ /* Consume tag size. */
+ tsiz = *srcp;
+ srcp++;
+
+ /* At root, finis. */
+ if (tsiz == 0)
+ break;
+
+ tend = srcp + tsiz;
+
+ /* If the tag exceeds the source buffer, it's illegal.
+ * This should also trap compression pointers (which should
+ * not be in these buffers).
+ */
+ if (tend > send)
+ return -1;
+
+ /* dend-1 leaves room for a trailing dot and quote. */
+ status = pretty_escape(dst, dend-1, &srcp, tend);
+
+ if ((status == -1) || ((*dst + 1) > dend))
+ return -1;
+
+ **dst = '.';
+ (*dst)++;
+ count += status + 1;
+ }
+ while(1);
+
+ return count;
+}
+
+static int
pretty_domain(char **dst, char *dend, const unsigned char **src,
const unsigned char *send)
{
@@ -4495,8 +4576,10 @@ void parse_vendor_option(packet, lease, client_state, in_options,
in_options, out_options, scope, oc, MDL);
/* No name, all done */
- if (name.len == 0)
+ if (name.len == 0) {
+ data_string_forget(&name, MDL);
return;
+ }
/* Get any vendor option information from the request */
oc = lookup_option(&dhcp_universe, in_options, code);
diff --git a/common/parse.c b/common/parse.c
index c0fa4050..8619f852 100644
--- a/common/parse.c
+++ b/common/parse.c
@@ -5031,15 +5031,13 @@ int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
break;
case 'd': /* Domain name... */
- val = parse_host_name (cfile);
- if (!val) {
- parse_warn (cfile, "not a valid domain name.");
- skip_to_semi (cfile);
+ t = parse_domain_name(cfile);
+ if (!t) {
+ parse_warn(cfile, "not a valid domain name.");
+ skip_to_semi(cfile);
return 0;
}
- len = strlen (val);
- freeval = ISC_TRUE;
- goto make_string;
+ break;
case 't': /* Text string... */
token = next_token (&val, &len, cfile);
@@ -5051,7 +5049,6 @@ int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
}
return 0;
}
- make_string:
if (!make_const_data (&t, (const unsigned char *)val,
len, 1, 1, MDL))
log_fatal ("No memory for concatenation");
@@ -5703,3 +5700,42 @@ parse_domain_list(struct parse *cfile, int compress)
return t;
}
+struct expression *
+parse_domain_name(struct parse *cfile)
+{
+ const char *val;
+ struct expression *t = NULL;
+ unsigned len;
+ int result;
+ unsigned char buf[NS_MAXCDNAME];
+
+ val = parse_host_name(cfile);
+ if (!val) {
+ return NULL;
+ }
+ result = MRns_name_pton(val, buf, sizeof(buf));
+ /* No longer need val */
+ dfree((char *)val, MDL);
+
+ /* result == 1 means the input was fully qualified.
+ * result == 0 means the input wasn't.
+ * result == -1 means bad things.
+ */
+ if (result < 0) {
+ parse_warn(cfile, "Error assembling domain name: %m");
+ return NULL;
+ }
+
+ /* Compute the used length */
+ len = 0;
+ while (buf[len] != 0) {
+ len += buf[len] + 1;
+ }
+ /* Count the last label (0). */
+ len++;
+
+ if (!make_const_data(&t, buf, len, 1, 1, MDL))
+ log_fatal("No memory for domain name object.");
+
+ return t;
+}
diff --git a/common/socket.c b/common/socket.c
index 483eb9c3..90a971a5 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -3,7 +3,7 @@
BSD socket interface code... */
/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -877,7 +877,13 @@ ssize_t send_packet6(struct interface_info *interface,
m.msg_name = &dst;
m.msg_namelen = sizeof(dst);
ifindex = if_nametoindex(interface->name);
+
+// Per OpenBSD patch-common_socket_c,v 1.7 2018/03/06 08:37:39 sthen Exp
+// always set the scope id. We'll leave the test for no global socket
+// in place for all others.
+#ifndef __OpenBSD__
if (no_global_v6_socket)
+#endif
dst.sin6_scope_id = ifindex;
/*
diff --git a/common/tests/Kyuafile b/common/tests/Kyuafile
index f3c352db..4952b549 100644
--- a/common/tests/Kyuafile
+++ b/common/tests/Kyuafile
@@ -3,6 +3,7 @@ test_suite('isc-dhcp')
atf_test_program{name='alloc_unittest'}
atf_test_program{name='dns_unittest'}
+atf_test_program{name='domain_name_unittest'}
atf_test_program{name='misc_unittest'}
atf_test_program{name='ns_name_unittest'}
atf_test_program{name='option_unittest'}
diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am
index 322f77e8..a5152653 100644
--- a/common/tests/Makefile.am
+++ b/common/tests/Makefile.am
@@ -9,7 +9,7 @@ ATF_TESTS =
if HAVE_ATF
ATF_TESTS += alloc_unittest dns_unittest misc_unittest ns_name_unittest \
- option_unittest
+ option_unittest domain_name_unittest
alloc_unittest_SOURCES = test_alloc.c $(top_srcdir)/tests/t_api_dhcp.c
alloc_unittest_LDADD = $(ATF_LDFLAGS)
@@ -51,6 +51,15 @@ option_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \
@BINDLIBISCCFGDIR@/libisccfg.@A@ \
@BINDLIBISCDIR@/libisc.@A@
+domain_name_unittest_SOURCES = domain_name_test.c \
+ $(top_srcdir)/tests/t_api_dhcp.c
+domain_name_unittest_LDADD = $(ATF_LDFLAGS)
+domain_name_unittest_LDADD += ../libdhcp.@A@ ../../omapip/libomapi.@A@ \
+ @BINDLIBIRSDIR@/libirs.@A@ \
+ @BINDLIBDNSDIR@/libdns.@A@ \
+ @BINDLIBISCCFGDIR@/libisccfg.@A@ \
+ @BINDLIBISCDIR@/libisc.@A@
+
check: $(ATF_TESTS)
@if test $(top_srcdir) != ${top_builddir}; then \
cp $(top_srcdir)/common/tests/Atffile Atffile; \
diff --git a/common/tests/Makefile.in b/common/tests/Makefile.in
index 89ca98d7..b0d92063 100644
--- a/common/tests/Makefile.in
+++ b/common/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -88,7 +88,7 @@ POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
@HAVE_ATF_TRUE@am__append_1 = alloc_unittest dns_unittest misc_unittest ns_name_unittest \
-@HAVE_ATF_TRUE@ option_unittest
+@HAVE_ATF_TRUE@ option_unittest domain_name_unittest
check_PROGRAMS = $(am__EXEEXT_2)
subdir = common/tests
@@ -104,7 +104,8 @@ CONFIG_CLEAN_VPATH_FILES =
@HAVE_ATF_TRUE@am__EXEEXT_1 = alloc_unittest$(EXEEXT) \
@HAVE_ATF_TRUE@ dns_unittest$(EXEEXT) misc_unittest$(EXEEXT) \
@HAVE_ATF_TRUE@ ns_name_unittest$(EXEEXT) \
-@HAVE_ATF_TRUE@ option_unittest$(EXEEXT)
+@HAVE_ATF_TRUE@ option_unittest$(EXEEXT) \
+@HAVE_ATF_TRUE@ domain_name_unittest$(EXEEXT)
am__EXEEXT_2 = $(am__EXEEXT_1)
am__alloc_unittest_SOURCES_DIST = test_alloc.c \
$(top_srcdir)/tests/t_api_dhcp.c
@@ -121,6 +122,14 @@ am__dns_unittest_SOURCES_DIST = dns_unittest.c \
dns_unittest_OBJECTS = $(am_dns_unittest_OBJECTS)
@HAVE_ATF_TRUE@dns_unittest_DEPENDENCIES = $(am__DEPENDENCIES_1) \
@HAVE_ATF_TRUE@ ../libdhcp.@A@ ../../omapip/libomapi.@A@
+am__domain_name_unittest_SOURCES_DIST = domain_name_test.c \
+ $(top_srcdir)/tests/t_api_dhcp.c
+@HAVE_ATF_TRUE@am_domain_name_unittest_OBJECTS = \
+@HAVE_ATF_TRUE@ domain_name_test.$(OBJEXT) t_api_dhcp.$(OBJEXT)
+domain_name_unittest_OBJECTS = $(am_domain_name_unittest_OBJECTS)
+@HAVE_ATF_TRUE@domain_name_unittest_DEPENDENCIES = \
+@HAVE_ATF_TRUE@ $(am__DEPENDENCIES_1) ../libdhcp.@A@ \
+@HAVE_ATF_TRUE@ ../../omapip/libomapi.@A@
am__misc_unittest_SOURCES_DIST = misc_unittest.c \
$(top_srcdir)/tests/t_api_dhcp.c
@HAVE_ATF_TRUE@am_misc_unittest_OBJECTS = misc_unittest.$(OBJEXT) \
@@ -156,7 +165,11 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dns_unittest.Po \
+ ./$(DEPDIR)/domain_name_test.Po ./$(DEPDIR)/misc_unittest.Po \
+ ./$(DEPDIR)/ns_name_test.Po ./$(DEPDIR)/option_unittest.Po \
+ ./$(DEPDIR)/t_api_dhcp.Po ./$(DEPDIR)/test_alloc.Po
am__mv = mv -f
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -175,10 +188,11 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(alloc_unittest_SOURCES) $(dns_unittest_SOURCES) \
- $(misc_unittest_SOURCES) $(ns_name_unittest_SOURCES) \
- $(option_unittest_SOURCES)
+ $(domain_name_unittest_SOURCES) $(misc_unittest_SOURCES) \
+ $(ns_name_unittest_SOURCES) $(option_unittest_SOURCES)
DIST_SOURCES = $(am__alloc_unittest_SOURCES_DIST) \
$(am__dns_unittest_SOURCES_DIST) \
+ $(am__domain_name_unittest_SOURCES_DIST) \
$(am__misc_unittest_SOURCES_DIST) \
$(am__ns_name_unittest_SOURCES_DIST) \
$(am__option_unittest_SOURCES_DIST)
@@ -202,7 +216,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
+ distdir distdir-am
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -412,6 +426,15 @@ ATF_TESTS = $(am__append_1)
@HAVE_ATF_TRUE@ @BINDLIBDNSDIR@/libdns.@A@ \
@HAVE_ATF_TRUE@ @BINDLIBISCCFGDIR@/libisccfg.@A@ \
@HAVE_ATF_TRUE@ @BINDLIBISCDIR@/libisc.@A@
+@HAVE_ATF_TRUE@domain_name_unittest_SOURCES = domain_name_test.c \
+@HAVE_ATF_TRUE@ $(top_srcdir)/tests/t_api_dhcp.c
+
+@HAVE_ATF_TRUE@domain_name_unittest_LDADD = $(ATF_LDFLAGS) \
+@HAVE_ATF_TRUE@ ../libdhcp.@A@ ../../omapip/libomapi.@A@ \
+@HAVE_ATF_TRUE@ @BINDLIBIRSDIR@/libirs.@A@ \
+@HAVE_ATF_TRUE@ @BINDLIBDNSDIR@/libdns.@A@ \
+@HAVE_ATF_TRUE@ @BINDLIBISCCFGDIR@/libisccfg.@A@ \
+@HAVE_ATF_TRUE@ @BINDLIBISCDIR@/libisc.@A@
all: all-recursive
.SUFFIXES:
@@ -433,8 +456,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -457,6 +480,10 @@ dns_unittest$(EXEEXT): $(dns_unittest_OBJECTS) $(dns_unittest_DEPENDENCIES) $(EX
@rm -f dns_unittest$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(dns_unittest_OBJECTS) $(dns_unittest_LDADD) $(LIBS)
+domain_name_unittest$(EXEEXT): $(domain_name_unittest_OBJECTS) $(domain_name_unittest_DEPENDENCIES) $(EXTRA_domain_name_unittest_DEPENDENCIES)
+ @rm -f domain_name_unittest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(domain_name_unittest_OBJECTS) $(domain_name_unittest_LDADD) $(LIBS)
+
misc_unittest$(EXEEXT): $(misc_unittest_OBJECTS) $(misc_unittest_DEPENDENCIES) $(EXTRA_misc_unittest_DEPENDENCIES)
@rm -f misc_unittest$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(misc_unittest_OBJECTS) $(misc_unittest_LDADD) $(LIBS)
@@ -475,12 +502,19 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_name_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/option_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_alloc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/domain_name_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_name_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/option_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_alloc.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -609,7 +643,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -706,7 +743,13 @@ clean: clean-recursive
clean-am: clean-checkPROGRAMS clean-generic mostlyclean-am
distclean: distclean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/dns_unittest.Po
+ -rm -f ./$(DEPDIR)/domain_name_test.Po
+ -rm -f ./$(DEPDIR)/misc_unittest.Po
+ -rm -f ./$(DEPDIR)/ns_name_test.Po
+ -rm -f ./$(DEPDIR)/option_unittest.Po
+ -rm -f ./$(DEPDIR)/t_api_dhcp.Po
+ -rm -f ./$(DEPDIR)/test_alloc.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-local distclean-tags
@@ -752,7 +795,13 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/dns_unittest.Po
+ -rm -f ./$(DEPDIR)/domain_name_test.Po
+ -rm -f ./$(DEPDIR)/misc_unittest.Po
+ -rm -f ./$(DEPDIR)/ns_name_test.Po
+ -rm -f ./$(DEPDIR)/option_unittest.Po
+ -rm -f ./$(DEPDIR)/t_api_dhcp.Po
+ -rm -f ./$(DEPDIR)/test_alloc.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -772,19 +821,19 @@ uninstall-am:
.MAKE: $(am__recursive_targets) check-am install-am install-strip
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-checkPROGRAMS clean-generic cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-local distclean-tags distdir dvi dvi-am html html-am \
- info info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-checkPROGRAMS \
+ clean-generic cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-local \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
diff --git a/common/tests/domain_name_test.c b/common/tests/domain_name_test.c
new file mode 100644
index 00000000..8bd70aa2
--- /dev/null
+++ b/common/tests/domain_name_test.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * 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 <config.h>
+#include <atf-c.h>
+#include "dhcpd.h"
+
+ATF_TC(pretty_print_domain_name);
+
+ATF_TC_HEAD(pretty_print_domain_name, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify pretty_print_option can render a domain name.");
+}
+
+/*
+ * This test verifies that pretty_print_option() correctly render a
+ * domain name.
+ *
+ */
+ATF_TC_BODY(pretty_print_domain_name, tc)
+{
+ struct option *option;
+ unsigned code;
+ unsigned char good_data[] =
+ { 0x05, 0x62, 0x6f, 0x6f, 0x79, 0x61, 0x03, 0x63, 0x6f, 0x6d, 0x00 };
+ unsigned char short_data[] =
+ { 0x05, 0x62, 0x6f, 0x6f, 0x79, 0x61, 0x03, 0x63, 0x6f, 0x6d };
+ unsigned char long_data[] =
+ { 0x05, 0x62, 0x6f, 0x6f, 0x79, 0x61, 0x03, 0x63, 0x6f, 0x6d, 0x00,
+ 0x01, 0x02 };
+ const char *output_buf;
+
+ initialize_common_option_spaces();
+
+ /* We'll use v4-lost because it happens to be format d */
+ code = 137;
+ option = NULL;
+ if (!option_code_hash_lookup(&option, dhcp_universe.code_hash,
+ &code, 0, MDL)) {
+ atf_tc_fail("can't find option %d", code);
+ }
+
+ if (option == NULL) {
+ atf_tc_fail("option is NULL");
+ }
+
+ /* First we will try a good value we know should fit. */
+ output_buf = pretty_print_option(option, good_data, sizeof(good_data), 0,
+ 0);
+
+ /* Make sure we get what we expect */
+ if (!output_buf || strcmp(output_buf, "booya.com.")) {
+ atf_tc_fail("pretty_print_option did not return 'booya.com.'");
+ }
+
+ fprintf(stderr, "good!\n");
+
+ /* Now we'll try a data value that's too short */
+ output_buf = pretty_print_option(option, short_data, sizeof(short_data),
+ 0, 0);
+
+ /* Make sure we safely get an error */
+ if (!output_buf || strcmp(output_buf, "<error>")) {
+ atf_tc_fail("pretty_print_option did not return \"<error>\"");
+ }
+
+ /* Now we'll try a data value that's too large */
+ output_buf = pretty_print_option(option, long_data, sizeof(long_data), 0,
+ 0);
+
+ /* This logs but does not return an error */
+ if (!output_buf || strcmp(output_buf, "booya.com.")) {
+ atf_tc_fail("pretty_print_option did not return 'booya.com.' (large)");
+ }
+}
+
+
+/* This macro defines main() method that will call specified
+ test cases. tp and simple_test_case names can be whatever you want
+ as long as it is a valid variable identifier. */
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, pretty_print_domain_name);
+
+ return (atf_no_error());
+}
diff --git a/configure b/configure
index ddb4695f..1c0302d7 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for DHCP 4.4.1.
+# Generated by GNU Autoconf 2.69 for DHCP 4.4.2-dev.
#
# Report bugs to <dhcp-users@isc.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='DHCP'
PACKAGE_TARNAME='dhcp'
-PACKAGE_VERSION='4.4.1'
-PACKAGE_STRING='DHCP 4.4.1'
+PACKAGE_VERSION='4.4.2-dev'
+PACKAGE_STRING='DHCP 4.4.2-dev'
PACKAGE_BUGREPORT='dhcp-users@isc.org'
PACKAGE_URL=''
@@ -670,7 +670,6 @@ am__nodep
AMDEPBACKSLASH
AMDEP_FALSE
AMDEP_TRUE
-am__quote
am__include
DEPDIR
OBJEXT
@@ -755,7 +754,8 @@ PACKAGE_VERSION
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
-SHELL'
+SHELL
+am__quote'
ac_subst_files=''
ac_user_opts='
enable_option_checking
@@ -1350,7 +1350,7 @@ 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 DHCP 4.4.1 to adapt to many kinds of systems.
+\`configure' configures DHCP 4.4.2-dev to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1420,7 +1420,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of DHCP 4.4.1:";;
+ short | recursive ) echo "Configuration of DHCP 4.4.2-dev:";;
esac
cat <<\_ACEOF
@@ -1591,7 +1591,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-DHCP configure 4.4.1
+DHCP configure 4.4.2-dev
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2384,7 +2384,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by DHCP $as_me 4.4.1, which was
+It was created by DHCP $as_me 4.4.2-dev, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2736,7 +2736,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
# we specify "foreign" to avoid having to have the GNU mandated files,
# like AUTHORS, COPYING, and such
-am__api_version='1.15'
+am__api_version='1.16'
ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
@@ -3251,7 +3251,7 @@ fi
# Define the identity of the package.
PACKAGE='dhcp'
- VERSION='4.4.1'
+ VERSION='4.4.2-dev'
cat >>confdefs.h <<_ACEOF
@@ -3281,8 +3281,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
mkdir_p='$(MKDIR_P)'
# We need awk for the "check" target (and possibly the TAP driver). The
@@ -3333,7 +3333,7 @@ END
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
@@ -4303,45 +4303,45 @@ DEPDIR="${am__leading_dot}deps"
ac_config_commands="$ac_config_commands depfiles"
-
-am_make=${MAKE-make}
-cat > confinc << 'END'
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
am__doit:
- @echo this is the am__doit target
+ @echo this is the am__doit target >confinc.out
.PHONY: am__doit
END
-# If we don't find an include directive, just comment out the code.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
-$as_echo_n "checking for style of include used by $am_make... " >&6; }
am__include="#"
am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# Ignore all kinds of additional output from 'make'.
-case `$am_make -s -f confmf 2> /dev/null` in #(
-*the\ am__doit\ target*)
- am__include=include
- am__quote=
- _am_result=GNU
- ;;
-esac
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
- echo '.include "confinc"' > confmf
- case `$am_make -s -f confmf 2> /dev/null` in #(
- *the\ am__doit\ target*)
- am__include=.include
- am__quote="\""
- _am_result=BSD
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+ { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+ (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+ case $?:`cat confinc.out 2>/dev/null` in #(
+ '0:this is the am__doit target') :
+ case $s in #(
+ BSD) :
+ am__include='.include' am__quote='"' ;; #(
+ *) :
+ am__include='include' am__quote='' ;;
+esac ;; #(
+ *) :
;;
- esac
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
-$as_echo "$_am_result" >&6; }
-rm -f confinc confmf
+esac
+ if test "$am__include" != "#"; then
+ _am_result="yes ($s style)"
+ break
+ fi
+done
+rm -f confinc.* confmf.*
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+$as_echo "${_am_result}" >&6; }
# Check whether --enable-dependency-tracking was given.
if test "${enable_dependency_tracking+set}" = set; then :
@@ -7549,7 +7549,7 @@ $as_echo "#define FLEXIBLE_ARRAY_MEMBER /**/" >>confdefs.h
fi
-ac_config_files="$ac_config_files Makefile client/Makefile client/tests/Makefile common/Makefile.am common/Makefile common/tests/Makefile dhcpctl/Makefile.am dhcpctl/Makefile includes/Makefile omapip/Makefile.am omapip/Makefile relay/Makefile server/Makefile tests/Makefile.am tests/Makefile tests/unittest.sh server/tests/Makefile doc/devel/doxyfile"
+ac_config_files="$ac_config_files Makefile client/Makefile client/tests/Makefile common/Makefile.am common/Makefile common/tests/Makefile dhcpctl/Makefile.am dhcpctl/Makefile includes/Makefile keama/Makefile omapip/Makefile.am omapip/Makefile relay/Makefile server/Makefile tests/Makefile.am tests/Makefile tests/unittest.sh server/tests/Makefile doc/devel/doxyfile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -8102,7 +8102,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by DHCP $as_me 4.4.1, which was
+This file was extended by DHCP $as_me 4.4.2-dev, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -8168,7 +8168,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-DHCP config.status 4.4.1
+DHCP config.status 4.4.2-dev
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -8287,7 +8287,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
-AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
_ACEOF
@@ -8309,6 +8309,7 @@ do
"dhcpctl/Makefile.am") CONFIG_FILES="$CONFIG_FILES dhcpctl/Makefile.am" ;;
"dhcpctl/Makefile") CONFIG_FILES="$CONFIG_FILES dhcpctl/Makefile" ;;
"includes/Makefile") CONFIG_FILES="$CONFIG_FILES includes/Makefile" ;;
+ "keama/Makefile") CONFIG_FILES="$CONFIG_FILES keama/Makefile" ;;
"omapip/Makefile.am") CONFIG_FILES="$CONFIG_FILES omapip/Makefile.am" ;;
"omapip/Makefile") CONFIG_FILES="$CONFIG_FILES omapip/Makefile" ;;
"relay/Makefile") CONFIG_FILES="$CONFIG_FILES relay/Makefile" ;;
@@ -8878,29 +8879,35 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
- case $CONFIG_FILES in
- *\'*) eval set x "$CONFIG_FILES" ;;
- *) set x $CONFIG_FILES ;;
- esac
+ # TODO: see whether this extra hack can be removed once we start
+ # requiring Autoconf 2.70 or later.
+ case $CONFIG_FILES in #(
+ *\'*) :
+ eval set x "$CONFIG_FILES" ;; #(
+ *) :
+ set x $CONFIG_FILES ;; #(
+ *) :
+ ;;
+esac
shift
- for mf
+ # Used to flag and report bootstrapping failures.
+ am_rc=0
+ for am_mf
do
# Strip MF so we end up with the name of the file.
- mf=`echo "$mf" | sed -e 's/:.*$//'`
- # Check whether this is an Automake generated Makefile or not.
- # We used to match only the files named 'Makefile.in', but
- # some people rename them; so instead we look at the file content.
- # Grep'ing the first line is not enough: some people post-process
- # each Makefile.in and add a new line on top of each file to say so.
- # Grep'ing the whole file is not good either: AIX grep has a line
+ am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile which includes
+ # dependency-tracking related rules and includes.
+ # Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
- if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
- dirpart=`$as_dirname -- "$mf" ||
-$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$mf" : 'X\(//\)[^/]' \| \
- X"$mf" : 'X\(//\)$' \| \
- X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$mf" |
+ sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+ || continue
+ am_dirpart=`$as_dirname -- "$am_mf" ||
+$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$am_mf" : 'X\(//\)[^/]' \| \
+ X"$am_mf" : 'X\(//\)$' \| \
+ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$am_mf" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -8918,53 +8925,48 @@ $as_echo X"$mf" |
q
}
s/.*/./; q'`
- else
- continue
- fi
- # Extract the definition of DEPDIR, am__include, and am__quote
- # from the Makefile without running 'make'.
- DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
- test -z "$DEPDIR" && continue
- am__include=`sed -n 's/^am__include = //p' < "$mf"`
- test -z "$am__include" && continue
- am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
- # Find all dependency output files, they are included files with
- # $(DEPDIR) in their names. We invoke sed twice because it is the
- # simplest approach to changing $(DEPDIR) to its actual value in the
- # expansion.
- for file in `sed -n "
- s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
- # Make sure the directory exists.
- test -f "$dirpart/$file" && continue
- fdir=`$as_dirname -- "$file" ||
-$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$file" : 'X\(//\)[^/]' \| \
- X"$file" : 'X\(//\)$' \| \
- X"$file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$file" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
+ am_filepart=`$as_basename -- "$am_mf" ||
+$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$am_mf" : 'X\(//\)$' \| \
+ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$am_mf" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
- /^X\(\/\/\)$/{
+ /^X\/\(\/\/\)$/{
s//\1/
q
}
- /^X\(\/\).*/{
+ /^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
- as_dir=$dirpart/$fdir; as_fn_mkdir_p
- # echo "creating $dirpart/$file"
- echo '# dummy' > "$dirpart/$file"
- done
+ { echo "$as_me:$LINENO: cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles" >&5
+ (cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } || am_rc=$?
done
+ if test $am_rc -ne 0; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Something went wrong bootstrapping makefile fragments
+ for automatic dependency tracking. Try re-running configure with the
+ '--disable-dependency-tracking' option to at least be able to build
+ the package (albeit without support for automatic dependency tracking).
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ { am_dirpart=; unset am_dirpart;}
+ { am_filepart=; unset am_filepart;}
+ { am_mf=; unset am_mf;}
+ { am_rc=; unset am_rc;}
+ rm -f conftest-deps.mk
}
;;
diff --git a/configure.ac b/configure.ac
index 6ed9a948..dcca3894 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1009,6 +1009,7 @@ AC_CONFIG_FILES([
dhcpctl/Makefile.am
dhcpctl/Makefile
includes/Makefile
+ keama/Makefile
omapip/Makefile.am
omapip/Makefile
relay/Makefile
diff --git a/configure.ac+lt b/configure.ac+lt
index bfa672af..2e00862d 100644
--- a/configure.ac+lt
+++ b/configure.ac+lt
@@ -1,4 +1,4 @@
-AC_INIT([DHCP],[4.4.1],[dhcp-users@isc.org])
+AC_INIT([DHCP],[4.4.2-dev],[dhcp-users@isc.org])
# for libtool
AC_CONFIG_MACRO_DIR([m4])
@@ -1010,6 +1010,7 @@ AC_CONFIG_FILES([
dhcpctl/Makefile.am
dhcpctl/Makefile
includes/Makefile
+ keama/Makefile
omapip/Makefile.am
omapip/Makefile
relay/Makefile
diff --git a/configure.ac-base b/configure.ac-base
index 880894ce..f570c6f5 100644
--- a/configure.ac-base
+++ b/configure.ac-base
@@ -1,4 +1,4 @@
-AC_INIT([DHCP],[4.4.1],[dhcp-users@isc.org])
+AC_INIT([DHCP],[4.4.2-dev],[dhcp-users@isc.org])
@BEGIN WITH LIBTOOL
# for libtool
@@ -1044,6 +1044,7 @@ AC_CONFIG_FILES([
dhcpctl/Makefile.am
dhcpctl/Makefile
includes/Makefile
+ keama/Makefile
omapip/Makefile.am
omapip/Makefile
relay/Makefile
diff --git a/configure.ac-lt b/configure.ac-lt
index a7974385..dcca3894 100644
--- a/configure.ac-lt
+++ b/configure.ac-lt
@@ -1,4 +1,4 @@
-AC_INIT([DHCP],[4.4.1],[dhcp-users@isc.org])
+AC_INIT([DHCP],[4.4.2-dev],[dhcp-users@isc.org])
# we specify "foreign" to avoid having to have the GNU mandated files,
@@ -1009,6 +1009,7 @@ AC_CONFIG_FILES([
dhcpctl/Makefile.am
dhcpctl/Makefile
includes/Makefile
+ keama/Makefile
omapip/Makefile.am
omapip/Makefile
relay/Makefile
diff --git a/contrib/dhcp-lease-list.pl b/contrib/dhcp-lease-list.pl
index aa6372df..3a87516c 100644
--- a/contrib/dhcp-lease-list.pl
+++ b/contrib/dhcp-lease-list.pl
@@ -20,6 +20,8 @@
#
# 2016-01-18 - Mainly cosmetics. Eliminated spurious output in "parsable" mode.
# Provided for the various conventional lease file locations. (cbp)
+#
+# 2019-06-20 - Updated OUI_URL location. -TS
use strict;
use warnings;
@@ -30,7 +32,7 @@ my @all_leases;
my @leases;
my @OUIS = ('/usr/share/misc/oui.txt', '/usr/local/etc/oui.txt');
-my $OUI_URL = 'http://standards.ieee.org/regauth/oui/oui.txt';
+my $OUI_URL = 'http://standards-oui.ieee.org/oui.txt';
my $oui;
my %data;
diff --git a/dhcpctl/Makefile.in b/dhcpctl/Makefile.in
index 201df62c..3c8232d1 100644
--- a/dhcpctl/Makefile.in
+++ b/dhcpctl/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -101,6 +101,9 @@ mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/includes/config.h
CONFIG_CLEAN_FILES = Makefile.am
CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)"
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
@@ -128,8 +131,6 @@ am__uninstall_files_from_dir = { \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
-am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
- "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)"
LIBRARIES = $(lib_LIBRARIES)
ARFLAGS = cru
AM_V_AR = $(am__v_AR_@AM_V@)
@@ -141,7 +142,6 @@ libdhcpctl_a_LIBADD =
am_libdhcpctl_a_OBJECTS = dhcpctl.$(OBJEXT) callback.$(OBJEXT) \
remote.$(OBJEXT)
libdhcpctl_a_OBJECTS = $(am_libdhcpctl_a_OBJECTS)
-PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
am_cltest_OBJECTS = cltest.$(OBJEXT)
cltest_OBJECTS = $(am_cltest_OBJECTS)
cltest_DEPENDENCIES = libdhcpctl.a ../common/libdhcp.a \
@@ -168,7 +168,10 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/callback.Po ./$(DEPDIR)/cltest.Po \
+ ./$(DEPDIR)/dhcpctl.Po ./$(DEPDIR)/omshell.Po \
+ ./$(DEPDIR)/remote.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -378,8 +381,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -392,42 +395,6 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
Makefile.am: $(top_builddir)/config.status $(srcdir)/Makefile.am.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
-install-libLIBRARIES: $(lib_LIBRARIES)
- @$(NORMAL_INSTALL)
- @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
- list2=; for p in $$list; do \
- if test -f $$p; then \
- list2="$$list2 $$p"; \
- else :; fi; \
- done; \
- test -z "$$list2" || { \
- echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
- $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
- echo " $(INSTALL_DATA) $$list2 '$(DESTDIR)$(libdir)'"; \
- $(INSTALL_DATA) $$list2 "$(DESTDIR)$(libdir)" || exit $$?; }
- @$(POST_INSTALL)
- @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
- for p in $$list; do \
- if test -f $$p; then \
- $(am__strip_dir) \
- echo " ( cd '$(DESTDIR)$(libdir)' && $(RANLIB) $$f )"; \
- ( cd "$(DESTDIR)$(libdir)" && $(RANLIB) $$f ) || exit $$?; \
- else :; fi; \
- done
-
-uninstall-libLIBRARIES:
- @$(NORMAL_UNINSTALL)
- @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- dir='$(DESTDIR)$(libdir)'; $(am__uninstall_files_from_dir)
-
-clean-libLIBRARIES:
- -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
-
-libdhcpctl.a: $(libdhcpctl_a_OBJECTS) $(libdhcpctl_a_DEPENDENCIES) $(EXTRA_libdhcpctl_a_DEPENDENCIES)
- $(AM_V_at)-rm -f libdhcpctl.a
- $(AM_V_AR)$(libdhcpctl_a_AR) libdhcpctl.a $(libdhcpctl_a_OBJECTS) $(libdhcpctl_a_LIBADD)
- $(AM_V_at)$(RANLIB) libdhcpctl.a
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
@@ -473,6 +440,42 @@ clean-binPROGRAMS:
clean-noinstPROGRAMS:
-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+install-libLIBRARIES: $(lib_LIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(INSTALL_DATA) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(INSTALL_DATA) $$list2 "$(DESTDIR)$(libdir)" || exit $$?; }
+ @$(POST_INSTALL)
+ @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ if test -f $$p; then \
+ $(am__strip_dir) \
+ echo " ( cd '$(DESTDIR)$(libdir)' && $(RANLIB) $$f )"; \
+ ( cd "$(DESTDIR)$(libdir)" && $(RANLIB) $$f ) || exit $$?; \
+ else :; fi; \
+ done
+
+uninstall-libLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libdir)'; $(am__uninstall_files_from_dir)
+
+clean-libLIBRARIES:
+ -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
+
+libdhcpctl.a: $(libdhcpctl_a_OBJECTS) $(libdhcpctl_a_DEPENDENCIES) $(EXTRA_libdhcpctl_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libdhcpctl.a
+ $(AM_V_AR)$(libdhcpctl_a_AR) libdhcpctl.a $(libdhcpctl_a_OBJECTS) $(libdhcpctl_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libdhcpctl.a
cltest$(EXEEXT): $(cltest_OBJECTS) $(cltest_DEPENDENCIES) $(EXTRA_cltest_DEPENDENCIES)
@rm -f cltest$(EXEEXT)
@@ -488,11 +491,17 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cltest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpctl.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/omshell.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cltest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpctl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/omshell.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -646,7 +655,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -678,9 +690,9 @@ distdir: $(DISTFILES)
done
check-am: all-am
check: check-am
-all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(MANS)
+all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(MANS)
installdirs:
- for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)"; do \
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
@@ -719,7 +731,11 @@ clean-am: clean-binPROGRAMS clean-generic clean-libLIBRARIES \
clean-noinstPROGRAMS mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/callback.Po
+ -rm -f ./$(DEPDIR)/cltest.Po
+ -rm -f ./$(DEPDIR)/dhcpctl.Po
+ -rm -f ./$(DEPDIR)/omshell.Po
+ -rm -f ./$(DEPDIR)/remote.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -765,7 +781,11 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/callback.Po
+ -rm -f ./$(DEPDIR)/cltest.Po
+ -rm -f ./$(DEPDIR)/dhcpctl.Po
+ -rm -f ./$(DEPDIR)/omshell.Po
+ -rm -f ./$(DEPDIR)/remote.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -788,7 +808,7 @@ uninstall-man: uninstall-man1 uninstall-man3
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-binPROGRAMS clean-generic clean-libLIBRARIES \
clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
distclean-compile distclean-generic distclean-tags distdir dvi \
diff --git a/includes/Makefile.in b/includes/Makefile.in
index 3bfe6a80..09125f9e 100644
--- a/includes/Makefile.in
+++ b/includes/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -324,8 +324,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -428,7 +428,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
diff --git a/includes/cf/aix.h b/includes/cf/aix.h
deleted file mode 100644
index 67b05f62..00000000
--- a/includes/cf/aix.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* aix.h
-
- Configuration file for IBM's AIX operating system. */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#define int8_t char
-#define int16_t short
-#define int32_t long
-
-#define u_int8_t unsigned char
-#define u_int16_t unsigned short
-#define u_int32_t unsigned long
-
-#include <sys/types.h>
-
-#include <syslog.h>
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/select.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#if !defined (h_errno) /* It's a macro on newer instances of AIX. */
-extern int h_errno;
-#endif
-
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/if_dl.h>
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list)
-#define va_dcl
-
-/* The vsnprint function definition in /usr/include/ appears to use this
- * as a key for whether or not it should be declared. Seems reasoanble for
- * us to use the same key.
- */
-#if (_XOPEN_SOURCE != 500)
-#define NO_SNPRINTF
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-#include <time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define random rand
-
-#define USE_SOCKETS 1
-#define HAVE_SA_LEN 1
-#undef FDDI
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -ef 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/uptime 2>&1",
- "/usr/bin/printenv 2>&1",
- "/usr/bin/netstat -s 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/spool/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/var/adm/wtmp",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/alphaosf.h b/includes/cf/alphaosf.h
deleted file mode 100644
index f2689369..00000000
--- a/includes/cf/alphaosf.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/* alphaosf.h
-
- System dependencies for DEC Alpha/OSF1... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <malloc.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-
-/* Define the basic integer types... */
-#if !defined (__BIT_TYPES_DEFINED__)
-typedef char int8_t;
-typedef short int16_t;
-typedef int int32_t;
-
-typedef unsigned char u_int8_t;
-typedef unsigned short u_int16_t;
-typedef unsigned int u_int32_t;
-typedef unsigned long u_int64_t;
-#endif
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#undef va_dcl
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-/* Our reports say that OSF/1 versions 4.0G and later have v/snprintf
- * implementations. 4.0F and previous do not. This is detected at
- * ./configure time from 'sizer -v'.
- */
-#ifndef HAVE_SNPRINTF
-# define NO_SNPRINTF
-#endif
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/var/run/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-/* The jmp_buf type is an array on OSF/1, so we can't dereference it
- and must declare it differently. */
-#define jbp_decl(x) jmp_buf x
-#define jref(x) (x)
-#define jdref(x) (x)
-#define jrefproto jmp_buf
-
-#define NEED_OSF_PFILT_HACKS
-#define BPF_FORMAT "/dev/pf/pfilt%d"
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_BPF
-# define DEC_FDDI
-# define FDDI_HEADER_SIZE 14
-#endif
-
-#define PTRSIZE_64BIT
-
-#define SOCKLEN_T int
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -ef 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/bin/iostat 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/spool/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/var/adm/messages",
- "/var/adm/wtmp",
- "/var/adm/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/bsdos.h b/includes/cf/bsdos.h
deleted file mode 100644
index f42ef797..00000000
--- a/includes/cf/bsdos.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/* bsdos.h
-
- System dependencies for BSDI BSD/OS... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#define INADDR_LOOPBACK ((u_int32_t)0x7f000001)
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/var/run/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/var/db/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/var/db/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define HAVE_SA_LEN
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_BPF
-#endif
-
-#if _BSDI_VERSION < 199802
-typedef int socklen_t;
-#endif
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/usr/sbin/iostat 2>&1",
- "/usr/sbin/vmstat 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/dev",
- "/var/mail",
- "/usr/home",
- NULL
-};
-
-const char *files[] = {
- "/var/log/messages",
- "/var/log/wtmp",
- "/var/log/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/cygwin32.h b/includes/cf/cygwin32.h
deleted file mode 100644
index 17aef735..00000000
--- a/includes/cf/cygwin32.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* cygwin32.h
-
- System dependencies for Win32, compiled with Cygwin32... This
- doesn't work yet, so don't get too excited! */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <sys/time.h>
-
-#define IN
-#define OUT
-#undef fd_set
-#undef FD_SET
-#undef FD_CLR
-#undef FD_ZERO
-#undef FD_ISSET
-#undef FD_ISCLR
-#undef FD_SETSIZE
-#define IFNAMSIZ 16
-#include <winsock.h>
-
-#include <syslog.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-#define NO_H_ERRNO
-
-#include <sys/param.h>
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-/* XXX: System is not believed to have vsnprintf. Someone please verify. */
-#define NO_SNPRINTF
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "//e/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "//e/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "//e/etc/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "//e/etc/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCPD_CONF
-#define _PATH_DHCPD_CONF "//e/etc/dhcpd.conf"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "//e/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "//e/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "//e/etc/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "//e/etc/dhclient6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_CONF
-#define _PATH_DHCLIENT_CONF "//e/etc/dhclient.conf"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "//e/etc/dhcrelay.pid"
-#endif
-
-#ifndef _PATH_RESOLV_CONF
-#define _PATH_RESOLV_CONF "//e/etc/resolv.conf"
-#endif
-
-#define int8_t char
-#define int16_t short
-#define int32_t long
-
-#define u_int8_t unsigned char /* Not quite POSIX... */
-#define u_int16_t unsigned short
-#define u_int32_t unsigned long
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_SOCKETS
-#endif
-
-#ifdef __alpha__
-#define PTRSIZE_64BIT
-#endif
diff --git a/includes/cf/freebsd.h b/includes/cf/freebsd.h
deleted file mode 100644
index cd672ecb..00000000
--- a/includes/cf/freebsd.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/* freebsd.h
-
- System dependencies for FreeBSD... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/if_arp.h>
-#if !defined (INADDR_LOOPBACK)
-# define INADDR_LOOPBACK ((u_int32_t)0x7f000001)
-#endif
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-#if defined(__alpha__) || defined(__amd64__) || defined(__ia64__) || \
- defined(__sparc64__)
-# define PTRSIZE_64BIT
-#endif
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/var/run/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/var/db/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/var/db/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define HAVE_SA_LEN
-
-/* socklen_t was first defined on November 24 in sys/socket.h, and
- __FreeBSD_version was changed to 400013 on December 4, so if you
- get a compile error on this, and you updated between those dates,
- that's why. Also, it may be that some 3.x version after 3.4 will
- have socklen_t, but no such change has been made so far. */
-
-#if __FreeBSD_version < 400013
-#define SOCKLEN_T int
-#endif
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_BPF
-#endif
-#define HAVE_MKSTEMP
-#ifdef NEED_PRAND_CONF
-#ifndef HAVE_DEV_RANDOM
- # define HAVE_DEV_RANDOM 1
- #endif /* HAVE_DEV_RANDOM */
-
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/usr/bin/dig . soa +ti=1 +retry=0 2>&1",
- "/usr/sbin/iostat 2>&1",
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/usr/tmp",
- ".",
- "/",
- "/var/spool",
- "/dev",
- "/var/mail",
- "/home",
- "/usr/home",
- NULL
-};
-
-const char *files[] = {
- "/var/log/messages",
- "/var/log/wtmp",
- "/var/log/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/hpux.h b/includes/cf/hpux.h
deleted file mode 100644
index 0246779d..00000000
--- a/includes/cf/hpux.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* hpux.h
-
- Configuration file for HP-UX */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#define int8_t char
-#define int16_t short
-#define int32_t long
-
-#define u_int8_t unsigned char
-#define u_int16_t unsigned short
-#define u_int32_t unsigned long
-
-#include <sys/types.h>
-
-#include <syslog.h>
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_arp.h>
-
-#define htons(x) (x) /* sigh */
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#if !defined (__ANSI__)
-/* Varargs stuff: use stdarg.h instead ... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-#else
-#include <varargs.h>
-#define VA_DOTDOTDOT va_alist
-#define VA_start(list, last) va_start (list)
-#endif
-
-#define USE_SOCKETS 1
-#define EOL '\n'
-#define VOIDPTR void *
-
-#include <time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define random rand
-
-#define BYTE_ORDER BIG_ENDIAN
-#define BIG_ENDIAN 1
-
-#define SOCKLEN_T int
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/usr/bin/ps -ef 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/usr/bin/df 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/var/adm/wtmp",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
-
-/* these are #defined somewhere in /usr/include headers. */
-#undef NO
-#undef SERVER
-
diff --git a/includes/cf/irix.h b/includes/cf/irix.h
deleted file mode 100644
index 4a3eb8a6..00000000
--- a/includes/cf/irix.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* irix.h */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- */
-
-#define int8_t char
-#define int16_t short
-#define int32_t long
-
-#define u_int8_t unsigned char
-#define u_int16_t unsigned short
-#define u_int32_t unsigned long
-
-#include <sys/types.h>
-
-#include <syslog.h>
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-#include <net/if_dl.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_arp.h>
-
-#define _PATH_DHCPD_CONF "/usr/local/etc/dhcpd.conf"
-
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/usr/local/etc/dhcp/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/usr/local/etc/dhcp/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-
-/* XXX: System is not believed to have snprintf/vsnprintf. Please verify. */
-#define NO_SNPRINTF
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_RAW_SOCKETS
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-#include <time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define random rand
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -ef 2>&1",
- "/usr/etc/arp -a 2>&1",
- "/usr/etc/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 2>&1",
- "/usr/bsd/uptime 2>&1",
- "/usr/bin/printenv 2>&1",
- "/usr/etc/netstat -s 2>&1",
- "/usr/bin/dig . soa +ti=1 2>&1",
- "/usr/bsd/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/mail",
- NULL
-};
-
-const char *files[] = {
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/linux.h b/includes/cf/linux.h
deleted file mode 100644
index 57344e85..00000000
--- a/includes/cf/linux.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/* linux.h
-
- System dependencies for Linux.
-
- Based on a configuration originally supplied by Jonathan Stone. */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <features.h>
-#ifndef __BIT_TYPES_DEFINED__
-#define __BIT_TYPES_DEFINED__
-#undef __USE_BSD
-typedef char int8_t;
-typedef short int16_t;
-typedef long int32_t;
-
-typedef unsigned char u_int8_t;
-typedef unsigned short u_int16_t;
-typedef unsigned long u_int32_t;
-#endif /* __BIT_TYPES_DEFINED__ */
-
-typedef u_int8_t u8;
-typedef u_int16_t u16;
-typedef u_int32_t u32;
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/route.h>
-
-#if LINUX_MAJOR == 1
-# include <linux/if_arp.h>
-# include <linux/time.h> /* also necessary */
-#else
-# include <net/if_arp.h>
-#endif
-
-#include <sys/time.h> /* gettimeofday()*/
-
-/* Databases go in /var/state/dhcp. It would also be valid to put them
- in /var/state/misc - indeed, given that there's only one lease file, it
- would probably be better. However, I have some ideas for optimizing
- the lease database that may result in a _lot_ of smaller files being
- created, so in that context it makes more sense to have a separate
- directory. */
-
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/var/state/dhcp/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/var/state/dhcp/dhcpd6.leases"
-#endif
-
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/var/state/dhcp/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/var/state/dhcp/dhclient6.leases"
-#endif
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-
-#define VOIDPTR void *
-
-#if defined(__alpha__) || defined(__amd64__) || defined(__ia64__) || \
- defined(__sparc64__)
-# define PTRSIZE_64BIT
-#endif
-
-#define EOL '\n'
-
-/* Time stuff... */
-
-#include <time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#if (LINUX_MAJOR >= 2)
-# if ((LINUX_MAJOR > 2) || (LINUX_MINOR >= 1))
-# if defined (USE_DEFAULT_NETWORK)
-# define USE_LPF
-# endif
-# if !defined (__sparc__) /* XXX hopefully this will be fixed someday */
-# define SIOCGIFCONF_ZERO_PROBE
-# endif
-# define LINUX_SLASHPROC_DISCOVERY
-# define PROCDEV_DEVICE "/proc/net/dev"
-# define HAVE_ARPHRD_TUNNEL
-# define HAVE_TR_SUPPORT
-# endif
-# define HAVE_ARPHRD_METRICOM
-# define HAVE_ARPHRD_IEEE802
-# define HAVE_ARPHRD_LOOPBACK
-# define HAVE_SO_BINDTODEVICE
-# define HAVE_SIOCGIFHWADDR
-# define HAVE_SETFD
-#endif
-
-#if defined (SIOCGIFHWADDR) && !defined (HAVE_SIOCGIFHWADDR)
-# define HAVE_SIOCGIFHWADDR
-#endif
-
-#if !defined (USE_LPF)
-# if defined (USE_DEFAULT_NETWORK)
-# define USE_SOCKETS
-# define SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED
-# endif
-# define IGNORE_HOSTUNREACH
-#endif
-
-#define ALIAS_NAMES_PERMUTED
-#define SKIP_DUMMY_INTERFACES
-
-#ifdef NEED_PRAND_CONF
-#ifndef HAVE_DEV_RANDOM
- # define HAVE_DEV_RANDOM 1
- #endif /* HAVE_DEV_RANDOM */
-
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/sbin/arp -an 2>&1",
- "/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/bin/uptime 2>&1",
- "/bin/netstat -s 2>&1",
- "/usr/bin/dig . soa +ti=1 +retry=0 2>&1",
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/usr/tmp",
- ".",
- "/",
- "/var/spool",
- "/dev",
- "/var/spool/mail",
- "/home",
- "/usr/home",
- NULL
-};
-
-const char *files[] = {
- "/proc/stat",
- "/proc/rtc",
- "/proc/meminfo",
- "/proc/interrupts",
- "/proc/self/status",
- "/var/log/messages",
- "/var/log/wtmp",
- "/var/log/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/netbsd.h b/includes/cf/netbsd.h
deleted file mode 100644
index de862d51..00000000
--- a/includes/cf/netbsd.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/* netbsd.h
-
- System dependencies for NetBSD... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <malloc.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/route.h>
-#include <sys/sockio.h>
-
-#define ifr_netmask ifr_addr
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/var/run/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/var/db/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/var/db/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define HAVE_SA_LEN
-#define HAVE_MKSTEMP
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_BPF
-#endif
-
-#if defined (__alpha__) || defined (__sparc64__)
-#define PTRSIZE_64BIT
-#endif
-
-/* NetBSD added socklen_t in 1.3J, just prior to the 1.4 release. */
-#if __NetBSD_Version__ < 103090000
-#define SOCKLEN_T int
-#endif
-
-#ifdef NEED_PRAND_CONF
-/* prand_conf.h goop - remove when BIND 9 comes around. */
-#ifndef HAVE_DEV_RANDOM
- # define HAVE_DEV_RANDOM 1
- #endif /* HAVE_DEV_RANDOM */
-
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/usr/bin/dig . soa +ti=1 +retry=0 2>&1",
- "/usr/sbin/iostat 2>&1",
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/dev",
- "/var/mail",
- "/home",
- "/usr/home",
- NULL
-};
-
-const char *files[] = {
- "/var/log/messages",
- "/var/log/wtmp",
- "/var/log/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/nextstep.h b/includes/cf/nextstep.h
deleted file mode 100644
index 74c0f4c4..00000000
--- a/includes/cf/nextstep.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/* nextstep.h
-
- System dependencies for NEXTSTEP 3 & 4 (tested on 4.2PR2)... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-/* NeXT needs BSD44 ssize_t */
-typedef int ssize_t;
-/* NeXT doesn't have BSD setsid() */
-#define setsid getpid
-#import <sys/types.h>
-/* Porting::
- The jmp_buf type as declared in <setjmp.h> is sometimes a structure
- and sometimes an array. By default, we assume it's a structure.
- If it's an array on your system, you may get compile warnings or errors
- as a result in confpars.c. If so, try including the following definitions,
- which treat jmp_buf as an array: */
-#if 0
-#define jbp_decl(x) jmp_buf x
-#define jref(x) (x)
-#define jdref(x) (x)
-#define jrefproto jmp_buf
-#endif
-#import <syslog.h>
-#import <string.h>
-#import <errno.h>
-#import <unistd.h>
-#import <sys/wait.h>
-#import <signal.h>
-#import <setjmp.h>
-#import <limits.h>
-extern int h_errno;
-#import <net/if.h>
-#import <net/if_arp.h>
-/* Porting::
- Some older systems do not have defines for IP type-of-service,
- or don't define them the way we expect. If you get undefined
- symbol errors on the following symbols, they probably need to be
- defined here. */
-#if 0
-#define IPTOS_LOWDELAY 0x10
-#define IPTOS_THROUGHPUT 0x08
-#define IPTOS_RELIABILITY 0x04
-#endif
-
-#if !defined (_PATH_DHCPD_PID)
-# define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-
-#if !defined (_PATH_DHCPD6_PID)
-# define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-
-#if !defined (_PATH_DHCLIENT_PID)
-# define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-
-#if !defined (_PATH_DHCLIENT6_PID)
-# define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-
-#if !defined (_PATH_DHCRELAY_PID)
-# define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-/* Stdarg definitions for ANSI-compliant C compilers. */
-#import <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-
-/* NeXT lacks snprintf */
-#define NO_SNPRINTF
-
-/* Porting::
- You must define the default network API for your port. This
- will depend on whether one of the existing APIs will work for
- you, or whether you need to implement support for a new API.
- Currently, the following APIs are supported:
- The BSD socket API: define USE_SOCKETS.
- The Berkeley Packet Filter: define USE_BPF.
- The Streams Network Interface Tap (NIT): define USE_NIT.
- Raw sockets: define USE_RAW_SOCKETS
- If your system supports the BSD socket API and doesn't provide
- one of the supported interfaces to the physical packet layer,
- you can either provide support for the low-level API that your
- system does support (if any) or just use the BSD socket interface.
- The BSD socket interface doesn't support multiple network interfaces,
- and on many systems, it does not support the all-ones broadcast
- address, which can cause problems with some DHCP clients (e.g.
- Microsoft Windows 95). */
-#define USE_BPF
-#if 0
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_SOCKETS
-#endif
-#endif
-#define EOL '\n'
-#define VOIDPTR void *
-#import <time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/etc/arp -a 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/local/bin/dig com. soa +ti=1 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/usr/ucb/printenv 2>&1",
- "/usr/ucb/netstat -s 2>&1",
- "/usr/local/bin/dig . soa +ti=1 2>&1",
- "/usr/bin/iostat 2>&1",
- "/usr/bin/vm_stat 2>&1",
- "/usr/ucb/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/usr/tmp",
- ".",
- "/",
- "/usr/spool",
- "/dev",
- NULL
-};
-
-const char *files[] = {
- "/usr/adm/messages",
- "/usr/adm/wtmp",
- "/usr/adm/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/openbsd.h b/includes/cf/openbsd.h
deleted file mode 100644
index 14afc8ef..00000000
--- a/includes/cf/openbsd.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* openbsd.h
-
- System dependencies for OpenBSD... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/route.h>
-#include <sys/sockio.h>
-
-#define ifr_netmask ifr_addr
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/var/run/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/var/db/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/var/db/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define HAVE_SA_LEN
-#define HAVE_MKSTEMP
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_BPF
-#endif
-
-#ifdef __alpha__
-#define PTRSIZE_64BIT
-#endif
-
-/* socklen_t first used right around the time 2.5 branched, it looks like. */
-#if OpenBSD < 199905
-#define SOCKLEN_T int
-#endif
-
-#ifdef NEED_PRAND_CONF
-#ifndef HAVE_DEV_RANDOM
- # define HAVE_DEV_RANDOM 1
- #endif /* HAVE_DEV_RANDOM */
-
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/usr/sbin/iostat 2>&1",
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/dev",
- "/var/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/var/log/messages",
- "/var/log/wtmp",
- "/var/log/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/qnx.h b/includes/cf/qnx.h
deleted file mode 100644
index 2692ce95..00000000
--- a/includes/cf/qnx.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/* qnx.h
-
- System dependencies for QNX... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-#include <syslog.h>
-#include <sys/select.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-#ifdef __QNXNTO__
-#include <sys/param.h>
-#endif
-
-#include <netdb.h>
-extern int h_errno;
-
-#include <net/if.h>
-#ifndef __QNXNTO__
-# define INADDR_LOOPBACK ((u_long)0x7f000001)
-#endif
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-#define TIME_DIFF(high, low) (*(high) - *(low))
-#define SET_TIME(x, y) (*(x) = (y))
-#define ADD_TIME(d, s1, s2) (*(d) = *(s1) + *(s2))
-#define SET_MAX_TIME(x) (*(x) = INT_MAX)
-
-#ifndef __QNXNTO__
-typedef unsigned char u_int8_t;
-typedef unsigned short u_int16_t;
-typedef unsigned long u_int32_t;
-typedef signed short int16_t;
-typedef signed long int32_t;
-#endif
-
-#ifdef __QNXNTO__
-typedef int socklen_t;
-#endif
-
-#define strcasecmp( s1, s2 ) stricmp( s1, s2 )
-#define strncasecmp( s1, s2, n ) strnicmp( s1, s2, n )
-#define random() rand()
-
-#define HAVE_SA_LEN
-#define BROKEN_TM_GMT
-#define USE_SOCKETS
-#undef AF_LINK
-
-#ifndef __QNXNTO__
-# define NO_SNPRINTF
-#endif
-
-#ifdef __QNXNTO__
-# define GET_HOST_ID_MISSING
-#endif
-
-/*
- NOTE: to get the routing of the 255.255.255.255 broadcasts to work
- under QNX, you need to issue the following command before starting
- the daemon:
-
- route add -interface 255.255.255.0 <hostname>
-
- where <hostname> is replaced by the hostname or IP number of the
- machine that dhcpd is running on.
-*/
-
-#ifndef __QNXNTO__
-# if defined (NSUPDATE)
-# error NSUPDATE is not supported on QNX at this time!!
-# endif
-#endif
-
-
-#ifdef NEED_PRAND_CONF
-#ifndef HAVE_DEV_RANDOM
-/* You should find and install the /dev/random driver */
- # define HAVE_DEV_RANDOM 1
- #endif /* HAVE_DEV_RANDOM */
-
-const char *cmds[] = {
- "/bin/ps -a 2>&1",
- "/bin/sin 2>&1",
- "/sbin/arp -an 2>&1",
- "/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/bin/sin fds 2>&1",
- "/bin/netstat -s 2>&1",
- "/bin/sin memory 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- ".",
- "/",
- "/var/spool",
- "/dev",
- "/var/spool/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/proc/ipstats",
- "/proc/dumper",
- "/proc/self/as",
- "/var/log/messages",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
-
diff --git a/includes/cf/rhapsody.h b/includes/cf/rhapsody.h
deleted file mode 100644
index dbe900c6..00000000
--- a/includes/cf/rhapsody.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/* rhapsody.h
-
- System dependencies for NetBSD... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <string.h>
-#include <paths.h>
-#include <errno.h>
-#include <unistd.h>
-#include <setjmp.h>
-#include <limits.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/route.h>
-#include <sys/sockio.h>
-#import <net/if_arp.h>
-
-#define ifr_netmask ifr_addr
-
-/* Varargs stuff... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define va_dcl
-#define VA_start(list, last) va_start (list, last)
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/var/run/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/var/run/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/var/db/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/var/db/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/var/run/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/var/run/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/var/db/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/var/db/dhclient6.leases"
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-#include <sys/time.h>
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define HAVE_SA_LEN
-#define HAVE_MKSTEMP
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_BPF
-#endif
-
-#ifdef __alpha__
-#define PTRSIZE_64BIT
-#endif
-
-#define SOCKLEN_T int
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/uptime 2>&1",
- "/usr/bin/printenv 2>&1",
- "/usr/sbin/netstat -s 2>&1",
- "/usr/bin/vm_stat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/mail",
- NULL
-};
-
-const char *files[] = {
- "/var/log/wtmp",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/sample.h b/includes/cf/sample.h
deleted file mode 100644
index 465ea843..00000000
--- a/includes/cf/sample.h
+++ /dev/null
@@ -1,299 +0,0 @@
-/* sample.h
-
- Sample config file for clients.
-
- This file is provided as a sample in case the system you want to run
- on is not currently supported. If that is the case, follow the Porting::
- comments here and in other files as guides for what to change. Also,
- note that this file hasn't been updated in a lo-o-o-ong time, so it's
- probably worth looking at the config files for working ports as well. */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-/* Porting::
-
- Some systems do not define basic integer types as shown below.
- On some systems, you need to include <bitypes.h> or <sys/bitypes.h>.
- If you get parse errors in dhcpd.h while compiling dhcpd.conf, try
- including bitypes.h, and if that fails, use the hard-coded definitions
- shown below. */
-
-#if 0
-#include <sys/bitypes.h>
-#endif
-
-#if 0
-#define int8_t char
-#define int16_t short
-#define int32_t long
-
-#define u_int8_t unsigned char
-#define u_int16_t unsigned short
-#define u_int32_t unsigned long
-#endif
-
-#include <sys/types.h>
-
-/* Porting::
-
- The jmp_buf type as declared in <setjmp.h> is sometimes a structure
- and sometimes an array. By default, we assume it's a structure.
- If it's an array on your system, you may get compile warnings or errors
- as a result in confpars.c. If so, try including the following definitions,
- which treat jmp_buf as an array: */
-
-#if 0
-#define jbp_decl(x) jmp_buf x
-#define jref(x) (x)
-#define jdref(x) (x)
-#define jrefproto jmp_buf
-#endif
-
-/* Porting::
-
- Some older systems (e.g., Ultrix) still use the 4.2BSD-style syslog
- API. These differ from later versions of the syslog API in that the
- openlog system call takes two arguments instead of three, and the
- facility code (the third argument to modern versions of openlog())
- is ORed into the log priority in the syslog() call.
-
- If you are running with the 4.2BSD-style syslog interface, define
- SYSLOG_4_2. */
-
-/* #define SYSLOG_4_2 */
-
-#include <syslog.h>
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_arp.h>
-
-/* Porting::
-
- Some older systems do not have defines for IP type-of-service,
- or don't define them the way we expect. If you get undefined
- symbol errors on the following symbols, they probably need to be
- defined here. */
-
-#if 0
-#define IPTOS_LOWDELAY 0x10
-#define IPTOS_THROUGHPUT 0x08
-#define IPTOS_RELIABILITY 0x04
-#endif
-
-/* Porting::
-
- Newer BSD derivatives store non-permanent daemon files in a
- directory called /var/run. If your system has a /var/run,
- use it; otherwise, use /etc. */
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-/* Porting::
-
- If your system supports standard ANSI C, it should provide the file
- /usr/include/stdarg.h. This contains the ANSI standard declarations
- for functions which take a variable number of arguments.
-
- Older systems with non-ANSI compilers cannot support this interface,
- and generally use the older varargs interface, defined in <varargs.h>.
- Some systems only support varargs, but define the interface in
- <stdarg.h> anyway.
-
- You must choose one of the two sets of definitions below. Try
- stdarg.h first, unless you know it won't work. If you have
- trouble compiling errwarn.c, try switching to the varargs.h definitions.
- If that fails, try using stdarg.h with the varargs definitions. */
-
-#if 0
-/* Stdarg definitions for ANSI-compliant C compilers. */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-#endif
-
-#if 0
-/* Varargs definitions, for non-ANSI-compliant C compilers. */
-#include <varargs.h>
-#define VA_DOTDOTDOT va_alist
-#define VA_start(list, last) va_start (list)
-#endif
-
-/* Porting::
-
- Some systems (notably 4.4BSD derivatives) support versions of the
- sprintf functions which will deposit a limited number of characters
- into the buffer; that limit is provided in an extra argument.
- If your system doesn't support this functionality, you must include
- the definition below for a dhcp-distribution-local version to be
- built and used: */
-
-#if 0
-#define NO_SNPRINTF
-#endif
-
-/* Porting::
-
- Some systems provide a function, strerror(), which takes the unix
- error number (see errno) and returns a pointer to a static buffer
- containing the corresponding error message.
-
- If your system doesn't provide strerror(), define NO_STRERROR
- as shown below: */
-
-#if 0
-#define NO_STRERROR
-char *strerror (int);
-#endif
-
-/* Porting::
-
- Once dhcpd has initialized itself, it loops forever waiting for
- packets to come in. Since we need to support multiple input streams
- in order to support multiple interfaces, dhcpd needs to be able to
- do a syscall to determine which descriptors have input waiting on
- them.
-
- Normally, dhcpd uses the select() system call, which is a 4.2BSD
- syscall invented precisely for this purpose. Unfortunately, some
- System V-based systems do not support select() properly when it
- operates on streams. The System V interface which does (largely)
- the same thing as select is called poll(). In some cases, this may
- work better than select() - if you find that dhcpd is hanging and not
- responding to packets very consistently, you might try defining
- USE_POLL and including <poll.h>. */
-
-#if 0
-#define USE_POLL
-#include <poll.h>
-#endif
-
-/* Porting::
-
- You must define the default network API for your port. This
- will depend on whether one of the existing APIs will work for
- you, or whether you need to implement support for a new API.
- Currently, the following APIs are supported:
-
- The BSD socket API: define USE_SOCKETS.
- The Berkeley Packet Filter: define USE_BPF.
- The Streams Network Interface Tap (NIT): define USE_NIT.
- Raw sockets: define USE_RAW_SOCKETS
-
- If your system supports the BSD socket API and doesn't provide
- one of the supported interfaces to the physical packet layer,
- you can either provide support for the low-level API that your
- system does support (if any) or just use the BSD socket interface.
- The BSD socket interface doesn't support multiple network interfaces,
- and on many systems, it does not support the all-ones broadcast
- address, which can cause problems with some DHCP clients (e.g.
- Microsoft Windows 95). */
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_SOCKETS
-#endif
-
-/* Porting::
-
- Recent versions of BSD added a new element to the sockaddr structure:
- sa_len. This indicates the length of the structure, and is used
- in a variety of places, not the least of which is the SIOCGIFCONF
- ioctl, which is used to figure out what interfaces are attached to
- the system.
-
- You should be able to determine if your system has an sa_len element
- by looking at the struct sockaddr definition in /usr/include/sys/socket.h.
- If it does, you must define HAVE_SA_LEN. Otherwise, you must not.
- The most obvious symptom that you've got this wrong is either a compile
- error complaining about the use of the sa_len structure element, or
- the failure of dhcpd to find any interfaces. */
-
-/* #define HAVE_SA_LEN */
-
-/* Every operating system has its own way of separating lines in a
- sequential text file. Most modern systems use a single character,
- either an ASCII Newline (10) or an ASCII Carriage Return (13).
-
- The most notable exception is MS-DOS (and consequently, Windows),
- which uses an ASCII Carriage Return followed by a Newline to
- separate each line. Fortunately, MS-DOS C compiler libraries
- typically hide this from the programmer, returning just a Newline.
-
- Define EOL to be whatever getc() returns for a newline. */
-
-#define EOL '\n'
-
-/* Some older C compilers don't support the void pointer type.
- ANSI C defines void * to be a pointer type that matches
- any other pointer type. This is handy for returning a pointer
- which will always need to be cast to a different value. For
- example, malloc() on an ANSI C-compliant system returns void *.
-
- If your compiler doesn't support void pointers, you may need to
- define VOIDPTR to be char *; otherwise, define it to be void *. */
-
-#define VOIDPTR void *
-
-/* Porting::
-
- The following definitions for time should work on any unix machine.
- They may not work (or at least, may not work well) on a variety of
- non-unix machines. If you are porting to a non-unix machine, you
- probably need to change the definitions below and perhaps include
- different headers.
-
- I should note that dhcpd is not yet entirely clean of unix-specific
- time references, so the list of defines shown below probably isn't
- good enough if you're porting to a system that really doesn't support
- unix time. It's probably a reasonable place to start, though. */
-
-#include <time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
diff --git a/includes/cf/sco.h b/includes/cf/sco.h
deleted file mode 100644
index 9e8486de..00000000
--- a/includes/cf/sco.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/* sco.h
-
- System dependencies for SCO ODT 3.0...
-
- Based on changes contributed by Gerald Rosenberg. */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-#include <syslog.h>
-#include <sys/types.h>
-
-/* Basic Integer Types not defined in SCO headers... */
-
-typedef char int8_t;
-typedef short int16_t;
-typedef long int32_t;
-
-typedef unsigned char u_int8_t;
-typedef unsigned short u_int16_t;
-typedef unsigned long u_int32_t;
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/if_arp.h>
-#include <netinet/tcp.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-
-/* XXX dunno if this is required for SCO... */
-/*
- * Definitions for IP type of service (ip_tos)
- */
-#define IPTOS_LOWDELAY 0x10
-#define IPTOS_THROUGHPUT 0x08
-#define IPTOS_RELIABILITY 0x04
-/* IPTOS_LOWCOST 0x02 XXX */
-
-/* SCO doesn't have /var/run. */
-#ifndef _PATH_DHCPD_CONF
-#define _PATH_DHCPD_CONF "/etc/dhcpd.conf"
-#endif
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-#ifndef _PATH_DHCPD_DB
-#define _PATH_DHCPD_DB "/etc/dhcpd.leases"
-#endif
-#ifndef _PATH_DHCPD6_DB
-#define _PATH_DHCPD6_DB "/etc/dhcpd6.leases"
-#endif
-#ifndef _PATH_DHCLIENT_DB
-#define _PATH_DHCLIENT_DB "/etc/dhclient.leases"
-#endif
-#ifndef _PATH_DHCLIENT6_DB
-#define _PATH_DHCLIENT6_DB "/etc/dhclient6.leases"
-#endif
-
-
-#if !defined (INADDR_LOOPBACK)
-#define INADDR_LOOPBACK ((u_int32_t)0x7f000001)
-#endif
-
-/* Varargs stuff: use stdarg.h instead ... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-
-/* SCO doesn't support limited sprintfs. */
-#define NO_SNPRINTF
-
-/* By default, use BSD Socket API for receiving and sending packets.
- This actually works pretty well on Solaris, which doesn't censor
- the all-ones broadcast address. */
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_SOCKETS
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* socklen_t */
-typedef int socklen_t;
-
-/*
- * Time stuff...
- *
- * Definitions for an ISC DHCPD system that uses time_t
- * to represent time internally as opposed to, for example, struct timeval.)
- */
-
-#include <time.h>
-#include <sys/time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -ef 2>&1",
- "/etc/arp -n -a 2>&1",
- "/usr/bin/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/uptime 2>&1",
- "/usr/bin/netstat -s 2>&1",
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/usr/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- NULL
-};
-
-const char *files[] = {
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/sunos4.h b/includes/cf/sunos4.h
deleted file mode 100644
index ebc56388..00000000
--- a/includes/cf/sunos4.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/* sunos4.h
-
- System dependencies for SunOS 4 (tested on 4.1.4)... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-/* Basic Integer Types not defined in SunOS headers... */
-
-#define int8_t char
-#define int16_t short
-#define int32_t int
-
-#define u_int8_t unsigned char
-#define u_int16_t unsigned short
-#define u_int32_t unsigned int
-
-#define ssize_t int
-
-#define SOCKLEN_T int
-
-#define fpos_t long
-#define fgetpos(f, p) (((*(p)) = ftell (f)) == -1 ? -1 : 0)
-#define fsetpos(f, p) (fseek (f, p, SEEK_SET))
-
-/* No endian.h either. */
-/*
- * Definitions for byte order, according to byte significance from low
- * address to high.
- */
-#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */
-#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */
-#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long */
-
-#define BYTE_ORDER BIG_ENDIAN
-
-/* The jmp_buf type is an array on SunOS, so we can't dereference it
- and must declare it differently. */
-#define jbp_decl(x) jmp_buf x
-#define jref(x) (x)
-#define jdref(x) (x)
-#define jrefproto jmp_buf
-
-#include <syslog.h>
-#include <sys/types.h>
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-#include <poll.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_arp.h>
-
-/*
- * Definitions for IP type of service (ip_tos)
- */
-#define IPTOS_LOWDELAY 0x10
-#define IPTOS_THROUGHPUT 0x08
-#define IPTOS_RELIABILITY 0x04
-/* IPTOS_LOWCOST 0x02 XXX */
-
-/* SunOS systems don't have /var/run, but some sites have added it.
- If you want to put dhcpd.pid in /var/run, define _PATH_DHCPD_PID
- in site.h. */
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#ifdef __GNUC__
-/* Varargs stuff: use stdarg.h instead ... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-#else /* !__GNUC__*/
-/* Varargs stuff... */
-#include <varargs.h>
-#define VA_DOTDOTDOT va_alist
-#define VA_start(list, last) va_start (list)
-#endif /* !__GNUC__*/
-
-/* SunOS doesn't support limited sprintfs. */
-#define NO_SNPRINTF
-
-/* SunOS doesn't supply strerror... */
-#define NO_STRERROR
-char *strerror (int);
-
-#define NEED_INET_ATON
-
-/* By default, use NIT API for receiving and sending packets... */
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_NIT
-#endif
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-#include <time.h>
-#include <sys/time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -axlw 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/bin/iostat 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/var/adm/messages",
- "/var/adm/wtmp",
- "/var/adm/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/sunos5-5.h b/includes/cf/sunos5-5.h
deleted file mode 100644
index f4b2458a..00000000
--- a/includes/cf/sunos5-5.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/* sunos5-5.h
-
- System dependencies for Solaris 2.x (tested on 2.5 with gcc)... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-/* SunOS defines uint*_t and int*_t, but not u_int*_t. */
-
-#if defined(_SYS_INT_TYPES_H)
-typedef uint8_t u_int8_t;
-typedef uint16_t u_int16_t;
-typedef uint32_t u_int32_t;
-typedef uint64_t u_int64_t;
-#else /* Older SunOS has no idea what these things mean. */
-typedef int8_t char
-typedef int16_t short
-typedef int32_t int /* If _LP64, long is 64-bit, int is still 32. */
-typedef u_int8_t unsigned char
-typedef u_int16_t unsigned short
-typedef u_int32_t unsigned int
-#endif /* defined(_SYS_INT_TYPES_H) */
-
-/* The jmp_buf type is an array on Solaris, so we can't dereference it
- and must declare it differently. */
-
-#define jbp_decl(x) jmp_buf x
-#define jref(x) (x)
-#define jdref(x) (x)
-#define jrefproto jmp_buf
-
-#include <syslog.h>
-#include <sys/types.h>
-#include <sys/sockio.h>
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-#include <net/if_arp.h>
-
-/* Solaris 2.6 defines AF_LINK, so we need the rest of the baggage that
- comes with it, but of course Solaris 2.5 and previous do not. */
-#if defined (AF_LINK)
-#include <net/if_dl.h>
-#endif
-
-/*
- * Definitions for IP type of service (ip_tos)
- */
-#define IPTOS_LOWDELAY 0x10
-#define IPTOS_THROUGHPUT 0x08
-#define IPTOS_RELIABILITY 0x04
-/* IPTOS_LOWCOST 0x02 XXX */
-
-/* Solaris systems don't have /var/run, but some sites have added it.
- If you want to put dhcpd.pid in /var/run, define _PATH_DHCPD_PID
- in site.h. */
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#if defined (__GNUC__) || defined (__SVR4)
-/* Varargs stuff: use stdarg.h instead ... */
-#include <stdarg.h>
-#define VA_DOTDOTDOT ...
-#define VA_start(list, last) va_start (list, last)
-#define va_dcl
-#else /* !__GNUC__*/
-/* Varargs stuff... */
-#include <varargs.h>
-#define VA_DOTDOTDOT va_alist
-#define VA_start(list, last) va_start (list)
-#endif /* !__GNUC__*/
-
-#define NEED_INET_ATON
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_DLPI
-# define USE_DLPI_PFMOD
-#endif
-
-#define USE_POLL
-
-#define EOL '\n'
-#define VOIDPTR void *
-
-/* Time stuff... */
-
-#include <time.h>
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-#define HAVE_MKSTEMP
-
-/* Solaris prior to 2.5 didn't have random(). Rather than being clever and
- using random() only on versions >2.5, always use rand() and srand(). */
-
-#if SOLARIS_MAJOR == 5 && SOLARIS_MINOR < 5
-#define random() rand()
-#define srandom(x) srand(x)
-#endif
-
-/* Solaris doesn't provide an endian.h, so we have to do it. */
-
-#if !defined (BIG_ENDIAN)
-# define BIG_ENDIAN 1
-#endif
-
-#if !defined (BIG_ENDIAN)
-# define LITTLE_ENDIAN 2
-#endif
-
-#if !defined (BYTE_ORDER)
-# if defined (__i386) || defined (i386)
-# define BYTE_ORDER LITTLE_ENDIAN
-# else
-# if defined (__sparc) || defined (sparc)
-# define BYTE_ORDER BIG_ENDIAN
-# else
-@@@ ERROR @@@ Unable to determine byte order!
-# endif
-# endif
-#endif
-
-#define ALIAS_NAMES_PERMUTED
-
-#if SOLARIS_MAJOR == 5 && SOLARIS_MINOR < 7
-typedef int socklen_t;
-#endif
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -ef 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/bin/iostat 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/mail",
- "/home",
- NULL
-};
-
-const char *files[] = {
- "/proc/self/status",
- "/var/adm/messages",
- "/var/adm/wtmp",
- "/var/adm/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/cf/ultrix.h b/includes/cf/ultrix.h
deleted file mode 100644
index b7047e72..00000000
--- a/includes/cf/ultrix.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/* ultrix.h
-
- System dependencies for Ultrix 4.2 (tested on 4.2a+multicast)... */
-
-/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996-2003 by Internet Software Consortium
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * https://www.isc.org/
- *
- */
-
-/* Ultrix uses the old 4.2BSD-style syslog(). */
-#include <sys/syslog.h>
-#define SYSLOG_4_2
-
-#include <sys/types.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <limits.h>
-
-extern int h_errno;
-
-#include <net/if.h>
-
-#ifndef _PATH_DHCPD_PID
-#define _PATH_DHCPD_PID "/etc/dhcpd.pid"
-#endif
-#ifndef _PATH_DHCPD6_PID
-#define _PATH_DHCPD6_PID "/etc/dhcpd6.pid"
-#endif
-#ifndef _PATH_DHCLIENT_PID
-#define _PATH_DHCLIENT_PID "/etc/dhclient.pid"
-#endif
-#ifndef _PATH_DHCLIENT6_PID
-#define _PATH_DHCLIENT6_PID "/etc/dhclient6.pid"
-#endif
-#ifndef _PATH_DHCRELAY_PID
-#define _PATH_DHCRELAY_PID "/etc/dhcrelay.pid"
-#endif
-
-#define int8_t char
-#define int16_t short
-#define int32_t long
-#define ssize_t long
-
-#define u_int8_t unsigned char /* Not quite POSIX... */
-#define u_int16_t unsigned short
-#define u_int32_t unsigned long
-
-#define ssize_t size_t
-
-/* The jmp_buf type is an array on ultrix, so we can't dereference it
- and must declare it differently. */
-#define jbp_decl(x) jmp_buf x
-#define jref(x) (x)
-#define jdref(x) (x)
-#define jrefproto jmp_buf
-
-#define IPTOS_LOWDELAY 0x10
-/* IPTOS_LOWCOST 0x02 XXX */
-
-/* Varargs stuff... */
-#include <varargs.h>
-#define VA_DOTDOTDOT va_alist
-#define VA_start(list, last) va_start (list)
-
-/* XXX: System is not thought to support snprintf/vsnprintf. Please verify. */
-#define NO_SNPRINTF
-
-#define NEED_INET_ATON
-
-#define INADDR_LOOPBACK ((u_int32_t)0x7f000001)
-#define EOL '\n'
-#define VOIDPTR void *
-#define SOCKLEN_T int
-
-/*
- * Time stuff...
- *
- * Definitions for an ISC DHCPD system that uses time_t
- * to represent time internally as opposed to, for example, struct timeval.)
- */
-
-#define TIME time_t
-#define GET_TIME(x) time ((x))
-
-/* Ultrix doesn't provide an endian.h, but it only runs on little-endian
- machines, so we'll just hack around the issue. */
-#define BIG_ENDIAN 1
-#define LITTLE_ENDIAN 2
-#define BYTE_ORDER LITTLE_ENDIAN
-
-#if defined (USE_DEFAULT_NETWORK)
-# define USE_UPF
-#endif
-
-#ifdef NEED_PRAND_CONF
-const char *cmds[] = {
- "/bin/ps -aux 2>&1",
- "/usr/etc/arp -an 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/usr/bin/df 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- "/usr/bin/iostat 2>&1",
- NULL
-};
-
-const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/spool/mail",
- NULL
-};
-
-const char *files[] = {
- "/var/spool/mqueue/syslog",
- "/var/adm/wtmp",
- "/var/adm/lastlog",
- NULL
-};
-#endif /* NEED_PRAND_CONF */
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index d4c81fc1..b20dd240 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -2311,7 +2311,7 @@ int parse_auth_key (struct data_string *, struct parse *);
int parse_warn (struct parse *, const char *, ...)
__attribute__((__format__(__printf__,2,3)));
struct expression *parse_domain_list(struct parse *cfile, int);
-
+struct expression *parse_domain_name(struct parse *cfile);
/* tree.c */
extern struct binding_scope *global_scope;
diff --git a/includes/ns_name.h b/includes/ns_name.h
index 5095c1ce..a2c42769 100644
--- a/includes/ns_name.h
+++ b/includes/ns_name.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2001-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -31,6 +31,7 @@
* <viraj_bais@ccm.fm.intel.com>
*/
+int MRns_name_len(const unsigned char *, const unsigned char *);
int MRns_name_compress(const char *, u_char *, size_t, const unsigned char **,
const unsigned char **);
int MRns_name_unpack(const unsigned char *, const unsigned char *,
diff --git a/includes/omapip/isclib.h b/includes/omapip/isclib.h
index a93117c9..b8f7c397 100644
--- a/includes/omapip/isclib.h
+++ b/includes/omapip/isclib.h
@@ -94,7 +94,8 @@
typedef struct dhcp_context {
isc_mem_t *mctx;
isc_appctx_t *actx;
- int actx_started;
+ int actx_started; // ISC_TRUE if ctxstart has been called
+ int actx_running; // ISC_TRUE if ctxrun has been called
isc_taskmgr_t *taskmgr;
isc_task_t *task;
isc_socketmgr_t *socketmgr;
diff --git a/includes/osdep.h b/includes/osdep.h
index 73cbef41..3ee70bc0 100644
--- a/includes/osdep.h
+++ b/includes/osdep.h
@@ -138,6 +138,10 @@
# define USE_UPF_RECEIVE
#endif
+#if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE)
+# define HAVE_SO_BINDTODEVICE
+#endif
+
/* Porting::
If you add support for sending packets directly out an interface,
@@ -263,10 +267,6 @@
# define HAVE_ARPHRD_METRICOM
#endif
-#if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE)
-# define HAVE_SO_BINDTODEVICE
-#endif
-
#if defined (AF_LINK) && !defined (HAVE_AF_LINK)
# define HAVE_AF_LINK
#endif
diff --git a/keama/.gitignore b/keama/.gitignore
new file mode 100644
index 00000000..deb1cb67
--- /dev/null
+++ b/keama/.gitignore
@@ -0,0 +1 @@
+keama
diff --git a/keama/ChangeLog.md b/keama/ChangeLog.md
new file mode 100644
index 00000000..fd386d9e
--- /dev/null
+++ b/keama/ChangeLog.md
@@ -0,0 +1,18 @@
+* 3 [doc] fdupont
+
+ New documentation including this file.
+ (Gitlab #34)
+
+* 2 [bug] fdupont
+
+ Fixed dhcp4 option 67 wrong name.
+ (Gitlab #22)
+
+* 1 [func] fdupont
+
+ Initial revision.
+
+LEGEND
+* [bug] Bug fix.
+* [doc] Update to documentation.
+* [func] New feature.
diff --git a/keama/Makefile.am b/keama/Makefile.am
new file mode 100644
index 00000000..5630cc91
--- /dev/null
+++ b/keama/Makefile.am
@@ -0,0 +1,5 @@
+sbin_PROGRAMS = keama
+keama_SOURCES = keama.c data.c conflex.c json.c confparse.c parse.c options.c
+keama_SOURCES += reduce.c print.c eval.c
+man_MANS = keama.8
+EXTRA_DIST = $(man_MANS)
diff --git a/keama/Makefile.in b/keama/Makefile.in
new file mode 100644
index 00000000..43e9bc26
--- /dev/null
+++ b/keama/Makefile.in
@@ -0,0 +1,721 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = keama$(EXEEXT)
+subdir = keama
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_keama_OBJECTS = keama.$(OBJEXT) data.$(OBJEXT) conflex.$(OBJEXT) \
+ json.$(OBJEXT) confparse.$(OBJEXT) parse.$(OBJEXT) \
+ options.$(OBJEXT) reduce.$(OBJEXT) print.$(OBJEXT) \
+ eval.$(OBJEXT)
+keama_OBJECTS = $(am_keama_OBJECTS)
+keama_LDADD = $(LDADD)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/conflex.Po ./$(DEPDIR)/confparse.Po \
+ ./$(DEPDIR)/data.Po ./$(DEPDIR)/eval.Po ./$(DEPDIR)/json.Po \
+ ./$(DEPDIR)/keama.Po ./$(DEPDIR)/options.Po \
+ ./$(DEPDIR)/parse.Po ./$(DEPDIR)/print.Po \
+ ./$(DEPDIR)/reduce.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(keama_SOURCES)
+DIST_SOURCES = $(keama_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+A = @A@
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ATF_BIN = @ATF_BIN@
+ATF_CFLAGS = @ATF_CFLAGS@
+ATF_LDFLAGS = @ATF_LDFLAGS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINDCONFIG = @BINDCONFIG@
+BINDDIR = @BINDDIR@
+BINDIOMUX = @BINDIOMUX@
+BINDLIBDNSDIR = @BINDLIBDNSDIR@
+BINDLIBIRSDIR = @BINDLIBIRSDIR@
+BINDLIBISCCFGDIR = @BINDLIBISCCFGDIR@
+BINDLIBISCDIR = @BINDLIBISCDIR@
+BINDLT = @BINDLT@
+BINDSRCDIR = @BINDSRCDIR@
+BINDSUBDIR = @BINDSUBDIR@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHLIBS = @DHLIBS@
+DISTCHECK_ATF_CONFIGURE_FLAG = @DISTCHECK_ATF_CONFIGURE_FLAG@
+DISTCHECK_LIBBIND_CONFIGURE_FLAG = @DISTCHECK_LIBBIND_CONFIGURE_FLAG@
+DISTCHECK_LIBTOOL_CONFIGURE_FLAG = @DISTCHECK_LIBTOOL_CONFIGURE_FLAG@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDAP_LIBS = @LDAP_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+Q = @Q@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+UNITTESTS = @UNITTESTS@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgcfg_found = @pkgcfg_found@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+keama_SOURCES = keama.c data.c conflex.c json.c confparse.c parse.c \
+ options.c reduce.c print.c eval.c
+man_MANS = keama.8
+EXTRA_DIST = $(man_MANS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keama/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign keama/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+
+keama$(EXEEXT): $(keama_OBJECTS) $(keama_DEPENDENCIES) $(EXTRA_keama_DEPENDENCIES)
+ @rm -f keama$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(keama_OBJECTS) $(keama_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conflex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/confparse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keama.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reduce.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+install-man8: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/conflex.Po
+ -rm -f ./$(DEPDIR)/confparse.Po
+ -rm -f ./$(DEPDIR)/data.Po
+ -rm -f ./$(DEPDIR)/eval.Po
+ -rm -f ./$(DEPDIR)/json.Po
+ -rm -f ./$(DEPDIR)/keama.Po
+ -rm -f ./$(DEPDIR)/options.Po
+ -rm -f ./$(DEPDIR)/parse.Po
+ -rm -f ./$(DEPDIR)/print.Po
+ -rm -f ./$(DEPDIR)/reduce.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/conflex.Po
+ -rm -f ./$(DEPDIR)/confparse.Po
+ -rm -f ./$(DEPDIR)/data.Po
+ -rm -f ./$(DEPDIR)/eval.Po
+ -rm -f ./$(DEPDIR)/json.Po
+ -rm -f ./$(DEPDIR)/keama.Po
+ -rm -f ./$(DEPDIR)/options.Po
+ -rm -f ./$(DEPDIR)/parse.Po
+ -rm -f ./$(DEPDIR)/print.Po
+ -rm -f ./$(DEPDIR)/reduce.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-sbinPROGRAMS cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/keama/README.md b/keama/README.md
new file mode 100644
index 00000000..4028fcba
--- /dev/null
+++ b/keama/README.md
@@ -0,0 +1,53 @@
+# KEA Migration Assistant Short Guide.
+
+The KEA Migration Assistant (aka _keama_) is an experimental tool
+which helps to translate ISC DHCP configurations to Kea.
+
+## How to get last sources
+
+From time to time the _keama_ is upgraded for bug fixes, support of
+new or not yet ISC DHCP features or more likely support of new KEA
+features.
+
+As now _keama_ is included in ISC DHCP the most recent code can be
+found with the most recent ISC DHCP code in the master branch of the
+gitlab repository.
+
+## How to build and install
+
+After the ISC DHCP build go to the keama directory and type:
+```console
+make
+```
+To install it:
+```console
+make install
+```
+
+## Known limitations
+
+_keama_ uses a subset of the ISC DHCP configuration file parser with a lot
+of sanaity checks removed so it does not know how to handle an incorrect
+ISC DHCP configuration file and eventually can even crash on it.
+
+ISC DHCP and KEA have different models for many things, for instance
+ISC DHCP supports the failover protocol when KEA supports High Availability.
+In some cases _keama_ tries to cope with that, for instance for host
+reservations which are global in ISC DHCP and by default per subnet in KEA.
+
+## How to use
+
+The manual explains how parameters guide _keama_ choices for lifetimes,
+name literals, host reservation scope, etc. Directives were added to
+the ISC DHCP syntax (they are valid but ignored) for options.
+
+Each time _keama_ finds a feature it can't translate it emits a comment
+with a reference to the feature description in a kea (not isc dhcp) gitlab
+issue in the "ISC DHCP Migration" milestone. The number of reports is
+returned by _keama_ when it exits.
+
+## How to help
+
+If you have configuration patterns you would like to see supported
+by Keama please feel free to reach out to us. We are always looking
+to improve the tool.
diff --git a/keama/REAME.md b/keama/REAME.md
new file mode 100644
index 00000000..5d1243f1
--- /dev/null
+++ b/keama/REAME.md
@@ -0,0 +1,53 @@
+# KEA Migration Assistant Short Guide.
+
+The KEA Migration Assistant (aka _keama_) is an experimental tool
+which helps to translate ISC DHCP configurations to Kea.
+
+## How to get last sources
+
+From time to time the _keama_ is upgraded for bug fixes, support of
+new or not yet ISC DHCP features or more likely support of new KEA
+features.
+
+As now _keama_ is included in ISC DHCP the most recent code can be
+found with the most recent ISC DHCP code in the master branch of the
+gitlab repository.
+
+## How to build and install
+
+After the ISC DHCP build go to the keama directory and type:
+```console
+make
+```
+To install it:
+```console
+make install
+```
+
+## Known limitations
+
+_keama_ uses a subset of the ISC DHCP configuration file parser with a lot
+of sanaity checks removed so it does not know how to handle an incorrect
+ISC DHCP configuration file and eventually can even crash on it.
+
+ISC DHCP and KEA have different models for many things, for instance
+ISC DHCP supports the failover protocol when KEA supports High Availability.
+In some cases _keama_ tries to cope with that, for instance for host
+reservations which are global in ISC DHCP and by default per subnet in KEA.
+
+## How to use
+
+The manual explains how parameters guide _keama_ choices for lifetimes,
+name literals, host reservation scope, etc. Directives were added to
+the ISC DHCP syntax (they are valid but ignored) for options.
+
+Each time _keama_ finds a feature it can't translate it emits a comment
+with a reference to the feature description in a kea (not isc dhcp) gitlab
+issue in the "ISC DHCP Migration" milestone. The number of reports is
+returned by _keama_ when it exits.
+
+## How to help
+
+We already collected a lot of ISC DHCP configurations used in production
+but you can supply new ones, in particular if they use a common
+paradigm which can be easily translated but not (yet) supported.
diff --git a/keama/conflex.c b/keama/conflex.c
new file mode 100644
index 00000000..143487b0
--- /dev/null
+++ b/keama/conflex.c
@@ -0,0 +1,1550 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+/* From common/conflex.c */
+
+#include "keama.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int get_char(struct parse *);
+static void unget_char(struct parse *, int);
+static void skip_to_eol(struct parse *);
+static enum dhcp_token read_whitespace(int c, struct parse *cfile);
+static enum dhcp_token read_string(struct parse *);
+static enum dhcp_token read_number(int, struct parse *);
+static enum dhcp_token read_num_or_name(int, struct parse *);
+static enum dhcp_token intern(char *, enum dhcp_token);
+
+struct parse *
+new_parse(int file, char *inbuf, size_t buflen, const char *name, int eolp)
+{
+ struct parse *tmp;
+
+ tmp = (struct parse *)malloc(sizeof(struct parse));
+ assert(tmp != NULL);
+ memset(tmp, 0, sizeof(struct parse));
+
+ TAILQ_INSERT_TAIL(&parses, tmp);
+
+ tmp->tlname = name;
+ tmp->lpos = tmp->line = 1;
+ tmp->cur_line = tmp->line1;
+ tmp->prev_line = tmp->line2;
+ tmp->token_line = tmp->cur_line;
+ tmp->cur_line[0] = tmp->prev_line[0] = 0;
+ tmp->file = file;
+ tmp->eol_token = eolp;
+ TAILQ_INIT(&tmp->comments);
+
+ if (inbuf != NULL) {
+ tmp->inbuf = inbuf;
+ tmp->buflen = buflen;
+ tmp->bufsiz = 0;
+ } else {
+ struct stat sb;
+
+ if (fstat(file, &sb) < 0) {
+ fprintf(stderr, "can't stat input\n");
+ exit(1);
+ }
+
+ if (sb.st_size == 0)
+ return tmp;
+
+ tmp->bufsiz = tmp->buflen = (size_t) sb.st_size;
+ tmp->inbuf = mmap(NULL, tmp->bufsiz, PROT_READ, MAP_SHARED,
+ file, 0);
+
+ if (tmp->inbuf == MAP_FAILED) {
+ fprintf(stderr, "can't map input\n");
+ exit(1);
+ }
+ }
+
+ return tmp;
+}
+
+void
+end_parse(struct parse *cfile)
+{
+ /* "Memory" config files have no file. */
+ if (cfile->file != -1) {
+ munmap(cfile->inbuf, cfile->bufsiz);
+ close(cfile->file);
+ }
+
+ while (TAILQ_NEXT(cfile) != NULL) {
+ struct parse *saved_state;
+
+ saved_state = TAILQ_NEXT(cfile);
+ TAILQ_REMOVE(&parses, saved_state);
+ free(saved_state);
+ }
+
+ cfile->stack_size = 0;
+ if (cfile->stack != NULL)
+ free(cfile->stack);
+ cfile->stack = NULL;
+ TAILQ_REMOVE(&parses, cfile);
+ free(cfile);
+}
+
+/*
+ * Save the current state of the parser.
+ *
+ * Only one state may be saved. Any previous saved state is
+ * lost.
+ */
+void
+save_parse_state(struct parse *cfile) {
+ struct parse *tmp;
+
+ /*
+ * Free any previous saved states.
+ */
+ while (TAILQ_NEXT(cfile) != NULL) {
+ struct parse *saved_state;
+
+ saved_state = TAILQ_NEXT(cfile);
+ TAILQ_REMOVE(&parses, saved_state);
+ free(saved_state);
+ }
+
+ /*
+ * Save our current state.
+ */
+ tmp = (struct parse *)malloc(sizeof(struct parse));
+ if (tmp == NULL)
+ parse_error(cfile, "can't allocate state to be saved");
+ memset(tmp, 0, sizeof(struct parse));
+ /* save up to comments field */
+ memcpy(tmp, cfile, (size_t)&(((struct parse *)0)->comments));
+ TAILQ_INSERT_AFTER(&parses, cfile, tmp);
+}
+
+/*
+ * Return the parser to the previous saved state.
+ *
+ * You must call save_parse_state() every time before calling
+ * restore_parse_state().
+ */
+void
+restore_parse_state(struct parse *cfile) {
+ struct parse *saved_state;
+
+ if (TAILQ_NEXT(cfile) == NULL)
+ parse_error(cfile, "can't find saved state");
+
+ saved_state = TAILQ_NEXT(cfile);
+ TAILQ_REMOVE(&parses, saved_state);
+ /* restore up to comments field */
+ memcpy(cfile, saved_state, (size_t)&(((struct parse *)0)->comments));
+ free(saved_state);
+}
+
+static int
+get_char(struct parse *cfile)
+{
+ /* My kingdom for WITH... */
+ int c;
+
+ if (cfile->bufix == cfile->buflen) {
+ c = EOF;
+ } else {
+ c = cfile->inbuf[cfile->bufix];
+ cfile->bufix++;
+ }
+
+ if (!cfile->ugflag) {
+ if (c == EOL) {
+ if (cfile->cur_line == cfile->line1) {
+ cfile->cur_line = cfile->line2;
+ cfile->prev_line = cfile->line1;
+ } else {
+ cfile->cur_line = cfile->line1;
+ cfile->prev_line = cfile->line2;
+ }
+ cfile->line++;
+ cfile->lpos = 1;
+ cfile->cur_line[0] = 0;
+ } else if (c != EOF) {
+ if (cfile->lpos <= 80) {
+ cfile->cur_line[cfile->lpos - 1] = c;
+ cfile->cur_line[cfile->lpos] = 0;
+ }
+ cfile->lpos++;
+ }
+ } else
+ cfile->ugflag = 0;
+ return c;
+}
+
+/*
+ * Return a character to our input buffer.
+ */
+static void
+unget_char(struct parse *cfile, int c) {
+ if (c != EOF) {
+ cfile->bufix--;
+ cfile->ugflag = 1; /* do not put characters into
+ our error buffer on the next
+ call to get_char() */
+ }
+}
+
+/*
+ * GENERAL NOTE ABOUT TOKENS
+ *
+ * We normally only want non-whitespace tokens. There are some
+ * circumstances where we *do* want to see whitespace (for example
+ * when parsing IPv6 addresses).
+ *
+ * Generally we use the next_token() function to read tokens. This
+ * in turn calls get_next_token, which does *not* return tokens for
+ * whitespace. Rather, it skips these.
+ *
+ * When we need to see whitespace, we us next_raw_token(), which also
+ * returns the WHITESPACE token.
+ *
+ * The peek_token() and peek_raw_token() functions work as expected.
+ *
+ * Warning: if you invoke peek_token(), then if there is a whitespace
+ * token, it will be lost, and subsequent use of next_raw_token() or
+ * peek_raw_token() will NOT see it.
+ */
+
+static enum dhcp_token
+get_raw_token(struct parse *cfile) {
+ int c;
+ enum dhcp_token ttok;
+ static char tb[2];
+ int l, p;
+
+ for (;;) {
+ l = cfile->line;
+ p = cfile->lpos;
+
+ c = get_char(cfile);
+ if (!((c == '\n') && cfile->eol_token) &&
+ isascii(c) && isspace(c)) {
+ ttok = read_whitespace(c, cfile);
+ break;
+ }
+ if (c == '#') {
+ skip_to_eol(cfile);
+ continue;
+ }
+ if (c == '"') {
+ cfile->lexline = l;
+ cfile->lexchar = p;
+ ttok = read_string(cfile);
+ break;
+ }
+ if ((isascii(c) && isdigit(c)) || c == '-') {
+ cfile->lexline = l;
+ cfile->lexchar = p;
+ ttok = read_number(c, cfile);
+ break;
+ } else if (isascii(c) && isalpha(c)) {
+ cfile->lexline = l;
+ cfile->lexchar = p;
+ ttok = read_num_or_name(c, cfile);
+ break;
+ } else if (c == EOF) {
+ ttok = END_OF_FILE;
+ cfile->tlen = 0;
+ break;
+ } else {
+ cfile->lexline = l;
+ cfile->lexchar = p;
+ tb[0] = c;
+ tb[1] = 0;
+ cfile->tval = tb;
+ cfile->tlen = 1;
+ ttok = c;
+ break;
+ }
+ }
+ return ttok;
+}
+
+/*
+ * The get_next_token() function consumes the next token and
+ * returns it to the caller.
+ *
+ * Since the code is almost the same for "normal" and "raw"
+ * input, we pass a flag to alter the way it works.
+ */
+
+static enum dhcp_token
+get_next_token(const char **rval, unsigned *rlen,
+ struct parse *cfile, isc_boolean_t raw) {
+ int rv;
+
+ if (cfile->token) {
+ if (cfile->lexline != cfile->tline)
+ cfile->token_line = cfile->cur_line;
+ cfile->lexchar = cfile->tlpos;
+ cfile->lexline = cfile->tline;
+ rv = cfile->token;
+ cfile->token = 0;
+ } else {
+ rv = get_raw_token(cfile);
+ cfile->token_line = cfile->cur_line;
+ }
+
+ if (!raw) {
+ while (rv == WHITESPACE) {
+ rv = get_raw_token(cfile);
+ cfile->token_line = cfile->cur_line;
+ }
+ }
+
+ if (rval)
+ *rval = cfile->tval;
+ if (rlen)
+ *rlen = cfile->tlen;
+ return rv;
+}
+
+/*
+ * Get the next token from cfile and return it.
+ *
+ * If rval is non-NULL, set the pointer it contains to
+ * the contents of the token.
+ *
+ * If rlen is non-NULL, set the integer it contains to
+ * the length of the token.
+ */
+
+enum dhcp_token
+next_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return get_next_token(rval, rlen, cfile, ISC_FALSE);
+}
+
+
+/*
+ * The same as the next_token() function above, but will return space
+ * as the WHITESPACE token.
+ */
+
+enum dhcp_token
+next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return get_next_token(rval, rlen, cfile, ISC_TRUE);
+}
+
+
+/*
+ * The do_peek_token() function checks the next token without
+ * consuming it, and returns it to the caller.
+ *
+ * Since the code is almost the same for "normal" and "raw"
+ * input, we pass a flag to alter the way it works. (See the
+ * warning in the GENERAL NOTES ABOUT TOKENS above though.)
+ */
+
+enum dhcp_token
+do_peek_token(const char **rval, unsigned int *rlen,
+ struct parse *cfile, isc_boolean_t raw) {
+ int x;
+
+ if (!cfile->token || (!raw && (cfile->token == WHITESPACE))) {
+ cfile->tlpos = cfile->lexchar;
+ cfile->tline = cfile->lexline;
+
+ do {
+ cfile->token = get_raw_token(cfile);
+ } while (!raw && (cfile->token == WHITESPACE));
+
+ if (cfile->lexline != cfile->tline)
+ cfile->token_line = cfile->prev_line;
+
+ x = cfile->lexchar;
+ cfile->lexchar = cfile->tlpos;
+ cfile->tlpos = x;
+
+ x = cfile->lexline;
+ cfile->lexline = cfile->tline;
+ cfile->tline = x;
+ }
+ if (rval)
+ *rval = cfile->tval;
+ if (rlen)
+ *rlen = cfile->tlen;
+ return cfile->token;
+}
+
+
+/*
+ * Get the next token from cfile and return it, leaving it for a
+ * subsequent call to next_token().
+ *
+ * Note that it WILL consume whitespace tokens.
+ *
+ * If rval is non-NULL, set the pointer it contains to
+ * the contents of the token.
+ *
+ * If rlen is non-NULL, set the integer it contains to
+ * the length of the token.
+ */
+
+enum dhcp_token
+peek_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return do_peek_token(rval, rlen, cfile, ISC_FALSE);
+}
+
+
+/*
+ * The same as the peek_token() function above, but will return space
+ * as the WHITESPACE token.
+ */
+
+enum dhcp_token
+peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return do_peek_token(rval, rlen, cfile, ISC_TRUE);
+}
+
+/*
+ * The comment up to but not including EOL is saved.
+ */
+
+static void
+skip_to_eol(struct parse *cfile)
+{
+ char buf[128];
+ unsigned cc = 0;
+ struct comment *comment;
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = '#';
+ for (;;) {
+ int c;
+
+ c = get_char(cfile);
+ if (c == EOF)
+ break;
+ if (c == EOL) {
+ break;
+ }
+ if (++cc < sizeof(buf) - 1)
+ buf[cc] = c;
+ }
+ comment = createComment(buf);
+ TAILQ_INSERT_TAIL(&cfile->comments, comment);
+}
+
+static enum dhcp_token
+read_whitespace(int c, struct parse *cfile) {
+ int ofs;
+
+ /*
+ * Read as much whitespace as we have available.
+ */
+ ofs = 0;
+ do {
+ if (ofs >= (sizeof(cfile->tokbuf) - 1)) {
+ /*
+ * As the file includes a huge amount of whitespace,
+ * it's probably broken.
+ * Print out a warning and bail out.
+ */
+ parse_error(cfile,
+ "whitespace too long, buffer overflow.");
+ }
+ cfile->tokbuf[ofs++] = c;
+ c = get_char(cfile);
+ if (c == EOF)
+ return END_OF_FILE;
+ } while (!((c == '\n') && cfile->eol_token) &&
+ isascii(c) && isspace(c));
+
+ /*
+ * Put the last (non-whitespace) character back.
+ */
+ unget_char(cfile, c);
+
+ /*
+ * Return our token.
+ */
+ cfile->tokbuf[ofs] = '\0';
+ cfile->tlen = ofs;
+ cfile->tval = cfile->tokbuf;
+ return WHITESPACE;
+}
+
+static enum dhcp_token
+read_string(struct parse *cfile)
+{
+ unsigned i;
+ int bs = 0;
+ int c;
+ int value = 0;
+ int hex = 0;
+
+ for (i = 0; i < sizeof(cfile->tokbuf); i++) {
+ again:
+ c = get_char(cfile);
+ if (c == EOF)
+ parse_error(cfile, "eof in string constant");
+ if (bs == 1) {
+ switch (c) {
+ case 't':
+ cfile->tokbuf[i] = '\t';
+ break;
+ case 'r':
+ cfile->tokbuf[i] = '\r';
+ break;
+ case 'n':
+ cfile->tokbuf[i] = '\n';
+ break;
+ case 'b':
+ cfile->tokbuf[i] = '\b';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ hex = 0;
+ value = c - '0';
+ ++bs;
+ goto again;
+ case 'x':
+ hex = 1;
+ value = 0;
+ ++bs;
+ goto again;
+ default:
+ cfile->tokbuf[i] = c;
+ break;
+ }
+ bs = 0;
+ } else if (bs > 1) {
+ if (hex) {
+ if (c >= '0' && c <= '9') {
+ value = value * 16 + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ value = value * 16 + (c - 'a' + 10);
+ } else if (c >= 'A' && c <= 'F') {
+ value = value * 16 + (c - 'A' + 10);
+ } else
+ parse_error(cfile,
+ "invalid hex digit: %x",
+ c);
+ if (++bs == 4) {
+ cfile->tokbuf[i] = value;
+ bs = 0;
+ } else
+ goto again;
+ } else {
+ if (c >= '0' && c <= '7') {
+ value = value * 8 + (c - '0');
+ } else {
+ if (value != 0)
+ parse_error(cfile,
+ "invalid octal digit %x",
+ c);
+ else
+ cfile->tokbuf[i] = 0;
+ bs = 0;
+ }
+ if (++bs == 4) {
+ cfile->tokbuf[i] = value;
+ bs = 0;
+ } else
+ goto again;
+ }
+ } else if (c == '\\') {
+ bs = 1;
+ goto again;
+ } else if (c == '"')
+ break;
+ else
+ cfile->tokbuf[i] = c;
+ }
+ /* Normally, I'd feel guilty about this, but we're talking about
+ strings that'll fit in a DHCP packet here... */
+ if (i == sizeof(cfile->tokbuf))
+ parse_error(cfile,
+ "string constant larger than internal buffer");
+ cfile->tokbuf[i] = 0;
+ cfile->tlen = i;
+ cfile->tval = cfile->tokbuf;
+ return STRING;
+}
+
+static enum dhcp_token
+read_number(int c, struct parse *cfile)
+{
+ unsigned i = 0;
+ int token = NUMBER;
+
+ cfile->tokbuf[i++] = c;
+ for (; i < sizeof(cfile->tokbuf); i++) {
+ c = get_char(cfile);
+
+ /* Promote NUMBER->NUMBER_OR_NAME->NAME, never demote.
+ * Except in the case of '0x' syntax hex, which gets called
+ * a NAME at '0x', and returned to NUMBER_OR_NAME once it's
+ * verified to be at least 0xf or less.
+ */
+ switch (isascii(c) ? token : BREAK) {
+ case NUMBER:
+ if (isdigit(c))
+ break;
+ /* FALLTHROUGH */
+ case NUMBER_OR_NAME:
+ if (isxdigit(c)) {
+ token = NUMBER_OR_NAME;
+ break;
+ }
+ /* FALLTHROUGH */
+ case NAME:
+ if ((i == 2) && isxdigit(c) &&
+ (cfile->tokbuf[0] == '0') &&
+ ((cfile->tokbuf[1] == 'x') ||
+ (cfile->tokbuf[1] == 'X'))) {
+ token = NUMBER_OR_NAME;
+ break;
+ } else if (((c == '-') || (c == '_') || isalnum(c))) {
+ token = NAME;
+ break;
+ }
+ /* FALLTHROUGH */
+ case BREAK:
+ /* At this point c is either EOF or part of the next
+ * token. If not EOF, rewind the file one byte so
+ * the next token is read from there.
+ */
+ unget_char(cfile, c);
+ goto end_read;
+
+ default:
+ parse_error(cfile,
+ "read_number(): impossible case");
+ }
+
+ cfile->tokbuf[i] = c;
+ }
+
+ if (i == sizeof(cfile->tokbuf))
+ parse_error(cfile,
+ "numeric token larger than internal buffer");
+
+ end_read:
+ cfile->tokbuf[i] = 0;
+ cfile->tlen = i;
+ cfile->tval = cfile->tokbuf;
+
+ /*
+ * If this entire token from start to finish was "-", such as
+ * the middle parameter in "42 - 7", return just the MINUS token.
+ */
+ if ((i == 1) && (cfile->tokbuf[i] == '-'))
+ return MINUS;
+ else
+ return token;
+}
+
+static enum dhcp_token
+read_num_or_name(int c, struct parse *cfile)
+{
+ unsigned i = 0;
+ enum dhcp_token rv = NUMBER_OR_NAME;
+
+ cfile->tokbuf[i++] = c;
+ for (; i < sizeof(cfile->tokbuf); i++) {
+ c = get_char(cfile);
+ if (!isascii(c) ||
+ (c != '-' && c != '_' && !isalnum(c))) {
+ unget_char(cfile, c);
+ break;
+ }
+ if (!isxdigit(c))
+ rv = NAME;
+ cfile->tokbuf[i] = c;
+ }
+ if (i == sizeof(cfile->tokbuf))
+ parse_error(cfile, "token larger than internal buffer");
+ cfile->tokbuf[i] = 0;
+ cfile->tlen = i;
+ cfile->tval = cfile->tokbuf;
+ return intern(cfile->tval, rv);
+}
+
+static enum dhcp_token
+intern(char *atom, enum dhcp_token dfv) {
+ if (!isascii(atom[0]))
+ return dfv;
+
+ switch (tolower((unsigned char)atom[0])) {
+ case '-':
+ if (atom [1] == 0)
+ return MINUS;
+ break;
+
+ case 'a':
+ if (!strcasecmp(atom + 1, "bandoned"))
+ return TOKEN_ABANDONED;
+ if (!strcasecmp(atom + 1, "ctive"))
+ return TOKEN_ACTIVE;
+ if (!strncasecmp(atom + 1, "dd", 2)) {
+ if (atom[3] == '\0')
+ return TOKEN_ADD;
+ else if (!strcasecmp(atom + 3, "ress"))
+ return ADDRESS;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "fter"))
+ return AFTER;
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 'l')) {
+ if (!strcasecmp(atom + 2, "gorithm"))
+ return ALGORITHM;
+ if (!strcasecmp(atom + 2, "ias"))
+ return ALIAS;
+ if (isascii(atom[2]) &&
+ (tolower((unsigned char)atom[2]) == 'l')) {
+ if (atom[3] == '\0')
+ return ALL;
+ else if (!strcasecmp(atom + 3, "ow"))
+ return ALLOW;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "so"))
+ return TOKEN_ALSO;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 'n')) {
+ if (!strcasecmp(atom + 2, "d"))
+ return AND;
+ if (!strcasecmp(atom + 2, "ycast-mac"))
+ return ANYCAST_MAC;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "ppend"))
+ return APPEND;
+ if (!strcasecmp(atom + 1, "rray"))
+ return ARRAY;
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 't')) {
+ if (atom[2] == '\0')
+ return AT;
+ if (!strcasecmp(atom + 2, "sfp"))
+ return ATSFP;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "uthoring-byte-order"))
+ return AUTHORING_BYTE_ORDER;
+ if (!strncasecmp(atom + 1, "ut", 2)) {
+ if (isascii(atom[3]) &&
+ (tolower((unsigned char)atom[3]) == 'h')) {
+ if (!strncasecmp(atom + 4, "enticat", 7)) {
+ if (!strcasecmp(atom + 11, "ed"))
+ return AUTHENTICATED;
+ if (!strcasecmp(atom + 11, "ion"))
+ return AUTHENTICATION;
+ break;
+ }
+ if (!strcasecmp(atom + 4, "oritative"))
+ return AUTHORITATIVE;
+ break;
+ }
+ if (!strcasecmp(atom + 3, "o-partner-down"))
+ return AUTO_PARTNER_DOWN;
+ break;
+ }
+ break;
+ case 'b':
+ if (!strcasecmp(atom + 1, "ackup"))
+ return TOKEN_BACKUP;
+ if (!strcasecmp(atom + 1, "ootp"))
+ return TOKEN_BOOTP;
+ if (!strcasecmp(atom + 1, "inding"))
+ return BINDING;
+ if (!strcasecmp(atom + 1, "inary-to-ascii"))
+ return BINARY_TO_ASCII;
+ if (!strcasecmp(atom + 1, "ackoff-cutoff"))
+ return BACKOFF_CUTOFF;
+ if (!strcasecmp(atom + 1, "ooting"))
+ return BOOTING;
+ if (!strcasecmp(atom + 1, "oot-unknown-clients"))
+ return BOOT_UNKNOWN_CLIENTS;
+ if (!strcasecmp(atom + 1, "reak"))
+ return BREAK;
+ if (!strcasecmp(atom + 1, "illing"))
+ return BILLING;
+ if (!strcasecmp(atom + 1, "oolean"))
+ return BOOLEAN;
+ if (!strcasecmp(atom + 1, "alance"))
+ return BALANCE;
+ if (!strcasecmp(atom + 1, "ound"))
+ return BOUND;
+ if (!strcasecmp(atom+1, "ig-endian")) {
+ return TOKEN_BIG_ENDIAN;
+ }
+ break;
+ case 'c':
+ if (!strcasecmp(atom + 1, "ase"))
+ return CASE;
+ if (!strcasecmp(atom + 1, "heck"))
+ return CHECK;
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return CIADDR;
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'l') {
+ if (!strcasecmp(atom + 2, "ass"))
+ return CLASS;
+ if (!strncasecmp(atom + 2, "ient", 4)) {
+ if (!strcasecmp(atom + 6, "s"))
+ return CLIENTS;
+ if (atom[6] == '-') {
+ if (!strcasecmp(atom + 7, "hostname"))
+ return CLIENT_HOSTNAME;
+ if (!strcasecmp(atom + 7, "identifier"))
+ return CLIENT_IDENTIFIER;
+ if (!strcasecmp(atom + 7, "state"))
+ return CLIENT_STATE;
+ if (!strcasecmp(atom + 7, "updates"))
+ return CLIENT_UPDATES;
+ break;
+ }
+ break;
+ }
+ if (!strcasecmp(atom + 2, "ose"))
+ return TOKEN_CLOSE;
+ if (!strcasecmp(atom + 2, "tt"))
+ return CLTT;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'o') {
+ if (!strcasecmp(atom + 2, "de"))
+ return CODE;
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'm') {
+ if (!strcasecmp(atom + 3, "mit"))
+ return COMMIT;
+ if (!strcasecmp(atom + 3,
+ "munications-interrupted"))
+ return COMMUNICATIONS_INTERRUPTED;
+ if (!strcasecmp(atom + 3, "pressed"))
+ return COMPRESSED;
+ break;
+ }
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'n') {
+ if (!strcasecmp(atom + 3, "cat"))
+ return CONCAT;
+ if (!strcasecmp(atom + 3, "fig-option"))
+ return CONFIG_OPTION;
+ if (!strcasecmp(atom + 3, "flict-done"))
+ return CONFLICT_DONE;
+ if (!strcasecmp(atom + 3, "nect"))
+ return CONNECT;
+ break;
+ }
+ break;
+ }
+ if (!strcasecmp(atom + 1, "reate"))
+ return TOKEN_CREATE;
+ break;
+ case 'd':
+ if (!strcasecmp(atom + 1, "b-time-format"))
+ return DB_TIME_FORMAT;
+ if (!strcasecmp(atom + 1, "omain"))
+ return DOMAIN;
+ if (!strncasecmp(atom + 1, "omain-", 6)) {
+ if (!strcasecmp(atom + 7, "name"))
+ return DOMAIN_NAME;
+ if (!strcasecmp(atom + 7, "list"))
+ return DOMAIN_LIST;
+ }
+ if (!strcasecmp(atom + 1, "o-forward-updates"))
+ return DO_FORWARD_UPDATE;
+ /* do-forward-update is included for historical reasons */
+ if (!strcasecmp(atom + 1, "o-forward-update"))
+ return DO_FORWARD_UPDATE;
+ if (!strcasecmp(atom + 1, "ebug"))
+ return TOKEN_DEBUG;
+ if (!strcasecmp(atom + 1, "eny"))
+ return DENY;
+ if (!strcasecmp(atom + 1, "eleted"))
+ return TOKEN_DELETED;
+ if (!strcasecmp(atom + 1, "elete"))
+ return TOKEN_DELETE;
+ if (!strncasecmp(atom + 1, "efault", 6)) {
+ if (!atom [7])
+ return DEFAULT;
+ if (!strcasecmp(atom + 7, "-duid"))
+ return DEFAULT_DUID;
+ if (!strcasecmp(atom + 7, "-lease-time"))
+ return DEFAULT_LEASE_TIME;
+ break;
+ }
+ if (!strncasecmp(atom + 1, "ynamic", 6)) {
+ if (!atom [7])
+ return DYNAMIC;
+ if (!strncasecmp(atom + 7, "-bootp", 6)) {
+ if (!atom [13])
+ return DYNAMIC_BOOTP;
+ if (!strcasecmp(atom + 13, "-lease-cutoff"))
+ return DYNAMIC_BOOTP_LEASE_CUTOFF;
+ if (!strcasecmp(atom + 13, "-lease-length"))
+ return DYNAMIC_BOOTP_LEASE_LENGTH;
+ break;
+ }
+ }
+ if (!strcasecmp(atom + 1, "uplicates"))
+ return DUPLICATES;
+ if (!strcasecmp(atom + 1, "eclines"))
+ return DECLINES;
+ if (!strncasecmp(atom + 1, "efine", 5)) {
+ if (!strcasecmp(atom + 6, "d"))
+ return DEFINED;
+ if (!atom [6])
+ return DEFINE;
+ }
+ break;
+ case 'e':
+ if (isascii(atom [1]) &&
+ tolower((unsigned char)atom[1]) == 'x') {
+ if (!strcasecmp(atom + 2, "tract-int"))
+ return EXTRACT_INT;
+ if (!strcasecmp(atom + 2, "ists"))
+ return EXISTS;
+ if (!strcasecmp(atom + 2, "piry"))
+ return EXPIRY;
+ if (!strcasecmp(atom + 2, "pire"))
+ return EXPIRE;
+ if (!strcasecmp(atom + 2, "pired"))
+ return TOKEN_EXPIRED;
+ }
+ if (!strcasecmp(atom + 1, "ncode-int"))
+ return ENCODE_INT;
+ if (!strcasecmp(atom + 1, "poch"))
+ return EPOCH;
+ if (!strcasecmp(atom + 1, "thernet"))
+ return ETHERNET;
+ if (!strcasecmp(atom + 1, "nds"))
+ return ENDS;
+ if (!strncasecmp(atom + 1, "ls", 2)) {
+ if (!strcasecmp(atom + 3, "e"))
+ return ELSE;
+ if (!strcasecmp(atom + 3, "if"))
+ return ELSIF;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "rror"))
+ return ERROR;
+ if (!strcasecmp(atom + 1, "val"))
+ return EVAL;
+ if (!strcasecmp(atom + 1, "ncapsulate"))
+ return ENCAPSULATE;
+ if (!strcasecmp(atom + 1, "xecute"))
+ return EXECUTE;
+ if (!strcasecmp(atom+1, "n")) {
+ return EN;
+ }
+ break;
+ case 'f':
+ if (!strcasecmp(atom + 1, "atal"))
+ return FATAL;
+ if (!strcasecmp(atom + 1, "ilename"))
+ return FILENAME;
+ if (!strcasecmp(atom + 1, "ixed-address"))
+ return FIXED_ADDR;
+ if (!strcasecmp(atom + 1, "ixed-address6"))
+ return FIXED_ADDR6;
+ if (!strcasecmp(atom + 1, "ixed-prefix6"))
+ return FIXED_PREFIX6;
+ if (!strcasecmp(atom + 1, "ddi"))
+ return TOKEN_FDDI;
+ if (!strcasecmp(atom + 1, "ormerr"))
+ return NS_FORMERR;
+ if (!strcasecmp(atom + 1, "unction"))
+ return FUNCTION;
+ if (!strcasecmp(atom + 1, "ailover"))
+ return FAILOVER;
+ if (!strcasecmp(atom + 1, "ree"))
+ return TOKEN_FREE;
+ break;
+ case 'g':
+ if (!strncasecmp(atom + 1, "et", 2)) {
+ if (!strcasecmp(atom + 3, "-lease-hostnames"))
+ return GET_LEASE_HOSTNAMES;
+ if (!strcasecmp(atom + 3, "hostbyname"))
+ return GETHOSTBYNAME;
+ if (!strcasecmp(atom + 3, "hostname"))
+ return GETHOSTNAME;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return GIADDR;
+ if (!strcasecmp(atom + 1, "roup"))
+ return GROUP;
+ break;
+ case 'h':
+ if (!strcasecmp(atom + 1, "ash"))
+ return HASH;
+ if (!strcasecmp(atom + 1, "ba"))
+ return HBA;
+ if (!strcasecmp(atom + 1, "ost"))
+ return HOST;
+ if (!strcasecmp(atom + 1, "ost-decl-name"))
+ return HOST_DECL_NAME;
+ if (!strcasecmp(atom + 1, "ost-identifier"))
+ return HOST_IDENTIFIER;
+ if (!strcasecmp(atom + 1, "ardware"))
+ return HARDWARE;
+ if (!strcasecmp(atom + 1, "ostname"))
+ return HOSTNAME;
+ if (!strcasecmp(atom + 1, "elp"))
+ return TOKEN_HELP;
+ if (!strcasecmp(atom + 1, "ex")) {
+ return TOKEN_HEX;
+ }
+ break;
+ case 'i':
+ if (!strcasecmp(atom+1, "a-na"))
+ return IA_NA;
+ if (!strcasecmp(atom+1, "a-ta"))
+ return IA_TA;
+ if (!strcasecmp(atom+1, "a-pd"))
+ return IA_PD;
+ if (!strcasecmp(atom+1, "aaddr"))
+ return IAADDR;
+ if (!strcasecmp(atom+1, "aprefix"))
+ return IAPREFIX;
+ if (!strcasecmp(atom + 1, "nclude"))
+ return INCLUDE;
+ if (!strcasecmp(atom + 1, "nteger"))
+ return INTEGER;
+ if (!strcasecmp(atom + 1, "nfiniband"))
+ return TOKEN_INFINIBAND;
+ if (!strcasecmp(atom + 1, "nfinite"))
+ return INFINITE;
+ if (!strcasecmp(atom + 1, "nfo"))
+ return INFO;
+ if (!strcasecmp(atom + 1, "p-address"))
+ return IP_ADDRESS;
+ if (!strcasecmp(atom + 1, "p6-address"))
+ return IP6_ADDRESS;
+ if (!strcasecmp(atom + 1, "nitial-interval"))
+ return INITIAL_INTERVAL;
+ if (!strcasecmp(atom + 1, "nitial-delay"))
+ return INITIAL_DELAY;
+ if (!strcasecmp(atom + 1, "nterface"))
+ return INTERFACE;
+ if (!strcasecmp(atom + 1, "dentifier"))
+ return IDENTIFIER;
+ if (!strcasecmp(atom + 1, "f"))
+ return IF;
+ if (!strcasecmp(atom + 1, "s"))
+ return IS;
+ if (!strcasecmp(atom + 1, "gnore"))
+ return IGNORE;
+ break;
+ case 'k':
+ if (!strncasecmp(atom + 1, "nown", 4)) {
+ if (!strcasecmp(atom + 5, "-clients"))
+ return KNOWN_CLIENTS;
+ if (!atom[5])
+ return KNOWN;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "ey"))
+ return KEY;
+ if (!strcasecmp (atom + 1, "ey-algorithm"))
+ return KEY_ALGORITHM;
+ break;
+ case 'l':
+ if (!strcasecmp(atom + 1, "case"))
+ return LCASE;
+ if (!strcasecmp(atom + 1, "ease"))
+ return LEASE;
+ if (!strcasecmp(atom + 1, "ease6"))
+ return LEASE6;
+ if (!strcasecmp(atom + 1, "eased-address"))
+ return LEASED_ADDRESS;
+ if (!strcasecmp(atom + 1, "ease-time"))
+ return LEASE_TIME;
+ if (!strcasecmp(atom + 1, "easequery"))
+ return LEASEQUERY;
+ if (!strcasecmp(atom + 1, "ength"))
+ return LENGTH;
+ if (!strcasecmp(atom + 1, "imit"))
+ return LIMIT;
+ if (!strcasecmp(atom + 1, "et"))
+ return LET;
+ if (!strcasecmp(atom + 1, "oad"))
+ return LOAD;
+ if (!strcasecmp(atom + 1, "ocal"))
+ return LOCAL;
+ if (!strcasecmp(atom + 1, "og"))
+ return LOG;
+ if (!strcasecmp(atom+1, "lt")) {
+ return LLT;
+ }
+ if (!strcasecmp(atom+1, "l")) {
+ return LL;
+ }
+ if (!strcasecmp(atom+1, "ittle-endian")) {
+ return TOKEN_LITTLE_ENDIAN;
+ }
+ if (!strcasecmp(atom + 1, "ease-id-format")) {
+ return LEASE_ID_FORMAT;
+ }
+ break;
+ case 'm':
+ if (!strncasecmp(atom + 1, "ax", 2)) {
+ if (!atom [3])
+ return TOKEN_MAX;
+ if (!strcasecmp(atom + 3, "-balance"))
+ return MAX_BALANCE;
+ if (!strncasecmp(atom + 3, "-lease-", 7)) {
+ if (!strcasecmp(atom + 10, "misbalance"))
+ return MAX_LEASE_MISBALANCE;
+ if (!strcasecmp(atom + 10, "ownership"))
+ return MAX_LEASE_OWNERSHIP;
+ if (!strcasecmp(atom + 10, "time"))
+ return MAX_LEASE_TIME;
+ }
+ if (!strcasecmp(atom + 3, "-life"))
+ return MAX_LIFE;
+ if (!strcasecmp(atom + 3, "-transmit-idle"))
+ return MAX_TRANSMIT_IDLE;
+ if (!strcasecmp(atom + 3, "-response-delay"))
+ return MAX_RESPONSE_DELAY;
+ if (!strcasecmp(atom + 3, "-unacked-updates"))
+ return MAX_UNACKED_UPDATES;
+ }
+ if (!strncasecmp(atom + 1, "in-", 3)) {
+ if (!strcasecmp(atom + 4, "balance"))
+ return MIN_BALANCE;
+ if (!strcasecmp(atom + 4, "lease-time"))
+ return MIN_LEASE_TIME;
+ if (!strcasecmp(atom + 4, "secs"))
+ return MIN_SECS;
+ break;
+ }
+ if (!strncasecmp(atom + 1, "edi", 3)) {
+ if (!strcasecmp(atom + 4, "a"))
+ return MEDIA;
+ if (!strcasecmp(atom + 4, "um"))
+ return MEDIUM;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "atch"))
+ return MATCH;
+ if (!strcasecmp(atom + 1, "embers"))
+ return MEMBERS;
+ if (!strcasecmp(atom + 1, "y"))
+ return MY;
+ if (!strcasecmp(atom + 1, "clt"))
+ return MCLT;
+ break;
+ case 'n':
+ if (!strcasecmp(atom + 1, "ormal"))
+ return NORMAL;
+ if (!strcasecmp(atom + 1, "ameserver"))
+ return NAMESERVER;
+ if (!strcasecmp(atom + 1, "etmask"))
+ return NETMASK;
+ if (!strcasecmp(atom + 1, "ever"))
+ return NEVER;
+ if (!strcasecmp(atom + 1, "ext-server"))
+ return NEXT_SERVER;
+ if (!strcasecmp(atom + 1, "ot"))
+ return TOKEN_NOT;
+ if (!strcasecmp(atom + 1, "o"))
+ return TOKEN_NO;
+ if (!strcasecmp(atom + 1, "oerror"))
+ return NS_NOERROR;
+ if (!strcasecmp(atom + 1, "otauth"))
+ return NS_NOTAUTH;
+ if (!strcasecmp(atom + 1, "otimp"))
+ return NS_NOTIMP;
+ if (!strcasecmp(atom + 1, "otzone"))
+ return NS_NOTZONE;
+ if (!strcasecmp(atom + 1, "xdomain"))
+ return NS_NXDOMAIN;
+ if (!strcasecmp(atom + 1, "xrrset"))
+ return NS_NXRRSET;
+ if (!strcasecmp(atom + 1, "ull"))
+ return TOKEN_NULL;
+ if (!strcasecmp(atom + 1, "ext"))
+ return TOKEN_NEXT;
+ if (!strcasecmp(atom + 1, "ew"))
+ return TOKEN_NEW;
+ break;
+ case 'o':
+ if (!strcasecmp(atom + 1, "mapi"))
+ return OMAPI;
+ if (!strcasecmp(atom + 1, "r"))
+ return OR;
+ if (!strcasecmp(atom + 1, "n"))
+ return ON;
+ if (!strcasecmp(atom + 1, "pen"))
+ return TOKEN_OPEN;
+ if (!strcasecmp(atom + 1, "ption"))
+ return OPTION;
+ if (!strcasecmp(atom + 1, "ne-lease-per-client"))
+ return ONE_LEASE_PER_CLIENT;
+ if (!strcasecmp(atom + 1, "f"))
+ return OF;
+ if (!strcasecmp(atom + 1, "wner"))
+ return OWNER;
+ if (!strcasecmp(atom + 1, "ctal")) {
+ return TOKEN_OCTAL;
+ }
+ break;
+ case 'p':
+ if (!strcasecmp(atom + 1, "arse-vendor-option"))
+ return PARSE_VENDOR_OPT;
+ if (!strcasecmp(atom + 1, "repend"))
+ return PREPEND;
+ if (!strcasecmp(atom + 1, "referred-life"))
+ return PREFERRED_LIFE;
+ if (!strcasecmp(atom + 1, "acket"))
+ return PACKET;
+ if (!strcasecmp(atom + 1, "ool"))
+ return POOL;
+ if (!strcasecmp(atom + 1, "ool6"))
+ return POOL6;
+ if (!strcasecmp(atom + 1, "refix6"))
+ return PREFIX6;
+ if (!strcasecmp(atom + 1, "seudo"))
+ return PSEUDO;
+ if (!strcasecmp(atom + 1, "eer"))
+ return PEER;
+ if (!strcasecmp(atom + 1, "rimary"))
+ return PRIMARY;
+ if (!strcasecmp(atom + 1, "rimary6"))
+ return PRIMARY6;
+ if (!strncasecmp(atom + 1, "artner", 6)) {
+ if (!atom [7])
+ return PARTNER;
+ if (!strcasecmp(atom + 7, "-down"))
+ return PARTNER_DOWN;
+ }
+ if (!strcasecmp(atom + 1, "ort"))
+ return PORT;
+ if (!strcasecmp(atom + 1, "otential-conflict"))
+ return POTENTIAL_CONFLICT;
+ if (!strcasecmp(atom + 1, "ick-first-value") ||
+ !strcasecmp(atom + 1, "ick"))
+ return PICK;
+ if (!strcasecmp(atom + 1, "aused"))
+ return PAUSED;
+ break;
+ case 'r':
+ if (!strcasecmp(atom + 1, "ange"))
+ return RANGE;
+ if (!strcasecmp(atom + 1, "ange6"))
+ return RANGE6;
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 'e')) {
+ if (!strcasecmp(atom + 2, "bind"))
+ return REBIND;
+ if (!strcasecmp(atom + 2, "boot"))
+ return REBOOT;
+ if (!strcasecmp(atom + 2, "contact-interval"))
+ return RECONTACT_INTERVAL;
+ if (!strncasecmp(atom + 2, "cover", 5)) {
+ if (atom[7] == '\0')
+ return RECOVER;
+ if (!strcasecmp(atom + 7, "-done"))
+ return RECOVER_DONE;
+ if (!strcasecmp(atom + 7, "-wait"))
+ return RECOVER_WAIT;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "fresh"))
+ return REFRESH;
+ if (!strcasecmp(atom + 2, "fused"))
+ return NS_REFUSED;
+ if (!strcasecmp(atom + 2, "ject"))
+ return REJECT;
+ if (!strcasecmp(atom + 2, "lease"))
+ return RELEASE;
+ if (!strcasecmp(atom + 2, "leased"))
+ return TOKEN_RELEASED;
+ if (!strcasecmp(atom + 2, "move"))
+ return REMOVE;
+ if (!strcasecmp(atom + 2, "new"))
+ return RENEW;
+ if (!strcasecmp(atom + 2, "quest"))
+ return REQUEST;
+ if (!strcasecmp(atom + 2, "quire"))
+ return REQUIRE;
+ if (isascii(atom[2]) &&
+ (tolower((unsigned char)atom[2]) == 's')) {
+ if (!strcasecmp(atom + 3, "erved"))
+ return TOKEN_RESERVED;
+ if (!strcasecmp(atom + 3, "et"))
+ return TOKEN_RESET;
+ if (!strcasecmp(atom + 3,
+ "olution-interrupted"))
+ return RESOLUTION_INTERRUPTED;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "try"))
+ return RETRY;
+ if (!strcasecmp(atom + 2, "turn"))
+ return RETURN;
+ if (!strcasecmp(atom + 2, "verse"))
+ return REVERSE;
+ if (!strcasecmp(atom + 2, "wind"))
+ return REWIND;
+ break;
+ }
+ break;
+ case 's':
+ if (!strcasecmp(atom + 1, "cript"))
+ return SCRIPT;
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'e') {
+ if (!strcasecmp(atom + 2, "arch"))
+ return SEARCH;
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'c') {
+ if (!strncasecmp(atom + 3, "ond", 3)) {
+ if (!strcasecmp(atom + 6, "ary"))
+ return SECONDARY;
+ if (!strcasecmp(atom + 6, "ary6"))
+ return SECONDARY6;
+ if (!strcasecmp(atom + 6, "s"))
+ return SECONDS;
+ break;
+ }
+ if (!strcasecmp(atom + 3, "ret"))
+ return SECRET;
+ break;
+ }
+ if (!strncasecmp(atom + 2, "lect", 4)) {
+ if (atom[6] == '\0')
+ return SELECT;
+ if (!strcasecmp(atom + 6, "-timeout"))
+ return SELECT_TIMEOUT;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "nd"))
+ return SEND;
+ if (!strncasecmp(atom + 2, "rv", 2)) {
+ if (!strncasecmp(atom + 4, "er", 2)) {
+ if (atom[6] == '\0')
+ return TOKEN_SERVER;
+ if (atom[6] == '-') {
+ if (!strcasecmp(atom + 7,
+ "duid"))
+ return SERVER_DUID;
+ if (!strcasecmp(atom + 7,
+ "name"))
+ return SERVER_NAME;
+ if (!strcasecmp(atom + 7,
+ "identifier"))
+ return SERVER_IDENTIFIER;
+ break;
+ }
+ break;
+ }
+ if (!strcasecmp(atom + 4, "fail"))
+ return NS_SERVFAIL;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "t"))
+ return TOKEN_SET;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'h') {
+ if (!strcasecmp(atom + 2, "ared-network"))
+ return SHARED_NETWORK;
+ if (!strcasecmp(atom + 2, "utdown"))
+ return SHUTDOWN;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'i') {
+ if (!strcasecmp(atom + 2, "addr"))
+ return SIADDR;
+ if (!strcasecmp(atom + 2, "gned"))
+ return SIGNED;
+ if (!strcasecmp(atom + 2, "ze"))
+ return SIZE;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'p') {
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'a') {
+ if (!strcasecmp(atom + 3, "ce"))
+ return SPACE;
+ if (!strcasecmp(atom + 3, "wn"))
+ return SPAWN;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "lit"))
+ return SPLIT;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 't') {
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'a') {
+ if (!strncasecmp(atom + 3, "rt", 2)) {
+ if (!strcasecmp(atom + 5, "s"))
+ return STARTS;
+ if (!strcasecmp(atom + 5, "up"))
+ return STARTUP;
+ break;
+ }
+ if (isascii(atom[3]) &&
+ tolower((unsigned char)atom[3]) == 't') {
+ if (!strcasecmp(atom + 4, "e"))
+ return STATE;
+ if (!strcasecmp(atom + 4, "ic"))
+ return STATIC;
+ break;
+ }
+ }
+ if (!strcasecmp(atom + 2, "ring"))
+ return STRING_TOKEN;
+ break;
+ }
+ if (!strncasecmp(atom + 1, "ub", 2)) {
+ if (!strcasecmp(atom + 3, "class"))
+ return SUBCLASS;
+ if (!strcasecmp(atom + 3, "net"))
+ return SUBNET;
+ if (!strcasecmp(atom + 3, "net6"))
+ return SUBNET6;
+ if (!strcasecmp(atom + 3, "string"))
+ return SUBSTRING;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'u') {
+ if (!strcasecmp(atom + 2, "ffix"))
+ return SUFFIX;
+ if (!strcasecmp(atom + 2, "persede"))
+ return SUPERSEDE;
+ }
+ if (!strcasecmp(atom + 1, "witch"))
+ return SWITCH;
+ break;
+ case 't':
+ if (!strcasecmp(atom + 1, "imestamp"))
+ return TIMESTAMP;
+ if (!strcasecmp(atom + 1, "imeout"))
+ return TIMEOUT;
+ if (!strcasecmp(atom + 1, "oken-ring"))
+ return TOKEN_RING;
+ if (!strcasecmp(atom + 1, "ext"))
+ return TEXT;
+ if (!strcasecmp(atom + 1, "stp"))
+ return TSTP;
+ if (!strcasecmp(atom + 1, "sfp"))
+ return TSFP;
+ if (!strcasecmp(atom + 1, "ransmission"))
+ return TRANSMISSION;
+ if (!strcasecmp(atom + 1, "emporary"))
+ return TEMPORARY;
+ break;
+ case 'u':
+ if (!strcasecmp(atom + 1, "case"))
+ return UCASE;
+ if (!strcasecmp(atom + 1, "nset"))
+ return UNSET;
+ if (!strcasecmp(atom + 1, "nsigned"))
+ return UNSIGNED;
+ if (!strcasecmp(atom + 1, "id"))
+ return UID;
+ if (!strncasecmp(atom + 1, "se", 2)) {
+ if (!strcasecmp(atom + 3, "r-class"))
+ return USER_CLASS;
+ if (!strcasecmp(atom + 3, "-host-decl-names"))
+ return USE_HOST_DECL_NAMES;
+ if (!strcasecmp(atom + 3,
+ "-lease-addr-for-default-route"))
+ return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE;
+ break;
+ }
+ if (!strncasecmp(atom + 1, "nknown", 6)) {
+ if (!strcasecmp(atom + 7, "-clients"))
+ return UNKNOWN_CLIENTS;
+ if (!strcasecmp(atom + 7, "-state"))
+ return UNKNOWN_STATE;
+ if (!atom [7])
+ return UNKNOWN;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "nauthenticated"))
+ return UNAUTHENTICATED;
+ if (!strcasecmp(atom + 1, "pdate"))
+ return UPDATE;
+ break;
+ case 'v':
+ if (!strcasecmp(atom + 1, "6relay"))
+ return V6RELAY;
+ if (!strcasecmp(atom + 1, "6relopt"))
+ return V6RELOPT;
+ if (!strcasecmp(atom + 1, "endor-class"))
+ return VENDOR_CLASS;
+ if (!strcasecmp(atom + 1, "endor"))
+ return VENDOR;
+ break;
+ case 'w':
+ if (!strcasecmp(atom + 1, "ith"))
+ return WITH;
+ if (!strcasecmp(atom + 1, "idth"))
+ return WIDTH;
+ break;
+ case 'y':
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return YIADDR;
+ if (!strcasecmp(atom + 1, "xdomain"))
+ return NS_YXDOMAIN;
+ if (!strcasecmp(atom + 1, "xrrset"))
+ return NS_YXRRSET;
+ break;
+ case 'z':
+ if (!strcasecmp(atom + 1, "erolen"))
+ return ZEROLEN;
+ if (!strcasecmp(atom + 1, "one"))
+ return ZONE;
+ break;
+ }
+ return dfv;
+}
+
diff --git a/keama/confparse.c b/keama/confparse.c
new file mode 100644
index 00000000..5b916064
--- /dev/null
+++ b/keama/confparse.c
@@ -0,0 +1,4992 @@
+/*
+ * Copyright (c) 2017, 2018 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+/* From server/confpars.c */
+
+#include "keama.h"
+
+#include <sys/errno.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Print failover stuff once */
+isc_boolean_t failover_once = ISC_TRUE;
+
+/* To manage host-reservation-identifiers */
+isc_boolean_t use_client_id = ISC_FALSE;
+isc_boolean_t use_flex_id = ISC_FALSE;
+isc_boolean_t use_hw_address = ISC_FALSE;
+
+/* option and relays used for flexible host identifier */
+const struct option *host_id_option = NULL;
+int host_id_relays = 0;
+
+/* Simple or complex config */
+unsigned subnet_counter = 0;
+
+/* For subclass name generation */
+unsigned subclass_counter = 0;
+
+/* To map reservations to declared subnets */
+struct subnet {
+ struct element *subnet;
+ struct element *share;
+ struct string *addr;
+ struct string *mask;
+ TAILQ_ENTRY(subnet) next;
+};
+
+TAILQ_HEAD(subnets, subnet) known_subnets;
+
+/* To map pools to subnets inside a shared-network */
+struct range {
+ struct element *pool;
+ struct element *share;
+ struct string *low;
+ TAILQ_ENTRY(range) next;
+};
+
+TAILQ_HEAD(ranges, range) known_ranges;
+
+static void post_process_lifetimes(struct parse *);
+static size_t post_process_reservations(struct parse *);
+static void post_process_classes(struct parse *);
+static void post_process_generated_classes(struct parse *);
+static void check_depend(struct element *, struct element *);
+static void post_process_option_definitions(struct parse *);
+static void add_host_reservation_identifiers(struct parse *, const char *);
+static void add_host_id_option(struct parse *, const struct option *, int);
+static void subclass_inherit(struct parse *, struct element *,
+ struct element *);
+static void add_match_class(struct parse *, struct element *,
+ struct element *);
+static void option_data_derive(struct parse *, struct handle *,
+ struct handle *);
+static void derive_classes(struct parse *, struct handle *, struct handle *);
+static isc_boolean_t is_hexa_only(const char *, unsigned len);
+static void new_network_interface(struct parse *, struct element *);
+static struct string *addrmask(const struct string *, const struct string *);
+static struct element *find_match(struct parse *, struct element *,
+ isc_boolean_t *);
+static struct element *find_location(struct element *, struct range *);
+static int get_prefix_length(const char *, const char *);
+static struct element *get_class(struct parse *, struct element *);
+static void concat_classes(struct parse *, struct element *, struct element *);
+static void generate_class(struct parse *, struct element *, struct element *,
+ struct element *);
+
+static struct string *CLASS_ALL;
+static struct string *CLASS_KNOWN;
+
+/* Add head config file comments to the DHCP server map */
+
+size_t
+conf_file_parse(struct parse *cfile)
+{
+ struct element *top;
+ struct element *dhcp;
+ size_t issues;
+
+ TAILQ_INIT(&known_subnets);
+ TAILQ_INIT(&known_ranges);
+ CLASS_ALL = makeString(-1, "ALL");
+ CLASS_KNOWN = makeString(-1, "KNOWN");
+
+ top = createMap();
+ top->kind = TOPLEVEL;
+ TAILQ_CONCAT(&top->comments, &cfile->comments);
+
+ dhcp = createMap();
+ dhcp->kind = ROOT_GROUP;
+ (void) peek_token(NULL, NULL, cfile);
+ TAILQ_CONCAT(&dhcp->comments, &cfile->comments);
+ stackPush(cfile, dhcp);
+ assert(cfile->stack_top == 1);
+ cfile->stack[0] = top;
+
+ if (local_family == AF_INET)
+ mapSet(top, dhcp, "Dhcp4");
+ else if (local_family == AF_INET6)
+ mapSet(top, dhcp, "Dhcp6");
+ else
+ parse_error(cfile, "address family is not set");
+
+ issues = conf_file_subparse(cfile, ROOT_GROUP);
+
+ /* Add a warning when interfaces-config is not present */
+ if (subnet_counter > 0) {
+ struct element *ifconf;
+
+ ifconf = mapGet(cfile->stack[1], "interfaces-config");
+ if (ifconf == NULL) {
+ struct comment *comment;
+
+ comment = createComment("/// This configuration "
+ "declares some subnets but "
+ "has no interfaces-config");
+ TAILQ_INSERT_TAIL(&cfile->stack[1]->comments, comment);
+ comment = createComment("/// Reference Kea #245");
+ TAILQ_INSERT_TAIL(&cfile->stack[1]->comments, comment);
+ }
+ }
+
+ post_process_lifetimes(cfile);
+ if (!global_hr)
+ issues += post_process_reservations(cfile);
+ post_process_classes(cfile);
+ post_process_generated_classes(cfile);
+ post_process_option_definitions(cfile);
+
+ return issues;
+}
+
+/* Lifetime post-processing */
+static void
+post_process_lifetimes(struct parse *cfile)
+{
+ struct element *entry;
+
+ entry = mapGet(cfile->stack[1], "valid-lifetime");
+ if ((entry == NULL) && use_isc_lifetimes) {
+ struct comment *comment;
+
+ /* DEFAULT_DEFAULT_LEASE_TIME is 43200 */
+ entry = createInt(43200);
+ comment = createComment("/// Use ISC DHCP default lifetime");
+ TAILQ_INSERT_TAIL(&entry->comments, comment);
+ mapSet(cfile->stack[1], entry, "valid-lifetime");
+ }
+
+ entry = mapGet(cfile->stack[1], "min-valid-lifetime");
+ if ((entry == NULL) && use_isc_lifetimes) {
+ struct comment *comment;
+
+ /* DEFAULT_MIN_LEASE_TIME is 300 */
+ entry = createInt(300);
+ comment = createComment("/// Use ISC DHCP min lifetime");
+ TAILQ_INSERT_TAIL(&entry->comments, comment);
+ mapSet(cfile->stack[1], entry, "min-valid-lifetime");
+ }
+
+ entry = mapGet(cfile->stack[1], "max-valid-lifetime");
+ if ((entry == NULL) && use_isc_lifetimes) {
+ struct comment *comment;
+
+ /* DEFAULT_MAX_LEASE_TIME is 86400 */
+ entry = createInt(86400);
+ comment = createComment("/// Use ISC DHCP max lifetime");
+ TAILQ_INSERT_TAIL(&entry->comments, comment);
+ mapSet(cfile->stack[1], entry, "max-valid-lifetime");
+ }
+
+ /* Done for DHCPv4 */
+ if (local_family == AF_INET)
+ return;
+
+ /* There is no builtin default for preferred-lifetime,
+ nor min/max values in ISC DHCP. */
+}
+
+/* Reservation post-processing */
+
+static size_t
+post_process_reservations(struct parse *cfile)
+{
+ struct element *hosts;
+ struct element *orphans;
+ struct element *host;
+ struct element *where;
+ struct element *dest;
+ isc_boolean_t used_heuristic;
+ size_t issues;
+
+ issues = 0;
+ hosts = mapGet(cfile->stack[1], "reservations");
+ if ((hosts == NULL) || global_hr)
+ return issues;
+ mapRemove(cfile->stack[1], "reservations");
+ orphans = createList();
+ orphans->kind = HOST_DECL;
+ while (listSize(hosts) > 0) {
+ host = listGet(hosts, 0);
+ listRemove(hosts, 0);
+ used_heuristic = ISC_FALSE;
+ where = find_match(cfile, host, &used_heuristic);
+ if (where == cfile->stack[1])
+ dest = orphans;
+ else
+ dest = mapGet(where, "reservations");
+ if (dest == NULL) {
+ dest = createList();
+ dest->kind = HOST_DECL;
+ mapSet(where, dest, "reservations");
+ }
+ listPush(dest, host);
+ }
+ if (listSize(orphans) > 0) {
+ struct comment *comment;
+
+ comment = createComment("/// Orphan reservations");
+ TAILQ_INSERT_TAIL(&orphans->comments, comment);
+ comment = createComment("/// Kea reservations are per subnet");
+ TAILQ_INSERT_TAIL(&orphans->comments, comment);
+ comment = createComment("/// Reference Kea #231");
+ TAILQ_INSERT_TAIL(&orphans->comments, comment);
+ orphans->skip = ISC_TRUE;
+ issues++;
+ mapSet(cfile->stack[1], orphans, "reservations");
+ }
+ return issues;
+}
+
+/* Cleanup classes */
+
+static void
+post_process_classes(struct parse *cfile)
+{
+ struct element *classes;
+ struct element *class;
+ struct element *name;
+ struct element *entry;
+ struct element *reduced;
+ struct string *msg;
+ struct comment *comment;
+ isc_boolean_t lose;
+ size_t i;
+
+ classes = mapGet(cfile->stack[1], "client-classes");
+ if ((classes == NULL) || (listSize(classes) == 0))
+ return;
+ for (i = 0; i < listSize(classes); i++) {
+ class = listGet(classes, i);
+ if ((class == NULL) || (class->type != ELEMENT_MAP))
+ parse_error(cfile, "null global class at %i",
+ (unsigned)i);
+ name = mapGet(class, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING))
+ parse_error(cfile, "global class at %u "
+ "without a name", (unsigned)i);
+ if (!mapContains(class, "super"))
+ goto cleanup_superclass;
+
+ /* cleanup subclass */
+ mapRemove(class,"super");
+ entry = mapGet(class, "string");
+ if (entry != NULL) {
+ if (entry->type != ELEMENT_STRING)
+ parse_error(cfile, "subclass %s has "
+ "a bad string selector",
+ stringValue(name)->content);
+ msg = makeString(-1, "/// subclass selector ");
+ appendString(msg, "'");
+ concatString(msg, stringValue(entry));
+ appendString(msg, "'");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&class->comments, comment);
+ mapRemove(class, "string");
+ continue;
+ }
+ entry = mapGet(class, "binary");
+ if (entry == NULL)
+ parse_error(cfile, "subclass %s has no selector",
+ stringValue(name)->content);
+ msg = makeString(-1, "/// subclass selector 0x");
+ concatString(msg, stringValue(entry));
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&class->comments, comment);
+ mapRemove(class, "binary");
+
+ cleanup_superclass:
+ /* cleanup superclass */
+ entry = mapGet(class, "spawning");
+ if (entry == NULL)
+ goto cleanup_class;
+ if (entry->type != ELEMENT_BOOLEAN)
+ parse_error(cfile, "superclass %s has bad "
+ "spawning flag",
+ stringValue(name)->content);
+ if (boolValue(entry)) {
+ msg = makeString(-1, "/// Spawning classes "
+ "are not supported by Kea");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&class->comments, comment);
+ msg = makeString(-1, "/// Reference Kea #248");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&class->comments, comment);
+ msg = makeString(-1, "/// spawn with: ");
+ } else
+ msg = makeString(-1, "/// match: ");
+ entry = mapGet(class, "submatch");
+
+ if (entry == NULL)
+ parse_error(cfile, "superclass %s has no submatch",
+ stringValue(name)->content);
+ lose = ISC_FALSE;
+ appendString(msg, print_data_expression(entry, &lose));
+ if (!lose) {
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&class->comments, comment);
+ mapRemove(class, "spawning");
+ mapRemove(class, "submatch");
+ }
+
+ cleanup_class:
+ /* cleanup class */
+ entry = mapGet(class, "match-if");
+ if (entry == NULL)
+ continue;
+ reduced = mapGet(class, "test");
+ lose = ISC_FALSE;
+ if (reduced != NULL)
+ msg = makeString(-1, "/// from: match if ");
+ else
+ msg = makeString(-1, "/// match if ");
+ appendString(msg, print_boolean_expression(entry, &lose));
+ if (!lose) {
+ comment = createComment(msg->content);
+ if (reduced != NULL) {
+ TAILQ_INSERT_TAIL(&reduced->comments, comment);
+ mapRemove(class, "match-if");
+ continue;
+ }
+ TAILQ_INSERT_TAIL(&entry->comments, comment);
+ }
+ }
+}
+
+/* Move generated client classes to the end of client class list */
+
+static void
+post_process_generated_classes(struct parse *cfile)
+{
+ struct element *generated;
+ struct element *classes;
+ struct element *class;
+
+ generated = mapGet(cfile->stack[1], "generated-classes");
+ if (generated == NULL)
+ return;
+ mapRemove(cfile->stack[1], "generated-classes");
+ if (listSize(generated) == 0)
+ return;
+ classes = mapGet(cfile->stack[1], "client-classes");
+ if (classes == NULL) {
+ classes = createList();
+ mapSet(cfile->stack[1], classes, "client-classes");
+ }
+
+ while (listSize(generated) > 0) {
+ class = listGet(generated, 0);
+ listRemove(generated, 0);
+ check_depend(class, classes);
+ listPush(classes, class);
+ }
+}
+
+static void
+check_depend(struct element *class, struct element *classes)
+{
+ struct element *list;
+
+ if (!mapContains(class, "depend"))
+ return;
+ list = mapGet(class, "depend");
+ mapRemove(class, "depend");
+ while (listSize(list) > 0) {
+ struct element *depend;
+ struct string *dname;
+ struct string *msg;
+ struct comment *comment;
+ isc_boolean_t found;
+ size_t i;
+
+ depend = listGet(list, 0);
+ listRemove(list, 0);
+ assert(depend != NULL);
+ assert(depend->type == ELEMENT_STRING);
+ dname = stringValue(depend);
+ if (eqString(dname, CLASS_ALL) ||
+ eqString(dname, CLASS_KNOWN))
+ continue;
+ found = ISC_FALSE;
+ for (i = 0; i < listSize(classes); i++) {
+ struct element *item;
+ struct element *name;
+
+ item = listGet(classes, i);
+ assert(item != NULL);
+ assert(item->type == ELEMENT_MAP);
+ name = mapGet(item, "name");
+ if (name == NULL)
+ continue;
+ assert(name->type == ELEMENT_STRING);
+ if (eqString(stringValue(name), dname)) {
+ found = ISC_TRUE;
+ break;
+ }
+ }
+ if (found)
+ continue;
+ msg = makeString(-1, "/// Depend on missing '");
+ concatString(msg, dname);
+ appendString(msg, "' class");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&class->comments, comment);
+ class->skip = ISC_TRUE;
+ }
+}
+
+static void
+post_process_option_definitions(struct parse *cfile)
+{
+ struct element *opt_def;
+ struct element *def, *ndef;
+
+ opt_def = mapGet(cfile->stack[1], "option-def");
+ if (opt_def == NULL)
+ return;
+ TAILQ_FOREACH_SAFE(def, &opt_def->value.list_value, ndef) {
+ if (mapContains(def, "no-export"))
+ TAILQ_REMOVE(&opt_def->value.list_value, def);
+ }
+}
+
+void
+read_conf_file(struct parse *parent, const char *filename, int group_type)
+{
+ int file;
+ struct parse *cfile;
+ struct string *msg;
+ struct comment *comment;
+ size_t amount = parent->stack_size * sizeof(struct element *);
+
+ if ((file = open (filename, O_RDONLY)) < 0)
+
+ parse_error(parent, "Can't open %s: %s",
+ filename, strerror(errno));
+
+ cfile = new_parse(file, NULL, 0, filename, 0);
+ if (cfile == NULL)
+ parse_error(parent, "Can't create new parse structure");
+
+ cfile->stack = (struct element **)malloc(amount);
+ if (cfile->stack == NULL)
+ parse_error(parent, "Can't create new element stack");
+ memcpy(cfile->stack, parent->stack, amount);
+ cfile->stack_size = parent->stack_size;
+ cfile->stack_top = parent->stack_top;
+ cfile->issue_counter = parent->issue_counter;
+
+ msg = makeString(-1, "/// Begin file ");
+ concatString(msg, makeString(-1, filename));
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&cfile->comments, comment);
+
+ conf_file_subparse(cfile, group_type);
+
+ amount = cfile->stack_size * sizeof(struct element *);
+ if (cfile->stack_size > parent->stack_size) {
+ parent->stack =
+ (struct element **)realloc(parent->stack, amount);
+ if (parent->stack == NULL)
+ parse_error(cfile, "can't resize element stack");
+ }
+ memcpy(parent->stack, cfile->stack, amount);
+ parent->stack_size = cfile->stack_size;
+ parent->stack_top = cfile->stack_top;
+ parent->issue_counter = cfile->issue_counter;
+ msg = makeString(-1, "/// End file ");
+ concatString(msg, makeString(-1, filename));
+ comment= createComment(msg->content);
+ TAILQ_INSERT_TAIL(&parent->comments, comment);
+ end_parse(cfile);
+}
+
+/* conf-file :== parameters declarations END_OF_FILE
+ parameters :== <nil> | parameter | parameters parameter
+ declarations :== <nil> | declaration | declarations declaration */
+
+size_t
+conf_file_subparse(struct parse *cfile, int type)
+{
+ const char *val;
+ enum dhcp_token token;
+ isc_boolean_t declaration = ISC_FALSE;
+
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == END_OF_FILE)
+ break;
+ declaration = parse_statement(cfile, type, declaration);
+ }
+ skip_token(&val, NULL, cfile);
+
+ return cfile->issue_counter;
+}
+
+/* statement :== parameter | declaration | PERCENT directive
+
+ parameter :== DEFAULT_LEASE_TIME lease_time
+ | MAX_LEASE_TIME lease_time
+ | DYNAMIC_BOOTP_LEASE_CUTOFF date
+ | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
+ | BOOT_UNKNOWN_CLIENTS boolean
+ | ONE_LEASE_PER_CLIENT boolean
+ | GET_LEASE_HOSTNAMES boolean
+ | USE_HOST_DECL_NAME boolean
+ | NEXT_SERVER ip-addr-or-hostname SEMI
+ | option_parameter
+ | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
+ | FILENAME string-parameter
+ | SERVER_NAME string-parameter
+ | hardware-parameter
+ | fixed-address-parameter
+ | ALLOW allow-deny-keyword
+ | DENY allow-deny-keyword
+ | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
+ | AUTHORITATIVE
+ | NOT AUTHORITATIVE
+
+ declaration :== host-declaration
+ | group-declaration
+ | shared-network-declaration
+ | subnet-declaration
+ | VENDOR_CLASS class-declaration
+ | USER_CLASS class-declaration
+ | RANGE address-range-declaration */
+
+isc_boolean_t
+parse_statement(struct parse *cfile, int type, isc_boolean_t declaration)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *hardware;
+ struct element *cache;
+ struct element *et;
+ isc_boolean_t lose;
+ isc_boolean_t known;
+ isc_boolean_t authoritative;
+ struct option *option;
+ size_t host_decl = 0;
+ size_t subnet = 0;
+ size_t i;
+
+ token = peek_token(&val, NULL, cfile);
+
+ switch (token) {
+ case INCLUDE:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "filename string expected.");
+ read_conf_file(cfile, val, type);
+ parse_semi(cfile);
+ return 1;
+
+ case HOST:
+ skip_token(&val, NULL, cfile);
+ if (type != HOST_DECL && type != CLASS_DECL)
+ parse_host_declaration(cfile);
+ else
+ parse_error(cfile,
+ "host declarations not allowed here.");
+ return 1;
+
+ case GROUP:
+ skip_token(&val, NULL, cfile);
+ if (type != HOST_DECL && type != CLASS_DECL)
+ parse_group_declaration(cfile);
+ else
+ parse_error(cfile,
+ "group declarations not allowed here.");
+ return 1;
+
+ case SHARED_NETWORK:
+ skip_token(&val, NULL, cfile);
+ if (type == SHARED_NET_DECL ||
+ type == HOST_DECL ||
+ type == SUBNET_DECL ||
+ type == CLASS_DECL)
+ parse_error(cfile, "shared-network parameters not %s.",
+ "allowed here");
+ parse_shared_net_declaration(cfile);
+ return 1;
+
+ case SUBNET:
+ case SUBNET6:
+ skip_token(&val, NULL, cfile);
+ if (type == HOST_DECL || type == SUBNET_DECL ||
+ type == CLASS_DECL)
+ parse_error(cfile,
+ "subnet declarations not allowed here.");
+
+ if (token == SUBNET)
+ parse_subnet_declaration(cfile);
+ else
+ parse_subnet6_declaration(cfile);
+ return 1;
+
+ case VENDOR_CLASS:
+ case USER_CLASS:
+ case CLASS:
+ case SUBCLASS:
+ skip_token(&val, NULL, cfile);
+ if (token == VENDOR_CLASS)
+ parse_error(cfile, "obsolete 'vendor-class' "
+ "declaration");
+ if (token == USER_CLASS)
+ parse_error(cfile, "obsolete 'user-class' "
+ "declaration");
+ if (type == CLASS_DECL)
+ parse_error(cfile,
+ "class declarations not allowed here.");
+ parse_class_declaration(cfile, token == CLASS
+ ? CLASS_TYPE_CLASS
+ : CLASS_TYPE_SUBCLASS);
+ return 1;
+
+ case HARDWARE:
+ if (!use_hw_address) {
+ add_host_reservation_identifiers(cfile,
+ "hw-address");
+ use_hw_address = ISC_TRUE;
+ }
+
+ skip_token(&val, NULL, cfile);
+ if (!host_decl) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ if (cfile->stack[i]->kind == HOST_DECL) {
+ host_decl = i;
+ break;
+ }
+ }
+ }
+ if (!host_decl)
+ parse_error(cfile, "hardware address parameter %s",
+ "not allowed here.");
+ if (mapContains(cfile->stack[host_decl], "hw-address"))
+ parse_error(cfile, "Host hardware address already "
+ "configured.");
+ hardware = parse_hardware_param(cfile);
+ mapSet(cfile->stack[host_decl], hardware, "hw-address");
+ if (hardware->skip)
+ cfile->stack[host_decl]->skip = ISC_TRUE;
+ break;
+
+ case FIXED_ADDR:
+ case FIXED_ADDR6:
+ skip_token(&val, NULL, cfile);
+ if (!host_decl) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ if (cfile->stack[i]->kind == HOST_DECL) {
+ host_decl = i;
+ break;
+ }
+ }
+ }
+ if (!host_decl)
+ parse_error(cfile,
+ "fixed-address parameter not "
+ "allowed here.");
+ cache = parse_fixed_addr_param(cfile, token);
+ if (token == FIXED_ADDR) {
+ struct element *addr;
+
+ if (mapContains(cfile->stack[host_decl], "ip-address"))
+ parse_error(cfile, "Only one fixed address "
+ "declaration per host.");
+ addr = listGet(cache, 0);
+ listRemove(cache, 0);
+ mapSet(cfile->stack[host_decl], addr, "ip-address");
+ if (listSize(cache) > 0) {
+ cache->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(cfile->stack[host_decl],
+ cache, "extra-ip-addresses");
+ }
+ } else {
+ if (mapContains(cfile->stack[host_decl],
+ "ip-addresses"))
+ parse_error(cfile, "Only one fixed address "
+ "declaration per host.");
+ mapSet(cfile->stack[host_decl], cache, "ip-addresses");
+ }
+ break;
+
+ case POOL:
+ skip_token(&val, NULL, cfile);
+ if (type == POOL_DECL)
+ parse_error(cfile, "pool declared within pool.");
+ if (type != SUBNET_DECL && type != SHARED_NET_DECL)
+ parse_error(cfile, "pool declared outside of network");
+ parse_pool_statement(cfile, type);
+
+ return declaration;
+
+ case RANGE:
+ skip_token(&val, NULL, cfile);
+ if (!subnet) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ if (cfile->stack[i]->kind == SUBNET_DECL) {
+ subnet = i;
+ break;
+ }
+ }
+ }
+ if (type != SUBNET_DECL || !subnet)
+ parse_error(cfile,
+ "range declaration not allowed here.");
+ parse_address_range(cfile, type, subnet);
+ return declaration;
+
+ case RANGE6:
+ if (local_family != AF_INET6)
+ goto unknown;
+ skip_token(NULL, NULL, cfile);
+ if (!subnet) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ if (cfile->stack[i]->kind == SUBNET_DECL) {
+ subnet = i;
+ break;
+ }
+ }
+ }
+ if ((type != SUBNET_DECL) || !subnet)
+ parse_error(cfile,
+ "range6 declaration not allowed here.");
+ parse_address_range6(cfile, type, subnet);
+ return declaration;
+
+ case PREFIX6:
+ if (local_family != AF_INET6)
+ goto unknown;
+ skip_token(NULL, NULL, cfile);
+ if (!subnet) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ if (cfile->stack[i]->kind == SUBNET_DECL) {
+ subnet = i;
+ break;
+ }
+ }
+ }
+ if ((type != SUBNET_DECL) || !subnet)
+ parse_error(cfile,
+ "prefix6 declaration not allowed here.");
+ parse_prefix6(cfile, type, subnet);
+ return declaration;
+
+ case FIXED_PREFIX6:
+ if (local_family != AF_INET6)
+ goto unknown;
+ skip_token(&val, NULL, cfile);
+ if (!host_decl) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ if (cfile->stack[i]->kind == HOST_DECL) {
+ host_decl = i;
+ break;
+ }
+ }
+ }
+ if (!host_decl)
+ parse_error(cfile,
+ "fixed-prefix6 declaration not "
+ "allowed here.");
+ parse_fixed_prefix6(cfile, host_decl);
+ break;
+
+ case POOL6:
+ if (local_family != AF_INET6)
+ goto unknown;
+ skip_token(&val, NULL, cfile);
+ if (type == POOL_DECL)
+ parse_error(cfile, "pool6 declared within pool.");
+ if (type != SUBNET_DECL)
+ parse_error(cfile,
+ "pool6 declared outside of network");
+ parse_pool6_statement(cfile, type);
+
+ return declaration;
+
+ case TOKEN_NOT:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case AUTHORITATIVE:
+ authoritative = ISC_FALSE;
+ goto authoritative;
+ default:
+ parse_error(cfile, "expecting assertion");
+ }
+ break;
+
+ case AUTHORITATIVE:
+ skip_token(&val, NULL, cfile);
+ authoritative = ISC_TRUE;
+ authoritative:
+ if (type == HOST_DECL)
+ parse_error(cfile, "authority makes no sense here.");
+ if (!subnet) {
+ for (i = cfile->stack_top; i > 0; --i) {
+ int kind;
+
+ kind = cfile->stack[i]->kind;
+ if ((kind == SUBNET_DECL) ||
+ (kind == SHARED_NET_DECL) ||
+ (kind == ROOT_GROUP)) {
+ subnet = i;
+ break;
+ }
+ }
+ }
+ if (!subnet)
+ parse_error(cfile, "can't find root group");
+ if (local_family == AF_INET) {
+ cache = createBool(authoritative);
+ TAILQ_CONCAT(&cache->comments, &cfile->comments);
+ mapSet(cfile->stack[subnet], cache, "authoritative");
+ }
+ parse_semi(cfile);
+ break;
+
+ /* "server-identifier" is a special hack, equivalent to
+ "option dhcp-server-identifier". */
+ case SERVER_IDENTIFIER:
+ option = option_lookup_code("dhcp",
+ DHO_DHCP_SERVER_IDENTIFIER);
+ assert(option);
+ skip_token(&val, NULL, cfile);
+ goto finish_option;
+
+ case OPTION:
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token == SPACE) {
+ if (type != ROOT_GROUP)
+ parse_error(cfile,
+ "option space definitions %s",
+ "may not be scoped.");
+ parse_option_space_decl(cfile);
+ return declaration;
+ }
+
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_TRUE, &known);
+ token = peek_token(&val, NULL, cfile);
+ if (token == CODE) {
+ if (type != ROOT_GROUP)
+ parse_error(cfile,
+ "option definitions%s",
+ " may not be scoped.");
+ skip_token(&val, NULL, cfile);
+
+ /* next function must deal with redefinitions */
+ parse_option_code_definition(cfile, option);
+ return declaration;
+ }
+ /* If this wasn't an option code definition, don't
+ allow an unknown option. */
+ if (!known)
+ parse_error(cfile, "unknown option %s.%s",
+ option->space->old, option->old);
+ finish_option:
+ parse_option_statement(NULL, cfile, option,
+ supersede_option_statement);
+ return declaration;
+
+ case FAILOVER:
+ if (failover_once)
+ fprintf(stderr, "ignoring failover\n");
+ failover_once = ISC_FALSE;
+ skip_to_semi(cfile);
+ break;
+
+ case SERVER_DUID:
+ if (local_family != AF_INET6)
+ goto unknown;
+ parse_server_duid_conf(cfile);
+ break;
+
+ case LEASE_ID_FORMAT:
+ token = next_token(&val, NULL, cfile);
+ /* ignore: ISC DHCP specific */
+ break;
+
+ case PERCENT:
+ skip_token(&val, NULL, cfile);
+ if (type != ROOT_GROUP)
+ parse_error(cfile, "directives are only supported "
+ "at toplevel");
+ parse_directive(cfile);
+ return declaration;
+
+ unknown:
+ skip_token(&val, NULL, cfile);
+
+ default:
+ et = createMap();
+ TAILQ_CONCAT(&et->comments, &cfile->comments);
+ lose = ISC_FALSE;
+ if (!parse_executable_statement(et, cfile, &lose,
+ context_any, ISC_TRUE)) {
+ if (!lose) {
+ if (declaration)
+ parse_error(cfile,
+ "expecting a declaration");
+ else
+ parse_error(cfile,
+ "expecting a parameter %s",
+ "or declaration");
+ }
+ return declaration;
+ }
+ if (mapSize(et) == 0)
+ return declaration;
+
+ et->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(cfile->stack[cfile->stack_top], et, "statement");
+ }
+
+ return 0;
+}
+
+/*!
+ *
+ * \brief Parse allow and deny statements
+ *
+ * This function handles the common processing code for permit and deny
+ * statements in the parse_pool_statement and parse_pool6_statement functions.
+ *
+ * The allow or deny token should already be consumed, this function expects
+ * one of the following:
+ * known-clients;
+ * unknown-clients;
+ * known clients;
+ * unknown clients;
+ * authenticated clients;
+ * unauthenticated clients;
+ * all clients;
+ * dynamic bootp clients;
+ * members of <class name>;
+ * after <date>;
+ *
+ * \param[in] cfile = the configuration file being parsed
+ * \param[in] permit_head = the head of the permit list (permit or prohibit)
+ * to which to attach the newly created permit structure
+ */
+
+void
+get_permit(struct parse *cfile, struct element *permit_head)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct string *permit;
+ struct string *alias = NULL;
+ struct comment *comment = NULL;
+ struct element *member;
+ isc_boolean_t need_clients = ISC_TRUE;
+ isc_boolean_t negative = ISC_FALSE;
+
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case UNKNOWN:
+ permit = CLASS_KNOWN;
+ negative = ISC_TRUE;
+ alias = makeString(-1, "unknown clients");
+ break;
+
+ case KNOWN_CLIENTS:
+ need_clients = ISC_FALSE;
+ permit = CLASS_KNOWN;
+ alias = makeString(-1, "known-clients");
+ break;
+
+ case UNKNOWN_CLIENTS:
+ need_clients = ISC_FALSE;
+ permit = CLASS_KNOWN;
+ negative = ISC_TRUE;
+ alias = makeString(-1, "unknown-clients");
+ break;
+
+ case KNOWN:
+ permit = CLASS_KNOWN;
+ alias = makeString(-1, "known clients");
+ break;
+
+ case AUTHENTICATED:
+ permit = CLASS_ALL;
+ alias = makeString(-1, "authenticated clients");
+ negative = ISC_TRUE;
+ authenticated_clients:
+ comment = createComment("/// [un]authenticated-clients is "
+ "not supported by ISC DHCP and Kea");
+ break;
+
+ case UNAUTHENTICATED:
+ permit = CLASS_ALL;
+ alias = makeString(-1, "unauthenticated clients");
+ goto authenticated_clients;
+ break;
+
+ case ALL:
+ permit = CLASS_ALL;
+ alias = makeString(-1, "all clients");
+ break;
+
+ case DYNAMIC:
+ /* bootp is not supported by Kea so the dynamic bootp
+ * client set is the empty set. */
+ if (next_token(&val, NULL, cfile) != TOKEN_BOOTP)
+ parse_error(cfile, "expecting \"bootp\"");
+ permit = CLASS_ALL;
+ negative = ISC_TRUE;
+ alias = makeString(-1, "dynamic bootp clients");
+ cfile->issue_counter++;
+ comment = createComment("/// dynamic-bootp-client is not "
+ "supported by Kea");
+ break;
+
+ case MEMBERS:
+ /* we don't check the class... */
+ need_clients = ISC_FALSE;
+ if (next_token(&val, NULL, cfile) != OF)
+ parse_error(cfile, "expecting \"of\"");
+ if (next_token(&val, NULL, cfile) != STRING)
+ parse_error(cfile, "expecting class name.");
+ permit = makeString(-1, val);
+ break;
+
+ case AFTER:
+ /* don't use parse_date_code() */
+ need_clients = ISC_FALSE;
+ permit = makeString(-1, "AFTER_");
+ alias = makeString(-1, "after ");
+ while (peek_raw_token(NULL, NULL, cfile) != SEMI) {
+ next_raw_token(&val, NULL, cfile);
+ appendString(permit, val);
+ appendString(alias, val);
+ }
+ permit_head->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ comment = createComment("/// after <date> is not yet "
+ "supported by Kea");
+ break;
+
+ default:
+ parse_error(cfile, "expecting permit type.");
+ }
+
+ /*
+ * The need_clients flag is set if we are expecting the
+ * CLIENTS token
+ */
+ if (need_clients && (next_token(&val, NULL, cfile) != CLIENTS))
+ parse_error(cfile, "expecting \"clients\"");
+ member = createMap();
+ mapSet(member, createString(permit), "class");
+ mapSet(member, createBool(!negative), "way");
+ if (alias != NULL)
+ mapSet(member, createString(alias), "real");
+ if (comment != NULL)
+ TAILQ_INSERT_TAIL(&permit_head->comments, comment);
+ listPush(permit_head, member);
+ parse_semi(cfile);
+
+ return;
+}
+
+/*!
+ *
+ * \brief Parse a pool statement
+ *
+ * Pool statements are used to group declarations and permit & deny information
+ * with a specific address range. They must be declared within a shared network
+ * or subnet and there may be multiple pools withing a shared network or subnet.
+ * Each pool may have a different set of permit or deny options.
+ *
+ * \param[in] cfile = the configuration file being parsed
+ * \param[in] type = the type of the enclosing statement. This must be
+ * SHARED_NET_DECL or SUBNET_DECL for this function.
+ *
+ * \return
+ * void - This function either parses the statement and updates the structures
+ * or it generates an error message and possible halts the program if
+ * it encounters a problem.
+ */
+void
+parse_pool_statement(struct parse *cfile, int type)
+{
+ enum dhcp_token token;
+ const char *val;
+ isc_boolean_t done = ISC_FALSE;
+ struct element *pool;
+ struct element *pools;
+ struct element *permit;
+ struct element *prohibit;
+ int declaration = 0;
+ unsigned range_counter = 0;
+
+ pool = createMap();
+ pool->kind = POOL_DECL;
+ TAILQ_CONCAT(&pool->comments, &cfile->comments);
+
+ if (type != SUBNET_DECL && type != SHARED_NET_DECL)
+ parse_error(cfile, "Dynamic pools are only valid inside "
+ "subnet or shared-network statements.");
+ parse_lbrace(cfile);
+
+ stackPush(cfile, pool);
+ type = POOL_DECL;
+
+ permit = createList();
+ prohibit = createList();
+
+ do {
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case TOKEN_NO:
+ case FAILOVER:
+ if (failover_once)
+ fprintf(stderr, "ignoring failover\n");
+ failover_once = ISC_FALSE;
+ skip_to_semi(cfile);
+ break;
+
+ case RANGE:
+ skip_token(&val, NULL, cfile);
+ parse_address_range(cfile, type, cfile->stack_top);
+ range_counter++;
+ break;
+
+ case ALLOW:
+ skip_token(&val, NULL, cfile);
+ get_permit(cfile, permit);
+ break;
+
+ case DENY:
+ skip_token(&val, NULL, cfile);
+ get_permit(cfile, prohibit);
+ break;
+
+ case RBRACE:
+ skip_token(&val, NULL, cfile);
+ done = ISC_TRUE;
+ break;
+
+ case END_OF_FILE:
+ /*
+ * We can get to END_OF_FILE if, for instance,
+ * the parse_statement() reads all available tokens
+ * and leaves us at the end.
+ */
+ parse_error(cfile, "unexpected end of file");
+
+ default:
+ declaration = parse_statement(cfile, type,
+ declaration);
+ break;
+ }
+ } while (!done);
+
+ cfile->stack_top--;
+
+ generate_class(cfile, pool, permit, prohibit);
+
+ pools = mapGet(cfile->stack[cfile->stack_top], "pools");
+ if (pools == NULL) {
+ pools = createList();
+ pools->kind = POOL_DECL;
+ mapSet(cfile->stack[cfile->stack_top], pools, "pools");
+ }
+ if (range_counter == 0) {
+ struct comment *comment;
+
+ /* no range */
+ comment = createComment("empty pool");
+ TAILQ_INSERT_TAIL(&pool->comments, comment);
+ pool->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ listPush(pools, pool);
+ return;
+ }
+ /* spread extra ranges into pool copies */
+ while (--range_counter != 0) {
+ struct handle *handle;
+ struct element *first;
+ struct element *saved;
+ isc_boolean_t seen = ISC_FALSE;
+
+ first = createMap();
+ saved = copy(pool);
+ TAILQ_CONCAT(&first->comments, &pool->comments);
+ while (mapSize(pool) > 0) {
+ handle = mapPop(pool);
+ if ((handle == NULL) || (handle->key == NULL) ||
+ (handle->value == NULL))
+ parse_error(cfile, "bad pool entry");
+ if (strcmp(handle->key, "pool") != 0)
+ mapSet(first, handle->value, handle->key);
+ else if (!seen) {
+ mapSet(first, handle->value, handle->key);
+ mapRemove(saved, "pool");
+ seen = ISC_TRUE;
+ }
+ }
+ listPush(pools, first);
+ pool = saved;
+ }
+ listPush(pools, pool);
+}
+
+/* Expect a left brace */
+
+void
+parse_lbrace(struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE)
+ parse_error(cfile, "expecting left brace.");
+}
+
+/* host-declaration :== hostname RBRACE parameters declarations LBRACE */
+
+void
+parse_host_declaration(struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct element *host;
+ struct string *name;
+ struct element *where;
+ struct element *hosts = NULL;
+ int declaration = 0;
+ isc_boolean_t used_heuristic = ISC_FALSE;
+
+ host = createMap();
+ host->kind = HOST_DECL;
+ TAILQ_CONCAT(&host->comments, &cfile->comments);
+
+ name = parse_host_name(cfile);
+ if (!name)
+ parse_error(cfile, "expecting a name for host declaration.");
+
+ mapSet(host, createString(name), "hostname");
+
+ parse_lbrace(cfile);
+
+ stackPush(cfile, host);
+
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ skip_token(&val, NULL, cfile);
+ break;
+ }
+ if (token == END_OF_FILE)
+ parse_error(cfile, "unexpected end of file");
+ /* If the host declaration was created by the server,
+ remember to save it. */
+ if (token == DYNAMIC) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "dynamic hosts don't exist "
+ "in the config file");
+ }
+ /* If the host declaration was created by the server,
+ remember to save it. */
+ if (token == TOKEN_DELETED) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "deleted hosts don't exist "
+ "in the config file");
+ }
+
+ if (token == GROUP) {
+ struct element *group;
+ struct comment *comment;
+
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING && !is_identifier(token))
+ parse_error(cfile,
+ "expecting string or identifier.");
+ group = createString(makeString(-1, val));
+ group->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ comment = createComment("/// Unsupported group in "
+ "host reservations");
+ TAILQ_INSERT_TAIL(&group->comments, comment);
+ comment = createComment("/// Reference Kea #233");
+ TAILQ_INSERT_TAIL(&group->comments, comment);
+ mapSet(host, group, "group");
+ parse_semi(cfile);
+ continue;
+ }
+
+ if (token == UID) {
+ struct string *client_id;
+
+ if (!use_client_id) {
+ add_host_reservation_identifiers(cfile,
+ "client-id");
+ use_client_id = ISC_TRUE;
+ }
+
+ skip_token(&val, NULL, cfile);
+
+ if (mapContains(host, "client-id"))
+ parse_error(cfile, "Host %s already has a "
+ "client identifier.",
+ name->content);
+
+ /* See if it's a string or a cshl. */
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ skip_token(&val, NULL, cfile);
+ client_id = makeString(-1, val);
+ } else {
+ struct string *bin;
+ unsigned len = 0;
+
+ bin = parse_numeric_aggregate
+ (cfile, NULL, &len, ':', 16, 8);
+ if (!bin)
+ parse_error(cfile,
+ "expecting hex list.");
+ client_id = makeStringExt(bin->length,
+ bin->content, 'H');
+ }
+ mapSet(host, createString(client_id), "client-id");
+
+ parse_semi(cfile);
+ continue;
+ }
+
+ if (token == HOST_IDENTIFIER) {
+ struct string *host_id;
+ isc_boolean_t known;
+ struct option *option;
+ struct element *expr;
+ struct string *data;
+ int relays = 0;
+
+ if (!use_flex_id) {
+ add_host_reservation_identifiers(cfile,
+ "flex-id");
+ use_flex_id = ISC_TRUE;
+ }
+
+ if (mapContains(host, "host-identifier") ||
+ mapContains(host, "flex-id"))
+ parse_error(cfile,
+ "only one host-identifier allowed "
+ "per host");
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ host_id = makeString(-1, val);
+ appendString(host_id, " ");
+ if (token == V6RELOPT) {
+ token = next_token(&val, NULL, cfile);
+
+ if (token != NUMBER)
+ parse_error(cfile,
+ "host-identifier v6relopt "
+ "must have a number");
+ appendString(host_id, val);
+ appendString(host_id, " ");
+ relays = atoi(val);
+ if (relays < 0)
+ parse_error(cfile,
+ "host-identifier v6relopt "
+ "must have a number >= 0");
+ if (relays > MAX_V6RELAY_HOPS)
+ relays = MAX_V6RELAY_HOPS + 1;
+ } else if (token != OPTION)
+ parse_error(cfile,
+ "host-identifier must be an option"
+ " or v6relopt");
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_TRUE, &known);
+ if (!known)
+ parse_error(cfile, "unknown option %s.%s",
+ option->space->old, option->old);
+ appendString(host_id, option->space->name);
+ appendString(host_id, ".");
+ appendString(host_id, option->name);
+ appendString(host_id, " ");
+
+ data = parse_option_textbin(cfile, option);
+ parse_semi(cfile);
+
+ if (data == NULL)
+ parse_error(cfile, "can't get option data");
+ concatString(host_id, data);
+ expr = createString(host_id);
+ expr->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(host, expr, "host-identifier");
+
+ if (host_id_option == NULL)
+ add_host_id_option(cfile, option, relays);
+ else if ((host_id_option != option) ||
+ (host_id_relays != relays)) {
+ struct string *msg;
+ struct comment *comment;
+
+ msg = allocString();
+ appendString(msg, "/// Another option (");
+ appendString(msg, host_id_option->name);
+ appendString(msg, ") is already used as ");
+ appendString(msg, "host-identifier");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ continue;
+ }
+
+ /*
+ * Everything good: set a flex-id and remove
+ * the host-identifier entry.
+ */
+ mapSet(host, createString(data), "flex-id");
+ mapRemove(host, "host-identifier");
+ continue;
+ }
+
+ declaration = parse_statement(cfile, HOST_DECL, declaration);
+ }
+
+ cfile->stack_top--;
+
+ where = find_match(cfile, host, &used_heuristic);
+ hosts = mapGet(where, "reservations");
+ if (hosts == NULL) {
+ hosts = createList();
+ hosts->kind = HOST_DECL;
+ mapSet(where, hosts, "reservations");
+ if (used_heuristic) {
+ struct comment *comment;
+
+ comment = createComment("/// Host reservations "
+ "without fixed addresses "
+ "were put in the last "
+ "declared subnet");
+ TAILQ_INSERT_TAIL(&hosts->comments, comment);
+ comment = createComment("/// Reference Kea #231");
+ TAILQ_INSERT_TAIL(&hosts->comments, comment);
+ }
+ }
+ listPush(hosts, host);
+}
+
+/* Simple tool to declare used (and only used) reservation identifiers */
+static void
+add_host_reservation_identifiers(struct parse *cfile, const char *id)
+{
+ struct element *ids;
+
+ ids = mapGet(cfile->stack[1], "host-reservation-identifiers");
+ if (ids == NULL) {
+ ids = createList();
+ mapSet(cfile->stack[1], ids, "host-reservation-identifiers");
+ }
+ listPush(ids, createString(makeString(-1, id)));
+}
+
+/* Add the flexible host identifier glue */
+static void
+add_host_id_option(struct parse *cfile,
+ const struct option *option, int relays)
+{
+ struct string *path;
+ struct string *expr;
+ struct element *params;
+ struct element *entry;
+ struct element *hooks;
+ struct comment *comment;
+ char buf[40];
+
+ host_id_option = option;
+ host_id_relays = relays;
+
+ /*
+ * Using the example from the Kea Administrator Reference Manual
+ * as recommended by Tomek
+ */
+ hooks = createList();
+ mapSet(cfile->stack[1], hooks, "hooks-libraries");
+ comment = createComment("/// The flexible host identifier "
+ "is a premium feature");
+ TAILQ_INSERT_TAIL(&hooks->comments, comment);
+ entry = createMap();
+ listPush(hooks, entry);
+ if (hook_library_path != NULL)
+ path = makeString(-1, hook_library_path);
+ else
+ path = makeString(-1, "/path/");
+ appendString(path, "libdhcp_flex_id.so");
+ params = createString(path);
+ if (hook_library_path == NULL) {
+ comment = createComment("/// Please update the path here");
+ TAILQ_INSERT_TAIL(&params->comments, comment);
+ }
+ mapSet(entry, params, "library");
+ params = createMap();
+ mapSet(entry, params, "parameters");
+
+ snprintf(buf, sizeof(buf), "%soption[%u].hex",
+ relays > 0 ? "relay[0]." : "", option->code);
+ expr = makeString(-1, buf);
+ mapSet(params, createString(expr), "identifier-expression");
+}
+
+static void add_host_reservation_identifiers(struct parse *, const char *);
+/* class-declaration :== STRING LBRACE parameters declarations RBRACE
+ *
+ * in fact:
+ * (CLASS) NAME(STRING) LBRACE ... RBRACE
+ * (SUBCLASS) SUPER(STRING) DATA/HASH(STRING | <hexa>) [BRACE ... RBRACE]
+ *
+ * class "name" { MATCH IF <boolean-expr> }: direct: belong when true
+ * class "name" { MATCH <data-expr> }: indirect: use subclasses
+ * class "name" { MATCH <data-expr> SPAWN WITH <data-expr> }: indirect:
+ * create dynamically a subclass
+ * subclass "super" <data-expr = string or binary aka hash>: belongs when
+ * super <data-expr> == <hash>
+ */
+
+void
+parse_class_declaration(struct parse *cfile, int type)
+{
+ const char *val = NULL;
+ enum dhcp_token token;
+ size_t group = 0;
+ size_t i = 0;
+ struct element *group_classes = NULL;
+ struct element *classes = NULL;
+ struct element *class = NULL;
+ struct element *pc = NULL; /* p(arent)c(lass) */
+ struct element *tmp = NULL;
+ struct element *expr = NULL;
+ struct element *data = NULL;
+ isc_boolean_t binary = ISC_FALSE;
+ int declaration = 0;
+ struct string *name = NULL;
+ isc_boolean_t lose = ISC_FALSE;
+ isc_boolean_t matchedonce = ISC_FALSE;
+ isc_boolean_t submatchedonce = ISC_FALSE;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "Expecting class name");
+
+ /* Find group and root classes */
+ classes = mapGet(cfile->stack[1], "client-classes");
+ if (classes == NULL) {
+ classes = createList();
+ classes->kind = CLASS_DECL;
+ mapSet(cfile->stack[1], classes, "client-classes");
+ }
+ for (group = cfile->stack_top; group > 0; --group) {
+ int kind;
+
+ kind = cfile->stack[group]->kind;
+ if (kind == CLASS_DECL)
+ parse_error(cfile, "class in class");
+ if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
+ break;
+ }
+ if (!group)
+ parse_error(cfile, "can't find root group");
+ if (cfile->stack[group]->kind == GROUP_DECL) {
+ group_classes = mapGet(cfile->stack[group], "client-classes");
+ if (group_classes == NULL) {
+ group_classes = createList();
+ group_classes->kind = CLASS_DECL;
+ mapSet(cfile->stack[group], group_classes,
+ "client-classes");
+ }
+ } else
+ group_classes = classes;
+
+ /* See if there's already a class with the specified name. */
+ for (i = 0; i < listSize(classes); i++) {
+ struct element *name;
+
+ tmp = listGet(classes, i);
+ name = mapGet(tmp, "name");
+ if (name == NULL)
+ continue;
+ if (strcmp(stringValue(name)->content, val) == 0) {
+ pc = tmp;
+ break;
+ }
+ }
+
+ /* If it is a class, we're updating it. If it's any of the other
+ * types (subclass, vendor or user class), the named class is a
+ * reference to the parent class so its mandatory.
+ */
+ if ((pc != NULL) && (type == CLASS_TYPE_CLASS)) {
+ class = pc;
+ pc = NULL;
+ } else if (type != CLASS_TYPE_CLASS) {
+ if (pc == NULL)
+ parse_error(cfile, "no class named %s", val);
+ if (!mapContains(pc, "spawning") ||
+ !mapContains(pc, "submatch"))
+ parse_error(cfile, "found class name %s but it is "
+ "not a suitable superclass", val);
+ }
+
+ name = makeString(-1, val);
+ /* If this is a straight subclass, parse the hash string. */
+ if (type == CLASS_TYPE_SUBCLASS) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ unsigned len;
+
+ skip_token(&val, &len, cfile);
+ data = createString(makeString(len, val));
+ } else if (token == NUMBER_OR_NAME || token == NUMBER) {
+ data = createHexa(parse_hexa(cfile));
+ binary = ISC_TRUE;
+ } else {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "Expecting string or hex list.");
+ }
+ }
+
+ /* See if there's already a class in the hash table matching the
+ hash data. */
+ if (type != CLASS_TYPE_CLASS) {
+ for (i = 0; i < listSize(classes); i++) {
+ struct element *super;
+ struct element *selector;
+
+ tmp = listGet(classes, i);
+ super = mapGet(tmp, "super");
+ if (super == NULL)
+ continue;
+ if (!eqString(stringValue(super), name))
+ continue;
+ if (binary)
+ selector = mapGet(tmp, "binary");
+ else
+ selector = mapGet(tmp, "string");
+ if (selector == NULL)
+ continue;
+ if (eqString(stringValue(selector),
+ stringValue(data))) {
+ class = tmp;
+ break;
+ }
+ }
+ }
+
+ /* Note the class declaration in the enclosing group */
+ if (group_classes != classes) {
+ struct element *gc;
+
+ gc = createMap();
+ gc->kind = CLASS_DECL;
+ tmp = createString(name);
+ if (type == CLASS_TYPE_CLASS)
+ mapSet(gc, tmp, "name");
+ else {
+ tmp->skip = ISC_TRUE;
+ mapSet(gc, tmp, "super");
+ data->skip = ISC_TRUE;
+ if (binary)
+ mapSet(gc, data, "binary");
+ else
+ mapSet(gc, data, "string");
+ }
+ listPush(group_classes, gc);
+ }
+
+ /* If we didn't find an existing class, allocate a new one. */
+ if (!class) {
+ /* Allocate the class structure... */
+ class = createMap();
+ class->kind = CLASS_DECL;
+ TAILQ_CONCAT(&class->comments, &cfile->comments);
+ if (type == CLASS_TYPE_SUBCLASS) {
+ struct string *subname;
+ char buf[40];
+
+ cfile->issue_counter++;
+ tmp = createString(name);
+ tmp->skip = ISC_TRUE;
+ mapSet(class, tmp, "super");
+ data->skip = ISC_TRUE;
+ if (binary)
+ mapSet(class, data, "binary");
+ else
+ mapSet(class, data, "string");
+ subname = makeString(-1, "sub#");
+ concatString(subname, name);
+ snprintf(buf, sizeof(buf),
+ "#%u", subclass_counter++);
+ appendString(subname, buf);
+ mapSet(class, createString(subname), "name");
+ } else
+ /* Save the name, if there is one. */
+ mapSet(class, createString(name), "name");
+ listPush(classes, class);
+ }
+
+ /* Spawned classes don't have to have their own settings. */
+ if (type == CLASS_TYPE_SUBCLASS) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ skip_token(&val, NULL, cfile);
+ subclass_inherit(cfile, class, copy(pc));
+ return;
+ }
+ }
+
+ parse_lbrace(cfile);
+
+ stackPush(cfile, class);
+
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ skip_token(&val, NULL, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "unexpected end of file");
+ } else if (token == DYNAMIC) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "dynamic classes don't exist "
+ "in the config file");
+ } else if (token == TOKEN_DELETED) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "deleted hosts don't exist "
+ "in the config file");
+ } else if (token == MATCH) {
+ skip_token(&val, NULL, cfile);
+ if (pc)
+ parse_error(cfile,
+ "invalid match in subclass.");
+ token = peek_token(&val, NULL, cfile);
+ if (token != IF) {
+ expr = createBool(ISC_FALSE);
+ expr->skip = 1;
+ mapSet(class, expr, "spawning");
+ goto submatch;
+ }
+
+ skip_token(&val, NULL, cfile);
+ if (matchedonce)
+ parse_error(cfile,
+ "A class may only have "
+ "one 'match if' clause.");
+ matchedonce = ISC_TRUE;
+ expr = createMap();
+ if (!parse_boolean_expression(expr, cfile, &lose)) {
+ if (!lose)
+ parse_error(cfile,
+ "expecting boolean expr.");
+ } else {
+ expr->skip = ISC_TRUE;
+ mapSet(class, expr, "match-if");
+ add_match_class(cfile, class, copy(expr));
+ parse_semi(cfile);
+ }
+ } else if (token == SPAWN) {
+ skip_token(&val, NULL, cfile);
+ if (pc)
+ parse_error(cfile,
+ "invalid spawn in subclass.");
+ expr = createBool(ISC_TRUE);
+ expr->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(class, expr, "spawning");
+ token = next_token(&val, NULL, cfile);
+ if (token != WITH)
+ parse_error(cfile,
+ "expecting with after spawn");
+ submatch:
+ if (submatchedonce)
+ parse_error(cfile,
+ "can't override existing "
+ "submatch/spawn");
+ submatchedonce = ISC_TRUE;
+ expr = createMap();
+ if (!parse_data_expression(expr, cfile, &lose)) {
+ if (!lose)
+ parse_error(cfile,
+ "expecting data expr.");
+ } else {
+ expr->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(class, expr, "submatch");
+ parse_semi(cfile);
+ }
+ } else if (token == LEASE) {
+ struct comment *comment;
+
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != LIMIT)
+ parse_error(cfile, "expecting \"limit\"");
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting a number");
+ tmp = createInt(atoll(val));
+ tmp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ comment = createComment("/// Per-class limit is not "
+ "supported by Kea");
+ TAILQ_INSERT_TAIL(&tmp->comments, comment);
+ comment = createComment("/// Reference Kea #237");
+ TAILQ_INSERT_TAIL(&tmp->comments, comment);
+ mapSet(class, tmp, "lease-limit");
+ parse_semi(cfile);
+ } else
+ declaration = parse_statement(cfile, CLASS_DECL,
+ declaration);
+ }
+
+ cfile->stack_top--;
+
+ if (type == CLASS_TYPE_SUBCLASS)
+ subclass_inherit(cfile, class, copy(pc));
+}
+
+/*
+ * Inherit entries:
+ * - first copy entries from the current superclass to the subclass
+ * - second try to reduce the subclass matching condition
+ */
+
+static void
+subclass_inherit(struct parse *cfile,
+ struct element *class,
+ struct element *superclass)
+{
+ struct string *name;
+ struct element *guard;
+ struct element *submatch;
+ struct handle *handle;
+ struct string *gmsg;
+ struct string *mmsg;
+ struct string *dmsg;
+ struct element *expr;
+ struct element *data;
+ struct element *match;
+ struct element *reduced;
+ unsigned order = 0;
+ struct comment *comment;
+ isc_boolean_t marked = ISC_FALSE;
+ isc_boolean_t lose = ISC_FALSE;
+ isc_boolean_t modified = ISC_FALSE;
+
+ expr = mapGet(superclass, "name");
+ if (expr == NULL)
+ parse_error(cfile, "can't get superclass name");
+ name = stringValue(expr);
+ guard = mapGet(superclass, "match-if");
+ submatch = mapGet(superclass, "submatch");
+ if (submatch == NULL)
+ parse_error(cfile, "can't get superclass submatch");
+
+ /* Iterates on (copy of) superclass entries */
+ while (mapSize(superclass) > 0) {
+ handle = mapPop(superclass);
+ if ((handle == NULL) || (handle->key == NULL) ||
+ (handle->value == NULL))
+ parse_error(cfile, "can't get superclass %s item at "
+ "%u", name->content, order);
+ handle->order = order++;
+ /* Superclass specific entries */
+ if ((strcmp(handle->key, "name") == 0) ||
+ (strcmp(handle->key, "spawning") == 0) ||
+ (strcmp(handle->key, "match-if") == 0) ||
+ (strcmp(handle->key, "test") == 0) ||
+ (strcmp(handle->key, "submatch") == 0))
+ continue;
+ /* Subclass specific so impossible entries */
+ if ((strcmp(handle->key, "super") == 0) ||
+ (strcmp(handle->key, "binary") == 0) ||
+ (strcmp(handle->key, "string") == 0))
+ parse_error(cfile, "superclass %s has unexpected %s "
+ "at %u",
+ name->content, handle->key, order);
+ /* Special entries */
+ if (strcmp(handle->key, "option-data") == 0) {
+ struct element *opt_list;
+
+ opt_list = mapGet(class, handle->key);
+ if (opt_list != NULL)
+ merge_option_data(handle->value, opt_list);
+ else
+ mapSet(class, handle->value, handle->key);
+ continue;
+ }
+ /* Just copy */
+ if ((strcmp(handle->key, "lease-limit") == 0) ||
+ (strcmp(handle->key, "boot-file-name") == 0) ||
+ (strcmp(handle->key, "serverhostname") == 0) ||
+ (strcmp(handle->key, "next-server") == 0)) {
+ mapSet(class, handle->value, handle->key);
+ continue;
+ }
+ /* Unknown */
+ if (!marked) {
+ marked = ISC_TRUE;
+ comment = createComment("/// copied from superclass");
+ TAILQ_INSERT_TAIL(&handle->value->comments, comment);
+ }
+ comment = createComment("/// unhandled entry");
+ TAILQ_INSERT_TAIL(&handle->value->comments, comment);
+ if (!handle->value->skip) {
+ handle->value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(class, handle->value, handle->key);
+ }
+
+ /* build [guard and] submatch = data */
+ expr = mapGet(class, "binary");
+ if (expr != NULL) {
+ data = createMap();
+ mapSet(data, copy(expr), "const-data");
+ } else
+ data = mapGet(class, "string");
+ if (data == NULL)
+ parse_error(cfile, "can't get subclass %s data",
+ name->content);
+ match = createMap();
+ mapSet(match, copy(submatch), "left");
+ mapSet(match, copy(data), "right");
+ expr = createMap();
+ mapSet(expr, match, "equal");
+
+ if (guard != NULL) {
+ match = createMap();
+ mapSet(match, copy(guard), "left");
+ mapSet(match, expr, "right");
+ expr = createMap();
+ mapSet(expr, match, "and");
+
+ gmsg = makeString(-1, "/// from: match-if ");
+ appendString(gmsg, print_boolean_expression(guard, &lose));
+ mmsg = makeString(-1, "/// match: ");
+ } else {
+ gmsg = NULL;
+ mmsg = makeString(-1, "/// from: match ");
+ }
+
+ appendString(mmsg, print_data_expression(submatch, &lose));
+ dmsg = makeString(-1, "/// data: ");
+ appendString(dmsg, print_data_expression(data, &lose));
+
+ /* evaluate the expression and try to reduce it */
+ reduced = eval_boolean_expression(expr, &modified);
+ reduced = reduce_boolean_expression(reduced);
+ if ((reduced != NULL) && (reduced->type == ELEMENT_BOOLEAN))
+ parse_error(cfile, "class matching rule evaluated to a "
+ "constant boolean expression: %s = %s",
+ print_data_expression(submatch, &lose),
+ print_data_expression(data, &lose));
+ if ((reduced == NULL) || (reduced->type != ELEMENT_STRING))
+ return;
+ if (!lose) {
+ if (gmsg != NULL) {
+ comment = createComment(gmsg->content);
+ TAILQ_INSERT_TAIL(&reduced->comments, comment);
+ }
+ comment = createComment(mmsg->content);
+ TAILQ_INSERT_TAIL(&reduced->comments, comment);
+ comment = createComment(dmsg->content);
+ TAILQ_INSERT_TAIL(&reduced->comments, comment);
+ }
+ mapSet(class, reduced, "test");
+}
+
+/*
+ * Try to reduce a match-if condition into a Kea evaluate bool "test"
+ */
+
+static void
+add_match_class(struct parse *cfile,
+ struct element *class,
+ struct element *expr)
+{
+ struct element *reduced;
+ isc_boolean_t modified = ISC_FALSE;
+ isc_boolean_t lose = ISC_FALSE;
+
+ /* evaluate the expression and try to reduce it */
+ reduced = eval_boolean_expression(expr, &modified);
+ reduced = reduce_boolean_expression(reduced);
+ if ((reduced != NULL) && (reduced->type == ELEMENT_BOOLEAN))
+ parse_error(cfile, "'match if' with a constant boolean "
+ "expression %s",
+ print_boolean_expression(expr, &lose));
+ if ((reduced != NULL) && (reduced->type == ELEMENT_STRING))
+ mapSet(class, reduced, "test");
+ else
+ cfile->issue_counter++;
+}
+
+/* Move pools to subnets */
+
+static void
+relocate_pools(struct element *share)
+{
+ struct element *srcs;
+ struct element *dsts;
+ struct element *subnet;
+ struct range *range;
+ size_t i;
+
+ srcs = mapGet(share, "pools");
+ if (srcs == NULL)
+ return;
+ if (listSize(srcs) == 0)
+ return;
+ TAILQ_FOREACH(range, &known_ranges) {
+ if (range->share != share)
+ continue;
+ subnet = find_location(share, range);
+ if (subnet == NULL)
+ continue;
+ for (i = 0; i < listSize(srcs); i++) {
+ struct element *pool;
+
+ pool = listGet(srcs, i);
+ if (range->pool != pool)
+ continue;
+ listRemove(srcs, i);
+ dsts = mapGet(subnet, "pools");
+ if (dsts == NULL) {
+ dsts = createList();
+ mapSet(subnet, dsts, "pools");
+ }
+ listPush(dsts, pool);
+ }
+ }
+}
+
+/* shared-network-declaration :==
+ hostname LBRACE declarations parameters RBRACE */
+
+void
+parse_shared_net_declaration(struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct element *share;
+ struct element *subnets;
+ struct element *interface;
+ struct element *subnet;
+ struct string *name;
+ int declaration = 0;
+
+ share = createMap();
+ share->kind = SHARED_NET_DECL;
+ TAILQ_CONCAT(&share->comments, &cfile->comments);
+
+ /* Get the name of the shared network... */
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ skip_token(&val, NULL, cfile);
+
+ if (val[0] == 0)
+ parse_error(cfile, "zero-length shared network name");
+ name = makeString(-1, val);
+ } else {
+ name = parse_host_name(cfile);
+ if (!name)
+ parse_error(cfile,
+ "expecting a name for shared-network");
+ }
+ mapSet(share, createString(name), "name");
+
+ subnets = createList();
+ mapSet(share, subnets,
+ local_family == AF_INET ? "subnet4" : "subnet6");
+
+ parse_lbrace(cfile);
+
+ stackPush(cfile, share);
+
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ skip_token(&val, NULL, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "unexpected end of file");
+ } else if (token == INTERFACE) {
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (mapContains(share, "interface"))
+ parse_error(cfile,
+ "A shared network can't be "
+ "connected to two interfaces.");
+ interface = createString(makeString(-1, val));
+ mapSet(share, interface, "interface");
+ new_network_interface(cfile, interface);
+ parse_semi(cfile);
+ continue;
+ }
+
+ declaration = parse_statement(cfile, SHARED_NET_DECL,
+ declaration);
+ }
+
+ cfile->stack_top--;
+
+ if (listSize(subnets) == 0)
+ parse_error(cfile, "empty shared-network decl");
+ if (listSize(subnets) > 1) {
+ struct element *shares;
+ struct element *pools;
+
+ shares = mapGet(cfile->stack[cfile->stack_top],
+ "shared-networks");
+ if (shares == NULL) {
+ struct comment *comment;
+
+ shares = createList();
+ shares->kind = SHARED_NET_DECL;
+ mapSet(cfile->stack[cfile->stack_top],
+ shares, "shared-networks");
+ comment = createComment("/// Kea shared-networks "
+ "are different, cf Kea #236");
+ TAILQ_INSERT_TAIL(&shares->comments, comment);
+ }
+ listPush(shares, share);
+
+ /* Pools are forbidden at shared-network level in Kea */
+ relocate_pools(share);
+ pools = mapGet(share, "pools");
+ if ((pools != NULL) && (listSize(pools) == 0)) {
+ mapRemove(share, "pools");
+ pools = NULL;
+ }
+ if (pools != NULL) {
+ struct comment *comment;
+
+ pools->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ comment = createComment("/// Kea pools must be "
+ "in a subnet");
+ TAILQ_INSERT_TAIL(&pools->comments, comment);
+ comment = createComment("/// Reference Kea #249");
+ TAILQ_INSERT_TAIL(&pools->comments, comment);
+ }
+ pools = mapGet(share, "pd-pools");
+ if ((pools != NULL) && (listSize(pools) == 0)) {
+ mapRemove(share, "pd-pools");
+ pools = NULL;
+ }
+ if (pools != NULL) {
+ struct comment *comment;
+
+ pools->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ comment = createComment("/// Kea pools must be "
+ "in a subnet");
+ TAILQ_INSERT_TAIL(&pools->comments, comment);
+ comment = createComment("/// Reference Kea #249");
+ TAILQ_INSERT_TAIL(&pools->comments, comment);
+ }
+ return;
+ }
+
+ /* There is one subnet so the shared network is useless */
+ subnet = listGet(subnets, 0);
+ listRemove(subnets, 0);
+ mapRemove(share, "name");
+ mapRemove(share, local_family == AF_INET ? "subnet4" : "subnet6");
+ /* specific case before calling generic merge */
+ if (mapContains(share, "pools") &&
+ mapContains(subnet, "pools")) {
+ struct element *pools;
+ struct element *sub;
+
+ pools = mapGet(share, "pools");
+ mapRemove(share, "pools");
+ sub = mapGet(subnet, "pools");
+ concat(sub, pools);
+ }
+ if (mapContains(share, "pd-pools") &&
+ mapContains(subnet, "pd-pools")) {
+ struct element *pools;
+ struct element *sub;
+
+ pools = mapGet(share, "pd-pools");
+ mapRemove(share, "pd-pools");
+ sub = mapGet(subnet, "pd-pools");
+ concat(sub, pools);
+ }
+ if (mapContains(share, "option-data") &&
+ mapContains(subnet, "option-data")) {
+ struct element *opt_list;
+ struct element *sub;
+
+ opt_list = mapGet(share, "option-data");
+ mapRemove(share, "option-data");
+ sub = mapGet(subnet, "option-data");
+ merge_option_data(opt_list, sub);
+ }
+ merge(subnet, share);
+
+ if (local_family == AF_INET) {
+ subnets = mapGet(cfile->stack[1], "subnet4");
+ if (subnets == NULL) {
+ subnets = createList();
+ subnets->kind = SUBNET_DECL;
+ mapSet(cfile->stack[1], subnets, "subnet4");
+ }
+ } else {
+ subnets = mapGet(cfile->stack[1], "subnet6");
+ if (subnets == NULL) {
+ subnets = createList();
+ subnets->kind = SUBNET_DECL;
+ mapSet(cfile->stack[1], subnets, "subnet6");
+ }
+ }
+ listPush(subnets, subnet);
+}
+
+static void
+common_subnet_parsing(struct parse *cfile,
+ struct element *subnets,
+ struct element *subnet)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *interface;
+ int declaration = 0;
+
+ parse_lbrace(cfile);
+
+ stackPush(cfile, subnet);
+
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ skip_token(&val, NULL, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "unexpected end of file");
+ break;
+ } else if (token == INTERFACE) {
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (mapContains(subnet, "interface"))
+ parse_error(cfile,
+ "A subnet can't be connected "
+ "to two interfaces.");
+ interface = createString(makeString(-1, val));
+ mapSet(subnet, interface, "interface");
+ new_network_interface(cfile, interface);
+ parse_semi(cfile);
+ continue;
+ }
+ declaration = parse_statement(cfile, SUBNET_DECL, declaration);
+ }
+
+ cfile->stack_top--;
+
+ /* Add the subnet to the list of subnets in this shared net. */
+ listPush(subnets, subnet);
+
+ return;
+}
+
+/* subnet-declaration :==
+ net NETMASK netmask RBRACE parameters declarations LBRACE */
+
+void
+parse_subnet_declaration(struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct element *subnet;
+ struct subnet *chain;
+ struct element *subnets;
+ struct string *address;
+ struct string *netmask;
+ struct string *prefix;
+ unsigned char addr[4];
+ unsigned len = sizeof(addr);
+ size_t parent = 0;
+ size_t i;
+ int kind = 0;
+
+ subnet = createMap();
+ subnet->kind = SUBNET_DECL;
+ TAILQ_CONCAT(&subnet->comments, &cfile->comments);
+
+ subnet_counter++;
+ mapSet(subnet, createInt(subnet_counter), "id");
+
+ chain = (struct subnet *)malloc(sizeof(*chain));
+ if (chain == NULL)
+ parse_error(cfile, "can't allocate subnet");
+ memset(chain, 0, sizeof(*chain));
+ chain->subnet = subnet;
+ TAILQ_INSERT_TAIL(&known_subnets, chain);
+
+ /* Find parent */
+ for (i = cfile->stack_top; i > 0; --i) {
+ kind = cfile->stack[i]->kind;
+ if ((kind == SHARED_NET_DECL) ||
+ (kind == GROUP_DECL) ||
+ (kind == ROOT_GROUP)) {
+ parent = i;
+ break;
+ }
+ }
+ if (kind == 0)
+ parse_error(cfile, "can't find a place to put subnet");
+ if (kind == SHARED_NET_DECL)
+ chain->share = cfile->stack[parent];
+ subnets = mapGet(cfile->stack[parent], "subnet4");
+ if (subnets == NULL) {
+ if (kind == SHARED_NET_DECL)
+ parse_error(cfile, "shared network without subnets");
+ subnets = createList();
+ subnets->kind = SUBNET_DECL;
+ mapSet(cfile->stack[parent], subnets, "subnet4");
+ }
+
+ /* Get the network number... */
+ address = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
+ if (address == NULL)
+ parse_error(cfile, "can't decode network number");
+ if (address->length != 4)
+ parse_error(cfile, "bad IPv4 address length");
+ chain->addr = address;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NETMASK)
+ parse_error(cfile, "Expecting netmask");
+
+ /* Get the netmask... */
+ netmask = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
+ if (netmask == NULL)
+ parse_error(cfile, "can't decode network mask");
+ if (netmask->length != address->length)
+ parse_error(cfile, "bad IPv4 mask length");
+ chain->mask = netmask;
+
+ prefix = addrmask(address, netmask);
+ if (prefix == NULL) {
+ char bufa[INET_ADDRSTRLEN];
+ char bufm[INET_ADDRSTRLEN];
+
+ inet_ntop(AF_INET, address->content, bufa, INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, netmask->content, bufm, INET_ADDRSTRLEN);
+ parse_error(cfile, "can't get a prefix from %s mask %s",
+ bufa, bufm);
+ }
+ mapSet(subnet, createString(prefix), "subnet");
+
+ common_subnet_parsing(cfile, subnets, subnet);
+}
+
+/* subnet6-declaration :==
+ net / bits RBRACE parameters declarations LBRACE */
+
+void
+parse_subnet6_declaration(struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *subnet;
+ struct subnet *chain;
+ struct element *subnets;
+ struct string *address;
+ struct string *prefix;
+ struct string *netmask;
+ size_t parent = 0;
+ size_t i;
+ int kind = 0;
+ char *p;
+
+ if (local_family != AF_INET6)
+ parse_error(cfile, "subnet6 statement is only supported "
+ "in DHCPv6 mode.");
+
+ subnet = createMap();
+ subnet->kind = SUBNET_DECL;
+ TAILQ_CONCAT(&subnet->comments, &cfile->comments);
+
+ subnet_counter++;
+ mapSet(subnet, createInt(subnet_counter), "id");
+
+ chain = (struct subnet *)malloc(sizeof(*chain));
+ if (chain == NULL)
+ parse_error(cfile, "can't allocate subnet");
+ memset(chain, 0, sizeof(*chain));
+ chain->subnet = subnet;
+ TAILQ_INSERT_TAIL(&known_subnets, chain);
+
+ /* Find parent */
+ for (i = cfile->stack_top; i > 0; --i) {
+ kind = cfile->stack[i]->kind;
+ if ((kind == SHARED_NET_DECL) ||
+ (kind == GROUP_DECL) ||
+ (kind == ROOT_GROUP)) {
+ parent = i;
+ break;
+ }
+ }
+ if (kind == 0)
+ parse_error(cfile, "can't find a place to put subnet");
+ if (kind == SHARED_NET_DECL)
+ chain->share = cfile->stack[parent];
+ subnets = mapGet(cfile->stack[parent], "subnet6");
+ if (subnets == NULL) {
+ if (kind == SHARED_NET_DECL)
+ parse_error(cfile, "shared network without subnets");
+ subnets = createList();
+ subnets->kind = SUBNET_DECL;
+ mapSet(cfile->stack[parent], subnets, "subnet6");
+ }
+
+ address = parse_ip6_addr(cfile);
+ if (address == NULL)
+ parse_error(cfile, "can't decode network number");
+ if (address->length != 16)
+ parse_error(cfile, "bad IPv6 address length");
+ chain->addr = address;
+
+ prefix = makeStringExt(address->length, address->content, '6');
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SLASH)
+ parse_error(cfile, "Expecting a '/'.");
+ appendString(prefix, val);
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "Expecting a number.");
+ appendString(prefix, val);
+
+ netmask = makeString(16, "0123456789abcdef");
+ memset(netmask->content, 0, 16);
+ p = netmask->content;
+ for (i = atoi(val); i >= 8; i -= 8)
+ *p++ = 0xff;
+ *p = 0xff << (8 - i);
+ chain->mask = netmask;
+
+ mapSet(subnet, createString(prefix), "subnet");
+
+ common_subnet_parsing(cfile, subnets, subnet);
+}
+
+/* group-declaration :== RBRACE parameters declarations LBRACE */
+
+void
+parse_group_declaration(struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct element *group;
+ int declaration = 0;
+ struct string *name = NULL;
+
+ if (mapContains(cfile->stack[cfile->stack_top], "group"))
+ parse_error(cfile, "another group is already open");
+ group = createMap();
+ group->skip = ISC_TRUE;
+ group->kind = GROUP_DECL;
+ TAILQ_CONCAT(&group->comments, &cfile->comments);
+ mapSet(cfile->stack[cfile->stack_top], group, "group");
+
+ token = peek_token(&val, NULL, cfile);
+ if (is_identifier(token) || token == STRING) {
+ skip_token(&val, NULL, cfile);
+
+ name = makeString(-1, val);
+ if (!name)
+ parse_error(cfile, "no memory for group decl name %s",
+ val);
+ }
+
+ parse_lbrace(cfile);
+
+ stackPush(cfile, group);
+
+ for (;;) {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ skip_token(&val, NULL, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "unexpected end of file");
+ break;
+ } else if (token == TOKEN_DELETED) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "deleted groups don't exist "
+ "in the config file");
+ } else if (token == DYNAMIC) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "dynamic groups don't exist "
+ "in the config file");
+ } else if (token == STATIC) {
+ skip_token(&val, NULL, cfile);
+ parse_error(cfile, "static groups don't exist "
+ "in the config file");
+ }
+ declaration = parse_statement(cfile, GROUP_DECL, declaration);
+ }
+
+ cfile->stack_top--;
+
+ if (name != NULL)
+ mapSet(group, createString(name), "name");
+ close_group(cfile, group);
+}
+
+/*
+ * Close a group. Called when a group is closed.
+ * - spread parameters to children
+ * - attach declarations at an upper level
+ */
+
+void
+close_group(struct parse *cfile, struct element *group)
+{
+ struct handle *handle;
+ struct handle *nh;
+ struct element *parent;
+ struct element *item;
+ struct element *param;
+ struct handle *hosts = NULL;
+ struct handle *shares = NULL;
+ struct handle *subnets = NULL;
+ struct handle *classes = NULL;
+ struct handle *pdpools = NULL;
+ struct handle *pools = NULL;
+ struct handles downs;
+ struct comment *comment;
+ const char *key = NULL;
+ const char *name = NULL;
+ unsigned order = 0;
+ isc_boolean_t marked = ISC_FALSE;
+
+ TAILQ_INIT(&downs);
+
+ /* check that group is in its parent */
+ parent = cfile->stack[cfile->stack_top];
+ if (parent->kind == PARAMETER)
+ parse_error(cfile, "unexpected kind for group parent %d",
+ parent->kind);
+ item = mapGet(parent, "group");
+ if (item == NULL)
+ parse_error(cfile, "no group in parent");
+ if (item != group)
+ parse_error(cfile, "got a different group from parent");
+ mapRemove(parent, "group");
+
+ /* classify content */
+ while (mapSize(group) > 0) {
+ handle = mapPop(group);
+ if ((handle == NULL) || (handle->key == NULL) ||
+ (handle->value == NULL))
+ parse_error(cfile, "can't get group item at %u",
+ order);
+ handle->order = order++;
+ switch (handle->value->kind) {
+ case TOPLEVEL:
+ case ROOT_GROUP:
+ case GROUP_DECL:
+ badkind:
+ parse_error(cfile, "impossible group item (kind %d) "
+ "for %s at order %u",
+ handle->value->kind, handle->key, order);
+
+ case HOST_DECL:
+ if (strcmp(handle->key, "reservations") != 0)
+ parse_error(cfile, "expected reservations "
+ "got %s at %u",
+ handle->key, order);
+ if (hosts != NULL)
+ parse_error(cfile, "got reservations twice "
+ "at %u and %u",
+ hosts->order, order);
+ if ((parent->kind == HOST_DECL) ||
+ (parent->kind == CLASS_DECL))
+ parse_error(cfile, "host declarations not "
+ "allowed here.");
+ hosts = handle;
+ handle = NULL;
+ break;
+
+ case SHARED_NET_DECL:
+ if (strcmp(handle->key, "shared-networks") != 0)
+ parse_error(cfile, "expected shared-networks "
+ "got %s at %u",
+ handle->key, order);
+ if ((parent->kind == SHARED_NET_DECL) ||
+ (parent->kind == HOST_DECL) ||
+ (parent->kind == SUBNET_DECL) ||
+ (parent->kind == CLASS_DECL))
+ parse_error(cfile, "shared-network parameters "
+ "not allowed here.");
+ shares = handle;
+ handle = NULL;
+ break;
+
+ case SUBNET_DECL:
+ key = local_family == AF_INET ? "subnet4" : "subnet6";
+ if (strcmp(handle->key, key) != 0)
+ parse_error(cfile, "expected %s got %s at %u",
+ key, handle->key, order);
+ if (subnets != NULL)
+ parse_error(cfile, "got %s twice at %u and %u",
+ key, subnets->order, order);
+ if ((parent->kind == HOST_DECL) ||
+ (parent->kind == SUBNET_DECL) ||
+ (parent->kind == CLASS_DECL))
+ parse_error(cfile, "subnet declarations not "
+ "allowed here.");
+ subnets = handle;
+ handle = NULL;
+ break;
+
+ case CLASS_DECL:
+ if (strcmp(handle->key, "client-classes") != 0)
+ parse_error(cfile, "expected client-classes "
+ "got %s at %u",
+ handle->key, order);
+ if (classes != NULL)
+ parse_error(cfile, "got %s twice at %u and %u",
+ key, classes->order, order);
+ if (parent->kind == CLASS_DECL)
+ parse_error(cfile, "class declarations not "
+ "allowed here.");
+ classes = handle;
+ handle = NULL;
+ break;
+
+ case POOL_DECL:
+ if (strcmp(handle->key, "pd-pools") == 0) {
+ if (pdpools != NULL)
+ parse_error(cfile, "got pd-pools "
+ "twice at %u and %u",
+ pdpools->order, order);
+ pdpools = handle;
+ } else if (strcmp(handle->key, "pools") == 0) {
+ if (pools != NULL)
+ parse_error(cfile, "got pools twice "
+ "at %u and %u",
+ pools->order, order);
+ pools = handle;
+ } else
+ parse_error(cfile, "expecyed [pd-]pools got "
+ "%s at %u",
+ handle->key, order);
+ if (parent->kind == POOL_DECL)
+ parse_error(cfile, "pool declared within "
+ "pool.");
+ if ((parent->kind == HOST_DECL) ||
+ (parent->kind == CLASS_DECL))
+ parse_error(cfile, "pool declared outside "
+ "of network");
+ handle = NULL;
+ break;
+ default:
+ if (handle->value->kind != PARAMETER)
+ goto badkind;
+ }
+ if (handle == NULL)
+ continue;
+
+ /* we have a parameter */
+ param = handle->value;
+ /* group name */
+ if (strcmp(handle->key, "name") == 0) {
+ name = stringValue(param)->content;
+ continue;
+ }
+ /* unexpected values */
+ if ((strcmp(handle->key, "reservations") == 0) ||
+ (strcmp(handle->key, "group") == 0) ||
+ (strcmp(handle->key, "shared-networks") == 0) ||
+ (strcmp(handle->key, "subnet4") == 0) ||
+ (strcmp(handle->key, "subnet6") == 0) ||
+ (strcmp(handle->key, "subnet") == 0) ||
+ (strcmp(handle->key, "client-classes") == 0) ||
+ (strcmp(handle->key, "hw-address") == 0) ||
+ (strcmp(handle->key, "ip-address") == 0) ||
+ (strcmp(handle->key, "extra-ip-addresses") == 0) ||
+ (strcmp(handle->key, "ip-addresses") == 0) ||
+ (strcmp(handle->key, "prefixes") == 0) ||
+ (strcmp(handle->key, "pool") == 0) ||
+ (strcmp(handle->key, "prefix") == 0) ||
+ (strcmp(handle->key, "delegated-len") == 0) ||
+ (strcmp(handle->key, "prefix-len") == 0) ||
+ (strcmp(handle->key, "prefix-highest") == 0) ||
+ (strcmp(handle->key, "option-def") == 0) ||
+ (strcmp(handle->key, "hostname") == 0) ||
+ (strcmp(handle->key, "client-id") == 0) ||
+ (strcmp(handle->key, "host-identifier") == 0) ||
+ (strcmp(handle->key, "flex-id") == 0) ||
+ (strcmp(handle->key, "test") == 0) ||
+ (strcmp(handle->key, "authoritative") == 0) ||
+ (strcmp(handle->key, "dhcp-ddns") == 0) ||
+ (strcmp(handle->key, "host-reservation-identifiers") == 0))
+ parse_error(cfile, "unexpected parameter %s "
+ "in group at %u",
+ handle->key, order);
+
+ /* to parent at group position */
+ if ((strcmp(handle->key, "option-space") == 0) ||
+ (strcmp(handle->key, "server-duid") == 0) ||
+ (strcmp(handle->key, "statement") == 0) ||
+ (strcmp(handle->key, "config") == 0) ||
+ (strcmp(handle->key, "ddns-update-style") == 0) ||
+ (strcmp(handle->key, "echo-client-id") == 0)) {
+ if (!marked) {
+ struct string *msg;
+
+ marked = ISC_TRUE;
+ msg = makeString(-1, "/// moved from group");
+ if (name != NULL)
+ appendString(msg, " ");
+ appendString(msg, name);
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&param->comments, comment);
+ }
+ mapSet(parent, param, handle->key);
+ free(handle);
+ continue;
+ }
+ /* To reconsider: qualifying-suffix, enable-updates */
+ if ((strcmp(handle->key, "option-data") == 0) ||
+ (strcmp(handle->key, "allow") == 0) ||
+ (strcmp(handle->key, "deny") == 0) ||
+ (strcmp(handle->key, "interface") == 0) ||
+ (strcmp(handle->key, "valid-lifetime") == 0) ||
+ (strcmp(handle->key, "preferred-lifetime") == 0) ||
+ (strcmp(handle->key, "renew-timer") == 0) ||
+ (strcmp(handle->key, "rebind-timer") == 0) ||
+ (strcmp(handle->key, "boot-file-name") == 0) ||
+ (strcmp(handle->key, "server-hostname") == 0) ||
+ (strcmp(handle->key, "next-server") == 0) ||
+ (strcmp(handle->key, "match-client-id") == 0)) {
+ TAILQ_INSERT_TAIL(&downs, handle);
+ continue;
+ }
+ /* unknown */
+ if (!marked) {
+ struct string *msg;
+
+ marked = ISC_TRUE;
+ msg = makeString(-1, "/// moved from group");
+ if (name != NULL)
+ appendString(msg, " ");
+ appendString(msg, name);
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&param->comments, comment);
+ }
+ comment = createComment("/// unhandled parameter");
+ TAILQ_INSERT_TAIL(&param->comments, comment);
+ param->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(parent, param, handle->key);
+ free(handle);
+ }
+ TAILQ_FOREACH_SAFE(handle, &downs, nh) {
+ if (strcmp(handle->key, "option-data") == 0) {
+ option_data_derive(cfile, handle, hosts);
+ option_data_derive(cfile, handle, shares);
+ option_data_derive(cfile, handle, subnets);
+ derive_classes(cfile, handle, classes);
+ option_data_derive(cfile, handle, pdpools);
+ option_data_derive(cfile, handle, pools);
+ } else if ((strcmp(handle->key, "allow") == 0) ||
+ (strcmp(handle->key, "deny") == 0)) {
+ derive(handle, pdpools);
+ derive(handle, pools);
+ } else if ((strcmp(handle->key, "interface") == 0) ||
+ (strcmp(handle->key, "valid-lifetime") == 0) ||
+ (strcmp(handle->key, "preferred-lifetime") == 0) ||
+ (strcmp(handle->key, "renew-timer") == 0) ||
+ (strcmp(handle->key, "rebind-timer") == 0) ||
+ (strcmp(handle->key, "match-client-id") == 0)) {
+ derive(handle, shares);
+ derive(handle, subnets);
+ } else if ((strcmp(handle->key, "boot-file-name") == 0) ||
+ (strcmp(handle->key, "server-hostname") == 0)) {
+ derive(handle, hosts);
+ derive_classes(cfile, handle, classes);
+ } else if (strcmp(handle->key, "next-server") == 0) {
+ derive(handle, hosts);
+ derive(handle, subnets);
+ derive_classes(cfile, handle, classes);
+ } else
+ parse_error(cfile, "unexpected parameter %s to derive",
+ handle->key);
+ }
+ if (hosts != NULL) {
+ struct element *root;
+
+ root = mapGet(cfile->stack[1], "reservations");
+ if (root == NULL)
+ mapSet(cfile->stack[1], hosts->value, "reservations");
+ else
+ concat(root, hosts->value);
+ }
+ if (shares != NULL) {
+ struct element *upper;
+
+ upper = mapGet(parent, "shared-networks");
+ if (upper == NULL)
+ mapSet(parent, shares->value, "shared-networks");
+ else
+ concat(upper, shares->value);
+ }
+ key = local_family == AF_INET ? "subnet4" : "subnet6";
+ if (subnets != NULL) {
+ struct element *upper;
+
+ upper = mapGet(parent, key);
+ if (upper == NULL)
+ mapSet(parent, subnets->value, key);
+ else
+ concat(upper, subnets->value);
+ }
+ if (classes != NULL) {
+ struct element *upper;
+ size_t where;
+ int kind = 0;
+
+ for (where = cfile->stack_top; where > 0; --where) {
+ kind = cfile->stack[where]->kind;
+ if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
+ break;
+ }
+ if (kind == GROUP_DECL) {
+ upper = mapGet(cfile->stack[where], "client-classes");
+ if (upper == NULL)
+ mapSet(cfile->stack[where],
+ classes->value,
+ "client-classes");
+ else
+ concat_classes(cfile, upper, classes->value);
+ }
+ }
+ if (pdpools != NULL) {
+ struct element *upper;
+
+ upper = mapGet(parent, "pd-pools");
+ if (upper == NULL)
+ mapSet(parent, pdpools->value, "pools");
+ else
+ concat(upper, pdpools->value);
+ }
+ if (pools != NULL) {
+ struct element *upper;
+
+ upper = mapGet(parent, "pools");
+ if (upper == NULL)
+ mapSet(parent, pools->value, "pools");
+ else
+ concat(upper, pools->value);
+ }
+}
+
+/*
+ * Specialized derivation routine for option-data
+ * (options are identified by space + name and/or code
+ */
+
+static void
+option_data_derive(struct parse *cfile, struct handle *src, struct handle *dst)
+{
+ struct element *list;
+ struct element *item;
+ struct element *opt_list;
+ size_t i;
+
+ if (dst == NULL)
+ return;
+ list = dst->value;
+ assert(list != NULL);
+ assert(list->type == ELEMENT_LIST);
+ for (i = 0; i < listSize(list); i++) {
+ item = listGet(list, i);
+ assert(item != NULL);
+ assert(item->type == ELEMENT_MAP);
+ opt_list = mapGet(item, src->key);
+ if (opt_list != NULL) {
+ merge_option_data(src->value, opt_list);
+ continue;
+ }
+ opt_list = copy(src->value);
+ mapSet(item, opt_list, src->key);
+ }
+}
+
+/*
+ * Specialized derivation routine for classes
+ * (which are by reference so a resolution step is needed)
+ */
+static void
+derive_classes(struct parse *cfile, struct handle *src, struct handle *dst)
+{
+ struct element *list;
+ struct element *item;
+ size_t i;
+
+ if (dst == NULL)
+ return;
+ list = dst->value;
+ assert(list != NULL);
+ assert(list->type == ELEMENT_LIST);
+ for (i = 0; i < listSize(list); i++) {
+ item = listGet(list, i);
+ assert(item != NULL);
+ assert(item->type == ELEMENT_MAP);
+ item = get_class(cfile, item);
+ if (item == NULL)
+ parse_error(cfile, "dangling class reference");
+ if (strcmp(src->key, "option-data") == 0) {
+ struct element *opt_list;
+
+ opt_list = mapGet(item, "option-data");
+ if (opt_list != NULL)
+ merge_option_data(src->value, opt_list);
+ else
+ mapSet(item, copy(src->value), "option-data");
+ continue;
+ }
+ if (mapContains(item, src->key))
+ continue;
+ mapSet(item, copy(src->value), src->key);
+ }
+}
+
+/* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
+ ip-addrs-or-hostnames :== ip-addr-or-hostname
+ | ip-addrs-or-hostnames ip-addr-or-hostname */
+
+struct element *
+parse_fixed_addr_param(struct parse *cfile, enum dhcp_token type) {
+ const char *val;
+ enum dhcp_token token;
+ struct element *addr;
+ struct element *addresses;
+ struct string *address;
+
+ addresses = createList();
+ TAILQ_CONCAT(&addresses->comments, &cfile->comments);
+
+ do {
+ address = NULL;
+ if (type == FIXED_ADDR)
+ address = parse_ip_addr_or_hostname(cfile, ISC_TRUE);
+ else if (type == FIXED_ADDR6)
+ address = parse_ip6_addr_txt(cfile);
+ else
+ parse_error(cfile, "requires FIXED_ADDR[6]");
+ if (address == NULL)
+ parse_error(cfile, "can't parse fixed address");
+ addr = createString(address);
+ /* Take the comment for resolution into multiple addresses */
+ TAILQ_CONCAT(&addr->comments, &cfile->comments);
+ listPush(addresses, addr);
+ token = peek_token(&val, NULL, cfile);
+ if (token == COMMA)
+ token = next_token(&val, NULL, cfile);
+ } while (token == COMMA);
+
+ parse_semi(cfile);
+
+ /* Sanity */
+ if (listSize(addresses) == 0)
+ parse_error(cfile, "can't get fixed address");
+
+ return addresses;
+
+}
+
+#ifdef notyet
+/* Parse the right side of a 'binding value'.
+ *
+ * set foo = "bar"; is a string
+ * set foo = false; is a boolean
+ * set foo = %31; is a numeric value.
+ */
+static struct element *
+parse_binding_value(struct parse *cfile)
+{
+ struct element *value = NULL;
+ struct string *data;
+ const char *val;
+ unsigned buflen;
+ int token;
+
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ skip_token(&val, &buflen, cfile);
+ data = makeString(buflen, val);
+ value = createString(data);
+ } else if (token == NUMBER_OR_NAME) {
+ value = createMap();
+ data = parse_hexa(cfile);
+ mapSet(value, createHexa(data), "const-data");
+ } else if (token == PERCENT) {
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting decimal number.");
+ value = createInt(atol(val));
+ } else if (token == NAME) {
+ token = next_token(&val, NULL, cfile);
+ if (!strcasecmp(val, "true"))
+ value = createBool(ISC_TRUE);
+ else if (!strcasecmp(val, "false"))
+ value = createBool(ISC_FALSE);
+ else
+ parse_error(cfile, "expecting true or false");
+ } else
+ parse_error(cfile, "expecting a constant value.");
+
+ return value;
+}
+#endif
+
+/* address-range-declaration :== ip-address ip-address SEMI
+ | DYNAMIC_BOOTP ip-address ip-address SEMI */
+
+void
+parse_address_range(struct parse *cfile, int type, size_t where)
+{
+ struct string *low, *high, *range;
+ unsigned char addr[4];
+ unsigned len = sizeof(addr);
+ enum dhcp_token token;
+ const char *val;
+ struct element *pool;
+ struct element *r;
+ struct range *chain;
+ size_t i;
+ int kind;
+
+ if ((token = peek_token(&val, NULL, cfile)) == DYNAMIC_BOOTP) {
+ skip_token(&val, NULL, cfile);
+ }
+
+ /* Get the bottom address in the range... */
+ low = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
+ if (low == NULL)
+ parse_error(cfile, "can't parse range (low)");
+
+ /* Only one address? */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI)
+ high = low;
+ else {
+ /* Get the top address in the range... */
+ high = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
+ if (high == NULL)
+ parse_error(cfile, "can't parse range (high)");
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+
+ if (type != POOL_DECL) {
+ struct element *group;
+ struct element *pools;
+#ifdef want_bootp
+ struct element *permit;
+#endif
+
+ group = cfile->stack[where];
+ pool = createMap();
+#ifdef want_bootp
+ permit = createList();
+ permit->skip = ISC_TRUE;
+
+ /* Dynamic pools permit all clients. Otherwise
+ we prohibit BOOTP clients. */
+ if (dynamic) {
+ struct string *all;
+
+ all = makeString(-1, "all clients");
+ listPush(permit, createString(all));
+ mapSet(pool, permit, "allow");
+ } else {
+ struct string *dyn_bootp;
+
+ dyn_bootp = makeString(-1, "dynamic bootp clients");
+ listPush(permit, createString(dyn_bootp));
+ mapSet(pool, permit, "deny");
+ }
+#endif
+
+ pools = mapGet(group, "pools");
+ if (pools == NULL) {
+ pools = createList();
+ pools->kind = POOL_DECL;
+ mapSet(group, pools, "pools");
+ }
+ listPush(pools, pool);
+ } else
+ pool = cfile->stack[where];
+
+ /* Create the new address range... */
+ if (memcmp(high->content, low->content, high->length) < 0) {
+ struct string *swap;
+
+ swap = low;
+ low = high;
+ high = swap;
+ }
+ range = makeStringExt(low->length, low->content, 'I');
+ appendString(range, " - ");
+ concatString(range, makeStringExt(high->length, high->content, 'I'));
+
+ r = createString(range);
+ TAILQ_CONCAT(&r->comments, &cfile->comments);
+
+ mapSet(pool, r, "pool");
+
+ chain = (struct range *)malloc(sizeof(*chain));
+ if (chain == NULL)
+ parse_error(cfile, "can't allocate range");
+ memset(chain, 0, sizeof(*chain));
+ chain->pool = pool;
+ for (i = where; i > 0; --i) {
+ kind = cfile->stack[i]->kind;
+ if (kind == SHARED_NET_DECL) {
+ chain->share = cfile->stack[i];
+ break;
+ }
+ }
+ chain->low = low;
+ TAILQ_INSERT_TAIL(&known_ranges, chain);
+}
+
+/* address-range6-declaration :== ip-address6 ip-address6 SEMI
+ | ip-address6 SLASH number SEMI
+ | ip-address6 [SLASH number] TEMPORARY SEMI */
+
+void
+parse_address_range6(struct parse *cfile, int type, size_t where)
+{
+ struct string *low, *high, *range;
+ enum dhcp_token token;
+ const char *val;
+ isc_boolean_t is_temporary = ISC_FALSE;
+ struct element *pool;
+ struct element *r;
+ struct range *chain;
+ size_t i;
+ int kind;
+
+ if (local_family != AF_INET6)
+ parse_error(cfile, "range6 statement is only supported "
+ "in DHCPv6 mode.");
+
+ /*
+ * Read starting address as text.
+ */
+ low = parse_ip6_addr_txt(cfile);
+ if (low == NULL)
+ parse_error(cfile, "can't parse range6 address (low)");
+ range = allocString();
+ concatString(range, low);
+
+ /*
+ * See if we we're using range or CIDR notation or TEMPORARY
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SLASH) {
+ appendString(range, val);
+ /*
+ * '/' means CIDR notation, so read the bits we want.
+ */
+ skip_token(NULL, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting number");
+ /*
+ * no sanity checks
+ */
+ appendString(range, val);
+ /*
+ * can be temporary (RFC 4941 like)
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == TEMPORARY) {
+ is_temporary = ISC_TRUE;
+ appendString(range, " ");
+ appendString(range, val);
+ skip_token(NULL, NULL, cfile);
+ }
+ } else if (token == TEMPORARY) {
+ /*
+ * temporary (RFC 4941)
+ */
+ is_temporary = ISC_TRUE;
+ appendString(range, "/64 ");
+ appendString(range, val);
+ skip_token(NULL, NULL, cfile);
+ } else {
+ /*
+ * No '/', so we are looking for the end address of
+ * the IPv6 pool.
+ */
+ high = parse_ip6_addr_txt(cfile);
+ if (high == NULL)
+ parse_error(cfile,
+ "can't parse range6 address (high)");
+ /* No sanity checks */
+ appendString(range, " - ");
+ appendString(range, high->content);
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+
+ if (type != POOL_DECL) {
+ struct element *group;
+ struct element *pools;
+
+ group = cfile->stack[where];
+ pool = createMap();
+ pools = mapGet(group, "pools");
+ if (pools == NULL) {
+ pools = createList();
+ pools->kind = POOL_DECL;
+ mapSet(group, pools, "pools");
+ }
+ listPush(pools, pool);
+ } else
+ pool = cfile->stack[where];
+
+ r = createString(range);
+ TAILQ_CONCAT(&r->comments, &cfile->comments);
+ if (is_temporary) {
+ pool->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(pool, r, "pool");
+
+ chain = (struct range *)malloc(sizeof(*chain));
+ if (chain == NULL)
+ parse_error(cfile, "can't allocate range");
+ memset(chain, 0, sizeof(*chain));
+ chain->pool = pool;
+ for (i = where; i > 0; --i) {
+ kind = cfile->stack[i]->kind;
+ if (kind == SHARED_NET_DECL) {
+ chain->share = cfile->stack[i];
+ break;
+ }
+ }
+ chain->low = low;
+ TAILQ_INSERT_TAIL(&known_ranges, chain);
+}
+
+/* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
+
+void
+parse_prefix6(struct parse *cfile, int type, size_t where)
+{
+ struct string *lo, *hi;
+ int plen;
+ int bits;
+ enum dhcp_token token;
+ const char *val;
+ struct element *pool;
+ struct element *prefix;
+
+ if (local_family != AF_INET6)
+ parse_error(cfile, "prefix6 statement is only supported "
+ "in DHCPv6 mode.");
+
+ /*
+ * Read starting and ending address as text.
+ */
+ lo = parse_ip6_addr_txt(cfile);
+ if (lo == NULL)
+ parse_error(cfile, "can't parse prefix6 address (low)");
+
+ hi = parse_ip6_addr_txt(cfile);
+ if (hi == NULL)
+ parse_error(cfile, "can't parse prefix6 address (high)");
+
+ /*
+ * Next is '/' number ';'.
+ */
+ token = next_token(NULL, NULL, cfile);
+ if (token != SLASH)
+ parse_error(cfile, "expecting '/'");
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting number");
+ bits = atoi(val);
+ if ((bits <= 0) || (bits >= 128))
+ parse_error(cfile, "networks have 0 to 128 bits (exclusive)");
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+
+ if (type != POOL_DECL) {
+ struct element *group;
+ struct element *pools;
+
+ group = cfile->stack[where];
+ pool = createMap();
+ pools = mapGet(group, "pd-pools");
+ if (pools == NULL) {
+ pools = createList();
+ pools->kind = POOL_DECL;
+ mapSet(group, pools, "pd-pools");
+ }
+ listPush(pools, pool);
+ } else
+ pool = cfile->stack[where];
+
+ prefix = createString(lo);
+ TAILQ_CONCAT(&prefix->comments, &cfile->comments);
+ mapSet(pool, prefix, "prefix");
+ mapSet(pool, createInt(bits), "delegated-len");
+ plen = get_prefix_length(lo->content, hi->content);
+ if (plen >= 0)
+ mapSet(pool, createInt(plen), "prefix-len");
+ else {
+ if (!pool->skip)
+ cfile->issue_counter++;
+ pool->skip = ISC_TRUE;
+ mapSet(pool, createString(hi), "prefix-highest");
+ }
+}
+
+/* fixed-prefix6 :== ip6-address SLASH number SEMI */
+
+void
+parse_fixed_prefix6(struct parse *cfile, size_t host_decl)
+{
+ struct string *ia;
+ enum dhcp_token token;
+ const char *val;
+ struct element *host;
+ struct element *prefixes;
+ struct element *prefix;
+
+ if (local_family != AF_INET6)
+ parse_error(cfile, "fixed-prefix6 statement is only "
+ "supported in DHCPv6 mode.");
+
+ /*
+ * Get the fixed-prefix list.
+ */
+ host = cfile->stack[host_decl];
+ prefixes = mapGet(host, "prefixes");
+ if (prefixes == NULL) {
+ prefixes = createList();
+ mapSet(host, prefixes, "prefixes");
+ }
+
+ ia = parse_ip6_addr_txt(cfile);
+ if (ia == NULL)
+ parse_error(cfile, "can't parse fixed-prefix6 address");
+ token = next_token(&val, NULL, cfile);
+ if (token != SLASH)
+ parse_error(cfile, "expecting '/'");
+ appendString(ia, val);
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting number");
+ appendString(ia, val);
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+
+ prefix = createString(ia);
+ TAILQ_CONCAT(&prefix->comments, &cfile->comments);
+ listPush(prefixes, prefix);
+}
+
+/*!
+ *
+ * \brief Parse a pool6 statement
+ *
+ * Pool statements are used to group declarations and permit & deny information
+ * with a specific address range. They must be declared within a shared network
+ * or subnet and there may be multiple pools withing a shared network or subnet.
+ * Each pool may have a different set of permit or deny options.
+ *
+ * \param[in] cfile = the configuration file being parsed
+ * \param[in] type = the type of the enclosing statement. This must be
+ * SUBNET_DECL for this function.
+ *
+ * \return
+ * void - This function either parses the statement and updates the structures
+ * or it generates an error message and possible halts the program if
+ * it encounters a problem.
+ */
+
+void
+parse_pool6_statement(struct parse *cfile, int type)
+{
+ enum dhcp_token token;
+ const char *val;
+ isc_boolean_t done = ISC_FALSE;
+ struct element *pool;
+ struct element *pools;
+ struct element *pdpool;
+ struct element *pdpools;
+ struct element *permit;
+ struct element *prohibit;
+ int declaration = 0;
+ unsigned range_counter = 0;
+ unsigned prefix_counter = 0;
+
+ if (local_family != AF_INET6)
+ parse_error(cfile, "pool6 statement is only supported "
+ "in DHCPv6 mode.");
+
+ pool = createMap();
+ pool->kind = POOL_DECL;
+ TAILQ_CONCAT(&pool->comments, &cfile->comments);
+
+ if (type != SUBNET_DECL)
+ parse_error(cfile, "pool6s are only valid inside "
+ "subnet statements.");
+ parse_lbrace(cfile);
+
+ stackPush(cfile, pool);
+ type = POOL_DECL;
+
+ permit = createList();
+ prohibit = createList();
+
+ do {
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case RANGE6:
+ skip_token(NULL, NULL, cfile);
+ parse_address_range6(cfile, type, cfile->stack_top);
+ range_counter++;
+ break;
+
+ case PREFIX6:
+ skip_token(NULL, NULL, cfile);
+ parse_prefix6(cfile, type, cfile->stack_top);
+ mapSet(pool, createNull(), "***mark***");
+ prefix_counter++;
+ break;
+
+ case ALLOW:
+ skip_token(NULL, NULL, cfile);
+ get_permit(cfile, permit);
+ break;
+
+ case DENY:
+ skip_token(NULL, NULL, cfile);
+ get_permit(cfile, prohibit);
+ break;
+
+ case RBRACE:
+ skip_token(&val, NULL, cfile);
+ done = ISC_TRUE;
+ break;
+
+ case END_OF_FILE:
+ /*
+ * We can get to END_OF_FILE if, for instance,
+ * the parse_statement() reads all available tokens
+ * and leaves us at the end.
+ */
+ parse_error(cfile, "unexpected end of file");
+
+ default:
+ declaration = parse_statement(cfile, POOL_DECL,
+ declaration);
+ break;
+ }
+ } while (!done);
+
+ cfile->stack_top--;
+
+ generate_class(cfile, pool, permit, prohibit);
+
+ /*
+ * Spread and eventually split between pools and pd-pools
+ */
+ if (prefix_counter == 0) {
+ /* we need pools list */
+ pools = mapGet(cfile->stack[cfile->stack_top], "pools");
+ if (pools == NULL) {
+ pools = createList();
+ pools->kind = POOL_DECL;
+ mapSet(cfile->stack[cfile->stack_top], pools, "pools");
+ }
+
+ /* no address or prefix range */
+ if (range_counter == 0) {
+ struct comment *comment;
+
+ comment = createComment("empty pool6");
+ TAILQ_INSERT_TAIL(&pool->comments, comment);
+ pool->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ listPush(pools, pool);
+ return;
+ }
+ } else {
+ /* we need pd-pools list */
+ pdpools = mapGet(cfile->stack[cfile->stack_top], "pd-pools");
+ if (pdpools == NULL) {
+ pdpools = createList();
+ pdpools->kind = POOL_DECL;
+ mapSet(cfile->stack[cfile->stack_top],
+ pdpools, "pd-pools");
+ }
+
+ /* split and purge copies */
+ pdpool = copy(pool);
+ while (mapContains(pdpool, "pool"))
+ mapRemove(pdpool, "pool");
+ while (mapContains(pool, "prefix"))
+ mapRemove(pool, "prefix");
+ while (mapContains(pool, "prefix-len"))
+ mapRemove(pool, "prefix-len");
+ while (mapContains(pool, "delegated-len"))
+ mapRemove(pool, "delegated-len");
+ while (mapContains(pool, "excluded-prefix"))
+ mapRemove(pool, "excluded-prefix");
+ while (mapContains(pool, "excluded-prefix-len"))
+ mapRemove(pool, "excluded-prefix-len");
+ while (mapContains(pool, "***mark***"))
+ mapRemove(pool, "***mark***");
+
+ /* spread extra prefixes into pdpool copies */
+ while (--prefix_counter != 0) {
+ struct handle *handle;
+ struct element *first;
+ struct element *saved;
+ isc_boolean_t seen = ISC_FALSE;
+
+ first = createMap();
+ saved = copy(pdpool);
+ while (mapSize(pdpool) > 0) {
+ handle = mapPop(pdpool);
+ if ((handle == NULL) ||
+ (handle->key == NULL) ||
+ (handle->value == NULL))
+ parse_error(cfile, "bad pdpool entry");
+ if (strcmp(handle->key, "***mark***") == 0) {
+ if (!seen) {
+ mapRemove(saved, handle->key);
+ seen = ISC_TRUE;
+ }
+ continue;
+ }
+ if ((strcmp(handle->key, "prefix") != 0) &&
+ (strcmp(handle->key, "prefix-len") != 0) &&
+ (strcmp(handle->key,
+ "delegated-len") != 0) &&
+ (strcmp(handle->key,
+ "excluded-prefix") != 0) &&
+ (strcmp(handle->key,
+ "excluded-prefix-len") != 0))
+ mapSet(first, handle->value,
+ handle->key);
+ else if (!seen) {
+ mapSet(first, handle->value,
+ handle->key);
+ mapRemove(saved, handle->key);
+ }
+ }
+ listPush(pdpools, first);
+ pdpool = saved;
+ }
+ if (!mapContains(pdpool, "***mark***"))
+ parse_error(cfile, "can't find prefix marker");
+ mapRemove(pdpool, "***mark***");
+ if (mapContains(pdpool, "***mark***"))
+ parse_error(cfile, "unexpected prefix marker");
+ listPush(pdpools, pdpool);
+ }
+
+ /* Do pools now */
+ if (range_counter != 0) {
+ /* we need pools list */
+ pools = mapGet(cfile->stack[cfile->stack_top], "pools");
+ if (pools == NULL) {
+ pools = createList();
+ pools->kind = POOL_DECL;
+ mapSet(cfile->stack[cfile->stack_top], pools, "pools");
+ }
+
+ /* spread extra prefixes into pool copies */
+ while (--range_counter != 0) {
+ struct handle *handle;
+ struct element *first;
+ struct element *saved;
+ isc_boolean_t seen = ISC_FALSE;
+
+ first = createMap();
+ saved = copy(pool);
+ while (mapSize(pool) > 0) {
+ handle = mapPop(pool);
+ if ((handle == NULL) ||
+ (handle->key == NULL) ||
+ (handle->value == NULL))
+ parse_error(cfile, "bad pool entry");
+ if (strcmp(handle->key, "pool") != 0)
+ mapSet(first, handle->value,
+ handle->key);
+ else if (!seen) {
+ mapSet(first, handle->value,
+ handle->key);
+ mapRemove(saved, "pool");
+ seen = ISC_TRUE;
+ }
+ }
+ listPush(pools, first);
+ pool = saved;
+ }
+ listPush(pools, pool);
+ }
+}
+
+/* allow-deny-keyword :== BOOTP
+ | BOOTING
+ | DYNAMIC_BOOTP
+ | UNKNOWN_CLIENTS */
+
+struct element *
+parse_allow_deny(struct parse *cfile, int flag)
+{
+ enum dhcp_token token;
+ const char *val;
+ const char *value;
+ const char *name;
+ struct element *config;
+ struct option *option;
+
+ switch (flag) {
+ case 0:
+ value = "deny";
+ break;
+ case 1:
+ value = "allow";
+ break;
+ case 2:
+ value = "ignore";
+ break;
+ default:
+ value = "unknown?";
+ break;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case TOKEN_BOOTP:
+ name = "allow-bootp";
+ break;
+
+ case BOOTING:
+ name = "allow-booting";
+ break;
+
+ case DYNAMIC_BOOTP:
+ name = "dynamic-bootp";
+ break;
+
+ case UNKNOWN_CLIENTS:
+ name = "boot-unknown-clients";
+ break;
+
+ case DUPLICATES:
+ name = "duplicates";
+ break;
+
+ case DECLINES:
+ name = "declines";
+ break;
+
+ case CLIENT_UPDATES:
+ name = "client-updates";
+ break;
+
+ case LEASEQUERY:
+ name = "leasequery";
+ break;
+
+ default:
+ parse_error(cfile, "expecting allow/deny key");
+ }
+ parse_semi(cfile);
+
+ config = createMap();
+ mapSet(config, createString(makeString(-1, value)), "value");
+ mapSet(config, createString(makeString(-1, name)), "name");
+ option = option_lookup_name("server", name);
+ if (option == NULL)
+ parse_error(cfile, "unknown allow/deny keyword (%s)", name);
+ mapSet(config, createInt(option->code), "code");
+ config->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ return config;
+}
+
+/*
+ * When we parse a server-duid statement in a config file, we will
+ * have the type of the server DUID to generate, and possibly the
+ * actual value defined.
+ *
+ * server-duid llt;
+ * server-duid llt ethernet|ieee802|fddi 213982198 00:16:6F:49:7D:9B;
+ * server-duid ll;
+ * server-duid ll ethernet|ieee802|fddi 00:16:6F:49:7D:9B;
+ * server-duid en 2495 "enterprise-specific-identifier-1234";
+ */
+void
+parse_server_duid_conf(struct parse *cfile) {
+ enum dhcp_token token;
+ const char *val;
+ unsigned int len;
+ struct string *ll_addr;
+ struct element *duid;
+ struct element *item;
+ int ll_type;
+
+ duid = createMap();
+ TAILQ_CONCAT(&duid->comments, &cfile->comments);
+
+ /*
+ * Consume the SERVER_DUID token.
+ */
+ next_token(&val, NULL, cfile);
+
+ /*
+ * Obtain the DUID type.
+ */
+ token = next_token(&val, NULL, cfile);
+
+ /*
+ * Enterprise is the easiest - enterprise number and raw data
+ * are required.
+ */
+ if (token == EN) {
+ item = createString(makeString(-1, "EN"));
+ mapSet(duid, item, "type");
+
+ /*
+ * Get enterprise number and identifier.
+ */
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "enterprise number expected");
+ item = createInt(atoi(val));
+ mapSet(duid, item, "enterprise-id");
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ parse_error(cfile, "identifier expected");
+ /* Kea requires a hexadecimal identifier */
+ if (is_hexa_only(val, len))
+ item = createString(makeString(len, val));
+ else
+ item = createString(makeStringExt(len, val, 'X'));
+ mapSet(duid, item, "identifier");
+ }
+
+ /*
+ * Next easiest is the link-layer DUID. It consists only of
+ * the LL directive, or optionally the specific value to use.
+ *
+ * If we have LL only, then we set the type. If we have the
+ * value, then we set the actual DUID.
+ */
+ else if (token == LL) {
+ item = createString(makeString(-1, "LL"));
+ mapSet(duid, item, "type");
+
+ if (peek_token(NULL, NULL, cfile) != SEMI) {
+ /*
+ * Get our hardware type and address.
+ */
+ token = next_token(NULL, NULL, cfile);
+ switch (token) {
+ case ETHERNET:
+ ll_type = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ ll_type = HTYPE_IEEE802;
+ break;
+ case TOKEN_FDDI:
+ ll_type = HTYPE_FDDI;
+ break;
+ default:
+ parse_error(cfile, "hardware type expected");
+ }
+ item = createInt(ll_type);
+ mapSet(duid, item, "htype");
+
+ ll_addr = parse_hexa(cfile);
+ if (ll_addr == NULL)
+ parse_error(cfile,
+ "can't get hardware address");
+ item = createString(ll_addr);
+ mapSet(duid, item, "identifier");
+ }
+ }
+
+ /*
+ * Finally the link-layer DUID plus time. It consists only of
+ * the LLT directive, or optionally the specific value to use.
+ *
+ * If we have LLT only, then we set the type. If we have the
+ * value, then we set the actual DUID.
+ */
+ else if (token == LLT) {
+ item = createString(makeString(-1, "LLT"));
+ mapSet(duid, item, "type");
+
+ if (peek_token(NULL, NULL, cfile) != SEMI) {
+ /*
+ * Get our hardware type, timestamp, and address.
+ */
+ token = next_token(NULL, NULL, cfile);
+ switch (token) {
+ case ETHERNET:
+ ll_type = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ ll_type = HTYPE_IEEE802;
+ break;
+ case TOKEN_FDDI:
+ ll_type = HTYPE_FDDI;
+ break;
+ default:
+ parse_error(cfile, "hardware type expected");
+ }
+ item = createInt(ll_type);
+ mapSet(duid, item, "htype");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "timestamp expected");
+ item = createInt(atoi(val));
+ mapSet(duid, item, "time");
+
+ ll_addr = parse_hexa(cfile);
+ if (ll_addr == NULL)
+ parse_error(cfile,
+ "can't get hardware address");
+ item = createString(ll_addr);
+ mapSet(duid, item, "identifier");
+ }
+ }
+
+ /*
+ * If users want they can use a number for DUID types.
+ * This is useful for supporting future, not-yet-defined
+ * DUID types.
+ *
+ * In this case, they have to put in the complete value.
+ *
+ * This also works for existing DUID types of course.
+ */
+ else if (token == NUMBER) {
+ item = createString(makeString(-1, val));
+ item->skip = ISC_TRUE;
+ /* Kea wants EN, LL or LLT so skip the whole thing */
+ duid->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(duid, item, "type");
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ parse_error(cfile, "identifier expected");
+ item = createString(makeString(len, val));
+ mapSet(duid, item, "identifier");
+ }
+
+ /*
+ * Anything else is an error.
+ */
+ else
+ parse_error(cfile, "DUID type of LLT, EN, or LL expected");
+
+ /*
+ * Finally consume our trailing semicolon.
+ */
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected");
+
+ /* server-id is a global parameter */
+ if (mapContains(cfile->stack[1], "server-id"))
+ parse_error(cfile, "there is already a server-id");
+ /* DHCPv6 only but not fatal */
+ if ((local_family != AF_INET6) && !duid->skip) {
+ duid->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(cfile->stack[1], duid, "server-id");
+}
+
+/* Check whether the argument is encoded in hexadecimal or not */
+static isc_boolean_t
+is_hexa_only(const char *s, unsigned l)
+{
+ unsigned i;
+
+ for (i = 0; i < l; i++)
+ if (!isxdigit((int)s[i]))
+ return ISC_FALSE;
+ return ISC_TRUE;
+}
+
+/*!
+ *
+ * \brief Parse (and execute) a directive (extension)
+ *
+ * OPTION SPACE <name> [ALIAS <kea-name>] [KNOWN*2|UNKNOWN*2|DYNAMIC]
+ * OPTION <universe>.<name> [CHECK]
+ * [ALIAS <name>]
+ * [CODE <code> = "<format>"]
+ * [KNOWN*2|UNKNOWN*2|DYNAMIC]
+ * [LOCAL|DEFINE]
+ */
+
+void
+parse_directive(struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+ isc_boolean_t known;
+ struct option *option;
+
+ token = peek_token(&val, NULL, cfile);
+
+ switch (token) {
+ case OPTION:
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token == SPACE) {
+ parse_option_space_dir(cfile);
+ return;
+ }
+
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_TRUE, &known);
+ token = next_token(&val, NULL, cfile);
+ if (token == CHECK) {
+ struct string *datatype;
+ isc_boolean_t is_array = ISC_FALSE;
+ isc_boolean_t encapsulate = ISC_FALSE;
+
+ datatype = convert_format(option->format,
+ &is_array,
+ &encapsulate);
+ printf("option ISC DHCP (Kea)\n"
+ " %s.%s (%s.%s)\n"
+ " format \"%s\" (type \"%s\" "
+ "array %s encap %s)\n"
+ " status %s\n",
+ option->space->old, option->old,
+ option->space->name, option->name,
+ option->format, datatype->content,
+ is_array ? "true" : "false",
+ encapsulate ? "true" : "false",
+ display_status(option->status));
+ parse_semi(cfile);
+ return;
+ }
+ if (option->space->status == special)
+ parse_error(cfile, "attempt to modify config %s.%s",
+ option->space->old, option->name);
+ if (token == ALIAS) {
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile,
+ "expecting identifier after "
+ "alias keyword.");
+ if (option->status != dynamic)
+ parse_error(cfile,
+ "attempt to rename %s.%s to %s",
+ option->space->name,
+ option->name, val);
+ option->name = strdup(val);
+ parse_semi(cfile);
+ return;
+ }
+ if (token == CODE) {
+ parse_option_code_dir(cfile, option);
+ return;
+ }
+ if ((token == KNOWN) || (token == UNKNOWN) ||
+ (token == DYNAMIC)) {
+ parse_option_status_dir(cfile, option, token);
+ return;
+ }
+ if (token == LOCAL) {
+ parse_option_local_dir(cfile, option);
+ parse_semi(cfile);
+ return;
+ }
+ if (token == DEFINE) {
+ parse_option_define_dir(cfile, option);
+ parse_semi(cfile);
+ return;
+ }
+ parse_error(cfile, "unknown option directive %s", val);
+
+ default:
+ parse_error(cfile, "unknown directive %s", val);
+ }
+}
+
+/* Set alias and status for option spaces */
+
+void
+parse_option_space_dir(struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct space *space;
+
+ skip_token(NULL, NULL, cfile); /* Discard SPACE */
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting identifier.");
+ space = space_lookup(val);
+ if (space == NULL)
+ parse_error(cfile, "can't find space '%s", val);
+
+ token = next_token(&val, NULL, cfile);
+ if (token == CHECK) {
+ printf("space ISC DHCP (kea)\n"
+ " %s (%s)\n status %s\n%s",
+ space->old, space->name,
+ display_status(space->status),
+ space->vendor != NULL ? " vendor\n" : "");
+ parse_semi(cfile);
+ return;
+ }
+ if (token == ALIAS) {
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile,
+ "expecting identifier after "
+ "alias keyword.");
+ if (space->status != dynamic)
+ parse_error(cfile,
+ "attempt to rename %s to %s",
+ space->name, val);
+ space->name = strdup(val);
+ parse_semi(cfile);
+ return;
+ }
+ if (token == DYNAMIC)
+ space->status = dynamic;
+ else if (token == UNKNOWN) {
+ token = next_token(NULL, NULL, cfile);
+ if (token == KNOWN)
+ space->status = known;
+ else if (token == UNKNOWN)
+ space->status = kea_unknown;
+ else
+ parse_error(cfile, "expected KNOW or UNKNOWN");
+ } else if (token != UNKNOWN)
+ parse_error(cfile, "expected KNOW or UNKNOWN or DYNAMIC");
+ else {
+ if (token == KNOWN)
+ space->status = isc_dhcp_unknown;
+ else if (token == UNKNOWN)
+ parse_error(cfile, "illicit combination: space "
+ "%s is known by nobody", space->name);
+ else
+ parse_error(cfile, "expected KNOW or UNKNOWN");
+ }
+ parse_semi(cfile);
+}
+
+/* Alternative to parse_option_code_decl using the raw ISC DHCP format */
+
+void
+parse_option_code_dir(struct parse *cfile, struct option *option)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned code;
+ struct element *def;
+ struct element *optdef;
+ struct string *datatype;
+ isc_boolean_t is_array = ISC_FALSE;
+ isc_boolean_t encapsulate = ISC_FALSE;
+
+ def = createMap();
+ mapSet(def,
+ createString(makeString(-1, option->space->name)),
+ "space");
+ mapSet(def, createString(makeString(-1, option->name)), "name");
+
+ /* Parse the option code. */
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting option code number.");
+ code = atoi(val);
+ mapSet(def, createInt(code), "code");
+
+ /* We have the code so we can get the real option now */
+ if (option->code == 0) {
+ struct option *from_code;
+
+ option->code = code;
+ from_code = option_lookup_code(option->space->old, code);
+ if (from_code != NULL)
+ option = from_code;
+ }
+
+ /* Redefinitions are not allowed */
+ if ((option->status != dynamic) ||
+ (strcmp(option->format, "u") != 0))
+ parse_error(cfile, "attempt to redefine %s.%s code %u",
+ option->space->name, option->name, code);
+
+ token = next_token(&val, NULL, cfile);
+ if (token != EQUAL)
+ parse_error(cfile, "expecting \"=\"");
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "expecting format string");
+ option->format = strdup(val);
+ parse_semi(cfile);
+
+ datatype = convert_format(val, &is_array, &encapsulate);
+
+ if ((datatype == NULL) && (strchr(datatype->content, '?') != NULL))
+ parse_error(cfile, "failed to convert format \"%s\" for "
+ "option %s.%s code %u",
+ val, option->space->name, option->name, code);
+ /* todo */
+ if (encapsulate)
+ parse_error(cfile, "option %s.%s code %u encapsulate?",
+ option->space->name, option->name, code);
+
+ if (strchr(datatype->content, ',') == NULL)
+ mapSet(def, createString(datatype), "type");
+ else {
+ mapSet(def, createString(datatype), "record-types");
+ mapSet(def, createString(makeString(-1, "record")), "type");
+ }
+ if (is_array)
+ mapSet(def, createBool(ISC_TRUE), "array");
+
+ optdef = mapGet(cfile->stack[1], "option-def");
+ if (optdef == NULL) {
+ optdef = createList();
+ mapSet(cfile->stack[1], optdef, "option-def");
+ }
+ listPush(optdef, def);
+}
+
+/* Update the option status for instance to add standard options */
+
+void
+parse_option_status_dir(struct parse *cfile, struct option *option,
+ enum dhcp_token token)
+{
+ if (token == DYNAMIC)
+ option->status = dynamic;
+ else if (token == KNOWN) {
+ token = next_token(NULL, NULL, cfile);
+ if (token == KNOWN)
+ option->status = known;
+ else if (token == UNKNOWN)
+ option->status = kea_unknown;
+ else
+ parse_error(cfile, "expected KNOW or UNKNOWN");
+ } else if (token != UNKNOWN)
+ parse_error(cfile, "expected KNOW or UNKNOWN or DYNAMIC");
+ else {
+ if (token == KNOWN)
+ option->status = isc_dhcp_unknown;
+ else if (token == UNKNOWN)
+ parse_error(cfile, "illicit combination: option "
+ "%s.%s code %u is known by nobody",
+ option->space->name, option->name,
+ option->code);
+ else
+ parse_error(cfile, "expected KNOW or UNKNOWN");
+ }
+ parse_semi(cfile);
+}
+
+/* Make the option definition not exported to Kea */
+
+void
+parse_option_local_dir(struct parse *cfile, struct option *option)
+{
+ struct element *optdef;
+ struct element *def;
+ struct element *elem;
+ size_t i;
+
+ def = NULL;
+ if (option->code == 0)
+ parse_error(cfile, "unknown code for option %s.%s",
+ option->space->name, option->name);
+
+ optdef = mapGet(cfile->stack[1], "option-def");
+ if (optdef == NULL) {
+ optdef = createList();
+ mapSet(cfile->stack[1], optdef, "option-def");
+ goto not_found;
+ }
+ for (i = 0; i < listSize(optdef); i++) {
+ def = listGet(optdef, i);
+ elem = mapGet(def, "space");
+ if ((elem == NULL) || (elem->type != ELEMENT_STRING))
+ parse_error(cfile, "got an option definition "
+ "without space at %u", (unsigned)i);
+ if (strcmp(option->space->name,
+ stringValue(elem)->content) != 0)
+ continue;
+ elem = mapGet(def, "code");
+ if ((elem == NULL) || (elem->type != ELEMENT_INTEGER))
+ parse_error(cfile, "got an option definition "
+ "without code at %u", (unsigned)i);
+ if (intValue(elem) == option->code)
+ break;
+ }
+ if (def == NULL)
+ goto not_found;
+ def->skip = ISC_TRUE;
+ mapSet(def, createNull(), "no-export");
+ return;
+
+not_found:
+ parse_error(cfile, "can't find option %s.%s code %u in definitions",
+ option->space->name, option->name, option->code);
+}
+
+/* Make the opposite: force the definition */
+
+void
+parse_option_define_dir(struct parse *cfile, struct option *option)
+{
+ struct element *optdef;
+ struct element *def;
+ struct element *elem;
+ struct string *datatype;
+ isc_boolean_t is_array = ISC_FALSE;
+ isc_boolean_t encapsulate = ISC_FALSE;
+ size_t i;
+
+ def = NULL;
+ if (option->code == 0)
+ parse_error(cfile, "unknown code for option %s.%s",
+ option->space->name, option->name);
+
+ optdef = mapGet(cfile->stack[1], "option-def");
+ if (optdef == NULL) {
+ optdef = createList();
+ mapSet(cfile->stack[1], optdef, "option-def");
+ goto no_search;
+ }
+ for (i = 0; i < listSize(optdef); i++) {
+ def = listGet(optdef, i);
+ elem = mapGet(def, "space");
+ if ((elem == NULL) || (elem->type != ELEMENT_STRING))
+ parse_error(cfile, "got an option definition "
+ "without space at %u", (unsigned)i);
+ if (strcmp(option->space->name,
+ stringValue(elem)->content) != 0)
+ continue;
+ elem = mapGet(def, "code");
+ if ((elem == NULL) || (elem->type != ELEMENT_INTEGER))
+ parse_error(cfile, "got an option definition "
+ "without code at %u", (unsigned)i);
+ if (intValue(elem) == option->code)
+ parse_error(cfile, "unexpected definition for "
+ "option %s.%s code %u",
+ option->space->name, option->name,
+ option->code);
+ }
+no_search:
+ def = createMap();
+ mapSet(def,
+ createString(makeString(-1, option->space->name)),
+ "space");
+ mapSet(def, createString(makeString(-1, option->name)), "name");
+ mapSet(def, createInt(option->code), "code");
+
+ datatype = convert_format(option->format, &is_array, &encapsulate);
+
+ if ((datatype == NULL) && (strchr(datatype->content, '?') != NULL))
+ parse_error(cfile, "failed to convert format \"%s\" for "
+ "option %s.%s code %u",
+ option->format, option->space->name,
+ option->name, option->code);
+ /* todo */
+ if (encapsulate)
+ parse_error(cfile, "option %s.%s code %u encapsulate?",
+ option->space->name, option->name, option->code);
+
+ if (strchr(datatype->content, ',') == NULL)
+ mapSet(def, createString(datatype), "type");
+ else {
+ mapSet(def, createString(datatype), "record-types");
+ mapSet(def, createString(makeString(-1, "record")), "type");
+ }
+ if (is_array)
+ mapSet(def, createBool(ISC_TRUE), "array");
+
+ listPush(optdef, def);
+
+ return;
+}
+
+/*
+ * Push new interface on the interface list when it is not already.
+ */
+
+static void
+new_network_interface(struct parse *cfile, struct element *iface)
+{
+ struct element *ifconf;
+ struct element *iflist;
+ struct string *name = stringValue(iface);
+ int i;
+
+ ifconf = mapGet(cfile->stack[1], "interfaces-config");
+ if (ifconf == NULL) {
+ ifconf = createMap();
+ mapSet(cfile->stack[1], ifconf, "interfaces-config");
+ }
+
+ iflist = mapGet(ifconf, "interfaces");
+ if (iflist == NULL) {
+ iflist = createList();
+ mapSet(ifconf, iflist, "interfaces");
+ }
+
+ for (i = 0; i < listSize(iflist); i++) {
+ struct element *item;
+
+ item = listGet(iflist, i);
+ if ((item != NULL) &&
+ (item->type == ELEMENT_STRING) &&
+ eqString(stringValue(item), name))
+ return;
+ }
+
+ listPush(iflist, createString(name));
+}
+
+/* Convert address and mask in binary into address/len text */
+
+static const uint32_t bitmasks[32 + 1] = {
+ 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
+ 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
+ 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
+ 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
+ 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
+ 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
+ 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
+ 0x0000000f, 0x00000007, 0x00000003, 0x00000001,
+ 0x00000000 };
+
+static struct string *
+addrmask(const struct string *address, const struct string *netmask)
+{
+ struct string *result;
+ uint8_t plen;
+ uint32_t mask;
+
+ result = makeStringExt(address->length, address->content, 'I');
+
+ memcpy(&mask, netmask->content, 4);
+ mask = ntohl(mask);
+ for (plen = 0; plen <= 32; ++plen)
+ if (~mask == bitmasks[plen])
+ break;
+ if (plen > 32)
+ return NULL;
+
+ appendString(result, "/");
+ concatString(result, makeStringExt(1, (char *)&plen, 'B'));
+ return result;
+}
+
+/*
+ * find a place where to put a reservation
+ * (reservations aka hosts must be in a subnet in Kea < 1.5)
+ * (defaulting to the last defined subnet (e.g. for reservations
+ * without any address).
+ * (first step is to find an enclosing group).
+ */
+
+static struct element *
+find_match(struct parse *cfile, struct element *host,
+ isc_boolean_t *used_heuristicp)
+{
+ struct element *address;
+ struct subnet *subnet;
+ char addr[16];
+ size_t group;
+ size_t i, len;
+ int kind;
+
+ if (global_hr) {
+ struct element *hosts;
+
+ hosts = mapGet(cfile->stack[1], "reservations");
+ if (!hosts) {
+ mapSet(cfile->stack[1],
+ createString(makeString(-1, "global")),
+ "reservation-mode");
+ hosts = createList();
+ mapSet(cfile->stack[1], hosts, "reservations");
+ }
+ *used_heuristicp = ISC_FALSE;
+ return cfile->stack[1];
+ }
+
+ for (group = cfile->stack_top; group > 0; --group) {
+ kind = cfile->stack[group]->kind;
+ if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
+ break;
+ }
+ if (!group)
+ parse_error(cfile, "can't find root group");
+ if (kind == GROUP_DECL)
+ return cfile->stack[group];
+
+ if (local_family == AF_INET) {
+ address = mapGet(host, "ip-address");
+ if (address == NULL) {
+ if (TAILQ_EMPTY(&known_subnets))
+ return cfile->stack[1];
+ if (used_heuristicp)
+ *used_heuristicp = ISC_TRUE;
+ return TAILQ_LAST(&known_subnets, subnets)->subnet;
+ }
+ len = 4;
+ } else {
+ address = mapGet(host, "ip-addresses");
+ if (address == NULL) {
+ if (TAILQ_EMPTY(&known_subnets))
+ return cfile->stack[1];
+ if (used_heuristicp)
+ *used_heuristicp = ISC_TRUE;
+ return TAILQ_LAST(&known_subnets, subnets)->subnet;
+ }
+ address = listGet(address, 0);
+ if (address == NULL)
+ return TAILQ_LAST(&known_subnets, subnets)->subnet;
+ len = 16;
+ }
+
+ if (inet_pton(local_family, stringValue(address)->content, addr) != 1)
+ parse_error(cfile, "bad address %s",
+ stringValue(address)->content);
+ TAILQ_FOREACH(subnet, &known_subnets) {
+ isc_boolean_t matching = ISC_TRUE;
+
+ if (subnet->mask->length != len)
+ continue;
+ for (i = 0; i < len; i++)
+ if ((addr[i] & subnet->mask->content[i]) !=
+ subnet->addr->content[i]) {
+ matching = ISC_FALSE;
+ break;
+ }
+ if (matching)
+ return subnet->subnet;
+ }
+ return cfile->stack[1];
+}
+
+/*
+ * find a subnet where to put a pool
+ * (pools are not allowed at shared-network level in Kea)
+ */
+
+static struct element *
+find_location(struct element *share, struct range *range)
+{
+ struct subnet *subnet;
+ size_t i;
+
+ TAILQ_FOREACH(subnet, &known_subnets) {
+ isc_boolean_t matching = ISC_TRUE;
+
+ if (subnet->share != share)
+ continue;
+ if (subnet->mask->length != range->low->length)
+ continue;
+ for (i = 0; i < range->low->length; i++)
+ if ((range->low->content[i] &
+ subnet->mask->content[i]) !=
+ subnet->addr->content[i]) {
+ matching = ISC_FALSE;
+ break;
+ }
+ if (matching)
+ return subnet->subnet;
+ }
+ return NULL;
+}
+
+/*
+ * Compute a prefix length from lower - higher IPv6 addresses.
+ */
+
+static const uint8_t bytemasks[8] = {
+ 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+static int
+get_prefix_length(const char *low, const char *high)
+{
+ uint8_t lo[16];
+ uint8_t hi[16];
+ uint8_t xor[16];
+ int i, plen;
+
+ if ((inet_pton(AF_INET6, low, lo) != 1) ||
+ (inet_pton(AF_INET6, high, hi) != 1))
+ return -100;
+
+ for (i = 0; i < 16; i++)
+ xor[i] = lo[i] ^ hi[i];
+ for (plen = 0; plen < 128; plen += 8)
+ if (xor[plen / 8] != 0)
+ break;
+ if (plen == 128)
+ return plen;
+ for (i = (plen / 8) + 1; i < 16; i++)
+ if (xor[i] != 0)
+ return -2;
+ for (i = 0; i < 8; i++) {
+ uint8_t msk = ~xor[plen / 8];
+
+ if (msk == bytemasks[i])
+ return plen + i + 1;
+ }
+ return -1;
+}
+
+/*
+ * Get a (global) class from its reference, i.e.:
+ * - name for a (super)class
+ * - super, and binary or string for a subclass
+ */
+static struct element *
+get_class(struct parse *cfile, struct element *ref)
+{
+ struct element *classes;
+ struct element *class;
+ struct element *name;
+ struct element *selector;
+ struct element *param;
+ size_t i;
+
+ classes = mapGet(cfile->stack[1], "client-classes");
+ if ((classes == NULL) || (listSize(classes) == 0))
+ return NULL;
+
+ name = mapGet(ref, "super");
+ if (name == NULL) {
+ name = mapGet(ref, "name");
+ if (name == NULL)
+ return NULL;
+ for (i = 0; i < listSize(classes); i++) {
+ class = listGet(classes, i);
+ if (mapContains(ref, "super"))
+ continue;
+ param = mapGet(class, "name");
+ if (param == NULL)
+ continue;
+ if (eqString(stringValue(name), stringValue(param)))
+ return class;
+ }
+ return NULL;
+ }
+ selector = mapGet(ref, "string");
+ if (selector == NULL) {
+ selector = mapGet(ref, "binary");
+ if (selector == NULL)
+ return NULL;
+ for (i = 0; i <listSize(classes); i++) {
+ class = listGet(classes, i);
+ param = mapGet(class, "super");
+ if (param == NULL)
+ continue;
+ if (!eqString(stringValue(name), stringValue(param)))
+ continue;
+ param = mapGet(class, "binary");
+ if (param == NULL)
+ continue;
+ if (eqString(stringValue(selector),
+ stringValue(param)))
+ return class;
+ }
+ return NULL;
+ }
+ for (i = 0; i <listSize(classes); i++) {
+ class = listGet(classes, i);
+ param = mapGet(class, "super");
+ if (param == NULL)
+ continue;
+ if (!eqString(stringValue(name), stringValue(param)))
+ continue;
+ param = mapGet(class, "string");
+ if (param == NULL)
+ continue;
+ if (eqString(stringValue(selector), stringValue(param)))
+ return class;
+ }
+ return NULL;
+}
+
+/*
+ * Concatenate two class reference lists eliminating duplicates
+ * (complexity is bad: if this becomes a performance pig, use a hash table)
+ */
+
+static void
+concat_classes(struct parse *cfile, struct element *dst, struct element *src)
+{
+ struct element *class;
+ struct element *sitem;
+ struct element *ditem;
+ size_t i;
+ isc_boolean_t dup;
+
+ while (listSize(src) > 0) {
+ sitem = listGet(src, 0);
+ listRemove(src, 0);
+ class = get_class(cfile, sitem);
+ if (class == NULL)
+ /* just ignore */
+ continue;
+ dup = ISC_FALSE;
+ for (i = 0; i < listSize(dst); i++) {
+ ditem = listGet(dst, i);
+ if (class == get_class(cfile, ditem)) {
+ dup = ISC_TRUE;
+ break;
+ }
+ }
+ if (dup)
+ continue;
+ listPush(dst, sitem);
+ }
+}
+
+/* Generate a class from allow/deny member lists */
+
+static void
+generate_class(struct parse *cfile, struct element *pool,
+ struct element *allow, struct element *deny)
+{
+ struct element *classes;
+ struct element *class;
+ struct element *elem;
+ struct element *prop;
+ struct element *depend;
+ struct element *result = NULL;
+ struct string *name;
+ struct string *expr;
+ struct string *msg;
+ struct comments comments;
+ struct comment *comment;
+ isc_boolean_t rescan;
+ size_t i;
+
+ if ((listSize(allow) == 0) && (listSize(deny) == 0))
+ return;
+
+ classes = mapGet(cfile->stack[1], "generated-classes");
+ if (classes == NULL) {
+ classes = createList();
+ mapSet(cfile->stack[1], classes, "generated-classes");
+ }
+
+ /* Create comments */
+ TAILQ_INIT(&comments);
+ comment = createComment("/// From:");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ for (i = 0; i < listSize(allow); i++) {
+ struct element *alias;
+
+ elem = listGet(allow, i);
+ assert(elem != NULL);
+ prop = mapGet(elem, "class");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_STRING);
+ alias = mapGet(elem, "real");
+ msg = makeString(-1, "/// allow ");
+ concatString(msg, stringValue(alias ? alias : prop));
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&comments, comment);
+ }
+ for (i = 0; i < listSize(deny); i++) {
+ struct element *alias;
+
+ elem = listGet(deny, i);
+ assert(elem != NULL);
+ prop = mapGet(elem, "class");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_STRING);
+ alias = mapGet(elem, "real");
+ msg = makeString(-1, "/// deny ");
+ concatString(msg, stringValue(alias ? alias : prop));
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&comments, comment);
+ }
+ TAILQ_CONCAT(&comments, &allow->comments);
+ TAILQ_CONCAT(&comments, &deny->comments);
+
+ /* Deal with special cases */
+ for (;;) {
+ rescan = ISC_FALSE;
+ for (i = 0; i < listSize(allow); i++) {
+ elem = listGet(allow, i);
+ assert(elem != NULL);
+ prop = mapGet(elem, "way");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_BOOLEAN);
+ class = mapGet(elem, "class");
+ assert(class != NULL);
+ assert(class->type == ELEMENT_STRING);
+ /* allow !ALL and other */
+ if (eqString(stringValue(class), CLASS_ALL) &&
+ !boolValue(prop) && (listSize(allow) > 1)) {
+ listRemove(allow, i);
+ rescan = ISC_TRUE;
+ break;
+ }
+ /* allow ALL alone */
+ if (eqString(stringValue(class), CLASS_ALL) &&
+ boolValue(prop) && (listSize(allow) == 1)) {
+ resetList(allow);
+ rescan = ISC_TRUE;
+ break;
+ }
+ }
+ if (!rescan)
+ break;
+ }
+
+ for (;;) {
+ rescan = ISC_FALSE;
+ for (i = 0; i < listSize(deny); i++) {
+ elem = listGet(deny, i);
+ assert(elem != NULL);
+ prop = mapGet(elem, "way");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_BOOLEAN);
+ class = mapGet(elem, "class");
+ assert(class != NULL);
+ assert(class->type == ELEMENT_STRING);
+ /* DENY !ALL */
+ if (eqString(stringValue(class), CLASS_ALL) &&
+ !boolValue(prop)) {
+ listRemove(deny, i);
+ rescan = ISC_TRUE;
+ break;
+ }
+ /* DENY ALL */
+ if (eqString(stringValue(class), CLASS_ALL) &&
+ boolValue(prop)) {
+ resetList(allow);
+ if (listSize(deny) > 1) {
+ listRemove(deny, i);
+ resetList(deny);
+ listPush(deny, elem);
+ }
+ break;
+ }
+ }
+ if (!rescan)
+ break;
+ }
+
+ /* Fully cleaned? */
+ if ((listSize(allow) == 0) && (listSize(deny) == 0)) {
+ if (result != NULL)
+ TAILQ_CONCAT(&result->comments, &comments);
+ else
+ TAILQ_CONCAT(&pool->comments, &comments);
+ return;
+ }
+
+ /* Unique allow member short cut */
+ if ((listSize(allow) == 1) && (listSize(deny) == 0) &&
+ !allow->skip && !deny->skip) {
+ elem = listGet(allow, 0);
+ assert(elem != NULL);
+ prop = mapGet(elem, "way");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_BOOLEAN);
+ class = mapGet(elem, "class");
+ assert(class != NULL);
+ assert(class->type == ELEMENT_STRING);
+ if (boolValue(prop)) {
+ result = createString(stringValue(class));
+ TAILQ_CONCAT(&result->comments, &comments);
+ mapSet(pool, result, "client-class");
+ return;
+ }
+ }
+
+ /* Build name */
+ name = makeString(-1, "gen#");
+ for (i = 0; i < listSize(allow); i++) {
+ elem = listGet(allow, i);
+ assert(elem != NULL);
+ prop = mapGet(elem, "way");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_BOOLEAN);
+ if (!boolValue(prop))
+ appendString(name, "!");
+ prop = mapGet(elem, "class");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_STRING);
+ concatString(name, stringValue(prop));
+ appendString(name, "#");
+ }
+ if (listSize(deny) > 0) {
+ appendString(name, "_AND_#");
+ for (i = 0; i < listSize(deny); i++) {
+ elem = listGet(deny, i);
+ assert(elem != NULL);
+ prop = mapGet(elem, "way");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_BOOLEAN);
+ if (boolValue(prop))
+ appendString(name, "!");
+ prop = mapGet(elem, "class");
+ assert(prop != NULL);
+ assert(prop->type == ELEMENT_STRING);
+ concatString(name, stringValue(prop));
+ appendString(name, "#");
+ }
+ }
+
+ /* Check if it already exists */
+ for (i = 0; i < listSize(classes); i++) {
+ struct element *descr;
+
+ class = listGet(classes, i);
+ assert(class != NULL);
+ descr = mapGet(class, "name");
+ assert(descr != NULL);
+ assert(descr->type == ELEMENT_STRING);
+ if (!eqString(name, stringValue(descr)))
+ continue;
+ result = createString(name);
+ TAILQ_CONCAT(&result->comments, &comments);
+ mapSet(pool, result, "client-class");
+ return;
+ }
+
+ /* Create expression */
+ class = createMap();
+ depend = createList();
+ expr = allocString();
+
+ if ((listSize(allow) > 0) && (listSize(deny) > 0))
+ appendString(expr, "(");
+
+ for (i = 0; i < listSize(allow); i++) {
+ isc_boolean_t negative;
+
+ if (i > 0)
+ appendString(expr, " or ");
+ elem = listGet(allow, i);
+ prop = mapGet(elem, "way");
+ negative = !boolValue(prop);
+ prop = mapGet(elem, "class");
+ if (negative)
+ appendString(expr, "not ");
+ appendString(expr, "member('");
+ concatString(expr, stringValue(prop));
+ appendString(expr, "')");
+ listPush(depend, createString(stringValue(prop)));
+ }
+
+ if ((listSize(allow) > 0) && (listSize(deny) > 0))
+ appendString(expr, ") and ");
+
+ for (i = 0; i < listSize(deny); i++) {
+ isc_boolean_t negative;
+
+ if (i > 0)
+ appendString(expr, " and ");
+ elem = listGet(deny, i);
+ prop = mapGet(elem, "way");
+ negative = boolValue(prop);
+ prop = mapGet(elem, "class");
+ if (negative)
+ appendString(expr, "not ");
+ appendString(expr, "member('");
+ concatString(expr, stringValue(prop));
+ appendString(expr, "')");
+ listPush(depend, createString(stringValue(prop)));
+ }
+
+ mapSet(class, createString(name), "name");
+ mapSet(class, createString(expr), "test");
+ mapSet(class, depend, "depend");
+ /* inherit untranslatable cases */
+ class->skip |= allow->skip || deny->skip;
+ listPush(classes, class);
+
+ result = createString(name);
+ TAILQ_CONCAT(&result->comments, &comments);
+ mapSet(pool, result, "client-class");
+}
diff --git a/keama/data.c b/keama/data.c
new file mode 100644
index 00000000..bd656289
--- /dev/null
+++ b/keama/data.c
@@ -0,0 +1,1258 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ */
+
+#include "data.h"
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct string *
+allocString(void)
+{
+ struct string *result;
+
+ result = (struct string *)malloc(sizeof(struct string));
+ assert(result != NULL);
+ memset(result, 0, sizeof(struct string));
+
+ return result;
+}
+
+struct string *
+makeString(int l, const char *s)
+{
+ struct string *result;
+
+ result = allocString();
+ if (l < 0)
+ result->length = strlen(s);
+ else
+ result->length = (size_t)l;
+ if (result->length > 0) {
+ result->content = (char *)malloc(result->length + 1);
+ assert(result->content != NULL);
+ memcpy(result->content, s, result->length);
+ result->content[result->length] = 0;
+ }
+
+ return result;
+}
+
+struct string *
+makeStringExt(int l, const char *s, char fmt)
+{
+ switch (fmt) {
+ case 'Z':
+ /* zero-length */
+ return allocString();
+
+ case 'l': {
+ /* 32-bit signed integer */
+ int32_t x;
+ char buf[40];
+
+ assert(s != NULL);
+ assert(l > 3);
+
+ memcpy(&x, s, 4);
+ x = (int32_t)ntohl((uint32_t)x);
+ snprintf(buf, sizeof(buf), "%lld", (long long)x);
+ return makeString(-1, buf);
+ }
+
+ case 'L': {
+ /* 32-bit unsigned integer */
+ uint32_t x;
+ char buf[40];
+
+ assert(s != NULL);
+ assert(l > 3);
+
+ memcpy(&x, s, 4);
+ x = ntohl(x);
+ snprintf(buf, sizeof(buf), "%llu", (unsigned long long)x);
+ return makeString(-1, buf);
+ }
+
+ case 's': {
+ /* 16-bit signed integer */
+ int16_t x;
+ char buf[20];
+
+ assert(s != NULL);
+ assert(l > 1);
+
+ memcpy(&x, s, 2);
+ x = (int16_t)ntohs((uint16_t)x);
+ snprintf(buf, sizeof(buf), "%hd", x);
+ return makeString(-1, buf);
+ }
+
+ case 'S': {
+ /* 16-bit unsigned integer */
+ uint16_t x;
+ char buf[20];
+
+ assert(s != NULL);
+ assert(l > 1);
+
+ memcpy(&x, s, 2);
+ x = ntohs(x);
+ snprintf(buf, sizeof(buf), "%hu", x);
+ return makeString(-1, buf);
+ }
+
+ case 'b': {
+ /* 8-bit signed integer */
+ int8_t x;
+ char buf[10];
+
+ assert(s != NULL);
+ assert(l > 0);
+
+ memcpy(&x, s, 1);
+ snprintf(buf, sizeof(buf), "%hhd", x);
+ return makeString(-1, buf);
+ }
+
+ case 'B': {
+ /* 8-bit unsigned integer */
+ uint8_t x;
+ char buf[10];
+
+ assert(s != NULL);
+ assert(l > 0);
+
+ memcpy(&x, s, 1);
+ snprintf(buf, sizeof(buf), "%hhu", x);
+ return makeString(-1, buf);
+ }
+
+ case 'f': {
+ /* flag (true or false) */
+ uint8_t f;
+
+ assert(s != NULL);
+ assert(l > 0);
+
+ f = *s;
+ return makeString(-1, f ? "true" : "false");
+ }
+
+ case 'X': {
+ /* binary data */
+ struct string *result;
+ size_t i;
+ char buf[4];
+
+ assert((l == 0) || (s != NULL));
+
+ result = allocString();
+ for (i = 0; i < l; i++) {
+ snprintf(buf, sizeof(buf), "%02hhx", (uint8_t)s[i]);
+ appendString(result, buf);
+ }
+ return result;
+ }
+
+ case 'H': {
+ /* binary data with colons */
+ struct string *result;
+ size_t i;
+ isc_boolean_t first = ISC_TRUE;
+ char buf[4];
+
+ assert((l == 0) || (s != NULL));
+
+ result = allocString();
+ for (i = 0; i < l; i++) {
+ if (!first)
+ appendString(result, ":");
+ first = ISC_FALSE;
+ snprintf(buf, sizeof(buf), "%02hhx", (uint8_t)s[i]);
+ appendString(result, buf);
+ }
+ return result;
+ }
+
+ case 'I': {
+ /* IPv4 address to text */
+ char buf[40 /* INET_ADDRSTRLEN == 26 */];
+
+ assert(l > 3);
+ assert(inet_ntop(AF_INET, s, buf, sizeof(buf)) != NULL);
+ return makeString(-1, buf);
+ }
+
+ case 'i': {
+ /* IPv4 address to hexa */
+ uint8_t a[4];
+ char buf[10];
+
+ assert(inet_pton(AF_INET, s, a) == 1);
+ snprintf(buf, sizeof(buf), "%02hhx%02hhx%02hhx%02hhx",
+ a[0], a[1], a[2], a[3]);
+ return makeString(-1, buf);
+ }
+
+ case '6': {
+ /* IPv6 address */
+ char buf[80 /* INET6_ADDRSTRLEN == 46 */];
+
+ assert(l > 15);
+ assert(inet_ntop(AF_INET6, s, buf, sizeof(buf)) != NULL);
+ return makeString(-1, buf);
+ }
+
+ case 'd': {
+ /* FQDN to DNS wire format */
+ struct string *result;
+ const char *p;
+ const char *dot;
+ char ll;
+
+ assert(s[l] == '0');
+
+ result = allocString();
+ p = s;
+ while ((dot = strchr(p, '.')) != NULL) {
+ int len;
+
+ len = dot - p - 1;
+ if ((len & 0xc0) != 0)
+ return NULL;
+ if (dot - s >= l)
+ return NULL;
+ ll = len & 0x3f;
+ concatString(result, makeString(1, &ll));
+ concatString(result, makeString(len, p));
+ p = dot + 1;
+ if (p - s == l)
+ break;
+ }
+ if (dot == NULL) {
+ ll = 0;
+ concatString(result, makeString(1, &ll));
+ }
+ return result;
+ }
+
+ default:
+ assert(0);
+ }
+}
+
+struct string *
+makeStringArray(int l, const char *s, char fmt)
+{
+ struct string *result;
+ size_t step;
+ isc_boolean_t first = ISC_TRUE;
+
+ switch (fmt) {
+ case '6':
+ step = 16;
+ break;
+ case 'l':
+ case 'L':
+ case 'I':
+ step = 4;
+ break;
+ case 's':
+ case 'S':
+ step = 2;
+ break;
+ case 'b':
+ case 'B':
+ case 'f':
+ step = 1;
+ break;
+ default:
+ assert(0);
+ }
+
+ assert((l % step) == 0);
+
+ result = allocString();
+ while (l > 0) {
+ if (!first)
+ appendString(result, ",");
+ first = ISC_FALSE;
+ concatString(result, makeStringExt(l, s, fmt));
+ s += step;
+ l -= step;
+ }
+ return result;
+}
+
+void
+appendString(struct string *s, const char *a)
+{
+ size_t n;
+
+ assert(s != NULL);
+
+ if (a == NULL)
+ return;
+ n = strlen(a);
+ if (n == 0)
+ return;
+ s->content = (char *)realloc(s->content, s->length + n + 1);
+ assert(s->content != NULL);
+ memcpy(s->content + s->length, a, n);
+ s->length += n;
+ s->content[s->length] = 0;
+}
+
+void
+concatString(struct string *s, const struct string *a)
+{
+ assert(s != NULL);
+ assert(a != NULL);
+
+ s->content = (char *)realloc(s->content, s->length + a->length + 1);
+ assert(s->content != NULL);
+ memcpy(s->content + s->length, a->content, a->length);
+ s->length += a->length;
+ s->content[s->length] = 0;
+}
+
+isc_boolean_t
+eqString(const struct string *s, const struct string *o)
+{
+ assert(s != NULL);
+ assert(o != NULL);
+
+ if (s->length != o->length)
+ return ISC_FALSE;
+ if (s->length == 0)
+ return ISC_TRUE;
+ return ISC_TF(memcmp(s->content, o->content, s->length) == 0);
+}
+
+struct string *
+quote(struct string *s)
+{
+ struct string *result;
+
+ result = makeString(-1, "'");
+ concatString(result, s);
+ appendString(result, "'");
+ return result;
+}
+
+struct comment *
+createComment(const char *line)
+{
+ struct comment *comment;
+
+ assert(line != NULL);
+
+ comment = (struct comment *)malloc(sizeof(struct comment));
+ assert(comment != NULL);
+ memset(comment, 0, sizeof(struct comment));
+
+ comment->line = strdup(line);
+
+ return comment;
+}
+
+int64_t
+intValue(const struct element *e)
+{
+ assert(e != NULL);
+ assert(e->type == ELEMENT_INTEGER);
+ return e->value.int_value;
+}
+
+double
+doubleValue(const struct element *e)
+{
+ assert(e != NULL);
+ assert(e->type == ELEMENT_REAL);
+ return e->value.double_value;
+}
+
+isc_boolean_t
+boolValue(const struct element *e)
+{
+ assert(e != NULL);
+ assert(e->type == ELEMENT_BOOLEAN);
+ /* could check if 0 or 1 */
+ return e->value.bool_value;
+}
+
+struct string *
+stringValue(struct element *e)
+{
+ assert(e != NULL);
+ assert(e->type == ELEMENT_STRING);
+ return &e->value.string_value;
+}
+
+struct list *
+listValue(struct element *e)
+{
+ assert(e != NULL);
+ assert(e->type == ELEMENT_LIST);
+ return &e->value.list_value;
+}
+
+struct map *
+mapValue(struct element *e)
+{
+ assert(e != NULL);
+ assert(e->type == ELEMENT_MAP);
+ return &e->value.map_value;
+}
+
+struct element *
+create(void)
+{
+ struct element *elem;
+
+ elem = (struct element *)malloc(sizeof(struct element));
+ assert(elem != NULL);
+ memset(elem, 0, sizeof(struct element));
+ TAILQ_INIT(&elem->comments);
+
+ return elem;
+}
+
+struct element *
+createInt(int64_t i)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_INTEGER;
+ elem->value.int_value = i;
+
+ return elem;
+}
+
+struct element *
+createDouble(double d)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_REAL;
+ elem->value.double_value = d;
+
+ return elem;
+}
+
+struct element *
+createBool(isc_boolean_t b)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_BOOLEAN;
+ elem->value.bool_value = b;
+
+ return elem;
+}
+
+struct element *
+createNull(void)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_NULL;
+
+ return elem;
+}
+
+struct element *
+createString(const struct string *s)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_STRING;
+ elem->value.string_value = *s;
+
+ return elem;
+}
+
+struct element *
+createList(void)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_LIST;
+ TAILQ_INIT(&elem->value.list_value);
+
+ return elem;
+}
+
+struct element *
+createMap(void)
+{
+ struct element *elem;
+
+ elem = create();
+ elem->type = ELEMENT_MAP;
+ TAILQ_INIT(&elem->value.map_value);
+
+ return elem;
+}
+
+static void
+reset(struct element *e)
+{
+ e->type = 0;
+ e->kind = 0;
+ assert(e->key == NULL);
+ memset(&e->value, 0, sizeof(e->value));
+}
+
+void
+resetInt(struct element *e, int64_t i)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_INTEGER;
+ e->value.int_value = i;
+}
+
+void
+resetDouble(struct element *e, double d)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_REAL;
+ e->value.double_value = d;
+}
+
+void
+resetBool(struct element *e, isc_boolean_t b)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_BOOLEAN;
+ e->value.bool_value = b;
+}
+
+void resetNull(struct element *e)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_NULL;
+}
+
+void
+resetString(struct element *e, const struct string *s)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_STRING;
+ e->value.string_value = *s;
+}
+
+void
+resetList(struct element *e)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_LIST;
+ TAILQ_INIT(&e->value.list_value);
+}
+
+void
+resetMap(struct element *e)
+{
+ assert(e != NULL);
+
+ reset(e);
+ e->type = ELEMENT_MAP;
+ TAILQ_INIT(&e->value.map_value);
+}
+
+void
+resetBy(struct element *e, struct element *o)
+{
+ assert(e != NULL);
+ assert(o != NULL);
+
+ reset(e);
+ e->type = o->type;
+ e->kind = o->kind;
+ e->skip = o->skip;
+ e->key = o->key;
+ o->key = NULL;
+ TAILQ_CONCAT(&e->comments, &o->comments);
+
+ switch (e->type) {
+ case ELEMENT_INTEGER:
+ e->value.int_value = o->value.int_value;
+ break;
+ case ELEMENT_REAL:
+ e->value.double_value = o->value.double_value;
+ break;
+ case ELEMENT_BOOLEAN:
+ e->value.bool_value = o->value.bool_value;
+ break;
+ case ELEMENT_STRING:
+ e->value.string_value = o->value.string_value;
+ break;
+ case ELEMENT_LIST:
+ TAILQ_INIT(&e->value.list_value);
+ TAILQ_CONCAT(&e->value.list_value, &o->value.list_value);
+ break;
+ case ELEMENT_MAP:
+ TAILQ_INIT(&e->value.map_value);
+ TAILQ_CONCAT(&e->value.map_value, &o->value.map_value);
+ break;
+ default:
+ assert(0);
+ }
+ reset(o);
+}
+
+struct element *
+listGet(struct element *l, int i)
+{
+ struct element *elem;
+
+ assert(l != NULL);
+ assert(l->type == ELEMENT_LIST);
+ assert(i >= 0);
+
+ elem = TAILQ_FIRST(&l->value.list_value);
+ assert(elem != NULL);
+ assert(elem->key == NULL);
+
+ unsigned j;
+ for (j = i; j > 0; --j) {
+ elem = TAILQ_NEXT(elem);
+ assert(elem != NULL);
+ assert(elem->key == NULL);
+ }
+
+ return elem;
+}
+
+void
+listSet(struct element *l, struct element *e, int i)
+{
+ assert(l != NULL);
+ assert(l->type == ELEMENT_LIST);
+ assert(e != NULL);
+ assert(i >= 0);
+
+ if (i == 0) {
+ TAILQ_INSERT_HEAD(&l->value.list_value, e);
+ } else {
+ struct element *prev;
+
+ prev = TAILQ_FIRST(&l->value.list_value);
+ assert(prev != NULL);
+ assert(prev->key == NULL);
+
+ unsigned j;
+ for (j = i; j > 1; --j) {
+ prev = TAILQ_NEXT(prev);
+ assert(prev != NULL);
+ assert(prev->key == NULL);
+ }
+
+ TAILQ_INSERT_AFTER(&l->value.list_value, prev, e);
+ }
+}
+
+void
+listPush(struct element *l, struct element *e)
+{
+ assert(l != NULL);
+ assert(l->type == ELEMENT_LIST);
+ assert(e != NULL);
+
+ TAILQ_INSERT_TAIL(&l->value.list_value, e);
+}
+
+void
+listRemove(struct element *l, int i)
+{
+ struct element *elem;
+
+ assert(l != NULL);
+ assert(l->type == ELEMENT_LIST);
+ assert(i >= 0);
+
+ elem = TAILQ_FIRST(&l->value.list_value);
+ assert(elem != NULL);
+ assert(elem->key == NULL);
+
+ unsigned j;
+ for (j = i; j > 0; --j) {
+ elem = TAILQ_NEXT(elem);
+ assert(elem != NULL);
+ assert(elem->key == NULL);
+ }
+
+ TAILQ_REMOVE(&l->value.list_value, elem);
+}
+
+size_t
+listSize(const struct element *l)
+{
+ struct element *elem;
+ size_t cnt;
+
+ assert(l != NULL);
+ assert(l->type == ELEMENT_LIST);
+
+ cnt = 0;
+ TAILQ_FOREACH(elem, &l->value.list_value) {
+ assert(elem->key == NULL);
+ cnt++;
+ }
+
+ return cnt;
+}
+
+void
+concat(struct element *l, struct element *o)
+{
+ assert(l != NULL);
+ assert(l->type == ELEMENT_LIST);
+ assert(o != NULL);
+ assert(o->type == ELEMENT_LIST);
+
+ TAILQ_CONCAT(&l->value.list_value, &o->value.list_value);
+}
+
+struct element *
+mapGet(struct element *m, const char *k)
+{
+ struct element *elem;
+
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+ assert(k != NULL);
+
+ TAILQ_FOREACH(elem, &m->value.map_value) {
+ assert(elem->key != NULL);
+ if (strcmp(elem->key, k) == 0)
+ break;
+ }
+
+ return elem;
+}
+
+void
+mapSet(struct element *m, struct element *e, const char *k)
+{
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+ assert(e != NULL);
+ assert(k != NULL);
+#if 0
+ assert(mapGet(m, k) == NULL);
+#endif
+ e->key = strdup(k);
+ assert(e->key != NULL);
+ TAILQ_INSERT_TAIL(&m->value.map_value, e);
+}
+
+void
+mapRemove(struct element *m, const char *k)
+{
+ struct element *elem;
+
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+ assert(k != NULL);
+
+ TAILQ_FOREACH(elem, &m->value.map_value) {
+ assert(elem->key != NULL);
+ if (strcmp(elem->key, k) == 0)
+ break;
+ }
+
+ assert(elem != NULL);
+ TAILQ_REMOVE(&m->value.map_value, elem);
+}
+
+isc_boolean_t
+mapContains(const struct element *m, const char *k)
+{
+ struct element *elem;
+
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+ assert(k != NULL);
+
+ TAILQ_FOREACH(elem, &m->value.map_value) {
+ assert(elem->key != NULL);
+ if (strcmp(elem->key, k) == 0)
+ break;
+ }
+
+ return ISC_TF(elem != NULL);
+}
+
+size_t
+mapSize(const struct element *m)
+{
+ struct element *elem;
+ size_t cnt;
+
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+
+ cnt = 0;
+ TAILQ_FOREACH(elem, &m->value.map_value) {
+ assert(elem->key != NULL);
+ cnt++;
+ }
+
+ return cnt;
+}
+
+void
+merge(struct element *m, struct element *o)
+{
+ struct element *elem;
+ struct element *ne;
+
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+ assert(o != NULL);
+ assert(o->type == ELEMENT_MAP);
+
+ TAILQ_FOREACH_SAFE(elem, &o->value.map_value, ne) {
+ assert(elem->key != NULL);
+ TAILQ_REMOVE(&o->value.map_value, elem);
+ if (!mapContains(m, elem->key)) {
+ TAILQ_INSERT_TAIL(&m->value.map_value, elem);
+ }
+ }
+}
+
+const char *
+type2name(int t)
+{
+ switch (t) {
+ case ELEMENT_NONE:
+ return "not initialized?";
+ case ELEMENT_INTEGER:
+ return "integer";
+ case ELEMENT_REAL:
+ return "real";
+ case ELEMENT_BOOLEAN:
+ return "boolean";
+ case ELEMENT_NULL:
+ return "(unused) null";
+ case ELEMENT_STRING:
+ return "string";
+ case ELEMENT_LIST:
+ return "list";
+ case ELEMENT_MAP:
+ return "map";
+ default:
+#if 0
+ assert(0);
+#endif
+ return "unknown?";
+ }
+}
+
+int
+name2type(const char *n)
+{
+ assert(n != NULL);
+ if (strcmp(n, "integer") == 0)
+ return ELEMENT_INTEGER;
+ if (strcmp(n, "real") == 0)
+ return ELEMENT_REAL;
+ if (strcmp(n, "boolean") == 0)
+ return ELEMENT_BOOLEAN;
+ if (strcmp(n, "null") == 0)
+ return ELEMENT_NULL;
+ if (strcmp(n, "string") == 0)
+ return ELEMENT_STRING;
+ if (strcmp(n, "list") == 0)
+ return ELEMENT_LIST;
+ if (strcmp(n, "map") == 0)
+ return ELEMENT_MAP;
+#if 0
+ assert(0);
+#endif
+ return ELEMENT_NONE;
+}
+
+void
+print(FILE *fp, const struct element *e, isc_boolean_t skip, unsigned indent)
+{
+ assert(fp != NULL);
+ assert(e != NULL);
+
+ switch (e->type) {
+ case ELEMENT_LIST:
+ printList(fp, &e->value.list_value, skip, indent);
+ return;
+ case ELEMENT_MAP:
+ printMap(fp, &e->value.map_value, skip, indent);
+ return;
+ case ELEMENT_STRING:
+ printString(fp, &e->value.string_value);
+ return;
+ case ELEMENT_INTEGER:
+ fprintf(fp, "%lld", (long long)e->value.int_value);
+ return;
+ case ELEMENT_REAL:
+ fprintf(fp, "%f", e->value.double_value);
+ return;
+ case ELEMENT_BOOLEAN:
+ if (e->value.bool_value)
+ fprintf(fp, "true");
+ else
+ fprintf(fp, "false");
+ return;
+ case ELEMENT_NULL:
+ fprintf(fp, "null");
+ return;
+ default:
+ assert(0);
+ }
+}
+
+static void
+addIndent(FILE *fp, int skip, unsigned indent)
+{
+ unsigned sp;
+
+ if (skip) {
+ fprintf(fp, "//");
+ if (indent > 2)
+ for (sp = 0; sp < indent - 2; ++sp)
+ fprintf(fp, " ");
+ } else
+ for (sp = 0; sp < indent; ++sp)
+ fprintf(fp, " ");
+}
+
+void
+printList(FILE *fp, const struct list *l, isc_boolean_t skip, unsigned indent)
+{
+ struct element *elem;
+ struct comment *comment;
+ isc_boolean_t first;
+
+ assert(fp != NULL);
+ assert(l != NULL);
+
+ if (TAILQ_EMPTY(l)) {
+ fprintf(fp, "[ ]");
+ return;
+ }
+
+ fprintf(fp, "[\n");
+ first = ISC_TRUE;
+ TAILQ_FOREACH(elem, l) {
+ isc_boolean_t skip_elem = skip;
+
+ assert(elem->key == NULL);
+ if (!skip) {
+ skip_elem = elem->skip;
+ if (skip_to_end(elem)) {
+ if (!first)
+ fprintf(fp, "\n");
+ first = ISC_TRUE;
+ }
+ }
+ if (!first)
+ fprintf(fp, ",\n");
+ first = ISC_FALSE;
+ TAILQ_FOREACH(comment, &elem->comments) {
+ addIndent(fp, skip_elem, indent + 2);
+ fprintf(fp, "%s\n", comment->line);
+ }
+ addIndent(fp, skip_elem, indent + 2);
+ print(fp, elem, skip_elem, indent + 2);
+ }
+ fprintf(fp, "\n");
+ addIndent(fp, skip, indent);
+ fprintf(fp, "]");
+}
+
+void
+printMap(FILE *fp, const struct map *m, isc_boolean_t skip, unsigned indent)
+{
+ struct element *elem;
+ struct comment *comment;
+ isc_boolean_t first;
+
+ assert(fp != NULL);
+ assert(m != NULL);
+
+ if (TAILQ_EMPTY(m)) {
+ fprintf(fp, "{ }");
+ return;
+ }
+
+ fprintf(fp, "{\n");
+ first = ISC_TRUE;
+ TAILQ_FOREACH(elem, m) {
+ isc_boolean_t skip_elem = skip;
+
+ assert(elem->key != NULL);
+ if (!skip) {
+ skip_elem = elem->skip;
+ if (skip_to_end(elem)) {
+ if (!first)
+ fprintf(fp, "\n");
+ first = ISC_TRUE;
+ }
+ }
+ if (!first)
+ fprintf(fp, ",\n");
+ first = ISC_FALSE;
+ TAILQ_FOREACH(comment, &elem->comments) {
+ addIndent(fp, skip_elem, indent + 2);
+ fprintf(fp, "%s\n", comment->line);
+ }
+ addIndent(fp, skip_elem, indent + 2);
+ fprintf(fp, "\"%s\": ", elem->key);
+ print(fp, elem, skip_elem, indent + 2);
+ }
+ fprintf(fp, "\n");
+ addIndent(fp, skip, indent);
+ fprintf(fp, "}");
+}
+
+void
+printString(FILE *fp, const struct string *s)
+{
+ size_t i;
+
+ assert(fp != NULL);
+ assert(s != NULL);
+
+ fprintf(fp, "\"");
+ for (i = 0; i < s->length; i++) {
+ char c = *(s->content + i);
+
+ switch (c) {
+ case '"':
+ fprintf(fp, "\\\"");
+ break;
+ case '\\':
+ fprintf(fp, "\\\\");
+ break;
+ case '\b':
+ fprintf(fp, "\\b");
+ break;
+ case '\f':
+ fprintf(fp, "\\f");
+ break;
+ case '\n':
+ fprintf(fp, "\\n");
+ break;
+ case '\r':
+ fprintf(fp, "\\r");
+ break;
+ case '\t':
+ fprintf(fp, "\\t");
+ break;
+ default:
+ if ((c >= 0) && (c < 0x20)) {
+ fprintf(fp, "\\u%04x", (unsigned)c & 0xff);
+ } else {
+ fprintf(fp, "%c", c);
+ }
+ }
+ }
+ fprintf(fp, "\"");
+}
+
+isc_boolean_t
+skip_to_end(const struct element *e)
+{
+ do {
+ if (!e->skip)
+ return ISC_FALSE;
+ e = TAILQ_NEXT(e);
+ } while (e != NULL);
+ return ISC_TRUE;
+}
+
+struct element *
+copy(struct element *e)
+{
+ struct element *result;
+ struct comment *comment;
+
+ assert(e != NULL);
+
+ switch (e->type) {
+ case ELEMENT_INTEGER:
+ result = createInt(intValue(e));
+ break;
+ case ELEMENT_REAL:
+ result = createDouble(doubleValue(e));
+ break;
+ case ELEMENT_BOOLEAN:
+ result = createBool(boolValue(e));
+ break;
+ case ELEMENT_NULL:
+ result = createNull();
+ break;
+ case ELEMENT_STRING:
+ result = createString(stringValue(e));
+ break;
+ case ELEMENT_LIST:
+ result = copyList(e);
+ break;
+ case ELEMENT_MAP:
+ result = copyMap(e);
+ break;
+ default:
+ assert(0);
+ }
+ result->kind = e->kind;
+ result->skip = e->skip;
+ /* don't copy key */
+ /* copy comments */
+ TAILQ_FOREACH(comment, &e->comments) {
+ /* do not reuse comment variable! */
+ struct comment *tmp;
+
+ tmp = createComment(comment->line);
+ TAILQ_INSERT_TAIL(&result->comments, tmp);
+ }
+ return result;
+}
+
+struct element *
+copyList(struct element *l)
+{
+ struct element *result;
+ size_t i;
+
+ result = createList();
+ for (i = 0; i < listSize(l); i++)
+ listPush(result, copy(listGet(l, i)));
+ return result;
+}
+
+struct element *
+copyMap(struct element *m)
+{
+ struct element *result;
+ struct element *item;
+
+ result = createMap();
+ TAILQ_FOREACH(item, &m->value.map_value)
+ mapSet(result, copy(item), item->key);
+ return result;
+}
+
+struct handle *
+mapPop(struct element *m)
+{
+ struct element *item;
+ struct handle *h;
+
+ assert(m != NULL);
+ assert(m->type == ELEMENT_MAP);
+
+ h = (struct handle *)malloc(sizeof(struct handle));
+ assert(h != NULL);
+ memset(h, 0, sizeof(struct handle));
+ TAILQ_INIT(&h->values);
+
+ item = TAILQ_FIRST(&m->value.map_value);
+ assert(item != NULL);
+ assert(item->key != NULL);
+ h->key = strdup(item->key);
+ assert(h->key != NULL);
+ h->value = item;
+
+ TAILQ_REMOVE(&m->value.map_value, item);
+
+ return h;
+}
+
+void
+derive(struct handle *src, struct handle *dst)
+{
+ struct element *list;
+ struct element *item;
+ size_t i;
+
+ if (dst == NULL)
+ return;
+ list = dst->value;
+ assert(list != NULL);
+ assert(list->type == ELEMENT_LIST);
+ for (i = 0; i < listSize(list); i++) {
+ item = listGet(list, i);
+ assert(item != NULL);
+ assert(item->type == ELEMENT_MAP);
+ if (mapContains(item, src->key))
+ continue;
+ mapSet(item, copy(src->value), src->key);
+ }
+}
+
+struct string *
+hexaValue(struct element *s)
+{
+ struct string *h;
+
+ assert(s != NULL);
+ assert(s->type == ELEMENT_STRING);
+
+ h = stringValue(s);
+ assert(h->length >= 2);
+
+ /* string leading 0x */
+ return makeString(h->length - 2, h->content + 2);
+}
+
+struct element *
+createHexa(struct string *h)
+{
+ struct string *s;
+
+ assert(h != NULL);
+
+ s = makeString(-1, "0x");
+ concatString(s, h);
+ return createString(s);
+}
diff --git a/keama/data.h b/keama/data.h
new file mode 100644
index 00000000..b8078cc3
--- /dev/null
+++ b/keama/data.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ */
+
+#ifndef DATA_H
+#define DATA_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+/* From FreeBSD sys/queue.h */
+
+/*
+ * 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_CONCAT(head1, head2) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->next.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var)))
+
+#define TAILQ_FOREACH_SAFE(var, head, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var)), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm) do { \
+ if ((TAILQ_NEXT((elm)) = TAILQ_NEXT((listelm))) != NULL) \
+ TAILQ_NEXT((elm))->next.tqe_prev = \
+ &TAILQ_NEXT((elm)); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm)); \
+ } \
+ TAILQ_NEXT((listelm)) = (elm); \
+ (elm)->next.tqe_prev = &TAILQ_NEXT((listelm)); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm) do { \
+ (elm)->next.tqe_prev = (listelm)->next.tqe_prev; \
+ TAILQ_NEXT((elm)) = (listelm); \
+ *(listelm)->next.tqe_prev = (elm); \
+ (listelm)->next.tqe_prev = &TAILQ_NEXT((elm)); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm) do { \
+ if ((TAILQ_NEXT((elm)) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->next.tqe_prev = \
+ &TAILQ_NEXT((elm)); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm)); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->next.tqe_prev = &TAILQ_FIRST((head)); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm) do { \
+ TAILQ_NEXT((elm)) = NULL; \
+ (elm)->next.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm)); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm) ((elm)->next.tqe_next)
+
+#define TAILQ_PREV(elm, headname) \
+ (*(((struct headname *)((elm)->next.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm) do { \
+ if ((TAILQ_NEXT((elm))) != NULL) \
+ TAILQ_NEXT((elm))->next.tqe_prev = \
+ (elm)->next.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->next.tqe_prev; \
+ *(elm)->next.tqe_prev = TAILQ_NEXT((elm)); \
+ (elm)->next.tqe_next = (void *)-1; \
+ (elm)->next.tqe_prev = (void *)-1; \
+} while (0)
+
+#define TAILQ_SWAP(head1, head2, type) do { \
+ struct type *swap_first = (head1)->tqh_first; \
+ struct type **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->next.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->next.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+} while (0)
+
+/* From bind9 lib/isc/include/isc/boolean.h */
+
+typedef enum { isc_boolean_false = 0, isc_boolean_true = 1 } isc_boolean_t;
+
+#define ISC_FALSE isc_boolean_false
+#define ISC_TRUE isc_boolean_true
+#define ISC_TF(x) ((x) ? ISC_TRUE : ISC_FALSE)
+
+/* From Kea src/lib/cc/data.h */
+
+struct element;
+
+/* Element types */
+#define ELEMENT_NONE 0
+#define ELEMENT_INTEGER 1
+#define ELEMENT_REAL 2
+#define ELEMENT_BOOLEAN 3
+#define ELEMENT_NULL 4
+#define ELEMENT_STRING 5
+#define ELEMENT_LIST 6
+#define ELEMENT_MAP 7
+
+/* Element string */
+struct string {
+ size_t length; /* string length */
+ char *content; /* string data */
+};
+
+struct string *allocString(void);
+/* In makeString() l == -1 means use strlen(s) */
+struct string *makeString(int l, const char *s);
+/* format ZlLsSbBfXI6 + h */
+struct string *makeStringExt(int l, const char *s, char fmt);
+/* format 6lLIsSbBj */
+struct string *makeStringArray(int l, const char *s, char fmt);
+void appendString(struct string *s, const char *a);
+void concatString(struct string *s, const struct string *a);
+isc_boolean_t eqString(const struct string *s, const struct string *o);
+/* quoting */
+struct string *quote(struct string *);
+
+/* Comments */
+struct comment {
+ char *line; /* comment line */
+ TAILQ_ENTRY(comment) next; /* next line */
+};
+TAILQ_HEAD(comments, comment);
+
+struct comment *createComment(const char *line);
+
+/* Element list */
+TAILQ_HEAD(list, element);
+
+/* Element map */
+TAILQ_HEAD(map, element);
+
+/* Element value */
+union value {
+ int64_t int_value; /* integer */
+ double double_value; /* real */
+ isc_boolean_t bool_value; /* boolean */
+ /**/ /* null */
+ struct string string_value; /* string */
+ struct list list_value; /* list */
+ struct map map_value; /* map */
+};
+
+/* Element */
+struct element {
+ int type; /* element type (ELEMENT_XXX) */
+ int kind; /* element kind (e.g. ROOT_GROUP) */
+ isc_boolean_t skip; /* skip as not converted */
+ char *key; /* element key (for map) */
+ union value value; /* value */
+ struct comments comments; /* associated comments */
+ TAILQ_ENTRY(element) next; /* next item in list or map chain */
+};
+
+/* Value getters */
+int64_t intValue(const struct element *e);
+double doubleValue(const struct element *e);
+isc_boolean_t boolValue(const struct element *e);
+struct string *stringValue(struct element *e);
+struct list *listValue(struct element *e);
+struct map *mapValue(struct element *e);
+
+/* Creators */
+struct element *create(void);
+struct element *createInt(int64_t i);
+struct element *createDouble(double d);
+struct element *createBool(isc_boolean_t b);
+struct element *createNull(void);
+struct element *createString(const struct string *s);
+struct element *createList(void);
+struct element *createMap(void);
+
+/* Reset */
+void resetInt(struct element *e, int64_t i);
+void resetDouble(struct element *e, double d);
+void resetBool(struct element *e, isc_boolean_t b);
+void resetNull(struct element *e);
+void resetString(struct element *e, const struct string *s);
+void resetList(struct element *e);
+void resetMap(struct element *e);
+void resetBy(struct element *e, struct element *o);
+
+/* List functions */
+struct element *listGet(struct element *l, int i);
+void listSet(struct element *l, struct element *e, int i);
+void listPush(struct element *l, struct element *e);
+void listRemove(struct element *l, int i);
+size_t listSize(const struct element *l);
+void concat(struct element *l, struct element *o);
+
+/* Map functions */
+struct element *mapGet(struct element *m, const char *k);
+void mapSet(struct element *m, struct element *e, const char *k);
+void mapRemove(struct element *m, const char *k);
+isc_boolean_t mapContains(const struct element *m, const char *k);
+size_t mapSize(const struct element *m);
+void merge(struct element *m, struct element *o);
+
+/* Tools */
+const char *type2name(int t);
+int name2type(const char *n);
+void print(FILE *fp, const struct element *e,
+ isc_boolean_t skip, unsigned indent);
+void printList(FILE *fp, const struct list *l,
+ isc_boolean_t skip, unsigned indent);
+void printMap(FILE *fp, const struct map *m,
+ isc_boolean_t skip, unsigned indent);
+void printString(FILE *fp, const struct string *s);
+isc_boolean_t skip_to_end(const struct element *e);
+
+struct element *copy(struct element *e);
+struct element *copyList(struct element *l);
+struct element *copyMap(struct element *m);
+
+/* Handles */
+TAILQ_HEAD(handles, handle);
+
+struct handle {
+ unsigned order; /* order */
+ char *key; /* key */
+ struct element *value; /* value */
+ struct handles values; /* children */
+ TAILQ_ENTRY(handle) next; /* siblings */
+};
+
+struct handle* mapPop(struct element *);
+void derive(struct handle *, struct handle *);
+
+/* Hexadecimal literals */
+struct string *hexaValue(struct element *);
+struct element *createHexa(struct string *);
+
+#endif /* DATA_H */
diff --git a/keama/dhctoken.h b/keama/dhctoken.h
new file mode 100644
index 00000000..ac24463a
--- /dev/null
+++ b/keama/dhctoken.h
@@ -0,0 +1,389 @@
+/* dhctoken.h
+
+ Tokens for config file lexer and parser. */
+
+/*
+ * Copyright (c) 2004-2016 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+/*
+ * The following tokens have been deprecated and aren't in use anymore.
+ * They have been left in place to avoid disturbing the code.
+ * DNS_UPDATE, DNS_DELETE, NS_UPDATE, UPDATED_DNS_RR
+ */
+/*
+ * For the Kea Migration Assistant only '[' and ']' where added for
+ * the JSON test parser (no required cast to int in switches on tokens)
+ */
+enum dhcp_token {
+ SEMI = ';',
+ DOT = '.',
+ COLON = ':',
+ COMMA = ',',
+ SLASH = '/',
+ LBRACE = '{',
+ RBRACE = '}',
+ LBRACKET = '[',
+ RBRACKET = ']',
+ LPAREN = '(',
+ RPAREN = ')',
+ EQUAL = '=',
+ TILDE = '~',
+ BANG = '!',
+ PERCENT = '%',
+ PLUS = '+',
+ MINUS = '-',
+ ASTERISK = '*',
+ AMPERSAND = '&',
+ PIPE = '|',
+ CARET = '^',
+ ENDOFLINE = '\n',
+ QUESTIONMARK = '?',
+
+ HOST = 256,
+ FIRST_TOKEN = HOST,
+ HARDWARE = 257,
+ FILENAME = 258,
+ FIXED_ADDR = 259,
+ OPTION = 260,
+ ETHERNET = 261,
+ STRING = 262,
+ NUMBER = 263,
+ NUMBER_OR_NAME = 264,
+ NAME = 265,
+ TIMESTAMP = 266,
+ STARTS = 267,
+ ENDS = 268,
+ UID = 269,
+ CLASS = 270,
+ LEASE = 271,
+ RANGE = 272,
+ PACKET = 273,
+ CIADDR = 274,
+ YIADDR = 275,
+ SIADDR = 276,
+ GIADDR = 277,
+ SUBNET = 278,
+ NETMASK = 279,
+ DEFAULT_LEASE_TIME = 280,
+ MAX_LEASE_TIME = 281,
+ VENDOR_CLASS = 282,
+ USER_CLASS = 283,
+ SHARED_NETWORK = 284,
+ SERVER_NAME = 285,
+ DYNAMIC_BOOTP = 286,
+ SERVER_IDENTIFIER = 287,
+ DYNAMIC_BOOTP_LEASE_CUTOFF = 288,
+ DYNAMIC_BOOTP_LEASE_LENGTH = 289,
+ BOOT_UNKNOWN_CLIENTS = 290,
+ NEXT_SERVER = 291,
+ TOKEN_RING = 292,
+ GROUP = 293,
+ ONE_LEASE_PER_CLIENT = 294,
+ GET_LEASE_HOSTNAMES = 295,
+ USE_HOST_DECL_NAMES = 296,
+ SEND = 297,
+ CLIENT_IDENTIFIER = 298,
+ REQUEST = 299,
+ REQUIRE = 300,
+ TIMEOUT = 301,
+ RETRY = 302,
+ SELECT_TIMEOUT = 303,
+ SCRIPT = 304,
+ INTERFACE = 305,
+ RENEW = 306,
+ REBIND = 307,
+ EXPIRE = 308,
+ UNKNOWN_CLIENTS = 309,
+ ALLOW = 310,
+ DENY = 312,
+ BOOTING = 313,
+ DEFAULT = 314,
+ MEDIA = 315,
+ MEDIUM = 316,
+ ALIAS = 317,
+ REBOOT = 318,
+ TOKEN_ABANDONED = 319,
+ BACKOFF_CUTOFF = 320,
+ INITIAL_INTERVAL = 321,
+ NAMESERVER = 322,
+ DOMAIN = 323,
+ SEARCH = 324,
+ SUPERSEDE = 325,
+ APPEND = 326,
+ PREPEND = 327,
+ HOSTNAME = 328,
+ CLIENT_HOSTNAME = 329,
+ REJECT = 330,
+ USE_LEASE_ADDR_FOR_DEFAULT_ROUTE = 331,
+ MIN_LEASE_TIME = 332,
+ MIN_SECS = 333,
+ AND = 334,
+ OR = 335,
+ SUBSTRING = 337,
+ SUFFIX = 338,
+ CHECK = 339,
+ EXTRACT_INT = 340,
+ IF = 341,
+ TOKEN_ADD = 342,
+ BREAK = 343,
+ ELSE = 344,
+ ELSIF = 345,
+ SUBCLASS = 346,
+ MATCH = 347,
+ SPAWN = 348,
+ WITH = 349,
+ EXISTS = 350,
+ POOL = 351,
+ UNKNOWN = 352,
+ CLIENTS = 353,
+ KNOWN = 354,
+ AUTHENTICATED = 355,
+ UNAUTHENTICATED = 356,
+ ALL = 357,
+ DYNAMIC = 358,
+ MEMBERS = 359,
+ OF = 360,
+ PSEUDO = 361,
+ LIMIT = 362,
+ BILLING = 363,
+ PEER = 364,
+ FAILOVER = 365,
+ MY = 366,
+ PARTNER = 367,
+ PRIMARY = 368,
+ SECONDARY = 369,
+ IDENTIFIER = 370,
+ PORT = 371,
+ MAX_TRANSMIT_IDLE = 372,
+ MAX_RESPONSE_DELAY = 373,
+ PARTNER_DOWN = 374,
+ NORMAL = 375,
+ COMMUNICATIONS_INTERRUPTED = 376,
+ POTENTIAL_CONFLICT = 377,
+ RECOVER = 378,
+ TOKEN_FDDI = 379,
+ AUTHORITATIVE = 380,
+ TOKEN_NOT = 381,
+ AUTHENTICATION = 383,
+ IGNORE = 384,
+ ACCEPT = 385,
+ PREFER = 386,
+ DONT = 387,
+ CODE = 388,
+ ARRAY = 389,
+ BOOLEAN = 390,
+ INTEGER = 391,
+ SIGNED = 392,
+ UNSIGNED = 393,
+ IP_ADDRESS = 394,
+ TEXT = 395,
+ STRING_TOKEN = 396,
+ SPACE = 397,
+ CONCAT = 398,
+ ENCODE_INT = 399,
+ REVERSE = 402,
+ LEASED_ADDRESS = 403,
+ BINARY_TO_ASCII = 404,
+ PICK = 405,
+ CONFIG_OPTION = 406,
+ HOST_DECL_NAME = 407,
+ ON = 408,
+ EXPIRY = 409,
+ RELEASE = 410,
+ COMMIT = 411,
+ DNS_UPDATE = 412,
+ LEASE_TIME = 413,
+ STATIC = 414,
+ NEVER = 415,
+ INFINITE = 416,
+ TOKEN_DELETED = 417,
+ UPDATED_DNS_RR = 418,
+ DNS_DELETE = 419,
+ DUPLICATES = 420,
+ DECLINES = 421,
+ TSTP = 422,
+ TSFP = 423,
+ OWNER = 424,
+ IS = 425,
+ HBA = 426,
+ MAX_UNACKED_UPDATES = 427,
+ MCLT = 428,
+ SPLIT = 429,
+ AT = 430,
+ TOKEN_NO = 431,
+ TOKEN_DELETE = 432,
+ NS_UPDATE = 433,
+ UPDATE = 434,
+ SWITCH = 435,
+ CASE = 436,
+ NS_FORMERR = 437,
+ NS_NOERROR = 438,
+ NS_NOTAUTH = 439,
+ NS_NOTIMP = 440,
+ NS_NOTZONE = 441,
+ NS_NXDOMAIN = 442,
+ NS_NXRRSET = 443,
+ NS_REFUSED = 444,
+ NS_SERVFAIL = 445,
+ NS_YXDOMAIN = 446,
+ NS_YXRRSET = 447,
+ TOKEN_NULL = 448,
+ TOKEN_SET = 449,
+ DEFINED = 450,
+ UNSET = 451,
+ EVAL = 452,
+ LET = 453,
+ FUNCTION = 454,
+ DEFINE = 455,
+ ZONE = 456,
+ KEY = 457,
+ SECRET = 458,
+ ALGORITHM = 459,
+ LOAD = 460,
+ BALANCE = 461,
+ TOKEN_MAX = 462,
+ SECONDS = 463,
+ ADDRESS = 464,
+ RESOLUTION_INTERRUPTED = 465,
+ STATE = 466,
+ UNKNOWN_STATE = 567,
+ CLTT = 568,
+ INCLUDE = 569,
+ BINDING = 570,
+ TOKEN_FREE = 571,
+ TOKEN_ACTIVE = 572,
+ TOKEN_EXPIRED = 573,
+ TOKEN_RELEASED = 574,
+ TOKEN_RESET = 575,
+ TOKEN_BACKUP = 576,
+ TOKEN_RESERVED = 577,
+ TOKEN_BOOTP = 578,
+ TOKEN_NEXT = 579,
+ OMAPI = 580,
+ LOG = 581,
+ FATAL = 582,
+ ERROR = 583,
+ TOKEN_DEBUG = 584,
+ INFO = 585,
+ RETURN = 586,
+ PAUSED = 587,
+ RECOVER_DONE = 588,
+ SHUTDOWN = 589,
+ STARTUP = 590,
+ ENCAPSULATE = 591,
+ VENDOR = 592,
+ CLIENT_STATE = 593,
+ INIT_REBOOT = 594,
+ TOKEN_INIT = 595,
+ SELECT = 596,
+ BOUND = 597,
+ RENEWING = 598,
+ REBINDING = 599,
+ RECONTACT_INTERVAL = 600,
+ CLIENT_UPDATES = 601,
+ TOKEN_NEW = 601,
+ TRANSMISSION = 602,
+ TOKEN_CLOSE = 603,
+ TOKEN_CREATE = 604,
+ TOKEN_OPEN = 605,
+ TOKEN_HELP = 606,
+ END_OF_FILE = 607,
+ RECOVER_WAIT = 608,
+ TOKEN_SERVER = 609,
+ CONNECT = 610,
+ REMOVE = 611,
+ REFRESH = 612,
+ DOMAIN_NAME = 613,
+ DO_FORWARD_UPDATE = 614,
+ KNOWN_CLIENTS = 615,
+ ATSFP = 616,
+ LCASE = 617,
+ UCASE = 618,
+ WIDTH = 619,
+ LENGTH = 620,
+ HASH = 621,
+ SIZE = 622,
+ EPOCH = 623,
+ DB_TIME_FORMAT = 624,
+ LOCAL = 625,
+ MAX_LEASE_MISBALANCE = 626,
+ MAX_LEASE_OWNERSHIP = 627,
+ MAX_BALANCE = 628,
+ MIN_BALANCE = 629,
+ DOMAIN_LIST = 630,
+ LEASEQUERY = 631,
+ EXECUTE = 632,
+ IP6_ADDRESS = 633,
+ FIXED_ADDR6 = 634,
+ COMPRESSED = 635,
+ SUBNET6 = 636,
+ HOST_IDENTIFIER = 637,
+ IA_NA = 638,
+ IA_TA = 639,
+ IA_PD = 640,
+ IAADDR = 641,
+ IAPREFIX = 642,
+ LEASE6 = 643,
+ PREFERRED_LIFE = 644,
+ MAX_LIFE = 645,
+ DEFAULT_DUID = 646,
+ SERVER_DUID = 647,
+ LLT = 648,
+ EN = 649,
+ LL = 650,
+ RANGE6 = 651,
+ WHITESPACE = 652,
+ TOKEN_ALSO = 653,
+ AFTER = 654,
+ ZEROLEN = 655,
+ TEMPORARY = 656,
+ PREFIX6 = 657,
+ FIXED_PREFIX6 = 658,
+ ANYCAST_MAC = 659,
+ CONFLICT_DONE = 660,
+ AUTO_PARTNER_DOWN = 661,
+ GETHOSTNAME = 662,
+ REWIND = 663,
+ INITIAL_DELAY = 664,
+ GETHOSTBYNAME = 665,
+ PRIMARY6 = 666,
+ SECONDARY6 = 667,
+ TOKEN_INFINIBAND = 668,
+ POOL6 = 669,
+ V6RELAY = 670,
+ V6RELOPT = 671,
+ PARSE_VENDOR_OPT = 672,
+ AUTHORING_BYTE_ORDER = 673,
+ TOKEN_LITTLE_ENDIAN = 674,
+ TOKEN_BIG_ENDIAN = 675,
+ LEASE_ID_FORMAT = 676,
+ TOKEN_HEX = 677,
+ TOKEN_OCTAL = 678,
+ KEY_ALGORITHM = 679
+};
+
+#define is_identifier(x) ((x) >= FIRST_TOKEN && \
+ (x) != STRING && \
+ (x) != NUMBER && \
+ (x) != END_OF_FILE)
diff --git a/keama/doc.txt b/keama/doc.txt
new file mode 100644
index 00000000..a1664a22
--- /dev/null
+++ b/keama/doc.txt
@@ -0,0 +1,516 @@
+Part 1: Kea Migration Assistant support
+=======================================
+
+Files:
+------
+ - data.h (tailq list and element type declarations)
+ - data.c (element type code)
+ - keama.h (DHCP declarations)
+ - keama.c (main() code)
+ - json.c (JSON parser)
+ - option.c (option tables and code)
+ - keama.8 (man page)
+
+The code heavily uses tailq lists, i.e. doubled linked lists with
+a pointer to the last (tail) element.
+
+The element structure mimics the Kea Element class with a few differences:
+ - no smart pointers
+ - extra fields to handle declaration kind, skip and comments
+ - maps are implemented as lists with an extra key field so the order
+ of insertion is kept and duplicates are possible
+ - strings are length + content (vs C strings)
+
+There is no attempt to avoid memory leaks.
+
+The skip flag is printed as '//' at the beginning of lines. It is set
+when something cannot be converted and the issue counter (returned
+by the keama command) incremented.
+
+Part 2: ISC DHCP lexer organization
+===================================
+
+Files:
+-----
+ - dhctoken.h (from includes, enum dhcp_token definition)
+ - conflex.c (from common, lexical analyzer code)
+
+Tokens (dhcp_token enum): characters are set to their ASCII value,
+ others are >= 256 without real organization (e.g. END_OF_FILE is 607).
+
+The state is in a parse structure named "cfile". There is one per file
+and a few routine save it in order to do a backtrack on a larger
+set than the usual lookahead.
+The largest function is intern() which recognizes keywords with
+a switch on the first character and a tree of if strcasecmp's.
+
+Standard routines:
+-----------------
+enum dhcp_token
+next_token(const char **rval, unsigned *rlen, struct parse *cfile);
+
+and
+
+enum dhcp_token
+peek_token(const char **rval, unsigned *rlen, struct parse *cfile);
+
+rval: if not null the content of the token is put in it
+rlen: if not null the length of the token is put in it
+cfile: lexer context
+return: the integer value of the token
+
+Changes:
+-------
+
+Added LBRACKET '[' and RBRACKET ']' tokens for JSON parser
+(switch on dhcp_token type).
+
+Added comments to collect ISC DHCP # comments, element stack to follow
+declaration hierarchy, and issue counter to struct parse.
+
+Moved the parse_warn (renamed into parse_error and made fatal) routine
+from conflex.c to keama.c
+
+Part 3: ISC DHCP parser organization
+====================================
+
+Files:
+-----
+ - confparse.c (from server)
+ for the server in parse_statement())
+ - parse.c (from common)
+
+4 classes: parameters, declarations, executable statements and expressions.
+
+the original code parses config and lease files, I kept only the first
+at the exception of parse_binding_value().
+
+entry point
+ |
+ V
+conf_file_parse
+ |
+ V
+conf_file_subparse <- read_conf_file (for include)
+ until END_OF_FILE call
+ |
+ V
+parse_statement
+ parse parameters and declarations
+ switch on token and call parse_xxx_declaration routines
+ on default or DHCPv6 token in DHCPv4 mode call parse_executable_statement
+ and put the result under the "statement" key
+ |
+ V
+parse_executable_statement
+
+According to comments the grammar is:
+
+ conf-file :== parameters declarations END_OF_FILE
+ parameters :== <nil> | parameter | parameters parameter
+ declarations :== <nil> | declaration | declarations declaration
+
+ statement :== parameter | declaration
+
+ parameter :== DEFAULT_LEASE_TIME lease_time
+ | MAX_LEASE_TIME lease_time
+ | DYNAMIC_BOOTP_LEASE_CUTOFF date
+ | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
+ | BOOT_UNKNOWN_CLIENTS boolean
+ | ONE_LEASE_PER_CLIENT boolean
+ | GET_LEASE_HOSTNAMES boolean
+ | USE_HOST_DECL_NAME boolean
+ | NEXT_SERVER ip-addr-or-hostname SEMI
+ | option_parameter
+ | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
+ | FILENAME string-parameter
+ | SERVER_NAME string-parameter
+ | hardware-parameter
+ | fixed-address-parameter
+ | ALLOW allow-deny-keyword
+ | DENY allow-deny-keyword
+ | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
+ | AUTHORITATIVE
+ | NOT AUTHORITATIVE
+
+ declaration :== host-declaration
+ | group-declaration
+ | shared-network-declaration
+ | subnet-declaration
+ | VENDOR_CLASS class-declaration
+ | USER_CLASS class-declaration
+ | RANGE address-range-declaration
+
+Typically declarations use { } and are associated with a group
+(changed to a type) in ROOT_GROUP (global), HOST_DECL, SHARED_NET_DECL,
+SUBNET_DECL, CLASS_DECL, GROUP_DECL and POOL_DECL.
+
+ROOT: parent = TOPLEVEL, children = everythig but not POOL
+HOST: parent = ROOT, GROUP, warn on SHARED or SUBNET, children = none
+SHARED_NET: parent = ROOT, GROUP, children = HOST (warn), SUBNET, POOL4
+SUBNET: parent = ROOT, GROUP, SHARED, children = HOST (warn), POOL
+CLASS: parent = ROOT, GROUP, children = none
+GROUP: parent = ROOT, SHARED, children = anything but not POOL
+POOL: parent = SHARED4, SUBNET, warn on others, children = none
+
+isc_boolean_t
+parse_statement(struct parse *cfile, int type, isc_boolean_t declaration);
+
+cfile: parser context
+type: declaration type
+declaration and return: declaration or parameter
+
+On the common side:
+
+ executable-statements :== executable-statement executable-statements |
+ executable-statement
+
+ executable-statement :==
+ IF if-statement |
+ ADD class-name SEMI |
+ BREAK SEMI |
+ OPTION option-parameter SEMI |
+ SUPERSEDE option-parameter SEMI |
+ PREPEND option-parameter SEMI |
+ APPEND option-parameter SEMI
+
+isc_boolean_t
+parse_executable_statement(struct element *result,
+ struct parse *cfile, isc_boolean_t *lose,
+ enum expression_context case_context,
+ isc_boolean_t direct);
+
+result: map element where to put the statement
+cfile: parser context
+lose: set to ISC_TRUE on failure
+case_context: expression context
+direct: called directly by parse_statement so can execute config statements
+return: success
+
+parse_executable_statement
+ switch on keywords (far more than in the comments)
+ on default with an identifier try a config option, on number or name
+ call parse_expression for a function call
+ |
+ V
+parse_expression
+
+expressions are divided into boolean, data (string) and numeric expressions
+
+ boolean_expression :== CHECK STRING |
+ NOT boolean-expression |
+ data-expression EQUAL data-expression |
+ data-expression BANG EQUAL data-expression |
+ data-expression REGEX_MATCH data-expression |
+ boolean-expression AND boolean-expression |
+ boolean-expression OR boolean-expression
+ EXISTS OPTION-NAME
+
+ data_expression :== SUBSTRING LPAREN data-expression COMMA
+ numeric-expression COMMA
+ numeric-expression RPAREN |
+ CONCAT LPAREN data-expression COMMA
+ data-expression RPAREN
+ SUFFIX LPAREN data_expression COMMA
+ numeric-expression RPAREN |
+ LCASE LPAREN data_expression RPAREN |
+ UCASE LPAREN data_expression RPAREN |
+ OPTION option_name |
+ HARDWARE |
+ PACKET LPAREN numeric-expression COMMA
+ numeric-expression RPAREN |
+ V6RELAY LPAREN numeric-expression COMMA
+ data-expression RPAREN |
+ STRING |
+ colon_separated_hex_list
+
+ numeric-expression :== EXTRACT_INT LPAREN data-expression
+ COMMA number RPAREN |
+ NUMBER
+
+parse_boolean_expression, parse_data_expression and parse_numeric_expression
+calls parse_expression and check its result
+
+parse_expression itself is divided into parse_non_binary and internal
+handling of binary operators
+
+isc_boolean_t
+parse_non_binary(struct element *expr, struct parse *cfile,
+ isc_boolean_t *lose, enum expression_context context)
+
+isc_boolean_t
+parse_expression(struct element *expr, struct parse *cfile,
+ isc_boolean_t *lose, enum expression_context context,
+ struct element *lhs, enum expr_op binop)
+
+expr: map element where to put the result
+cfile: parser context
+lose: set to ISC_TRUE on failure
+context: expression context
+lhs: NULL or left hand side
+binop: expr_none or binary operation
+return: success
+
+parse_non_binary
+ switch on unary and nullary operator keywords
+ on default try a variable reference or a function call
+
+parse_expression
+ call parse_non_binary to get the right hand side
+ switch on binary operator keywords to get the next operation
+ with one side if expr_none return else get the second hand
+ handle operator precedence, can call itself
+ return a map entry with the operator name as the key, and
+ left and right expression branches
+
+Part 4: Expression processing
+=============================
+
+Files:
+------
+ - print.c (new)
+ - eval.c (new)
+ - reduce.c (new)
+
+Print:
+------
+
+const char *
+print_expression(struct element *expr, isc_boolean_t *lose);
+const char *
+print_boolean_expression(struct element *expr, isc_boolean_t *lose);
+const char *
+print_data_expression(struct element *expr, isc_boolean_t *lose);
+const char *
+print_numeric_expression(struct element *expr, isc_boolean_t *lose);
+
+expr: expression to print
+lose: failure (??? in output) flag
+return: the text representing the expression
+
+Eval:
+-----
+
+struct element *
+eval_expression(struct element *expr, isc_boolean_t *modifiedp);
+struct element *
+eval_boolean_expression(struct element *expr, isc_boolean_t *modifiedp);
+struct element *
+eval_data_expression(struct element *expr, isc_boolean_t *modifiedp);
+struct element *
+eval_numeric_expression(struct element *expr, isc_boolean_t *modifiedp);
+
+expr: expression to evaluate
+modifiedp: a different element was returned (still false for updates
+ inside a map)
+return: the evaluated element (can have been updated for a map or a list,
+ or can be a fully different element)
+
+Evaluation is at parsing time so it is mainly a constant propagation.
+(no beta reduction for instance)
+
+Reduce:
+-------
+
+struct element *
+reduce_boolean_expression(struct element *expr);
+struct element *
+reduce_data_expression(struct element *expr);
+struct element *
+reduce_numeric_expression(struct element *expr);
+
+expr: expression to reduce
+return: NULL or the reduced expression as a Kea eval string
+
+reducing works for a limited (but interesting) set of expressions which
+can be converted to kea evaluatebool and for literals.
+
+Part 5: Specific issues
+=======================
+
+Reservations:
+-------------
+ ISC DHCP host declarations are global, Kea reservations were per subnet
+ only until 1.5.
+ It is possible to use the fixed address but:
+ - it is possible to finish with orphan reservations, i.e.
+ reservations with an address which match no subnets
+ - a reservation can have no fixed address. In this case the MA puts
+ the reservation in the last declared subnet.
+ - a reservation can have more than one fixed address and these
+ addresses can belong to different subnets. Current code pushes
+ IPv4 extra addresses in a commented extra-ip-addresses but
+ it is legal feature for IPv6.
+ - it is not easy to use prefix6
+ The use of groups in host declarations is unclear.
+ ISC DHCP UID is mapped to client-id, host-identifier to flex-id
+ Host reservation identifiers are generated on first use.
+
+Groups:
+-------
+TODO: search missing parameters from the Kea syntax.
+ (will be done in the third pass)
+
+Shared-Networks:
+----------------
+ Waiting for the feature to be supported by Kea.
+ Currently at the end of a shared network declaration:
+ - if there is no subnets it is a fatal error
+ - if there is one subnet the shared-network is squeezed
+ - if there are more than one subnet the shared-network is commented
+TODO (useful only with Kea support for shared networks): combine permit /
+deny classes (e.g. create negation) and pop filters to subnets when
+there is one pool.
+
+Vendor-Classes and User-Classes:
+--------------------------------
+ ISC DHCP code is inconsistent: in particular before setting the
+ super-class "tname" to "implicit-vendor-class" / "implicit-user-class"
+ it allocates a buffer for data but does not copy the lexical value
+ "val" into it... So I removed support.
+
+Classes:
+--------
+ Only pure client-classes are supported by kea.
+ Dynamic/deleted stuff is not supported but does it make sense?
+ To spawn classes is not supported.
+ Match class selector is converted to Kea eval test when the corresponding
+ expression can be reduced. Fortunately it seems to be the common case!
+ Lease limit is not supported.
+
+Subclasses:
+-----------
+ Understood how it works:
+ - (super) class defined with a MATCH <data-expression> (vs.
+ MATCH IF <boolean-expression>)
+ - subclasses defined by <superclass-name> <data-literal> which
+ are equivalent to
+ MATCH IF <superclass-data-expression> EQUAL <data-literal>
+ So subclasses are convertible when the data expression can be reduced.
+ Cf https://kb.isc.org/article/AA-01092/202/OMAPI-support-for-classes-and-subclasses.html
+ which BTW suggests the management API could manage classes...
+
+Hardware Addresses:
+-------------------
+ Kea supports only Ethernet.
+
+Pools:
+------
+ All permissions are not supported by Kea at the exception of class members
+ but in a very different way so not convertible.
+ Mixed DHCPv6 address and prefix pools are not supported, perhaps in this
+ case the pool should be duplicated into pool and pd-pool instances?
+ The bootp stuff was ifdef's as bootp is obsolete.
+ Temporary (aka IA_TA) is commented ny the MA.
+ ISC DHCP supports interval ranges for prefix6. Kea has a different
+ and IMHO more powerful model.
+ Pool6 permissions are not supported.
+
+Failover:
+---------
+ Display a warning on the first use.
+
+Interfaces:
+-----------
+ Referenced interface names are pushed to an interfaces-config but it is
+ very (too!) easy to finish with a Kea config without any interface.
+
+Hostnames:
+----------
+ ISC DHCP does dynamic resolution in parse_ip_addr_or_hostname.
+ Static (at conversion time) resolution to one address is done by
+ the MA for fixed-address. Resolution is considered as painful
+ there are better (and safer) ways to do this. The -r (resolve)
+ command line parameter controls the at-conversion-time resolution.
+ Note only the first address is returned.
+TODO: check the multiple address comment is correctly taken
+ (need a known host resolving in a stable set of addresses)
+
+Options:
+--------
+ Some options are known only in ISC DHCP (almost fixed), a few only by Kea.
+ Formats are supposed to be the same, the only known exception
+ (DHCPv4 domain-search) was fixed by #5087.
+ For option spaces DHCPv4 vendor-encapsulated-options (code 43, in general
+ associated to vendor-class-identifier code 60) uses a dedicated feature
+ which had no equivalent in Kea (fixed).
+ Option definitions are convertible with a few exception:
+ - no support in Kea for an array of records (mainly by the lack
+ of a corresponding syntax). BTW there is no known use too.
+ - no support in Kea for an array at the end of a record (fixed)
+ All unsupported option declarations are set to full binary (X).
+ - X format means ASCII or hexa:
+ * standard options are in general mapped to binary
+ * new options are mapped to string with format x (vs x)
+ * when a string got hexadecimal data a warning in added in comments
+ suggesting to switch to plain binary.
+ - ISC DHCP use quotes for a domain-list but not for a domain-name,
+ this is no very coherent and makes domain-list different than
+ domain-name array.
+Each time an option data has a format which is not convertible than
+a CSV false binary data is produced.
+ We have no example in ISC DHCP, Kea or standard but it is possible
+ than an option defined as a fixed sized record followed by
+ (encapsulated) suboptions bugs (it already bugs toElement).
+ For operations on options ISC DHCP has supersede, send, append,
+ prepend, default (set if not yet present), Kea puts them in code order
+ with a few built-in exceptions.
+ To finish there is the way to enforce Kea to add an option in a response
+ is pretty different and can't be automatically translated (cf Kea #250).
+
+Duplicates:
+-----------
+ Many things in ISC DHCP can be duplicated:
+ - options can be redefined
+ - same host identifier used twice
+ - same fixed address used in tow different hosts
+ etc.
+ Kea is far more strict and IMHO it is a good thing. Now the MA does
+ no particular check and multiple definitions work only for classes
+ (because it is the way the ISC DHCP parse works).
+ If we have Docsis space options, they are standard in Kea so they
+ will conflict.
+
+Dynamic DNS:
+------------
+ Details are very different so the MA maps only basic parameters
+ at the global scope.
+
+Expressions:
+------------
+ ISC DHCP expressions are typed: boolean, numeric, and data aka string.
+ The default for a literal is to be a string so literal numbers are
+ interpreted in hexadecimal (for a strange consequence look at
+ https://kb.isc.org/article/AA-00334/56/Do-the-list-of-parameters-in-the-dhcp-parameter-request-list-need-to-be-in-hex.html ).
+ String literals are converted to string elements, hexadecimal literals
+ are converted to const-data maps.
+TODO reduce more hexa aka const-data
+ As booleans are not data there is no way to fix this:
+ /tmp/bool line 9: Expecting a data expression.
+ option ip-forwarding = foo = foo;
+ ^
+ Cf Kea #247
+ The tautology 'foo = foo' is not a data expression so is rejected by
+ both the MA and dhcpd (BTW the role of the MA is not to fix ISC DHCP
+ shortcomings so it does what it is expected to do here).
+ Note this does not work too:
+ option ip-forwarding = true;
+ because "true" is not a keyword and it is converted into a variable
+ reference... And I expect ISC DHCP makes this true a false at runtime
+ because the variable "true" is not defined by default.
+ Reduced expressions are pretty printed to allow an extra check.
+ Hardware for DHCPv4 is expansed into a concatenation of hw-type and
+ hw-address, this allows to simplify expression where only one is used.
+
+Variables:
+----------
+ ISC DHCP has a notion of variables in a scope where the scope can be
+ a lexical scope in the config or a scope in a function body
+ (ISC DHCP has even an unused "let" statement).
+ There is a variant of bindings for lease files using types and able
+ to recognize booleans and numbers. Unfortunately this is very specific...
+
+TODO:
+ - global host reservations
+ - class like if statement
+ - add more tests for classes in pools and class generation
diff --git a/keama/eval.c b/keama/eval.c
new file mode 100644
index 00000000..8e2dbdb5
--- /dev/null
+++ b/keama/eval.c
@@ -0,0 +1,2252 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "keama.h"
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static struct element *eval_equal_expression(struct element *,
+ struct element *);
+static isc_boolean_t cmp_hexa(struct element *, isc_boolean_t,
+ struct element *,isc_boolean_t);
+static void debug(const char* fmt, ...);
+
+struct element *
+eval_expression(struct element *expr, isc_boolean_t *modifiedp)
+{
+ if ((expr->type == ELEMENT_BOOLEAN) ||
+ (expr->type == ELEMENT_INTEGER) ||
+ (expr->type == ELEMENT_STRING))
+ return expr;
+
+ if (is_boolean_expression(expr))
+ return eval_boolean_expression(expr, modifiedp);
+ if (is_numeric_expression(expr))
+ return eval_numeric_expression(expr, modifiedp);
+ if (is_data_expression(expr))
+ return eval_data_expression(expr, modifiedp);
+ debug("can't type expression");
+ return expr;
+}
+
+/*
+ * boolean_expression :== CHECK STRING |
+ * NOT boolean-expression |
+ * data-expression EQUAL data-expression |
+ * data-expression BANG EQUAL data-expression |
+ * data-expression REGEX_MATCH data-expression |
+ * boolean-expression AND boolean-expression |
+ * boolean-expression OR boolean-expression
+ * EXISTS OPTION-NAME
+ */
+
+struct element *
+eval_boolean_expression(struct element *expr, isc_boolean_t *modifiedp)
+{
+ /* trivial case: already done */
+ if (expr->type == ELEMENT_BOOLEAN)
+ return expr;
+
+ /*
+ * From is_boolean_expression
+ */
+
+ if (expr->type != ELEMENT_MAP)
+ return expr;
+
+
+ /* check */
+ if (mapContains(expr, "check"))
+ /*
+ * syntax := { "check": <collection_name> }
+ * semantic: check_collection
+ * on server try to match classes of the collection
+ */
+ return expr;
+
+
+ /* exists */
+ if (mapContains(expr, "exists"))
+ /*
+ * syntax := { "exists":
+ * { "universe": <option_space_old>,
+ * "name": <option_name> }
+ * }
+ * semantic: check universe/code from incoming packet
+ */
+ return expr;
+
+ /* variable-exists */
+ if (mapContains(expr, "variable-exists"))
+ /*
+ * syntax := { "variable-exists": <variable_name> }
+ * semantics: find_binding(scope, name)
+ */
+ return expr;
+
+ /* equal */
+ if (mapContains(expr, "equal")) {
+ /*
+ * syntax := { "equal":
+ * { "left": <expression>,
+ * "right": <expression> }
+ * }
+ * semantics: evaluate branches and return true
+ * if same type and same value
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *equal;
+ struct comments comments;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "equal");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ equal = eval_equal_expression(left, right);
+ if ((equal == NULL) || (equal->type != ELEMENT_BOOLEAN))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ TAILQ_INIT(&comments);
+ TAILQ_CONCAT(&comments, &expr->comments);
+ TAILQ_CONCAT(&comments, &arg->comments);
+ TAILQ_CONCAT(&comments, &equal->comments);
+ TAILQ_CONCAT(&equal->comments, &comments);
+ return equal;
+ }
+
+ /* not-equal */
+ if (mapContains(expr, "not-equal")) {
+ /*
+ * syntax := { "not-equal":
+ * { "left": <expression>,
+ * "right": <expression> }
+ * }
+ * semantics: evaluate branches and return true
+ * if different type or different value
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *equal;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "not-equal");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ equal = eval_equal_expression(left, right);
+ if ((equal == NULL) || (equal->type != ELEMENT_BOOLEAN))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createBool(ISC_TF(!boolValue(equal)));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &equal->comments);
+ return result;
+ }
+
+ /* regex-match */
+ if (mapContains(expr, "regex-match"))
+ /*
+ * syntax := { "regex-match":
+ * { "left": <data_expression>,
+ * "right": <data_expression> }
+ * }
+ * semantics: evaluate branches, compile right as a
+ * regex and apply it to left
+ */
+ return expr;
+
+ /* iregex-match */
+ if (mapContains(expr, "iregex-match"))
+ /*
+ * syntax := { "regex-match":
+ * { "left": <data_expression>,
+ * "right": <data_expression> }
+ * }
+ * semantics: evaluate branches, compile right as a
+ * case insensistive regex and apply it to left
+ */
+ return expr;
+
+ /* and */
+ if (mapContains(expr, "and")) {
+ /*
+ * syntax := { "and":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return true
+ * if both are true
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "and");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ debug("can't get and right branch");
+ left = eval_boolean_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_boolean_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if (left->type == ELEMENT_BOOLEAN) {
+ *modifiedp = ISC_TRUE;
+ if (!boolValue(left))
+ result = createBool(ISC_FALSE);
+ else {
+ result = copy(right);
+ TAILQ_INIT(&result->comments);
+ }
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+ if (right->type == ELEMENT_BOOLEAN) {
+ *modifiedp = ISC_TRUE;
+ if (!boolValue(right))
+ result = createBool(ISC_FALSE);
+ else {
+ result = copy(left);
+ TAILQ_INIT(&result->comments);
+ }
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+ return expr;
+ }
+
+ /* or */
+ if (mapContains(expr, "or")) {
+ /*
+ * syntax := { "or":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return true
+ * if any is true
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "or");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_boolean_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_boolean_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if (left->type == ELEMENT_BOOLEAN) {
+ *modifiedp = ISC_TRUE;
+ if (boolValue(left))
+ result = createBool(ISC_TRUE);
+ else {
+ result = copy(right);
+ TAILQ_INIT(&result->comments);
+ }
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+ if (right->type == ELEMENT_BOOLEAN) {
+ *modifiedp = ISC_TRUE;
+ if (boolValue(right))
+ result = createBool(ISC_TRUE);
+ else {
+ result = copy(left);
+ TAILQ_INIT(&result->comments);
+ }
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+ return expr;
+ }
+
+ /* not */
+ if (mapContains(expr, "not")) {
+ /*
+ * syntax := { "not": <boolean_expression> }
+ * semantic: evaluate its branch and return its negation
+ */
+ struct element *arg;
+ struct element *result;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "not");
+ if (arg == NULL)
+ return expr;
+ arg = eval_boolean_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "not");
+ mapSet(expr, arg, "not");
+ }
+
+ /* remove double not */
+ if ((arg->type == ELEMENT_MAP) && mapContains(arg, "not")) {
+ arg = mapGet(arg, "not");
+ if (arg == NULL)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ return arg;
+ }
+
+ /* compose with equal */
+ if ((arg->type == ELEMENT_MAP) &&
+ mapContains(arg, "equal")) {
+ arg = mapGet(arg, "equal");
+ if (arg == NULL)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createMap();
+ mapSet(result, arg, "not-equal");
+ return result;
+ }
+
+ /* compose with not-equal */
+ if ((arg->type == ELEMENT_MAP) &&
+ mapContains(arg, "not-equal")) {
+ arg = mapGet(arg, "not-equal");
+ if (arg == NULL)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createMap();
+ mapSet(result, arg, "equal");
+ return result;
+ }
+
+ if (arg->type != ELEMENT_BOOLEAN)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createBool(ISC_TF(!boolValue(arg)));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* known */
+ if (mapContains(expr, "known"))
+ /*
+ * syntax := { "known": null }
+ * semantics: client is known, i.e., has a matching
+ * host declaration (aka reservation in Kea)
+ */
+ return expr;
+
+ /* static */
+ if (mapContains(expr, "static"))
+ /*
+ * syntax := { "static": null }
+ * semantics: lease is static (doesn't exist in Kea)
+ */
+ return expr;
+
+ return expr;
+}
+
+/*
+ * data_expression :== SUBSTRING LPAREN data-expression COMMA
+ * numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * CONCAT LPAREN data-expression COMMA
+ * data-expression RPAREN
+ * SUFFIX LPAREN data_expression COMMA
+ * numeric-expression RPAREN |
+ * LCASE LPAREN data_expression RPAREN |
+ * UCASE LPAREN data_expression RPAREN |
+ * OPTION option_name |
+ * HARDWARE |
+ * PACKET LPAREN numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * V6RELAY LPAREN numeric-expression COMMA
+ * data-expression RPAREN |
+ * STRING |
+ * colon_separated_hex_list
+ */
+
+struct element *
+eval_data_expression(struct element *expr, isc_boolean_t *modifiedp)
+{
+ /* trivial case: already done */
+ if (expr->type == ELEMENT_STRING)
+ return expr;
+
+ /*
+ * From is_data_expression
+ */
+
+ if (expr->type != ELEMENT_MAP)
+ return expr;
+
+ /* substring */
+ if (mapContains(expr, "substring")) {
+ /*
+ * syntax := { "substring":
+ * { "expression": <data_expression>,
+ * "offset": <numeric_expression>,
+ * "length": <numeric_expression> }
+ * }
+ * semantic: evaluate arguments, if the string is
+ * shorter than offset return "" else return substring
+ */
+ struct element *arg;
+ struct element *string;
+ struct element *offset;
+ struct element *length;
+ struct element *result;
+ struct string *s;
+ struct string *r;
+ int64_t off;
+ int64_t len;
+ isc_boolean_t smodified = ISC_FALSE;
+ isc_boolean_t omodified = ISC_FALSE;
+ isc_boolean_t lmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "substring");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ string = mapGet(arg, "expression");
+ if (string == NULL)
+ return expr;
+ offset = mapGet(arg, "offset");
+ if (offset == NULL)
+ return expr;
+ length = mapGet(arg, "length");
+ if (length == NULL)
+ return expr;
+ string = eval_data_expression(string, &smodified);
+ if (smodified) {
+ mapRemove(arg, "expression");
+ mapSet(arg, string, "expression");
+ }
+ offset = eval_numeric_expression(offset, &omodified);
+ if (omodified) {
+ mapRemove(arg, "offset");
+ mapSet(arg, offset, "offset");
+ }
+ length = eval_numeric_expression(length, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "length");
+ mapSet(arg, length, "length");
+ }
+
+ if ((offset->type != ELEMENT_INTEGER) ||
+ (length->type != ELEMENT_INTEGER))
+ return expr;
+ off = intValue(offset);
+ len = intValue(length);
+ if ((off < 0) || (len < 0))
+ return expr;
+ /* degenerated case */
+ if (len == 0) {
+ *modifiedp = ISC_TRUE;
+ r = allocString();
+ result = createString(r);
+ return result;
+ }
+
+ /* return (part of) hw-address? */
+ if ((local_family == AF_INET) &&
+ (string->type == ELEMENT_MAP) &&
+ mapContains(string, "concat") &&
+ (off >= 1)) {
+ struct element *concat;
+ struct element *left;
+ struct element *right;
+
+ concat = mapGet(string, "concat");
+ if (concat->type != ELEMENT_MAP)
+ return expr;
+ left = mapGet(concat, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(concat, "right");
+ if (right == NULL)
+ return expr;
+ /* from substring(hardware, ...) */
+ if ((left->type == ELEMENT_MAP) &&
+ mapContains(left, "hw-type")) {
+ *modifiedp = ISC_TRUE;
+ mapRemove(arg, "expression");
+ mapSet(arg, right, "expression");
+ mapRemove(arg, "offset");
+ mapSet(arg, createInt(off - 1), "offset");
+ return expr;
+ }
+ return expr;
+ }
+
+ /* return hw-type? */
+ if ((local_family == AF_INET) &&
+ (string->type == ELEMENT_MAP) &&
+ mapContains(string, "concat") &&
+ (off == 0) && (len == 1)) {
+ struct element *concat;
+ struct element *left;
+ struct element *right;
+
+ concat = mapGet(string, "concat");
+ if (concat->type != ELEMENT_MAP)
+ return expr;
+ left = mapGet(concat, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(concat, "right");
+ if (right == NULL)
+ return expr;
+ /* from substring(hardware, ...) */
+ if ((left->type == ELEMENT_MAP) &&
+ mapContains(left, "hw-type")) {
+ *modifiedp = ISC_TRUE;
+ return left;
+ }
+ return expr;
+ }
+
+ if (string->type != ELEMENT_STRING)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ s = stringValue(string);
+ if (s->length <= off)
+ r = allocString();
+ else {
+ r = makeString(s->length - off, s->content + off);
+ if (r->length > len)
+ r->length = len;
+ }
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &string->comments);
+ TAILQ_CONCAT(&result->comments, &offset->comments);
+ TAILQ_CONCAT(&result->comments, &length->comments);
+ return result;
+ }
+
+ /* suffix */
+ if (mapContains(expr, "suffix")) {
+ /*
+ * syntax := { "suffix":
+ * { "expression": <data_expression>,
+ * "length": <numeric_expression> }
+ * }
+ * semantic: evaluate arguments, if the string is
+ * shorter than length return it else return suffix
+ */
+ struct element *arg;
+ struct element *string;
+ struct element *length;
+ struct element *result;
+ struct string *r;
+ int64_t len;
+ isc_boolean_t smodified = ISC_FALSE;
+ isc_boolean_t lmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "suffix");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ string = mapGet(arg, "expression");
+ if (string == NULL)
+ return expr;
+ length = mapGet(arg, "length");
+ if (length == NULL)
+ return expr;
+ string = eval_data_expression(string, &smodified);
+ if (smodified) {
+ mapRemove(arg, "expression");
+ mapSet(arg, string, "expression");
+ }
+ length = eval_numeric_expression(length, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "length");
+ mapSet(arg, length, "length");
+ }
+
+ if ((string->type != ELEMENT_STRING) ||
+ (length->type != ELEMENT_INTEGER))
+ return expr;
+ len = intValue(length);
+ if (len < 0)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ r = stringValue(string);
+ if (r->length > len)
+ r = makeString(r->length - len, r->content + len);
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &string->comments);
+ TAILQ_CONCAT(&result->comments, &length->comments);
+ return result;
+ }
+
+ /* lowercase */
+ if (mapContains(expr, "lowercase")) {
+ /*
+ * syntax := { "lowercase": <data_expression> }
+ * semantic: evaluate its argument and apply tolower to
+ * its content
+ */
+ struct element *arg;
+ struct element *result;
+ struct string *r;
+ size_t i;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "lowercase");
+ if (arg == NULL)
+ return expr;
+ arg = eval_data_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "lowercase");
+ mapSet(expr, arg, "lowercase");
+ }
+
+ if (arg->type != ELEMENT_STRING)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ r = allocString();
+ concatString(r, stringValue(arg));
+ for (i = 0; i < r->length; i++)
+ r->content[i] = tolower(r->content[i]);
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* uppercase */
+ if (mapContains(expr, "uppercase")) {
+ /*
+ * syntax := { "uppercase": <data_expression> }
+ * semantic: evaluate its argument and apply toupper to
+ * its content
+ */
+ struct element *arg;
+ struct element *result;
+ struct string *r;
+ size_t i;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "uppercase");
+ if (arg == NULL)
+ return expr;
+ arg = eval_data_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "lowercase");
+ mapSet(expr, arg, "lowercase");
+ }
+
+ if (arg->type != ELEMENT_STRING)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ r = allocString();
+ concatString(r, stringValue(arg));
+ for (i = 0; i < r->length; i++)
+ r->content[i] = toupper(r->content[i]);
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* option */
+ if (mapContains(expr, "option"))
+ /*
+ * syntax := { "option":
+ * { "universe": <option_space_old>,
+ * "name": <option_name> }
+ * }
+ * semantic: get universe/code option from incoming packet
+ */
+ return expr;
+
+ /* hardware */
+ if (mapContains(expr, "hardware")) {
+ /*
+ * syntax := { "hardware": null }
+ * semantic: get mac type and address from incoming packet
+ */
+ struct element *left;
+ struct element *right;
+ struct element *concat;
+ struct element *result;
+
+ if (local_family != AF_INET)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ left = createMap();
+ mapSet(left, createNull(), "hw-type");
+ concat = createMap();
+ mapSet(concat, left, "left");
+ right = createMap();
+ mapSet(right, createNull(), "hw-address");
+ mapSet(concat, right, "right");
+ result = createMap();
+ mapSet(result, concat, "concat");
+ return result;
+ }
+
+ /* hw-type */
+ if (mapContains(expr, "hw-type"))
+ /*
+ * syntax := { "hw-type": null }
+ * semantic: get mac type and address from incoming packet
+ */
+ return expr;
+
+ /* hw-address */
+ if (mapContains(expr, "hw-address"))
+ /*
+ * syntax := { "hw-address": null }
+ * semantic: get mac type and address from incoming packet
+ */
+ return expr;
+
+ /* const-data */
+ if (mapContains(expr, "const-data"))
+ /*
+ * syntax := { "const-data": <string> }
+ * semantic: embedded string value
+ */
+ return expr;
+
+ /* packet */
+ if (mapContains(expr, "packet"))
+ /*
+ * syntax := { "packet":
+ * { "offset": <numeric_expression>,
+ * "length": <numeric_expression> }
+ * }
+ * semantic: return the selected substring of the incoming
+ * packet content
+ */
+ return expr;
+
+ /* concat */
+ if (mapContains(expr, "concat")) {
+ /*
+ * syntax := { "concat":
+ * { "left": <data_expression>,
+ * "right": <data_expression> }
+ * }
+ * semantic: evaluate arguments and return the concatenation
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ struct string *r;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "concat");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_data_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_data_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ /* degenerated cases */
+ if ((left->type == ELEMENT_STRING) &&
+ (stringValue(left)->length == 0)) {
+ *modifiedp = ISC_TRUE;
+ return right;
+ }
+ if ((right->type == ELEMENT_STRING) &&
+ (stringValue(right)->length == 0)) {
+ *modifiedp = ISC_TRUE;
+ return left;
+ }
+
+ if ((left->type != ELEMENT_STRING) ||
+ (right->type != ELEMENT_STRING))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ r = allocString();
+ concatString(r, stringValue(left));
+ concatString(r, stringValue(right));
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* encapsulate */
+ if (mapContains(expr, "encapsulate"))
+ /*
+ * syntax := { "encapsulate": <encapsulated_space> }
+ * semantic: encapsulate options of the given space
+ */
+ return expr;
+
+ /* encode-int8 */
+ if (mapContains(expr, "encode-int8")) {
+ /*
+ * syntax := { "encode-int8": <numeric_expression> }
+ * semantic: return a string buffer with the evaluated
+ * number as content
+ */
+ struct element *arg;
+ struct element *result;
+ struct string *r;
+ uint8_t val;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "encode-int8");
+ if (arg == NULL)
+ return expr;
+ arg = eval_numeric_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "encode-int8");
+ mapSet(expr, arg, "encode-int8");
+ }
+
+ if (arg->type != ELEMENT_INTEGER)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ val = (uint8_t)intValue(arg);
+ r = makeString(sizeof(val), (char *)&val);
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* encode-int16 */
+ if (mapContains(expr, "encode-int16")) {
+ /*
+ * syntax := { "encode-int16": <numeric_expression> }
+ * semantic: return a string buffer with the evaluated
+ * number as content
+ */
+ struct element *arg;
+ struct element *result;
+ struct string *r;
+ uint16_t val;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "encode-int16");
+ if (arg == NULL)
+ return expr;
+ arg = eval_numeric_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "encode-int16");
+ mapSet(expr, arg, "encode-int16");
+ }
+
+ if (arg->type != ELEMENT_INTEGER)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ val = (uint16_t)intValue(arg);
+ val = htons(val);
+ r = makeString(sizeof(val), (char *)&val);
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* encode-int32 */
+ if (mapContains(expr, "encode-int32")) {
+ /*
+ * syntax := { "encode-int32": <numeric_expression> }
+ * semantic: return a string buffer with the evaluated
+ * number as content
+ */
+ struct element *arg;
+ struct element *result;
+ struct string *r;
+ uint32_t val;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "encode-int32");
+ if (arg == NULL)
+ return expr;
+ arg = eval_numeric_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "encode-int32");
+ mapSet(expr, arg, "encode-int32");
+ }
+
+ if (arg->type != ELEMENT_INTEGER)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ val = (uint32_t)intValue(arg);
+ val = htonl(val);
+ r = makeString(sizeof(val), (char *)&val);
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* gethostbyname */
+ if (mapContains(expr, "gethostbyname")) {
+ /*
+ * syntax := { "gethostbyname": <string> }
+ * semantic: call gethostbyname and return
+ * a binary buffer with addresses
+ */
+ struct element *arg;
+ struct element *result;
+ struct string *r;
+ char *hostname;
+ struct hostent *h;
+ size_t i;
+
+ if (local_family != AF_INET)
+ return expr;
+ arg = mapGet(expr, "gethostbyname");
+ if ((arg == NULL) || (arg->type != ELEMENT_STRING))
+ return expr;
+ hostname = stringValue(arg)->content;
+ h = gethostbyname(hostname);
+ r = allocString();
+ if (h == NULL) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ debug("gethostbyname: %s: host unknown",
+ hostname);
+ break;
+ case TRY_AGAIN:
+ debug("gethostbyname: %s: temporary name "
+ "server failure", hostname);
+ break;
+ case NO_RECOVERY:
+ debug("gethostbyname: %s: name server failed",
+ hostname);
+ break;
+ case NO_DATA:
+ debug("gethostbyname: %s: no A record "
+ "associated with address", hostname);
+ break;
+ }
+ } else
+ for (i = 0; h->h_addr_list[i] != NULL; i++) {
+ struct string *addr;
+
+ addr = makeString(4, h->h_addr_list[i]);
+ concatString(r, addr);
+ }
+ *modifiedp = ISC_TRUE;
+ r = makeStringExt(r->length, r->content, 'X');
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* binary-to-ascii */
+ if (mapContains(expr, "binary-to-ascii")) {
+ /*
+ * syntax := { "binary-to-ascii":
+ * { "base": <numeric_expression 2..16>,
+ * "width": <numeric_expression 8, 16 or 32>,
+ * "separator": <data_expression>,
+ * "buffer": <data_expression> }
+ * }
+ * semantic: split the input buffer into int8/16/32 numbers,
+ * output them separated by the given string
+ */
+ struct element *arg;
+ struct element *base;
+ struct element *width;
+ struct element *separator;
+ struct element *buffer;
+ struct element *result;
+ struct string *sep;
+ struct string *buf;
+ struct string *r;
+ int64_t b;
+ int64_t w;
+ isc_boolean_t bmodified = ISC_FALSE;
+ isc_boolean_t wmodified = ISC_FALSE;
+ isc_boolean_t smodified = ISC_FALSE;
+ isc_boolean_t dmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "binary-to-ascii");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ base = mapGet(arg, "base");
+ if (base == NULL)
+ return expr;
+ width = mapGet(arg, "width");
+ if (width == NULL)
+ return expr;
+ separator = mapGet(arg, "separator");
+ if (separator == NULL)
+ return expr;
+ buffer = mapGet(arg, "buffer");
+ if (buffer == NULL)
+ return expr;
+ base = eval_numeric_expression(base, &bmodified);
+ if (bmodified) {
+ mapRemove(arg, "base");
+ mapSet(arg, base, "base");
+ }
+ width = eval_numeric_expression(width, &wmodified);
+ if (wmodified) {
+ mapRemove(arg, "width");
+ mapSet(arg, width, "width");
+ }
+ separator = eval_data_expression(separator, &smodified);
+ if (smodified) {
+ mapRemove(arg, "separator");
+ mapSet(arg, separator, "separator");
+ }
+ buffer = eval_data_expression(buffer, &dmodified);
+ if (dmodified) {
+ mapRemove(arg, "buffer");
+ mapSet(arg, buffer, "buffer");
+ }
+
+ if ((base->type != ELEMENT_INTEGER) ||
+ (width->type != ELEMENT_INTEGER) ||
+ (separator->type != ELEMENT_STRING) ||
+ (buffer->type != ELEMENT_STRING))
+ return expr;
+ b = intValue(base);
+ if ((b < 2) || (b > 16))
+ return expr;
+ if ((b != 8) && (b != 10) && (b != 16))
+ return expr;
+ w = intValue(width);
+ if ((w != 8) && (w != 16) && (w != 32))
+ return expr;
+ sep = stringValue(separator);
+ buf = stringValue(buffer);
+ r = allocString();
+ if (w == 8) {
+ size_t i;
+ char *fmt;
+
+ switch (b) {
+ case 8:
+ fmt = "%o";
+ break;
+ case 10:
+ fmt = "%d";
+ break;
+ case 16:
+ default:
+ fmt = "%x";
+ break;
+ }
+
+ for (i = 0; i < buf->length; i++) {
+ uint8_t val;
+ char num[4];
+
+ if (i != 0)
+ concatString(r, sep);
+ val = (uint8_t)buf->content[i];
+ snprintf(num, sizeof(num), fmt, (int)val);
+ appendString(r, num);
+ }
+ } else if (w == 16) {
+ size_t i;
+ char *fmt;
+
+ if ((buf->length % 2) != 0)
+ return expr;
+
+ switch (b) {
+ case 8:
+ fmt = "%o";
+ break;
+ case 10:
+ fmt = "%d";
+ break;
+ case 16:
+ default:
+ fmt = "%x";
+ break;
+ }
+
+ for (i = 0; i < buf->length; i += 2) {
+ uint16_t val;
+ char num[8];
+
+ if (i != 0)
+ concatString(r, sep);
+ memcpy(&val, buf->content + i, 2);
+ val = ntohs(val);
+ snprintf(num, sizeof(num), fmt, (int)val);
+ appendString(r, num);
+ }
+ } else if (w == 32) {
+ size_t i;
+ char *fmt;
+
+ if ((buf->length % 4) != 0)
+ return expr;
+
+ switch (b) {
+ case 8:
+ fmt = "%llo";
+ break;
+ case 10:
+ fmt = "%lld";
+ break;
+ case 16:
+ default:
+ fmt = "%llx";
+ break;
+ }
+
+ for (i = 0; i < buf->length; i += 4) {
+ uint32_t val;
+ char num[40];
+
+ if (i != 0)
+ concatString(r, sep);
+ memcpy(&val, buf->content + i, 4);
+ val = ntohl(val);
+ snprintf(num, sizeof(num), fmt,
+ (long long)val);
+ appendString(r, num);
+ }
+ }
+ *modifiedp = ISC_TRUE;
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &base->comments);
+ TAILQ_CONCAT(&result->comments, &width->comments);
+ TAILQ_CONCAT(&result->comments, &separator->comments);
+ TAILQ_CONCAT(&result->comments, &buffer->comments);
+ return result;
+ }
+
+ /* filename */
+ if (mapContains(expr, "filename"))
+ /*
+ * syntax := { "filename": null }
+ * semantic: get filename field from incoming DHCPv4 packet
+ */
+ return expr;
+
+ /* server-name */
+ if (mapContains(expr, "server-name"))
+ /*
+ * syntax := { "server-name": null }
+ * semantic: get server-name field from incoming DHCPv4 packet
+ */
+ return expr;
+
+ /* reverse */
+ if (mapContains(expr, "reverse")) {
+ /*
+ * syntax := { "reverse":
+ * { "width": <numeric_expression>,
+ * "buffer": <data_expression> }
+ * }
+ * semantic: reverse the input buffer by width chunks of bytes
+ */
+ struct element *arg;
+ struct element *width;
+ struct element *buffer;
+ struct element *result;
+ struct string *buf;
+ struct string *r;
+ int64_t w;
+ size_t i;
+ isc_boolean_t wmodified = ISC_FALSE;
+ isc_boolean_t bmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "reverse");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ width = mapGet(arg, "width");
+ if (width == NULL)
+ return expr;
+ buffer = mapGet(arg, "buffer");
+ if (buffer == NULL)
+ return expr;
+ width = eval_numeric_expression(width, &wmodified);
+ if (wmodified) {
+ mapRemove(arg, "width");
+ mapSet(arg, width, "width");
+ }
+ buffer = eval_data_expression(buffer, &bmodified);
+ if (bmodified) {
+ mapRemove(arg, "buffer");
+ mapSet(arg, buffer, "buffer");
+ }
+
+ if ((width->type != ELEMENT_INTEGER) ||
+ (buffer->type != ELEMENT_STRING))
+ return expr;
+ w = intValue(width);
+ if (w <= 0)
+ return expr;
+ buf = stringValue(buffer);
+ if ((buf->length % w) != 0)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ r = allocString();
+ concatString(r, buf);
+ for (i = 0; i < buf->length; i += w) {
+ memcpy(r->content + i,
+ buf->content + (buf->length - i - w),
+ w);
+ }
+ result = createString(r);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &width->comments);
+ TAILQ_CONCAT(&result->comments, &buffer->comments);
+ return result;
+ }
+
+ /* pick-first-value */
+ if (mapContains(expr, "pick-first-value")) {
+ /*
+ * syntax := { "pick-first-value":
+ * [ <data_expression>, ... ]
+ * }
+ * semantic: evaluates expressions and return the first
+ * not null, return null if all are null
+ */
+ struct element *arg;
+ struct element *result;
+ size_t i;
+ isc_boolean_t modified;
+ isc_boolean_t can_decide = ISC_TRUE;
+
+ arg = mapGet(expr, "pick-first-value");
+ if ((arg == NULL) || (arg->type != ELEMENT_LIST))
+ return expr;
+
+ for (i = 0; i < listSize(arg); i++) {
+ struct element *item;
+
+ item = listGet(arg, i);
+ if (item == NULL)
+ return expr;
+ modified = ISC_FALSE;
+ item = eval_data_expression(item, &modified);
+ if (modified)
+ listRemove(arg, i);
+ if (!can_decide)
+ goto restore;
+ if (item->type != ELEMENT_STRING) {
+ can_decide = ISC_FALSE;
+ goto restore;
+ }
+ if (stringValue(item)->length != 0) {
+ *modifiedp = ISC_TRUE;
+ TAILQ_CONCAT(&item->comments, &expr->comments);
+ TAILQ_CONCAT(&item->comments, &arg->comments);
+ return item;
+ }
+ restore:
+ listSet(arg, item, i);
+ }
+ if (!can_decide)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createString(allocString());
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* host-decl-name */
+ if (mapContains(expr, "host-decl-name"))
+ /*
+ * syntax := { "host-decl-name": null }
+ * semantic: return the name of the matching host
+ * declaration (aka revervation in kea) or null
+ */
+ return expr;
+
+ /* leased-address */
+ if (mapContains(expr, "leased-address"))
+ /*
+ * syntax := { "leased-address": null }
+ * semantic: return the address of the assigned lease or
+ * log a message
+ */
+ return expr;
+
+ /* config-option */
+ if (mapContains(expr, "config-option"))
+ /*
+ * syntax := { "config-option":
+ * { "universe": <option_space_old>,
+ * "name": <option_name> }
+ * }
+ * semantic: get universe/code option to send
+ */
+ return expr;
+
+ /* null */
+ if (mapContains(expr, "null")) {
+ /*
+ * syntax := { "null": null }
+ * semantic: return null
+ */
+ struct element *result;
+
+ *modifiedp = ISC_TRUE;
+ result = createString(allocString());
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ return result;
+ }
+
+ /* gethostname */
+ if (mapContains(expr, "gethostname")) {
+ /*
+ * syntax := { "gethostname": null }
+ * semantic: return gethostname
+ */
+ struct element *result;
+ char buf[300 /* >= 255 + 1 */];
+
+ if (gethostname(buf, sizeof(buf)) != 0) {
+ debug("gethostname fails: %s", strerror(errno));
+ return expr;
+ }
+ *modifiedp = ISC_TRUE;
+ result = createString(makeString(-1, buf));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ return result;
+ }
+
+ /* v6relay */
+ if (mapContains(expr, "v6relay")) {
+ /*
+ * syntax := { "v6relay":
+ * { "relay": <numeric_expression>,
+ * "relay-option" <data_expression> }
+ * }
+ * semantic: relay is a counter from client, 0 is no-op,
+ * 1 is the relay closest to the client, etc, option
+ * is a dhcp6 option ans is return when found
+ */
+ struct element *arg;
+ struct element *relay;
+ isc_boolean_t modified = ISC_FALSE;
+
+ if (local_family != AF_INET6)
+ return expr;
+ arg = mapGet(expr, "v6relay");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ relay = mapGet(arg, "relay");
+ if (relay == NULL)
+ return expr;
+ relay = eval_numeric_expression(relay, &modified);
+ if (modified) {
+ mapRemove(arg, "relay");
+ mapSet(arg, relay, "relay");
+ }
+ return expr;
+ }
+
+ return expr;
+}
+
+/*
+ * numeric-expression :== EXTRACT_INT LPAREN data-expression
+ * COMMA number RPAREN |
+ * NUMBER
+ */
+
+struct element *
+eval_numeric_expression(struct element *expr, isc_boolean_t *modifiedp)
+{
+ /* trivial case: already done */
+ if (expr->type == ELEMENT_INTEGER)
+ return expr;
+
+ /*
+ * From is_numeric_expression
+ */
+
+ if (expr->type != ELEMENT_MAP)
+ return expr;
+
+ /* extract-int8 */
+ if (mapContains(expr, "extract-int8")) {
+ /*
+ * syntax := { "extract-int8": <data_expression> }
+ * semantic: extract from the evalkuated string buffer
+ * a number
+ */
+ struct element *arg;
+ struct element *result;
+ uint8_t val = 0;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "extract-int8");
+ if (arg == NULL)
+ return expr;
+ arg = eval_data_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "extract-int8");
+ mapSet(expr, arg, "extract-int8");
+ }
+
+ if (arg->type != ELEMENT_STRING)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ if (stringValue(arg)->length > 0)
+ val = (uint8_t) stringValue(arg)->content[0];
+ result = createInt(val);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* extract-int16 */
+ if (mapContains(expr, "extract-int16")) {
+ /*
+ * syntax := { "extract-int16": <data_expression> }
+ * semantic: extract from the evalkuated string buffer
+ * a number
+ */
+ struct element *arg;
+ struct element *result;
+ uint16_t val;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "extract-int16");
+ if (arg == NULL)
+ return expr;
+ arg = eval_data_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "extract-int16");
+ mapSet(expr, arg, "extract-int16");
+ }
+
+ if (arg->type != ELEMENT_STRING)
+ return expr;
+ if (stringValue(arg)->length < 2)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ memcpy(&val, stringValue(arg)->content, 2);
+ val = ntohs(val);
+ result = createInt(val);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* extract-int32 */
+ if (mapContains(expr, "extract-int32")) {
+ /*
+ * syntax := { "extract-int32": <data_expression> }
+ * semantic: extract from the evalkuated string buffer
+ * a number
+ */
+ struct element *arg;
+ struct element *result;
+ uint32_t val;
+ isc_boolean_t modified = ISC_FALSE;
+
+ arg = mapGet(expr, "extract-int32");
+ if (arg == NULL)
+ return expr;
+ arg = eval_data_expression(arg, &modified);
+ if (modified) {
+ mapRemove(expr, "extract-int32");
+ mapSet(expr, arg, "extract-int32");
+ }
+
+ if (arg->type != ELEMENT_STRING)
+ return expr;
+ if (stringValue(arg)->length < 4)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ memcpy(&val, stringValue(arg)->content, 4);
+ val = ntohl(val);
+ result = createInt(val);
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* const-int */
+ if (mapContains(expr, "const-int")) {
+ /*
+ * syntax := { "const-int": <integer> }
+ * semantic: embedded integer value
+ */
+ struct element *arg;
+ struct element *result;
+
+ arg = mapGet(expr, "const-int");
+ if ((arg == NULL) || (arg->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(arg));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ return result;
+ }
+
+ /* lease-time */
+ if (mapContains(expr, "lease-time"))
+ /*
+ * syntax := { "lease-time": null }
+ * semantic: return duration of the current lease, i.e
+ * the difference between expire time and now
+ */
+ return expr;
+
+ /* add */
+ if (mapContains(expr, "add")) {
+ /*
+ * syntax := { "add":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "add");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) + intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* subtract */
+ if (mapContains(expr, "subtract")) {
+ /*
+ * syntax := { "subtract":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "subtract");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) - intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* multiply */
+ if (mapContains(expr, "multiply")) {
+ /*
+ * syntax := { "multiply":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "multiply");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) * intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* divide */
+ if (mapContains(expr, "divide")) {
+ /*
+ * syntax := { "divide":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "divide");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ if (intValue(right) == 0)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) / intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* remainder */
+ if (mapContains(expr, "remainder")) {
+ /*
+ * syntax := { "remainder":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "remainder");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ if (intValue(right) == 0)
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) % intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* binary-and */
+ if (mapContains(expr, "binary-and")) {
+ /*
+ * syntax := { "binary-and":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "binary-and");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) & intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* binary-or */
+ if (mapContains(expr, "binary-or")) {
+ /*
+ * syntax := { "binary-or":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "binary-or");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) | intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* binary-xor */
+ if (mapContains(expr, "binary-xor")) {
+ /*
+ * syntax := { "binary-xor":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return left plus right
+ * branches
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *result;
+ isc_boolean_t lmodified = ISC_FALSE;
+ isc_boolean_t rmodified = ISC_FALSE;
+
+ arg = mapGet(expr, "binary-xor");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP))
+ return expr;
+ left = mapGet(arg, "left");
+ if (left == NULL)
+ return expr;
+ right = mapGet(arg, "right");
+ if (right == NULL)
+ return expr;
+ left = eval_numeric_expression(left, &lmodified);
+ if (lmodified) {
+ mapRemove(arg, "left");
+ mapSet(arg, left, "left");
+ }
+ right = eval_numeric_expression(right, &rmodified);
+ if (rmodified) {
+ mapRemove(arg, "right");
+ mapSet(arg, right, "right");
+ }
+
+ if ((left->type != ELEMENT_INTEGER) ||
+ (right->type != ELEMENT_INTEGER))
+ return expr;
+ *modifiedp = ISC_TRUE;
+ result = createInt(intValue(left) ^ intValue(right));
+ TAILQ_CONCAT(&result->comments, &expr->comments);
+ TAILQ_CONCAT(&result->comments, &arg->comments);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+ }
+
+ /* client-state */
+ if (mapContains(expr, "client-state"))
+ /*
+ * syntax := { "client-state": null }
+ * semantic: return client state
+ */
+ return expr;
+
+ return expr;
+}
+
+/*
+ * Check if the two evaluated expressions are equal, not equal,
+ * or we can't decide.
+ */
+
+static struct element *
+eval_equal_expression(struct element *left, struct element *right)
+{
+ struct element *result = NULL;
+ isc_boolean_t val;
+
+ /* in theory boolean is not possible */
+ if (left->type == ELEMENT_BOOLEAN) {
+ if (right->type == ELEMENT_BOOLEAN)
+ val = ISC_TF(boolValue(left) == boolValue(right));
+ else if (right->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* right is boolean */
+ if (right->type == ELEMENT_BOOLEAN) {
+ if (left->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* left is numeric literal */
+ if (left->type == ELEMENT_INTEGER) {
+ if (right->type == ELEMENT_INTEGER)
+ val = ISC_TF(intValue(left) == intValue(right));
+ else if ((right->type == ELEMENT_MAP) &&
+ mapContains(right, "const-int")) {
+ struct element *ci;
+
+ ci = mapGet(right, "const-int");
+ if ((ci == NULL) || (ci->type != ELEMENT_INTEGER)) {
+ debug("bad const-int");
+ return NULL;
+ }
+ val = ISC_TF(intValue(left) == intValue(ci));
+ } else if (right->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* left is const-int */
+ if ((left->type == ELEMENT_MAP) && mapContains(left, "const-int")) {
+ if (right->type == ELEMENT_INTEGER) {
+ struct element *ci;
+
+ ci = mapGet(left, "const-int");
+ if ((ci == NULL) || (ci->type != ELEMENT_INTEGER)) {
+ debug("bad const-int");
+ return NULL;
+ }
+ val = ISC_TF(intValue(ci) == intValue(right));
+ } else if ((right->type == ELEMENT_MAP) &&
+ mapContains(right, "const-int")) {
+ struct element *lci;
+ struct element *rci;
+
+ lci = mapGet(left, "const-int");
+ rci = mapGet(right, "const-int");
+ if ((lci == NULL) || (lci->type != ELEMENT_INTEGER) ||
+ (rci == NULL) || (rci->type != ELEMENT_INTEGER)) {
+ debug("bad const-int");
+ return NULL;
+ }
+ val = ISC_TF(intValue(lci) == intValue(rci));
+ } else if (right->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* right is numeric literal */
+ if (right->type == ELEMENT_INTEGER) {
+ if (left->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* right is const-int */
+ if ((right->type == ELEMENT_MAP) && mapContains(right, "const-int")) {
+ if (left->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* left is data literal */
+ if (left->type == ELEMENT_STRING) {
+ if (right->type == ELEMENT_STRING)
+ val = cmp_hexa(left, ISC_FALSE, right, ISC_FALSE);
+ else if ((right->type == ELEMENT_MAP) &&
+ mapContains(right, "const-data")) {
+ struct element *cd;
+
+ cd = mapGet(right, "const-data");
+ if ((cd == NULL) || (cd->type != ELEMENT_STRING)) {
+ debug("bad const-data");
+ return NULL;
+ }
+ val = cmp_hexa(left, ISC_FALSE, cd, ISC_TRUE);
+ } else if (right->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* left is const-data */
+ if ((left->type == ELEMENT_MAP) && mapContains(left, "const-data")) {
+ if (right->type == ELEMENT_STRING) {
+ struct element *cd;
+
+ cd = mapGet(left, "const-data");
+ if ((cd == NULL) || (cd->type != ELEMENT_STRING)) {
+ debug("bad const-data");
+ return NULL;
+ }
+ val = cmp_hexa(cd, ISC_TRUE, right, ISC_FALSE);
+ } else if ((right->type == ELEMENT_MAP) &&
+ mapContains(right, "const-data")) {
+ struct element *lcd;
+ struct element *rcd;
+
+ lcd = mapGet(left, "const-data");
+ rcd = mapGet(right, "const-data");
+ if ((lcd == NULL) || (lcd->type != ELEMENT_STRING) ||
+ (rcd == NULL) || (rcd->type != ELEMENT_STRING)) {
+ debug("bad const-data");
+ return NULL;
+ }
+ val = cmp_hexa(lcd, ISC_TRUE, rcd, ISC_TRUE);
+ } else if (right->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* right is data literal */
+ if (right->type == ELEMENT_STRING) {
+ if (left->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* right is const-data */
+ if ((right->type == ELEMENT_MAP) && mapContains(right, "const-data")) {
+ if (left->type == ELEMENT_MAP)
+ return NULL;
+ else
+ val = ISC_FALSE;
+ } else
+ /* impossible cases */
+ if ((left->type != ELEMENT_MAP) || (right->type != ELEMENT_MAP)) {
+ debug("equal between unexpected %s and %s",
+ type2name(left->type), type2name(right->type));
+ val = ISC_FALSE;
+ } else
+ /* can't decide */
+ return NULL;
+
+ result = createBool(val);
+ TAILQ_CONCAT(&result->comments, &left->comments);
+ TAILQ_CONCAT(&result->comments, &right->comments);
+ return result;
+}
+
+static isc_boolean_t
+cmp_hexa(struct element *left, isc_boolean_t left_is_hexa,
+ struct element *right, isc_boolean_t right_is_hexa)
+{
+ struct string *sleft;
+ struct string *sright;
+
+ /* both are not hexa */
+ if (!left_is_hexa && !right_is_hexa) {
+ sleft = stringValue(left);
+ sright = stringValue(right);
+ /* eqString() compares lengths them use memcmp() */
+ return eqString(sleft, sright);
+ }
+
+ /* both are hexa */
+ if (left_is_hexa && right_is_hexa) {
+ sleft = stringValue(left);
+ sright = stringValue(right);
+ if (sleft->length != sright->length)
+ return ISC_FALSE;
+ if (sleft->length == 0) {
+ debug("empty const-data");
+ return ISC_TRUE;
+ }
+ return ISC_TF(strcasecmp(sleft->content,
+ sright->content) == 0);
+ }
+
+ /* put the hexa at left */
+ if (left_is_hexa) {
+ sleft = hexaValue(left);
+ sright = stringValue(right);
+ } else {
+ sleft = hexaValue(right);
+ sright = stringValue(left);
+ }
+
+ /* hexa is double length */
+ if (sleft->length != 2 * sright->length)
+ return ISC_FALSE;
+
+ /* build the hexa representation */
+ makeStringExt(sright->length, sright->content, 'X');
+
+ return ISC_TF(strcasecmp(sleft->content, sright->content) == 0);
+}
+
+static void
+debug(const char* fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ vfprintf(stderr, fmt, list);
+ fprintf(stderr, "\n");
+ va_end(list);
+}
diff --git a/keama/json.c b/keama/json.c
new file mode 100644
index 00000000..0b2667a2
--- /dev/null
+++ b/keama/json.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+/* From Kea src/lib/cc/data.cc fromJSON() */
+
+#include "keama.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct element *
+json_parse(struct parse *cfile)
+{
+ struct element *elem;
+ const char *val;
+ enum dhcp_token token;
+
+ elem = create();
+ stackPush(cfile, elem);
+ cfile->stack[0] = elem;
+ cfile->stack_top = 0;
+
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case NUMBER:
+ elem = createInt(atoll(val));
+ TAILQ_CONCAT(&elem->comments, &cfile->comments);
+ break;
+ case STRING:
+ elem = createString(makeString(-1, val));
+ TAILQ_CONCAT(&elem->comments, &cfile->comments);
+ break;
+ case NAME:
+ if (strcmp(val, "null") == 0)
+ elem = createNull();
+ else if (strcmp(val, "true") == 0)
+ elem = createBool(ISC_TRUE);
+ else if (strcmp(val, "false") == 0) {
+ elem = createBool(ISC_FALSE);
+ elem->skip = ISC_TRUE;
+ } else
+ parse_error(cfile, "unknown name %s", val);
+ TAILQ_CONCAT(&elem->comments, &cfile->comments);
+ break;
+ case LBRACKET:
+ elem = json_list_parse(cfile);
+ break;
+ case LBRACE:
+ elem = json_map_parse(cfile);
+ break;
+ case END_OF_FILE:
+ parse_error(cfile, "unexpected end of file");
+ default:
+ parse_error(cfile, "unexpected %s", val);
+ }
+ return elem;
+}
+
+struct element *
+json_list_parse(struct parse *cfile)
+{
+ struct element *list;
+ struct element *item;
+ const char *val;
+ enum dhcp_token token;
+ isc_boolean_t done = ISC_FALSE;
+
+ list = createList();
+ TAILQ_CONCAT(&list->comments, &cfile->comments);
+ stackPush(cfile, list);
+ do {
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case RBRACKET:
+ done = ISC_TRUE;
+ break;
+ case END_OF_FILE:
+ parse_error(cfile, "unexpected end of file");
+ case COMMA:
+ skip_token(&val, NULL, cfile);
+ if (listSize(list) == 0)
+ parse_error(cfile, "unexpected ','");
+ item = json_parse(cfile);
+ listPush(list, item);
+ break;
+ default:
+ if (listSize(list) > 0)
+ parse_error(cfile, "expected ','");
+ item = json_parse(cfile);
+ listPush(list, item);
+ break;
+ }
+ } while (!done);
+ skip_token(&val, NULL, cfile);
+ cfile->stack_top--;
+ return list;
+}
+
+struct element *
+json_map_parse(struct parse *cfile)
+{
+ struct element *map;
+ struct element *item;
+ const char *val;
+ const char *key;
+ enum dhcp_token token;
+ isc_boolean_t done = ISC_FALSE;
+
+ map = createMap();
+ TAILQ_CONCAT(&map->comments, &cfile->comments);
+ stackPush(cfile, map);
+ do {
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case RBRACE:
+ done = ISC_TRUE;
+ break;
+ case END_OF_FILE:
+ parse_error(cfile, "unexpected end of file");
+ case COMMA:
+ skip_token(&val, NULL, cfile);
+ if (mapSize(map) == 0)
+ parse_error(cfile, "unexpected ','");
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "unexpected %s, "
+ "expected \"key\":value", val);
+ key = strdup(val);
+ token = next_token(&val, NULL, cfile);
+ if (token != COLON)
+ parse_error(cfile, "unexpected %s, "
+ "expected ':'", val);
+ item = json_parse(cfile);
+ mapSet(map, item, key);
+ break;
+ case STRING:
+ skip_token(&val, NULL, cfile);
+ if (mapSize(map) > 0)
+ parse_error(cfile, "unexpected \"%s\", "
+ "expected ','", val);
+ key = strdup(val);
+ token = next_token(&val, NULL, cfile);
+ if (token != COLON)
+ parse_error(cfile, "unexpected %s, "
+ "expected ':'", val);
+ item = json_parse(cfile);
+ mapSet(map, item, key);
+ break;
+ default:
+ if (mapSize(map) == 0)
+ parse_error(cfile, "unexpected %s, "
+ "expected \"key\":value or '}'",
+ val);
+ else
+ parse_error(cfile, "unexpected %s, "
+ "expected ',' or '}'", val);
+ }
+ } while (!done);
+ skip_token(&val, NULL, cfile);
+ cfile->stack_top--;
+ return map;
+}
diff --git a/keama/keama.8 b/keama/keama.8
new file mode 100644
index 00000000..80a2823a
--- /dev/null
+++ b/keama/keama.8
@@ -0,0 +1,104 @@
+.\" keama.8
+.\"
+.\" Copyright (c) 2017, 2018 by Internet Systems Consortium, Inc. ("ISC")
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.TH keama 8
+.SH NAME
+keama - Kea Migration Assistant
+.SH SYNOPSIS
+.B keama
+[
+.B -4
+|
+.B -6]
+[
+.B -D
+]
+[
+.B -N
+]
+[
+.B -r
+.I {perform|fatal|pass}
+]
+[
+.B -l
+.I hook-library-path
+]
+[
+.B -i
+.I input-file
+]
+[
+.B -o
+.I output-file
+]
+.SH DESCRIPTION
+The Kea Migration Assistant converts an ISC DHCP configuration file into
+the corresponding Kea configuration file.
+.SH COMMAND LINE
+.PP
+\fIProtocol selection options:\fR
+.TP
+-4
+The input configuration is for DHCPv4. Incompatible with the \fB-6\fR
+option.
+.TP
+-6
+The input configuration is for DHCPv6. Incompatible with the \fB-4\fR
+option.
+.TP
+-D
+Define ISC DHCP minimum, default and maximum builtin lifetimes.
+.TP
+-N
+Instead of using global host reservations, put them in the matching subnet.
+.TP
+-r \fIaction\fR
+Specify what to do with hostnames: resolve them into their first address,
+raise a fatal error or pass them silently.
+.TP
+-p hook-library-path
+Specify the path where hook libraries (e.g. flex-id) can be found
+.TP
+-i input-file
+Specify the ISC DHCP configuration file to read. When it is not
+given the standard input is used.
+.TP
+-o output-file
+Specify the Kea configuration file to write. When it is not given
+the standard output is used.
+.PP
+The number of conversion failures is returned. Note that any parsing warning
+or error is fatal so please check and fix the ISC DHCP configuration file
+before using this tool.
+.SH SEE ALSO
+dhcpd(8), kea-dhcp4(8), kea-dhcp6(8).
+.SH AUTHOR
+.B keama(8)
+To learn more about Internet Systems Consortium, see
+.B https://www.isc.org
diff --git a/keama/keama.c b/keama/keama.c
new file mode 100644
index 00000000..f1b50da0
--- /dev/null
+++ b/keama/keama.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright(c) 2017, 2018 by Internet Systems Consortium, Inc.("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include <sys/errno.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "keama.h"
+
+#define KEAMA_USAGE "Usage: keama [-4|-6] [-D] [-N]" \
+ " [-r {perform|fatal|pass}\\n" \
+ " [-l hook-library-path]" \
+ " [-i input-file] [-o output-file]\n"
+
+static void
+usage(const char *sfmt, const char *sarg) {
+ if (sfmt != NULL) {
+ fprintf(stderr, sfmt, sarg);
+ fprintf(stderr, "\n");
+ }
+ fputs(KEAMA_USAGE, stderr);
+ exit(1);
+}
+
+int local_family = 0;
+char *hook_library_path = NULL;
+char *input_file = NULL;
+char *output_file = NULL;
+FILE *input = NULL;
+FILE *output = NULL;
+isc_boolean_t use_isc_lifetimes = ISC_FALSE;
+isc_boolean_t global_hr = ISC_TRUE;
+isc_boolean_t json = ISC_FALSE;
+
+static const char use_noarg[] = "No argument for command: %s";
+static const char bad_resolve[] = "Bad -r argument: %s";
+
+int
+main(int argc, char **argv) {
+ int i, fd;
+ char *inbuf = NULL;
+ size_t oldsize = 0;
+ size_t newsize = 0;
+ ssize_t cc;
+ struct parse *cfile;
+ size_t cnt = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-4") == 0)
+ local_family = AF_INET;
+ else if (strcmp(argv[i], "-6") == 0)
+ local_family = AF_INET6;
+ else if (strcmp(argv[i], "-D") == 0)
+ use_isc_lifetimes = ISC_TRUE;
+ else if (strcmp(argv[i], "-N") == 0)
+ global_hr = ISC_FALSE;
+ else if (strcmp(argv[i], "-T") == 0)
+ json = ISC_TRUE;
+ else if (strcmp(argv[i], "-r") == 0) {
+ if (++i == argc)
+ usage(use_noarg, argv[i - 1]);
+ if (strcmp(argv[i], "perform") == 0)
+ resolve = perform;
+ else if (strcmp(argv[i], "fatal") == 0)
+ resolve = fatal;
+ else if (strcmp(argv[i], "pass") == 0)
+ resolve = pass;
+ else
+ usage(bad_resolve, argv[i]);
+ } else if (strcmp(argv[i], "-l") == 0) {
+ if (++i == argc)
+ usage(use_noarg, argv[i - 1]);
+ hook_library_path = argv[i];
+ } else if (strcmp(argv[i], "-i") == 0) {
+ if (++i == argc)
+ usage(use_noarg, argv[i - 1]);
+ input_file = argv[i];
+ } else if (strcmp(argv[i], "-o") == 0) {
+ if (++i == argc)
+ usage(use_noarg, argv[i - 1]);
+ output_file = argv[i];
+ } else
+ usage("Unknown command: %s", argv[i]);
+ }
+
+ if (!json && (local_family == 0))
+ usage("address family must be set using %s", "-4 or -6");
+
+ if (input_file == NULL) {
+ input_file = "--stdin--";
+ fd = fileno(stdin);
+ for (;;) {
+ if (newsize == 0)
+ newsize = 1024;
+ else {
+ oldsize = newsize;
+ newsize *= 4;
+ }
+ inbuf = (char *)realloc(inbuf, newsize);
+ if (inbuf == 0)
+ usage("out of memory reading standard "
+ "input: %s", strerror(errno));
+ cc = read(fd, inbuf + oldsize, newsize - oldsize);
+ if (cc < 0)
+ usage("error reading standard input: %s",
+ strerror(errno));
+ if (cc + oldsize < newsize) {
+ newsize = cc + oldsize;
+ break;
+ }
+ }
+ } else {
+ fd = open(input_file, O_RDONLY);
+ if (fd < 0)
+ usage("Cannot open '%s' for reading", input_file);
+ }
+
+ if (output_file) {
+ output = fopen(output_file, "w");
+ if (output == NULL)
+ usage("Cannot open '%s' for writing", output_file);
+ } else
+ output = stdout;
+
+ TAILQ_INIT(&parses);
+ cfile = new_parse(fd, inbuf, newsize, input_file, 0);
+ assert(cfile != NULL);
+
+ if (json) {
+ struct element *elem;
+
+ elem = json_parse(cfile);
+ if (elem != NULL) {
+ print(output, elem, 0, 0);
+ fprintf(output, "\n");
+ }
+ } else {
+ spaces_init();
+ options_init();
+ cnt = conf_file_parse(cfile);
+ if (cfile->stack_top > 0) {
+ print(output, cfile->stack[0], 0, 0);
+ fprintf(output, "\n");
+ }
+ }
+
+ end_parse(cfile);
+
+ exit(cnt);
+}
+
+void
+stackPush(struct parse *pc, struct element *elem)
+{
+ if (pc->stack_top + 2 >= pc->stack_size) {
+ size_t new_size = pc->stack_size + 10;
+ size_t amount = new_size * sizeof(struct element *);
+
+ pc->stack = (struct element **)realloc(pc->stack, amount);
+ if (pc->stack == NULL)
+ parse_error(pc, "can't resize element stack");
+ pc->stack_size = new_size;
+ }
+ pc->stack_top++;
+ pc->stack[pc->stack_top] = elem;
+}
+
+void
+parse_error(struct parse *cfile, const char *fmt, ...)
+{
+ va_list list;
+ char lexbuf[256];
+ char mbuf[1024];
+ char fbuf[1024];
+ unsigned i, lix;
+
+ snprintf(fbuf, sizeof(fbuf), "%s line %d: %s",
+ cfile->tlname, cfile->lexline, fmt);
+
+ va_start(list, fmt);
+ vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
+ va_end(list);
+
+ lix = 0;
+ for (i = 0;
+ cfile->token_line[i] && i < (cfile->lexchar - 1); i++) {
+ if (lix < sizeof(lexbuf) - 1)
+ lexbuf[lix++] = ' ';
+ if (cfile->token_line[i] == '\t') {
+ for (; lix < (sizeof lexbuf) - 1 && (lix & 7); lix++)
+ lexbuf[lix] = ' ';
+ }
+ }
+ lexbuf[lix] = 0;
+
+ fprintf(stderr, "%s\n%s\n", mbuf, cfile->token_line);
+ if (cfile->lexchar < 81)
+ fprintf(stderr, "%s^\n", lexbuf);
+ exit(-1);
+}
diff --git a/keama/keama.h b/keama/keama.h
new file mode 100644
index 00000000..19593ad7
--- /dev/null
+++ b/keama/keama.h
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2017, 2018 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#ifndef EOL
+#define EOL '\n'
+#endif
+
+#include "data.h"
+#include "dhctoken.h"
+
+#include <time.h>
+
+/* Resolution of FQDNs into IPv4 addresses */
+enum resolve {
+ perform = 0, /* resolve */
+ fatal, /* raise a fatal error */
+ pass /* pass the string wth a warning */
+} resolve;
+
+/* From includes/dhcp.h */
+
+#define HTYPE_ETHER 1
+#define HTYPE_IEEE802 6
+#define HTYPE_FDDI 8
+
+#define DHO_DHCP_SERVER_IDENTIFIER 54
+#define DHO_VENDOR_CLASS_IDENTIFIER 60
+#define DHO_USER_CLASS 77
+#define DHO_VIVSO_SUBOPTIONS 125
+
+/* From includes/dhcp6.h */
+#define D6O_VENDOR_OPTS 17
+#define MAX_V6RELAY_HOPS 32
+
+/* From includes/dhcpd.h */
+
+extern int local_family;
+
+/* A parsing context. */
+
+TAILQ_HEAD(parses, parse) parses;
+
+struct parse {
+ int lexline;
+ int lexchar;
+ char *token_line;
+ char *prev_line;
+ char *cur_line;
+ const char *tlname;
+ int eol_token;
+
+ /*
+ * In order to give nice output when we have a parsing error
+ * in our file, we keep track of where we are in the line so
+ * that we can show the user.
+ *
+ * We need to keep track of two lines, because we can look
+ * ahead, via the "peek" function, to the next line sometimes.
+ *
+ * The "line1" and "line2" variables act as buffers for this
+ * information. The "lpos" variable tells us where we are in the
+ * line.
+ *
+ * When we "put back" a character from the parsing context, we
+ * do not want to have the character appear twice in the error
+ * output. So, we set a flag, the "ugflag", which the
+ * get_char() function uses to check for this condition.
+ */
+ char line1[81];
+ char line2[81];
+ int lpos;
+ int line;
+ int tlpos;
+ int tline;
+ enum dhcp_token token;
+ int ugflag;
+ char *tval;
+ int tlen;
+ char tokbuf[1500];
+
+ int warnings_occurred;
+ int file;
+ char *inbuf;
+ size_t bufix, buflen;
+ size_t bufsiz;
+
+ /*
+ * Additions for the Kea Migration Assistant.
+ */
+ struct element **stack;
+ size_t stack_size;
+ size_t stack_top;
+ size_t issue_counter;
+
+ /* don't save below this */
+ struct comments comments;
+
+ /* TAILQ_NEXT(self) is the saved state */
+ TAILQ_ENTRY(parse) next;
+
+};
+
+#define PARAMETER 0
+#define TOPLEVEL 1
+#define ROOT_GROUP 2
+#define HOST_DECL 3
+#define SHARED_NET_DECL 4
+#define SUBNET_DECL 5
+#define CLASS_DECL 6
+#define GROUP_DECL 7
+#define POOL_DECL 8
+
+/* Used as an argument to parse_class_decl() */
+#define CLASS_TYPE_VENDOR 0
+#define CLASS_TYPE_USER 1
+#define CLASS_TYPE_CLASS 2
+#define CLASS_TYPE_SUBCLASS 3
+
+#define CLASS_DECL_DELETED 1
+#define CLASS_DECL_DYNAMIC 2
+#define CLASS_DECL_STATIC 4
+#define CLASS_DECL_SUBCLASS 8
+
+/* Hardware buffer size */
+#define HARDWARE_ADDR_LEN 20
+
+/* Expression context */
+
+enum expression_context {
+ context_any, /* indefinite */
+ context_boolean,
+ context_data,
+ context_numeric,
+ context_dns,
+ context_data_or_numeric, /* indefinite */
+ context_function
+};
+
+/* Statements */
+
+enum statement_op {
+ null_statement,
+ if_statement,
+ add_statement,
+ eval_statement,
+ break_statement,
+ default_option_statement,
+ supersede_option_statement,
+ append_option_statement,
+ prepend_option_statement,
+ send_option_statement,
+ statements_statement,
+ on_statement,
+ switch_statement,
+ case_statement,
+ default_statement,
+ set_statement,
+ unset_statement,
+ let_statement,
+ define_statement,
+ log_statement,
+ return_statement,
+ execute_statement,
+ vendor_opt_statement
+};
+
+/* Expression tree structure. */
+
+enum expr_op {
+ expr_none,
+ expr_match,
+ expr_check,
+ expr_equal,
+ expr_substring,
+ expr_suffix,
+ expr_concat,
+ expr_host_lookup,
+ expr_and,
+ expr_or,
+ expr_not,
+ expr_option,
+ expr_hardware,
+ expr_hw_type, /* derived from expr_hardware */
+ expr_hw_address, /* derived from expr_hardware */
+ expr_packet,
+ expr_const_data,
+ expr_extract_int8,
+ expr_extract_int16,
+ expr_extract_int32,
+ expr_encode_int8,
+ expr_encode_int16,
+ expr_encode_int32,
+ expr_const_int,
+ expr_exists,
+ expr_encapsulate,
+ expr_known,
+ expr_reverse,
+ expr_leased_address,
+ expr_binary_to_ascii,
+ expr_config_option,
+ expr_host_decl_name,
+ expr_pick_first_value,
+ expr_lease_time,
+ expr_dns_transaction,
+ expr_static,
+ expr_ns_add,
+ expr_ns_delete,
+ expr_ns_exists,
+ expr_ns_not_exists,
+ expr_not_equal,
+ expr_null,
+ expr_variable_exists,
+ expr_variable_reference,
+ expr_filename,
+ expr_sname,
+ expr_arg,
+ expr_funcall,
+ expr_function,
+ expr_add,
+ expr_subtract,
+ expr_multiply,
+ expr_divide,
+ expr_remainder,
+ expr_binary_and,
+ expr_binary_or,
+ expr_binary_xor,
+ expr_client_state,
+ expr_ucase,
+ expr_lcase,
+ expr_regex_match,
+ expr_iregex_match,
+ expr_gethostname,
+ expr_v6relay,
+ expr_concat_dclist
+};
+
+/* options */
+
+enum option_status {
+ kea_unknown = 0, /* known only by ISC DHCP */
+ isc_dhcp_unknown = 1, /* known only by Kea */
+ known = 2, /* known by both ISC DHCP and Kea */
+ special = 3, /* requires special processing */
+ dynamic = 4 /* dynamic entry */
+};
+
+struct option_def { /* ISC DHCP option definition */
+ const char *name; /* option name */
+ const char *format; /* format string */
+ const char *space; /* space (aka universe) */
+ unsigned code; /* code point */
+ enum option_status status; /* status */
+};
+
+struct space_def { /* ISC DHCP space definition */
+ const char *old; /* ISC DHCP space name */
+ const char *name; /* Kea space name */
+ enum option_status status; /* status */
+};
+
+struct space {
+ const char *old; /* ISC DHCP space name */
+ const char *name; /* Kea space name */
+ enum option_status status; /* status */
+ struct element *vendor; /* vendor option */
+ TAILQ_ENTRY(space) next; /* next space */
+};
+
+struct option {
+ const char *old; /* ISC DHCP option name */
+ const char *name; /* Kea option name */
+ const char *format; /* ISC DHCP format string */
+ const struct space *space; /* space (aka universe) */
+ unsigned code; /* code point */
+ enum option_status status; /* status */
+ TAILQ_ENTRY(option) next; /* next option */
+};
+
+/* Kea parse tools */
+void stackPush(struct parse *cfile, struct element *elem);
+
+/* From command line */
+extern char *hook_library_path;
+extern isc_boolean_t use_isc_lifetimes;
+extern isc_boolean_t global_hr;
+
+/* From common/parse.c */
+void parse_error(struct parse *, const char *, ...)
+ __attribute__((__format__(__printf__,2,3)))
+ __attribute__((noreturn));
+
+/* conflex.c */
+struct parse *new_parse(int, char *, size_t, const char *, int);
+void end_parse(struct parse *);
+void save_parse_state(struct parse *);
+void restore_parse_state(struct parse *);
+enum dhcp_token next_token(const char **, unsigned *, struct parse *);
+enum dhcp_token peek_token(const char **, unsigned *, struct parse *);
+enum dhcp_token next_raw_token(const char **, unsigned *, struct parse *);
+enum dhcp_token peek_raw_token(const char **, unsigned *, struct parse *);
+/*
+ * Use skip_token when we are skipping a token we have previously
+ * used peek_token on as we know what the result will be in this case.
+ */
+#define skip_token(a,b,c) ((void) next_token((a),(b),(c)))
+
+/* confparse.c */
+size_t conf_file_parse(struct parse *);
+void read_conf_file(struct parse *, const char *, int);
+size_t conf_file_subparse(struct parse *, int);
+isc_boolean_t parse_statement(struct parse *, int, isc_boolean_t);
+void get_permit(struct parse *, struct element *);
+void parse_pool_statement(struct parse *, int);
+void parse_lbrace(struct parse *);
+void parse_host_declaration(struct parse *);
+void parse_class_declaration(struct parse *, int);
+void parse_shared_net_declaration(struct parse *);
+void parse_subnet_declaration(struct parse *);
+void parse_subnet6_declaration(struct parse *);
+void parse_group_declaration(struct parse *);
+void close_group(struct parse *, struct element *);
+struct element *parse_fixed_addr_param(struct parse *, enum dhcp_token);
+void parse_address_range(struct parse *, int, size_t);
+void parse_address_range6(struct parse *, int, size_t);
+void parse_prefix6(struct parse *, int, size_t);
+void parse_fixed_prefix6(struct parse *, size_t);
+void parse_pool6_statement(struct parse *, int);
+struct element *parse_allow_deny(struct parse *, int);
+void parse_server_duid_conf(struct parse *);
+void parse_directive(struct parse *);
+void parse_option_space_dir(struct parse *);
+void parse_option_code_dir(struct parse *, struct option *);
+void parse_option_status_dir(struct parse *, struct option *, enum dhcp_token);
+void parse_option_local_dir(struct parse *, struct option *);
+void parse_option_define_dir(struct parse *, struct option *);
+
+/* parse.c */
+void skip_to_semi(struct parse *);
+void skip_to_rbrace(struct parse *, int);
+void parse_semi(struct parse *);
+void parse_string(struct parse *, char **, unsigned *);
+struct string *parse_host_name(struct parse *);
+struct string *parse_ip_addr_or_hostname(struct parse *, isc_boolean_t);
+struct string *parse_ip_addr(struct parse *);
+struct string *parse_ip6_addr(struct parse *);
+struct string *parse_ip6_addr_txt(struct parse *);
+struct element *parse_hardware_param(struct parse *);
+void parse_lease_time(struct parse *, time_t *);
+struct string *parse_numeric_aggregate(struct parse *,
+ unsigned char *, unsigned *,
+ int, int, unsigned);
+void convert_num(struct parse *, unsigned char *, const char *,
+ int, unsigned);
+struct option *parse_option_name(struct parse *, isc_boolean_t,
+ isc_boolean_t *);
+void parse_option_space_decl(struct parse *);
+void parse_option_code_definition(struct parse *, struct option *);
+void parse_vendor_code_definition(struct parse *, struct option *);
+struct string *convert_format(const char *, isc_boolean_t *, isc_boolean_t *);
+struct string *parse_base64(struct parse *);
+struct string *parse_cshl(struct parse *);
+struct string *parse_hexa(struct parse *);
+isc_boolean_t parse_executable_statements(struct element *,
+ struct parse *, isc_boolean_t *,
+ enum expression_context);
+isc_boolean_t parse_executable_statement(struct element *,
+ struct parse *, isc_boolean_t *,
+ enum expression_context,
+ isc_boolean_t);
+isc_boolean_t parse_zone(struct element *, struct parse *);
+isc_boolean_t parse_key(struct element *, struct parse *);
+isc_boolean_t parse_on_statement(struct element *, struct parse *,
+ isc_boolean_t *);
+isc_boolean_t parse_switch_statement(struct element *, struct parse *,
+ isc_boolean_t *);
+isc_boolean_t parse_case_statement(struct element *, struct parse *,
+ isc_boolean_t *, enum expression_context);
+isc_boolean_t parse_if_statement(struct element *, struct parse *,
+ isc_boolean_t *);
+isc_boolean_t parse_boolean_expression(struct element *, struct parse *,
+ isc_boolean_t *);
+/* currently unused */
+isc_boolean_t parse_boolean(struct parse *);
+isc_boolean_t parse_data_expression(struct element *, struct parse *,
+ isc_boolean_t *);
+isc_boolean_t numeric_expression(struct element *, struct parse *,
+ isc_boolean_t *);
+isc_boolean_t parse_non_binary(struct element *, struct parse *,
+ isc_boolean_t *, enum expression_context);
+isc_boolean_t parse_expression(struct element *, struct parse *,
+ isc_boolean_t *, enum expression_context,
+ struct element *, enum expr_op);
+struct string *escape_option_string(unsigned, const char *,
+ isc_boolean_t *, isc_boolean_t *);
+isc_boolean_t parse_option_data(struct element *, struct parse *,
+ struct option *);
+isc_boolean_t parse_option_binary(struct element *, struct parse *,
+ struct option *, isc_boolean_t);
+struct string * parse_option_textbin(struct parse *, struct option *);
+isc_boolean_t parse_option_statement(struct element *, struct parse *,
+ struct option *, enum statement_op);
+isc_boolean_t parse_config_data(struct element *, struct parse *,
+ struct option *);
+isc_boolean_t parse_config_statement(struct element *, struct parse *,
+ struct option *, enum statement_op);
+struct string *parse_option_token(struct parse *, const char *,
+ isc_boolean_t *, isc_boolean_t *,
+ isc_boolean_t *);
+struct string *parse_option_token_binary(struct parse *, const char *);
+struct string *parse_domain_list(struct parse *, isc_boolean_t);
+isc_boolean_t is_boolean_expression(struct element *);
+isc_boolean_t is_data_expression(struct element *);
+isc_boolean_t is_numeric_expression(struct element *);
+int expr_precedence(enum expr_op, struct element *);
+
+/* options.c */
+void spaces_init(void);
+void options_init(void);
+struct space *space_lookup(const char *);
+struct option *option_lookup_name(const char *, const char *);
+struct option *kea_lookup_name(const char *, const char *);
+struct option *option_lookup_code(const char *, unsigned);
+void push_space(struct space *);
+void push_option(struct option *);
+void add_option_data(struct element *, struct element *);
+void merge_option_data(struct element *, struct element *);
+struct comments *get_config_comments(unsigned);
+const char *display_status(enum option_status);
+
+/* json.c */
+struct element *json_parse(struct parse *);
+struct element *json_list_parse(struct parse *);
+struct element *json_map_parse(struct parse *);
+
+/* print.c */
+const char *print_expression(struct element *, isc_boolean_t *);
+const char *print_boolean_expression(struct element *, isc_boolean_t *);
+const char *print_data_expression(struct element *, isc_boolean_t *);
+const char *print_numeric_expression(struct element *, isc_boolean_t *);
+
+/* reduce.c */
+struct element *reduce_boolean_expression(struct element *);
+struct element *reduce_data_expression(struct element *);
+struct element *reduce_numeric_expression(struct element *);
+
+/* eval */
+struct element *eval_expression(struct element *, isc_boolean_t *);
+struct element *eval_boolean_expression(struct element *, isc_boolean_t *);
+struct element *eval_data_expression(struct element *, isc_boolean_t *);
+struct element *eval_numeric_expression(struct element *, isc_boolean_t *);
diff --git a/keama/options.c b/keama/options.c
new file mode 100644
index 00000000..12fa5ddf
--- /dev/null
+++ b/keama/options.c
@@ -0,0 +1,1154 @@
+/*
+ * Copyright (c) 2017, 2018 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "keama.h"
+
+TAILQ_HEAD(spaces, space) spaces;
+TAILQ_HEAD(options, option) options;
+
+/* From common/tables.c */
+
+/* Additional format codes:
+
+ x - ISC DHCP and Kea string
+ Y - force full binary
+ u - undefined (parsed as X)
+*/
+
+/// SPACES
+struct space_def space_defs[] = {
+ { "dhcp", "dhcp4", 2},
+ { "nwip", "nwip", 0},
+ { "agent", "dhcp-agent-options-space", 2},
+ { "vendor-class", "_vivco_", 0},
+ { "vendor", "_vivso_", 3},
+ { "isc", "_isc_", 0},
+ { "", "vendor-encapsulated-options-space", 1},
+ { "_docsis3_", "vendor-4491", 1},
+ { "dhcp6", "dhcp6", 2},
+ { "vsio", "_vendor-opts-space_", 3},
+ { "_vsio_", "vendor-opts-space", 1},
+ { "isc6", "_isc6_", 0},
+ { "_rsoo_", "rsoo-opts", 1},
+ { "_isc6_", "vendor-2495", 1},
+ { "server", "_server_", 0},
+ { NULL, NULL, 0}
+};
+
+/// DHCPv4
+struct option_def options4[] = {
+ { "subnet-mask", "I", "dhcp", 1, 2},
+ { "time-offset", "l", "dhcp", 2, 2},
+ { "routers", "Ia", "dhcp", 3, 2},
+ { "time-servers", "Ia", "dhcp", 4, 2},
+ { "ien116-name-servers", "Ia", "dhcp", 5, 2},
+ /// ien116-name-servers -> name-servers
+ { "domain-name-servers", "Ia", "dhcp", 6, 2},
+ { "log-servers", "Ia", "dhcp", 7, 2},
+ { "cookie-servers", "Ia", "dhcp", 8, 2},
+ { "lpr-servers", "Ia", "dhcp", 9, 2},
+ { "impress-servers", "Ia", "dhcp", 10, 2},
+ { "resource-location-servers", "Ia", "dhcp", 11, 2},
+ { "host-name", "t", "dhcp", 12, 2},
+ { "boot-size", "S", "dhcp", 13, 2},
+ { "merit-dump", "t", "dhcp", 14, 2},
+ { "domain-name", "t", "dhcp", 15, 2},
+ { "swap-server", "I", "dhcp", 16, 2},
+ { "root-path", "t", "dhcp", 17, 2},
+ { "extensions-path", "t", "dhcp", 18, 2},
+ { "ip-forwarding", "f", "dhcp", 19, 2},
+ { "non-local-source-routing", "f", "dhcp", 20, 2},
+ { "policy-filter", "IIa", "dhcp", 21, 2},
+ { "max-dgram-reassembly", "S", "dhcp", 22, 2},
+ { "default-ip-ttl", "B", "dhcp", 23, 2},
+ { "path-mtu-aging-timeout", "L", "dhcp", 24, 2},
+ { "path-mtu-plateau-table", "Sa", "dhcp", 25, 2},
+ { "interface-mtu", "S", "dhcp", 26, 2},
+ { "all-subnets-local", "f", "dhcp", 27, 2},
+ { "broadcast-address", "I", "dhcp", 28, 2},
+ { "perform-mask-discovery", "f", "dhcp", 29, 2},
+ { "mask-supplier", "f", "dhcp", 30, 2},
+ { "router-discovery", "f", "dhcp", 31, 2},
+ { "router-solicitation-address", "I", "dhcp", 32, 2},
+ { "static-routes", "IIa", "dhcp", 33, 2},
+ { "trailer-encapsulation", "f", "dhcp", 34, 2},
+ { "arp-cache-timeout", "L", "dhcp", 35, 2},
+ { "ieee802-3-encapsulation", "f", "dhcp", 36, 2},
+ { "default-tcp-ttl", "B", "dhcp", 37, 2},
+ { "tcp-keepalive-interval", "L", "dhcp", 38, 2},
+ { "tcp-keepalive-garbage", "f", "dhcp", 39, 2},
+ { "nis-domain", "t", "dhcp", 40, 2},
+ { "nis-servers", "Ia", "dhcp", 41, 2},
+ { "ntp-servers", "Ia", "dhcp", 42, 2},
+ { "vendor-encapsulated-options", "E.", "dhcp", 43, 2},
+ { "netbios-name-servers", "Ia", "dhcp", 44, 2},
+ { "netbios-dd-server", "Ia", "dhcp", 45, 2},
+ { "netbios-node-type", "B", "dhcp", 46, 2},
+ { "netbios-scope", "t", "dhcp", 47, 2},
+ { "font-servers", "Ia", "dhcp", 48, 2},
+ { "x-display-manager", "Ia", "dhcp", 49, 2},
+ { "dhcp-requested-address", "I", "dhcp", 50, 2},
+ { "dhcp-lease-time", "L", "dhcp", 51, 2},
+ { "dhcp-option-overload", "B", "dhcp", 52, 2},
+ { "dhcp-message-type", "B", "dhcp", 53, 2},
+ { "dhcp-server-identifier", "I", "dhcp", 54, 2},
+ { "dhcp-parameter-request-list", "Ba", "dhcp", 55, 2},
+ { "dhcp-message", "t", "dhcp", 56, 2},
+ { "dhcp-max-message-size", "S", "dhcp", 57, 2},
+ { "dhcp-renewal-time", "L", "dhcp", 58, 2},
+ { "dhcp-rebinding-time", "L", "dhcp", 59, 2},
+ { "vendor-class-identifier", "x", "dhcp", 60, 2},
+ { "dhcp-client-identifier", "X", "dhcp", 61, 2},
+ { "nwip-domain", "t", "dhcp", 62, 2},
+ /// nwip-domain nwip-domain-name
+ { "nwip-suboptions", "Enwip.", "dhcp", 63, 2},
+ { "nisplus-domain", "t", "dhcp", 64, 2},
+ /// nisplus-domain nisplus-domain-name
+ { "nisplus-servers", "Ia", "dhcp", 65, 2},
+ { "tftp-server-name", "t", "dhcp", 66, 2},
+ { "bootfile-name", "t", "dhcp", 67, 2},
+ /// bootfile-name boot-file-name
+ { "mobile-ip-home-agent", "Ia", "dhcp", 68, 2},
+ { "smtp-server", "Ia", "dhcp", 69, 2},
+ { "pop-server", "Ia", "dhcp", 70, 2},
+ { "nntp-server", "Ia", "dhcp", 71, 2},
+ { "www-server", "Ia", "dhcp", 72, 2},
+ { "finger-server", "Ia", "dhcp", 73, 2},
+ { "irc-server", "Ia", "dhcp", 74, 2},
+ { "streettalk-server", "Ia", "dhcp", 75, 2},
+ { "streettalk-directory-assistance-server", "Ia",
+ "dhcp", 76, 2},
+ { "user-class", "tY", "dhcp", 77, 2},
+ { "slp-directory-agent", "fIa", "dhcp", 78, 2},
+ { "slp-service-scope", "fto", "dhcp", 79, 2},
+ /* 80 is the zero-length rapid-commit (RFC 4039) */
+ { "fqdn", "Efqdn.", "dhcp", 81, 2},
+ { "relay-agent-information", "Eagent.", "dhcp", 82, 2},
+ /// relay-agent-information dhcp-agent-options
+ /* 83 is iSNS (RFC 4174) */
+ /* 84 is unassigned */
+ { "nds-servers", "Ia", "dhcp", 85, 2},
+ { "nds-tree-name", "t", "dhcp", 86, 2},
+ { "nds-context", "t", "dhcp", 87, 2},
+ { "bcms-controller-names", "D", "dhcp", 88, 2},
+ { "bcms-controller-address", "Ia", "dhcp", 89, 2},
+ { "authenticate", "X", "dhcp", 90, 1},
+ /// not supported by ISC DHCP
+ { "client-last-transaction-time", "L", "dhcp", 91, 2},
+ { "associated-ip", "Ia", "dhcp", 92, 2},
+ { "pxe-system-type", "Sa", "dhcp", 93, 2},
+ // pxe-system-type client-system
+ { "pxe-interface-id", "BBB", "dhcp", 94, 2},
+ // pxe-interface-id client-ndi
+ { "pxe-client-id", "BX", "dhcp", 97, 2},
+ // pxe-client-id uuid-guid
+ { "uap-servers", "t", "dhcp", 98, 2},
+ { "geoconf-civic", "X", "dhcp", 99, 2},
+ { "pcode", "t", "dhcp", 100, 2},
+ { "tcode", "t", "dhcp", 101, 2},
+ { "netinfo-server-address", "Ia", "dhcp", 112, 2},
+ { "netinfo-server-tag", "t", "dhcp", 113, 2},
+ { "default-url", "t", "dhcp", 114, 2},
+ { "auto-config", "B", "dhcp", 116, 2},
+ { "name-service-search", "Sa", "dhcp", 117, 2},
+ { "subnet-selection", "I", "dhcp", 118, 2},
+ { "domain-search", "Dc", "dhcp", 119, 2},
+ { "vivco", "Evendor-class.", "dhcp", 124, 2},
+ /// vivco vivco-suboptions
+ { "vivso", "Evendor.", "dhcp", 125, 2},
+ /// vivso vivso-suboptions
+ {"pana-agent", "Ia", "dhcp", 136, 2},
+ {"v4-lost", "d", "dhcp", 137, 2},
+ {"capwap-ac-v4", "Ia", "dhcp", 138, 2},
+ { "sip-ua-cs-domains", "Dc", "dhcp", 141, 2},
+ { "ipv4-address-andsf", "Ia", "dhcp", 142, 0},
+ /// not supported by Kea
+ { "rdnss-selection", "BIID", "dhcp", 146, 2},
+ { "tftp-server-address", "Ia", "dhcp", 150, 0},
+ /// not supported by Kea
+ { "v4-portparams", "BBS", "dhcp", 159, 2},
+ { "v4-captive-portal", "t", "dhcp", 160, 2},
+ { "option-6rd", "BB6Ia", "dhcp", 212, 2},
+ {"v4-access-domain", "d", "dhcp", 213, 2},
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+/// DHCPv6
+struct option_def options6[] = {
+ { "client-id", "X", "dhcp6", 1, 2},
+ /// client-id clientid
+ { "server-id", "X", "dhcp6", 2, 2},
+ /// server-id serverid
+ { "ia-na", "X", "dhcp6", 3, 2},
+ { "ia-ta", "X", "dhcp6", 4, 2},
+ { "ia-addr", "X", "dhcp6", 5, 2},
+ /// ia-addr iaaddr
+ { "oro", "Sa", "dhcp6", 6, 2},
+ { "preference", "B", "dhcp6", 7, 2},
+ { "elapsed-time", "S", "dhcp6", 8, 2},
+ { "relay-msg", "X", "dhcp6", 9, 2},
+ /// 10 is unassigned
+ { "auth", "X", "dhcp6", 11, 1},
+ /// not supported by ISC DHCP
+ { "unicast", "6", "dhcp6", 12, 2},
+ { "status-code", "Nstatus-codes.to", "dhcp6", 13, 2},
+ { "rapid-commit", "Z", "dhcp6", 14, 2},
+ { "user-class", "X", "dhcp6", 15, 1},
+ /// not supported by ISC DHCP
+ { "vendor-class", "LX", "dhcp6", 16, 1},
+ /// not supported by ISC DHCP
+ { "vendor-opts", "Evsio.", "dhcp6", 17, 2},
+ { "interface-id", "X", "dhcp6", 18, 2},
+ { "reconf-msg", "Ndhcpv6-messages.", "dhcp6", 19, 2},
+ { "reconf-accept", "Z", "dhcp6", 20, 2},
+ { "sip-servers-names", "D", "dhcp6", 21, 2},
+ /// sip-servers-names sip-server-dns
+ { "sip-servers-addresses", "6a", "dhcp6", 22, 2},
+ /// sip-servers-addresses sip-server-addr
+ { "name-servers", "6a", "dhcp6", 23, 2},
+ /// name-servers dns-servers
+ { "domain-search", "D", "dhcp6", 24, 2},
+ { "ia-pd", "X", "dhcp6", 25, 2},
+ { "ia-prefix", "X", "dhcp6", 26, 2},
+ /// ia-prefix iaprefix
+ { "nis-servers", "6a", "dhcp6", 27, 2},
+ { "nisp-servers", "6a", "dhcp6", 28, 2},
+ { "nis-domain-name", "D", "dhcp6", 29, 2},
+ { "nisp-domain-name", "D", "dhcp6", 30, 2},
+ { "sntp-servers", "6a", "dhcp6", 31, 2},
+ { "info-refresh-time", "T", "dhcp6", 32, 2},
+ /// info-refresh-time information-refresh-time
+ { "bcms-server-d", "D", "dhcp6", 33, 2},
+ /// bcms-server-d bcms-server-dns
+ { "bcms-server-a", "6a", "dhcp6", 34, 2},
+ /// bcms-server-a bcms-server-addr
+ /* Note that 35 is not assigned. */
+ { "geoconf-civic", "X", "dhcp6", 36, 2},
+ { "remote-id", "X", "dhcp6", 37, 2},
+ { "subscriber-id", "X", "dhcp6", 38, 2},
+ { "fqdn", "Efqdn6-if-you-see-me-its-a-bug-bug-bug.",
+ "dhcp6", 39, 2},
+ /// fqdn client-fqdn
+ { "pana-agent", "6a", "dhcp6", 40, 2},
+ { "new-posix-timezone", "t", "dhcp6", 41, 2},
+ { "new-tzdb-timezone", "t", "dhcp6", 42, 2},
+ { "ero", "Sa", "dhcp6", 43, 2},
+ { "lq-query", "X", "dhcp6", 44, 2},
+ { "client-data", "X", "dhcp6", 45, 2},
+ { "clt-time", "L", "dhcp6", 46, 2},
+ { "lq-relay-data", "6X", "dhcp6", 47, 2},
+ { "lq-client-link", "6a", "dhcp6", 48, 2},
+ { "v6-lost", "d", "dhcp6", 51, 2},
+ { "capwap-ac-v6", "6a", "dhcp6", 52, 2},
+ { "relay-id", "X", "dhcp6", 53, 2},
+ { "v6-access-domain", "d", "dhcp6", 57, 2},
+ { "sip-ua-cs-list", "D", "dhcp6", 58, 2},
+ { "bootfile-url", "t", "dhcp6", 59, 2},
+ { "bootfile-param", "X", "dhcp6", 60, 2},
+ { "client-arch-type", "Sa", "dhcp6", 61, 2},
+ { "nii", "BBB", "dhcp6", 62, 2},
+ { "aftr-name", "d", "dhcp6", 64, 2},
+ { "erp-local-domain-name", "d", "dhcp6", 65, 2},
+ { "rsoo", "Ersoo.", "dhcp6", 66, 1},
+ /// not supported by ISC DHCP
+ { "pd-exclude", "X", "dhcp6", 67, 1},
+ /// not supported by ISC DHCP (prefix6 format)
+ { "rdnss-selection", "6BD", "dhcp6", 74, 2},
+ { "client-linklayer-addr", "X", "dhcp6", 79, 2},
+ { "link-address", "6", "dhcp6", 80, 2},
+ { "solmax-rt", "L", "dhcp6", 82, 2},
+ { "inf-max-rt", "L", "dhcp6", 83, 2},
+ { "dhcpv4-msg", "X", "dhcp6", 87, 2},
+ /// dhcpv4-msg dhcpv4-message
+ { "dhcp4-o-dhcp6-server", "6a", "dhcp6", 88, 2},
+ /// dhcp4-o-dhcp6-server dhcp4o6-server-addr
+ { "v6-captive-portal", "t", "dhcp6", 103, 2},
+ { "relay-source-port", "S", "dhcp6", 135, 2},
+ { "ipv6-address-andsf", "6a", "dhcp6", 143, 2},
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+/// DHCPv4 AGENT
+struct option_def agents[] = {
+ /// All not supported by Kea
+ { "circuit-id", "X", "agent", 1, 0},
+ { "remote-id", "X", "agent", 2, 0},
+ { "agent-id", "I", "agent", 3, 0},
+ { "DOCSIS-device-class", "L", "agent", 4, 0},
+ { "link-selection", "I", "agent", 5, 0},
+ { "relay-port", "Z", "agent", 19, 0},
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+/// SERVER
+struct option_def configs[] = {
+ { "default-lease-time", "T", "server", 1, 3},
+ { "max-lease-time", "T", "server", 2, 3},
+ { "min-lease-time", "T", "server", 3, 3},
+ { "dynamic-bootp-lease-cutoff", "T", "server", 4, 0},
+ { "dynamic-bootp-lease-length", "L", "server", 5, 0},
+ { "boot-unknown-clients", "f", "server", 6, 0},
+ { "dynamic-bootp", "f", "server", 7, 0},
+ { "allow-bootp", "f", "server", 8, 0},
+ { "allow-booting", "f", "server", 9, 0},
+ { "one-lease-per-client", "f", "server", 10, 0},
+ { "get-lease-hostnames", "f", "server", 11, 0},
+ { "use-host-decl-names", "f", "server", 12, 0},
+ { "use-lease-addr-for-default-route", "f",
+ "server", 13, 0},
+ { "min-secs", "B", "server", 14, 0},
+ { "filename", "t", "server", 15, 3},
+ { "server-name", "t", "server", 16, 3},
+ { "next-server", "I", "server", 17, 3},
+ { "authoritative", "f", "server", 18, 3},
+ { "vendor-option-space", "U", "server", 19, 3},
+ { "always-reply-rfc1048", "f", "server", 20, 0},
+ { "site-option-space", "X", "server", 21, 3},
+ { "always-broadcast", "f", "server", 22, 0},
+ { "ddns-domainname", "t", "server", 23, 3},
+ { "ddns-hostname", "t", "server", 24, 0},
+ { "ddns-rev-domainname", "t", "server", 25, 0},
+ { "lease-file-name", "t", "server", 26, 0},
+ { "pid-file-name", "t", "server", 27, 0},
+ { "duplicates", "f", "server", 28, 0},
+ { "declines", "f", "server", 29, 0},
+ { "ddns-updates", "f", "server", 30, 3},
+ { "omapi-port", "S", "server", 31, 0},
+ { "local-port", "S", "server", 32, 0},
+ { "limited-broadcast-address", "I", "server", 33, 0},
+ { "remote-port", "S", "server", 34, 0},
+ { "local-address", "I", "server", 35, 0},
+ { "omapi-key", "d", "server", 36, 0},
+ { "stash-agent-options", "f", "server", 37, 0},
+ { "ddns-ttl", "T", "server", 38, 0},
+ { "ddns-update-style", "Nddns-styles.", "server", 39, 3},
+ { "client-updates", "f", "server", 40, 0},
+ { "update-optimization", "f", "server", 41, 0},
+ { "ping-check", "f", "server", 42, 0},
+ { "update-static-leases", "f", "server", 43, 0},
+ { "log-facility", "Nsyslog-facilities.",
+ "server", 44, 0},
+ { "do-forward-updates", "f", "server", 45, 0},
+ { "ping-timeout", "T", "server", 46, 0},
+ { "infinite-is-reserved", "f", "server", 47, 0},
+ { "update-conflict-detection", "f", "server", 48, 0},
+ { "leasequery", "f", "server", 49, 0},
+ { "adaptive-lease-time-threshold", "B", "server", 50, 0},
+ { "do-reverse-updates", "f", "server", 51, 0},
+ { "fqdn-reply", "f", "server", 52, 0},
+ { "preferred-lifetime", "T", "server", 53, 3},
+ { "dhcpv6-lease-file-name", "t", "server", 54, 0},
+ { "dhcpv6-pid-file-name", "t", "server", 55, 0},
+ { "limit-addrs-per-ia", "L", "server", 56, 0},
+ { "limit-prefs-per-ia", "L", "server", 57, 0},
+ { "delayed-ack", "S", "server", 58, 0},
+ { "max-ack-delay", "L", "server", 59, 0},
+ /* LDAP */
+ { "dhcp-cache-threshold", "B", "server", 78, 0},
+ { "dont-use-fsync", "f", "server", 79, 0},
+ { "ddns-local-address4", "I", "server", 80, 0},
+ { "ddns-local-address6", "6", "server", 81, 0},
+ { "ignore-client-uids", "f", "server", 82, 3},
+ { "log-threshold-low", "B", "server", 83, 0},
+ { "log-threshold-high", "B", "server", 84, 0},
+ { "echo-client-id", "f", "server", 85, 3},
+ { "server-id-check", "f", "server", 86, 0},
+ { "prefix-length-mode", "Nprefix_length_modes.",
+ "server", 87, 0},
+ { "dhcpv6-set-tee-times", "f", "server", 88, 0},
+ { "abandon-lease-time", "T", "server", 89, 0},
+ { "use-eui-64", "f", "server", 90, 0},
+ { "check-secs-byte-order", "f", "server", 91, 0},
+ { "persist-eui-64-leases", "f", "server", 92, 0},
+ { "ddns-dual-stack-mixed-mode", "f", "server", 93, 0},
+ { "ddns-guard-id-must-match", "f", "server", 94, 0},
+ { "ddns-other-guard-is-dynamic", "f", "server", 95, 0},
+ { "release-on-roam", "f", "server", 96, 0},
+ { "local-address6", "6", "server", 97, 0},
+ { "bind-local-address6", "f", "server", 98, 0},
+ { "ping-cltt-secs", "T", "server", 99, 0},
+ { "ping-timeout-ms", "T", "server", 100, 0},
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+void
+spaces_init(void)
+{
+ struct space_def *def;
+ struct space *space;
+
+ TAILQ_INIT(&spaces);
+
+ /* Fill spaces */
+ for (def = space_defs; def->name != NULL; def++) {
+ space = (struct space *)malloc(sizeof(*space));
+ assert(space != NULL);
+ memset(space, 0, sizeof(*space));
+ space->old = def->old;
+ space->name = def->name;
+ space->status = def->status;
+ TAILQ_INSERT_TAIL(&spaces, space);
+ }
+}
+
+void
+options_init(void)
+{
+ struct option_def *def;
+ struct option *option;
+
+ TAILQ_INIT(&options);
+
+ /* Fill DHCPv4 options */
+ for (def = options4; def->name != NULL; def++) {
+ option = (struct option *)malloc(sizeof(*option));
+ assert(option != NULL);
+ memset(option, 0, sizeof(*option));
+ option->old = def->name;
+ switch (def->code) {
+ case 5:
+ option->name = "name-servers";
+ break;
+ case 62:
+ option->name = "nwip-domain-name";
+ break;
+ case 64:
+ option->name = "nisplus-domain-name";
+ break;
+ case 67:
+ option->name = "boot-file-name";
+ break;
+ case 82:
+ option->name = "dhcp-agent-options";
+ break;
+ case 93:
+ option->name = "client-system";
+ break;
+ case 94:
+ option->name = "client-ndi";
+ break;
+ case 97:
+ option->name = "uuid-guid";
+ break;
+ case 124:
+ option->name = "vivco-suboptions";
+ break;
+ case 125:
+ option->name = "vivso-suboptions";
+ break;
+ default:
+ option->name = def->name;
+ }
+ option->format = def->format;
+ option->space = space_lookup(def->space);
+ assert(option->space != NULL);
+ option->code = def->code;
+ option->status = def->status;
+ TAILQ_INSERT_TAIL(&options, option);
+ }
+
+ /* Fill DHCPv6 options */
+ for (def = options6; def->name != NULL; def++) {
+ option = (struct option *)malloc(sizeof(*option));
+ assert(option != NULL);
+ memset(option, 0, sizeof(*option));
+ option->old = def->name;
+ switch (def->code) {
+ case 1:
+ option->name = "clientid";
+ break;
+ case 2:
+ option->name = "serverid";
+ break;
+ case 5:
+ option->name = "iaaddr";
+ break;
+ case 21:
+ option->name = "sip-server-dns";
+ break;
+ case 22:
+ option->name = "sip-server-addr";
+ break;
+ case 23:
+ option->name = "dns-servers";
+ break;
+ case 26:
+ option->name = "iaprefix";
+ break;
+ case 32:
+ option->name = "information-refresh-time";
+ break;
+ case 33:
+ option->name = "bcms-server-dns";
+ break;
+ case 34:
+ option->name = "bcms-server-addr ";
+ break;
+ case 39:
+ option->name = "client-fqdn";
+ break;
+ case 87:
+ option->name = "dhcpv4-message";
+ break;
+ case 88:
+ option->name = "dhcp4o6-server-addr";
+ break;
+ default:
+ option->name = def->name;
+ break;
+ }
+ option->format = def->format;
+ option->space = space_lookup(def->space);
+ assert(option->space != NULL);
+ option->code = def->code;
+ option->status = def->status;
+ TAILQ_INSERT_TAIL(&options, option);
+ }
+
+ /* Fill agent options */
+ for (def = agents; def->name != NULL; def++) {
+ option = (struct option *)malloc(sizeof(*option));
+ assert(option != NULL);
+ memset(option, 0, sizeof(*option));
+ option->old = def->name;
+ option->name = def->name;
+ option->format = def->format;
+ option->space = space_lookup(def->space);
+ assert(option->space != NULL);
+ option->code = def->code;
+ option->status = def->status;
+ TAILQ_INSERT_TAIL(&options, option);
+ }
+
+ /* Fill server config options */
+ for (def = configs; def->name != NULL; def++) {
+ option = (struct option *)malloc(sizeof(*option));
+ assert(option != NULL);
+ memset(option, 0, sizeof(*option));
+ option->old = def->name;
+ option->name = def->name;
+ option->format = def->format;
+ option->space = space_lookup(def->space);
+ assert(option->space != NULL);
+ option->code = def->code;
+ option->status = def->status;
+ TAILQ_INSERT_TAIL(&options, option);
+ }
+}
+
+struct space *
+space_lookup(const char *name)
+{
+ struct space *space;
+
+ TAILQ_FOREACH(space, &spaces) {
+ if (space->status == isc_dhcp_unknown)
+ continue;
+ if (strcmp(name, space->old) == 0)
+ return space;
+ }
+ return NULL;
+}
+
+struct option *
+option_lookup_name(const char *space, const char *name)
+{
+ struct space *universe;
+ struct option *option;
+
+ universe = space_lookup(space);
+ if (universe == NULL)
+ return NULL;
+ TAILQ_FOREACH(option, &options) {
+ if (option->status == isc_dhcp_unknown)
+ continue;
+ if (universe != option->space)
+ continue;
+ if (strcmp(name, option->old) == 0)
+ return option;
+ }
+ return NULL;
+}
+
+struct option *
+kea_lookup_name(const char *space, const char *name)
+{
+ struct space *universe;
+ struct option *option;
+
+ TAILQ_FOREACH(universe, &spaces) {
+ if (universe->status == kea_unknown)
+ continue;
+ if (strcmp(name, universe->name) == 0)
+ break;
+ }
+ if (universe == NULL)
+ return NULL;
+ TAILQ_FOREACH(option, &options) {
+ if (option->status == kea_unknown)
+ continue;
+ if (universe != option->space)
+ continue;
+ if (strcmp(name, option->name) == 0)
+ return option;
+ }
+ return NULL;
+}
+
+struct option *
+option_lookup_code(const char *space, unsigned code)
+{
+ struct space *universe;
+ struct option *option;
+
+ universe = space_lookup(space);
+ if (universe == NULL)
+ return NULL;
+ TAILQ_FOREACH(option, &options) {
+ if (universe != option->space)
+ continue;
+ if (code == option->code)
+ return option;
+ }
+ return NULL;
+}
+
+void
+push_space(struct space *space)
+{
+ space->status = dynamic;
+ TAILQ_INSERT_TAIL(&spaces, space);
+}
+
+void
+push_option(struct option *option)
+{
+ assert(option->space != NULL);
+ option->old = option->name;
+ option->status = dynamic;
+ TAILQ_INSERT_TAIL(&options, option);
+}
+
+void
+add_option_data(struct element *src, struct element *dst)
+{
+ struct string *sspace;
+ struct element *scode;
+ struct element *name;
+ struct option *option;
+ size_t i;
+
+ sspace = stringValue(mapGet(src, "space"));
+ scode = mapGet(src, "code");
+ name = mapGet(src, "name");
+ assert((scode != NULL) || (name != NULL));
+
+ /* We'll use the code so fill it even it should always be available */
+ if (scode == NULL) {
+ option = kea_lookup_name(sspace->content,
+ stringValue(name)->content);
+ assert(option != NULL);
+ scode = createInt(option->code);
+ mapSet(src, scode, "code");
+ }
+ assert(intValue(scode) != 0);
+
+ for (i = 0; i < listSize(dst); i++) {
+ struct element *od;
+ struct element *space;
+ struct element *code;
+
+ od = listGet(dst, i);
+ space = mapGet(od, "space");
+ if (!eqString(sspace, stringValue(space)))
+ continue;
+ code = mapGet(od, "code");
+ if (code == NULL) {
+ name = mapGet(od, "name");
+ assert(name != NULL);
+ option = kea_lookup_name(sspace->content,
+ stringValue(name)->content);
+ assert(option != NULL);
+ code = createInt(option->code);
+ mapSet(od, code, "code");
+ }
+ /* check if the option is already present */
+ if (intValue(scode) == intValue(code))
+ return;
+ }
+ listPush(dst, copy(src));
+}
+
+void
+merge_option_data(struct element *src, struct element *dst)
+{
+ struct element *od;
+ size_t i;
+
+ for (i = 0; i < listSize(src); i++) {
+ od = listGet(src, i);
+ add_option_data(od, dst);
+ }
+}
+
+struct comments *
+get_config_comments(unsigned code)
+{
+ static struct comments comments;
+ struct comment *comment = NULL;
+
+ TAILQ_INIT(&comments);
+ switch (code) {
+ case 4: /* dynamic-bootp-lease-cutoff */
+ case 5: /* dynamic-bootp-lease-length */
+ case 6: /* boot-unknown-clients */
+ case 7: /* dynamic-bootp */
+ case 8: /* allow-bootp */
+ no_bootp:
+ comment = createComment("/// bootp protocol is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 9: /* allow-booting */
+ comment = createComment("/// allow-booting is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// no concrete usage known?");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #239");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 10: /* one-lease-per-client */
+ comment = createComment("/// one-lease-per-client is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #238");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 11: /* get-lease-hostnames */
+ comment = createComment("/// get-lease-hostnames is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #240");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 12: /* use-host-decl-names */
+ comment = createComment("/// use-host-decl-names defaults "
+ "to always on");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 13: /* use-lease-addr-for-default-route */
+ comment = createComment("/// use-lease-addr-for-default-route "
+ "is obsolete");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 14: /* min-secs */
+ comment = createComment("/// min-secs is not (yet?) "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #223");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 20: /* always-reply-rfc1048 */
+ goto no_bootp;
+
+ case 22: /* always-broadcast */
+ comment = createComment("/// always-broadcast is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #241");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 24: /* ddns-hostname */
+ comment = createComment("/// ddns-hostname is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Please use hostname in a "
+ "host reservation instead");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 25: /* ddns-rev-domainname */
+ comment = createComment("/// ddns-rev-domainname is an "
+ "obsolete (so not supported) feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 26: /* lease-file-name */
+ comment = createComment("/// lease-file-name is an internal "
+ "ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 27: /* pid-file-name */
+ comment = createComment("/// pid-file-nam is an internal "
+ "ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 28: /* duplicates */
+ comment = createComment("/// duplicates is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea model is different (and "
+ "stricter)");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 29: /* declines */
+ comment = createComment("/// declines is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea honors decline messages "
+ " and holds address for "
+ "decline-probation-period");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 31: /* omapi-port */
+ comment = createComment("/// omapi-port is an internal "
+ "ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 32: /* local-port */
+ comment = createComment("/// local-port is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// command line -p parameter "
+ "should be used instead");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 33: /* limited-broadcast-address */
+ comment = createComment("/// limited-broadcast-address "
+ "is not (yet?) supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #224");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 34: /* remote-port */
+ comment = createComment("/// remote-port is a not portable "
+ "(so not supported) feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 35: /* local-address */
+ comment = createComment("/// local-address is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea equivalent feature is "
+ "to specify an interface address");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 36: /* omapi-key */
+ comment = createComment("/// omapi-key is an internal "
+ "ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 37: /* stash-agent-options */
+ comment = createComment("/// stash-agent-options is not "
+ "(yet?) supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #218");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 38: /* ddns-ttl */
+ comment = createComment("/// ddns-ttl is a D2 not (yet?) "
+ "supported feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #225");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 40: /* client-updates */
+ comment = createComment("/// ddns-ttl client-updates is "
+ "not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea model is very different");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 41: /* update-optimization */
+ comment = createComment("/// update-optimization is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea follows RFC 4702");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 42: /* ping-check */
+ comment = createComment("/// ping-check is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ no_ping:
+ comment = createComment("/// Kea has no ping probing");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 43: /* update-static-leases */
+ comment = createComment("/// update-static-leases is an "
+ "obsolete feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 44: /* log-facility */
+ comment = createComment("/// log-facility is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Please use the "
+ "KEA_LOGGER_DESTINATION environment "
+ "variable instead");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 45: /* do-forward-updates */
+ comment = createComment("/// do-forward-updates is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ ddns_updates:
+ comment = createComment("/// Kea model is equivalent but "
+ "different");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 46: /* ping-timeout */
+ comment = createComment("/// ping-timeout is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto no_ping;
+
+ case 47: /* infinite-is-reserved */
+ comment = createComment("/// infinite-is-reserved is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea does not support reserved "
+ "leases");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 48: /* update-conflict-detection */
+ comment = createComment("/// update-conflict-detection is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// DDNS is handled by the D2 "
+ "server using a dedicated config");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 49: /* leasequery */
+ comment = createComment("/// leasequery is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea does not (yet) support "
+ "the leasequery protocol");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 50: /* adaptive-lease-time-threshold */
+ comment = createComment("/// adaptive-lease-time-threshold is "
+ "not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #226");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 51: /* do-reverse-updates */
+ comment = createComment("/// do-reverse-updates is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto ddns_updates;
+
+ case 52: /* fqdn-reply */
+ comment = createComment("/// fqdn-reply is an obsolete "
+ "feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 54: /* dhcpv6-lease-file-name */
+ comment = createComment("/// dhcpv6-lease-file-name "
+ "is an internal ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 55: /* dhcpv6-pid-file-name */
+ comment = createComment("/// dhcpv6-pid-file-name "
+ "is an internal ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 56: /* limit-addrs-per-ia */
+ comment = createComment("/// limit-addrs-per-ia "
+ "is not (yet?) supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ limit_resources:
+ comment = createComment("/// Reference Kea #227");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 57: /* limit-prefs-per-ia */
+ comment = createComment("/// limit-prefs-per-ia"
+ "is not (yet?) supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto limit_resources;
+
+ case 58: /* delayed-ack */
+ case 59: /* max-ack-delay */
+ comment = createComment("/// delayed ack no supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 78: /* dhcp-cache-threshold */
+ comment = createComment("/// dhcp-cache-threshold "
+ "is not (yet?) supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #228");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 79: /* dont-use-fsync */
+ comment = createComment("/// dont-use-fsync is an internal "
+ "ISC DHCP feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 80: /* ddns-local-address4 */
+ comment = createComment("/// ddns-local-address4 is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ d2_ip_address:
+ comment = createComment("/// Kea D2 equivalent config is "
+ "ip-address");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 81: /* ddns-local-address6 */
+ comment = createComment("/// ddns-local-address6 is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto d2_ip_address;
+
+ case 83: /* log-threshold-low */
+ comment = createComment("/// log-threshold-low is not (yet?) "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ log_threshold:
+ comment = createComment("/// Reference Kea #222");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 84: /* log-threshold-high */
+ comment = createComment("/// log-threshold-high is not (yet?) "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto log_threshold;
+
+ case 86: /* server-id-check */
+ comment = createComment("/// server-id-check is not (yet?) "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #242");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+
+ case 87: /* prefix-length-mode */
+ comment = createComment("/// prefix-length-mode is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea model is different (and "
+ "simpler?)");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+ case 88: /* dhcpv6-set-tee-times */
+ comment = createComment("/// dhcpv6-set-tee-times is a "
+ "transitional (so not supported) "
+ "feature");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// T1 and T2 are .5 and .8 times "
+ "preferred-lifetime");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+ case 89: /* abandon-lease-time */
+ comment = createComment("/// abandon-lease-time is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea support equivalent (and "
+ "richer) expired-lease-processing "
+ "and decline-probation-period");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+ case 90: /* use-eui-64 */
+ comment = createComment("/// EUI-64 is not (yet) supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #265");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+ case 96: /* release-on-roam */
+ comment = createComment("/// release-on-roam is not (yet) "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Reference Kea #266");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+ case 97: /* local-address6 */
+ comment = createComment("/// local-address6 is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ comment = createComment("/// Kea equivalent feature is "
+ "to specify an interface address");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ break;
+ case 99: /* ping-cltt-secs */
+ comment = createComment("/// ping-cltt-secs is not supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto no_ping;
+ case 100: /* ping-timeout-ms */
+ comment = createComment("/// ping-timeout-ms is not "
+ "supported");
+ TAILQ_INSERT_TAIL(&comments, comment);
+ goto no_ping;
+ }
+ return &comments;
+}
+
+const char *
+display_status(enum option_status status)
+{
+ switch (status) {
+ case kea_unknown:
+ case special:
+ return "known (unknown)";
+ case isc_dhcp_unknown:
+ return "unknown (known)";
+ case known:
+ return "known (known)";
+ case dynamic:
+ return "dynamic (dynamic)";
+ default:
+ return "??? (" "???" ")";
+ }
+}
diff --git a/keama/parse.c b/keama/parse.c
new file mode 100644
index 00000000..3596d5d5
--- /dev/null
+++ b/keama/parse.c
@@ -0,0 +1,6140 @@
+/*
+ * Copyright (c) 2017, 2018 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "keama.h"
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void config_min_valid_lifetime(struct element *, struct parse *);
+static void config_def_valid_lifetime(struct element *, struct parse *);
+static void config_max_valid_lifetime(struct element *, struct parse *);
+static void config_file(struct element *, struct parse *);
+static void config_sname(struct element *, struct parse *);
+static void config_next_server(struct element *, struct parse *);
+static void config_vendor_option_space(struct element *, struct parse *);
+static void config_site_option_space(struct element *, struct parse *);
+static struct element *default_qualifying_suffix(void);
+static void config_qualifying_suffix(struct element *, struct parse *);
+static void config_enable_updates(struct element *, struct parse *);
+static void config_ddns_update_style(struct element *, struct parse *);
+static void config_preferred_lifetime(struct element *, struct parse *);
+static void config_match_client_id(struct element *, struct parse *);
+static void config_echo_client_id(struct element *, struct parse *);
+
+/*
+static uint32_t getULong(const unsigned char *buf);
+static int32_t getLong(const unsigned char *buf);
+static uint32_t getUShort(const unsigned char *buf);
+static int32_t getShort(const unsigned char *buf);
+static uint32_t getUChar(const unsigned char *obuf);
+*/
+static void putULong(unsigned char *obuf, uint32_t val);
+static void putLong(unsigned char *obuf, int32_t val);
+static void putUShort(unsigned char *obuf, uint32_t val);
+static void putShort(unsigned char *obuf, int32_t val);
+/*
+static void putUChar(unsigned char *obuf, uint32_t val);
+*/
+
+/*
+static isc_boolean_t is_compound_expression(struct element *);
+*/
+static enum expression_context op_context(enum expr_op);
+static int op_val(enum expr_op);
+static int op_precedence(enum expr_op, enum expr_op);
+static enum expression_context expression_context(struct element *);
+static enum expr_op expression(struct element *);
+
+/* Skip to the semicolon ending the current statement. If we encounter
+ braces, the matching closing brace terminates the statement.
+*/
+void
+skip_to_semi(struct parse *cfile)
+{
+ skip_to_rbrace(cfile, 0);
+}
+
+/* Skips everything from the current point upto (and including) the given
+ number of right braces. If we encounter a semicolon but haven't seen a
+ left brace, consume it and return.
+ This lets us skip over:
+
+ statement;
+ statement foo bar { }
+ statement foo bar { statement { } }
+ statement}
+
+ ...et cetera. */
+void
+skip_to_rbrace(struct parse *cfile, int brace_count)
+{
+ enum dhcp_token token;
+ const char *val;
+
+ do {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ if (brace_count > 0) {
+ --brace_count;
+ }
+
+ if (brace_count == 0) {
+ /* Eat the brace and return. */
+ skip_token(&val, NULL, cfile);
+ return;
+ }
+ } else if (token == LBRACE) {
+ brace_count++;
+ } else if (token == SEMI && (brace_count == 0)) {
+ /* Eat the semicolon and return. */
+ skip_token(&val, NULL, cfile);
+ return;
+ } else if (token == EOL) {
+ /* EOL only happens when parsing /etc/resolv.conf,
+ and we treat it like a semicolon because the
+ resolv.conf file is line-oriented. */
+ skip_token(&val, NULL, cfile);
+ return;
+ }
+
+ /* Eat the current token */
+ token = next_token(&val, NULL, cfile);
+ } while (token != END_OF_FILE);
+}
+
+void
+parse_semi(struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+}
+
+/* string-parameter :== STRING SEMI */
+
+void
+parse_string(struct parse *cfile, char **sptr, unsigned *lptr)
+{
+ const char *val;
+ enum dhcp_token token;
+ char *s;
+ unsigned len;
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ parse_error(cfile, "expecting a string");
+ s = (char *)malloc(len + 1);
+ parse_error(cfile, "no memory for string %s.", val);
+ memcpy(s, val, len + 1);
+
+ parse_semi(cfile);
+ if (sptr)
+ *sptr = s;
+ else
+ free(s);
+ if (lptr)
+ *lptr = len;
+}
+
+/*
+ * hostname :== IDENTIFIER
+ * | IDENTIFIER DOT
+ * | hostname DOT IDENTIFIER
+ */
+
+struct string *
+parse_host_name(struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct string *s = NULL;
+
+ /* Read a dotted hostname... */
+ do {
+ /* Read a token, which should be an identifier. */
+ token = peek_token(&val, NULL, cfile);
+ if (!is_identifier(token) && token != NUMBER)
+ break;
+ skip_token(&val, NULL, cfile);
+
+ /* Store this identifier... */
+ if (s == NULL)
+ s = makeString(-1, val);
+ else
+ appendString(s, val);
+ /* Look for a dot; if it's there, keep going, otherwise
+ we're done. */
+ token = peek_token(&val, NULL, cfile);
+ if (token == DOT) {
+ token = next_token(&val, NULL, cfile);
+ appendString(s, val);
+ }
+ } while (token == DOT);
+
+ return s;
+}
+
+/* ip-addr-or-hostname :== ip-address | hostname
+ ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+
+ Parse an ip address or a hostname.
+
+ Note that RFC1123 permits hostnames to consist of all digits,
+ making it difficult to quickly disambiguate them from ip addresses.
+*/
+
+struct string *
+parse_ip_addr_or_hostname(struct parse *cfile, isc_boolean_t check_multi)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned char addr[4];
+ unsigned len = sizeof(addr);
+ isc_boolean_t ipaddr = ISC_FALSE;
+ struct string *bin = NULL;
+
+ token = peek_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ /*
+ * a hostname may be numeric, but domain names must
+ * start with a letter, so we can disambiguate by
+ * looking ahead a few tokens. we save the parse
+ * context first, and restore it after we know what
+ * we're dealing with.
+ */
+ save_parse_state(cfile);
+ skip_token(NULL, NULL, cfile);
+ if (next_token(NULL, NULL, cfile) == DOT &&
+ next_token(NULL, NULL, cfile) == NUMBER)
+ ipaddr = ISC_TRUE;
+ restore_parse_state(cfile);
+
+ if (ipaddr)
+ bin = parse_numeric_aggregate(cfile, addr, &len,
+ DOT, 10, 8);
+ }
+
+ if ((bin == NULL) && (is_identifier(token) || token == NUMBER)) {
+ struct string *name;
+ struct hostent *h;
+
+ name = parse_host_name(cfile);
+ if (name == NULL)
+ return NULL;
+
+ if (resolve == fatal)
+ parse_error(cfile, "expected IPv4 address. got "
+ "hostname %s", name->content);
+ else if (resolve == pass)
+ return name;
+
+ /* from do_host_lookup */
+ h = gethostbyname(name->content);
+ if ((h == NULL) || (h->h_addr_list[0] == NULL))
+ parse_error(cfile, "%s: host unknown.", name->content);
+ if (check_multi && h->h_addr_list[1]) {
+ struct comment *comment;
+ char msg[128];
+
+ snprintf(msg, sizeof(msg),
+ "/// %s resolves into multiple addresses",
+ name->content);
+ comment = createComment(msg);
+ TAILQ_INSERT_TAIL(&cfile->comments, comment);
+ }
+ bin = makeString(4, h->h_addr_list[0]);
+ }
+
+ if (bin == NULL) {
+ if (token != RBRACE && token != LBRACE)
+ token = next_token(&val, NULL, cfile);
+ parse_error(cfile, "%s (%d): expecting IP address or hostname",
+ val, token);
+ }
+
+ return makeStringExt(bin->length, bin->content, 'I');
+}
+
+/*
+ * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+ */
+
+struct string *
+parse_ip_addr(struct parse *cfile)
+{
+ unsigned char addr[4];
+ unsigned len = sizeof(addr);
+
+ return parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
+}
+
+/*
+ * Return true if every character in the string is hexadecimal.
+ */
+static isc_boolean_t
+is_hex_string(const char *s)
+{
+ while (*s != '\0') {
+ if (!isxdigit((int)*s)) {
+ return ISC_FALSE;
+ }
+ s++;
+ }
+ return ISC_TRUE;
+}
+
+/*
+ * ip-address6 :== (complicated set of rules)
+ *
+ * See section 2.2 of RFC 1884 for details.
+ *
+ * We are lazy for this. We pull numbers, names, colons, and dots
+ * together and then throw the resulting string at the inet_pton()
+ * function.
+ */
+
+struct string *
+parse_ip6_addr(struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+ char addr[16];
+ int val_len;
+ char v6[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ int v6_len;
+
+ /*
+ * First token is non-raw. This way we eat any whitespace before
+ * our IPv6 address begins, like one would expect.
+ */
+ token = peek_token(&val, NULL, cfile);
+
+ /*
+ * Gather symbols.
+ */
+ v6_len = 0;
+ for (;;) {
+ if ((((token == NAME) || (token == NUMBER_OR_NAME)) &&
+ is_hex_string(val)) ||
+ (token == NUMBER) ||
+ (token == TOKEN_ADD) ||
+ (token == DOT) ||
+ (token == COLON)) {
+
+ next_raw_token(&val, NULL, cfile);
+ val_len = strlen(val);
+ if ((v6_len + val_len) >= sizeof(v6))
+ parse_error(cfile, "Invalid IPv6 address.");
+ memcpy(v6+v6_len, val, val_len);
+ v6_len += val_len;
+
+ } else {
+ break;
+ }
+ token = peek_raw_token(&val, NULL, cfile);
+ }
+ v6[v6_len] = '\0';
+
+ /*
+ * Use inet_pton() for actual work.
+ */
+ if (inet_pton(AF_INET6, v6, addr) <= 0)
+ parse_error(cfile, "Invalid IPv6 address.");
+ return makeString(16, addr);
+}
+
+/*
+ * Same as parse_ip6_addr() above, but returns the value as a text
+ * rather than in an address binary structure.
+ */
+struct string *
+parse_ip6_addr_txt(struct parse *cfile)
+{
+ const struct string *bin;
+
+ bin = parse_ip6_addr(cfile);
+ return makeStringExt(bin->length, bin->content, '6');
+}
+
+/*
+ * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
+ * hardware-type :== ETHERNET | TOKEN_RING | TOKEN_FDDI | INFINIBAND
+ * Note that INFINIBAND may not be useful for some items, such as classification
+ * as the hardware address won't always be available.
+ */
+
+struct element *
+parse_hardware_param(struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ isc_boolean_t ether = ISC_FALSE;
+ unsigned hlen;
+ struct string *t, *r;
+ struct element *hw;
+
+ token = next_token(&val, NULL, cfile);
+ if (token == ETHERNET)
+ ether = ISC_TRUE;
+ else {
+ r = makeString(-1, val);
+ appendString(r, " ");
+ }
+
+ /* Parse the hardware address information. Technically,
+ it would make a lot of sense to restrict the length of the
+ data we'll accept here to the length of a particular hardware
+ address type. Unfortunately, there are some broken clients
+ out there that put bogus data in the chaddr buffer, and we accept
+ that data in the lease file rather than simply failing on such
+ clients. Yuck. */
+ hlen = 0;
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI)
+ parse_error(cfile, "empty hardware address");
+ t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8);
+ if (t == NULL)
+ parse_error(cfile, "can't get hardware address");
+ if (hlen > HARDWARE_ADDR_LEN)
+ parse_error(cfile, "hardware address too long");
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "expecting semicolon.");
+ if (ether)
+ r = makeStringExt(hlen, t->content, 'H');
+ else
+ concatString(r, makeStringExt(hlen,t->content, 'H'));
+ hw = createString(r);
+ TAILQ_CONCAT(&hw->comments, &cfile->comments);
+ if (!ether || (hlen != 6)) {
+ hw->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ return hw;
+}
+
+/* No BNF for numeric aggregates - that's defined by the caller. What
+ this function does is to parse a sequence of numbers separated by
+ the token specified in separator. If max is zero, any number of
+ numbers will be parsed; otherwise, exactly max numbers are
+ expected. Base and size tell us how to internalize the numbers
+ once they've been tokenized.
+
+ buf - A pointer to space to return the parsed value, if it is null
+ then the function will allocate space for the return.
+
+ max - The maximum number of items to store. If zero there is no
+ maximum. When buf is null and the function needs to allocate space
+ it will do an allocation of max size at the beginning if max is non
+ zero. If max is zero then the allocation will be done later, after
+ the function has determined the size necessary for the incoming
+ string.
+
+ returns NULL on errors or a pointer to the string structure on success.
+ */
+
+struct string *
+parse_numeric_aggregate(struct parse *cfile, unsigned char *buf,
+ unsigned *max, int separator,
+ int base, unsigned size)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned char *bufp = buf, *s;
+ unsigned count = 0;
+ struct string *r = NULL, *t = NULL;
+
+ if (!bufp && *max) {
+ bufp = (unsigned char *)malloc(*max * size / 8);
+ if (!bufp)
+ parse_error(cfile, "no space for numeric aggregate");
+ }
+ s = bufp;
+ if (!s) {
+ r = allocString();
+ t = makeString(size / 8, "bigger than needed");
+ }
+
+ do {
+ if (count) {
+ token = peek_token(&val, NULL, cfile);
+ if (token != separator) {
+ if (!*max)
+ break;
+ if (token != RBRACE && token != LBRACE)
+ token = next_token(&val, NULL, cfile);
+ parse_error(cfile, "too few numbers.");
+ }
+ skip_token(&val, NULL, cfile);
+ }
+ token = next_token(&val, NULL, cfile);
+
+ if (token == END_OF_FILE)
+ parse_error(cfile, "unexpected end of file");
+
+ /* Allow NUMBER_OR_NAME if base is 16. */
+ if (token != NUMBER &&
+ (base != 16 || token != NUMBER_OR_NAME))
+ parse_error(cfile, "expecting numeric value.");
+ /* If we can, convert the number now; otherwise, build
+ a linked list of all the numbers. */
+ if (s) {
+ convert_num(cfile, s, val, base, size);
+ s += size / 8;
+ } else {
+ convert_num(cfile, (unsigned char *)t->content,
+ val, base, size);
+ concatString(r, t);
+ }
+ } while (++count != *max);
+
+ *max = count;
+ if (bufp)
+ r = makeString(count * size / 8, (char *)bufp);
+
+ return r;
+}
+
+void
+convert_num(struct parse *cfile, unsigned char *buf, const char *str,
+ int base, unsigned size)
+{
+ const unsigned char *ptr = (const unsigned char *)str;
+ int negative = 0;
+ uint32_t val = 0;
+ int tval;
+ int max;
+
+ if (*ptr == '-') {
+ negative = 1;
+ ++ptr;
+ }
+
+ /* If base wasn't specified, figure it out from the data. */
+ if (!base) {
+ if (ptr[0] == '0') {
+ if (ptr[1] == 'x') {
+ base = 16;
+ ptr += 2;
+ } else if (isascii(ptr[1]) && isdigit(ptr[1])) {
+ base = 8;
+ ptr += 1;
+ } else {
+ base = 10;
+ }
+ } else {
+ base = 10;
+ }
+ }
+
+ do {
+ tval = *ptr++;
+ /* XXX assumes ASCII... */
+ if (tval >= 'a')
+ tval = tval - 'a' + 10;
+ else if (tval >= 'A')
+ tval = tval - 'A' + 10;
+ else if (tval >= '0')
+ tval -= '0';
+ else
+ parse_error(cfile, "Bogus number: %s.", str);
+ if (tval >= base)
+ parse_error(cfile,
+ "Bogus number %s: digit %d not in base %d",
+ str, tval, base);
+ val = val * base + tval;
+ } while (*ptr);
+
+ if (negative)
+ max = (1 << (size - 1));
+ else
+ max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
+ if (val > max) {
+ switch (base) {
+ case 8:
+ parse_error(cfile,
+ "%s%lo exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
+ break;
+ case 16:
+ parse_error(cfile,
+ "%s%lx exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
+ break;
+ default:
+ parse_error(cfile,
+ "%s%lu exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
+ break;
+ }
+ }
+
+ if (negative) {
+ switch (size) {
+ case 8:
+ *buf = -(unsigned long)val;
+ break;
+ case 16:
+ putShort(buf, -(long)val);
+ break;
+ case 32:
+ putLong(buf, -(long)val);
+ break;
+ default:
+ parse_error(cfile,
+ "Unexpected integer size: %d\n", size);
+ break;
+ }
+ } else {
+ switch (size) {
+ case 8:
+ *buf = (uint8_t)val;
+ break;
+ case 16:
+ putUShort (buf, (uint16_t)val);
+ break;
+ case 32:
+ putULong (buf, val);
+ break;
+ default:
+ parse_error(cfile,
+ "Unexpected integer size: %d\n", size);
+ }
+ }
+}
+
+/*
+ * option-name :== IDENTIFIER |
+ IDENTIFIER . IDENTIFIER
+ */
+
+struct option *
+parse_option_name(struct parse *cfile,
+ isc_boolean_t allocate,
+ isc_boolean_t *known)
+{
+ const char *val;
+ enum dhcp_token token;
+ const char *uname;
+ struct space *space;
+ struct option *option = NULL;
+ unsigned code;
+
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile,
+ "expecting identifier after option keyword.");
+
+ uname = strdup(val);
+ if (!uname)
+ parse_error(cfile, "no memory for uname information.");
+ token = peek_token(&val, NULL, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ skip_token(&val, NULL, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting identifier after '.'");
+
+ /* Look up the option name hash table for the specified
+ uname. */
+ space = space_lookup(uname);
+ if (space == NULL)
+ parse_error(cfile, "no option space named %s.", uname);
+ } else {
+ /* Use the default hash table, which contains all the
+ standard dhcp option names. */
+ val = uname;
+ space = space_lookup("dhcp");
+ }
+
+ option = option_lookup_name(space->old, val);
+
+ if (option) {
+ if (known && (option->status != isc_dhcp_unknown))
+ *known = ISC_TRUE;
+ } else if (space == space_lookup("server"))
+ parse_error(cfile, "unknown server option %s.", val);
+
+ /* If the option name is of the form unknown-[decimal], use
+ * the trailing decimal value to find the option definition.
+ * If there is no definition, construct one. This is to
+ * support legacy use of unknown options in config files or
+ * lease databases.
+ */
+ else if (strncasecmp(val, "unknown-", 8) == 0) {
+ code = atoi(val + 8);
+
+ /* Option code 0 is always illegal for us, thanks
+ * to the option decoder.
+ */
+ if (code == 0)
+ parse_error(cfile, "Option code 0 is illegal "
+ "in the %s space.", space->old);
+ if ((local_family == AF_INET) && (code == 255))
+ parse_error(cfile, "Option code 255 is illegal "
+ "in the %s space.", space->old);
+
+ /* It's odd to think of unknown option codes as
+ * being known, but this means we know what the
+ * parsed name is talking about.
+ */
+ if (known)
+ *known = ISC_TRUE;
+ option = option_lookup_code(space->old, code);
+
+ /* If we did not find an option of that code,
+ * manufacture an unknown-xxx option definition.
+ */
+ if (option == NULL) {
+ option = (struct option *)malloc(sizeof(*option));
+ /* DHCP code does not check allocation failure? */
+ memset(option, 0, sizeof(*option));
+ option->name = strdup(val);
+ option->space = space;
+ option->code = code;
+ /* Mark format as undefined */
+ option->format = "u";
+ push_option(option);
+ } else {
+ struct comment *comment;
+ char msg[256];
+
+ snprintf(msg, sizeof(msg),
+ "/// option %s.%s redefinition",
+ space->name, val);
+ comment = createComment(msg);
+ TAILQ_INSERT_TAIL(&cfile->comments, comment);
+ }
+ /* If we've been told to allocate, that means that this
+ * (might) be an option code definition, so we'll create
+ * an option structure and return it for the parent to
+ * decide.
+ */
+ } else if (allocate) {
+ option = (struct option *)malloc(sizeof(*option));
+ /* DHCP code does not check allocation failure? */
+ memset(option, 0, sizeof(*option));
+ option->name = strdup(val);
+ option->space = space;
+ /* Mark format as undefined */
+ option->format = "u";
+ push_option(option);
+ } else
+ parse_error(cfile, "no option named %s in space %s",
+ val, space->old);
+
+ return option;
+}
+
+/* IDENTIFIER[WIDTHS] SEMI
+ * WIDTHS ~= LENGTH WIDTH NUMBER
+ * CODE WIDTH NUMBER
+ */
+
+void
+parse_option_space_decl(struct parse *cfile)
+{
+ int token;
+ const char *val;
+ struct element *nu;
+ struct element *p;
+ struct space *universe;
+ int tsize = 1, lsize = 1;
+
+ skip_token(&val, NULL, cfile); /* Discard the SPACE token,
+ which was checked by the
+ caller. */
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting identifier.");
+ nu = createMap();
+ nu->skip = ISC_TRUE;
+
+ /* Expect it will be usable in Kea */
+ universe = (struct space *)malloc(sizeof(*universe));
+ if (universe == NULL)
+ parse_error(cfile, "No memory for new option space.");
+ memset(universe, 0, sizeof(*universe));
+ universe->old = strdup(val);
+ universe->name = universe->old;
+ push_space(universe);
+
+ do {
+ token = next_token(&val, NULL, cfile);
+ switch(token) {
+ case SEMI:
+ break;
+
+ case CODE:
+ if (mapSize(nu) == 0) {
+ cfile->issue_counter++;
+ mapSet(nu,
+ createString(
+ makeString(-1, universe->old)),
+ "name");
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != WIDTH)
+ parse_error(cfile, "expecting width token.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile,
+ "expecting number 1, 2, 4.");
+
+ tsize = atoi(val);
+ p = NULL;
+ if ((local_family == AF_INET) && (tsize != 1)) {
+ struct comment *comment;
+
+ comment = createComment("/// Only code width "
+ "1 is supported");
+ p = createInt(tsize);
+ TAILQ_INSERT_TAIL(&p->comments, comment);
+ } else if ((local_family == AF_INET6) &&
+ (tsize != 2)) {
+ struct comment *comment;
+
+ comment = createComment("/// Only code width "
+ "2 is supported");
+ p = createInt(tsize);
+ TAILQ_INSERT_TAIL(&p->comments, comment);
+ }
+ if (p != NULL)
+ mapSet(nu, p, "code-width");
+ break;
+
+ case LENGTH:
+ if (mapSize(nu) == 0) {
+ cfile->issue_counter++;
+ mapSet(nu,
+ createString(
+ makeString(-1, universe->old)),
+ "name");
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != WIDTH)
+ parse_error(cfile, "expecting width token.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting number 1 or 2.");
+
+ lsize = atoi(val);
+ p = NULL;
+ if ((local_family == AF_INET) && (lsize != 1)) {
+ struct comment *comment;
+
+ comment = createComment("/// Only length "
+ "width 1 is "
+ "supported");
+ p = createInt(lsize);
+ TAILQ_INSERT_TAIL(&p->comments, comment);
+ } else if ((local_family == AF_INET6) &&
+ (lsize != 2)) {
+ struct comment *comment;
+
+ comment = createComment("/// Only length "
+ "width 2 is "
+ "supported");
+ p = createInt(lsize);
+ TAILQ_INSERT_TAIL(&p->comments, comment);
+ }
+ if (p != NULL)
+ mapSet(nu, p, "length-width");
+ break;
+
+ case HASH:
+ token = next_token(&val, NULL, cfile);
+ if (token != SIZE)
+ parse_error(cfile, "expecting size token.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile,
+ "expecting a 10base number");
+ break;
+
+ default:
+ parse_error(cfile, "Unexpected token.");
+ }
+ } while (token != SEMI);
+
+ if (mapSize(nu) > 1)
+ mapSet(cfile->stack[1], nu, "option-space");
+}
+
+/* This is faked up to look good right now. Ideally, this should do a
+ recursive parse and allow arbitrary data structure definitions, but for
+ now it just allows you to specify a single type, an array of single types,
+ a sequence of types, or an array of sequences of types.
+
+ ocd :== NUMBER EQUALS ocsd SEMI
+
+ ocsd :== ocsd_type |
+ ocsd_type_sequence |
+ ARRAY OF ocsd_simple_type_sequence
+
+ ocsd_type_sequence :== LBRACE ocsd_types RBRACE
+
+ ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE
+
+ ocsd_types :== ocsd_type |
+ ocsd_types ocsd_type
+
+ ocsd_type :== ocsd_simple_type |
+ ARRAY OF ocsd_simple_type
+
+ ocsd_simple_types :== ocsd_simple_type |
+ ocsd_simple_types ocsd_simple_type
+
+ ocsd_simple_type :== BOOLEAN |
+ INTEGER NUMBER |
+ SIGNED INTEGER NUMBER |
+ UNSIGNED INTEGER NUMBER |
+ IP-ADDRESS |
+ TEXT |
+ STRING |
+ ENCAPSULATE identifier */
+
+void
+parse_option_code_definition(struct parse *cfile, struct option *option)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct element *def;
+ unsigned code;
+ unsigned arrayp = 0;
+ isc_boolean_t is_array = ISC_FALSE;
+ int recordp = 0;
+ isc_boolean_t no_more_in_record = ISC_FALSE;
+ char *type;
+ isc_boolean_t is_signed;
+ isc_boolean_t has_encapsulation = ISC_FALSE;
+ isc_boolean_t not_supported = ISC_FALSE;
+ struct string *encapsulated;
+ struct string *datatype;
+ struct string *saved;
+ struct string *format;
+ struct element *optdef;
+
+ if (option->space->status == special) {
+ parse_vendor_code_definition(cfile, option);
+ return;
+ }
+
+ /* Put the option in the definition */
+ def = createMap();
+ mapSet(def,
+ createString(makeString(-1, option->space->name)),
+ "space");
+ mapSet(def, createString(makeString(-1, option->name)), "name");
+ TAILQ_CONCAT(&def->comments, &cfile->comments);
+
+ /* Parse the option code. */
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting option code number.");
+ TAILQ_CONCAT(&def->comments, &cfile->comments);
+ code = atoi(val);
+ mapSet(def, createInt(code), "code");
+
+ /* We have the code so we can get the real option now */
+ if (option->code == 0) {
+ struct option *from_code = NULL;
+
+ option->code = code;
+ from_code = option_lookup_code(option->space->old, code);
+ if (from_code != NULL) {
+ option->status = from_code->status;
+ option->format = from_code->format;
+ }
+ }
+
+ /* Redefinitions are not allowed */
+ if ((option->status != dynamic) ||
+ (strcmp(option->format, "u") != 0)) {
+ struct comment *comment;
+
+ comment = createComment("/// Kea does not allow redefinition "
+ "of options");
+ TAILQ_INSERT_TAIL(&def->comments, comment);
+ def->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ /* Avoid option-data per name */
+ option->status = kea_unknown;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != EQUAL)
+ parse_error(cfile, "expecting \"=\"");
+ saved = allocString();
+
+ /* See if this is an array. */
+ token = next_token(&val, NULL, cfile);
+ if (token == ARRAY) {
+ token = next_token(&val, NULL, cfile);
+ if (token != OF)
+ parse_error(cfile, "expecting \"of\".");
+ arrayp = 1;
+ token = next_token(&val, NULL, cfile);
+ appendString(saved, "array of");
+ }
+
+ if (token == LBRACE) {
+ recordp = 1;
+ token = next_token(&val, NULL, cfile);
+ if (arrayp)
+ appendString(saved, " ");
+ appendString(saved, "{");
+ }
+
+ /* At this point we're expecting a data type. */
+ datatype = allocString();
+ /* We record the format essentially for the binary one */
+ format = allocString();
+ next_type:
+ if (saved->length > 0)
+ appendString(saved, " ");
+ type = NULL;
+ if (has_encapsulation)
+ parse_error(cfile,
+ "encapsulate must always be the last item.");
+
+ switch (token) {
+ case ARRAY:
+ if (arrayp)
+ parse_error(cfile, "no nested arrays.");
+ if (recordp) {
+ struct comment *comment;
+
+ comment = createComment("/// unsupported array "
+ "inside a record");
+ TAILQ_INSERT_TAIL(&def->comments, comment);
+ not_supported = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != OF)
+ parse_error(cfile, "expecting \"of\".");
+ arrayp = recordp + 1;
+ token = next_token(&val, NULL, cfile);
+ if ((recordp) && (token == LBRACE))
+ parse_error(cfile,
+ "only uniform array inside record.");
+ appendString(saved, "array of");
+ if (token == LBRACE) {
+ struct comment *comment;
+
+ comment = createComment("/// unsupported record "
+ "inside an array");
+ TAILQ_INSERT_TAIL(&def->comments, comment);
+ not_supported = ISC_TRUE;
+ cfile->issue_counter++;
+ appendString(saved, " {");
+ }
+ goto next_type;
+ case BOOLEAN:
+ type = "boolean";
+ appendString(format, "f");
+ break;
+ case INTEGER:
+ is_signed = ISC_TRUE;
+ parse_integer:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting number.");
+ switch (atoi(val)) {
+ case 8:
+ if (is_signed) {
+ type = "int8";
+ appendString(format, "b");
+ } else {
+ type = "uint8";
+ appendString(format, "B");
+ }
+ break;
+ case 16:
+ if (is_signed) {
+ type = "int16";
+ appendString(format, "s");
+ } else {
+ type = "uint16";
+ appendString(format, "S");
+ }
+ break;
+ case 32:
+ if (is_signed) {
+ type = "int32";
+ appendString(format, "l");
+ } else {
+ type = "uint32";
+ appendString(format, "L");
+ }
+ break;
+ default:
+ parse_error(cfile,
+ "%s bit precision is not supported.", val);
+ }
+ break;
+ case SIGNED:
+ is_signed = ISC_TRUE;
+ parse_signed:
+ token = next_token(&val, NULL, cfile);
+ if (token != INTEGER)
+ parse_error(cfile, "expecting \"integer\" keyword.");
+ goto parse_integer;
+ case UNSIGNED:
+ is_signed = ISC_FALSE;
+ goto parse_signed;
+
+ case IP_ADDRESS:
+ type = "ipv4-address";
+ appendString(format, "I");
+ break;
+ case IP6_ADDRESS:
+ type = "ipv6-address";
+ appendString(format, "6");
+ break;
+ case DOMAIN_NAME:
+ type = "fqdn";
+ appendString(format, "d");
+ goto no_arrays;
+ case DOMAIN_LIST:
+ /* Consume optional compression indicator. */
+ token = peek_token(&val, NULL, cfile);
+ appendString(format, "D");
+ type = "fqdn";
+ is_array = ISC_TRUE;
+ if (token == COMPRESSED) {
+ if (local_family == AF_INET6)
+ parse_error(cfile, "domain list in DHCPv6 "
+ "MUST NOT be compressed");
+ skip_token(&val, NULL, cfile);
+ appendString(format, "c");
+ appendString(saved, "compressed ");
+ }
+ appendString(saved, "list of ");
+ goto no_arrays;
+ case TEXT:
+ type = "string";
+ appendString(format, "t");
+ no_arrays:
+ if (arrayp)
+ parse_error(cfile, "arrays of text strings not %s",
+ "yet supported.");
+ no_more_in_record = ISC_TRUE;
+ break;
+ case STRING_TOKEN:
+ /* can be binary too */
+ type = "string";
+ appendString(format, "x");
+ goto no_arrays;
+
+ case ENCAPSULATE:
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile,
+ "expecting option space identifier");
+ encapsulated = makeString(-1, val);
+ has_encapsulation = ISC_TRUE;
+ appendString(format, "E");
+ appendString(format, val);
+ appendString(format, ".");
+ appendString(saved, "encapsulate ");
+ appendString(saved, val);
+ if (datatype->length == 0)
+ type = "empty";
+ break;
+
+ case ZEROLEN:
+ type = "empty";
+ appendString(format, "Z");
+ if (arrayp)
+ parse_error(cfile, "array incompatible with zerolen.");
+ no_more_in_record = ISC_TRUE;
+ break;
+
+ default:
+ parse_error(cfile, "unknown data type %s", val);
+ }
+ appendString(saved, type);
+ appendString(datatype, type);
+
+ if (recordp) {
+ token = next_token(&val, NULL, cfile);
+ if (arrayp > recordp) {
+ is_array = ISC_TRUE;
+ arrayp = 0;
+ appendString(format, "a");
+ }
+ if (token == COMMA) {
+ if (no_more_in_record) {
+ char last;
+
+ last = format->content[format->length - 1];
+ parse_error(cfile,
+ "%s must be at end of record.",
+ last == 't' ? "text" : "string");
+ }
+ token = next_token(&val, NULL, cfile);
+ appendString(saved, ",");
+ appendString(datatype, ", ");
+ goto next_type;
+ }
+ if (token != RBRACE)
+ parse_error(cfile, "expecting right brace.");
+ appendString(saved, "}");
+ }
+ parse_semi(cfile);
+ if (has_encapsulation && arrayp)
+ parse_error(cfile,
+ "Arrays of encapsulations don't make sense.");
+ if (arrayp)
+ appendString(format, (arrayp > recordp) ? "a" : "A");
+ if (is_array || arrayp) {
+ struct element *array_def;
+
+ array_def = createBool(ISC_TRUE);
+ if (not_supported)
+ array_def->skip = ISC_TRUE;
+ mapSet(def, array_def, "array");
+ }
+
+ if (not_supported) {
+ struct element *type_def;
+ struct element *saved_def;
+ struct comment *comment;
+
+ saved_def = createString(saved);
+ saved_def->skip = ISC_TRUE;
+ mapSet(def, saved_def, "definition");
+ type_def = createString(makeString(-1, "binary"));
+ comment = createComment("/// Option definition is not "
+ "compatible with Kea");
+ TAILQ_INSERT_TAIL(&type_def->comments, comment);
+ comment = createComment("/// Fallback to full binary");
+ TAILQ_INSERT_TAIL(&type_def->comments, comment);
+ mapSet(def, type_def, "type");
+ } else if (recordp) {
+ mapSet(def, createString(datatype), "record-types");
+ mapSet(def, createString(makeString(-1, "record")), "type");
+ } else
+ mapSet(def, createString(datatype), "type");
+
+ /* Force full binary when the format is not supported by Kea */
+ if (not_supported)
+ appendString(format, "Y");
+ option->format = format->content;
+
+ if (has_encapsulation)
+ mapSet(def, createString(encapsulated), "encapsulate");
+
+ optdef = mapGet(cfile->stack[1], "option-def");
+ if (optdef == NULL) {
+ optdef = createList();
+ mapSet(cfile->stack[1], optdef, "option-def");
+ }
+ listPush(optdef, def);
+}
+
+/*
+ * Specialized version of parse_option_code_definition for vendor options
+ * DHCPv4 vivso (code 125, space vendor) and DHCPv6 vendor-opts (17,
+ * space vsio). The syntax is a subnet:
+ * vcd :== NUMBER EQUALS ENCAPSULATE identifier SEMI
+ */
+
+void
+parse_vendor_code_definition(struct parse *cfile, struct option *option)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct string *id;
+ struct string *space;
+ struct space *universe;
+ struct string *name;
+ unsigned code;
+ struct element *vendor;
+
+ space = makeString(-1, "vendor-");
+
+ /* Parse the option code / vendor id. */
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "expecting option code number.");
+ id = makeString(-1, val);
+ appendString(space, val);
+
+
+ token = next_token(&val, NULL, cfile);
+ if (token != EQUAL)
+ parse_error(cfile, "expecting \"=\"");
+ token = next_token(&val, NULL, cfile);
+ if (token != ENCAPSULATE)
+ parse_error(cfile, "expecting encapsulate");
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting option space identifier");
+ universe = space_lookup(val);
+ if (universe == NULL)
+ parse_error(cfile, "unknown option space %s", val);
+ /* Map the universe to vendor-<code> */
+ universe->name = space->content;
+ /* Create the vendor option */
+ vendor = createMap();
+ if (local_family == AF_INET) {
+ space = makeString(-1, "dhcp4");
+ name = makeString(-1, "vivso-suboptions");
+ code = DHO_VIVSO_SUBOPTIONS;
+ } else {
+ space =makeString(-1, "dhcp6");
+ name = makeString(-1, "vendor-opts");
+ code = D6O_VENDOR_OPTS;
+ }
+ mapSet(vendor, createString(space), "space");
+ mapSet(vendor, createString(name), "name");
+ mapSet(vendor, createInt(code), "code");
+ mapSet(vendor, createString(id), "data");
+ universe->vendor = vendor;
+ parse_semi(cfile);
+}
+
+struct string *
+convert_format(const char *fmt, isc_boolean_t *is_array,
+ isc_boolean_t *encapsulate)
+{
+ struct string *datatype;
+ const char *g;
+
+ if ((strchr(fmt, 'A') != NULL) || (strchr(fmt, 'a') != NULL) ||
+ (strchr(fmt, 'D') != NULL))
+ *is_array = ISC_TRUE;
+
+ if (strchr(fmt, 'E') != NULL)
+ *encapsulate = ISC_TRUE;
+
+ if ((strchr(fmt, 'Y') != NULL) || (strchr(fmt, 'A') != NULL) ||
+ (strchr(fmt, 'E') != NULL) || (strchr(fmt, 'o') != NULL) ||
+ (*fmt == 'X') || (*fmt == 'u'))
+ return makeString(-1, "binary");
+
+ datatype = allocString();
+
+ do {
+ if (datatype->length != 0)
+ appendString(datatype, ", ");
+
+ switch (*fmt) {
+ case 'U':
+ case 't':
+ case 'x':
+ appendString(datatype, "string");
+ break;
+ case 'I':
+ appendString(datatype, "ipv4-address");
+ break;
+ case '6':
+ appendString(datatype, "ipv6-address");
+ break;
+ case 'l':
+ appendString(datatype, "int32");
+ break;
+ case 'L':
+ case 'T':
+ appendString(datatype, "uint32");
+ break;
+ case 's':
+ appendString(datatype, "int16");
+ break;
+ case 'S':
+ appendString(datatype, "uint16");
+ break;
+ case 'b':
+ appendString(datatype, "int8");
+ break;
+ case 'B':
+ appendString(datatype, "uint8");
+ break;
+ case 'f':
+ appendString(datatype, "boolean");
+ break;
+ case 'E':
+ case 'N':
+ g = strchr(fmt, '.');
+ if (g == NULL)
+ return makeString(-1, "bad?!");
+ if (*fmt == 'N')
+ return makeString(-1, "unsupported?!");
+ fmt = g;
+ break;
+ case 'X':
+ appendString(datatype, "binary");
+ break;
+ case 'd':
+ case 'D':
+ appendString(datatype, "fqdn");
+ break;
+ case 'Z':
+ appendString(datatype, "empty");
+ break;
+ case 'A':
+ case 'a':
+ case 'c':
+ /* ignored */
+ break;
+ default:
+ return makeString(-1, "unknown?!");
+ }
+ fmt++;
+ } while (*fmt != '\0');
+
+ return datatype;
+}
+
+/*
+ * base64 :== NUMBER_OR_STRING
+ */
+
+struct string *
+parse_base64(struct parse *cfile)
+{
+ const char *val;
+ unsigned i;
+ static unsigned char
+ from64[] = {64, 64, 64, 64, 64, 64, 64, 64, /* \"#$%&' */
+ 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */
+ 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */
+ 60, 61, 64, 64, 64, 64, 64, 64, /* 89:;<=>? */
+ 64, 0, 1, 2, 3, 4, 5, 6, /* @ABCDEFG */
+ 7, 8, 9, 10, 11, 12, 13, 14, /* HIJKLMNO */
+ 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */
+ 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */
+ 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */
+ 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */
+ 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */
+ 49, 50, 51, 64, 64, 64, 64, 64}; /* xyz{|}~ */
+ struct string *t;
+ struct string *r;
+ isc_boolean_t valid_base64;
+
+ r = allocString();
+
+ /* It's possible for a + or a / to cause a base64 quantity to be
+ tokenized into more than one token, so we have to parse them all
+ in before decoding. */
+ do {
+ unsigned l;
+
+ (void)next_token(&val, &l, cfile);
+ t = makeString(l, val);
+ concatString(r, t);
+ (void)peek_token(&val, NULL, cfile);
+ valid_base64 = ISC_TRUE;
+ for (i = 0; val[i]; i++) {
+ /* Check to see if the character is valid. It
+ may be out of range or within the right range
+ but not used in the mapping */
+ if (((val[i] < ' ') || (val[i] > 'z')) ||
+ ((from64[val[i] - ' '] > 63) && (val[i] != '='))) {
+ valid_base64 = ISC_FALSE;
+ break; /* no need to continue for loop */
+ }
+ }
+ } while (valid_base64);
+
+ return r;
+}
+
+/*
+ * colon-separated-hex-list :== NUMBER |
+ * NUMBER COLON colon-separated-hex-list
+ */
+
+struct string *
+parse_cshl(struct parse *cfile)
+{
+ uint8_t ibuf;
+ char tbuf[4];
+ isc_boolean_t first = ISC_TRUE;
+ struct string *data;
+ enum dhcp_token token;
+ const char *val;
+
+ data = allocString();
+
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME)
+ parse_error(cfile, "expecting hexadecimal number.");
+ convert_num(cfile, &ibuf, val, 16, 8);
+ if (first)
+ snprintf(tbuf, sizeof(tbuf), "%02hhx", ibuf);
+ else
+ snprintf(tbuf, sizeof(tbuf), ":%02hhx", ibuf);
+ first = ISC_FALSE;
+ appendString(data, tbuf);
+
+ token = peek_token(&val, NULL, cfile);
+ if (token != COLON)
+ break;
+ skip_token(&val, NULL, cfile);
+ }
+
+ return data;
+}
+
+/* Same but without colons in output */
+
+struct string *
+parse_hexa(struct parse *cfile)
+{
+ uint8_t ibuf;
+ char tbuf[4];
+ struct string *data;
+ enum dhcp_token token;
+ const char *val;
+
+ data = allocString();
+
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME)
+ parse_error(cfile, "expecting hexadecimal number.");
+ convert_num(cfile, &ibuf, val, 16, 8);
+ snprintf(tbuf, sizeof(tbuf), "%02hhx", ibuf);
+ appendString(data, tbuf);
+
+ token = peek_token(&val, NULL, cfile);
+ if (token != COLON)
+ break;
+ skip_token(&val, NULL, cfile);
+ }
+
+ return data;
+}
+
+/*
+ * executable-statements :== executable-statement executable-statements |
+ * executable-statement
+ *
+ * executable-statement :==
+ * IF if-statement |
+ * ADD class-name SEMI |
+ * BREAK SEMI |
+ * OPTION option-parameter SEMI |
+ * SUPERSEDE option-parameter SEMI |
+ * PREPEND option-parameter SEMI |
+ * APPEND option-parameter SEMI
+ */
+
+isc_boolean_t
+parse_executable_statements(struct element *statements,
+ struct parse *cfile, isc_boolean_t *lose,
+ enum expression_context case_context)
+{
+ if (statements->type != ELEMENT_LIST)
+ parse_error(cfile, "statements is not a list?");
+ for (;;) {
+ struct element *statement;
+
+ statement = createMap();
+ TAILQ_CONCAT(&statement->comments, &cfile->comments);
+ if (!parse_executable_statement(statement, cfile, lose,
+ case_context, ISC_FALSE))
+ break;
+ TAILQ_CONCAT(&statement->comments, &cfile->comments);
+ listPush(statements, statement);
+ }
+ if (!*lose)
+ return ISC_TRUE;
+
+ return ISC_FALSE;
+}
+
+isc_boolean_t
+parse_executable_statement(struct element *result,
+ struct parse *cfile, isc_boolean_t *lose,
+ enum expression_context case_context,
+ isc_boolean_t direct)
+{
+ unsigned len;
+ enum dhcp_token token;
+ const char *val;
+ struct element *st;
+ struct option *option;
+ struct element *var;
+ struct element *pri;
+ struct element *expr;
+ isc_boolean_t known;
+ int flag;
+ int i;
+ struct element *zone;
+ struct string *s;
+ static isc_boolean_t log_warning = ISC_TRUE;
+
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case DB_TIME_FORMAT:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token == DEFAULT)
+ s = makeString(-1, val);
+ else if (token == LOCAL)
+ s = makeString(-1, val);
+ else
+ parse_error(cfile, "Expecting 'local' or 'default'.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "Expecting a semicolon.");
+ st = createString(s);
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, "db-time-format");
+
+ /* We're done here. */
+ return ISC_TRUE;
+
+ case IF:
+ skip_token(&val, NULL, cfile);
+ return parse_if_statement(result, cfile, lose);
+
+ case TOKEN_ADD:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "expecting class name.");
+ s = makeString(-1, val);
+ parse_semi(cfile);
+ st = createString(s);
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, "add-class");
+ break;
+
+ case BREAK:
+ skip_token(&val, NULL, cfile);
+ s = makeString(-1, val);
+ parse_semi(cfile);
+ st = createNull();
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, "break");
+ break;
+
+ case SEND:
+ skip_token(&val, NULL, cfile);
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ return parse_option_statement(result, cfile, option,
+ send_option_statement);
+
+ case SUPERSEDE:
+ case OPTION:
+ skip_token(&val, NULL, cfile);
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ return parse_option_statement(result, cfile, option,
+ supersede_option_statement);
+
+ case ALLOW:
+ flag = 1;
+ goto pad;
+ case DENY:
+ flag = 0;
+ goto pad;
+ case IGNORE:
+ flag = 2;
+ pad:
+ skip_token(&val, NULL, cfile);
+ st = parse_allow_deny(cfile, flag);
+ mapSet(result, st, "config");
+ break;
+
+ case DEFAULT:
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token == COLON)
+ goto switch_default;
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ return parse_option_statement(result, cfile, option,
+ default_option_statement);
+ case PREPEND:
+ skip_token(&val, NULL, cfile);
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ return parse_option_statement(result, cfile, option,
+ prepend_option_statement);
+ case APPEND:
+ skip_token(&val, NULL, cfile);
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ return parse_option_statement(result, cfile, option,
+ append_option_statement);
+
+ case ON:
+ skip_token(&val, NULL, cfile);
+ return parse_on_statement(result, cfile, lose);
+
+ case SWITCH:
+ skip_token(&val, NULL, cfile);
+ return parse_switch_statement(result, cfile, lose);
+
+ case CASE:
+ skip_token(&val, NULL, cfile);
+ if (case_context == context_any)
+ parse_error(cfile,
+ "case statement in inappropriate scope.");
+ return parse_case_statement(result,
+ cfile, lose, case_context);
+
+ switch_default:
+ skip_token(&val, NULL, cfile);
+ if (case_context == context_any)
+ parse_error(cfile, "switch default statement in %s",
+ "inappropriate scope.");
+ s = makeString(-1, "default");
+ st = createNull();
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, "default");
+ return ISC_TRUE;
+
+ case DEFINE:
+ case TOKEN_SET:
+ skip_token(&val, NULL, cfile);
+ if (token == DEFINE)
+ flag = 1;
+ else
+ flag = 0;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME)
+ parse_error(cfile,
+ "%s can't be a variable name", val);
+ st = createMap();
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, flag ? "define" : "set");
+ var = createString(makeString(-1, val));
+ mapSet(st, var, "name");
+ token = next_token(&val, NULL, cfile);
+
+ if (token == LPAREN) {
+ struct element *func;
+ struct string *args;
+
+ func = createMap();
+ args = allocString();
+ do {
+ token = next_token(&val, NULL, cfile);
+ if (token == RPAREN)
+ break;
+ if (token != NAME && token != NUMBER_OR_NAME)
+ parse_error(cfile,
+ "expecting argument name");
+ if (args->length > 0)
+ appendString(args, ", ");
+ appendString(args, val);
+ token = next_token(&val, NULL, cfile);
+ } while (token == COMMA);
+
+ if (token != RPAREN) {
+ parse_error(cfile, "expecting right paren.");
+ badx:
+ skip_to_semi(cfile);
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ mapSet(func, createString(args), "arguments");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE)
+ parse_error(cfile, "expecting left brace.");
+
+ expr = createList();
+ if (!parse_executable_statements(expr, cfile,
+ lose, case_context)) {
+ if (*lose)
+ goto badx;
+ }
+ mapSet(func, expr, "body");
+ mapSet(st, func, "function");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RBRACE)
+ parse_error(cfile, "expecting rigt brace.");
+ } else {
+ if (token != EQUAL)
+ parse_error(cfile,
+ "expecting '=' in %s statement.",
+ flag ? "define" : "set");
+
+ expr = createMap();
+ if (!parse_expression(expr, cfile, lose, context_any,
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting expression.");
+ else
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+ mapSet(st, expr, "value");
+ parse_semi(cfile);
+ }
+ break;
+
+ case UNSET:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME)
+ parse_error(cfile, "%s can't be a variable name", val);
+
+ st = createMap();
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, "unset");
+ var = createString(makeString(-1, val));
+ mapSet(st, var, "name");
+ parse_semi(cfile);
+ break;
+
+ case EVAL:
+ skip_token(&val, NULL, cfile);
+ expr = createMap();
+
+ if (!parse_expression(expr, cfile, lose,
+ context_data, /* XXX */
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting data expression.");
+ else
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+ mapSet(result, expr, "eval");
+ parse_semi(cfile);
+ break;
+
+ case EXECUTE:
+ skip_token(&val, NULL, cfile);
+ expr = createMap();
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ parse_error(cfile, "left parenthesis expected.");
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ parse_error(cfile, "Expecting a quoted string.");
+ mapSet(expr, createString(makeString(len, val)), "command");
+
+ st = createList();
+
+ while ((token = next_token(&val, NULL, cfile)) == COMMA) {
+ var = createMap();
+ if (!parse_data_expression(var, cfile, lose)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting expression.");
+ skip_to_semi(cfile);
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ listPush(st, var);
+ }
+ mapSet(expr, st, "arguments");
+
+ if (token != RPAREN)
+ parse_error(cfile, "right parenthesis expected.");
+ parse_semi(cfile);
+ mapSet(result, expr, "execute");
+ break;
+
+ case RETURN:
+ skip_token(&val, NULL, cfile);
+
+ expr = createMap();
+
+ if (!parse_expression(expr, cfile, lose, context_data,
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting data expression.");
+ else
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+ mapSet(result, expr, "return");
+ parse_semi(cfile);
+ break;
+
+ case LOG:
+ skip_token(&val, NULL, cfile);
+
+ st = createMap();
+ st->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, st, "log");
+ if (log_warning) {
+ struct comment *comment;
+
+ comment = createComment("/// Kea does not support "
+ "yet log statements");
+ TAILQ_INSERT_TAIL(&st->comments, comment);
+ comment= createComment("/// Reference Kea #234");
+ TAILQ_INSERT_TAIL(&st->comments, comment);
+ log_warning = ISC_FALSE;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ parse_error(cfile, "left parenthesis expected.");
+
+ token = peek_token(&val, NULL, cfile);
+ i = 1;
+ if (token == FATAL)
+ s = makeString(-1, val);
+ else if (token == ERROR)
+ s = makeString(-1, val);
+ else if (token == TOKEN_DEBUG)
+ s = makeString(-1, val);
+ else if (token == INFO)
+ s = makeString(-1, val);
+ else {
+ s = makeString(-1, "DEBUG");
+ i = 0;
+ }
+ if (i) {
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ parse_error(cfile, "comma expected.");
+ }
+ pri = createString(s);
+ mapSet(st, pri, "priority");
+
+ expr = createMap();
+ if (!parse_data_expression(expr, cfile, lose)) {
+ skip_to_semi(cfile);
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ mapSet(st, expr, "message");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ parse_error(cfile, "right parenthesis expected.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+ break;
+
+ case PARSE_VENDOR_OPT:
+ /* The parse-vendor-option; The statement has no arguments.
+ * We simply set up the statement and when it gets executed it
+ * will find all information it needs in the packet and options.
+ */
+ skip_token(&val, NULL, cfile);
+ parse_semi(cfile);
+
+ /* Done by Kea after classification so this statement
+ * silently does not translate */
+ break;
+
+ /* Not really a statement, but we parse it here anyway
+ because it's appropriate for all DHCP agents with
+ parsers. */
+ case ZONE:
+ skip_token(&val, NULL, cfile);
+ zone = createMap();
+ zone->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, zone, "zone");
+
+ s = parse_host_name(cfile);
+ if (s == NULL) {
+ parse_error(cfile, "expecting hostname.");
+ badzone:
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+ if (s->content[s->length - 1] != '.')
+ appendString(s, ".");
+ mapSet(zone, createString(s), "name");
+ if (!parse_zone(zone, cfile))
+ goto badzone;
+ return ISC_TRUE;
+
+ /* Also not really a statement, but same idea as above. */
+ case KEY:
+ skip_token(&val, NULL, cfile);
+ if (!parse_key(result, cfile)) {
+ /* Kea TODO */
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ return ISC_TRUE;
+
+ default:
+ if (is_identifier(token)) {
+ /* the config universe is the server one */
+ option = option_lookup_name("server", val);
+ if (option) {
+ skip_token(&val, NULL, cfile);
+ result->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ return parse_config_statement
+ (direct ? NULL : result,
+ cfile, option,
+ supersede_option_statement);
+ }
+ }
+
+ if (token == NUMBER_OR_NAME || token == NAME) {
+ /* This is rather ugly. Since function calls are
+ data expressions, fake up an eval statement. */
+ expr = createMap();
+
+ if (!parse_expression(expr, cfile, lose, context_data,
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile, "expecting "
+ "function call.");
+ else
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+ mapSet(result, expr, "eval");
+ parse_semi(cfile);
+ break;
+ }
+
+ *lose = ISC_FALSE;
+ return ISC_FALSE;
+ }
+
+ return ISC_TRUE;
+}
+
+/* zone-statements :== zone-statement |
+ zone-statement zone-statements
+ zone-statement :==
+ PRIMARY ip-addresses SEMI |
+ SECONDARY ip-addresses SEMI |
+ PRIMARY6 ip-address6 SEMI |
+ SECONDARY6 ip-address6 SEMI |
+ key-reference SEMI
+ ip-addresses :== ip-addr-or-hostname |
+ ip-addr-or-hostname COMMA ip-addresses
+ key-reference :== KEY STRING |
+ KEY identifier */
+
+isc_boolean_t
+parse_zone(struct element *zone, struct parse *cfile)
+{
+ int token;
+ const char *val;
+ struct element *values;
+ struct string *key_name;
+ isc_boolean_t done = ISC_FALSE;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE)
+ parse_error(cfile, "expecting left brace");
+
+ do {
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case PRIMARY:
+ if (mapContains(zone, "primary"))
+ parse_error(cfile, "more than one primary.");
+ values = createList();
+ mapSet(zone, values, "primary");
+ goto consemup;
+
+ case SECONDARY:
+ if (mapContains(zone, "secondary"))
+ parse_error(cfile, "more than one secondary.");
+ values = createList();
+ mapSet(zone, values, "secondary");
+ consemup:
+ skip_token(&val, NULL, cfile);
+ do {
+ struct string *value;
+
+ value = parse_ip_addr_or_hostname(cfile,
+ ISC_FALSE);
+ if (value == NULL)
+ parse_error(cfile,
+ "expecting IP addr or "
+ "hostname.");
+ listPush(values, createString(value));
+ token = next_token(&val, NULL, cfile);
+ } while (token == COMMA);
+ if (token != SEMI)
+ parse_error(cfile, "expecting semicolon.");
+ break;
+
+ case PRIMARY6:
+ if (mapContains(zone, "primary6"))
+ parse_error(cfile, "more than one primary6.");
+ values = createList();
+ mapSet(zone, values, "primary6");
+ goto consemup6;
+
+ case SECONDARY6:
+ if (mapContains(zone, "secondary6"))
+ parse_error(cfile, "more than one secondary6.");
+ values = createList();
+ mapSet(zone, values, "secondary6");
+ consemup6:
+ skip_token(&val, NULL, cfile);
+ do {
+ struct string *addr;
+
+ addr = parse_ip6_addr_txt(cfile);
+ if (addr == NULL)
+ parse_error(cfile, "expecting IPv6 addr.");
+ listPush(values, createString(addr));
+ token = next_token(&val, NULL, cfile);
+ } while (token == COMMA);
+ if (token != SEMI)
+ parse_error(cfile, "expecting semicolon.");
+ break;
+
+ case KEY:
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ skip_token(&val, NULL, cfile);
+ key_name = makeString(-1, val);
+ } else {
+ key_name = parse_host_name(cfile);
+ if (!key_name)
+ parse_error(cfile, "expecting key name.");
+ }
+ if (mapContains(zone, "key"))
+ parse_error(cfile, "Multiple key definitions");
+ mapSet(zone, createString(key_name), "key");
+ parse_semi(cfile);
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ } while (!done);
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RBRACE)
+ parse_error(cfile, "expecting right brace.");
+ return (1);
+}
+
+/* key-statements :== key-statement |
+ key-statement key-statements
+ key-statement :==
+ ALGORITHM host-name SEMI |
+ secret-definition SEMI
+ secret-definition :== SECRET base64val |
+ SECRET STRING
+
+ Kea: where to put this? It is a D2 value */
+
+isc_boolean_t
+parse_key(struct element* result, struct parse *cfile)
+{
+ int token;
+ const char *val;
+ isc_boolean_t done = ISC_FALSE;
+ struct element *key;
+ struct string *alg;
+ struct string *sec;
+ struct element *keys;
+ char *s;
+
+ key = createMap();
+ key->skip = ISC_TRUE;
+ cfile->issue_counter++;
+
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ skip_token(&val, NULL, cfile);
+ mapSet(key, createString(makeString(-1, val)), "name");
+ } else {
+ struct string *name;
+
+ name = parse_host_name(cfile);
+ if (name == NULL)
+ parse_error(cfile, "expecting key name.");
+ mapSet(key, createString(name), "name");
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE)
+ parse_error(cfile, "expecting left brace");
+
+ do {
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case ALGORITHM:
+ if (mapContains(key, "algorithm"))
+ parse_error(cfile, "key: too many algorithms");
+ alg = parse_host_name(cfile);
+ if (alg == NULL)
+ parse_error(cfile,
+ "expecting key algorithm name.");
+ parse_semi(cfile);
+ /* If the algorithm name isn't an FQDN, tack on
+ the .SIG-ALG.REG.NET. domain. */
+ s = strrchr(alg->content, '.');
+ if (!s)
+ appendString(alg, ".SIG-ALG.REG.INT.");
+ /* If there is no trailing '.', hack one in. */
+ else
+ appendString(alg, ".");
+ mapSet(key, createString(alg), "algorithm");
+ break;
+
+ case SECRET:
+ if (mapContains(key, "secret"))
+ parse_error(cfile, "key: too many secrets");
+
+ sec = parse_base64(cfile);
+ if (sec == NULL) {
+ skip_to_rbrace(cfile, 1);
+ return ISC_FALSE;
+ }
+ mapSet(key, createString(sec), "secret");
+
+ parse_semi(cfile);
+ break;
+
+ default:
+ done = ISC_TRUE;
+ break;
+ }
+ } while (!done);
+ if (token != RBRACE)
+ parse_error(cfile, "expecting right brace.");
+ /* Allow the BIND 8 syntax, which has a semicolon after each
+ closing brace. */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI)
+ skip_token(&val, NULL, cfile);
+
+ /* Remember the key. */
+ keys = mapGet(result, "tsig-keys");
+ if (keys == NULL) {
+ keys = createList();
+ mapSet(result, keys, "tsig-keys");
+ }
+ listPush(keys, key);
+ return ISC_TRUE;
+}
+
+/*
+ * on-statement :== event-types LBRACE executable-statements RBRACE
+ * event-types :== event-type OR event-types |
+ * event-type
+ * event-type :== EXPIRY | COMMIT | RELEASE
+ */
+
+isc_boolean_t
+parse_on_statement(struct element *result,
+ struct parse *cfile,
+ isc_boolean_t *lose)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *statement;
+ struct string *cond;
+ struct element *body;
+
+ statement = createMap();
+ statement->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, statement, "on");
+
+ cond = allocString();
+ do {
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case EXPIRY:
+ case COMMIT:
+ case RELEASE:
+ case TRANSMISSION:
+ appendString(cond, val);
+ break;
+
+ default:
+ parse_error(cfile, "expecting a lease event type");
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token == OR)
+ appendString(cond, " or ");
+ } while (token == OR);
+
+ mapSet(statement, createString(cond), "condition");
+
+ /* Semicolon means no statements. */
+ if (token == SEMI)
+ return ISC_TRUE;
+
+ if (token != LBRACE)
+ parse_error(cfile, "left brace expected.");
+
+ body = createList();
+ if (!parse_executable_statements(body, cfile, lose, context_any)) {
+ if (*lose) {
+ /* Try to even things up. */
+ do {
+ token = next_token(&val, NULL, cfile);
+ } while (token != END_OF_FILE && token != RBRACE);
+ return ISC_FALSE;
+ }
+ }
+ mapSet(statement, body, "body");
+ token = next_token(&val, NULL, cfile);
+ if (token != RBRACE)
+ parse_error(cfile, "right brace expected.");
+ return ISC_TRUE;
+}
+
+/*
+ * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE
+ *
+ */
+
+isc_boolean_t
+parse_switch_statement(struct element *result,
+ struct parse *cfile,
+ isc_boolean_t *lose)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *statement;
+ struct element *cond;
+ struct element *body;
+
+ statement = createMap();
+ statement->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(result, statement, "switch");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN) {
+ parse_error(cfile, "expecting left brace.");
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+
+ cond = createMap();
+ if (!parse_expression(cond, cfile, lose, context_data_or_numeric,
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting data or numeric expression.");
+ return ISC_FALSE;
+ }
+ mapSet(statement, cond, "condition");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ parse_error(cfile, "right paren expected.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE)
+ parse_error(cfile, "left brace expected.");
+
+ body = createList();
+ if (!parse_executable_statements(body, cfile, lose,
+ (is_data_expression(cond) ? context_data : context_numeric))) {
+ if (*lose) {
+ skip_to_rbrace(cfile, 1);
+ return ISC_FALSE;
+ }
+ }
+ mapSet(statement, body, "body");
+ token = next_token(&val, NULL, cfile);
+ if (token != RBRACE)
+ parse_error(cfile, "right brace expected.");
+ return ISC_TRUE;
+}
+
+/*
+ * case-statement :== CASE expr COLON
+ *
+ */
+
+isc_boolean_t
+parse_case_statement(struct element *result,
+ struct parse *cfile,
+ isc_boolean_t *lose,
+ enum expression_context case_context)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *expr;
+
+ expr = createMap();
+ if (!parse_expression(expr, cfile, lose, case_context,
+ NULL, expr_none))
+ {
+ if (!*lose)
+ parse_error(cfile, "expecting %s expression.",
+ (case_context == context_data
+ ? "data" : "numeric"));
+ *lose = ISC_TRUE;
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COLON)
+ parse_error(cfile, "colon expected.");
+ mapSet(result, expr, "case");
+ return ISC_TRUE;
+}
+
+/*
+ * if-statement :== boolean-expression LBRACE executable-statements RBRACE
+ * else-statement
+ *
+ * else-statement :== <null> |
+ * ELSE LBRACE executable-statements RBRACE |
+ * ELSE IF if-statement |
+ * ELSIF if-statement
+ */
+
+isc_boolean_t
+parse_if_statement(struct element *result,
+ struct parse *cfile,
+ isc_boolean_t *lose)
+{
+ enum dhcp_token token;
+ const char *val;
+ isc_boolean_t parenp;
+ struct element *statement;
+ struct element *cond;
+ struct element *branch;
+
+ statement = createMap();
+ statement->skip = ISC_TRUE;
+ cfile->issue_counter++;
+
+ mapSet(result, statement, "if");
+
+ token = peek_token(&val, NULL, cfile);
+ if (token == LPAREN) {
+ parenp = ISC_TRUE;
+ skip_token(&val, NULL, cfile);
+ } else
+ parenp = ISC_FALSE;
+
+ cond = createMap();
+ if (!parse_boolean_expression(cond, cfile, lose)) {
+ if (!*lose)
+ parse_error(cfile, "boolean expression expected.");
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ mapSet(statement, cond, "condition");
+ if (parenp) {
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ parse_error(cfile, "expecting right paren.");
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE)
+ parse_error(cfile, "left brace expected.");
+ branch = createList();
+ if (!parse_executable_statements(branch, cfile, lose, context_any)) {
+ if (*lose) {
+ /* Try to even things up. */
+ do {
+ token = next_token(&val, NULL, cfile);
+ } while (token != END_OF_FILE && token != RBRACE);
+ return ISC_FALSE;
+ }
+ }
+ mapSet(statement, branch, "then");
+ token = next_token(&val, NULL, cfile);
+ if (token != RBRACE)
+ parse_error(cfile, "right brace expected.");
+ token = peek_token(&val, NULL, cfile);
+ if (token == ELSE) {
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token == IF) {
+ skip_token(&val, NULL, cfile);
+ branch = createMap();
+ if (!parse_if_statement(branch, cfile, lose)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting if statement");
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ } else if (token != LBRACE)
+ parse_error(cfile, "left brace or if expected.");
+ else {
+ skip_token(&val, NULL, cfile);
+ branch = createList();
+ if (!parse_executable_statements(branch, cfile,
+ lose, context_any))
+ return ISC_FALSE;
+ token = next_token(&val, NULL, cfile);
+ if (token != RBRACE)
+ parse_error(cfile, "right brace expected.");
+ }
+ mapSet(statement, branch, "else");
+ } else if (token == ELSIF) {
+ skip_token(&val, NULL, cfile);
+ branch = createMap();
+ if (!parse_if_statement(branch, cfile, lose)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting conditional.");
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ mapSet(statement, branch, "else");
+ }
+
+ return ISC_TRUE;
+}
+
+/*
+ * boolean_expression :== CHECK STRING |
+ * NOT boolean-expression |
+ * data-expression EQUAL data-expression |
+ * data-expression BANG EQUAL data-expression |
+ * data-expression REGEX_MATCH data-expression |
+ * boolean-expression AND boolean-expression |
+ * boolean-expression OR boolean-expression
+ * EXISTS OPTION-NAME
+ */
+
+isc_boolean_t
+parse_boolean_expression(struct element *expr,
+ struct parse *cfile,
+ isc_boolean_t *lose)
+{
+ /* Parse an expression... */
+ if (!parse_expression(expr, cfile, lose, context_boolean,
+ NULL, expr_none))
+ return ISC_FALSE;
+
+ if (!is_boolean_expression(expr) &&
+ !mapContains(expr, "variable-reference") &&
+ !mapContains(expr, "funcall"))
+ parse_error(cfile, "Expecting a boolean expression.");
+ return ISC_TRUE;
+}
+
+/* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */
+
+isc_boolean_t
+parse_boolean(struct parse *cfile)
+{
+ const char *val;
+ isc_boolean_t rv;
+
+ (void)next_token(&val, NULL, cfile);
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ rv = ISC_TRUE;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ rv = ISC_FALSE;
+ else
+ parse_error(cfile,
+ "boolean value (true/false/on/off) expected");
+ parse_semi(cfile);
+ return rv;
+}
+
+/*
+ * data_expression :== SUBSTRING LPAREN data-expression COMMA
+ * numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * CONCAT LPAREN data-expression COMMA
+ * data-expression RPAREN
+ * SUFFIX LPAREN data_expression COMMA
+ * numeric-expression RPAREN |
+ * LCASE LPAREN data_expression RPAREN |
+ * UCASE LPAREN data_expression RPAREN |
+ * OPTION option_name |
+ * HARDWARE |
+ * PACKET LPAREN numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * V6RELAY LPAREN numeric-expression COMMA
+ * data-expression RPAREN |
+ * STRING |
+ * colon_separated_hex_list
+ */
+
+isc_boolean_t
+parse_data_expression(struct element *expr,
+ struct parse *cfile,
+ isc_boolean_t *lose)
+{
+ /* Parse an expression... */
+ if (!parse_expression(expr, cfile, lose, context_data,
+ NULL, expr_none))
+ return ISC_FALSE;
+
+ if (!is_data_expression(expr) &&
+ !mapContains(expr, "variable-reference") &&
+ !mapContains(expr, "funcall"))
+ parse_error(cfile, "Expecting a data expression.");
+ return ISC_TRUE;
+}
+
+/*
+ * numeric-expression :== EXTRACT_INT LPAREN data-expression
+ * COMMA number RPAREN |
+ * NUMBER
+ */
+
+isc_boolean_t
+parse_numeric_expression(struct element *expr,
+ struct parse *cfile,
+ isc_boolean_t *lose)
+{
+ /* Parse an expression... */
+ if (!parse_expression(expr, cfile, lose, context_numeric,
+ NULL, expr_none))
+ return ISC_FALSE;
+
+ if (!is_numeric_expression(expr) &&
+ !mapContains(expr, "variable-reference") &&
+ !mapContains(expr, "funcall"))
+ parse_error(cfile, "Expecting a numeric expression.");
+ return ISC_TRUE;
+}
+
+/* Parse a subexpression that does not contain a binary operator. */
+
+isc_boolean_t
+parse_non_binary(struct element *expr,
+ struct parse *cfile,
+ isc_boolean_t *lose,
+ enum expression_context context)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *nexp;
+ struct element *arg;
+ struct element *chain;
+ struct string *data;
+ struct comment *comment;
+ struct option *option;
+ isc_boolean_t known;
+ unsigned len;
+
+ token = peek_token(&val, NULL, cfile);
+
+ /* Check for unary operators... */
+ switch (token) {
+ case CHECK:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "string expected.");
+ nexp = createString(makeString(-1, val));
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "check");
+ break;
+
+ case TOKEN_NOT:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+
+ if (!parse_non_binary(nexp, cfile, lose, context_boolean)) {
+ if (!*lose)
+ parse_error(cfile, "expression expected");
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ if (!is_boolean_expression(nexp))
+ parse_error(cfile, "boolean expression expected");
+ if (!nexp->skip) {
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(expr, nexp, "not");
+ break;
+
+ case LPAREN:
+ skip_token(&val, NULL, cfile);
+ if (!parse_expression(expr, cfile, lose, context,
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile, "expression expected");
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ parse_error(cfile, "right paren expected");
+ break;
+
+ case EXISTS:
+ skip_token(&val, NULL, cfile);
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;;
+ }
+ nexp = createMap();
+ /* push infos to get it back trying to reduce it */
+ mapSet(nexp,
+ createString(makeString(-1, option->space->old)),
+ "universe");
+ mapSet(nexp,
+ createString(makeString(-1, option->name)),
+ "name");
+ mapSet(nexp, createInt(option->code), "code");
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "exists");
+ break;
+
+ case STATIC:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "static");
+ break;
+
+ case KNOWN:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "known");
+ break;
+
+ case SUBSTRING:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "substring");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN) {
+ nolparen:
+ parse_error(cfile, "left parenthesis expected.");
+ }
+
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose)) {
+ nodata:
+ if (!*lose)
+ parse_error(cfile,
+ "expecting data expression.");
+ return ISC_FALSE;
+ }
+ mapSet(nexp, arg, "expression");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA) {
+ nocomma:
+ parse_error(cfile, "comma expected.");
+ }
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose)) {
+ nonum:
+ if (!*lose)
+ parse_error(cfile,
+ "expecting numeric expression.");
+ return ISC_FALSE;
+ }
+ mapSet(nexp, arg, "offset");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nonum;
+ mapSet(nexp, arg, "length");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN) {
+ norparen:
+ parse_error(cfile, "right parenthesis expected.");
+ }
+ break;
+
+ case SUFFIX:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "suffix");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "expression");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nonum;
+ mapSet(nexp, arg, "length");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case LCASE:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression(nexp, cfile, lose))
+ goto nodata;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ if (!nexp->skip) {
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(expr, nexp, "lowercase");
+ break;
+
+ case UCASE:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression(nexp, cfile, lose))
+ goto nodata;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ if (!nexp->skip) {
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(expr, nexp, "uppercase");
+ break;
+
+ case CONCAT:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "concat");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "left");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ concat_another:
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose))
+ goto nodata;
+
+ token = next_token(&val, NULL, cfile);
+
+ if (token == COMMA) {
+ chain = createMap();
+ mapSet(nexp, chain, "right");
+ nexp = createMap();
+ mapSet(chain, nexp, "concat");
+ mapSet(nexp, arg, "left");
+ goto concat_another;
+ }
+ mapSet(nexp, arg, "right");
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case BINARY_TO_ASCII:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "binary-to-ascii");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "base");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "width");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "separator");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "buffer");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case REVERSE:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "reverse");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ arg = createMap();
+ if (!(parse_numeric_expression(arg, cfile, lose)))
+ goto nodata;
+ mapSet(nexp, arg, "width");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!(parse_data_expression(arg, cfile, lose)))
+ goto nodata;
+ mapSet(nexp, arg, "buffer");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case PICK:
+ /* pick (a, b, c) actually produces an internal representation
+ that looks like pick (a, pick (b, pick (c, nil))). */
+ skip_token(&val, NULL, cfile);
+ nexp = createList();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "pick-first-value");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ do {
+ arg = createMap();
+ if (!(parse_data_expression(arg, cfile, lose)))
+ goto nodata;
+ listPush(nexp, arg);
+
+ token = next_token(&val, NULL, cfile);
+ } while (token == COMMA);
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case OPTION:
+ case CONFIG_OPTION:
+ skip_token(&val, NULL, cfile);
+ known = ISC_FALSE;
+ option = parse_option_name(cfile, ISC_FALSE, &known);
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ return ISC_FALSE;
+ }
+ nexp = createMap();
+ mapSet(nexp,
+ createString(makeString(-1, option->space->old)),
+ "universe");
+ mapSet(nexp,
+ createString(makeString(-1, option->name)),
+ "name");
+ mapSet(nexp, createInt(option->code), "code");
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ if (token == OPTION)
+ mapSet(expr, nexp, "option");
+ else {
+ createComment("/// config-option is "
+ "not supported by Kea");
+ TAILQ_CONCAT(&nexp->comments, &cfile->comments);
+ mapSet(expr, nexp, "config-option");
+ }
+ break;
+
+ case HARDWARE:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "hardware");
+ break;
+
+ case LEASED_ADDRESS:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "leased-address");
+ break;
+
+ case CLIENT_STATE:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "client-state");
+ break;
+
+ case FILENAME:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "filename");
+ break;
+
+ case SERVER_NAME:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "server-name");
+ break;
+
+ case LEASE_TIME:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "lease-time");
+ break;
+
+ case TOKEN_NULL:
+ skip_token(&val, NULL, cfile);
+ /* can look at context to return directly ""? */
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "null");
+ break;
+
+ case HOST_DECL_NAME:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "host-decl-name");
+ break;
+
+ case PACKET:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "packet");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nonum;
+ mapSet(nexp, arg, "offset");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nonum;
+ mapSet(nexp, arg, "length");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case STRING:
+ skip_token(&val, &len, cfile);
+ resetString(expr, makeString(len, val));
+ break;
+
+ case EXTRACT_INT:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ parse_error(cfile, "left parenthesis expected.");
+
+ if (!parse_data_expression(nexp, cfile, lose)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting data expression.");
+ return ISC_FALSE;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ parse_error(cfile, "comma expected.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "number expected.");
+ switch (atoi(val)) {
+ case 8:
+ mapSet(expr, nexp, "extract-int8");
+ break;
+
+ case 16:
+ mapSet(expr, nexp, "extract-int16");
+ break;
+
+ case 32:
+ mapSet(expr, nexp, "extract-int32");
+ break;
+
+ default:
+ parse_error(cfile, "unsupported integer size %s", val);
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ parse_error(cfile, "right parenthesis expected.");
+ break;
+
+ case ENCODE_INT:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ parse_error(cfile, "left parenthesis expected.");
+
+ if (!parse_numeric_expression(nexp, cfile, lose))
+ parse_error(cfile, "expecting numeric expression.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ parse_error(cfile, "comma expected.");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER)
+ parse_error(cfile, "number expected.");
+ switch (atoi(val)) {
+ case 8:
+ mapSet(expr, nexp, "encode-int8");
+ break;
+
+ case 16:
+ mapSet(expr, nexp, "encode-int16");
+ break;
+
+ case 32:
+ mapSet(expr, nexp, "encode-int32");
+ break;
+
+ default:
+ parse_error(cfile, "unsupported integer size %s", val);
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ parse_error(cfile, "right parenthesis expected.");
+ break;
+
+ case NUMBER:
+ /* If we're in a numeric context, this should just be a
+ number, by itself. */
+ if (context == context_numeric ||
+ context == context_data_or_numeric) {
+ skip_token(&val, NULL, cfile);
+ /* can also return a const-int */
+ resetInt(expr, atoi(val));
+ break;
+ }
+
+ case NUMBER_OR_NAME:
+ /* Return a const-data to make a difference with
+ a string literal. createHexa() adds 0x */
+ mapSet(expr, createHexa(parse_hexa(cfile)), "const-data");
+ break;
+
+ case NS_FORMERR:
+ skip_token(&val, NULL, cfile);
+#ifndef FORMERR
+#define FORMERR 1
+#endif
+ resetInt(expr, FORMERR);
+ comment = createComment("/// constant FORMERR(1)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_NOERROR:
+ skip_token(&val, NULL, cfile);
+#ifndef ISC_R_SUCCESS
+#define ISC_R_SUCCESS 0
+#endif
+ resetInt(expr, ISC_R_SUCCESS);
+ comment = createComment("/// constant ISC_R_SUCCESS(0)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_NOTAUTH:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_NOTAUTH
+#define DHCP_R_NOTAUTH ((6 << 16) + 21)
+#endif
+ resetInt(expr, DHCP_R_NOTAUTH);
+ comment = createComment("/// constant DHCP_R_NOTAUTH(393237)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_NOTIMP:
+ skip_token(&val, NULL, cfile);
+#ifndef ISC_R_NOTIMPLEMENTED
+#define ISC_R_NOTIMPLEMENTED 27
+#endif
+ resetInt(expr, ISC_R_NOTIMPLEMENTED);
+ comment = createComment("/// constant ISC_R_NOTIMPLEMENTED(27)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_NOTZONE:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_NOTZONE
+#define DHCP_R_NOTZONE ((6 << 16) + 22)
+#endif
+ resetInt(expr, DHCP_R_NOTZONE);
+ comment = createComment("/// constant DHCP_R_NOTZONE(393238)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_NXDOMAIN:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_NXDOMAIN
+#define DHCP_R_NXDOMAIN ((6 << 16) + 15)
+#endif
+ resetInt(expr, DHCP_R_NXDOMAIN);
+ comment = createComment("/// constant DHCP_R_NXDOMAIN(393231)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_NXRRSET:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_NXRRSET
+#define DHCP_R_NXRRSET ((6 << 16) + 20)
+#endif
+ resetInt(expr, DHCP_R_NXRRSET);
+ comment = createComment("/// constant DHCP_R_NXRRSET(393236)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_REFUSED:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_REFUSED
+#define DHCP_R_REFUSED ((6 << 16) + 17)
+#endif
+ resetInt(expr, DHCP_R_REFUSED);
+ comment = createComment("/// constant DHCP_R_REFUSED(393233)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_SERVFAIL:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_SERVFAIL
+#define DHCP_R_SERVFAIL ((6 << 16) + 14)
+#endif
+ resetInt(expr, DHCP_R_SERVFAIL);
+ comment = createComment("/// constant DHCP_R_SERVFAIL(393230)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_YXDOMAIN:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_YXDOMAIN
+#define DHCP_R_YXDOMAIN ((6 << 16) + 18)
+#endif
+ resetInt(expr, DHCP_R_YXDOMAIN);
+ comment = createComment("/// constant DHCP_R_YXDOMAIN(393234)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case NS_YXRRSET:
+ skip_token(&val, NULL, cfile);
+#ifndef DHCP_R_YXRRSET
+#define DHCP_R_YXRRSET ((6 << 16) + 19)
+#endif
+ resetInt(expr, DHCP_R_YXRRSET);
+ comment = createComment("/// constant DHCP_R_YXRRSET(393235)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case BOOTING:
+ skip_token(&val, NULL, cfile);
+#ifndef S_INIT
+#define S_INIT 2
+#endif
+ resetInt(expr, S_INIT);
+ comment = createComment("/// constant S_INIT(2)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case REBOOT:
+ skip_token(&val, NULL, cfile);
+#ifndef S_REBOOTING
+#define S_REBOOTING 1
+#endif
+ resetInt(expr, S_REBOOTING);
+ comment = createComment("/// constant S_REBOOTING(1)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case SELECT:
+ skip_token(&val, NULL, cfile);
+#ifndef S_SELECTING
+#define S_SELECTING 3
+#endif
+ resetInt(expr, S_SELECTING);
+ comment = createComment("/// constant S_SELECTING(3)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case REQUEST:
+ skip_token(&val, NULL, cfile);
+#ifndef S_REQUESTING
+#define S_REQUESTING 4
+#endif
+ resetInt(expr, S_REQUESTING);
+ comment = createComment("/// constant S_REQUESTING(4)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case BOUND:
+ skip_token(&val, NULL, cfile);
+#ifndef S_BOUND
+#define S_BOUND 5
+#endif
+ resetInt(expr, S_BOUND);
+ comment = createComment("/// constant S_BOUND(5)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case RENEW:
+ skip_token(&val, NULL, cfile);
+#ifndef S_RENEWING
+#define S_RENEWING 6
+#endif
+ resetInt(expr, S_RENEWING);
+ comment = createComment("/// constant S_RENEWING(6)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case REBIND:
+ skip_token(&val, NULL, cfile);
+#ifndef S_REBINDING
+#define S_REBINDING 7
+#endif
+ resetInt(expr, S_REBINDING);
+ comment = createComment("/// constant S_REBINDING(7)");
+ TAILQ_INSERT_TAIL(&expr->comments, comment);
+ break;
+
+ case DEFINED:
+ skip_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME)
+ parse_error(cfile, "%s can't be a variable name", val);
+
+ nexp = createString(makeString(-1, val));
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "variable-exists");
+ token = next_token(&val, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* This parses 'gethostname()'. */
+ case GETHOSTNAME:
+ skip_token(&val, NULL, cfile);
+ nexp = createNull();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "gethostname");
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case GETHOSTBYNAME:
+ skip_token(&val, NULL, cfile);
+ token = next_token(NULL, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ /* The argument is a quoted string. */
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING)
+ parse_error(cfile, "Expecting quoted literal: "
+ "\"foo.example.com\"");
+ nexp = createString(makeString(-1, val));
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "gethostbyname");
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case V6RELAY:
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "v6relay");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ arg = createMap();
+ if (!parse_numeric_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "relay");
+
+ token = next_token(&val, NULL, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ arg = createMap();
+ if (!parse_data_expression(arg, cfile, lose))
+ goto nodata;
+ mapSet(nexp, arg, "relay-option");
+
+ token = next_token(&val, NULL, cfile);
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* Not a valid start to an expression... */
+ default:
+ if (token != NAME && token != NUMBER_OR_NAME)
+ return ISC_FALSE;
+
+ skip_token(&val, NULL, cfile);
+
+ /* Save the name of the variable being referenced. */
+ data = makeString(-1, val);
+
+ /* Simple variable reference, as far as we can tell. */
+ token = peek_token(&val, NULL, cfile);
+ if (token != LPAREN) {
+ nexp = createString(data);
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "variable-reference");
+ break;
+ }
+
+ skip_token(&val, NULL, cfile);
+ nexp = createMap();
+ nexp->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(expr, nexp, "funcall");
+ chain = createString(data);
+ mapSet(nexp, chain, "name");
+
+ /* Now parse the argument list. */
+ chain = createList();
+ do {
+ arg = createMap();
+ if (!parse_expression(arg, cfile, lose, context_any,
+ NULL, expr_none)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting expression.");
+ skip_to_semi(cfile);
+ return ISC_FALSE;
+ }
+ listPush(chain, arg);
+ token = next_token(&val, NULL, cfile);
+ } while (token == COMMA);
+ if (token != RPAREN)
+ parse_error(cfile, "Right parenthesis expected.");
+ mapSet(nexp, chain, "arguments");
+ break;
+ }
+ return ISC_TRUE;
+}
+
+/* Parse an expression. */
+
+isc_boolean_t
+parse_expression(struct element *expr, struct parse *cfile,
+ isc_boolean_t *lose, enum expression_context context,
+ struct element *lhs, enum expr_op binop)
+{
+ enum dhcp_token token;
+ const char *val;
+ struct element *rhs, *tmp;
+ enum expr_op next_op;
+ enum expression_context
+ lhs_context = context_any,
+ rhs_context = context_any;
+ const char *binop_name;
+
+new_rhs:
+ rhs = createMap();
+ if (!parse_non_binary(rhs, cfile, lose, context)) {
+ /* If we already have a left-hand side, then it's not
+ okay for there not to be a right-hand side here, so
+ we need to flag it as an error. */
+ if (lhs)
+ if (!*lose)
+ parse_error(cfile,
+ "expecting right-hand side.");
+ return ISC_FALSE;
+ }
+
+ /* At this point, rhs contains either an entire subexpression,
+ or at least a left-hand-side. If we do not see a binary token
+ as the next token, we're done with the expression. */
+
+ token = peek_token(&val, NULL, cfile);
+ switch (token) {
+ case BANG:
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+ if (token != EQUAL)
+ parse_error(cfile, "! in boolean context without =");
+ next_op = expr_not_equal;
+ context = expression_context(rhs);
+ break;
+
+ case EQUAL:
+ next_op = expr_equal;
+ context = expression_context(rhs);
+ break;
+
+ case TILDE:
+ skip_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+
+ if (token == TILDE)
+ next_op = expr_iregex_match;
+ else if (token == EQUAL)
+ next_op = expr_regex_match;
+ else
+ parse_error(cfile, "expecting ~= or ~~ operator");
+
+ context = expression_context(rhs);
+ break;
+
+ case AND:
+ next_op = expr_and;
+ context = expression_context(rhs);
+ break;
+
+ case OR:
+ next_op = expr_or;
+ context = expression_context(rhs);
+ break;
+
+ case PLUS:
+ next_op = expr_add;
+ context = expression_context(rhs);
+ break;
+
+ case MINUS:
+ next_op = expr_subtract;
+ context = expression_context(rhs);
+ break;
+
+ case SLASH:
+ next_op = expr_divide;
+ context = expression_context(rhs);
+ break;
+
+ case ASTERISK:
+ next_op = expr_multiply;
+ context = expression_context(rhs);
+ break;
+
+ case PERCENT:
+ next_op = expr_remainder;
+ context = expression_context(rhs);
+ break;
+
+ case AMPERSAND:
+ next_op = expr_binary_and;
+ context = expression_context(rhs);
+ break;
+
+ case PIPE:
+ next_op = expr_binary_or;
+ context = expression_context(rhs);
+ break;
+
+ case CARET:
+ next_op = expr_binary_xor;
+ context = expression_context(rhs);
+ break;
+
+ default:
+ next_op = expr_none;
+ }
+
+ /* If we have no lhs yet, we just parsed it. */
+ if (!lhs) {
+ /* If there was no operator following what we just parsed,
+ then we're done - return it. */
+ if (next_op == expr_none) {
+ resetBy(expr, rhs);
+ return ISC_TRUE;
+ }
+
+ lhs = rhs;
+ rhs = NULL;
+ binop = next_op;
+ skip_token(&val, NULL, cfile);
+ goto new_rhs;
+ }
+
+ /* If the next binary operator is of greater precedence than the
+ * current operator, then rhs we have parsed so far is actually
+ * the lhs of the next operator. To get this value, we have to
+ * recurse.
+ */
+ if (binop != expr_none && next_op != expr_none &&
+ op_precedence(binop, next_op) < 0) {
+
+ /* Eat the subexpression operator token, which we pass to
+ * parse_expression...we only peek()'d earlier.
+ */
+ skip_token(&val, NULL, cfile);
+
+ /* Continue parsing of the right hand side with that token. */
+ tmp = rhs;
+ rhs = createMap();
+ if (!parse_expression(rhs, cfile, lose, op_context(next_op),
+ tmp, next_op)) {
+ if (!*lose)
+ parse_error(cfile,
+ "expecting a subexpression");
+ return ISC_FALSE;
+ }
+ next_op = expr_none;
+ }
+
+ binop_name = "none";
+ if (binop != expr_none) {
+ rhs_context = expression_context(rhs);
+ lhs_context = expression_context(lhs);
+
+ if ((rhs_context != context_any) &&
+ (lhs_context != context_any) &&
+ (rhs_context != lhs_context))
+ parse_error(cfile, "illegal expression relating "
+ "different types");
+
+ switch (binop) {
+ case expr_not_equal:
+ binop_name = "not-equal";
+ goto data_numeric;
+ case expr_equal:
+ binop_name = "equal";
+ data_numeric:
+ if ((rhs_context != context_data_or_numeric) &&
+ (rhs_context != context_data) &&
+ (rhs_context != context_numeric) &&
+ (rhs_context != context_any))
+ parse_error(cfile, "expecting data/numeric "
+ "expression");
+ break;
+
+ case expr_iregex_match:
+ binop_name = "iregex-match";
+ break;
+
+ case expr_regex_match:
+ binop_name = "regex-match";
+ if (expression_context(rhs) != context_data)
+ parse_error(cfile,
+ "expecting data expression");
+ break;
+
+ case expr_and:
+ binop_name = "and";
+ goto boolean;
+ case expr_or:
+ binop_name = "or";
+ boolean:
+ if ((rhs_context != context_boolean) &&
+ (rhs_context != context_any)) {
+ parse_error(cfile,
+ "expecting boolean expressions");
+ }
+ break;
+
+ case expr_add:
+ binop_name = "add";
+ goto numeric;
+ case expr_subtract:
+ binop_name = "subtract";
+ goto numeric;
+ case expr_divide:
+ binop_name = "divide";
+ goto numeric;
+ case expr_multiply:
+ binop_name = "multiply";
+ goto numeric;
+ case expr_remainder:
+ binop_name = "remainder";
+ goto numeric;
+ case expr_binary_and:
+ binop_name = "binary-and";
+ goto numeric;
+ case expr_binary_or:
+ binop_name = "binary-or";
+ goto numeric;
+ case expr_binary_xor:
+ binop_name = "binary-xor";
+ numeric:
+ if ((rhs_context != context_numeric) &&
+ (rhs_context != context_any))
+ parse_error(cfile,
+ "expecting numeric expressions");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Now, if we didn't find a binary operator, we're done parsing
+ this subexpression, so combine it with the preceding binary
+ operator and return the result. */
+ if (next_op == expr_none) {
+ tmp = createMap();
+ tmp->skip = ISC_TRUE;
+ mapSet(expr, tmp, binop_name);
+ /* All the binary operators' data union members
+ are the same, so we'll cheat and use the member
+ for the equals operator. */
+ mapSet(tmp, lhs, "left");
+ mapSet(tmp, rhs, "right");
+ return ISC_TRUE;;
+ }
+
+ /* Eat the operator token - we now know it was a binary operator... */
+ skip_token(&val, NULL, cfile);
+
+ /* Now combine the LHS and the RHS using binop. */
+ tmp = createMap();
+ tmp->skip = ISC_TRUE;
+
+ /* Store the LHS and RHS. */
+ mapSet(tmp, lhs, "left");
+ mapSet(tmp, rhs, "right");
+
+ lhs = createMap();
+ mapSet(lhs, tmp, binop_name);
+
+ tmp = NULL;
+ rhs = NULL;
+
+ binop = next_op;
+ goto new_rhs;
+}
+
+/* Escape embedded commas, detected heading and leading space */
+struct string *
+escape_option_string(unsigned len, const char *val,
+ isc_boolean_t *require_binary,
+ isc_boolean_t *modified)
+{
+ struct string *result;
+ struct string *add;
+ unsigned i;
+ char s[2];
+
+ result = allocString();
+ add = allocString();
+ if ((len > 0) && (isspace(val[0]) || isspace(val[len - 1]))) {
+ *require_binary = ISC_TRUE;
+ return result;
+ }
+ for (i = 0; i < len; i++) {
+ if (val[i] == ',') {
+ add->length = 2;
+ add->content = "\\,";
+ *modified = ISC_TRUE;
+ } else {
+ add->length = 1;
+ s[0] = val[i];
+ s[1] = 0;
+ add->content = s;
+ }
+ concatString(result, add);
+ }
+ free(add);
+ return result;
+}
+
+isc_boolean_t
+parse_option_data(struct element *expr,
+ struct parse *cfile,
+ struct option *option)
+{
+ const char *val;
+ const char *fmt;
+ enum dhcp_token token;
+ unsigned len;
+ struct string *data;
+ struct string *saved;
+ struct string *item;
+ struct element *elem;
+ struct comment *comment;
+ isc_boolean_t require_binary = ISC_FALSE;
+ isc_boolean_t canon_bool = ISC_FALSE;
+ isc_boolean_t modified = ISC_FALSE;
+
+ /* Save the initial content */
+ saved = allocString();
+ save_parse_state(cfile);
+ for (;;) {
+ token = next_raw_token(&val, &len, cfile);
+ if ((token == SEMI) || (token == END_OF_FILE))
+ break;
+ item = makeString(len, val);
+ if (token == STRING) {
+ appendString(saved, "\"");
+ concatString(saved, item);
+ appendString(saved, "\"");
+ } else
+ concatString(saved, item);
+ }
+ restore_parse_state(cfile);
+
+ elem = createString(saved);
+ elem->skip = ISC_TRUE;
+ mapSet(expr, elem, "original-data");
+
+ /* Check for binary case */
+
+ fmt = option->format;
+
+ if ((fmt == NULL) || (*fmt == 0))
+ parse_error(cfile, "unknown format for option %s.%s\n",
+ option->space->name, option->name);
+
+ if ((strchr(fmt, 'Y') != NULL) || (strchr(fmt, 'A') != NULL) ||
+ (strchr(fmt, 'E') != NULL) || (strchr(fmt, 'o') != NULL) ||
+ (*fmt == 'X') || (*fmt == 'u'))
+ return parse_option_binary(expr, cfile, option, ISC_FALSE);
+
+ if (strchr(fmt, 'N') != NULL)
+ parse_error(cfile, "unsupported format %s for option %s.%s\n",
+ fmt, option->space->name, option->name);
+
+ data = allocString();
+
+ save_parse_state(cfile);
+ /* Just collect data expecting ISC DHCP and Kea are compatible */
+ do {
+ /* Set fmt one char back for 'a'. */
+ if ((fmt != option->format) && (*fmt == 'a'))
+ fmt -= 1;
+
+ do {
+ if (*fmt == 'a')
+ break;
+ if (data->length != 0)
+ appendString(data, ", ");
+ item = parse_option_token(cfile, fmt, &require_binary,
+ &canon_bool, &modified);
+ if ((*fmt == 'D') && (fmt[1] == 'c'))
+ fmt++;
+ if (require_binary) {
+ restore_parse_state(cfile);
+ return parse_option_binary(expr, cfile, option,
+ item == NULL);
+ }
+ if (item == NULL)
+ parse_error(cfile, "parse_option_data failed");
+ concatString(data, item);
+ fmt++;
+ } while (*fmt != '\0');
+
+ if (*fmt == 'a') {
+ token = peek_token(&val, NULL, cfile);
+ /* Comma means: continue with next element in array */
+ if (token == COMMA) {
+ skip_token(&val, NULL, cfile);
+ continue;
+ }
+ /* no comma: end of array.
+ end of string means: leave the loop */
+ if (fmt[1] == '\0')
+ break;
+ /* 'a' means: go on with next char */
+ if (*fmt == 'a') {
+ fmt++;
+ continue;
+ }
+ }
+ } while (*fmt == 'a');
+
+ if (!modified || eqString(saved, data))
+ mapRemove(expr, "original-data");
+
+ elem = createString(data);
+ if (canon_bool) {
+ comment = createComment("/// canonized booleans to "
+ "lowercase true or false");
+ TAILQ_INSERT_TAIL(&elem->comments, comment);
+ }
+ mapSet(expr, elem, "data");
+
+ return ISC_TRUE;
+}
+
+isc_boolean_t
+parse_option_binary(struct element *expr, struct parse *cfile,
+ struct option *option, isc_boolean_t ambiguous)
+{
+ const char *val;
+ const char *fmt;
+ enum dhcp_token token;
+ struct string *data;
+ struct string *item;
+ struct element *elem;
+ struct comment *comment;
+ const char *g;
+
+ data = allocString();
+ fmt = option->format;
+
+ mapSet(expr, createBool(ISC_FALSE), "csv-format");
+
+ /* Just collect data expecting ISC DHCP and Kea are compatible */
+ do {
+ /* Set fmt to start of format for 'A' and one char back
+ * for 'a'.
+ */
+ if ((fmt != option->format) && (*fmt == 'a'))
+ fmt -= 1;
+ else if (*fmt == 'A')
+ fmt = option->format;
+
+ do {
+ if ((*fmt == 'A') || (*fmt == 'a'))
+ break;
+ if (*fmt == 'o') {
+ /* consume the optional flag */
+ fmt++;
+ continue;
+ }
+
+ if (fmt[1] == 'o') {
+ /*
+ * A value for the current format is
+ * optional - check to see if the next
+ * token is a semi-colon if so we don't
+ * need to parse it and doing so would
+ * consume the semi-colon which our
+ * caller is expecting to parse
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ fmt++;
+ continue;
+ }
+ }
+
+ item = parse_option_token_binary(cfile, fmt);
+ switch (*fmt) {
+ case 'E':
+ g = strchr(fmt, '.');
+ if (g == NULL)
+ parse_error(cfile,
+ "malformed encapsulation "
+ "format (bug!)");
+ fmt = g;
+ break;
+ case 'D':
+ if (fmt[1] == 'c')
+ fmt++;
+ break;
+ case 'N':
+ g = strchr(fmt, '.');
+ if (g == NULL)
+ parse_error(cfile,
+ "malformed enumeration "
+ "format (bug!)");
+ fmt = g;
+ break;
+ }
+ if (item != NULL)
+ concatString(data, item);
+ else if (fmt[1] != 'o')
+ parse_error(cfile, "parse_option_token_binary "
+ "failed");
+ fmt++;
+ } while (*fmt != '\0');
+
+ if ((*fmt == 'A') || (*fmt == 'a')) {
+ token = peek_token(&val, NULL, cfile);
+ /* Comma means: continue with next element in array */
+ if (token == COMMA) {
+ skip_token(&val, NULL, cfile);
+ continue;
+ }
+ /* no comma: end of array.
+ 'A' or end of string means: leave the loop */
+ if ((*fmt == 'A') || (fmt[1] == '\0'))
+ break;
+ /* 'a' means: go on with next char */
+ if (*fmt == 'a') {
+ fmt++;
+ continue;
+ }
+ }
+ } while ((*fmt == 'A') || (*fmt == 'a'));
+
+ elem = mapGet(expr, "original-data");
+ if ((elem != NULL) && eqString(stringValue(elem), data))
+ mapRemove(expr, "original-data");
+
+ elem = createString(data);
+ if (ambiguous) {
+ comment = createComment("/// Please consider to change "
+ "last type in the record to binary");
+ TAILQ_INSERT_TAIL(&elem->comments, comment);
+ comment = createComment("/// Reference Kea #246");
+ TAILQ_INSERT_TAIL(&elem->comments, comment);
+ expr->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(expr, elem, "data");
+
+ return ISC_TRUE;
+}
+
+struct string *
+parse_option_textbin(struct parse *cfile, struct option *option)
+{
+ struct element *expr;
+ struct element *data;
+ const char *fmt;
+
+ expr = createMap();
+ fmt = option->format;
+
+ if ((fmt == NULL) || (*fmt == 0))
+ parse_error(cfile, "unknown format for option %s.%s\n",
+ option->space->name, option->name);
+
+ if (strcmp(fmt, "t") != 0) {
+ if (!parse_option_binary(expr, cfile, option, ISC_FALSE))
+ parse_error(cfile, "can't parse binary option data");
+ data = mapGet(expr, "data");
+ if (data == NULL)
+ parse_error(cfile, "can't get binary option data");
+ if (data->type != ELEMENT_STRING)
+ parse_error(cfile, "option data must be binary");
+ return stringValue(data);
+ }
+
+ if (!parse_option_data(expr, cfile, option))
+ parse_error(cfile, "can't parse text option data");
+ data = mapGet(expr, "data");
+ if (data == NULL)
+ parse_error(cfile, "can't get test option data");
+ if (data->type != ELEMENT_STRING)
+ parse_error(cfile, "option data must be a string");
+ return quote(stringValue(data));
+}
+
+/* option-statement :== identifier DOT identifier <syntax> SEMI
+ | identifier <syntax> SEMI
+
+ Option syntax is handled specially through format strings, so it
+ would be painful to come up with BNF for it. However, it always
+ starts as above and ends in a SEMI. */
+
+isc_boolean_t
+parse_option_statement(struct element *result,
+ struct parse *cfile,
+ struct option *option,
+ enum statement_op op)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct element *expr;
+ struct element *opt_data;
+ struct element *opt_data_list;
+ isc_boolean_t lose;
+ size_t where;
+
+ if (option->space == space_lookup("server"))
+ return parse_config_statement(result, cfile, option, op);
+
+ opt_data = createMap();
+ TAILQ_CONCAT(&opt_data->comments, &cfile->comments);
+ mapSet(opt_data,
+ createString(makeString(-1, option->space->name)), "space");
+ mapSet(opt_data, createString(makeString(-1, option->name)), "name");
+ mapSet(opt_data, createInt(option->code), "code");
+ if (option->status == kea_unknown) {
+ opt_data->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ if (op != supersede_option_statement) {
+ struct string *msg;
+ struct comment *comment;
+
+ msg = makeString(-1, "/// Kea does not support option data ");
+ appendString(msg, "set variants (");
+ switch (op) {
+ case send_option_statement:
+ appendString(msg, "send");
+ break;
+ case supersede_option_statement:
+ appendString(msg, "supersede");
+ break;
+ case default_option_statement:
+ appendString(msg, "default");
+ break;
+ case prepend_option_statement:
+ appendString(msg, "prepend");
+ break;
+ case append_option_statement:
+ appendString(msg, "append");
+ break;
+ default:
+ appendString(msg, "???");
+ break;
+ }
+ appendString(msg, ")");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&opt_data->comments, comment);
+ }
+
+ /* Setting PRL is a standard hack */
+ if ((option->space == space_lookup("dhcp")) &&
+ (option->code == 55)) {
+ struct comment *comment;
+
+ comment = createComment("/// Possible PRL hack");
+ TAILQ_INSERT_TAIL(&opt_data->comments, comment);
+ comment = createComment("/// Consider setting \"always-send\" "
+ "to true when setting data "
+ "for relevant options, cf Kea #250");
+ TAILQ_INSERT_TAIL(&opt_data->comments, comment);
+ }
+
+ /* Setting ORO is a standard hack */
+ if ((option->space == space_lookup("dhcp6")) &&
+ (option->code == 6)) {
+ struct comment *comment;
+
+ comment = createComment("/// Possible ORO hack");
+ TAILQ_INSERT_TAIL(&opt_data->comments, comment);
+ comment = createComment("/// Consider setting \"always-send\" "
+ "to true when setting data "
+ "for relevant options, cf Kea #250");
+ TAILQ_INSERT_TAIL(&opt_data->comments, comment);
+ }
+
+ token = peek_token(&val, NULL, cfile);
+ /* We should keep a list of defined empty options */
+ if ((token == SEMI) && (option->format[0] != 'Z')) {
+ /* Eat the semicolon... */
+ /*
+ * XXXSK: I'm not sure why we should ever get here, but we
+ * do during our startup. This confuses things if
+ * we are parsing a zero-length option, so don't
+ * eat the semicolon token in that case.
+ */
+ skip_token(&val, NULL, cfile);
+ } else if (token == EQUAL) {
+ struct element *data;
+ isc_boolean_t modified = ISC_FALSE;
+
+ /* Eat the equals sign. */
+ skip_token(&val, NULL, cfile);
+
+ /* Parse a data expression and use its value for the data. */
+ expr = createMap();
+ if (!parse_data_expression(expr, cfile, &lose)) {
+ /* In this context, we must have an executable
+ statement, so if we found something else, it's
+ still an error. */
+ if (!lose)
+ parse_error(cfile,
+ "expecting a data expression.");
+ return ISC_FALSE;
+ }
+ /* evaluate the expression */
+ expr = eval_data_expression(expr, &modified);
+
+ mapSet(opt_data, createBool(ISC_FALSE), "csv-format");
+
+ if (expr->type == ELEMENT_STRING) {
+ struct string *s;
+ struct string *r;
+
+ s = stringValue(expr);
+ expr->skip = ISC_TRUE;
+ mapSet(opt_data, expr, "original-data");
+
+ r = makeStringExt(s->length, s->content, 'X');
+ data = createString(r);
+ mapSet(opt_data, data, "data");
+ } else if ((expr->type == ELEMENT_MAP) &&
+ mapContains(expr, "const-data")) {
+ struct element *value;
+ struct string *r;
+
+ value = mapGet(expr, "const-data");
+ if ((value == NULL) || (value->type != ELEMENT_STRING))
+ parse_error(cfile, "can't get const-data");
+ r = hexaValue(value);
+ data = createString(r);
+ mapSet(opt_data, data, "data");
+ } else {
+ opt_data->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(opt_data, expr, "expression");
+ }
+ } else {
+ if (!parse_option_data(opt_data, cfile, option))
+ return ISC_FALSE;
+ }
+
+ parse_semi(cfile);
+
+ if (result != NULL) {
+ opt_data->skip = ISC_TRUE;
+ mapSet(result, opt_data, "option");
+ return ISC_TRUE;
+ }
+
+ for (where = cfile->stack_top; where > 0; --where) {
+ if (cfile->stack[where]->kind != PARAMETER)
+ break;
+ }
+
+ opt_data_list = mapGet(cfile->stack[where], "option-data");
+ if (opt_data_list == NULL) {
+ opt_data_list = createList();
+ mapSet(cfile->stack[where], opt_data_list, "option-data");
+ }
+ if (!opt_data->skip && (option->space->vendor != NULL))
+ add_option_data(option->space->vendor, opt_data_list);
+ listPush(opt_data_list, opt_data);
+
+ return ISC_TRUE;
+}
+
+/* Text version of parse_option_token */
+
+struct string *
+parse_option_token(struct parse *cfile, const char *fmt,
+ isc_boolean_t *require_binary,
+ isc_boolean_t *canon_bool,
+ isc_boolean_t *modified)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned len;
+ struct string *item;
+
+ switch (*fmt) {
+ case 'U':
+ token = next_token(&val, &len, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting identifier.");
+ return makeString(len, val);
+ case 'x':
+ token = peek_token(&val, NULL, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ *require_binary = ISC_TRUE;
+ return NULL;
+ }
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ parse_error(cfile, "expecting string "
+ "or hexadecimal data.");
+ /* STRING can return embedded unexpected characters */
+ return escape_option_string(len, val, require_binary,
+ modified);
+ case 'X':
+ token = peek_token(&val, NULL, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ return parse_hexa(cfile);
+ }
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ parse_error(cfile, "expecting string "
+ "or hexadecimal data.");
+ return makeStringExt(len, val, 'X');
+
+ case 'D': /* Domain list... */
+ *modified = ISC_TRUE;
+ return parse_domain_list(cfile, ISC_FALSE);
+
+ case 'd': /* Domain name... */
+ *modified = ISC_TRUE;
+ item = parse_host_name(cfile);
+ if (item == NULL)
+ parse_error(cfile, "not a valid domain name.");
+ return item;
+
+ case 't': /* Text string... */
+ token = next_token(&val, &len, cfile);
+ if (token != STRING && !is_identifier(token))
+ parse_error(cfile, "expecting string.");
+ /* STRING can return embedded unexpected characters */
+ return escape_option_string(len, val, require_binary,
+ modified);
+
+ case 'I': /* IP address or hostname. */
+ *modified = ISC_TRUE;
+ return parse_ip_addr_or_hostname(cfile, ISC_FALSE);
+
+ case '6': /* IPv6 address. */
+ *modified = ISC_TRUE;
+ return parse_ip6_addr_txt(cfile);
+
+ case 'T': /* Lease interval. */
+ token = next_token(&val, NULL, cfile);
+ if (token == INFINITE)
+ return makeString(-1, "0xffffffff");
+ goto check_number;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l':
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token(&val, NULL, cfile);
+ check_number:
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ parse_error(cfile, "expecting number.");
+ /* check octal */
+ if (val[0] == '0' && isascii(val[1]) && isdigit(val[1]))
+ *require_binary = ISC_TRUE;
+ return makeString(-1, val);
+
+ case 'f': /* Boolean flag. */
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting identifier.");
+ if (strcasecmp(val, "true") == 0)
+ return makeString(-1, "true");
+ if (strcasecmp(val, "on") == 0) {
+ *canon_bool = ISC_TRUE;
+ *modified = ISC_TRUE;
+ return makeString(-1, "true");
+ }
+ if (strcasecmp(val, "false") == 0)
+ return makeString(-1, "false");
+ if (strcasecmp(val, "off") == 0) {
+ *canon_bool = ISC_TRUE;
+ *modified = ISC_TRUE;
+ return makeString(-1, "false");
+ }
+ parse_error(cfile, "expecting boolean.");
+
+ case 'Z': /* Zero-length option. */
+ token = peek_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+ return allocString();
+
+ default:
+ parse_error(cfile, "Bad format '%c' in parse_option_token.",
+ *fmt);
+ }
+}
+
+/* Binary (aka hexadecimal) version of parse_option_token */
+
+struct string *
+parse_option_token_binary(struct parse *cfile, const char *fmt)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned len;
+ struct string *item;
+ uint8_t buf[4];
+
+ switch (*fmt) {
+ case 'U':
+ token = next_token(&val, &len, cfile);
+ if (!is_identifier(token)) {
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "expecting identifier.");
+ }
+ return makeStringExt(len, val, 'X');
+ case 'E':
+ case 'X':
+ case 'x':
+ case 'u':
+ token = peek_token(&val, NULL, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER)
+ return parse_hexa(cfile);
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "expecting string "
+ "or hexadecimal data.");
+ }
+ return makeStringExt(len, val, 'X');
+
+ case 'D': /* Domain list... */
+ item = parse_domain_list(cfile, ISC_TRUE);
+ if (item == NULL) {
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "parse_domain_list failed");
+ }
+ return NULL;
+
+ case 'd': /* Domain name... */
+ item = parse_host_name(cfile);
+ if (item == NULL)
+ parse_error(cfile, "not a valid domain name.");
+ item = makeStringExt(item->length, item->content, 'd');
+ if (item == NULL)
+ parse_error(cfile, "too long domain name.");
+ return makeStringExt(item->length, item->content, 'X');
+
+ case 't': /* Text string... */
+ token = next_token(&val, &len, cfile);
+ if (token != STRING && !is_identifier(token)) {
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "expecting string.");
+ }
+ return makeStringExt(len, val, 'X');
+
+ case 'I': /* IP address or hostname. */
+ item = parse_ip_addr_or_hostname(cfile, ISC_FALSE);
+ return makeStringExt(item->length, item->content, 'i');
+
+ case '6': /* IPv6 address. */
+ item = parse_ip6_addr(cfile);
+ return makeStringExt(item->length, item->content, 'X');
+
+ case 'T': /* Lease interval. */
+ token = next_token(&val, NULL, cfile);
+ if (token == INFINITE)
+ return makeString(-1, "ffffffff");
+ goto check_number;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token(&val, NULL, cfile);
+ check_number:
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME)) {
+ need_number:
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "expecting number.");
+ }
+ convert_num(cfile, buf, val, 0, 32);
+ return makeStringExt(4, (const char *)buf, 'X');
+
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token(&val, NULL, cfile);
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ goto need_number;
+ convert_num(cfile, buf, val, 0, 16);
+ return makeStringExt(2, (const char *)buf, 'X');
+
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token(&val, NULL, cfile);
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ goto need_number;
+ convert_num(cfile, buf, val, 0, 8);
+ return makeStringExt(1, (const char *)buf, 'X');
+
+ case 'f': /* Boolean flag. */
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token)) {
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "expecting identifier.");
+ }
+ if ((strcasecmp(val, "true") == 0) ||
+ (strcasecmp(val, "on") == 0))
+ return makeString(-1, "01");
+ if ((strcasecmp(val, "false") == 0) ||
+ (strcasecmp(val, "off") == 0))
+ return makeString(-1, "00");
+ if (strcasecmp(val, "ignore") == 0)
+ return makeString(-1, "02");
+ if (fmt[1] == 'o')
+ return NULL;
+ parse_error(cfile, "expecting boolean.");
+
+ case 'Z': /* Zero-length option. */
+ token = peek_token(&val, NULL, cfile);
+ if (token != SEMI)
+ parse_error(cfile, "semicolon expected.");
+ return allocString();
+
+ default:
+ parse_error(cfile, "Bad format '%c' in parse_option_token.",
+ *fmt);
+ }
+}
+
+struct string *
+parse_domain_list(struct parse *cfile, isc_boolean_t binary)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned len;
+ struct string *result;
+
+ token = SEMI;
+ result = allocString();
+
+ do {
+ /* Consume the COMMA token if peeked. */
+ if (token == COMMA) {
+ skip_token(&val, NULL, cfile);
+ if (!binary)
+ appendString(result, ", ");
+ }
+
+ /* Get next (or first) value. */
+ token = next_token(&val, &len, cfile);
+
+ if (token != STRING)
+ parse_error(cfile, "Expecting a domain string.");
+
+ /* Just pack the names in series into the buffer. */
+ if (binary) {
+ struct string *item;
+
+ item = makeStringExt(len, val, 'd');
+ if (item == NULL)
+ parse_error(cfile, "not a valid domain name.");
+ item = makeStringExt(item->length, item->content, 'X');
+ concatString(result, item);
+ } else
+ concatString(result, makeString(len, val));
+
+ token = peek_token(&val, NULL, cfile);
+ } while (token == COMMA);
+
+ return result;
+}
+
+/* Specialized version of parse_option_data working on config
+ * options which are scalar (I6LSBtTfUXdNxxx.) only. */
+
+isc_boolean_t
+parse_config_data(struct element *expr,
+ struct parse *cfile,
+ struct option *option)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct string *data;
+ struct element *elem;
+ unsigned len;
+ uint32_t u32;
+ uint16_t u16;
+ uint8_t u8;
+
+ token = peek_token(&val, NULL, cfile);
+
+ if (token == END_OF_FILE)
+ parse_error(cfile, "unexpected end of file");
+ if (token == SEMI)
+ parse_error(cfile, "empty config option");
+ if (token == COMMA)
+ parse_error(cfile, "multiple value config option");
+
+ /* from parse_option_token */
+
+ switch (option->format[0]) {
+ case 'U': /* universe */
+ token = next_token(&val, &len, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting identifier.");
+ elem = createString(makeString(len, val));
+ break;
+
+ case 'X': /* string or binary */
+ token = next_token(&val, &len, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER)
+ data = parse_cshl(cfile);
+ else if (token == STRING)
+ data = makeString(len, val);
+ else
+ parse_error(cfile, "expecting string "
+ "or hexadecimal data.");
+ elem = createString(data);
+ break;
+
+ case 'd': /* FQDN */
+ data = parse_host_name(cfile);
+ if (data == NULL)
+ parse_error(cfile, "not a valid domain name.");
+ elem = createString(data);
+ break;
+
+ case 't': /* text */
+ token = next_token(&val, &len, cfile);
+ elem = createString(makeString(len, val));
+ break;
+
+ case 'N': /* enumeration */
+ token = next_token(&val, &len, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "identifier expected");
+ elem = createString(makeString(len, val));
+ break;
+
+ case 'I': /* IP address or hostname. */
+ data = parse_ip_addr_or_hostname(cfile, ISC_FALSE);
+ if (data == NULL)
+ parse_error(cfile, "expecting IP address of hostname");
+ elem = createString(data);
+ break;
+
+ case '6': /* IPv6 address. */
+ data = parse_ip6_addr_txt(cfile);
+ if (data == NULL)
+ parse_error(cfile, "expecting IPv6 address");
+ elem = createString(data);
+ break;
+
+ case 'T': /* Lease interval. */
+ token = next_token(&val, NULL, cfile);
+ if (token != INFINITE)
+ goto check_number;
+ elem = createInt(-1);
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ token = next_token(&val, NULL, cfile);
+ check_number:
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ parse_error(cfile, "expecting number.");
+ convert_num(cfile, (unsigned char *)&u32, val, 0, 32);
+ elem = createInt(ntohl(u32));
+ break;
+
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token(&val, NULL, cfile);
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ parse_error(cfile, "expecting number.");
+ convert_num(cfile, (unsigned char *)&u16, val, 0, 16);
+ elem = createInt(ntohs(u16));
+ break;
+
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token(&val, NULL, cfile);
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ parse_error(cfile, "expecting number.");
+ convert_num(cfile, (unsigned char *)&u8, val, 0, 8);
+ elem = createInt(ntohs(u8));
+ break;
+
+ case 'f':
+ token = next_token(&val, NULL, cfile);
+ if (!is_identifier(token))
+ parse_error(cfile, "expecting boolean.");
+ if ((strcasecmp(val, "true") == 0) ||
+ (strcasecmp(val, "on") == 0))
+ elem = createBool(ISC_TRUE);
+ else if ((strcasecmp(val, "false") == 0) ||
+ (strcasecmp(val, "off") == 0))
+ elem = createBool(ISC_FALSE);
+ else if (strcasecmp(val, "ignore") == 0) {
+ elem = createNull();
+ elem->skip = ISC_TRUE;
+ } else
+ parse_error(cfile, "expecting boolean.");
+ break;
+
+ default:
+ parse_error(cfile, "Bad format '%c' in parse_config_data.",
+ option->format[0]);
+ }
+
+ mapSet(expr, elem, "value");
+
+ return ISC_TRUE;
+}
+
+/* Specialized version of parse_option_statement for config options */
+
+isc_boolean_t
+parse_config_statement(struct element *result,
+ struct parse *cfile,
+ struct option *option,
+ enum statement_op op)
+{
+ const char *val;
+ enum dhcp_token token;
+ struct comments *comments;
+ struct element *expr;
+ struct element *config;
+ struct element *config_list;
+ isc_boolean_t lose;
+ size_t where;
+
+ config = createMap();
+ TAILQ_CONCAT(&config->comments, &cfile->comments);
+ comments = get_config_comments(option->code);
+ TAILQ_CONCAT(&config->comments, comments);
+ mapSet(config, createString(makeString(-1, option->name)), "name");
+ mapSet(config, createInt(option->code), "code");
+ if (option->status == kea_unknown) {
+ config->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ if (op != supersede_option_statement) {
+ struct string *msg;
+ struct comment *comment;
+
+ msg = makeString(-1, "/// Kea does not support option data ");
+ appendString(msg, "set variants (");
+ switch (op) {
+ case send_option_statement:
+ appendString(msg, "send");
+ break;
+ case supersede_option_statement:
+ appendString(msg, "supersede");
+ break;
+ case default_option_statement:
+ appendString(msg, "default");
+ break;
+ case prepend_option_statement:
+ appendString(msg, "prepend");
+ break;
+ case append_option_statement:
+ appendString(msg, "append");
+ break;
+ default:
+ appendString(msg, "???");
+ break;
+ }
+ appendString(msg, ")");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&config->comments, comment);
+ }
+
+ token = peek_token(&val, NULL, cfile);
+ /* We should keep a list of defined empty options */
+ if ((token == SEMI) && (option->format[0] != 'Z')) {
+ /* Eat the semicolon... */
+ /*
+ * XXXSK: I'm not sure why we should ever get here, but we
+ * do during our startup. This confuses things if
+ * we are parsing a zero-length option, so don't
+ * eat the semicolon token in that case.
+ */
+ skip_token(&val, NULL, cfile);
+ } else if (token == EQUAL) {
+ /* Eat the equals sign. */
+ skip_token(&val, NULL, cfile);
+
+ /* Parse a data expression and use its value for the data. */
+ expr = createMap();
+ if (!parse_data_expression(expr, cfile, &lose)) {
+ /* In this context, we must have an executable
+ statement, so if we found something else, it's
+ still an error. */
+ if (!lose)
+ parse_error(cfile,
+ "expecting a data expression.");
+ return ISC_FALSE;
+ }
+ mapSet(config, expr, "value");
+ } else {
+ if (!parse_config_data(config, cfile, option))
+ return ISC_FALSE;
+ }
+
+ parse_semi(cfile);
+
+ if (result != NULL) {
+ config->skip = ISC_TRUE;
+ mapSet(result, config, "config");
+ return ISC_TRUE;
+ }
+
+ for (where = cfile->stack_top; where > 0; --where) {
+ if ((cfile->stack[where]->kind == PARAMETER) ||
+ (cfile->stack[where]->kind == POOL_DECL))
+ continue;
+ break;
+ }
+
+ if (option->status != special) {
+ config_list = mapGet(cfile->stack[where], "config");
+ if (config_list == NULL) {
+ config_list = createList();
+ config_list->skip = ISC_TRUE;
+ mapSet(cfile->stack[where], config_list, "config");
+ }
+ listPush(config_list, config);
+ return ISC_TRUE;
+ }
+
+ /* deal with all special cases */
+
+ switch (option->code) {
+ case 1: /* default-lease-time */
+ config_def_valid_lifetime(config, cfile);
+ break;
+ case 2: /* max-lease-time */
+ config_max_valid_lifetime(config, cfile);
+ break;
+ case 3: /* min-lease-time */
+ config_min_valid_lifetime(config, cfile);
+ break;
+ case 15: /* filename */
+ config_file(config, cfile);
+ break;
+ case 16: /* server-name */
+ config_sname(config, cfile);
+ break;
+ case 17: /* next-server */
+ config_next_server(config, cfile);
+ break;
+ case 18: /* authoritative */
+ parse_error(cfile, "authoritative is a statement, "
+ "here it is used as a config option");
+ case 19: /* vendor-option-space */
+ config_vendor_option_space(config, cfile);
+ break;
+ case 21: /* site-option-space */
+ config_site_option_space(config, cfile);
+ break;
+ case 23: /* ddns-domainname */
+ config_qualifying_suffix(config, cfile);
+ break;
+ case 30: /* ddns-updates */
+ config_enable_updates(config, cfile);
+ break;
+ case 39: /* ddns-update-style */
+ config_ddns_update_style(config, cfile);
+ break;
+ case 53: /* preferred-lifetime */
+ config_preferred_lifetime(config, cfile);
+ break;
+ case 82: /* ignore-client-uids */
+ config_match_client_id(config, cfile);
+ break;
+ case 85: /* echo-client-id */
+ config_echo_client_id(config, cfile);
+ break;
+ default:
+ parse_error(cfile, "unsupported config option %s (%u)",
+ option->name, option->code);
+ }
+
+ return ISC_TRUE;
+}
+
+static void
+config_def_valid_lifetime(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t pop_from_pool = ISC_FALSE;
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == ROOT_GROUP) ||
+ (kind == SHARED_NET_DECL) ||
+ (kind == SUBNET_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == POOL_DECL) {
+ pop_from_pool = ISC_TRUE;
+ continue;
+ }
+ comment = createComment("/// default-valid-lifetime in "
+ "unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ break;
+ }
+ if (pop_from_pool) {
+ comment= createComment("/// default-valid-lifetime moved from "
+ "an internal pool scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ }
+ mapSet(cfile->stack[scope], value, "valid-lifetime");
+}
+
+static void
+config_min_valid_lifetime(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t pop_from_pool = ISC_FALSE;
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == ROOT_GROUP) ||
+ (kind == SHARED_NET_DECL) ||
+ (kind == SUBNET_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == POOL_DECL) {
+ pop_from_pool = ISC_TRUE;
+ continue;
+ }
+ comment = createComment("/// min-valid-lifetime in "
+ "unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ break;
+ }
+ if (pop_from_pool) {
+ comment= createComment("/// min-valid-lifetime moved from "
+ "an internal pool scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ }
+ mapSet(cfile->stack[scope], value, "min-valid-lifetime");
+}
+
+static void
+config_max_valid_lifetime(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t pop_from_pool = ISC_FALSE;
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == ROOT_GROUP) ||
+ (kind == SHARED_NET_DECL) ||
+ (kind == SUBNET_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == POOL_DECL) {
+ pop_from_pool = ISC_TRUE;
+ continue;
+ }
+ comment = createComment("/// max-valid-lifetime in "
+ "unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ break;
+ }
+ if (pop_from_pool) {
+ comment= createComment("/// max-valid-lifetime moved from "
+ "an internal pool scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ }
+ mapSet(cfile->stack[scope], value, "max-valid-lifetime");
+}
+
+static void
+config_file(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t popped = ISC_FALSE;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "boot-file-name is DHCPv4 only");
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == HOST_DECL) ||
+ (kind == CLASS_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == ROOT_GROUP) {
+ popped = ISC_TRUE;
+ break;
+ }
+ }
+ if (popped) {
+ comment = createComment("/// boot-file-name was defined in "
+ "an unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(cfile->stack[scope], value, "boot-file-name");
+}
+
+static void
+config_sname(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t popped = ISC_FALSE;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "server-hostname is DHCPv4 only");
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == HOST_DECL) ||
+ (kind == CLASS_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == ROOT_GROUP) {
+ popped = ISC_TRUE;
+ break;
+ }
+ }
+ if (popped) {
+ comment = createComment("/// server-hostname was defined in "
+ "an unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(cfile->stack[scope], value, "server-hostname");
+}
+
+static void
+config_next_server(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t popped = ISC_FALSE;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "next-server is DHCPv4 only");
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == ROOT_GROUP) ||
+ (kind == HOST_DECL) ||
+ (kind == CLASS_DECL) ||
+ (kind == SUBNET_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ popped = ISC_TRUE;
+ }
+ if (popped) {
+ comment = createComment("/// next-server moved from "
+ "an internal unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ }
+ mapSet(cfile->stack[scope], value, "next-server");
+}
+
+static void
+config_vendor_option_space(struct element *config, struct parse *cfile)
+{
+ struct element *defs;
+ struct element *def;
+ struct element *opts;
+ struct element *opt;
+ struct element *space;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "vendor-option-space is DHCPv4 only");
+
+ /* create local option definition */
+ def = createMap();
+ mapSet(def,
+ createString(makeString(-1, "vendor-encapsulated-options")),
+ "name");
+ mapSet(def, createInt(43), "code");
+ mapSet(def, createString(makeString(-1, "empty")), "type");
+ space = mapGet(config, "value");
+ if (space == NULL)
+ parse_error(cfile, "vendor-option-space has no value");
+ if (space->type != ELEMENT_STRING)
+ parse_error(cfile,
+ "vendor-option-space value is not a string");
+ mapSet(def, space, "encapsulate");
+
+ /* add it */
+ defs = mapGet(cfile->stack[cfile->stack_top], "option-def");
+ if (defs == NULL) {
+ defs = createList();
+ mapSet(cfile->stack[cfile->stack_top], defs, "option-def");
+ } else {
+ size_t i;
+
+ /* Look for duplicate */
+ for (i = 0; i < listSize(defs); i++) {
+ struct element *item;
+ struct element *code;
+ struct element *old;
+
+ item = listGet(defs, i);
+ if ((item == NULL) || (item->type != ELEMENT_MAP))
+ continue;
+ code = mapGet(item, "code");
+ if ((code == NULL) ||
+ (code->type != ELEMENT_INTEGER) ||
+ (intValue(code) != 43))
+ continue;
+ old = mapGet(item, "encapsulate");
+ if ((old == NULL) || (old->type != ELEMENT_STRING))
+ continue;
+ if (eqString(stringValue(space), stringValue(old)))
+ return;
+ }
+ }
+ listPush(defs, def);
+
+ /* add a data too assuming at least one suboption exists */
+ opt = createMap();
+ mapSet(opt,
+ createString(makeString(-1, "vendor-encapsulated-options")),
+ "name");
+ mapSet(opt, createInt(43), "code");
+ opts = mapGet(cfile->stack[cfile->stack_top], "option-data");
+ if (opts == NULL) {
+ opts = createList();
+ mapSet(cfile->stack[cfile->stack_top], opts, "option-data");
+ }
+ listPush(opts, opt);
+}
+
+static void
+config_site_option_space(struct element *config, struct parse *cfile)
+{
+ struct element *defs;
+ struct element *space;
+ struct string *msg;
+ struct comment *comment;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "site-option-space is DHCPv4 only");
+
+ space = mapGet(config, "value");
+ if (space == NULL)
+ parse_error(cfile, "site-option-space has no value");
+ if (space->type != ELEMENT_STRING)
+ parse_error(cfile, "site-option-space value is not a string");
+
+ defs = mapGet(cfile->stack[cfile->stack_top], "option-def");
+ if (defs == NULL) {
+ defs = createList();
+ mapSet(cfile->stack[cfile->stack_top], defs, "option-def");
+ }
+
+ msg = makeString(-1, "/// site-option-space '");
+ concatString(msg, stringValue(space));
+ appendString(msg, "'");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&defs->comments, comment);
+ msg = makeString(-1, "/// Please to move private (code 224..254)");
+ appendString(msg, " option definitions from '");
+ concatString(msg, stringValue(space));
+ appendString(msg, "' to 'dhcp4' space");
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&defs->comments, comment);
+}
+
+static struct element *
+default_qualifying_suffix(void)
+{
+ struct element *qs;
+ struct comment *comment;
+
+ qs = createString(allocString());
+ comment = createComment("/// Unspecified ddns-domainname (default "
+ "domain-name option value)");
+ TAILQ_INSERT_TAIL(&qs->comments, comment);
+ comment = createComment("/// Kea requires a qualifying-suffix");
+ TAILQ_INSERT_TAIL(&qs->comments, comment);
+ comment = createComment("/// Initialized to \"\": please put a value");
+ TAILQ_INSERT_TAIL(&qs->comments, comment);
+ return qs;
+}
+
+static void
+config_qualifying_suffix(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ size_t scope;
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope)
+ if ((cfile->stack[scope]->kind != PARAMETER) ||
+ (cfile->stack[scope]->kind != POOL_DECL))
+ break;
+ if (cfile->stack[scope]->kind != ROOT_GROUP) {
+ struct comment *comment;
+
+ comment = createComment("/// Only global qualifying-suffix "
+ "is supported");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(cfile->stack[scope], value, "qualifying-suffix");
+ } else {
+ struct element *d2;
+
+ d2 = mapGet(cfile->stack[1], "dhcp-ddns");
+ if (d2 == NULL) {
+ d2 = createMap();
+ mapSet(d2, createBool(ISC_FALSE), "enable-updates");
+ mapSet(cfile->stack[1], d2, "dhcp-ddns");
+ } else if (mapContains(d2, "qualifying-suffix"))
+ mapRemove(d2, "qualifying-suffix");
+ mapSet(d2, value, "qualifying-suffix");
+ }
+}
+
+static void
+config_enable_updates(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ size_t scope;
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope)
+ if ((cfile->stack[scope]->kind != PARAMETER) ||
+ (cfile->stack[scope]->kind != POOL_DECL))
+ break;
+ if (cfile->stack[scope]->kind != ROOT_GROUP) {
+ struct comment *comment;
+
+ comment = createComment("/// Only global enable-updates "
+ "is supported");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(cfile->stack[scope], value, "enable-updates");
+ } else {
+ struct element *d2;
+
+ d2 = mapGet(cfile->stack[1], "dhcp-ddns");
+ if (d2 == NULL) {
+ d2 = createMap();
+ mapSet(cfile->stack[1], d2, "dhcp-ddns");
+ if (boolValue(value)) {
+ struct element *qs;
+
+ qs = default_qualifying_suffix();
+ mapSet(d2, qs, "qualifying-suffix");
+ }
+ } else if (mapContains(d2, "enable-updates"))
+ mapRemove(d2, "enable-updates");
+ mapSet(d2, value, "enable-updates");
+ }
+}
+
+static void
+config_ddns_update_style(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ isc_boolean_t enable = ISC_TRUE;
+ size_t scope;
+
+ value = mapGet(config, "value");
+ if (strcmp(stringValue(value)->content, "standard") == 0)
+ enable = ISC_TRUE;
+ else if (strcmp(stringValue(value)->content, "none") == 0)
+ enable = ISC_FALSE;
+ else {
+ struct string *msg;
+ struct comment *comment;
+
+ for (scope = cfile->stack_top; scope > 0; --scope)
+ if ((cfile->stack[scope]->kind != PARAMETER) ||
+ (cfile->stack[scope]->kind != POOL_DECL))
+ break;
+ msg = makeString(-1, "/// Unsupported ddns-update-style ");
+ concatString(msg, stringValue(value));
+ comment = createComment(msg->content);
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(cfile->stack[scope], value, "ddns-update-style");
+ }
+
+ for (scope = cfile->stack_top; scope > 0; --scope)
+ if ((cfile->stack[scope]->kind != PARAMETER) ||
+ (cfile->stack[scope]->kind != POOL_DECL))
+ break;
+ if (cfile->stack[scope]->kind != ROOT_GROUP) {
+ struct comment *comment;
+
+ comment = createComment("/// Only global ddns-update-style "
+ "is supported");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ mapSet(cfile->stack[scope], value, "ddns-update-style");
+ } else {
+ struct element *d2;
+
+ /* map ddns-update-style into enable-updates */
+ value = createBool(enable);
+ d2 = mapGet(cfile->stack[1], "dhcp-ddns");
+ if (d2 == NULL) {
+ d2 = createMap();
+ mapSet(cfile->stack[1], d2, "dhcp-ddns");
+ if (boolValue(value)) {
+ struct element *qs;
+
+ qs = default_qualifying_suffix();
+ mapSet(d2, qs, "qualifying-suffix");
+ }
+ } else if (mapContains(d2, "enable-updates"))
+ mapRemove(d2, "enable-updates");
+ mapSet(d2, value, "enable-updates");
+ }
+}
+
+static void
+config_preferred_lifetime(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct element *child;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t pop_from_pool = ISC_FALSE;
+
+ if (local_family != AF_INET6)
+ parse_error(cfile, "preferred-lifetime is DHCPv6 only");
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == ROOT_GROUP) ||
+ (kind == SHARED_NET_DECL) ||
+ (kind == SUBNET_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == POOL_DECL) {
+ pop_from_pool = ISC_TRUE;
+ continue;
+ }
+ comment = createComment("/// preferred-lifetime in "
+ "unsupported scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ break;
+ }
+ if (pop_from_pool) {
+ comment = createComment("/// preferred-lifetime moved from "
+ "an internal pool scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ /* if there is another specified value and we are
+ * enough lucky to have already got it... */
+ if (mapContains(cfile->stack[scope], "preferred-lifetime")) {
+ comment = createComment("/// Avoid to overwrite "
+ "current value...");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ }
+ }
+ mapSet(cfile->stack[scope], value, "preferred-lifetime");
+ /* derive T1 and T2 */
+ child = createInt(intValue(value) / 2);
+ child->skip = value->skip;
+ mapSet(cfile->stack[scope], child, "renew-timer");
+ child = createInt(intValue(value) * 4 / 5);
+ child->skip = value->skip;
+ mapSet(cfile->stack[scope], child, "rebind-timer");
+}
+
+static void
+config_match_client_id(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+ isc_boolean_t pop_from_pool = ISC_FALSE;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "ignore-client-uids is DHCPv4 only");
+
+ value = mapGet(config, "value");
+ /* match-client-id is !ignore-client-uids */
+ value = createBool(!boolValue(value));
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if ((kind == ROOT_GROUP) ||
+ (kind == SHARED_NET_DECL) ||
+ (kind == SUBNET_DECL) ||
+ (kind == GROUP_DECL))
+ break;
+ if (kind == POOL_DECL) {
+ pop_from_pool = ISC_TRUE;
+ continue;
+ }
+ comment = createComment("/// match-client-id in unsupported "
+ "scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ break;
+ }
+ if (pop_from_pool) {
+ comment= createComment("/// match-client-id moved from "
+ "an internal pool scope");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ }
+ mapSet(cfile->stack[scope], value, "match-client-id");
+}
+
+static void
+config_echo_client_id(struct element *config, struct parse *cfile)
+{
+ struct element *value;
+ struct comment *comment;
+ size_t scope;
+
+ if (local_family != AF_INET)
+ parse_error(cfile, "echo-client-id is DHCPv4 only");
+
+ value = mapGet(config, "value");
+
+ for (scope = cfile->stack_top; scope > 0; --scope) {
+ int kind = cfile->stack[scope]->kind;
+
+ if (kind == PARAMETER)
+ continue;
+ if (kind == ROOT_GROUP)
+ break;
+ comment = createComment("/// Only global echo-client-id "
+ "is supported");
+ TAILQ_INSERT_TAIL(&value->comments, comment);
+ value->skip = ISC_TRUE;
+ cfile->issue_counter++;
+ }
+ mapSet(cfile->stack[scope], value, "echo-client-id");
+}
+
+/* parse_error moved to keama.c */
+
+/* From omapi/convert.c */
+/*
+static uint32_t
+getULong(const unsigned char *buf)
+{
+ uint32_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(uint32_t));
+ return ntohl(ibuf);
+}
+
+static int32_t
+getLong(const unsigned char *buf)
+{
+ int32_t ibuf;
+
+ memcpy(&ibuf, buf, sizeof(int32_t));
+ return ntohl(ibuf);
+}
+
+static uint32_t
+getUShort(const unsigned char *buf)
+{
+ unsigned short ibuf;
+
+ memcpy(&ibuf, buf, sizeof(uint16_t));
+ return ntohs(ibuf);
+}
+
+static int32_t
+getShort(const unsigned char *buf)
+{
+ short ibuf;
+
+ memcpy(&ibuf, buf, sizeof(int16_t));
+ return ntohs(ibuf);
+}
+
+static uint32_t
+getUChar(const unsigned char *obuf)
+{
+ return obuf[0];
+}
+*/
+static void
+putULong(unsigned char *obuf, uint32_t val)
+{
+ uint32_t tmp = htonl(val);
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+static void
+putLong(unsigned char *obuf, int32_t val)
+{
+ int32_t tmp = htonl(val);
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+static void
+putUShort(unsigned char *obuf, uint32_t val)
+{
+ uint16_t tmp = htons(val);
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+
+static void
+putShort(unsigned char *obuf, int32_t val)
+{
+ int16_t tmp = htons(val);
+ memcpy(obuf, &tmp, sizeof(tmp));
+}
+/*
+static void
+putUChar(unsigned char *obuf, uint32_t val)
+{
+ *obuf = val;
+}
+*/
+/* From common/tree.c */
+
+isc_boolean_t
+is_boolean_expression(struct element *expr)
+{
+ if (expr->type == ELEMENT_BOOLEAN)
+ return ISC_TRUE;
+ if (expr->type != ELEMENT_MAP)
+ return ISC_FALSE;
+ return (mapContains(expr, "check") ||
+ mapContains(expr, "exists") ||
+ mapContains(expr, "variable-exists") ||
+ mapContains(expr, "equal") ||
+ mapContains(expr, "not-equal") ||
+ mapContains(expr, "regex-match") ||
+ mapContains(expr, "iregex-match") ||
+ mapContains(expr, "and") ||
+ mapContains(expr, "or") ||
+ mapContains(expr, "not") ||
+ mapContains(expr, "known") ||
+ mapContains(expr, "static"));
+}
+
+isc_boolean_t
+is_data_expression(struct element *expr)
+{
+ if (expr->type == ELEMENT_STRING)
+ return ISC_TRUE;
+ if (expr->type != ELEMENT_MAP)
+ return ISC_FALSE;
+ return (mapContains(expr, "substring") ||
+ mapContains(expr, "suffix") ||
+ mapContains(expr, "lowercase") ||
+ mapContains(expr, "uppercase") ||
+ mapContains(expr, "option") ||
+ mapContains(expr, "hardware") ||
+ mapContains(expr, "hw-type") ||
+ mapContains(expr, "hw-address") ||
+ mapContains(expr, "const-data") ||
+ mapContains(expr, "packet") ||
+ mapContains(expr, "concat") ||
+ mapContains(expr, "encapsulate") ||
+ mapContains(expr, "encode-int8") ||
+ mapContains(expr, "encode-int16") ||
+ mapContains(expr, "encode-int32") ||
+ mapContains(expr, "gethostbyname") ||
+ mapContains(expr, "binary-to-ascii") ||
+ mapContains(expr, "filename") ||
+ mapContains(expr, "server-name") ||
+ mapContains(expr, "reverse") ||
+ mapContains(expr, "pick-first-value") ||
+ mapContains(expr, "host-decl-name") ||
+ mapContains(expr, "leased-address") ||
+ mapContains(expr, "config-option") ||
+ mapContains(expr, "null") ||
+ mapContains(expr, "gethostname") ||
+ mapContains(expr, "v6relay"));
+}
+
+isc_boolean_t
+is_numeric_expression(struct element *expr)
+{
+ if (expr->type == ELEMENT_INTEGER)
+ return ISC_TRUE;
+ if (expr->type != ELEMENT_MAP)
+ return ISC_FALSE;
+ return (mapContains(expr, "extract-int8") ||
+ mapContains(expr, "extract-int16") ||
+ mapContains(expr, "extract-int32") ||
+ mapContains(expr, "const-int") ||
+ mapContains(expr, "lease-time") ||
+ mapContains(expr, "add") ||
+ mapContains(expr, "subtract") ||
+ mapContains(expr, "multiply") ||
+ mapContains(expr, "divide") ||
+ mapContains(expr, "remainder") ||
+ mapContains(expr, "binary-and") ||
+ mapContains(expr, "binary-or") ||
+ mapContains(expr, "binary-xor") ||
+ mapContains(expr, "client-state"));
+}
+/*
+static isc_boolean_t
+is_compound_expression(struct element *expr)
+{
+ return (mapContains(expr, "substring") ||
+ mapContains(expr, "suffix") ||
+ mapContains(expr, "option") ||
+ mapContains(expr, "concat") ||
+ mapContains(expr, "encode-int8") ||
+ mapContains(expr, "encode-int16") ||
+ mapContains(expr, "encode-int32") ||
+ mapContains(expr, "binary-to-ascii") ||
+ mapContains(expr, "reverse") ||
+ mapContains(expr, "pick-first-value") ||
+ mapContains(expr, "config-option") ||
+ mapContains(expr, "extract-int8") ||
+ mapContains(expr, "extract-int16") ||
+ mapContains(expr, "extract-int32") ||
+ mapContains(expr, "v6relay"));
+}
+*/
+static enum expression_context
+op_context(enum expr_op op)
+{
+ switch (op) {
+/* XXX Why aren't these specific? */
+ case expr_none:
+ case expr_match:
+ case expr_static:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_not:
+ case expr_option:
+ case expr_hardware:
+ case expr_hw_type:
+ case expr_hw_address:
+ case expr_packet:
+ case expr_const_data:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_variable_exists:
+ case expr_known:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_null:
+ case expr_variable_reference:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_dns_transaction:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ case expr_gethostname:
+ case expr_v6relay:
+ case expr_concat_dclist:
+ return context_any;
+
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ return context_data;
+
+ case expr_and:
+ return context_boolean;
+
+ case expr_or:
+ return context_boolean;
+
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ return context_numeric;
+ }
+ return context_any;
+}
+
+static int
+op_val(enum expr_op op)
+{
+ switch (op) {
+ case expr_none:
+ case expr_match:
+ case expr_static:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_not:
+ case expr_option:
+ case expr_hardware:
+ case expr_hw_type:
+ case expr_hw_address:
+ case expr_packet:
+#ifdef keep_expr_const_data_precedence
+ case expr_const_data:
+#endif
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_variable_exists:
+ case expr_known:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_null:
+ case expr_variable_reference:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ /* XXXDPN: Need to assign sane precedences to these. */
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ case expr_gethostname:
+ case expr_v6relay:
+ case expr_concat_dclist:
+ return 100;
+
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ return 4;
+
+ case expr_or:
+ case expr_and:
+ return 3;
+
+ case expr_add:
+ case expr_subtract:
+ return 2;
+
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ return 1;
+#ifndef keep_expr_const_data_precedence
+ case expr_const_data:
+ return 0;
+#endif
+ }
+ return 100;
+}
+
+static int
+op_precedence(enum expr_op op1, enum expr_op op2)
+{
+ return op_val(op1) - op_val(op2);
+}
+
+static enum expression_context
+expression_context(struct element *expr)
+{
+ if (is_data_expression(expr))
+ return context_data;
+ if (is_numeric_expression(expr))
+ return context_numeric;
+ if (is_boolean_expression(expr))
+ return context_boolean;
+ return context_any;
+}
+
+static enum expr_op
+expression(struct element *expr)
+{
+ if (expr->type != ELEMENT_MAP)
+ return expr_none;
+ if (mapContains(expr, "match"))
+ return expr_match;
+ if (mapContains(expr, "check"))
+ return expr_check;
+ if (mapContains(expr, "equal"))
+ return expr_equal;
+ if (mapContains(expr, "substring"))
+ return expr_substring;
+ if (mapContains(expr, "suffix"))
+ return expr_suffix;
+ if (mapContains(expr, "concat"))
+ return expr_concat;
+ if (mapContains(expr, "and"))
+ return expr_and;
+ if (mapContains(expr, "or"))
+ return expr_or;
+ if (mapContains(expr, "not"))
+ return expr_not;
+ if (mapContains(expr, "option"))
+ return expr_option;
+ if (mapContains(expr, "hardware"))
+ return expr_hardware;
+ if (mapContains(expr, "hw-type"))
+ return expr_hw_type;
+ if (mapContains(expr, "hw-address"))
+ return expr_hw_address;
+ if (mapContains(expr, "packet"))
+ return expr_packet;
+ if (mapContains(expr, "const-data"))
+ return expr_const_data;
+ if (mapContains(expr, "extract-int8"))
+ return expr_extract_int8;
+ if (mapContains(expr, "extract-int16"))
+ return expr_extract_int16;
+ if (mapContains(expr, "extract-int32"))
+ return expr_extract_int32;
+ if (mapContains(expr, "encode-int8"))
+ return expr_encode_int8;
+ if (mapContains(expr, "encode-int16"))
+ return expr_encode_int16;
+ if (mapContains(expr, "encode-int32"))
+ return expr_encode_int32;
+ if (mapContains(expr, "const-int"))
+ return expr_const_int;
+ if (mapContains(expr, "exists"))
+ return expr_exists;
+ if (mapContains(expr, "encapsulate"))
+ return expr_encapsulate;
+ if (mapContains(expr, "known"))
+ return expr_known;
+ if (mapContains(expr, "reverse"))
+ return expr_reverse;
+ if (mapContains(expr, "leased-address"))
+ return expr_leased_address;
+ if (mapContains(expr, "binary-to-ascii"))
+ return expr_binary_to_ascii;
+ if (mapContains(expr, "config-option"))
+ return expr_config_option;
+ if (mapContains(expr, "host-decl-name"))
+ return expr_host_decl_name;
+ if (mapContains(expr, "pick-first-value"))
+ return expr_pick_first_value;
+ if (mapContains(expr, "lease-time"))
+ return expr_lease_time;
+ if (mapContains(expr, "static"))
+ return expr_static;
+ if (mapContains(expr, "not-equal"))
+ return expr_not_equal;
+ if (mapContains(expr, "null"))
+ return expr_null;
+ if (mapContains(expr, "variable-exists"))
+ return expr_variable_exists;
+ if (mapContains(expr, "variable-reference"))
+ return expr_variable_reference;
+ if (mapContains(expr, "filename"))
+ return expr_filename;
+ if (mapContains(expr, "server-name"))
+ return expr_sname;
+ if (mapContains(expr, "arguments"))
+ return expr_arg;
+ if (mapContains(expr, "funcall"))
+ return expr_funcall;
+ if (mapContains(expr, "function"))
+ return expr_function;
+ if (mapContains(expr, "add"))
+ return expr_add;
+ if (mapContains(expr, "subtract"))
+ return expr_subtract;
+ if (mapContains(expr, "multiply"))
+ return expr_multiply;
+ if (mapContains(expr, "divide"))
+ return expr_divide;
+ if (mapContains(expr, "remainder"))
+ return expr_remainder;
+ if (mapContains(expr, "binary-and"))
+ return expr_binary_and;
+ if (mapContains(expr, "binary-or"))
+ return expr_binary_or;
+ if (mapContains(expr, "binary-xor"))
+ return expr_binary_xor;
+ if (mapContains(expr, "client-state"))
+ return expr_client_state;
+ if (mapContains(expr, "uppercase"))
+ return expr_ucase;
+ if (mapContains(expr, "lowercase"))
+ return expr_lcase;
+ if (mapContains(expr, "regex-match"))
+ return expr_regex_match;
+ if (mapContains(expr, "iregex-match"))
+ return expr_iregex_match;
+ if (mapContains(expr, "gethostname"))
+ return expr_gethostname;
+ if (mapContains(expr, "v6relay"))
+ return expr_v6relay;
+ if (TAILQ_EMPTY(&expr->value.map_value)) {
+ fprintf(stderr, "empty expression");
+ if (expr->key != NULL)
+ fprintf(stderr, " for %s", expr->key);
+ } else {
+ struct element *item;
+ isc_boolean_t first = ISC_TRUE;
+
+ TAILQ_FOREACH(item, &expr->value.map_value) {
+ const char *key;
+
+ key = item->key;
+ if (key == NULL)
+ continue;
+ if (first)
+ fprintf(stderr, ": %s", key);
+ else
+ fprintf(stderr, ", %s", key);
+ first = ISC_FALSE;
+ }
+ }
+ fputs("\n", stderr);
+ return expr_none;
+}
+
+int
+expr_precedence(enum expr_op op, struct element *expr)
+{
+ if (expr->type != ELEMENT_MAP)
+ return op_val(op);
+ return op_val(op) - op_val(expression(expr));
+}
diff --git a/keama/print.c b/keama/print.c
new file mode 100644
index 00000000..be85bd4d
--- /dev/null
+++ b/keama/print.c
@@ -0,0 +1,1490 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "keama.h"
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static void debug(const char* fmt, ...);
+
+const char *
+print_expression(struct element *expr, isc_boolean_t *lose)
+{
+ if (expr->type == ELEMENT_BOOLEAN)
+ return print_boolean_expression(expr, lose);
+ if (expr->type == ELEMENT_INTEGER)
+ return print_numeric_expression(expr, lose);
+ if (expr->type == ELEMENT_STRING)
+ return print_data_expression(expr, lose);
+
+ if (is_boolean_expression(expr))
+ return print_boolean_expression(expr, lose);
+ if (is_numeric_expression(expr))
+ return print_numeric_expression(expr, lose);
+ if (is_data_expression(expr))
+ return print_data_expression(expr, lose);
+ *lose = ISC_TRUE;
+ return "???";
+}
+
+const char *
+print_boolean_expression(struct element *expr, isc_boolean_t *lose)
+{
+ struct string *result;
+
+ if (expr->type == ELEMENT_BOOLEAN) {
+ if (boolValue(expr))
+ return "true";
+ else
+ return "false";
+ }
+
+ /*
+ * From is_boolean_expression
+ */
+ if (expr->type != ELEMENT_MAP) {
+ *lose = ISC_TRUE;
+ return "???";
+ }
+ result = allocString();
+
+ /* check */
+ if (mapContains(expr, "check")) {
+ struct element *name;
+
+ appendString(result, "check ");
+ name = mapGet(expr, "check");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ } else
+ concatString(result, stringValue(name));
+ return result->content;
+ }
+
+ /* exists */
+ if (mapContains(expr, "exists")) {
+ struct element *arg;
+ struct element *universe;
+ struct element *name;
+
+ appendString(result, "exists ");
+ arg = mapGet(expr, "exists");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ universe = mapGet(arg, "universe");
+ if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(universe));
+ appendString(result, ".");
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(name));
+ return result->content;
+ }
+
+ /* variable-exists */
+ if (mapContains(expr, "variable-exists")) {
+ struct element *name;
+
+ appendString(result, "variable-exists ");
+ name = mapGet(expr, "variable-exists");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ } else
+ concatString(result, stringValue(name));
+ return result->content;
+ }
+
+ /* equal */
+ if (mapContains(expr, "equal")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "equal ");
+ arg = mapGet(expr, "equal");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_equal,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " = ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_equal,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* not-equal */
+ if (mapContains(expr, "not-equal")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "not-equal ");
+ arg = mapGet(expr, "not-equal");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_not_equal,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " != ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_not_equal,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* regex-match */
+ if (mapContains(expr, "regex-match")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "regex-match ");
+ arg = mapGet(expr, "regex-match");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_regex_match,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " ~= ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ appendString(result, print_expression(right, lose));
+ return result->content;
+ }
+
+ /* iregex-match */
+ if (mapContains(expr, "iregex-match")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "iregex-match ");
+ arg = mapGet(expr, "iregex-match");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_iregex_match,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " ~~ ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ appendString(result, print_expression(right, lose));
+ return result->content;
+ }
+
+ /* and */
+ if (mapContains(expr, "and")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "and ");
+ arg = mapGet(expr, "and");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_and,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " and ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_and,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* or */
+ if (mapContains(expr, "or")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "or ");
+ arg = mapGet(expr, "or");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_or,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " or ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_or,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* not */
+ if (mapContains(expr, "not")) {
+ struct element *arg;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "not ");
+ arg = mapGet(expr, "not");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_not,
+ arg) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(arg, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* known */
+ if (mapContains(expr, "known")) {
+ return "known";
+ }
+
+ /* static */
+ if (mapContains(expr, "static")) {
+ return "static";
+ }
+
+ /* variable-reference */
+ if (mapContains(expr, "variable-reference")) {
+ struct element *name;
+
+ appendString(result, "variable-reference ");
+ name = mapGet(expr, "variable-reference");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ return stringValue(name)->content;
+ }
+
+ /* funcall */
+ if (mapContains(expr, "funcall")) {
+ struct element *arg;
+ struct element *name;
+ struct element *args;
+ size_t i;
+
+ appendString(result, "funcall ");
+ arg = mapGet(expr, "funcall");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ concatString(result, stringValue(name));
+ appendString(result, "(");
+ args = mapGet(arg, "arguments");
+ if ((args == NULL) || (args->type != ELEMENT_LIST)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ for (i = 0; i < listSize(args); i++) {
+ struct element *item;
+
+ if (i != 0)
+ appendString(result, ", ");
+ item = listGet(args, i);
+ if (item == NULL) {
+ debug("funcall null argument %u",
+ (unsigned)i);
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ continue;
+ }
+ appendString(result, print_expression(item, lose));
+ }
+ appendString(result, ")");
+ return result->content;
+ }
+
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+}
+
+const char *
+print_data_expression(struct element *expr, isc_boolean_t *lose)
+{
+ struct string *result;
+
+ if (expr->type == ELEMENT_STRING)
+ return quote(stringValue(expr))->content;
+
+ /*
+ * From is_data_expression
+ */
+ if (expr->type != ELEMENT_MAP) {
+ *lose = ISC_TRUE;
+ return "???";
+ }
+ result = allocString();
+
+ /* substring */
+ if (mapContains(expr, "substring")) {
+ struct element *arg;
+ struct element *string;
+ struct element *offset;
+ struct element *length;
+
+ appendString(result, "substring(");
+ arg = mapGet(expr, "substring");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ string = mapGet(arg, "expression");
+ if (string == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(string, lose));
+ appendString(result, ", ");
+ offset = mapGet(arg, "offset");
+ if (offset == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(offset, lose));
+ appendString(result, ", ");
+ length = mapGet(arg, "length");
+ if (length == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(length, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* suffix */
+ if (mapContains(expr, "suffix")) {
+ struct element *arg;
+ struct element *string;
+ struct element *length;
+
+ appendString(result, "suffix(");
+ arg = mapGet(expr, "suffix");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ string = mapGet(arg, "expression");
+ if (string == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(string, lose));
+ appendString(result, ", ");
+ length = mapGet(arg, "length");
+ if (length == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(length, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* lowercase */
+ if (mapContains(expr, "lowercase")) {
+ struct element *arg;
+
+ appendString(result, "lowercase(");
+ arg = mapGet(expr, "lowercase");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* uppercase */
+ if (mapContains(expr, "uppercase")) {
+ struct element *arg;
+
+ appendString(result, "uppercase(");
+ arg = mapGet(expr, "uppercase");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* option */
+ if (mapContains(expr, "option")) {
+ struct element *arg;
+ struct element *universe;
+ struct element *name;
+
+ appendString(result, "option ");
+ arg = mapGet(expr, "option");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ universe = mapGet(arg, "universe");
+ if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(universe));
+ appendString(result, ".");
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(name));
+ return result->content;
+ }
+
+ /* hardware */
+ if (mapContains(expr, "hardware"))
+ return "hardware";
+
+ /* hw-type */
+ if (mapContains(expr, "hw-type"))
+ return "hw-type";
+
+ /* hw-address */
+ if (mapContains(expr, "hw-address"))
+ return "hw-address";
+
+ /* const-data */
+ if (mapContains(expr, "const-data")) {
+ struct element *arg;
+
+ arg = mapGet(expr, "const-data");
+ if ((arg == NULL) || (arg->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(arg));
+ return result->content;
+ }
+
+ /* packet */
+ if (mapContains(expr, "packet")) {
+ struct element *arg;
+ struct element *offset;
+ struct element *length;
+
+ appendString(result, "packet(");
+ arg = mapGet(expr, "packet");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ offset = mapGet(arg, "offset");
+ if (offset == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(offset, lose));
+ appendString(result, ", ");
+ length = mapGet(arg, "length");
+ if (length == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(length, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* concat */
+ if (mapContains(expr, "concat")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+
+ appendString(result, "concat(");
+ arg = mapGet(expr, "concat");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(left, lose));
+ appendString(result, ", ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(right, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* encapsulate */
+ if (mapContains(expr, "encapsulate")) {
+ struct element *arg;
+
+ appendString(result, "encapsulate ");
+ arg = mapGet(expr, "encapsulate");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ return result->content;
+ }
+
+ /* encode-int8 */
+ if (mapContains(expr, "encode-int8")) {
+ struct element *arg;
+
+ appendString(result, "encode-int(");
+ arg = mapGet(expr, "encode-int8");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???, 8)");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(arg, lose));
+ appendString(result, ", 8)");
+ return result->content;
+ }
+
+ /* encode-int16 */
+ if (mapContains(expr, "encode-int16")) {
+ struct element *arg;
+
+ appendString(result, "encode-int(");
+ arg = mapGet(expr, "encode-int16");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???, 16)");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(arg, lose));
+ appendString(result, ", 16)");
+ return result->content;
+ }
+
+ /* encode-int32 */
+ if (mapContains(expr, "encode-int32")) {
+ struct element *arg;
+
+ appendString(result, "encode-int(");
+ arg = mapGet(expr, "encode-int32");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???, 32)");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(arg, lose));
+ appendString(result, ", 32)");
+ return result->content;
+ }
+
+ /* gethostbyname */
+ if (mapContains(expr, "gethostbyname")) {
+ struct element *arg;
+
+ appendString(result, "gethostbyname(");
+ arg = mapGet(expr, "gethostbyname");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* binary-to-ascii */
+ if (mapContains(expr, "binary-to-ascii")) {
+ struct element *arg;
+ struct element *base;
+ struct element *width;
+ struct element *separator;
+ struct element *buffer;
+
+ appendString(result, "binary-to-ascii(");
+ arg = mapGet(expr, "binary-to-ascii");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ base = mapGet(arg, "base");
+ if (base == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(base, lose));
+ appendString(result, ", ");
+ width = mapGet(arg, "width");
+ if (width == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(width, lose));
+ appendString(result, ", ");
+ separator = mapGet(arg, "separator");
+ if (separator == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(separator, lose));
+ appendString(result, ", ");
+ buffer = mapGet(arg, "buffer");
+ if (buffer == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(buffer, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* filename */
+ if (mapContains(expr, "filename"))
+ return "filename";
+
+ /* server-name */
+ if (mapContains(expr, "server-name"))
+ return "server-name";
+
+ /* reverse */
+ if (mapContains(expr, "reverse")) {
+ struct element *arg;
+ struct element *width;
+ struct element *buffer;
+
+ appendString(result, "reverse(");
+ arg = mapGet(expr, "reverse");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ width = mapGet(arg, "width");
+ if (width == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(width, lose));
+ appendString(result, ", ");
+ buffer = mapGet(arg, "buffer");
+ if (buffer == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(buffer, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* pick-first-value */
+ if (mapContains(expr, "pick-first-value")) {
+ struct element *arg;
+ size_t i;
+
+ appendString(result, "pick-first-value(");
+ arg = mapGet(expr, "pick-first-value");
+ if ((arg == NULL) || (arg->type != ELEMENT_LIST)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ for (i = 0; i < listSize(arg); i++) {
+ struct element *item;
+
+ if (i != 0)
+ appendString(result, ", ");
+ item = listGet(arg, i);
+ if (item == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ continue;
+ }
+ appendString(result,
+ print_data_expression(item, lose));
+ }
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* host-decl-name */
+ if (mapContains(expr, "host-decl-name"))
+ return "host-decl-name";
+
+ /* leased-address */
+ if (mapContains(expr, "leased-address"))
+ return "leased-address";
+
+ /* config-option */
+ if (mapContains(expr, "config-option")) {
+ struct element *arg;
+ struct element *universe;
+ struct element *name;
+
+ appendString(result, "config-option ");
+ arg = mapGet(expr, "config-option");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ universe = mapGet(arg, "universe");
+ if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(universe));
+ appendString(result, ".");
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ concatString(result, stringValue(name));
+ return result->content;
+ }
+
+ /* null */
+ if (mapContains(expr, "null"))
+ return "null";
+
+ /* gethostname */
+ if (mapContains(expr, "gethostname"))
+ return "gethostname";
+
+ /* v6relay */
+ if (mapContains(expr, "v6relay")) {
+ struct element *arg;
+ struct element *relay;
+ struct element *option;
+
+
+ appendString(result, "v6relay(");
+ arg = mapGet(expr, "v6relay");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ relay = mapGet(arg, "relay");
+ if (relay == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_numeric_expression(relay, lose));
+ appendString(result, ", ");
+ option = mapGet(arg, "relay-option");
+ if (option == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???" ")");
+ return result->content;
+ }
+ appendString(result, print_data_expression(option, lose));
+ appendString(result, ")");
+ return result->content;
+ }
+
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+}
+
+const char *
+print_numeric_expression(struct element *expr, isc_boolean_t *lose)
+{
+ struct string *result;
+
+ if (expr->type == ELEMENT_INTEGER) {
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%lld", (long long)intValue(expr));
+ result = makeString(-1, buf);
+ return result->content;
+ }
+
+ /*
+ * From is_numeric_expression
+ */
+ if (expr->type != ELEMENT_MAP) {
+ *lose = ISC_TRUE;
+ return "???";
+ }
+ result = allocString();
+
+ /* extract-int8 */
+ if (mapContains(expr, "extract-int8")) {
+ struct element *arg;
+
+ appendString(result, "extract-int(");
+ arg = mapGet(expr, "extract-int8");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???, 8)");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ appendString(result, ", 8)");
+ return result->content;
+ }
+
+ /* extract-int16 */
+ if (mapContains(expr, "extract-int16")) {
+ struct element *arg;
+
+ appendString(result, "extract-int(");
+ arg = mapGet(expr, "extract-int16");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???, 16)");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ appendString(result, ", 16)");
+ return result->content;
+ }
+
+ /* extract-int32 */
+ if (mapContains(expr, "extract-int32")) {
+ struct element *arg;
+
+ appendString(result, "extract-int(");
+ arg = mapGet(expr, "extract-int32");
+ if (arg == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???, 32)");
+ return result->content;
+ }
+ appendString(result, print_data_expression(arg, lose));
+ appendString(result, ", 32)");
+ return result->content;
+ }
+
+ /* const-int */
+ if (mapContains(expr, "const-int")) {
+ struct element *arg;
+ char buf[20];
+
+ arg = mapGet(expr, "const-int");
+ if ((arg == NULL) || (arg->type != ELEMENT_INTEGER)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ snprintf(buf, sizeof(buf), "%lld", (long long)intValue(arg));
+ result = makeString(-1, buf);
+ return result->content;
+ }
+
+ /* lease-time */
+ if (mapContains(expr, "lease-time"))
+ return "lease-time";
+
+ /* add */
+ if (mapContains(expr, "add")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "add ");
+ arg = mapGet(expr, "add");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_add,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " + ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_add,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* subtract */
+ if (mapContains(expr, "subtract")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "subtract ");
+ arg = mapGet(expr, "subtract");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_subtract,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " - ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_subtract,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* multiply */
+ if (mapContains(expr, "multiply")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "multiply ");
+ arg = mapGet(expr, "multiply");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_multiply,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " * ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_multiply,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* divide */
+ if (mapContains(expr, "divide")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "divide ");
+ arg = mapGet(expr, "divide");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_divide,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " / ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_divide,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* remainder */
+ if (mapContains(expr, "remainder")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "remainder ");
+ arg = mapGet(expr, "remainder");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_remainder,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " % ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_remainder,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* binary-and */
+ if (mapContains(expr, "binary-and")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "binary-and ");
+ arg = mapGet(expr, "binary-and");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_binary_and,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " & ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_binary_and,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* binary-or */
+ if (mapContains(expr, "binary-or")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "binary-or ");
+ arg = mapGet(expr, "binary-or");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_binary_or,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " | ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_binary_or,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* binary-xor */
+ if (mapContains(expr, "binary-xor")) {
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ isc_boolean_t add_parenthesis;
+
+ appendString(result, "binary-xor ");
+ arg = mapGet(expr, "binary-xor");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ result = allocString();
+ add_parenthesis = ISC_TF(expr_precedence(expr_binary_xor,
+ left) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(left, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ appendString(result, " ^ ");
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+ }
+ add_parenthesis = ISC_TF(expr_precedence(expr_binary_xor,
+ right) < 0);
+ if (add_parenthesis)
+ appendString(result, "(");
+ appendString(result, print_expression(right, lose));
+ if (add_parenthesis)
+ appendString(result, ")");
+ return result->content;
+ }
+
+ /* client-state */
+ if (mapContains(expr, "client-state"))
+ return "client-state";
+
+ *lose = ISC_TRUE;
+ appendString(result, "???");
+ return result->content;
+}
+
+static void
+debug(const char* fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ vfprintf(stderr, fmt, list);
+ fprintf(stderr, "\n");
+ va_end(list);
+}
diff --git a/keama/reduce.c b/keama/reduce.c
new file mode 100644
index 00000000..24939473
--- /dev/null
+++ b/keama/reduce.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "keama.h"
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static struct element *reduce_equal_expression(struct element *left,
+ struct element *right);
+static void debug(const char* fmt, ...);
+
+/*
+ * boolean_expression :== CHECK STRING |
+ * NOT boolean-expression |
+ * data-expression EQUAL data-expression |
+ * data-expression BANG EQUAL data-expression |
+ * data-expression REGEX_MATCH data-expression |
+ * boolean-expression AND boolean-expression |
+ * boolean-expression OR boolean-expression
+ * EXISTS OPTION-NAME
+ */
+
+struct element *
+reduce_boolean_expression(struct element *expr)
+{
+ /* trivial case: already done */
+ if (expr->type == ELEMENT_BOOLEAN)
+ return expr;
+
+ /*
+ * From is_boolean_expression
+ */
+
+ if (expr->type != ELEMENT_MAP)
+ return NULL;
+
+ /* check */
+ if (mapContains(expr, "check"))
+ /*
+ * syntax := { "check": <collection_name> }
+ * semantic: check_collection
+ * on server try to match classes of the collection
+ */
+ return NULL;
+
+
+ /* exists */
+ if (mapContains(expr, "exists")) {
+ /*
+ * syntax := { "exists":
+ * { "universe": <option_space_old>,
+ * "name": <option_name> }
+ * }
+ * semantic: check universe/code from incoming packet
+ */
+ struct element *arg;
+ struct element *universe;
+ struct element *name;
+ struct option *option;
+ char result[80];
+
+ arg = mapGet(expr, "exists");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get exists argument");
+ return NULL;
+ }
+ universe = mapGet(arg, "universe");
+ if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
+ debug("can't get exists option universe");
+ return NULL;
+ }
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ debug("can't get exists option name");
+ return NULL;
+ }
+ option = option_lookup_name(stringValue(universe)->content,
+ stringValue(name)->content);
+ if ((option == NULL) || (option->code == 0))
+ return NULL;
+ if (((local_family == AF_INET) &&
+ (strcmp(option->space->name, "dhcp4") != 0)) ||
+ ((local_family == AF_INET6) &&
+ (strcmp(option->space->name, "dhcp6") != 0)))
+ return NULL;
+ snprintf(result, sizeof(result),
+ "option[%u].exists", option->code);
+ return createString(makeString(-1, result));
+ }
+
+ /* variable-exists */
+ if (mapContains(expr, "variable-exists"))
+ /*
+ * syntax := { "variable-exists": <variable_name> }
+ * semantics: find_binding(scope, name)
+ */
+ return NULL;
+
+ /* equal */
+ if (mapContains(expr, "equal")) {
+ /*
+ * syntax := { "equal":
+ * { "left": <expression>,
+ * "right": <expression> }
+ * }
+ * semantics: evaluate branches and return true
+ * if same type and same value
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+
+ arg = mapGet(expr, "equal");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get equal argument");
+ return NULL;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ debug("can't get equal left branch");
+ return NULL;
+ }
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ debug("can't get equal right branch");
+ return NULL;
+ }
+ return reduce_equal_expression(left, right);
+ }
+
+ /* not-equal */
+ if (mapContains(expr, "not-equal")) {
+ /*
+ * syntax := { "not-equal":
+ * { "left": <expression>,
+ * "right": <expression> }
+ * }
+ * semantics: evaluate branches and return true
+ * if different type or different value
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct element *equal;
+ struct string *result;
+
+ arg = mapGet(expr, "not-equal");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get not-equal argument");
+ return NULL;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ debug("can't get not-equal left branch");
+ return NULL;
+ }
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ debug("can't get not-equal right branch");
+ return NULL;
+ }
+ equal = reduce_equal_expression(left, right);
+ if ((equal == NULL) || (equal->type != ELEMENT_STRING))
+ return NULL;
+ result = makeString(-1, "not (");
+ concatString(result, stringValue(equal));
+ appendString(result, ")");
+ return createString(result);
+ }
+
+ /* regex-match */
+ if (mapContains(expr, "regex-match"))
+ /*
+ * syntax := { "regex-match":
+ * { "left": <data_expression>,
+ * "right": <data_expression> }
+ * }
+ * semantics: evaluate branches, compile right as a
+ * regex and apply it to left
+ */
+ return NULL;
+
+ /* iregex-match */
+ if (mapContains(expr, "iregex-match"))
+ /*
+ * syntax := { "regex-match":
+ * { "left": <data_expression>,
+ * "right": <data_expression> }
+ * }
+ * semantics: evaluate branches, compile right as a
+ * case insensistive regex and apply it to left
+ */
+ return NULL;
+
+ /* and */
+ if (mapContains(expr, "and")) {
+ /*
+ * syntax := { "and":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return true
+ * if both are true
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct string *result;
+
+ arg = mapGet(expr, "and");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get and argument");
+ return NULL;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ debug("can't get and left branch");
+ return NULL;
+ }
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ debug("can't get and right branch");
+ return NULL;
+ }
+ left = reduce_boolean_expression(left);
+ if ((left == NULL) || (left->type != ELEMENT_STRING))
+ return NULL;
+ right = reduce_boolean_expression(right);
+ if ((right == NULL) || (right->type != ELEMENT_STRING))
+ return NULL;
+ result = makeString(-1, "(");
+ concatString(result, stringValue(left));
+ appendString(result, ") and (");
+ concatString(result, stringValue(right));
+ appendString(result, ")");
+ return createString(result);
+ }
+
+ /* or */
+ if (mapContains(expr, "or")) {
+ /*
+ * syntax := { "or":
+ * { "left": <boolean_expression>,
+ * "right": <boolean_expression> }
+ * }
+ * semantics: evaluate branches, return true
+ * if any is true
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct string *result;
+
+ arg = mapGet(expr, "or");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get or argument");
+ return NULL;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ debug("can't get or left branch");
+ return NULL;
+ }
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ debug("can't get or right branch");
+ return NULL;
+ }
+ left = reduce_boolean_expression(left);
+ if ((left == NULL) || (left->type != ELEMENT_STRING))
+ return NULL;
+ right = reduce_boolean_expression(right);
+ if ((right == NULL) || (right->type != ELEMENT_STRING))
+ return NULL;
+ result = makeString(-1, "(");
+ concatString(result, stringValue(left));
+ appendString(result, ") or (");
+ concatString(result, stringValue(right));
+ appendString(result, ")");
+ return createString(result);
+ }
+
+ /* not */
+ if (mapContains(expr, "not")) {
+ /*
+ * syntax := { "not": <boolean_expression> }
+ * semantic: evaluate its branch and return its negation
+ */
+ struct element *arg;
+ struct string *result;
+
+ arg = mapGet(expr, "not");
+ if (arg == NULL) {
+ debug("can't get not argument");
+ return NULL;
+ }
+ arg = reduce_boolean_expression(arg);
+ if ((arg == NULL) || (arg->type != ELEMENT_STRING))
+ return NULL;
+ result = makeString(-1, "not (");
+ concatString(result, stringValue(arg));
+ appendString(result, ")");
+ return createString(result);
+ }
+
+ /* known */
+ if (mapContains(expr, "known"))
+ /*
+ * syntax := { "known": null }
+ * semantics: client is known, i.e., has a matching
+ * host declaration (aka reservation in Kea)
+ */
+ return NULL;
+
+ /* static */
+ if (mapContains(expr, "static"))
+ /*
+ * syntax := { "static": null }
+ * semantics: lease is static (doesn't exist in Kea)
+ */
+ return NULL;
+
+ return NULL;
+}
+
+/*
+ * data_expression :== SUBSTRING LPAREN data-expression COMMA
+ * numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * CONCAT LPAREN data-expression COMMA
+ * data-expression RPAREN
+ * SUFFIX LPAREN data_expression COMMA
+ * numeric-expression RPAREN |
+ * LCASE LPAREN data_expression RPAREN |
+ * UCASE LPAREN data_expression RPAREN |
+ * OPTION option_name |
+ * HARDWARE |
+ * PACKET LPAREN numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * V6RELAY LPAREN numeric-expression COMMA
+ * data-expression RPAREN |
+ * STRING |
+ * colon_separated_hex_list
+ */
+
+struct element *
+reduce_data_expression(struct element *expr)
+{
+ /* trivial case: already done */
+ if (expr->type == ELEMENT_STRING)
+ return expr;
+
+ /*
+ * From is_data_expression
+ */
+
+ if (expr->type != ELEMENT_MAP)
+ return NULL;
+
+ /* substring */
+ if (mapContains(expr, "substring")) {
+ /*
+ * syntax := { "substring":
+ * { "expression": <data_expression>,
+ * "offset": <numeric_expression>,
+ * "length": <numeric_expression> }
+ * }
+ * semantic: evaluate arguments, if the string is
+ * shorter than offset return "" else return substring
+ */
+ struct element *arg;
+ struct element *string;
+ struct element *offset;
+ struct element *length;
+ struct string *result;
+ int64_t off;
+ int64_t len;
+ char buf[80];
+
+ arg = mapGet(expr, "substring");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get substring argument");
+ return NULL;
+ }
+ string = mapGet(arg, "expression");
+ if (string == NULL) {
+ debug("can't get substring expression");
+ return NULL;
+ }
+ offset = mapGet(arg, "offset");
+ if (offset == NULL) {
+ debug("can't get substring offset");
+ return NULL;
+ }
+ length = mapGet(arg, "length");
+ if (length == NULL) {
+ debug("can't get substring length");
+ return NULL;
+ }
+ /* can't be a literal as it was evaluated before */
+ string = reduce_data_expression(string);
+ if ((string == NULL) || (string->type != ELEMENT_STRING))
+ return NULL;
+ offset = reduce_numeric_expression(offset);
+ if ((offset == NULL) || (offset->type != ELEMENT_INTEGER))
+ return NULL;
+ off = intValue(offset);
+ if (off < 0) {
+ debug("substring with a negative offset (%lld)",
+ (long long)off);
+ return NULL;
+ }
+ length = reduce_numeric_expression(length);
+ if ((length == NULL) || (length->type != ELEMENT_INTEGER))
+ return NULL;
+ len = intValue(length);
+ if (len < 0) {
+ debug("substring with a negative length (%lld)",
+ (long long)len);
+ return NULL;
+ }
+ result = makeString(-1, "substring(");
+ concatString(result, stringValue(string));
+ snprintf(buf, sizeof(buf),
+ ",%u,%u)", (unsigned)off, (unsigned)len);
+ appendString(result, buf);
+ return createString(result);
+ }
+
+ /* suffix */
+ if (mapContains(expr, "suffix")) {
+ /*
+ * syntax := { "suffix":
+ * { "expression": <data_expression>,
+ * "length": <numeric_expression> }
+ * }
+ * semantic: evaluate arguments, if the string is
+ * shorter than length return it else return suffix
+ */
+ struct element *arg;
+ struct element *string;
+ struct element *length;
+ struct string *result;
+ int64_t len;
+ char buf[80];
+
+ arg = mapGet(expr, "suffix");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get suffix argument");
+ return NULL;
+ }
+ string = mapGet(arg, "expression");
+ if (string == NULL) {
+ debug("can't get suffix expression");
+ return NULL;
+ }
+ length = mapGet(arg, "length");
+ if (length == NULL) {
+ debug("can't get suffix length");
+ return NULL;
+ }
+ /* can't be a literal as it was evaluated before */
+ string = reduce_data_expression(string);
+ if ((string == NULL) || (string->type != ELEMENT_STRING))
+ return NULL;
+ length = reduce_numeric_expression(length);
+ if ((length == NULL) || (length->type != ELEMENT_INTEGER))
+ return NULL;
+ len = intValue(length);
+ if (len < 0) {
+ debug("suffix with a negative length (%lld)",
+ (long long)len);
+ return NULL;
+ }
+ result = makeString(-1, "substring(");
+ concatString(result, stringValue(string));
+ snprintf(buf, sizeof(buf), ",-%u,all)", (unsigned)len);
+ appendString(result, buf);
+ return createString(result);
+ }
+
+ /* lowercase */
+ if (mapContains(expr, "lowercase"))
+ /*
+ * syntax := { "lowercase": <data_expression> }
+ * semantic: evaluate its argument and apply tolower to
+ * its content
+ */
+ return NULL;
+
+ /* uppercase */
+ if (mapContains(expr, "uppercase"))
+ /*
+ * syntax := { "uppercase": <data_expression> }
+ * semantic: evaluate its argument and apply toupper to
+ * its content
+ */
+ return NULL;
+
+ /* option */
+ if (mapContains(expr, "option")) {
+ /*
+ * syntax := { "option":
+ * { "universe": <option_space_old>,
+ * "name": <option_name> }
+ * }
+ * semantic: get universe/code option from incoming packet
+ */
+ struct element *arg;
+ struct element *universe;
+ struct element *name;
+ struct option *option;
+ char result[80];
+
+ arg = mapGet(expr, "option");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get option argument");
+ return NULL;
+ }
+ universe = mapGet(arg, "universe");
+ if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
+ debug("can't get option universe");
+ return NULL;
+ }
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ debug("can't get option name");
+ return NULL;
+ }
+ option = option_lookup_name(stringValue(universe)->content,
+ stringValue(name)->content);
+ if ((option == NULL) || (option->code == 0))
+ return NULL;
+ if (((local_family == AF_INET) &&
+ (strcmp(option->space->name, "dhcp4") != 0)) ||
+ ((local_family == AF_INET6) &&
+ (strcmp(option->space->name, "dhcp6") != 0)))
+ return NULL;
+ snprintf(result, sizeof(result),
+ "option[%u].hex", option->code);
+ return createString(makeString(-1, result));
+ }
+
+ /* hardware */
+ if (mapContains(expr, "hardware")) {
+ /*
+ * syntax := { "hardware": null }
+ * semantic: get mac type and address from incoming packet
+ */
+ struct string *result;
+
+ if (local_family != AF_INET) {
+ debug("get hardware for DHCPv6");
+ return NULL;
+ }
+ result = makeString(-1,
+ "concat(substring(pkt4.htype,-1,all),pkt4.mac)");
+ return createString(result);
+ }
+
+ /* hw-type */
+ if (mapContains(expr, "hw-type")) {
+ /*
+ * ADDED
+ * syntax := { "hw-type": null }
+ * semantic: get mac type from incoming packet
+ */
+ struct string *result;
+
+ if (local_family != AF_INET) {
+ debug("get hw-type for DHCPv6");
+ return NULL;
+ }
+ result = makeString(-1, "substring(pkt4.htype,-1,all)");
+ return createString(result);
+ }
+
+ /* hw-address */
+ if (mapContains(expr, "hw-address")) {
+ /*
+ * ADDED
+ * syntax := { "hw-address": null }
+ * semantic: get mac address from incoming packet
+ */
+ struct string *result;
+
+ if (local_family != AF_INET) {
+ debug("get hw-address for DHCPv6");
+ return NULL;
+ }
+ result = makeString(-1, "pkt4.mac");
+ return createString(result);
+ }
+
+ /* const-data */
+ if (mapContains(expr, "const-data")) {
+ /*
+ * syntax := { "const-data": <string> }
+ * semantic: embedded string value
+ */
+ struct element *arg;
+
+ arg = mapGet(expr, "const-data");
+ if ((arg == NULL) || (arg->type != ELEMENT_STRING)) {
+ debug("can't get const-data argument");
+ return NULL;
+ }
+ return createString(stringValue(arg));
+ }
+
+ /* packet */
+ if (mapContains(expr, "packet"))
+ /*
+ * syntax := { "packet":
+ * { "offset": <numeric_expression>,
+ * "length": <numeric_expression> }
+ * }
+ * semantic: return the selected substring of the incoming
+ * packet content
+ */
+ return NULL;
+
+ /* concat */
+ if (mapContains(expr, "concat")) {
+ /*
+ * syntax := { "concat":
+ * { "left": <data_expression>,
+ * "right": <data_expression> }
+ * }
+ * semantic: evaluate arguments and return the concatenation
+ */
+ struct element *arg;
+ struct element *left;
+ struct element *right;
+ struct string *result;
+
+ arg = mapGet(expr, "concat");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get concat argument");
+ return NULL;
+ }
+ left = mapGet(arg, "left");
+ if (left == NULL) {
+ debug("can't get concat left branch");
+ return NULL;
+ }
+ right = mapGet(arg, "right");
+ if (right == NULL) {
+ debug("can't get concat right branch");
+ return NULL;
+ }
+ /* left is a literal case */
+ if (left->type == ELEMENT_STRING) {
+ /* can't be a literal as it was evaluated before */
+ right = reduce_data_expression(right);
+ if ((right == NULL) || (right->type != ELEMENT_STRING))
+ return NULL;
+ result = makeString(-1, "concat(");
+ concatString(result, quote(stringValue(left)));
+ appendString(result, ", ");
+ concatString(result, stringValue(right));
+ appendString(result, ")");
+ return createString(result);
+ }
+ left = reduce_data_expression(left);
+ if ((left == NULL) || (left->type != ELEMENT_STRING))
+ return NULL;
+ /* right is a literal case */
+ if (right->type == ELEMENT_STRING) {
+ /* literal left was handled before */
+ result = makeString(-1, "concat(");
+ concatString(result, stringValue(left));
+ appendString(result, ", ");
+ concatString(result, quote(stringValue(right)));
+ appendString(result, ")");
+ return createString(result);
+ }
+ right = reduce_data_expression(right);
+ if ((right == NULL) || (right->type != ELEMENT_STRING))
+ return NULL;
+ result = makeString(-1, "concat(");
+ concatString(result, stringValue(left));
+ appendString(result, ", ");
+ concatString(result, stringValue(right));
+ appendString(result, ")");
+ return createString(result);
+ }
+
+ /* encapsulate */
+ if (mapContains(expr, "encapsulate"))
+ /*
+ * syntax := { "encapsulate": <encapsulated_space> }
+ * semantic: encapsulate options of the given space
+ */
+ return NULL;
+
+ /* encode-int8 */
+ if (mapContains(expr, "encode-int8"))
+ /*
+ * syntax := { "encode-int8": <numeric_expression> }
+ * semantic: return a string buffer with the evaluated
+ * number as content
+ */
+ return NULL;
+
+ /* encode-int16 */
+ if (mapContains(expr, "encode-int16"))
+ /*
+ * syntax := { "encode-int16": <numeric_expression> }
+ * semantic: return a string buffer with the evaluated
+ * number as content
+ */
+ return NULL;
+
+ /* encode-int32 */
+ if (mapContains(expr, "encode-int32"))
+ /*
+ * syntax := { "encode-int32": <numeric_expression> }
+ * semantic: return a string buffer with the evaluated
+ * number as content
+ */
+ return NULL;
+
+ /* gethostbyname */
+ if (mapContains(expr, "gethostbyname"))
+ /*
+ * syntax := { "gethostbyname": <string> }
+ * semantic: call gethostbyname and return
+ * a binary buffer with addresses
+ */
+ return NULL;
+
+ /* binary-to-ascii */
+ if (mapContains(expr, "binary-to-ascii"))
+ /*
+ * syntax := { "binary-to-ascii":
+ * { "base": <numeric_expression 2..16>,
+ * "width": <numeric_expression 8, 16 or 32>,
+ * "separator": <data_expression>,
+ * "buffer": <data_expression> }
+ * }
+ * semantic: split the input buffer into int8/16/32 numbers,
+ * output them separated by the given string
+ */
+ return NULL;
+
+ /* filename */
+ if (mapContains(expr, "filename"))
+ /*
+ * syntax := { "filename": null }
+ * semantic: get filename field from incoming DHCPv4 packet
+ */
+ return NULL;
+
+ /* server-name */
+ if (mapContains(expr, "server-name"))
+ /*
+ * syntax := { "server-name": null }
+ * semantic: get server-name field from incoming DHCPv4 packet
+ */
+ return NULL;
+
+ /* reverse */
+ if (mapContains(expr, "reverse"))
+ /*
+ * syntax := { "reverse":
+ * { "width": <numeric_expression>,
+ * "buffer": <data_expression> }
+ * }
+ * semantic: reverse the input buffer by width chunks of bytes
+ */
+ return NULL;
+
+ /* pick-first-value */
+ if (mapContains(expr, "pick-first-value"))
+ /*
+ * syntax := { "pick-first-value":
+ * [ <data_expression>, ... ]
+ * }
+ * semantic: evaluates expressions and return the first
+ * not null, return null if all are null
+ */
+ return NULL;
+
+ /* host-decl-name */
+ if (mapContains(expr, "host-decl-name"))
+ /*
+ * syntax := { "host-decl-name": null }
+ * semantic: return the name of the matching host
+ * declaration (aka revervation in kea) or null
+ */
+ return NULL;
+
+ /* leased-address */
+ if (mapContains(expr, "leased-address"))
+ /*
+ * syntax := { "leased-address": null }
+ * semantic: return the address of the assigned lease or
+ * log a message
+ */
+ return NULL;
+
+ /* config-option */
+ if (mapContains(expr, "config-option"))
+ /*
+ * syntax := { "config-option":
+ * { "universe": <option_space_old>,
+ * "name": <option_name> }
+ * }
+ * semantic: get universe/code option to send
+ */
+ return NULL;
+
+ /* null */
+ if (mapContains(expr, "null")) {
+ /*
+ * syntax := { "null": null }
+ * semantic: return null
+ */
+ debug("unexpected null: this expression was not evaluated");
+ return NULL;
+ }
+
+ /* gethostname */
+ if (mapContains(expr, "gethostname")) {
+ /*
+ * syntax := { "gethostname": null }
+ * semantic: return gethostname
+ */
+ debug("unexpected gethostname: this expression was not "
+ "evaluated");
+ return NULL;
+ }
+
+ /* v6relay */
+ if (mapContains(expr, "v6relay")) {
+ /*
+ * syntax := { "v6relay":
+ * { "relay": <numeric_expression>,
+ * "relay-option" <data_expression> }
+ * }
+ * semantic: relay is a counter from client, 0 is no-op,
+ * 1 is the relay closest to the client, etc, option
+ * is a dhcp6 option ans is return when found
+ */
+ struct element *arg;
+ struct element *relay;
+ struct element *universe;
+ struct element *name;
+ struct option *option;
+ int64_t r;
+ char result[100];
+
+ if (local_family != AF_INET6) {
+ debug("get v6relay for DHCPv4");
+ return NULL;
+ }
+ arg = mapGet(expr, "v6relay");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get v6relay argument");
+ return NULL;
+ }
+ relay = mapGet(arg, "relay");
+ if (relay == NULL) {
+ debug("can't get v6relay relay");
+ return NULL;
+ }
+ relay = reduce_numeric_expression(relay);
+ if ((relay == NULL) || (relay->type != ELEMENT_INTEGER))
+ return NULL;
+ r = intValue(relay);
+ if (r < 0) {
+ debug("v6relay called with illegal relay (%lld)",
+ (long long)r);
+ return NULL;
+ }
+ arg = mapGet(arg, "relay-option");
+ if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
+ debug("can't get v6relay relay-option");
+ return NULL;
+ }
+ universe = mapGet(arg, "universe");
+ if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
+ debug("can't get v6relay option universe");
+ NULL;
+ }
+ name = mapGet(arg, "name");
+ if ((name == NULL) || (name->type != ELEMENT_STRING)) {
+ debug("can't get v6relay option name");
+ return NULL;
+ }
+ option = option_lookup_name(stringValue(universe)->content,
+ stringValue(name)->content);
+ if ((option == NULL) || (option->code == 0) ||
+ (strcmp(option->space->name, "dhcp6") != 0))
+ return NULL;
+ if (r == 0)
+ snprintf(result, sizeof(result),
+ "option[%u].hex", option->code);
+ else {
+ /* r > MAX_V6RELAY_HOPS means the relay closest
+ to server */
+ if (r > MAX_V6RELAY_HOPS)
+ r = 0;
+ /* Kea counts from the server, use negative nesting
+ levels to count from the client */
+ snprintf(result, sizeof(result),
+ "relay6[%d].option[%u].hex",
+ (int)-r, option->code);
+ }
+ return createString(makeString(-1, result));
+ }
+
+ return NULL;
+}
+
+struct element *
+reduce_numeric_expression(struct element *expr)
+{
+ /* trivial case: already done */
+ if (expr->type == ELEMENT_INTEGER)
+ return expr;
+
+ if (expr->type != ELEMENT_MAP)
+ return NULL;
+
+ /* Kea has no numeric operators... */
+ return NULL;
+}
+
+static struct element *
+reduce_equal_expression(struct element *left, struct element *right)
+{
+ struct string *result;
+
+ /*
+ * numeric case was handled by evaluation
+ */
+
+ if (!is_data_expression(left) || !is_data_expression(right))
+ return NULL;
+
+ /* left is a literal case */
+ if (left->type == ELEMENT_STRING) {
+ /* can't be a literal as it was evaluated before */
+ right = reduce_data_expression(right);
+ if ((right == NULL) || (right->type != ELEMENT_STRING))
+ return NULL;
+ result = allocString();
+ concatString(result, quote(stringValue(left)));
+ appendString(result, " == ");
+ concatString(result, stringValue(right));
+ return createString(result);
+ }
+ left = reduce_data_expression(left);
+ if ((left == NULL) || (left->type != ELEMENT_STRING))
+ return NULL;
+
+ /* right is a literal case */
+ if (right->type == ELEMENT_STRING) {
+ /* literal left was handled before */
+ result = allocString();
+ concatString(result, stringValue(left));
+ appendString(result, " == ");
+ concatString(result, quote(stringValue(right)));
+ return createString(result);
+ }
+ right = reduce_data_expression(right);
+ if ((right == NULL) || (right->type != ELEMENT_STRING))
+ return NULL;
+
+ result = allocString();
+ concatString(result, stringValue(left));
+ appendString(result, " == ");
+ concatString(result, stringValue(right));
+ return createString(result);
+}
+
+static void
+debug(const char* fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ vfprintf(stderr, fmt, list);
+ fprintf(stderr, "\n");
+ va_end(list);
+}
diff --git a/keama/tests/README b/keama/tests/README
new file mode 100644
index 00000000..69686155
--- /dev/null
+++ b/keama/tests/README
@@ -0,0 +1,42 @@
+Tests are dividing on error vs working, and DHCPv4 vs DHCPv6.
+
+Names of files about test xyz have xyz as body and an extension.
+
+Extensions:
+ - .err4 = source for error test in DHCPv4
+ - .errF = source for error test in DHCPv4 with -r fatal
+ - .errP = source for error test in DHCPv4 with -r pass
+ - .err6 = source for error test in DHCPv6
+ - .err = source for error test in DHCPv4 and DHCPv6
+ - .msg = resultat (first line of standard error) for error test
+ - .in4 = source for working test in DHCPv4
+ - .in6 = source for working test in DHCPv6
+ - .ind = source for working test in DHCPv4 with -D
+ - .inD = source for working test in DHCPv6 with -D
+ - .inn = source for working test in DHCPv4 with -N
+ - .inN = source for working test in DHCPv6 with -N
+ - .inl = source for working test in DHCPv4 with -l $HOOK
+ - .inL = source for working test in DHCPv6 with -l $HOOK
+ - .outl = resultat for working test with default hook library path
+ - .outL = resultat for working test with default hook library path
+ - .out = resultat for working test
+There is no working test in both DHCPv4 and DHCPv6.
+The body of the name of a working test must include 4 or 6 so
+the .out can be submitted to kea-dhcp4 or kea-dhcp6
+
+runone.sh xyz.ext
+ -> run the xyz test
+runall.sh
+ -> run all tests
+
+Check output syntax with kea-dhcp4 and kea-dhcp6:
+ - Set KEA4 and KEA6 environment variables to kea-dhcp4 and kea-dhcp6
+ - Set HOOK to a place to find hooks (currently libdhcp_flex_id.so),
+ please use the directory name with a trailing /
+ - The en0 interface is supposed to exist (or replace "en0" in all files)
+ - Note that runall.sh must be run before checkall.sh
+
+checkone.sh xyz.out
+ -> check the syntax of xyz.out
+checkall.sh
+ -> check the syntax of all .out files
diff --git a/keama/tests/badcasexsc.err b/keama/tests/badcasexsc.err
new file mode 100644
index 00000000..9cfdd87c
--- /dev/null
+++ b/keama/tests/badcasexsc.err
@@ -0,0 +1,7 @@
+# bad case executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# a raw default statement
+case "foo": break;
diff --git a/keama/tests/badcasexsc.msg b/keama/tests/badcasexsc.msg
new file mode 100644
index 00000000..636bb31a
--- /dev/null
+++ b/keama/tests/badcasexsc.msg
@@ -0,0 +1 @@
+badcasexsc.err line 7: case statement in inappropriate scope.
diff --git a/keama/tests/badclass.err b/keama/tests/badclass.err
new file mode 100644
index 00000000..8dc59c05
--- /dev/null
+++ b/keama/tests/badclass.err
@@ -0,0 +1,12 @@
+# bad (double match-if) class declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+
+# class declaration
+class "foobar" {
+ match if option mysystem = "version1";
+ match if option mysystem = "version2";
+ option myversion 1;
+}
diff --git a/keama/tests/badclass.msg b/keama/tests/badclass.msg
new file mode 100644
index 00000000..69a603ab
--- /dev/null
+++ b/keama/tests/badclass.msg
@@ -0,0 +1 @@
+badclass.err line 10: A class may only have one 'match if' clause.
diff --git a/keama/tests/badclass2.err b/keama/tests/badclass2.err
new file mode 100644
index 00000000..551a1729
--- /dev/null
+++ b/keama/tests/badclass2.err
@@ -0,0 +1,12 @@
+# bad (2 match) class declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ match option mysystem;
+ match option myversion;
+ option myversion 1;
+}
diff --git a/keama/tests/badclass2.msg b/keama/tests/badclass2.msg
new file mode 100644
index 00000000..3afd2b3b
--- /dev/null
+++ b/keama/tests/badclass2.msg
@@ -0,0 +1 @@
+badclass2.err line 10: can't override existing submatch/spawn
diff --git a/keama/tests/baddecl2array.err b/keama/tests/baddecl2array.err
new file mode 100644
index 00000000..80701141
--- /dev/null
+++ b/keama/tests/baddecl2array.err
@@ -0,0 +1,4 @@
+# bad 2a (array in array) option declaration
+
+option space foobar;
+option foobar.fmt-Bat code 1 = array of array of unsigned integer 8;
diff --git a/keama/tests/baddecl2array.msg b/keama/tests/baddecl2array.msg
new file mode 100644
index 00000000..e7ad089e
--- /dev/null
+++ b/keama/tests/baddecl2array.msg
@@ -0,0 +1 @@
+baddecl2array.err line 4: no nested arrays.
diff --git a/keama/tests/baddecl2record.err b/keama/tests/baddecl2record.err
new file mode 100644
index 00000000..d237b636
--- /dev/null
+++ b/keama/tests/baddecl2record.err
@@ -0,0 +1,4 @@
+# bad 2r (record in record) option declaration
+
+option space foobar;
+option foobar.fmt-Bat code 1 = { unsigned integer 8, { text } };
diff --git a/keama/tests/baddecl2record.msg b/keama/tests/baddecl2record.msg
new file mode 100644
index 00000000..4dfb2082
--- /dev/null
+++ b/keama/tests/baddecl2record.msg
@@ -0,0 +1 @@
+baddecl2record.err line 4: unknown data type {
diff --git a/keama/tests/baddeclBt.err b/keama/tests/baddeclBt.err
new file mode 100644
index 00000000..921521f2
--- /dev/null
+++ b/keama/tests/baddeclBt.err
@@ -0,0 +1,4 @@
+# bad Bt (not in record) option declaration
+
+option space foobar;
+option foobar.fmt-Bat code 1 = unsigned integer 8, text;
diff --git a/keama/tests/baddeclBt.msg b/keama/tests/baddeclBt.msg
new file mode 100644
index 00000000..49c63a58
--- /dev/null
+++ b/keama/tests/baddeclBt.msg
@@ -0,0 +1 @@
+baddeclBt.err line 4: semicolon expected.
diff --git a/keama/tests/baddefaultxsc.err b/keama/tests/baddefaultxsc.err
new file mode 100644
index 00000000..69157dd4
--- /dev/null
+++ b/keama/tests/baddefaultxsc.err
@@ -0,0 +1,7 @@
+# bad default executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# a raw default statement
+default: break;
diff --git a/keama/tests/baddefaultxsc.msg b/keama/tests/baddefaultxsc.msg
new file mode 100644
index 00000000..7780a1bb
--- /dev/null
+++ b/keama/tests/baddefaultxsc.msg
@@ -0,0 +1 @@
+baddefaultxsc.err line 7: switch default statement in inappropriate scope.
diff --git a/keama/tests/baddomain.notyet b/keama/tests/baddomain.notyet
new file mode 100644
index 00000000..a38f21c9
--- /dev/null
+++ b/keama/tests/baddomain.notyet
@@ -0,0 +1,6 @@
+# bad domain (label too long) option data
+
+option space foobar;
+option foobar.fmt-d code 1 = domain-name;
+option foobar.fmt-d
+ this-label-is-very-very-very-very-very-very-long-so-it-is-too-long.us;
diff --git a/keama/tests/badduid.err b/keama/tests/badduid.err
new file mode 100644
index 00000000..c3acab31
--- /dev/null
+++ b/keama/tests/badduid.err
@@ -0,0 +1,4 @@
+# bad (no type) server duid config
+
+# server duid declaration
+server-duid "enterprise-specific-identifier-1234";
diff --git a/keama/tests/badduid.msg b/keama/tests/badduid.msg
new file mode 100644
index 00000000..ebcaade4
--- /dev/null
+++ b/keama/tests/badduid.msg
@@ -0,0 +1 @@
+badduid.err line 4: DUID type of LLT, EN, or LL expected
diff --git a/keama/tests/badinclude.err b/keama/tests/badinclude.err
new file mode 100644
index 00000000..7ab310ff
--- /dev/null
+++ b/keama/tests/badinclude.err
@@ -0,0 +1,3 @@
+# bad include config
+
+include 192.168.0.1;
diff --git a/keama/tests/badinclude.msg b/keama/tests/badinclude.msg
new file mode 100644
index 00000000..e7ebf997
--- /dev/null
+++ b/keama/tests/badinclude.msg
@@ -0,0 +1 @@
+badinclude.err line 3: filename string expected.
diff --git a/keama/tests/badoption66.err6 b/keama/tests/badoption66.err6
new file mode 100644
index 00000000..a972d2f2
--- /dev/null
+++ b/keama/tests/badoption66.err6
@@ -0,0 +1,3 @@
+# bad '6' (Ipv6 address) option data
+
+option dhcp6.unicast "2001::1";
diff --git a/keama/tests/badoption66.msg b/keama/tests/badoption66.msg
new file mode 100644
index 00000000..227ce94d
--- /dev/null
+++ b/keama/tests/badoption66.msg
@@ -0,0 +1 @@
+badoption66.err6 line 3: Invalid IPv6 address.
diff --git a/keama/tests/badoptionD6.notyet b/keama/tests/badoptionD6.notyet
new file mode 100644
index 00000000..daf9541c
--- /dev/null
+++ b/keama/tests/badoptionD6.notyet
@@ -0,0 +1,4 @@
+# bad 'D' (domain-list) option data
+
+option dhcp6.domain-search example.com;
+
diff --git a/keama/tests/badoptionDc4.notyet b/keama/tests/badoptionDc4.notyet
new file mode 100644
index 00000000..f56fb46a
--- /dev/null
+++ b/keama/tests/badoptionDc4.notyet
@@ -0,0 +1,3 @@
+# bad 'Dc' (domain-list compressed) option data
+
+option domain-search example.com;
diff --git a/keama/tests/badoptionI4.err4 b/keama/tests/badoptionI4.err4
new file mode 100644
index 00000000..b0f6331d
--- /dev/null
+++ b/keama/tests/badoptionI4.err4
@@ -0,0 +1,3 @@
+# bad 'I' (IPv4 address) option data
+
+option swap-server "10.5.5.1";
diff --git a/keama/tests/badoptionI4.msg b/keama/tests/badoptionI4.msg
new file mode 100644
index 00000000..6609975d
--- /dev/null
+++ b/keama/tests/badoptionI4.msg
@@ -0,0 +1 @@
+badoptionI4.err4 line 3: 10.5.5.1 (262): expecting IP address or hostname
diff --git a/keama/tests/badoptiond4.err4 b/keama/tests/badoptiond4.err4
new file mode 100644
index 00000000..005c26cd
--- /dev/null
+++ b/keama/tests/badoptiond4.err4
@@ -0,0 +1,5 @@
+# bad d (domain-name) option data
+
+option space foobar;
+option foobar.fmt-d code 13 = domain-name;
+option foobar.fmt-d "www.example.com";
diff --git a/keama/tests/badoptiond4.msg b/keama/tests/badoptiond4.msg
new file mode 100644
index 00000000..e1f833e7
--- /dev/null
+++ b/keama/tests/badoptiond4.msg
@@ -0,0 +1 @@
+badoptiond4.err4 line 5: not a valid domain name.
diff --git a/keama/tests/badstatusdir.err b/keama/tests/badstatusdir.err
new file mode 100644
index 00000000..aab8fd56
--- /dev/null
+++ b/keama/tests/badstatusdir.err
@@ -0,0 +1,7 @@
+# Bad status directive
+
+option space foobar;
+option foobar.opt1 code 1 = text;
+
+# unknown/unknown does not make sense
+% option foobar.opt1 unknown unknown;
diff --git a/keama/tests/badstatusdir.msg b/keama/tests/badstatusdir.msg
new file mode 100644
index 00000000..d020cee4
--- /dev/null
+++ b/keama/tests/badstatusdir.msg
@@ -0,0 +1 @@
+badstatusdir.err line 7: illicit combination: option foobar.opt1 code 1 is known by nobody
diff --git a/keama/tests/badsubclass.err b/keama/tests/badsubclass.err
new file mode 100644
index 00000000..38c817b7
--- /dev/null
+++ b/keama/tests/badsubclass.err
@@ -0,0 +1,11 @@
+# bad (selector type) subclass declaration config
+
+# superclass declaration
+class "foobar" {
+ match substring(option vendor-class-identifier, 0, 3);
+}
+
+# subclass declaration
+subclass "foobar" true {
+ default-lease-time 1800;
+}
diff --git a/keama/tests/badsubclass.msg b/keama/tests/badsubclass.msg
new file mode 100644
index 00000000..a0a85028
--- /dev/null
+++ b/keama/tests/badsubclass.msg
@@ -0,0 +1 @@
+badsubclass.err line 9: Expecting string or hex list.
diff --git a/keama/tests/bintadx6.in6 b/keama/tests/bintadx6.in6
new file mode 100644
index 00000000..dc0cede7
--- /dev/null
+++ b/keama/tests/bintadx6.in6
@@ -0,0 +1,9 @@
+# binary-to-ascii data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# reduce literals
+class "literal" {
+ match if option dhcp6.client-data = binary-to-ascii(16, 8, "-", "\007foo");
+}
diff --git a/keama/tests/bintadx6.out b/keama/tests/bintadx6.out
new file mode 100644
index 00000000..999e3602
--- /dev/null
+++ b/keama/tests/bintadx6.out
@@ -0,0 +1,15 @@
+{
+ # binary-to-ascii data expression
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # reduce literals
+ {
+ "name": "literal",
+ /// from: match if (option dhcp6.client-data) = (binary-to-ascii(16, 8, '-', 'foo'))
+ "test": "option[45].hex == '7-66-6f-6f'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/bootfilename4.in4 b/keama/tests/bootfilename4.in4
new file mode 100644
index 00000000..5b6daeba
--- /dev/null
+++ b/keama/tests/bootfilename4.in4
@@ -0,0 +1,4 @@
+# Gitlab #22 dhcp4 option 67 wrong name
+
+option bootfile-name "boot.pxe";
+
diff --git a/keama/tests/bootfilename4.out b/keama/tests/bootfilename4.out
new file mode 100644
index 00000000..a08cb625
--- /dev/null
+++ b/keama/tests/bootfilename4.out
@@ -0,0 +1,13 @@
+{
+ # Gitlab #22 dhcp4 option 67 wrong name
+ "Dhcp4": {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "boot-file-name",
+ "code": 67,
+ "data": "boot.pxe"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/charcasedx4.in4 b/keama/tests/charcasedx4.in4
new file mode 100644
index 00000000..dd9ba03e
--- /dev/null
+++ b/keama/tests/charcasedx4.in4
@@ -0,0 +1,9 @@
+# lcase/ucase data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# reduce literals
+class "literal" {
+ match if option host-name = concat(ucase("www."), "example.", lcase("COM"));
+}
diff --git a/keama/tests/charcasedx4.out b/keama/tests/charcasedx4.out
new file mode 100644
index 00000000..fcdc20d1
--- /dev/null
+++ b/keama/tests/charcasedx4.out
@@ -0,0 +1,15 @@
+{
+ # lcase/ucase data expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # reduce literals
+ {
+ "name": "literal",
+ /// from: match if (option dhcp.host-name) = (concat(uppercase('www.'), concat('example.', lowercase('COM'))))
+ "test": "option[12].hex == 'WWW.example.com'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/checkall.sh b/keama/tests/checkall.sh
new file mode 100644
index 00000000..edeffdcb
--- /dev/null
+++ b/keama/tests/checkall.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+#set -x
+
+cd "$(dirname "$0")"
+
+log=/tmp/log
+rm $log 2> /dev/null
+
+for t in *.out
+do
+ /bin/sh checkone.sh $t
+ if [ $? -ne 0 ]; then
+ echo `basename $t` >> $log
+ fi
+done
diff --git a/keama/tests/checkone.sh b/keama/tests/checkone.sh
new file mode 100644
index 00000000..cbd5d9fd
--- /dev/null
+++ b/keama/tests/checkone.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+#set -x
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 test-name" >&2
+ exit 1
+fi
+
+if [ x$KEA4 = x ]; then
+ echo "KEA4 is not set" >&2
+fi
+if [ x$KEA6 = x ]; then
+ echo "KEA6 is not set" >&2
+fi
+
+file=$1
+
+cd "$(dirname "$0")"
+
+isout=$(expr $file : ".*\.out")
+if [ $isout -eq 0 ]; then
+ full=$file.out
+else
+ full=$file
+fi
+if [ ! -f $full ]; then
+ echo "can't find $file" >&2
+ exit 1
+fi
+
+is4=$(expr $file : ".*4")
+is6=$(expr $file : ".*6")
+if [ \( $is4 -eq 0 \) -a \( $is6 -eq 0 \) ]; then
+ echo "can't get version from $file" >&2
+ exit 1
+fi
+
+base=$(basename $full .out)
+log=/tmp/$base.log$$
+if [ $is4 -ne 0 ]; then
+ $KEA4 -t $full >& $log
+ if [ $? -ne 0 ]; then
+ echo "$full raised an error" >&2
+ exit 1
+ fi
+fi
+if [ $is6 -ne 0 ]; then
+ $KEA6 -t $full >& $log
+ if [ $? -ne 0 ]; then
+ echo "$full raised an error" >&2
+ exit 1
+ fi
+fi
diff --git a/keama/tests/class4.in4 b/keama/tests/class4.in4
new file mode 100644
index 00000000..f573a02c
--- /dev/null
+++ b/keama/tests/class4.in4
@@ -0,0 +1,11 @@
+# class declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+
+# class declaration
+class "foobar" {
+ match if option mysystem = "version1";
+ option myversion 1;
+}
diff --git a/keama/tests/class4.out b/keama/tests/class4.out
new file mode 100644
index 00000000..41ddf27f
--- /dev/null
+++ b/keama/tests/class4.out
@@ -0,0 +1,36 @@
+{
+ # class declaration config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "mysystem",
+ "code": 250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # class declaration
+ {
+ "name": "foobar",
+ /// from: match if (option dhcp.mysystem) = 'version1'
+ "test": "option[250].hex == 'version1'",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/class4empty.in4 b/keama/tests/class4empty.in4
new file mode 100644
index 00000000..d0b9faa5
--- /dev/null
+++ b/keama/tests/class4empty.in4
@@ -0,0 +1,5 @@
+# void class declaration config
+
+# class declaration
+class "foobar" {
+}
diff --git a/keama/tests/class4empty.out b/keama/tests/class4empty.out
new file mode 100644
index 00000000..d2359856
--- /dev/null
+++ b/keama/tests/class4empty.out
@@ -0,0 +1,11 @@
+{
+ # void class declaration config
+ # class declaration
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "foobar"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/class6.in6 b/keama/tests/class6.in6
new file mode 100644
index 00000000..039a80c9
--- /dev/null
+++ b/keama/tests/class6.in6
@@ -0,0 +1,11 @@
+# class declaration config
+
+# options
+option dhcp6.mysystem code 1250 = text;
+option dhcp6.myversion code 1251 = unsigned integer 16;
+
+# class declaration
+class "foobar" {
+ match if option dhcp6.mysystem = "version1";
+ option dhcp6.myversion 1;
+}
diff --git a/keama/tests/class6.out b/keama/tests/class6.out
new file mode 100644
index 00000000..8c9af83d
--- /dev/null
+++ b/keama/tests/class6.out
@@ -0,0 +1,36 @@
+{
+ # class declaration config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "mysystem",
+ "code": 1250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # class declaration
+ {
+ "name": "foobar",
+ /// from: match if (option dhcp6.mysystem) = 'version1'
+ "test": "option[1250].hex == 'version1'",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/class6empty.in6 b/keama/tests/class6empty.in6
new file mode 100644
index 00000000..d0b9faa5
--- /dev/null
+++ b/keama/tests/class6empty.in6
@@ -0,0 +1,5 @@
+# void class declaration config
+
+# class declaration
+class "foobar" {
+}
diff --git a/keama/tests/class6empty.out b/keama/tests/class6empty.out
new file mode 100644
index 00000000..a9869b5f
--- /dev/null
+++ b/keama/tests/class6empty.out
@@ -0,0 +1,11 @@
+{
+ # void class declaration config
+ # class declaration
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "foobar"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/classbadmatch.err b/keama/tests/classbadmatch.err
new file mode 100644
index 00000000..b9eac948
--- /dev/null
+++ b/keama/tests/classbadmatch.err
@@ -0,0 +1,7 @@
+# bad (match with a boolean expression) class declaration config
+
+# class declaration
+class "foobar" {
+ match option server.duplicates = 0;
+ default-lease-time 1800;
+}
diff --git a/keama/tests/classbadmatch.msg b/keama/tests/classbadmatch.msg
new file mode 100644
index 00000000..5fcda249
--- /dev/null
+++ b/keama/tests/classbadmatch.msg
@@ -0,0 +1 @@
+classbadmatch.err line 5: Expecting a data expression.
diff --git a/keama/tests/classbadmatchif.err b/keama/tests/classbadmatchif.err
new file mode 100644
index 00000000..7eb88d65
--- /dev/null
+++ b/keama/tests/classbadmatchif.err
@@ -0,0 +1,7 @@
+# bad (match if with a data expression) class declaration config
+
+# class declaration
+class "foobar" {
+ match if option server.duplicates;
+ default-lease-time 1800;
+}
diff --git a/keama/tests/classbadmatchif.msg b/keama/tests/classbadmatchif.msg
new file mode 100644
index 00000000..073d48c2
--- /dev/null
+++ b/keama/tests/classbadmatchif.msg
@@ -0,0 +1 @@
+classbadmatchif.err line 5: Expecting a boolean expression.
diff --git a/keama/tests/classinclass.err b/keama/tests/classinclass.err
new file mode 100644
index 00000000..36aa0b4b
--- /dev/null
+++ b/keama/tests/classinclass.err
@@ -0,0 +1,10 @@
+# class declaration inside class declaration config
+
+# class declaration
+class "foobar" {
+ # can't put a class declaration here
+ subclass "foobar" "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/classinclass.msg b/keama/tests/classinclass.msg
new file mode 100644
index 00000000..5e1c26c9
--- /dev/null
+++ b/keama/tests/classinclass.msg
@@ -0,0 +1 @@
+classinclass.err line 6: class declarations not allowed here.
diff --git a/keama/tests/concatdx4.in4 b/keama/tests/concatdx4.in4
new file mode 100644
index 00000000..b94a3a2f
--- /dev/null
+++ b/keama/tests/concatdx4.in4
@@ -0,0 +1,19 @@
+# concat data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# use substring in a reductible match
+class "reductible" {
+ match concat("domain=", suffix(option host-name, 3));
+}
+
+subclass "reductible" "domain=com" { }
+
+# reduce literals too
+class "literal" {
+ match if option host-name = concat("www.", "example.", "com");
+}
+
+# raw
+option host-name = concat("www.", option domain-name);
diff --git a/keama/tests/concatdx4.out b/keama/tests/concatdx4.out
new file mode 100644
index 00000000..4602a497
--- /dev/null
+++ b/keama/tests/concatdx4.out
@@ -0,0 +1,48 @@
+{
+ # concat data expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # use substring in a reductible match
+ /// match: concat('domain=', suffix(option dhcp.host-name, 3))
+ {
+ "name": "reductible"
+ },
+ /// subclass selector 'domain=com'
+ {
+ "name": "sub#reductible#0",
+ /// from: match concat('domain=', suffix(option dhcp.host-name, 3))
+ /// data: 'domain=com'
+ "test": "concat('domain=', substring(option[12].hex,-3,all)) == 'domain=com'"
+ },
+ # reduce literals too
+ {
+ "name": "literal",
+ /// from: match if (option dhcp.host-name) = (concat('www.', concat('example.', 'com')))
+ "test": "option[12].hex == 'www.example.com'"
+ }
+ ],
+ "option-data": [
+// # raw
+// {
+// "space": "dhcp4",
+// "name": "host-name",
+// "code": 12,
+// "csv-format": false,
+// "expression": {
+// "concat": {
+// "left": "www.",
+// "right": {
+// "option": {
+// "universe": "dhcp",
+// "name": "domain-name",
+// "code": 15
+// }
+// }
+// }
+// }
+// }
+ ]
+ }
+}
diff --git a/keama/tests/concatnulldx4.in4 b/keama/tests/concatnulldx4.in4
new file mode 100644
index 00000000..9b5e2a42
--- /dev/null
+++ b/keama/tests/concatnulldx4.in4
@@ -0,0 +1,18 @@
+# concat with null argument data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# null left argument
+class "null-left" {
+ match concat("", suffix(option host-name, 3));
+}
+
+subclass "null-left" "com" { }
+
+# null right argument
+class "null-right" {
+ match concat(suffix(option host-name, 3), substring("foobar", 0, 0));
+}
+
+subclass "null-right" "org" { }
diff --git a/keama/tests/concatnulldx4.out b/keama/tests/concatnulldx4.out
new file mode 100644
index 00000000..66cdbdf8
--- /dev/null
+++ b/keama/tests/concatnulldx4.out
@@ -0,0 +1,33 @@
+{
+ # concat with null argument data expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # null left argument
+ /// match: concat('', suffix(option dhcp.host-name, 3))
+ {
+ "name": "null-left"
+ },
+ /// subclass selector 'com'
+ {
+ "name": "sub#null-left#0",
+ /// from: match concat('', suffix(option dhcp.host-name, 3))
+ /// data: 'com'
+ "test": "substring(option[12].hex,-3,all) == 'com'"
+ },
+ # null right argument
+ /// match: concat(suffix(option dhcp.host-name, 3), substring('foobar', 0, 0))
+ {
+ "name": "null-right"
+ },
+ /// subclass selector 'org'
+ {
+ "name": "sub#null-right#1",
+ /// from: match concat(suffix(option dhcp.host-name, 3), substring('foobar', 0, 0))
+ /// data: 'org'
+ "test": "substring(option[12].hex,-3,all) == 'org'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/configdata4.in4 b/keama/tests/configdata4.in4
new file mode 100644
index 00000000..f8c5c7bc
--- /dev/null
+++ b/keama/tests/configdata4.in4
@@ -0,0 +1,26 @@
+# config (aka server option space data config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# configs
+dynamic-bootp-lease-length 1200;
+
+boot-unknown-clients false;
+
+min-secs 20;
+
+lease-file-name "/tmp/leases";
+
+local-port 10067;
+
+local-address 10.5.5.1;
+
+# not quoted?
+omapi-key my.key.biz;
+
+limit-addrs-per-ia 12345;
+
+ddns-local-address6 2001::1;
+
+pid-file-name = "/tmp/pid";
diff --git a/keama/tests/configdata4.out b/keama/tests/configdata4.out
new file mode 100644
index 00000000..a1af336e
--- /dev/null
+++ b/keama/tests/configdata4.out
@@ -0,0 +1,74 @@
+{
+ # config (aka server option space data config
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// "config": [
+// /// bootp protocol is not supported
+// {
+// "name": "dynamic-bootp-lease-length",
+// "code": 5,
+// "value": 1200
+// },
+// /// bootp protocol is not supported
+// {
+// "name": "boot-unknown-clients",
+// "code": 6,
+// "value": false
+// },
+// /// min-secs is not (yet?) supported
+// /// Reference Kea #223
+// {
+// "name": "min-secs",
+// "code": 14,
+// "value": 5120
+// },
+// /// lease-file-name is an internal ISC DHCP feature
+// {
+// "name": "lease-file-name",
+// "code": 26,
+// "value": "/tmp/leases"
+// },
+// /// local-port is not supported
+// /// command line -p parameter should be used instead
+// {
+// "name": "local-port",
+// "code": 32,
+// "value": 10067
+// },
+// /// local-address is not supported
+// /// Kea equivalent feature is to specify an interface address
+// {
+// "name": "local-address",
+// "code": 35,
+// "value": "10.5.5.1"
+// },
+// /// omapi-key is an internal ISC DHCP feature
+// {
+// "name": "omapi-key",
+// "code": 36,
+// "value": "my.key.biz"
+// },
+// /// limit-addrs-per-ia is not (yet?) supported
+// /// Reference Kea #227
+// {
+// "name": "limit-addrs-per-ia",
+// "code": 56,
+// "value": 12345
+// },
+// /// ddns-local-address6 is not supported
+// /// Kea D2 equivalent config is ip-address
+// {
+// "name": "ddns-local-address6",
+// "code": 81,
+// "value": "2001::1"
+// },
+// /// pid-file-nam is an internal ISC DHCP feature
+// {
+// "name": "pid-file-name",
+// "code": 27,
+// "value": "/tmp/pid"
+// }
+// ]
+ }
+}
diff --git a/keama/tests/dbtimeformat4.in4 b/keama/tests/dbtimeformat4.in4
new file mode 100644
index 00000000..b3225a82
--- /dev/null
+++ b/keama/tests/dbtimeformat4.in4
@@ -0,0 +1,6 @@
+# db-time-format config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+db-time-format default;
diff --git a/keama/tests/dbtimeformat4.out b/keama/tests/dbtimeformat4.out
new file mode 100644
index 00000000..0988bfbd
--- /dev/null
+++ b/keama/tests/dbtimeformat4.out
@@ -0,0 +1,10 @@
+{
+ # db-time-format config
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// "statement": {
+// "db-time-format": "default"
+// }
+ }
+}
diff --git a/keama/tests/dbtimeformat6.in6 b/keama/tests/dbtimeformat6.in6
new file mode 100644
index 00000000..619d1c91
--- /dev/null
+++ b/keama/tests/dbtimeformat6.in6
@@ -0,0 +1,6 @@
+# db-time-format config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+db-time-format local;
diff --git a/keama/tests/dbtimeformat6.out b/keama/tests/dbtimeformat6.out
new file mode 100644
index 00000000..42460de6
--- /dev/null
+++ b/keama/tests/dbtimeformat6.out
@@ -0,0 +1,10 @@
+{
+ # db-time-format config
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800
+// "statement": {
+// "db-time-format": "local"
+// }
+ }
+}
diff --git a/keama/tests/ddnsupdstyle6.in6 b/keama/tests/ddnsupdstyle6.in6
new file mode 100644
index 00000000..ef23fe38
--- /dev/null
+++ b/keama/tests/ddnsupdstyle6.in6
@@ -0,0 +1,12 @@
+# ddns-update-style
+
+ddns-update-style standard;
+
+# embedded in pool
+subnet6 2001::/64 {
+ pool6 {
+ ddns-update-style interim;
+ range6 2001::1000 2001::1fff;
+ }
+}
+
diff --git a/keama/tests/ddnsupdstyle6.out b/keama/tests/ddnsupdstyle6.out
new file mode 100644
index 00000000..88323bb0
--- /dev/null
+++ b/keama/tests/ddnsupdstyle6.out
@@ -0,0 +1,29 @@
+{
+ # ddns-update-style
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "dhcp-ddns": {
+ /// Unspecified ddns-domainname (default domain-name option value)
+ /// Kea requires a qualifying-suffix
+ /// Initialized to "": please put a value
+ "qualifying-suffix": "",
+ "enable-updates": true
+ },
+ "subnet6": [
+ # embedded in pool
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ {
+// /// Unsupported ddns-update-style interim
+// /// Only global ddns-update-style is supported
+// "ddns-update-style": "interim",
+ "pool": "2001::1000 - 2001::1fff"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/defaultexpr6.in6 b/keama/tests/defaultexpr6.in6
new file mode 100644
index 00000000..e81bd8d3
--- /dev/null
+++ b/keama/tests/defaultexpr6.in6
@@ -0,0 +1,10 @@
+# default expressions
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# default expression is a variable reference
+foo;
+
+# or a function call when there are parentheses
+bar ("abcd", "xyxt");
diff --git a/keama/tests/defaultexpr6.out b/keama/tests/defaultexpr6.out
new file mode 100644
index 00000000..8646e8af
--- /dev/null
+++ b/keama/tests/defaultexpr6.out
@@ -0,0 +1,25 @@
+{
+ # default expressions
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800
+// # default expression is a variable reference
+// "statement": {
+// "eval": {
+// "variable-reference": "foo"
+// }
+// }
+// # or a function call when there are parentheses
+// "statement": {
+// "eval": {
+// "funcall": {
+// "name": "bar",
+// "arguments": [
+// "abcd",
+// "xyxt"
+// ]
+// }
+// }
+// }
+ }
+}
diff --git a/keama/tests/denyunknown6.in6 b/keama/tests/denyunknown6.in6
new file mode 100644
index 00000000..ccd5541a
--- /dev/null
+++ b/keama/tests/denyunknown6.in6
@@ -0,0 +1,15 @@
+# DHCPv6 deny unknown client config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# subnet declaration
+subnet6 2001::/64 {
+ # pool declaration
+ pool6 {
+ # avoid empty pool
+ range6 2001::100 2001::200;
+ # call get_permit
+ deny unknown clients;
+ }
+}
diff --git a/keama/tests/denyunknown6.out b/keama/tests/denyunknown6.out
new file mode 100644
index 00000000..68db27b7
--- /dev/null
+++ b/keama/tests/denyunknown6.out
@@ -0,0 +1,32 @@
+{
+ # DHCPv6 deny unknown client config
+ # empty configs are not accepted by Kea
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "subnet6": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "2001::100 - 2001::200",
+ /// From:
+ /// deny unknown clients
+ "client-class": "gen#_AND_#KNOWN#"
+ }
+ ]
+ }
+ ],
+ "client-classes": [
+ {
+ "name": "gen#_AND_#KNOWN#",
+ "test": "member('KNOWN')"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/docsis4.dir b/keama/tests/docsis4.dir
new file mode 100644
index 00000000..82b206ef
--- /dev/null
+++ b/keama/tests/docsis4.dir
@@ -0,0 +1,11 @@
+# DOCSIS DHCPv4 vendor space
+
+option space docsis;
+
+option vendor.docsis code 4491 = encapsulate docsis;
+
+option docsis.oro code 1 = array of unsigned integer 8;
+option docsis.tftp-servers code 2 = array of ip-address;
+
+% option docsis.oro local;
+% option docsis.tftp-servers local;
diff --git a/keama/tests/docsis6.dir b/keama/tests/docsis6.dir
new file mode 100644
index 00000000..ff7cd62a
--- /dev/null
+++ b/keama/tests/docsis6.dir
@@ -0,0 +1,27 @@
+# DOCSIS DHCPv6 vendor space
+
+option space docsis;
+
+option vsio.docsis code 4491 = encapsulate docsis;
+
+option docsis.oro code 1 = array of unsigned integer 16;
+option docsis.device-type code 2 = text;
+option docsis.vendor-type code 10 = text;
+option docsis.tftp-servers code 32 = array of ip6-address;
+option docsis.config-file code 33 = text;
+option docsis.syslog-servers code 34 = array of ip6-address;
+% option docsis.device-id code 36 = "X";
+option docsis.time-servers code 37 = array of ip6-address;
+option docsis.time-offset code 38 = unsigned integer 32;
+% option docsis.cmts-cm-mac code 1026 = "X";
+
+% option docsis.oro local;
+% option docsis.device-type local;
+% option docsis.vendor-type local;
+% option docsis.tftp-servers local;
+% option docsis.config-file local;
+% option docsis.syslog-servers local;
+% option docsis.device-id local;
+% option docsis.time-servers local;
+% option docsis.time-offset local;
+% option docsis.cmts-cm-mac local;
diff --git a/keama/tests/duid2.err b/keama/tests/duid2.err
new file mode 100644
index 00000000..fcbef592
--- /dev/null
+++ b/keama/tests/duid2.err
@@ -0,0 +1,7 @@
+# two server duid config
+
+# EN server duid declaration
+server-duid en 2495 "enterprise-specific-identifier-1234";
+
+# LL server duid declaration
+server-duid ll ethernet 00:16:6F:49:7D:9B;
diff --git a/keama/tests/duid2.msg b/keama/tests/duid2.msg
new file mode 100644
index 00000000..06a8a00f
--- /dev/null
+++ b/keama/tests/duid2.msg
@@ -0,0 +1 @@
+duid2.err line 7: there is already a server-id
diff --git a/keama/tests/duiden6.in6 b/keama/tests/duiden6.in6
new file mode 100644
index 00000000..ae8385ea
--- /dev/null
+++ b/keama/tests/duiden6.in6
@@ -0,0 +1,4 @@
+# EN server duid config
+
+# EN server duid declaration
+server-duid en 2495 "enterprise-specific-identifier-1234";
diff --git a/keama/tests/duiden6.out b/keama/tests/duiden6.out
new file mode 100644
index 00000000..1e7e3021
--- /dev/null
+++ b/keama/tests/duiden6.out
@@ -0,0 +1,11 @@
+{
+ # EN server duid config
+ # EN server duid declaration
+ "Dhcp6": {
+ "server-id": {
+ "type": "EN",
+ "enterprise-id": 2495,
+ "identifier": "656e74657270726973652d73706563696669632d6964656e7469666965722d31323334"
+ }
+ }
+}
diff --git a/keama/tests/duidennoid.err b/keama/tests/duidennoid.err
new file mode 100644
index 00000000..ae6d12ae
--- /dev/null
+++ b/keama/tests/duidennoid.err
@@ -0,0 +1,5 @@
+# bad (no identifier) EN server duid config
+
+# EN server duid declaration
+server-duid en 2495;
+
diff --git a/keama/tests/duidennoid.msg b/keama/tests/duidennoid.msg
new file mode 100644
index 00000000..054fa65e
--- /dev/null
+++ b/keama/tests/duidennoid.msg
@@ -0,0 +1 @@
+duidennoid.err line 4: identifier expected
diff --git a/keama/tests/duidennonum.err b/keama/tests/duidennonum.err
new file mode 100644
index 00000000..abdfdd7c
--- /dev/null
+++ b/keama/tests/duidennonum.err
@@ -0,0 +1,4 @@
+# bad (no number) EN server duid config
+
+# EN server duid declaration
+server-duid en "enterprise-specific-identifier-1234";
diff --git a/keama/tests/duidennonum.msg b/keama/tests/duidennonum.msg
new file mode 100644
index 00000000..941a0d03
--- /dev/null
+++ b/keama/tests/duidennonum.msg
@@ -0,0 +1 @@
+duidennonum.err line 4: enterprise number expected
diff --git a/keama/tests/duidll6.in6 b/keama/tests/duidll6.in6
new file mode 100644
index 00000000..fac38c7a
--- /dev/null
+++ b/keama/tests/duidll6.in6
@@ -0,0 +1,5 @@
+# LL server duid config
+
+# LL server duid declaration
+server-duid ll;
+
diff --git a/keama/tests/duidll6.out b/keama/tests/duidll6.out
new file mode 100644
index 00000000..089539d7
--- /dev/null
+++ b/keama/tests/duidll6.out
@@ -0,0 +1,9 @@
+{
+ # LL server duid config
+ # LL server duid declaration
+ "Dhcp6": {
+ "server-id": {
+ "type": "LL"
+ }
+ }
+}
diff --git a/keama/tests/duidllbadtype.err b/keama/tests/duidllbadtype.err
new file mode 100644
index 00000000..2526f6e1
--- /dev/null
+++ b/keama/tests/duidllbadtype.err
@@ -0,0 +1,4 @@
+# bad (unknown hardware type) LL server duid config
+
+# LL server duid declaration
+server-duid ll foobar 00:16:6F:49:7D:9B;
diff --git a/keama/tests/duidllbadtype.msg b/keama/tests/duidllbadtype.msg
new file mode 100644
index 00000000..341ebd0b
--- /dev/null
+++ b/keama/tests/duidllbadtype.msg
@@ -0,0 +1 @@
+duidllbadtype.err line 4: hardware type expected
diff --git a/keama/tests/duidllhw6.in6 b/keama/tests/duidllhw6.in6
new file mode 100644
index 00000000..48312801
--- /dev/null
+++ b/keama/tests/duidllhw6.in6
@@ -0,0 +1,6 @@
+# LL server duid config
+
+# LL server duid declaration
+server-duid ll ethernet 00:16:6F:49:7D:9B;
+
+
diff --git a/keama/tests/duidllhw6.out b/keama/tests/duidllhw6.out
new file mode 100644
index 00000000..ad7fbdac
--- /dev/null
+++ b/keama/tests/duidllhw6.out
@@ -0,0 +1,11 @@
+{
+ # LL server duid config
+ # LL server duid declaration
+ "Dhcp6": {
+ "server-id": {
+ "type": "LL",
+ "htype": 1,
+ "identifier": "00166f497d9b"
+ }
+ }
+}
diff --git a/keama/tests/duidllnohw.err b/keama/tests/duidllnohw.err
new file mode 100644
index 00000000..a93b90d3
--- /dev/null
+++ b/keama/tests/duidllnohw.err
@@ -0,0 +1,4 @@
+# bad (no hardware address) LL server duid config
+
+# LL server duid declaration
+server-duid ll fddi;
diff --git a/keama/tests/duidllnohw.msg b/keama/tests/duidllnohw.msg
new file mode 100644
index 00000000..b2d955ec
--- /dev/null
+++ b/keama/tests/duidllnohw.msg
@@ -0,0 +1 @@
+duidllnohw.err line 4: expecting hexadecimal number.
diff --git a/keama/tests/duidllt6.in6 b/keama/tests/duidllt6.in6
new file mode 100644
index 00000000..25420b3c
--- /dev/null
+++ b/keama/tests/duidllt6.in6
@@ -0,0 +1,5 @@
+# LLT server duid config
+
+# LLT server duid declaration
+server-duid llt;
+
diff --git a/keama/tests/duidllt6.out b/keama/tests/duidllt6.out
new file mode 100644
index 00000000..2a1ea79e
--- /dev/null
+++ b/keama/tests/duidllt6.out
@@ -0,0 +1,9 @@
+{
+ # LLT server duid config
+ # LLT server duid declaration
+ "Dhcp6": {
+ "server-id": {
+ "type": "LLT"
+ }
+ }
+}
diff --git a/keama/tests/duidlltbadtype.err b/keama/tests/duidlltbadtype.err
new file mode 100644
index 00000000..66ab6642
--- /dev/null
+++ b/keama/tests/duidlltbadtype.err
@@ -0,0 +1,4 @@
+# bad (unknown hardware type) LLT server duid config
+
+# LLT server duid declaration
+server-duid llt foobar 213982198 00:16:6F:49:7D:9B;
diff --git a/keama/tests/duidlltbadtype.msg b/keama/tests/duidlltbadtype.msg
new file mode 100644
index 00000000..32305a75
--- /dev/null
+++ b/keama/tests/duidlltbadtype.msg
@@ -0,0 +1 @@
+duidlltbadtype.err line 4: hardware type expected
diff --git a/keama/tests/duidlltnohw.err b/keama/tests/duidlltnohw.err
new file mode 100644
index 00000000..3208ed60
--- /dev/null
+++ b/keama/tests/duidlltnohw.err
@@ -0,0 +1,4 @@
+# bad (no hardware address) LLT server duid config
+
+# LLT server duid declaration
+server-duid llt token-ring 213982198;
diff --git a/keama/tests/duidlltnohw.msg b/keama/tests/duidlltnohw.msg
new file mode 100644
index 00000000..7bfaa245
--- /dev/null
+++ b/keama/tests/duidlltnohw.msg
@@ -0,0 +1 @@
+duidlltnohw.err line 4: expecting hexadecimal number.
diff --git a/keama/tests/duidlltnotime.err b/keama/tests/duidlltnotime.err
new file mode 100644
index 00000000..f53321bb
--- /dev/null
+++ b/keama/tests/duidlltnotime.err
@@ -0,0 +1,4 @@
+# bad (no timestamp) LLT server duid config
+
+# LLT server duid declaration
+server-duid llt token-ring A8:16:6F:49:7D:9B;
diff --git a/keama/tests/duidlltnotime.msg b/keama/tests/duidlltnotime.msg
new file mode 100644
index 00000000..2b7fb8a3
--- /dev/null
+++ b/keama/tests/duidlltnotime.msg
@@ -0,0 +1 @@
+duidlltnotime.err line 4: timestamp expected
diff --git a/keama/tests/duidlltthw4.err4 b/keama/tests/duidlltthw4.err4
new file mode 100644
index 00000000..ae039d18
--- /dev/null
+++ b/keama/tests/duidlltthw4.err4
@@ -0,0 +1,4 @@
+# LLT server duid config
+
+# LLT server duid declaration
+server-duid llt token-ring 213982198 00:16:6F:49:7D:9B;
diff --git a/keama/tests/duidlltthw4.msg b/keama/tests/duidlltthw4.msg
new file mode 100644
index 00000000..df163bb1
--- /dev/null
+++ b/keama/tests/duidlltthw4.msg
@@ -0,0 +1 @@
+duidlltthw4.err4 line 4: expecting a parameter or declaration
diff --git a/keama/tests/duidlltthw6.in6 b/keama/tests/duidlltthw6.in6
new file mode 100644
index 00000000..ae039d18
--- /dev/null
+++ b/keama/tests/duidlltthw6.in6
@@ -0,0 +1,4 @@
+# LLT server duid config
+
+# LLT server duid declaration
+server-duid llt token-ring 213982198 00:16:6F:49:7D:9B;
diff --git a/keama/tests/duidlltthw6.out b/keama/tests/duidlltthw6.out
new file mode 100644
index 00000000..de2d0a48
--- /dev/null
+++ b/keama/tests/duidlltthw6.out
@@ -0,0 +1,12 @@
+{
+ # LLT server duid config
+ # LLT server duid declaration
+ "Dhcp6": {
+ "server-id": {
+ "type": "LLT",
+ "htype": 6,
+ "time": 213982198,
+ "identifier": "00166f497d9b"
+ }
+ }
+}
diff --git a/keama/tests/duidnoid.err b/keama/tests/duidnoid.err
new file mode 100644
index 00000000..32a2c9cb
--- /dev/null
+++ b/keama/tests/duidnoid.err
@@ -0,0 +1,4 @@
+# bad (no identifier) numeric server duid config
+
+# server duid declaration
+server-duid 9;
diff --git a/keama/tests/duidnoid.msg b/keama/tests/duidnoid.msg
new file mode 100644
index 00000000..3e44395a
--- /dev/null
+++ b/keama/tests/duidnoid.msg
@@ -0,0 +1 @@
+duidnoid.err line 4: identifier expected
diff --git a/keama/tests/enableupdates6.in6 b/keama/tests/enableupdates6.in6
new file mode 100644
index 00000000..b6641957
--- /dev/null
+++ b/keama/tests/enableupdates6.in6
@@ -0,0 +1,8 @@
+# ddns-updates (aka enable-updates)
+
+ddns-updates on;
+
+# embedded
+class "foo" {
+ ddns-updates off;
+}
diff --git a/keama/tests/enableupdates6.out b/keama/tests/enableupdates6.out
new file mode 100644
index 00000000..8d20a773
--- /dev/null
+++ b/keama/tests/enableupdates6.out
@@ -0,0 +1,20 @@
+{
+ # ddns-updates (aka enable-updates)
+ "Dhcp6": {
+ "dhcp-ddns": {
+ /// Unspecified ddns-domainname (default domain-name option value)
+ /// Kea requires a qualifying-suffix
+ /// Initialized to "": please put a value
+ "qualifying-suffix": "",
+ "enable-updates": true
+ },
+ "client-classes": [
+ # embedded
+ {
+ "name": "foo"
+// /// Only global enable-updates is supported
+// "enable-updates": false
+ }
+ ]
+ }
+}
diff --git a/keama/tests/encodedx6.in6 b/keama/tests/encodedx6.in6
new file mode 100644
index 00000000..f6b16377
--- /dev/null
+++ b/keama/tests/encodedx6.in6
@@ -0,0 +1,9 @@
+# encode data expression and extract numeric expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# reduce literals
+class "literal" {
+ match if option dhcp6.client-data != encode-int(extract-int("\bbar",32),16);
+}
diff --git a/keama/tests/encodedx6.out b/keama/tests/encodedx6.out
new file mode 100644
index 00000000..d1b8b38a
--- /dev/null
+++ b/keama/tests/encodedx6.out
@@ -0,0 +1,15 @@
+{
+ # encode data expression and extract numeric expression
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # reduce literals
+ {
+ "name": "literal",
+ /// from: match if (option dhcp6.client-data) != (encode-int(extract-int('bar', 32), 16))
+ "test": "not (option[45].hex == 'ar')"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/env b/keama/tests/env
new file mode 100644
index 00000000..9fe0fcd4
--- /dev/null
+++ b/keama/tests/env
@@ -0,0 +1,3 @@
+setenv KEA4 /tmp/kea/src/bin/dhcp4/kea-dhcp4
+setenv KEA6 /tmp/kea/src/bin/dhcp6/kea-dhcp6
+setenv HOOK /tmp/kea/premium/src/hooks/dhcp/flex_id/.libs/
diff --git a/keama/tests/escapestring4.in4 b/keama/tests/escapestring4.in4
new file mode 100644
index 00000000..3f318e6c
--- /dev/null
+++ b/keama/tests/escapestring4.in4
@@ -0,0 +1,6 @@
+# string option-data with embedded commas
+
+# vendor option space
+option a-string code 250 = text;
+
+option a-string "foo, bar";
diff --git a/keama/tests/escapestring4.out b/keama/tests/escapestring4.out
new file mode 100644
index 00000000..caef76c3
--- /dev/null
+++ b/keama/tests/escapestring4.out
@@ -0,0 +1,23 @@
+{
+ # string option-data with embedded commas
+ # vendor option space
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "a-string",
+ "code": 250,
+ "type": "string"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "a-string",
+ "code": 250,
+// "original-data": "\"foo, bar\"",
+ "data": "foo\\, bar"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/execstatement4.in4 b/keama/tests/execstatement4.in4
new file mode 100644
index 00000000..6adc6b1e
--- /dev/null
+++ b/keama/tests/execstatement4.in4
@@ -0,0 +1,7 @@
+# DHCPv4 executable statement config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# impossible to convert statement statement
+break;
diff --git a/keama/tests/execstatement4.out b/keama/tests/execstatement4.out
new file mode 100644
index 00000000..4988de92
--- /dev/null
+++ b/keama/tests/execstatement4.out
@@ -0,0 +1,11 @@
+{
+ # DHCPv4 executable statement config
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// # impossible to convert statement statement
+// "statement": {
+// "break": null
+// }
+ }
+}
diff --git a/keama/tests/execstatement6.in6 b/keama/tests/execstatement6.in6
new file mode 100644
index 00000000..fe49424a
--- /dev/null
+++ b/keama/tests/execstatement6.in6
@@ -0,0 +1,7 @@
+# DHCPv6 executable statement config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# impossible to convert statement statement
+break;
diff --git a/keama/tests/execstatement6.out b/keama/tests/execstatement6.out
new file mode 100644
index 00000000..25ac01af
--- /dev/null
+++ b/keama/tests/execstatement6.out
@@ -0,0 +1,11 @@
+{
+ # DHCPv6 executable statement config
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800
+// # impossible to convert statement statement
+// "statement": {
+// "break": null
+// }
+ }
+}
diff --git a/keama/tests/existsbx4.in4 b/keama/tests/existsbx4.in4
new file mode 100644
index 00000000..650a208e
--- /dev/null
+++ b/keama/tests/existsbx4.in4
@@ -0,0 +1,15 @@
+# exists boolean expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# use exists in a reductible match if
+class "reductible" {
+ match if exists host-name;
+}
+
+# if test is a boolean too
+if exists host-name {
+ log(info, concat("hostname:", option host-name));
+}
+
diff --git a/keama/tests/existsbx4.out b/keama/tests/existsbx4.out
new file mode 100644
index 00000000..7d977ac6
--- /dev/null
+++ b/keama/tests/existsbx4.out
@@ -0,0 +1,48 @@
+{
+ # exists boolean expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # use exists in a reductible match if
+ {
+ "name": "reductible",
+ /// from: match if exists dhcp.host-name
+ "test": "option[12].exists"
+ }
+ ]
+// # if test is a boolean too
+// "statement": {
+// "if": {
+// "condition": {
+// "exists": {
+// "universe": "dhcp",
+// "name": "host-name",
+// "code": 12
+// }
+// },
+// "then": [
+// {
+// /// Kea does not support yet log statements
+// /// Reference Kea #234
+// "log": {
+// "priority": "info",
+// "message": {
+// "concat": {
+// "left": "hostname:",
+// "right": {
+// "option": {
+// "universe": "dhcp",
+// "name": "host-name",
+// "code": 12
+// }
+// }
+// }
+// }
+// }
+// }
+// ]
+// }
+// }
+ }
+}
diff --git a/keama/tests/filename4.in4 b/keama/tests/filename4.in4
new file mode 100644
index 00000000..9bf3ad00
--- /dev/null
+++ b/keama/tests/filename4.in4
@@ -0,0 +1,10 @@
+# filename (aka boot-file-name) and server-name (aka server-hostname)
+
+filename "/var/boot/boot1";
+server-name "foobar.biz";
+
+# embedded
+class "foo" {
+ filename "/var/boot/boot2";
+ server-name "none.biz";
+}
diff --git a/keama/tests/filename4.out b/keama/tests/filename4.out
new file mode 100644
index 00000000..a391eca5
--- /dev/null
+++ b/keama/tests/filename4.out
@@ -0,0 +1,17 @@
+{
+ # filename (aka boot-file-name) and server-name (aka server-hostname)
+ "Dhcp4": {
+// /// boot-file-name was defined in an unsupported scope
+// "boot-file-name": "/var/boot/boot1",
+// /// server-hostname was defined in an unsupported scope
+// "server-hostname": "foobar.biz",
+ "client-classes": [
+ # embedded
+ {
+ "name": "foo",
+ "boot-file-name": "/var/boot/boot2",
+ "server-hostname": "none.biz"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/filenamedx4.notyet b/keama/tests/filenamedx4.notyet
new file mode 100644
index 00000000..127bcc7a
--- /dev/null
+++ b/keama/tests/filenamedx4.notyet
@@ -0,0 +1,20 @@
+# filename data expression
+# Kea has no filename extractor in libeval
+
+# authoritative is mandatory
+authoritative;
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# by filename superclass
+class "byfn" {
+ match filename;
+}
+
+subclass "byfn" "boot00070e364819" {
+ option host-name "test1";
+}
+
+# raw
+option host-name = concat("host-", substring(filename, 4, 12));
diff --git a/keama/tests/fixedaddressinroot4.err4 b/keama/tests/fixedaddressinroot4.err4
new file mode 100644
index 00000000..f5d2480a
--- /dev/null
+++ b/keama/tests/fixedaddressinroot4.err4
@@ -0,0 +1,4 @@
+# DHCPv4 fixed address declaration in root config
+
+# DHCPv4 fixed address declaration must be in a host declaration
+fixed-address 204.152.185.133;
diff --git a/keama/tests/fixedaddressinroot4.msg b/keama/tests/fixedaddressinroot4.msg
new file mode 100644
index 00000000..fd0424a5
--- /dev/null
+++ b/keama/tests/fixedaddressinroot4.msg
@@ -0,0 +1 @@
+fixedaddressinroot4.err4 line 4: fixed-address parameter not allowed here.
diff --git a/keama/tests/fixedaddressinroot6.err6 b/keama/tests/fixedaddressinroot6.err6
new file mode 100644
index 00000000..f42854a7
--- /dev/null
+++ b/keama/tests/fixedaddressinroot6.err6
@@ -0,0 +1,4 @@
+# DHCPv6 fixed address declaration in root config
+
+# DHCPv6 fixed address declaration must be in a host declaration
+fixed-address6 2001::1;
diff --git a/keama/tests/fixedaddressinroot6.msg b/keama/tests/fixedaddressinroot6.msg
new file mode 100644
index 00000000..21da6d84
--- /dev/null
+++ b/keama/tests/fixedaddressinroot6.msg
@@ -0,0 +1 @@
+fixedaddressinroot6.err6 line 4: fixed-address parameter not allowed here.
diff --git a/keama/tests/fixedprefixinroot.err6 b/keama/tests/fixedprefixinroot.err6
new file mode 100644
index 00000000..7415aaf3
--- /dev/null
+++ b/keama/tests/fixedprefixinroot.err6
@@ -0,0 +1,4 @@
+# DHCPv6 fixed prefix declaration in root config
+
+# DHCPv6 fixed prefix declaration must be in a host declaration
+fixed-prefix6 2001:0:0:1/64;
diff --git a/keama/tests/fixedprefixinroot.msg b/keama/tests/fixedprefixinroot.msg
new file mode 100644
index 00000000..0b835acd
--- /dev/null
+++ b/keama/tests/fixedprefixinroot.msg
@@ -0,0 +1 @@
+fixedprefixinroot.err6 line 4: fixed-prefix6 declaration not allowed here.
diff --git a/keama/tests/fqdncompressed.err6 b/keama/tests/fqdncompressed.err6
new file mode 100644
index 00000000..aed1fe63
--- /dev/null
+++ b/keama/tests/fqdncompressed.err6
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+
+# compressed list of FQDNs are forbidden in DHCPv6
+option foobar.compressed-list code 1 = domain-list compressed;
diff --git a/keama/tests/fqdncompressed.msg b/keama/tests/fqdncompressed.msg
new file mode 100644
index 00000000..cfd157ef
--- /dev/null
+++ b/keama/tests/fqdncompressed.msg
@@ -0,0 +1 @@
+fqdncompressed.err6 line 7: domain list in DHCPv6 MUST NOT be compressed
diff --git a/keama/tests/gethostdx4.notyet b/keama/tests/gethostdx4.notyet
new file mode 100644
index 00000000..567d66a4
--- /dev/null
+++ b/keama/tests/gethostdx4.notyet
@@ -0,0 +1,13 @@
+# gethostname and gethostbyname data expressions
+
+# authoritative is mandatory
+authoritative;
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# gethostname
+option domain-name = suffix(gethostname(), (1 + 2) * 2);
+
+# gethostbyaddr
+option ntp-servers = gethostbyname("www.apple.fr");
diff --git a/keama/tests/global4.in4 b/keama/tests/global4.in4
new file mode 100644
index 00000000..ddfaec67
--- /dev/null
+++ b/keama/tests/global4.in4
@@ -0,0 +1,10 @@
+# DHCPv4 global reservation config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# global reservation
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ option ip-forwarding off;
+}
diff --git a/keama/tests/global4.out b/keama/tests/global4.out
new file mode 100644
index 00000000..66b380ff
--- /dev/null
+++ b/keama/tests/global4.out
@@ -0,0 +1,28 @@
+{
+ # DHCPv4 global reservation config
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "host-reservation-identifiers": [
+ "hw-address"
+ ],
+ "reservation-mode": "global",
+ "reservations": [
+ # global reservation
+ {
+ "hostname": "foobar",
+ "hw-address": "00:0b:fd:32:e6:fa",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "ip-forwarding",
+ "code": 19,
+// "original-data": "off",
+ /// canonized booleans to lowercase true or false
+ "data": "false"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/global6.in6 b/keama/tests/global6.in6
new file mode 100644
index 00000000..8bba7082
--- /dev/null
+++ b/keama/tests/global6.in6
@@ -0,0 +1,10 @@
+# DHCPv6 global reservation config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# global reservation
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ option dhcp6.name-servers 2a01:e00::2, 2a01:e00::1;
+}
diff --git a/keama/tests/global6.out b/keama/tests/global6.out
new file mode 100644
index 00000000..5616e7ec
--- /dev/null
+++ b/keama/tests/global6.out
@@ -0,0 +1,26 @@
+{
+ # DHCPv6 global reservation config
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "host-reservation-identifiers": [
+ "hw-address"
+ ],
+ "reservation-mode": "global",
+ "reservations": [
+ # global reservation
+ {
+ "hostname": "foobar",
+ "hw-address": "00:0b:fd:32:e6:fa",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "dns-servers",
+ "code": 23,
+ "data": "2a01:e00::2, 2a01:e00::1"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/groupclass4.in4 b/keama/tests/groupclass4.in4
new file mode 100644
index 00000000..b306f66f
--- /dev/null
+++ b/keama/tests/groupclass4.in4
@@ -0,0 +1,32 @@
+# group and class declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ match option mysystem;
+ option myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" "version1";
+
+group machin {
+ next-server 10.10.10.1;
+ # this option is not propagated because the superclass takes precedence
+ option myversion 99;
+
+ # option setting subclass declaration
+ subclass "foobar" "version2" { option myversion 2; }
+
+ # complex subclass declaration
+ subclass "foobar" "version3" {
+ option myversion 3;
+ next-server 192.168.0.1;
+ }
+
+ # another simple subclass declaration
+ subclass "foobar" "version10";
+}
diff --git a/keama/tests/groupclass4.out b/keama/tests/groupclass4.out
new file mode 100644
index 00000000..71e40072
--- /dev/null
+++ b/keama/tests/groupclass4.out
@@ -0,0 +1,102 @@
+{
+ # group and class declaration config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "mysystem",
+ "code": 250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp.mysystem
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 'version1'
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version1'
+ "test": "option[250].hex == 'version1'"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'version2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "2"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version2'
+ "test": "option[250].hex == 'version2'",
+ "next-server": "10.10.10.1"
+ },
+ # complex subclass declaration
+ /// subclass selector 'version3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "3"
+ }
+ ],
+ "next-server": "192.168.0.1",
+ /// from: match option dhcp.mysystem
+ /// data: 'version3'
+ "test": "option[250].hex == 'version3'"
+ },
+ # another simple subclass declaration
+ /// subclass selector 'version10'
+ {
+ "name": "sub#foobar#3",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version10'
+ "test": "option[250].hex == 'version10'",
+ "next-server": "10.10.10.1"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/groupclass6.in6 b/keama/tests/groupclass6.in6
new file mode 100644
index 00000000..4cc21b78
--- /dev/null
+++ b/keama/tests/groupclass6.in6
@@ -0,0 +1,35 @@
+# group and class declaration config
+
+# options
+option dhcp6.mysystem code 1250 = text;
+option dhcp6.myversion code 1251 = unsigned integer 16;
+option dhcp6.myvalue code 1252 = text;
+
+# superclass declaration
+class "foobar" {
+ match option dhcp6.mysystem;
+ option dhcp6.myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" "version1";
+
+# anonymous group
+group {
+ # this option is not propagated because the superclass takes precedence
+ option dhcp6.myversion 99;
+
+ option dhcp6.myvalue "foo";
+
+ # option setting subclass declaration
+ subclass "foobar" "version2" { option dhcp6.myversion 2; }
+
+ # complex subclass declaration
+ subclass "foobar" "version3" {
+ option dhcp6.myversion 3;
+ option dhcp6.myvalue "bar";
+ }
+
+ # another simple subclass declaration
+ subclass "foobar" "version10";
+}
diff --git a/keama/tests/groupclass6.out b/keama/tests/groupclass6.out
new file mode 100644
index 00000000..68144b89
--- /dev/null
+++ b/keama/tests/groupclass6.out
@@ -0,0 +1,123 @@
+{
+ # group and class declaration config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "mysystem",
+ "code": 1250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "type": "uint16"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myvalue",
+ "code": 1252,
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp6.mysystem
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 'version1'
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version1'
+ "test": "option[1250].hex == 'version1'"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'version2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "2"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myvalue",
+ "code": 1252,
+ "data": "foo"
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version2'
+ "test": "option[1250].hex == 'version2'"
+ },
+ # complex subclass declaration
+ /// subclass selector 'version3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "3"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myvalue",
+ "code": 1252,
+ "data": "bar"
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version3'
+ "test": "option[1250].hex == 'version3'"
+ },
+ # another simple subclass declaration
+ /// subclass selector 'version10'
+ {
+ "name": "sub#foobar#3",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myvalue",
+ "code": 1252,
+ "data": "foo"
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version10'
+ "test": "option[1250].hex == 'version10'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/groupgroup4.in4 b/keama/tests/groupgroup4.in4
new file mode 100644
index 00000000..3be93d7d
--- /dev/null
+++ b/keama/tests/groupgroup4.in4
@@ -0,0 +1,45 @@
+# multiple groups declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+option myvalue code 252 = text;
+
+# superclass declaration
+class "foobar" {
+ match option mysystem;
+ option myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" "version1";
+
+group first {
+ next-server 10.10.10.1;
+ # this option is not propagated because the superclass takes precedence
+ option myversion 99;
+
+ # option setting subclass declaration
+ subclass "foobar" "version2" { option myversion 2; }
+
+ # complex subclass declaration
+ subclass "foobar" "version3" {
+ option myversion 3;
+ next-server 192.168.0.1;
+ }
+
+ group second {
+ # another simple subclass declaration
+ subclass "foobar" "version10";
+
+ # and a final subclass declaration
+ subclass "foobar" "version20" {
+ option myversion 20;
+ next-server 192.168.0.20;
+ option myvalue "twenty";
+ }
+
+ # positions of delaration do not matter
+ option myvalue "ten";
+ }
+}
diff --git a/keama/tests/groupgroup4.out b/keama/tests/groupgroup4.out
new file mode 100644
index 00000000..09dd0574
--- /dev/null
+++ b/keama/tests/groupgroup4.out
@@ -0,0 +1,138 @@
+{
+ # multiple groups declaration config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "mysystem",
+ "code": 250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "type": "uint16"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myvalue",
+ "code": 252,
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp.mysystem
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 'version1'
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version1'
+ "test": "option[250].hex == 'version1'"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'version2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "2"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version2'
+ "test": "option[250].hex == 'version2'",
+ "next-server": "10.10.10.1"
+ },
+ # complex subclass declaration
+ /// subclass selector 'version3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "3"
+ }
+ ],
+ "next-server": "192.168.0.1",
+ /// from: match option dhcp.mysystem
+ /// data: 'version3'
+ "test": "option[250].hex == 'version3'"
+ },
+ # another simple subclass declaration
+ /// subclass selector 'version10'
+ {
+ "name": "sub#foobar#3",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ },
+ # positions of delaration do not matter
+ {
+ "space": "dhcp4",
+ "name": "myvalue",
+ "code": 252,
+ "data": "ten"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version10'
+ "test": "option[250].hex == 'version10'",
+ "next-server": "10.10.10.1"
+ },
+ # and a final subclass declaration
+ /// subclass selector 'version20'
+ {
+ "name": "sub#foobar#4",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "20"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myvalue",
+ "code": 252,
+ "data": "twenty"
+ }
+ ],
+ "next-server": "192.168.0.20",
+ /// from: match option dhcp.mysystem
+ /// data: 'version20'
+ "test": "option[250].hex == 'version20'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/grouphost4.inn b/keama/tests/grouphost4.inn
new file mode 100644
index 00000000..da9d5291
--- /dev/null
+++ b/keama/tests/grouphost4.inn
@@ -0,0 +1,35 @@
+# group and host declarations config
+
+# subnet4 declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.5 10.5.5.10;
+}
+
+# host declaration
+host test1 {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ fixed-address 10.5.5.1, 10.10.10.10;
+}
+
+# group declaration
+group "foobar" {
+ default-lease-time 1800;
+ option domain-search "example.com", "example.org";
+ next-server 192.168.0.1;
+
+ # host declarations
+ host test2 {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address 10.5.5.2;
+ option domain-name "example.com";
+ option domain-search "example.com", "com";
+ }
+
+ host test3 {
+ hardware fddi 00:07:0E:36:48:19;
+ fixed-address 10.10.10.1;
+ default-lease-time 3600;
+ }
+}
+
+subnet 10.10.10.0 netmask 255.255.255.224 { }
diff --git a/keama/tests/grouphost4.out b/keama/tests/grouphost4.out
new file mode 100644
index 00000000..2027c536
--- /dev/null
+++ b/keama/tests/grouphost4.out
@@ -0,0 +1,78 @@
+{
+ # group and host declarations config
+ # subnet4 declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ],
+ "reservations": [
+ # host declaration
+ {
+ "hostname": "test1",
+ "hw-address": "00:0b:fd:32:e6:fa",
+ "ip-address": "10.5.5.1"
+// "extra-ip-addresses": [
+// "10.10.10.10"
+// ]
+ },
+ # host declarations
+ {
+ "hostname": "test2",
+ "hw-address": "00:07:0e:36:48:19",
+ "ip-address": "10.5.5.2",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-name",
+ "code": 15,
+ "data": "example.com"
+ },
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"com\"",
+ "data": "example.com, com"
+ }
+ ],
+ "next-server": "192.168.0.1"
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "10.10.10.0/27",
+ "reservations": [
+// {
+// "hostname": "test3",
+// "hw-address": "fddi 00:07:0e:36:48:19",
+// "ip-address": "10.10.10.1",
+// /// default-valid-lifetime in unsupported scope
+// "valid-lifetime": 3600,
+// "option-data": [
+// {
+// "space": "dhcp4",
+// "name": "domain-search",
+// "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+// "data": "example.com, example.org"
+// }
+// ],
+// "next-server": "192.168.0.1"
+// }
+ ]
+ }
+ ],
+ "host-reservation-identifiers": [
+ "hw-address"
+ ]
+ }
+}
diff --git a/keama/tests/groupinclass.err b/keama/tests/groupinclass.err
new file mode 100644
index 00000000..dbc320a4
--- /dev/null
+++ b/keama/tests/groupinclass.err
@@ -0,0 +1,10 @@
+# group declaration inside class declaration config
+
+# host declaration
+class "foobar" {
+ # can't put a group declaration here
+ group "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/groupinclass.msg b/keama/tests/groupinclass.msg
new file mode 100644
index 00000000..ee72c674
--- /dev/null
+++ b/keama/tests/groupinclass.msg
@@ -0,0 +1 @@
+groupinclass.err line 6: group declarations not allowed here.
diff --git a/keama/tests/groupsubnet4.in4 b/keama/tests/groupsubnet4.in4
new file mode 100644
index 00000000..a7a199dc
--- /dev/null
+++ b/keama/tests/groupsubnet4.in4
@@ -0,0 +1,24 @@
+# Group with DHCPv4 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1200;
+
+# group declaration
+group foobar {
+ # option
+ option domain-search "example.com", "example.org";
+
+ # parameters
+ default-lease-time 3600;
+ ignore-client-uids false;
+
+ # DHCPv4 subnet declaration
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ # at least one pool is required
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+ interface "en0";
+ default-lease-time 1800;
+ }
+}
diff --git a/keama/tests/groupsubnet4.out b/keama/tests/groupsubnet4.out
new file mode 100644
index 00000000..f2db5f85
--- /dev/null
+++ b/keama/tests/groupsubnet4.out
@@ -0,0 +1,38 @@
+{
+ # Group with DHCPv4 subnet declaration config
+ # parameter which will be changed in subnet
+ "Dhcp4": {
+ "valid-lifetime": 1200,
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ "subnet4": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # at least one pool is required
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ],
+ "interface": "en0",
+ "valid-lifetime": 1800,
+ "option-data": [
+ # option
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "match-client-id": true
+ }
+ ]
+ }
+}
diff --git a/keama/tests/groupsubnet6.in6 b/keama/tests/groupsubnet6.in6
new file mode 100644
index 00000000..90277618
--- /dev/null
+++ b/keama/tests/groupsubnet6.in6
@@ -0,0 +1,24 @@
+# Group with DHCPv6 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1200;
+
+# group declaration
+group foobar {
+ # option
+ option dhcp6.domain-search "example.com", "example.org";
+
+ # parameters
+ default-lease-time 3600;
+
+ # DHCPv4 subnet declaration
+ subnet6 2001::/64 {
+ # at least one pool is required
+ pool6 {
+ range6 2001::100 2001::200;
+ }
+ interface "en0";
+ default-lease-time 1800;
+ option dhcp6.lq-relay-data 2001::1 "foobar";
+ }
+}
diff --git a/keama/tests/groupsubnet6.out b/keama/tests/groupsubnet6.out
new file mode 100644
index 00000000..63d5794e
--- /dev/null
+++ b/keama/tests/groupsubnet6.out
@@ -0,0 +1,44 @@
+{
+ # Group with DHCPv6 subnet declaration config
+ # parameter which will be changed in subnet
+ "Dhcp6": {
+ "valid-lifetime": 1200,
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ "subnet6": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ # at least one pool is required
+ {
+ "pool": "2001::100 - 2001::200"
+ }
+ ],
+ "interface": "en0",
+ "valid-lifetime": 1800,
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "lq-relay-data",
+ "code": 47,
+// "original-data": "2001::1 \"foobar\"",
+ "data": "2001::1, 666f6f626172"
+ },
+ # option
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/groupsubnetif.err4 b/keama/tests/groupsubnetif.err4
new file mode 100644
index 00000000..9a407b11
--- /dev/null
+++ b/keama/tests/groupsubnetif.err4
@@ -0,0 +1,19 @@
+# bad (interface unlnown in this cope) group declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1200;
+
+# group declaration
+group foobar {
+ # interface
+ interface "foo";
+
+ # DHCPv4 subnet declaration
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ # at least one pool is required
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+ interface "bar";
+ }
+}
diff --git a/keama/tests/groupsubnetif.msg b/keama/tests/groupsubnetif.msg
new file mode 100644
index 00000000..1828b932
--- /dev/null
+++ b/keama/tests/groupsubnetif.msg
@@ -0,0 +1 @@
+groupsubnetif.err4 line 7: expecting a parameter or declaration
diff --git a/keama/tests/hardware2dx4.in4 b/keama/tests/hardware2dx4.in4
new file mode 100644
index 00000000..73046612
--- /dev/null
+++ b/keama/tests/hardware2dx4.in4
@@ -0,0 +1,13 @@
+# simplified hardware data expression
+
+# hardware type class
+class "ethernet" {
+ match if substring(hardware, 0, 1) = encode-int(1, 8);
+}
+
+# ethernet address superclass
+class "ethernet-address" {
+ match substring(hardware, 1, 6);
+}
+
+subclass "ethernet-address" 00:0B:FD:32:E6:FA { }
diff --git a/keama/tests/hardware2dx4.out b/keama/tests/hardware2dx4.out
new file mode 100644
index 00000000..77c3f533
--- /dev/null
+++ b/keama/tests/hardware2dx4.out
@@ -0,0 +1,25 @@
+{
+ # simplified hardware data expression
+ # hardware type class
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "ethernet",
+ /// from: match if (substring(hardware, 0, 1)) = (encode-int(1, 8))
+ "test": "substring(pkt4.htype,-1,all) == '\u0001'"
+ },
+ # ethernet address superclass
+ /// match: substring(hardware, 1, 6)
+ {
+ "name": "ethernet-address"
+ },
+ /// subclass selector 0x0x000bfd32e6fa
+ {
+ "name": "sub#ethernet-address#0",
+ /// from: match substring(hardware, 1, 6)
+ /// data: 0x000bfd32e6fa
+ "test": "substring(pkt4.mac,0,6) == 0x000bfd32e6fa"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/hardwaredx4.in4 b/keama/tests/hardwaredx4.in4
new file mode 100644
index 00000000..46160776
--- /dev/null
+++ b/keama/tests/hardwaredx4.in4
@@ -0,0 +1,16 @@
+# hardware data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# pretty standard hardware superclass
+class "byhw" {
+ match hardware;
+}
+
+subclass "byhw" 01:00:07:0E:36:48:19 {
+ option host-name "test1";
+}
+
+# raw
+option host-name = binary-to-ascii(16, 8, "-", hardware);
diff --git a/keama/tests/hardwaredx4.out b/keama/tests/hardwaredx4.out
new file mode 100644
index 00000000..a3f13938
--- /dev/null
+++ b/keama/tests/hardwaredx4.out
@@ -0,0 +1,55 @@
+{
+ # hardware data expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # pretty standard hardware superclass
+ /// match: hardware
+ {
+ "name": "byhw"
+ },
+ /// subclass selector 0x0x0100070e364819
+ {
+ "name": "sub#byhw#0",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "host-name",
+ "code": 12,
+ "data": "test1"
+ }
+ ],
+ /// from: match hardware
+ /// data: 0x0100070e364819
+ "test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x0100070e364819"
+ }
+ ],
+ "option-data": [
+// # raw
+// {
+// "space": "dhcp4",
+// "name": "host-name",
+// "code": 12,
+// "csv-format": false,
+// "expression": {
+// "binary-to-ascii": {
+// "base": 16,
+// "width": 8,
+// "separator": "-",
+// "buffer": {
+// "concat": {
+// "left": {
+// "hw-type": null
+// },
+// "right": {
+// "hw-address": null
+// }
+// }
+// }
+// }
+// }
+// }
+ ]
+ }
+}
diff --git a/keama/tests/hardwareinroot.err b/keama/tests/hardwareinroot.err
new file mode 100644
index 00000000..22902f20
--- /dev/null
+++ b/keama/tests/hardwareinroot.err
@@ -0,0 +1,5 @@
+# hardware declaration in root config
+
+# hardware declaration must be in a host declaration
+hardware ethernet 00:0B:FD:32:E6:FA;
+
diff --git a/keama/tests/hardwareinroot.msg b/keama/tests/hardwareinroot.msg
new file mode 100644
index 00000000..6cf018d0
--- /dev/null
+++ b/keama/tests/hardwareinroot.msg
@@ -0,0 +1 @@
+hardwareinroot.err line 4: hardware address parameter not allowed here.
diff --git a/keama/tests/host6.notyet b/keama/tests/host6.notyet
new file mode 100644
index 00000000..daff911e
--- /dev/null
+++ b/keama/tests/host6.notyet
@@ -0,0 +1,20 @@
+# DHCPv6 host declaration config
+
+# authoritative is mandatory
+authoritative;
+
+# subnet declaration
+subnet6 2001::/64 {
+ range6 2001::100 2001::200;
+}
+
+# host declarations
+host test1 {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address6 2001::1, 2001::10;
+}
+
+host test2 {
+ hardware fddi 00:07:0E:36:48:19;
+ fixed-prefix6 2001:0:0:1::/64;
+}
diff --git a/keama/tests/hostidentifier4.inl b/keama/tests/hostidentifier4.inl
new file mode 100644
index 00000000..9e50ddd3
--- /dev/null
+++ b/keama/tests/hostidentifier4.inl
@@ -0,0 +1,30 @@
+# host declaration with flexible identifiers config
+
+# subnet4 declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.5 10.5.5.10;
+}
+
+# option definition
+
+option my-id code 250 = text;
+
+# host declarations
+host test1 {
+ host-identifier option my-id test1;
+ option domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ fixed-address 10.5.5.1, 10.10.10.10;
+}
+
+host test2 {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address 10.5.5.2;
+}
+
+host test3 {
+ hardware fddi 00:07:0E:36:48:19;
+ fixed-address 10.10.10.1;
+}
+
+subnet 10.10.10.0 netmask 255.255.255.224 { }
diff --git a/keama/tests/hostidentifier4.outl b/keama/tests/hostidentifier4.outl
new file mode 100644
index 00000000..24af2ac5
--- /dev/null
+++ b/keama/tests/hostidentifier4.outl
@@ -0,0 +1,78 @@
+{
+ # host declaration with flexible identifiers config
+ # subnet4 declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "10.10.10.0/27"
+ }
+ ],
+ "option-def": [
+ # option definition
+ {
+ "space": "dhcp4",
+ "name": "my-id",
+ "code": 250,
+ "type": "string"
+ }
+ ],
+ "host-reservation-identifiers": [
+ "flex-id",
+ "hw-address"
+ ],
+ /// The flexible host identifier is a premium feature
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_flex_id.so",
+ "parameters": {
+ "identifier-expression": "option[250].hex"
+ }
+ }
+ ],
+ "reservation-mode": "global",
+ "reservations": [
+ # host declarations
+ {
+ "hostname": "test1",
+ "flex-id": "'test1'",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+// /// default-valid-lifetime in unsupported scope
+// "valid-lifetime": 1800,
+ "ip-address": "10.5.5.1"
+// "extra-ip-addresses": [
+// "10.10.10.10"
+// ]
+ },
+ {
+ "hostname": "test2",
+ "hw-address": "00:07:0e:36:48:19",
+ "ip-address": "10.5.5.2"
+ }
+// {
+// "hostname": "test3",
+// "hw-address": "fddi 00:07:0e:36:48:19",
+// "ip-address": "10.10.10.1"
+// }
+ ]
+ }
+}
diff --git a/keama/tests/hostinclass.err b/keama/tests/hostinclass.err
new file mode 100644
index 00000000..ac5075ee
--- /dev/null
+++ b/keama/tests/hostinclass.err
@@ -0,0 +1,10 @@
+# host declaration inside class declaration config
+
+# class declaration
+class "foobar" {
+ # can't put a host declaration here
+ host illegal {
+ hardware ethernet 00:07:0E:36:48:19;
+ }
+}
+
diff --git a/keama/tests/hostinclass.msg b/keama/tests/hostinclass.msg
new file mode 100644
index 00000000..004446ba
--- /dev/null
+++ b/keama/tests/hostinclass.msg
@@ -0,0 +1 @@
+hostinclass.err line 6: host declarations not allowed here.
diff --git a/keama/tests/hostinhost.err b/keama/tests/hostinhost.err
new file mode 100644
index 00000000..8da7d425
--- /dev/null
+++ b/keama/tests/hostinhost.err
@@ -0,0 +1,11 @@
+# host declaration inside host declaration config
+
+# host declaration
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ # can't put another host declaration here
+ host illegal {
+ hardware ethernet 00:07:0E:36:48:19;
+ }
+}
+
diff --git a/keama/tests/hostinhost.msg b/keama/tests/hostinhost.msg
new file mode 100644
index 00000000..3ddfbd7a
--- /dev/null
+++ b/keama/tests/hostinhost.msg
@@ -0,0 +1 @@
+hostinhost.err line 7: host declarations not allowed here.
diff --git a/keama/tests/hostname4.in4 b/keama/tests/hostname4.in4
new file mode 100644
index 00000000..8e2db319
--- /dev/null
+++ b/keama/tests/hostname4.in4
@@ -0,0 +1,18 @@
+# host name config
+
+# subnet4 declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.5 10.5.5.10;
+}
+
+# host declaration
+host test1 {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ fixed-address 10.5.5.1;
+}
+
+# host declaration using a longer name
+host test2.example.com {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address 10.5.5.2;
+}
diff --git a/keama/tests/hostname4.out b/keama/tests/hostname4.out
new file mode 100644
index 00000000..d6973a48
--- /dev/null
+++ b/keama/tests/hostname4.out
@@ -0,0 +1,37 @@
+{
+ # host name config
+ # subnet4 declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ }
+ ],
+ "host-reservation-identifiers": [
+ "hw-address"
+ ],
+ "reservation-mode": "global",
+ "reservations": [
+ # host declaration
+ {
+ "hostname": "test1",
+ "hw-address": "00:0b:fd:32:e6:fa",
+ "ip-address": "10.5.5.1"
+ },
+ # host declaration using a longer name
+ {
+ "hostname": "test2.example.com",
+ "hw-address": "00:07:0e:36:48:19",
+ "ip-address": "10.5.5.2"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/hostnum.errF b/keama/tests/hostnum.errF
new file mode 100644
index 00000000..4c89dc27
--- /dev/null
+++ b/keama/tests/hostnum.errF
@@ -0,0 +1,7 @@
+# numeric with address hostname config
+
+host 1901.fr {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address 1901.fr;
+}
+
diff --git a/keama/tests/hostnum.msg b/keama/tests/hostnum.msg
new file mode 100644
index 00000000..cc734bc0
--- /dev/null
+++ b/keama/tests/hostnum.msg
@@ -0,0 +1 @@
+hostnum.errF line 5: expected IPv4 address. got hostname 1901.fr
diff --git a/keama/tests/hostuid4.inn b/keama/tests/hostuid4.inn
new file mode 100644
index 00000000..867fa8b4
--- /dev/null
+++ b/keama/tests/hostuid4.inn
@@ -0,0 +1,29 @@
+# host declaration with client-identfiers config
+
+# subnet4 declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.5 10.5.5.10;
+}
+
+# recommended when using dhcp-client-identifier options
+ignore-client-uids false;
+
+# host declarations
+host test1 {
+ uid 01:02:03:04:05:0a:0b:0c:0d:0e:0f;
+ option domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ fixed-address 10.5.5.1, 10.10.10.10;
+}
+
+host test2 {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address 10.5.5.2;
+}
+
+host test3 {
+ hardware fddi 00:07:0E:36:48:19;
+ fixed-address 10.10.10.1;
+}
+
+subnet 10.10.10.0 netmask 255.255.255.224 { }
diff --git a/keama/tests/hostuid4.out b/keama/tests/hostuid4.out
new file mode 100644
index 00000000..ad3f3e36
--- /dev/null
+++ b/keama/tests/hostuid4.out
@@ -0,0 +1,62 @@
+{
+ # host declaration with client-identfiers config
+ # subnet4 declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ],
+ "reservations": [
+ # host declarations
+ {
+ "hostname": "test1",
+ "client-id": "01:02:03:04:05:0a:0b:0c:0d:0e:0f",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+// /// default-valid-lifetime in unsupported scope
+// "valid-lifetime": 1800,
+ "ip-address": "10.5.5.1"
+// "extra-ip-addresses": [
+// "10.10.10.10"
+// ]
+ },
+ {
+ "hostname": "test2",
+ "hw-address": "00:07:0e:36:48:19",
+ "ip-address": "10.5.5.2"
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "10.10.10.0/27",
+ "reservations": [
+// {
+// "hostname": "test3",
+// "hw-address": "fddi 00:07:0e:36:48:19",
+// "ip-address": "10.10.10.1"
+// }
+ ]
+ }
+ ],
+ "match-client-id": true,
+ "host-reservation-identifiers": [
+ "client-id",
+ "hw-address"
+ ]
+ }
+}
diff --git a/keama/tests/ifxsc4.in4 b/keama/tests/ifxsc4.in4
new file mode 100644
index 00000000..b3a59bcc
--- /dev/null
+++ b/keama/tests/ifxsc4.in4
@@ -0,0 +1,17 @@
+# if executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# if statement
+# first true is not recognized even as a boolean expression
+if true {
+ option ip-forwarding true;
+}
+
+# another
+if ( option user-class = "accounting" ) {
+ option boot-size 100000;
+} elsif option user-class = "engineering" {
+ option domain-name "example.com";
+}
diff --git a/keama/tests/ifxsc4.out b/keama/tests/ifxsc4.out
new file mode 100644
index 00000000..ca086910
--- /dev/null
+++ b/keama/tests/ifxsc4.out
@@ -0,0 +1,79 @@
+{
+ # if executable statement construct
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// # if statement
+// # first true is not recognized even as a boolean expression
+// "statement": {
+// "if": {
+// "condition": {
+// "variable-reference": "true"
+// },
+// "then": [
+// {
+// "option": {
+// "space": "dhcp4",
+// "name": "ip-forwarding",
+// "code": 19,
+// "data": "true"
+// }
+// }
+// ]
+// }
+// }
+// # another
+// "statement": {
+// "if": {
+// "condition": {
+// "equal": {
+// "left": {
+// "option": {
+// "universe": "dhcp",
+// "name": "user-class",
+// "code": 77
+// }
+// },
+// "right": "accounting"
+// }
+// },
+// "then": [
+// {
+// "option": {
+// "space": "dhcp4",
+// "name": "boot-size",
+// "code": 13,
+// "data": "100000"
+// }
+// }
+// ],
+// "else": {
+// "if": {
+// "condition": {
+// "equal": {
+// "left": {
+// "option": {
+// "universe": "dhcp",
+// "name": "user-class",
+// "code": 77
+// }
+// },
+// "right": "engineering"
+// }
+// },
+// "then": [
+// {
+// "option": {
+// "space": "dhcp4",
+// "name": "domain-name",
+// "code": 15,
+// "data": "example.com"
+// }
+// }
+// ]
+// }
+// }
+// }
+// }
+ }
+}
diff --git a/keama/tests/ipaddr6.in6 b/keama/tests/ipaddr6.in6
new file mode 100644
index 00000000..60c27175
--- /dev/null
+++ b/keama/tests/ipaddr6.in6
@@ -0,0 +1,3 @@
+# IPv6 addresses config
+
+option dhcp6.name-servers 2001::, 200a::0bF, 2001::192.168.0.1;
diff --git a/keama/tests/ipaddr6.out b/keama/tests/ipaddr6.out
new file mode 100644
index 00000000..b2118c61
--- /dev/null
+++ b/keama/tests/ipaddr6.out
@@ -0,0 +1,14 @@
+{
+ # IPv6 addresses config
+ "Dhcp6": {
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "dns-servers",
+ "code": 23,
+// "original-data": "2001::, 200a::0bF, 2001::192.168.0.1",
+ "data": "2001::, 200a::bf, 2001::c0a8:1"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/ipaddrhost4.in4 b/keama/tests/ipaddrhost4.in4
new file mode 100644
index 00000000..1c43f81b
--- /dev/null
+++ b/keama/tests/ipaddrhost4.in4
@@ -0,0 +1,8 @@
+# hostname config
+
+host test1 {
+ hardware ethernet 00:07:0E:36:48:19;
+ fixed-address www.isc.org;
+}
+
+subnet 149.20.64.0 netmask 255.255.255.128 { }
diff --git a/keama/tests/ipaddrhost4.out b/keama/tests/ipaddrhost4.out
new file mode 100644
index 00000000..22a36643
--- /dev/null
+++ b/keama/tests/ipaddrhost4.out
@@ -0,0 +1,24 @@
+{
+ # hostname config
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "host-reservation-identifiers": [
+ "hw-address"
+ ],
+ "reservation-mode": "global",
+ "reservations": [
+ {
+ "hostname": "test1",
+ "hw-address": "00:07:0e:36:48:19",
+ "ip-address": "151.101.122.217"
+ }
+ ],
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "149.20.64.0/25"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/ipaddrs4.notyet4 b/keama/tests/ipaddrs4.notyet4
new file mode 100644
index 00000000..6430e0f9
--- /dev/null
+++ b/keama/tests/ipaddrs4.notyet4
@@ -0,0 +1,11 @@
+# hostname config
+
+# authoritative is mandatory
+authoritative;
+
+host test1 {
+ hardware ethernet 00:07:0E:36:48:19;
+ # www.apple.fr has multiple addresses but they are not returned
+ # in a stable order
+ fixed-address www.apple.fr;
+}
diff --git a/keama/tests/lifetime4.ind b/keama/tests/lifetime4.ind
new file mode 100644
index 00000000..4e8594ab
--- /dev/null
+++ b/keama/tests/lifetime4.ind
@@ -0,0 +1,5 @@
+# DHCPv4 use ISC DHCP default lifetimes but not when configured.
+
+min-lease-time 3600;
+default-lease-time 7200;
+max-lease-time 14400;
diff --git a/keama/tests/lifetime4.out b/keama/tests/lifetime4.out
new file mode 100644
index 00000000..e5360af0
--- /dev/null
+++ b/keama/tests/lifetime4.out
@@ -0,0 +1,8 @@
+{
+ # DHCPv4 use ISC DHCP default lifetimes but not when configured.
+ "Dhcp4": {
+ "min-valid-lifetime": 3600,
+ "valid-lifetime": 7200,
+ "max-valid-lifetime": 14400
+ }
+}
diff --git a/keama/tests/lifetime6.inD b/keama/tests/lifetime6.inD
new file mode 100644
index 00000000..4e8594ab
--- /dev/null
+++ b/keama/tests/lifetime6.inD
@@ -0,0 +1,5 @@
+# DHCPv4 use ISC DHCP default lifetimes but not when configured.
+
+min-lease-time 3600;
+default-lease-time 7200;
+max-lease-time 14400;
diff --git a/keama/tests/lifetime6.out b/keama/tests/lifetime6.out
new file mode 100644
index 00000000..5cc28de0
--- /dev/null
+++ b/keama/tests/lifetime6.out
@@ -0,0 +1,8 @@
+{
+ # DHCPv4 use ISC DHCP default lifetimes but not when configured.
+ "Dhcp6": {
+ "min-valid-lifetime": 3600,
+ "valid-lifetime": 7200,
+ "max-valid-lifetime": 14400
+ }
+}
diff --git a/keama/tests/lifetimedef4.ind b/keama/tests/lifetimedef4.ind
new file mode 100644
index 00000000..881edf57
--- /dev/null
+++ b/keama/tests/lifetimedef4.ind
@@ -0,0 +1 @@
+# DHCPv4 use ISC DHCP default lifetimes
diff --git a/keama/tests/lifetimedef4.out b/keama/tests/lifetimedef4.out
new file mode 100644
index 00000000..1e194bf4
--- /dev/null
+++ b/keama/tests/lifetimedef4.out
@@ -0,0 +1,11 @@
+{
+ # DHCPv4 use ISC DHCP default lifetimes
+ "Dhcp4": {
+ /// Use ISC DHCP default lifetime
+ "valid-lifetime": 43200,
+ /// Use ISC DHCP min lifetime
+ "min-valid-lifetime": 300,
+ /// Use ISC DHCP max lifetime
+ "max-valid-lifetime": 86400
+ }
+}
diff --git a/keama/tests/lifetimedef6.inD b/keama/tests/lifetimedef6.inD
new file mode 100644
index 00000000..59718e40
--- /dev/null
+++ b/keama/tests/lifetimedef6.inD
@@ -0,0 +1 @@
+# DHCPv6 use ISC DHCP default lifetimes
diff --git a/keama/tests/lifetimedef6.out b/keama/tests/lifetimedef6.out
new file mode 100644
index 00000000..274997d1
--- /dev/null
+++ b/keama/tests/lifetimedef6.out
@@ -0,0 +1,11 @@
+{
+ # DHCPv6 use ISC DHCP default lifetimes
+ "Dhcp6": {
+ /// Use ISC DHCP default lifetime
+ "valid-lifetime": 43200,
+ /// Use ISC DHCP min lifetime
+ "min-valid-lifetime": 300,
+ /// Use ISC DHCP max lifetime
+ "max-valid-lifetime": 86400
+ }
+}
diff --git a/keama/tests/listarray.err b/keama/tests/listarray.err
new file mode 100644
index 00000000..14082605
--- /dev/null
+++ b/keama/tests/listarray.err
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+
+# arrays of domain lists are forbidden
+option foobar.array-list code 1 = array of domain-list;
diff --git a/keama/tests/listarray.msg b/keama/tests/listarray.msg
new file mode 100644
index 00000000..2a363531
--- /dev/null
+++ b/keama/tests/listarray.msg
@@ -0,0 +1 @@
+listarray.err line 7: arrays of text strings not yet supported.
diff --git a/keama/tests/minimal4.in4 b/keama/tests/minimal4.in4
new file mode 100644
index 00000000..cfcc1282
--- /dev/null
+++ b/keama/tests/minimal4.in4
@@ -0,0 +1,4 @@
+# DHCPv4 minimal config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
diff --git a/keama/tests/minimal4.out b/keama/tests/minimal4.out
new file mode 100644
index 00000000..e256972c
--- /dev/null
+++ b/keama/tests/minimal4.out
@@ -0,0 +1,7 @@
+{
+ # DHCPv4 minimal config
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+ }
+}
diff --git a/keama/tests/minimal6.in6 b/keama/tests/minimal6.in6
new file mode 100644
index 00000000..57c83491
--- /dev/null
+++ b/keama/tests/minimal6.in6
@@ -0,0 +1,4 @@
+# DHCPv6 minimal config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
diff --git a/keama/tests/minimal6.out b/keama/tests/minimal6.out
new file mode 100644
index 00000000..c07d37fd
--- /dev/null
+++ b/keama/tests/minimal6.out
@@ -0,0 +1,7 @@
+{
+ # DHCPv6 minimal config
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800
+ }
+}
diff --git a/keama/tests/mixedarray.err b/keama/tests/mixedarray.err
new file mode 100644
index 00000000..507aca78
--- /dev/null
+++ b/keama/tests/mixedarray.err
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+
+# not uniform arrays are forbidden
+option foobar.nestarray code 1 = { array of { unsigned integer 32 } };
diff --git a/keama/tests/mixedarray.msg b/keama/tests/mixedarray.msg
new file mode 100644
index 00000000..c409ad01
--- /dev/null
+++ b/keama/tests/mixedarray.msg
@@ -0,0 +1 @@
+mixedarray.err line 7: only uniform array inside record.
diff --git a/keama/tests/nestarray.err b/keama/tests/nestarray.err
new file mode 100644
index 00000000..58f16bbe
--- /dev/null
+++ b/keama/tests/nestarray.err
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+
+# nested arrays are forbidden
+option foobar.nestarray code 1 = array of array of unsigned integer 32;
diff --git a/keama/tests/nestarray.msg b/keama/tests/nestarray.msg
new file mode 100644
index 00000000..92335fad
--- /dev/null
+++ b/keama/tests/nestarray.msg
@@ -0,0 +1 @@
+nestarray.err line 7: no nested arrays.
diff --git a/keama/tests/noauth4.in4 b/keama/tests/noauth4.in4
new file mode 100644
index 00000000..e2fce24c
--- /dev/null
+++ b/keama/tests/noauth4.in4
@@ -0,0 +1,7 @@
+# no(t) authoritative config
+
+# authoritative is no longer mandatory
+#authoritative;
+
+subnet 10.5.5.0 netmask 255.255.255.224 { }
+
diff --git a/keama/tests/noauth4.out b/keama/tests/noauth4.out
new file mode 100644
index 00000000..8dac17f7
--- /dev/null
+++ b/keama/tests/noauth4.out
@@ -0,0 +1,15 @@
+{
+ # no(t) authoritative config
+ # authoritative is no longer mandatory
+ #authoritative;
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/noauth6.in6 b/keama/tests/noauth6.in6
new file mode 100644
index 00000000..42894e91
--- /dev/null
+++ b/keama/tests/noauth6.in6
@@ -0,0 +1,6 @@
+# no(t) authoritative config
+
+# authoritative is no longer mandatory
+#authoritative;
+
+subnet6 2001::/64 { }
diff --git a/keama/tests/noauth6.out b/keama/tests/noauth6.out
new file mode 100644
index 00000000..93f8429e
--- /dev/null
+++ b/keama/tests/noauth6.out
@@ -0,0 +1,15 @@
+{
+ # no(t) authoritative config
+ # authoritative is no longer mandatory
+ #authoritative;
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/noclass.err b/keama/tests/noclass.err
new file mode 100644
index 00000000..8aed2e34
--- /dev/null
+++ b/keama/tests/noclass.err
@@ -0,0 +1,7 @@
+# orphan subclass declaration config
+
+# class declaration
+subclass "foobar" "abcd {
+ default-lease-time 1800;
+}
+
diff --git a/keama/tests/noclass.msg b/keama/tests/noclass.msg
new file mode 100644
index 00000000..ab8719e3
--- /dev/null
+++ b/keama/tests/noclass.msg
@@ -0,0 +1 @@
+noclass.err line 4: no class named foobar
diff --git a/keama/tests/noinclude.err b/keama/tests/noinclude.err
new file mode 100644
index 00000000..f39f388b
--- /dev/null
+++ b/keama/tests/noinclude.err
@@ -0,0 +1,3 @@
+# no file include config
+
+include "do-not-exist";
diff --git a/keama/tests/noinclude.msg b/keama/tests/noinclude.msg
new file mode 100644
index 00000000..91b2284b
--- /dev/null
+++ b/keama/tests/noinclude.msg
@@ -0,0 +1 @@
+noinclude.err line 3: Can't open do-not-exist: No such file or directory
diff --git a/keama/tests/nosubclass.err b/keama/tests/nosubclass.err
new file mode 100644
index 00000000..cf421259
--- /dev/null
+++ b/keama/tests/nosubclass.err
@@ -0,0 +1,11 @@
+# bad (missing selector) subclass declaration config
+
+# superclass declaration
+class "foobar" {
+ match substring(option vendor-class-identifier, 0, 3);
+}
+
+# subclass declaration
+subclass "foobar" {
+ default-lease-time 1800;
+}
diff --git a/keama/tests/nosubclass.msg b/keama/tests/nosubclass.msg
new file mode 100644
index 00000000..762e440f
--- /dev/null
+++ b/keama/tests/nosubclass.msg
@@ -0,0 +1 @@
+nosubclass.err line 9: Expecting string or hex list.
diff --git a/keama/tests/nosuperclass.err b/keama/tests/nosuperclass.err
new file mode 100644
index 00000000..5d2e329f
--- /dev/null
+++ b/keama/tests/nosuperclass.err
@@ -0,0 +1,11 @@
+# bas superclass subclass declaration config
+
+# class (but not superclass) declaration
+class "foobar" {
+ match if substring(option vendor-class-identifier, 0, 3) = "APC";
+}
+
+# subclass declaration
+subclass "foobar" "abcd {
+ default-lease-time 1800;
+}
diff --git a/keama/tests/nosuperclass.msg b/keama/tests/nosuperclass.msg
new file mode 100644
index 00000000..ec448cad
--- /dev/null
+++ b/keama/tests/nosuperclass.msg
@@ -0,0 +1 @@
+nosuperclass.err line 9: found class name foobar but it is not a suitable superclass
diff --git a/keama/tests/notbx4.in4 b/keama/tests/notbx4.in4
new file mode 100644
index 00000000..6248b8c6
--- /dev/null
+++ b/keama/tests/notbx4.in4
@@ -0,0 +1,12 @@
+# not boolean expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# use not in a reductible match if
+class "reductible" {
+ match if not (option host-name = "www.example.com");
+}
+
+# if test is a boolean too
+if not check "foo" { add "bar"; }
diff --git a/keama/tests/notbx4.out b/keama/tests/notbx4.out
new file mode 100644
index 00000000..186cdfff
--- /dev/null
+++ b/keama/tests/notbx4.out
@@ -0,0 +1,30 @@
+{
+ # not boolean expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # use not in a reductible match if
+ {
+ "name": "reductible",
+ /// from: match if not (option dhcp.host-name) = 'www.example.com'
+ "test": "not (option[12].hex == 'www.example.com')"
+ }
+ ]
+// # if test is a boolean too
+// "statement": {
+// "if": {
+// "condition": {
+// "not": {
+// "check": "foo"
+// }
+// },
+// "then": [
+// {
+// "add-class": "bar"
+// }
+// ]
+// }
+// }
+ }
+}
diff --git a/keama/tests/notnotbx4.in4 b/keama/tests/notnotbx4.in4
new file mode 100644
index 00000000..e9d98800
--- /dev/null
+++ b/keama/tests/notnotbx4.in4
@@ -0,0 +1,15 @@
+# double not boolean expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# use double not in a reductible match if
+class "reductible" {
+ match if not (not ((option host-name = "www.example.com") or
+ (option host-name = "www.example.org")));
+}
+
+# use not with !=
+class "other" {
+ match if not (option host-name != "www.example.com");
+}
diff --git a/keama/tests/notnotbx4.out b/keama/tests/notnotbx4.out
new file mode 100644
index 00000000..ae1878cc
--- /dev/null
+++ b/keama/tests/notnotbx4.out
@@ -0,0 +1,21 @@
+{
+ # double not boolean expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # use double not in a reductible match if
+ {
+ "name": "reductible",
+ /// from: match if not not ((option dhcp.host-name) = 'www.example.com') or ((option dhcp.host-name) = 'www.example.org')
+ "test": "(option[12].hex == 'www.example.com') or (option[12].hex == 'www.example.org')"
+ },
+ # use not with !=
+ {
+ "name": "other",
+ /// from: match if not (option dhcp.host-name) != 'www.example.com'
+ "test": "option[12].hex == 'www.example.com'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/nxdomainnx6.in6 b/keama/tests/nxdomainnx6.in6
new file mode 100644
index 00000000..f2a2e0f5
--- /dev/null
+++ b/keama/tests/nxdomainnx6.in6
@@ -0,0 +1,12 @@
+# nxdomain numeric expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# reduce literals
+class "literal" {
+ match if substring(option dhcp6.interface-id, 0, nxdomain % 128) = ab:cd;
+}
+
+# raw
+option dhcp6.interface-id = encode-int(nxdomain + bound, 32);
diff --git a/keama/tests/nxdomainnx6.out b/keama/tests/nxdomainnx6.out
new file mode 100644
index 00000000..c90552e4
--- /dev/null
+++ b/keama/tests/nxdomainnx6.out
@@ -0,0 +1,28 @@
+{
+ # nxdomain numeric expression
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # reduce literals
+ {
+ "name": "literal",
+ /// from: match if (substring(option dhcp6.interface-id, 0, 393231 % 128)) = 0xabcd
+ "test": "substring(option[18].hex,0,15) == 0xabcd"
+ }
+ ],
+ "option-data": [
+ # raw
+ {
+ "space": "dhcp6",
+ "name": "interface-id",
+ "code": 18,
+ "csv-format": false,
+// /// constant DHCP_R_NXDOMAIN(393231)
+// /// constant S_BOUND(5)
+// "original-data": "\u0000\u0006\u0000\u0014",
+ "data": "00060014"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/onxsc4.in4 b/keama/tests/onxsc4.in4
new file mode 100644
index 00000000..f62b720c
--- /dev/null
+++ b/keama/tests/onxsc4.in4
@@ -0,0 +1,12 @@
+# on executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# empty on statement
+on expiry;
+
+# another one
+on commit or release {
+ execute ("myscript", packet(5, 2));
+}
diff --git a/keama/tests/onxsc4.out b/keama/tests/onxsc4.out
new file mode 100644
index 00000000..95134a52
--- /dev/null
+++ b/keama/tests/onxsc4.out
@@ -0,0 +1,34 @@
+{
+ # on executable statement construct
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// # empty on statement
+// "statement": {
+// "on": {
+// "condition": "expiry"
+// }
+// }
+// # another one
+// "statement": {
+// "on": {
+// "condition": "commit or release",
+// "body": [
+// {
+// "execute": {
+// "command": "myscript",
+// "arguments": [
+// {
+// "packet": {
+// "offset": 5,
+// "length": 2
+// }
+// }
+// ]
+// }
+// }
+// ]
+// }
+// }
+ }
+}
diff --git a/keama/tests/optdatagrouppool4.in4 b/keama/tests/optdatagrouppool4.in4
new file mode 100644
index 00000000..999abea2
--- /dev/null
+++ b/keama/tests/optdatagrouppool4.in4
@@ -0,0 +1,18 @@
+# embedded option-data in DHCPv4 pool config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # pool declaration
+ pool {
+ # avoid empty pool
+ range 10.5.5.5 10.5.5.10;
+ # for a silly reason option-data is not allowed in DHCPv4 pools
+ # try to fool this rule using a group
+ group fool {
+ option domain-search "example.com", "example.org";
+ }
+ }
+}
diff --git a/keama/tests/optdatagrouppool4.out b/keama/tests/optdatagrouppool4.out
new file mode 100644
index 00000000..4fb102d3
--- /dev/null
+++ b/keama/tests/optdatagrouppool4.out
@@ -0,0 +1,23 @@
+{
+ # embedded option-data in DHCPv4 pool config
+ # empty configs are not accepted by Kea
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "subnet4": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondata4.in4 b/keama/tests/optiondata4.in4
new file mode 100644
index 00000000..6c5ab639
--- /dev/null
+++ b/keama/tests/optiondata4.in4
@@ -0,0 +1,48 @@
+# option data config
+
+# options
+option space foobar;
+
+option ip-forwarding on;
+
+option foobar.fmt-b-si8 code 3 = signed integer 8;
+option foobar.fmt-b-si8 -100;
+
+option default-ip-ttl 20;
+
+option foobar.fmt-s-si16 code 6 = signed integer 16;
+option foobar.fmt-s-si16 -1000;
+
+option boot-size 16000;
+
+option time-offset -1200;
+
+option path-mtu-aging-timeout 86400;
+
+option swap-server 10.5.5.1;
+
+option foobar.fmt-6 code 12 = ip6-address;
+option foobar.fmt-6 2001::1;
+
+option foobar.fmt-d code 13 = domain-name;
+# Silly, d aka domain-name are without quotes, D aka domain-list are with
+option foobar.fmt-d www.example.com;
+
+option bcms-controller-names "foo.bar", "www.no-where.biz";
+
+option domain-search "example.com", "example.org";
+
+option tftp-server-name "my-server";
+
+option dhcp-client-identifier 01:02:aa:bb;
+
+option foobar.fmt-Z code 18 = zerolen;
+option foobar.fmt-Z;
+
+option foobar.fmt-Ba code 50 = array of unsigned integer 8;
+option dhcp-parameter-request-list 1, 2, 3;
+
+option foobar.fmt-fB code 100 = { boolean, unsigned integer 8 };
+option foobar.fmt-fB off 66;
+
+option routers 10.5.5.1, 10.5.5.2, 10.5.5.3;
diff --git a/keama/tests/optiondata4.out b/keama/tests/optiondata4.out
new file mode 100644
index 00000000..eca745d9
--- /dev/null
+++ b/keama/tests/optiondata4.out
@@ -0,0 +1,173 @@
+{
+ # option data config
+ # options
+ "Dhcp4": {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "ip-forwarding",
+ "code": 19,
+// "original-data": "on",
+ /// canonized booleans to lowercase true or false
+ "data": "true"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-si8",
+ "code": 3,
+ "data": "-100"
+ },
+ {
+ "space": "dhcp4",
+ "name": "default-ip-ttl",
+ "code": 23,
+ "data": "20"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-si16",
+ "code": 6,
+ "data": "-1000"
+ },
+ {
+ "space": "dhcp4",
+ "name": "boot-size",
+ "code": 13,
+ "data": "16000"
+ },
+ {
+ "space": "dhcp4",
+ "name": "time-offset",
+ "code": 2,
+ "data": "-1200"
+ },
+ {
+ "space": "dhcp4",
+ "name": "path-mtu-aging-timeout",
+ "code": 24,
+ "data": "86400"
+ },
+ {
+ "space": "dhcp4",
+ "name": "swap-server",
+ "code": 16,
+ "data": "10.5.5.1"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-6",
+ "code": 12,
+ "data": "2001::1"
+ },
+ # Silly, d aka domain-name are without quotes, D aka domain-list are with
+ {
+ "space": "foobar",
+ "name": "fmt-d",
+ "code": 13,
+ "data": "www.example.com"
+ },
+ {
+ "space": "dhcp4",
+ "name": "bcms-controller-names",
+ "code": 88,
+// "original-data": "\"foo.bar\", \"www.no-where.biz\"",
+ "data": "foo.bar, www.no-where.biz"
+ },
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ },
+ {
+ "space": "dhcp4",
+ "name": "tftp-server-name",
+ "code": 66,
+ "data": "my-server"
+ },
+ {
+ "space": "dhcp4",
+ "name": "dhcp-client-identifier",
+ "code": 61,
+// "original-data": "01:02:aa:bb",
+ "csv-format": false,
+ "data": "0102aabb"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Z",
+ "code": 18,
+ "data": ""
+ },
+ /// Possible PRL hack
+ /// Consider setting "always-send" to true when setting data for relevant options, cf Kea #250
+ {
+ "space": "dhcp4",
+ "name": "dhcp-parameter-request-list",
+ "code": 55,
+ "data": "1, 2, 3"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-fB",
+ "code": 100,
+// "original-data": "off 66",
+ /// canonized booleans to lowercase true or false
+ "data": "false, 66"
+ },
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "10.5.5.1, 10.5.5.2, 10.5.5.3"
+ }
+ ],
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "fmt-b-si8",
+ "code": 3,
+ "type": "int8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-si16",
+ "code": 6,
+ "type": "int16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-6",
+ "code": 12,
+ "type": "ipv6-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-d",
+ "code": 13,
+ "type": "fqdn"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Z",
+ "code": 18,
+ "type": "empty"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Ba",
+ "code": 50,
+ "array": true,
+ "type": "uint8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-fB",
+ "code": 100,
+ "record-types": "boolean, uint8",
+ "type": "record"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondata6.in6 b/keama/tests/optiondata6.in6
new file mode 100644
index 00000000..5af5b87b
--- /dev/null
+++ b/keama/tests/optiondata6.in6
@@ -0,0 +1,49 @@
+# option data config
+
+# options
+option space foobar;
+
+option foobar.fmt-f code 1 = boolean;
+option foobar.fmt-f true;
+
+option foobar.fmt-b-i8 code 2 = integer 8;
+option foobar.fmt-b-i8 -10;
+
+option dhcp6.preference 20;
+
+option foobar.fmt-s-i16 code 5 = integer 16;
+option foobar.fmt-s-i16 -10000;
+
+option foobar.fmt-S-ui16 code 7 = unsigned integer 16;
+option foobar.fmt-S-ui16 36000;
+
+option foobar.fmt-l-i32 code 8 = integer 32;
+option foobar.fmt-l-i32 -86400;
+
+option dhcp6.clt-time 604800;
+
+option foobar.fmt-I code 11 = ip-address;
+option foobar.fmt-I 10.5.5.1;
+
+option foobar.fmt-6 code 12 = ip6-address;
+option dhcp6.unicast 2001::1;
+
+option foobar.fmt-d code 13 = domain-name;
+# Silly, d aka domain-name are without quotes, D aka domain-list are with
+option foobar.fmt-d www.example.com;
+
+option dhcp6.domain-search "example.com", "example.org";
+
+option dhcp6.bootfile-url "http://nowhere/";
+
+option dhcp6.geoconf-civic de:ad:be:ef;
+
+option dhcp6.rapid-commit;
+
+option foobar.fmt-Ba code 50 = array of unsigned integer 8;
+option foobar.fmt-Ba 1, 2, 3;
+
+option foobar.fmt-fB code 100 = { boolean, unsigned integer 8 };
+option foobar.fmt-fB false 66;
+
+option dhcp6.name-servers 2a01:e00::2, 2a01:e00::2;
diff --git a/keama/tests/optiondata6.out b/keama/tests/optiondata6.out
new file mode 100644
index 00000000..b01ba36b
--- /dev/null
+++ b/keama/tests/optiondata6.out
@@ -0,0 +1,178 @@
+{
+ # option data config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "fmt-f",
+ "code": 1,
+ "type": "boolean"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-i8",
+ "code": 2,
+ "type": "int8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-i16",
+ "code": 5,
+ "type": "int16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-S-ui16",
+ "code": 7,
+ "type": "uint16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-l-i32",
+ "code": 8,
+ "type": "int32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-I",
+ "code": 11,
+ "type": "ipv4-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-6",
+ "code": 12,
+ "type": "ipv6-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-d",
+ "code": 13,
+ "type": "fqdn"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Ba",
+ "code": 50,
+ "array": true,
+ "type": "uint8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-fB",
+ "code": 100,
+ "record-types": "boolean, uint8",
+ "type": "record"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "foobar",
+ "name": "fmt-f",
+ "code": 1,
+ "data": "true"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-i8",
+ "code": 2,
+ "data": "-10"
+ },
+ {
+ "space": "dhcp6",
+ "name": "preference",
+ "code": 7,
+ "data": "20"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-i16",
+ "code": 5,
+ "data": "-10000"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-S-ui16",
+ "code": 7,
+ "data": "36000"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-l-i32",
+ "code": 8,
+ "data": "-86400"
+ },
+ {
+ "space": "dhcp6",
+ "name": "clt-time",
+ "code": 46,
+ "data": "604800"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-I",
+ "code": 11,
+ "data": "10.5.5.1"
+ },
+ {
+ "space": "dhcp6",
+ "name": "unicast",
+ "code": 12,
+ "data": "2001::1"
+ },
+ # Silly, d aka domain-name are without quotes, D aka domain-list are with
+ {
+ "space": "foobar",
+ "name": "fmt-d",
+ "code": 13,
+ "data": "www.example.com"
+ },
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ },
+ {
+ "space": "dhcp6",
+ "name": "bootfile-url",
+ "code": 59,
+ "data": "http://nowhere/"
+ },
+ {
+ "space": "dhcp6",
+ "name": "geoconf-civic",
+ "code": 36,
+// "original-data": "de:ad:be:ef",
+ "csv-format": false,
+ "data": "deadbeef"
+ },
+ {
+ "space": "dhcp6",
+ "name": "rapid-commit",
+ "code": 14,
+ "data": ""
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Ba",
+ "code": 50,
+ "data": "1, 2, 3"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-fB",
+ "code": 100,
+ "data": "false, 66"
+ },
+ {
+ "space": "dhcp6",
+ "name": "dns-servers",
+ "code": 23,
+ "data": "2a01:e00::2, 2a01:e00::2"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondatapool4.in4 b/keama/tests/optiondatapool4.in4
new file mode 100644
index 00000000..58751b01
--- /dev/null
+++ b/keama/tests/optiondatapool4.in4
@@ -0,0 +1,12 @@
+# option-data in DHCPv4 pool config
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # pool declaration
+ pool {
+ # avoid empty pool
+ range 10.5.5.5 10.5.5.10;
+ # fixed now
+ option domain-search "example.com", "example.org";
+ }
+}
diff --git a/keama/tests/optiondatapool4.out b/keama/tests/optiondatapool4.out
new file mode 100644
index 00000000..1d3ee427
--- /dev/null
+++ b/keama/tests/optiondatapool4.out
@@ -0,0 +1,31 @@
+{
+ # option-data in DHCPv4 pool config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "10.5.5.5 - 10.5.5.10",
+ "option-data": [
+ # fixed now
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondatapool6.in6 b/keama/tests/optiondatapool6.in6
new file mode 100644
index 00000000..d09ebaf9
--- /dev/null
+++ b/keama/tests/optiondatapool6.in6
@@ -0,0 +1,13 @@
+# option-data in DHCPv6 pool config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # pool declaration
+ pool6 {
+ # avoid empty pool
+ range6 2001::100 2001::200;
+ # for a silly reason option-data is not allowed in DHCPv4 pools
+ # but allowed in DHCPv6 pools
+ option dhcp6.domain-search "example.com", "example.org";
+ }
+}
diff --git a/keama/tests/optiondatapool6.out b/keama/tests/optiondatapool6.out
new file mode 100644
index 00000000..48f722d4
--- /dev/null
+++ b/keama/tests/optiondatapool6.out
@@ -0,0 +1,32 @@
+{
+ # option-data in DHCPv6 pool config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "2001::100 - 2001::200",
+ "option-data": [
+ # for a silly reason option-data is not allowed in DHCPv4 pools
+ # but allowed in DHCPv6 pools
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondecl4.in4 b/keama/tests/optiondecl4.in4
new file mode 100644
index 00000000..4be48838
--- /dev/null
+++ b/keama/tests/optiondecl4.in4
@@ -0,0 +1,27 @@
+# option definition config
+
+# options
+option space foobar;
+
+option foobar.fmt-f code 1 = boolean;
+option foobar.fmt-b-i8 code 2 = integer 8;
+option foobar.fmt-b-si8 code 3 = signed integer 8;
+option foobar.fmt-B-ui8 code 4 = unsigned integer 8;
+option foobar.fmt-s-i16 code 5 = integer 16;
+option foobar.fmt-s-si16 code 6 = signed integer 16;
+option foobar.fmt-S-ui16 code 7 = unsigned integer 16;
+option foobar.fmt-l-i32 code 8 = integer 32;
+option foobar.fmt-l-si32 code 9 = signed integer 32;
+option foobar.fmt-L-ui32 code 10 = unsigned integer 32;
+option foobar.fmt-I code 11 = ip-address;
+option foobar.fmt-6 code 12 = ip6-address;
+option foobar.fmt-d code 13 = domain-name;
+option foobar.fmt-D-list code 14 = domain-list;
+option foobar.fmt-Dc code 15 = domain-list compressed;
+option foobar.fmt-t code 16 = text;
+option foobar.fmt-X code 17 = string;
+option foobar.fmt-Z code 18 = zerolen;
+
+option foobar.fmt-Ba code 50 = array of unsigned integer 8;
+option foobar.fmt-fB code 100 = { boolean, unsigned integer 8 };
+option foobar.fmt-Ia code 150 = { unsigned integer 32, array of boolean };
diff --git a/keama/tests/optiondecl4.out b/keama/tests/optiondecl4.out
new file mode 100644
index 00000000..f74aa32f
--- /dev/null
+++ b/keama/tests/optiondecl4.out
@@ -0,0 +1,143 @@
+{
+ # option definition config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "fmt-f",
+ "code": 1,
+ "type": "boolean"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-i8",
+ "code": 2,
+ "type": "int8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-si8",
+ "code": 3,
+ "type": "int8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-B-ui8",
+ "code": 4,
+ "type": "uint8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-i16",
+ "code": 5,
+ "type": "int16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-si16",
+ "code": 6,
+ "type": "int16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-S-ui16",
+ "code": 7,
+ "type": "uint16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-l-i32",
+ "code": 8,
+ "type": "int32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-l-si32",
+ "code": 9,
+ "type": "int32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-L-ui32",
+ "code": 10,
+ "type": "uint32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-I",
+ "code": 11,
+ "type": "ipv4-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-6",
+ "code": 12,
+ "type": "ipv6-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-d",
+ "code": 13,
+ "type": "fqdn"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-D-list",
+ "code": 14,
+ "array": true,
+ "type": "fqdn"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Dc",
+ "code": 15,
+ "array": true,
+ "type": "fqdn"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-t",
+ "code": 16,
+ "type": "string"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-X",
+ "code": 17,
+ "type": "string"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Z",
+ "code": 18,
+ "type": "empty"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Ba",
+ "code": 50,
+ "array": true,
+ "type": "uint8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-fB",
+ "code": 100,
+ "record-types": "boolean, uint8",
+ "type": "record"
+ },
+ /// unsupported array inside a record
+ {
+ "space": "foobar",
+ "name": "fmt-Ia",
+ "code": 150,
+// "array": true,
+// "definition": "{ uint32, array of boolean}",
+ /// Option definition is not compatible with Kea
+ /// Fallback to full binary
+ "type": "binary"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondecl6.in6 b/keama/tests/optiondecl6.in6
new file mode 100644
index 00000000..37662c0d
--- /dev/null
+++ b/keama/tests/optiondecl6.in6
@@ -0,0 +1,27 @@
+# option definition config
+
+# options
+option space foobar;
+
+option foobar.fmt-f code 1 = boolean;
+option foobar.fmt-b-i8 code 2 = integer 8;
+option foobar.fmt-b-si8 code 3 = signed integer 8;
+option foobar.fmt-B-ui8 code 4 = unsigned integer 8;
+option foobar.fmt-s-i16 code 5 = integer 16;
+option foobar.fmt-s-si16 code 6 = signed integer 16;
+option foobar.fmt-S-ui16 code 7 = unsigned integer 16;
+option foobar.fmt-l-i32 code 8 = integer 32;
+option foobar.fmt-l-si32 code 9 = signed integer 32;
+option foobar.fmt-L-ui32 code 10 = unsigned integer 32;
+option foobar.fmt-I code 11 = ip-address;
+option foobar.fmt-6 code 12 = ip6-address;
+option foobar.fmt-d code 13 = domain-name;
+option foobar.fmt-D-list code 14 = domain-list;
+#option foobar.fmt-Dc code 15 = domain-list compressed;
+option foobar.fmt-t code 16 = text;
+option foobar.fmt-X code 17 = string;
+option foobar.fmt-Z code 18 = zerolen;
+
+option foobar.fmt-Ba code 50 = array of unsigned integer 8;
+option foobar.fmt-fB code 100 = { boolean, unsigned integer 8 };
+option foobar.fmt-Lfa code 150 = { unsigned integer 32, array of boolean };
diff --git a/keama/tests/optiondecl6.out b/keama/tests/optiondecl6.out
new file mode 100644
index 00000000..d4cf828b
--- /dev/null
+++ b/keama/tests/optiondecl6.out
@@ -0,0 +1,137 @@
+{
+ # option definition config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "fmt-f",
+ "code": 1,
+ "type": "boolean"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-i8",
+ "code": 2,
+ "type": "int8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-b-si8",
+ "code": 3,
+ "type": "int8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-B-ui8",
+ "code": 4,
+ "type": "uint8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-i16",
+ "code": 5,
+ "type": "int16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-s-si16",
+ "code": 6,
+ "type": "int16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-S-ui16",
+ "code": 7,
+ "type": "uint16"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-l-i32",
+ "code": 8,
+ "type": "int32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-l-si32",
+ "code": 9,
+ "type": "int32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-L-ui32",
+ "code": 10,
+ "type": "uint32"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-I",
+ "code": 11,
+ "type": "ipv4-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-6",
+ "code": 12,
+ "type": "ipv6-address"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-d",
+ "code": 13,
+ "type": "fqdn"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-D-list",
+ "code": 14,
+ "array": true,
+ "type": "fqdn"
+ },
+ #option foobar.fmt-Dc code 15 = domain-list compressed;
+ {
+ "space": "foobar",
+ "name": "fmt-t",
+ "code": 16,
+ "type": "string"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-X",
+ "code": 17,
+ "type": "string"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Z",
+ "code": 18,
+ "type": "empty"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-Ba",
+ "code": 50,
+ "array": true,
+ "type": "uint8"
+ },
+ {
+ "space": "foobar",
+ "name": "fmt-fB",
+ "code": 100,
+ "record-types": "boolean, uint8",
+ "type": "record"
+ },
+ /// unsupported array inside a record
+ {
+ "space": "foobar",
+ "name": "fmt-Lfa",
+ "code": 150,
+// "array": true,
+// "definition": "{ uint32, array of boolean}",
+ /// Option definition is not compatible with Kea
+ /// Fallback to full binary
+ "type": "binary"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optiondeclBat4.in4 b/keama/tests/optiondeclBat4.in4
new file mode 100644
index 00000000..0901a14f
--- /dev/null
+++ b/keama/tests/optiondeclBat4.in4
@@ -0,0 +1,6 @@
+# bad Bat (non terminal array) option declaration
+
+option space foobar;
+option foobar.fmt-Bat code 1 = { array of unsigned integer 8, text };
+
+
diff --git a/keama/tests/optiondeclBat4.out b/keama/tests/optiondeclBat4.out
new file mode 100644
index 00000000..e671dcfa
--- /dev/null
+++ b/keama/tests/optiondeclBat4.out
@@ -0,0 +1,18 @@
+{
+ # bad Bat (non terminal array) option declaration
+ "Dhcp4": {
+ "option-def": [
+ /// unsupported array inside a record
+ {
+ "space": "foobar",
+ "name": "fmt-Bat",
+ "code": 1,
+// "array": true,
+// "definition": "{ array of uint8, string}",
+ /// Option definition is not compatible with Kea
+ /// Fallback to full binary
+ "type": "binary"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optionencap4.in4 b/keama/tests/optionencap4.in4
new file mode 100644
index 00000000..f5dddfdb
--- /dev/null
+++ b/keama/tests/optionencap4.in4
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+option space xyz;
+
+option foobar.encap code 1 = encapsulate xyz;
diff --git a/keama/tests/optionencap4.out b/keama/tests/optionencap4.out
new file mode 100644
index 00000000..ee59fc42
--- /dev/null
+++ b/keama/tests/optionencap4.out
@@ -0,0 +1,15 @@
+{
+ # option definition config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "encap",
+ "code": 1,
+ "type": "empty",
+ "encapsulate": "xyz"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optionencap6.in6 b/keama/tests/optionencap6.in6
new file mode 100644
index 00000000..f5dddfdb
--- /dev/null
+++ b/keama/tests/optionencap6.in6
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+option space xyz;
+
+option foobar.encap code 1 = encapsulate xyz;
diff --git a/keama/tests/optionencap6.out b/keama/tests/optionencap6.out
new file mode 100644
index 00000000..733fc714
--- /dev/null
+++ b/keama/tests/optionencap6.out
@@ -0,0 +1,15 @@
+{
+ # option definition config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "encap",
+ "code": 1,
+ "type": "empty",
+ "encapsulate": "xyz"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optionexpr4.in4 b/keama/tests/optionexpr4.in4
new file mode 100644
index 00000000..bc0889dd
--- /dev/null
+++ b/keama/tests/optionexpr4.in4
@@ -0,0 +1,16 @@
+# option data expressions
+
+# options
+option mytext code 250 = text;
+option mytext = substring("foobar", 1, 3);
+
+option mydata code 251 = string;
+option mydata = concat("\b", "\125\126");
+
+option mydata2 code 252 = string;
+option mydata2 = aa:bb:cc;
+
+# not yet
+option mydata3 code 253 = string;
+option mydata3 = concat(aa, bb, cc);
+
diff --git a/keama/tests/optionexpr4.out b/keama/tests/optionexpr4.out
new file mode 100644
index 00000000..3582b3d7
--- /dev/null
+++ b/keama/tests/optionexpr4.out
@@ -0,0 +1,81 @@
+{
+ # option data expressions
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "mytext",
+ "code": 250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "mydata",
+ "code": 251,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "mydata2",
+ "code": 252,
+ "type": "string"
+ },
+ # not yet
+ {
+ "space": "dhcp4",
+ "name": "mydata3",
+ "code": 253,
+ "type": "string"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "mytext",
+ "code": 250,
+ "csv-format": false,
+// "original-data": "oob",
+ "data": "6f6f62"
+ },
+ {
+ "space": "dhcp4",
+ "name": "mydata",
+ "code": 251,
+ "csv-format": false,
+// "original-data": "\bUV",
+ "data": "085556"
+ },
+ {
+ "space": "dhcp4",
+ "name": "mydata2",
+ "code": 252,
+ "csv-format": false,
+ "data": "aabbcc"
+ }
+// {
+// "space": "dhcp4",
+// "name": "mydata3",
+// "code": 253,
+// "csv-format": false,
+// "expression": {
+// "concat": {
+// "left": {
+// "const-data": "0xaa"
+// },
+// "right": {
+// "concat": {
+// "left": {
+// "const-data": "0xbb"
+// },
+// "right": {
+// "const-data": "0xcc"
+// }
+// }
+// }
+// }
+// }
+// }
+ ]
+ }
+}
diff --git a/keama/tests/optionspace4.in4 b/keama/tests/optionspace4.in4
new file mode 100644
index 00000000..fa87b6a5
--- /dev/null
+++ b/keama/tests/optionspace4.in4
@@ -0,0 +1,15 @@
+# group and class declaration config
+
+# option spaces
+option space foobar;
+
+option space foo code width 1 length width 1;
+
+option space bar code width 4 length width 4;
+
+option space full code width 2 length width 2 hash size 111;
+
+option foobar.test code 1 = text;
+
+option foo.test code 1 = text;
+
diff --git a/keama/tests/optionspace4.out b/keama/tests/optionspace4.out
new file mode 100644
index 00000000..53482692
--- /dev/null
+++ b/keama/tests/optionspace4.out
@@ -0,0 +1,34 @@
+{
+ # group and class declaration config
+ # option spaces
+ "Dhcp4": {
+// "option-space": {
+// "name": "bar",
+// /// Only code width 1 is supported
+// "code-width": 4,
+// /// Only length width 1 is supported
+// "length-width": 4
+// },
+// "option-space": {
+// "name": "full",
+// /// Only code width 1 is supported
+// "code-width": 2,
+// /// Only length width 1 is supported
+// "length-width": 2
+// },
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "test",
+ "code": 1,
+ "type": "string"
+ },
+ {
+ "space": "foo",
+ "name": "test",
+ "code": 1,
+ "type": "string"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optionspace6.in6 b/keama/tests/optionspace6.in6
new file mode 100644
index 00000000..a94a665f
--- /dev/null
+++ b/keama/tests/optionspace6.in6
@@ -0,0 +1,15 @@
+# group and class declaration config
+
+# option spaces
+option space foobar;
+
+option space foo code width 2 length width 2;
+
+option space bar code width 4 length width 4;
+
+option space full code width 1 length width 1 hash size 111;
+
+option foobar.test code 1 = text;
+
+option foo.test code 1 = text;
+
diff --git a/keama/tests/optionspace6.out b/keama/tests/optionspace6.out
new file mode 100644
index 00000000..fdc9ea72
--- /dev/null
+++ b/keama/tests/optionspace6.out
@@ -0,0 +1,34 @@
+{
+ # group and class declaration config
+ # option spaces
+ "Dhcp6": {
+// "option-space": {
+// "name": "bar",
+// /// Only code width 2 is supported
+// "code-width": 4,
+// /// Only length width 2 is supported
+// "length-width": 4
+// },
+// "option-space": {
+// "name": "full",
+// /// Only code width 2 is supported
+// "code-width": 1,
+// /// Only length width 2 is supported
+// "length-width": 1
+// },
+ "option-def": [
+ {
+ "space": "foobar",
+ "name": "test",
+ "code": 1,
+ "type": "string"
+ },
+ {
+ "space": "foo",
+ "name": "test",
+ "code": 1,
+ "type": "string"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optionvendor4.in4 b/keama/tests/optionvendor4.in4
new file mode 100644
index 00000000..49be45d7
--- /dev/null
+++ b/keama/tests/optionvendor4.in4
@@ -0,0 +1,8 @@
+# group and class declaration config
+
+# vendor option space
+option space foobar;
+
+option vendor.foobar code 12345 = encapsulate foobar;
+option foobar.test code 1 = text;
+option foobar.test "a test";
diff --git a/keama/tests/optionvendor4.out b/keama/tests/optionvendor4.out
new file mode 100644
index 00000000..80a9a5e8
--- /dev/null
+++ b/keama/tests/optionvendor4.out
@@ -0,0 +1,28 @@
+{
+ # group and class declaration config
+ # vendor option space
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "vendor-12345",
+ "name": "test",
+ "code": 1,
+ "type": "string"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "vivso-suboptions",
+ "code": 125,
+ "data": "12345"
+ },
+ {
+ "space": "vendor-12345",
+ "name": "test",
+ "code": 1,
+ "data": "a test"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/optionvendor6.in6 b/keama/tests/optionvendor6.in6
new file mode 100644
index 00000000..9a1a3757
--- /dev/null
+++ b/keama/tests/optionvendor6.in6
@@ -0,0 +1,8 @@
+# group and class declaration config
+
+# vendor option space
+option space foobar;
+
+option vsio.foobar code 12345 = encapsulate foobar;
+option foobar.test code 1 = text;
+option foobar.test "a test";
diff --git a/keama/tests/optionvendor6.out b/keama/tests/optionvendor6.out
new file mode 100644
index 00000000..8e163978
--- /dev/null
+++ b/keama/tests/optionvendor6.out
@@ -0,0 +1,28 @@
+{
+ # group and class declaration config
+ # vendor option space
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "vendor-12345",
+ "name": "test",
+ "code": 1,
+ "type": "string"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "vendor-opts",
+ "code": 17,
+ "data": "12345"
+ },
+ {
+ "space": "vendor-12345",
+ "name": "test",
+ "code": 1,
+ "data": "a test"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/orphan4.inn b/keama/tests/orphan4.inn
new file mode 100644
index 00000000..85f95639
--- /dev/null
+++ b/keama/tests/orphan4.inn
@@ -0,0 +1,10 @@
+# DHCPv4 orphan reservation config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# orphan reservation
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ option ip-forwarding off;
+}
diff --git a/keama/tests/orphan4.out b/keama/tests/orphan4.out
new file mode 100644
index 00000000..c0146767
--- /dev/null
+++ b/keama/tests/orphan4.out
@@ -0,0 +1,30 @@
+{
+ # DHCPv4 orphan reservation config
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "host-reservation-identifiers": [
+ "hw-address"
+ ]
+// /// Orphan reservations
+// /// Kea reservations are per subnet
+// /// Reference Kea #231
+// "reservations": [
+// # orphan reservation
+// {
+// "hostname": "foobar",
+// "hw-address": "00:0b:fd:32:e6:fa",
+// "option-data": [
+// {
+// "space": "dhcp4",
+// "name": "ip-forwarding",
+// "code": 19,
+// "original-data": "off",
+// /// canonized booleans to lowercase true or false
+// "data": "false"
+// }
+// ]
+// }
+// ]
+ }
+}
diff --git a/keama/tests/orphan6.inN b/keama/tests/orphan6.inN
new file mode 100644
index 00000000..e85368f2
--- /dev/null
+++ b/keama/tests/orphan6.inN
@@ -0,0 +1,10 @@
+# DHCPv6 orphan reservation config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# orphan reservation
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ option dhcp6.name-servers 2a01:e00::2, 2a01:e00::1;
+}
diff --git a/keama/tests/orphan6.out b/keama/tests/orphan6.out
new file mode 100644
index 00000000..0c0b8114
--- /dev/null
+++ b/keama/tests/orphan6.out
@@ -0,0 +1,28 @@
+{
+ # DHCPv6 orphan reservation config
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "host-reservation-identifiers": [
+ "hw-address"
+ ]
+// /// Orphan reservations
+// /// Kea reservations are per subnet
+// /// Reference Kea #231
+// "reservations": [
+// # orphan reservation
+// {
+// "hostname": "foobar",
+// "hw-address": "00:0b:fd:32:e6:fa",
+// "option-data": [
+// {
+// "space": "dhcp6",
+// "name": "dns-servers",
+// "code": 23,
+// "data": "2a01:e00::2, 2a01:e00::1"
+// }
+// ]
+// }
+// ]
+ }
+}
diff --git a/keama/tests/packetdx4.notyet b/keama/tests/packetdx4.notyet
new file mode 100644
index 00000000..03f3237d
--- /dev/null
+++ b/keama/tests/packetdx4.notyet
@@ -0,0 +1,20 @@
+# packet data expression
+# Kea has no raw packet extractor in libeval
+
+# authoritative is mandatory
+authoritative;
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# pretty standard hardware superclass extracting the Ethernet address directly
+class "byhw" {
+ match packet(28, 6);
+}
+
+subclass "byhw" 00:07:0E:36:48:19 {
+ option host-name "test1";
+}
+
+# raw
+option host-name = binary-to-ascii(16, 8, "-", packet(28, 6));
diff --git a/keama/tests/permitauth4.in4 b/keama/tests/permitauth4.in4
new file mode 100644
index 00000000..b9663fd2
--- /dev/null
+++ b/keama/tests/permitauth4.in4
@@ -0,0 +1,15 @@
+# DHCPv4 permit authenticated client config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # pool declaration
+ pool {
+ # avoid empty pool
+ range 10.5.5.5 10.5.5.10;
+ # call get_permit
+ allow authenticated clients;
+ }
+}
diff --git a/keama/tests/permitauth4.out b/keama/tests/permitauth4.out
new file mode 100644
index 00000000..0f938566
--- /dev/null
+++ b/keama/tests/permitauth4.out
@@ -0,0 +1,33 @@
+{
+ # DHCPv4 permit authenticated client config
+ # empty configs are not accepted by Kea
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "subnet4": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "10.5.5.5 - 10.5.5.10",
+ /// From:
+ /// allow authenticated clients
+ /// [un]authenticated-clients is not supported by ISC DHCP and Kea
+ "client-class": "gen#!ALL#"
+ }
+ ]
+ }
+ ],
+ "client-classes": [
+ {
+ "name": "gen#!ALL#",
+ "test": "not member('ALL')"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/permitauth6.in6 b/keama/tests/permitauth6.in6
new file mode 100644
index 00000000..ce0a25aa
--- /dev/null
+++ b/keama/tests/permitauth6.in6
@@ -0,0 +1,15 @@
+# DHCPv6 permit authenticated client config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# subnet declaration
+subnet6 2001::/64 {
+ # pool declaration
+ pool6 {
+ # avoid empty pool
+ range6 2001::100 2001::200;
+ # call get_permit
+ deny unauthenticated clients;
+ }
+}
diff --git a/keama/tests/permitauth6.out b/keama/tests/permitauth6.out
new file mode 100644
index 00000000..f2013e77
--- /dev/null
+++ b/keama/tests/permitauth6.out
@@ -0,0 +1,33 @@
+{
+ # DHCPv6 permit authenticated client config
+ # empty configs are not accepted by Kea
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "subnet6": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "2001::100 - 2001::200",
+ /// From:
+ /// deny unauthenticated clients
+ /// [un]authenticated-clients is not supported by ISC DHCP and Kea
+ "client-class": "gen#_AND_#!ALL#"
+ }
+ ]
+ }
+ ],
+ "client-classes": [
+ {
+ "name": "gen#_AND_#!ALL#",
+ "test": "not member('ALL')"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/permitknown4.in4 b/keama/tests/permitknown4.in4
new file mode 100644
index 00000000..df15e11e
--- /dev/null
+++ b/keama/tests/permitknown4.in4
@@ -0,0 +1,15 @@
+# DHCPv4 permit known client config
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # pool declaration
+ pool {
+ # avoid empty pool
+ range 10.5.5.5 10.5.5.10;
+ # call get_permit
+ allow known clients;
+ }
+}
diff --git a/keama/tests/permitknown4.out b/keama/tests/permitknown4.out
new file mode 100644
index 00000000..1a27d5a9
--- /dev/null
+++ b/keama/tests/permitknown4.out
@@ -0,0 +1,26 @@
+{
+ # DHCPv4 permit known client config
+ # empty configs are not accepted by Kea
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "subnet4": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # pool declaration
+ {
+ # avoid empty pool
+ "pool": "10.5.5.5 - 10.5.5.10",
+ /// From:
+ /// allow known clients
+ "client-class": "KNOWN"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/pickdx6.in6 b/keama/tests/pickdx6.in6
new file mode 100644
index 00000000..ccfa48ce
--- /dev/null
+++ b/keama/tests/pickdx6.in6
@@ -0,0 +1,15 @@
+# pick-first-value data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# reduce literals
+class "literal" {
+ match if option dhcp6.client-data =
+ pick-first-value(substring("abcd",0,0),null,"foobar");
+}
+
+# null
+class "null" {
+ match if option dhcp6.client-data = pick(null);
+}
diff --git a/keama/tests/pickdx6.out b/keama/tests/pickdx6.out
new file mode 100644
index 00000000..d8de1130
--- /dev/null
+++ b/keama/tests/pickdx6.out
@@ -0,0 +1,21 @@
+{
+ # pick-first-value data expression
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # reduce literals
+ {
+ "name": "literal",
+ /// from: match if (option dhcp6.client-data) = (pick-first-value(substring('abcd', 0, 0), null, 'foobar'))
+ "test": "option[45].hex == 'foobar'"
+ },
+ # null
+ {
+ "name": "null",
+ /// from: match if (option dhcp6.client-data) = (pick-first-value(null))
+ "test": "option[45].hex == ''"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/pool4.in4 b/keama/tests/pool4.in4
new file mode 100644
index 00000000..0e156141
--- /dev/null
+++ b/keama/tests/pool4.in4
@@ -0,0 +1,11 @@
+# DHCPv4 pool config
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # pool declaration
+ pool {
+ option domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ range 10.5.5.5 10.5.5.10;
+ }
+}
diff --git a/keama/tests/pool4.out b/keama/tests/pool4.out
new file mode 100644
index 00000000..e0b6bee8
--- /dev/null
+++ b/keama/tests/pool4.out
@@ -0,0 +1,31 @@
+{
+ # DHCPv4 pool config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ /// default-valid-lifetime moved from an internal pool scope
+ "valid-lifetime": 1800,
+ "pools": [
+ # pool declaration
+ {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/pool42.in4 b/keama/tests/pool42.in4
new file mode 100644
index 00000000..7e7fea3f
--- /dev/null
+++ b/keama/tests/pool42.in4
@@ -0,0 +1,15 @@
+# DHCPv4 pool with 2 ranges config
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # pool declaration
+ pool {
+ option domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ range 10.5.5.5 10.5.5.10;
+ # add a second range
+ range 10.5.5.11 10.5.5.12;
+ }
+ # interface
+ interface "en0";
+}
diff --git a/keama/tests/pool42.out b/keama/tests/pool42.out
new file mode 100644
index 00000000..38a039b8
--- /dev/null
+++ b/keama/tests/pool42.out
@@ -0,0 +1,49 @@
+{
+ # DHCPv4 pool with 2 ranges config
+ # subnet declaration
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ /// default-valid-lifetime moved from an internal pool scope
+ "valid-lifetime": 1800,
+ "pools": [
+ # pool declaration
+ {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "pool": "10.5.5.5 - 10.5.5.10"
+ },
+ # pool declaration
+ {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ # add a second range
+ "pool": "10.5.5.11 - 10.5.5.12"
+ }
+ ],
+ "interface": "en0"
+ }
+ ],
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ }
+ }
+}
diff --git a/keama/tests/pool6.in6 b/keama/tests/pool6.in6
new file mode 100644
index 00000000..2cd2fa39
--- /dev/null
+++ b/keama/tests/pool6.in6
@@ -0,0 +1,11 @@
+# DHCPv6 pool config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # pool declaration
+ pool6 {
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ range6 2001::100 2001::200;
+ }
+}
diff --git a/keama/tests/pool6.out b/keama/tests/pool6.out
new file mode 100644
index 00000000..626610dc
--- /dev/null
+++ b/keama/tests/pool6.out
@@ -0,0 +1,31 @@
+{
+ # DHCPv6 pool config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ /// default-valid-lifetime moved from an internal pool scope
+ "valid-lifetime": 1800,
+ "pools": [
+ # pool declaration
+ {
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "pool": "2001::100 - 2001::200"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/pool6in4.err4 b/keama/tests/pool6in4.err4
new file mode 100644
index 00000000..44d19cf6
--- /dev/null
+++ b/keama/tests/pool6in4.err4
@@ -0,0 +1,13 @@
+# DHCPv6 pool declaration in DHCPv4 config
+
+# host declaration
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ default-lease-time 1800;
+}
+
+# There is a declaration so the error message is different
+
+# DHCPv6 pool declaration must be in DHCPv6 config
+range6 2001::/64;
+
diff --git a/keama/tests/pool6in4.msg b/keama/tests/pool6in4.msg
new file mode 100644
index 00000000..f6df706e
--- /dev/null
+++ b/keama/tests/pool6in4.msg
@@ -0,0 +1 @@
+pool6in4.err4 line 12: expecting a declaration
diff --git a/keama/tests/poolinroot4.err4 b/keama/tests/poolinroot4.err4
new file mode 100644
index 00000000..6caa6d47
--- /dev/null
+++ b/keama/tests/poolinroot4.err4
@@ -0,0 +1,7 @@
+# DHCPv4 pool declaration in root config
+
+# DHCPv4 pool declaration must be in a shared-network or subnet declaration
+pool {
+ range 204.152.185.135 204.152.185.185;
+}
+
diff --git a/keama/tests/poolinroot4.msg b/keama/tests/poolinroot4.msg
new file mode 100644
index 00000000..845744fd
--- /dev/null
+++ b/keama/tests/poolinroot4.msg
@@ -0,0 +1 @@
+poolinroot4.err4 line 4: pool declared outside of network
diff --git a/keama/tests/poolinroot6.err6 b/keama/tests/poolinroot6.err6
new file mode 100644
index 00000000..6605f086
--- /dev/null
+++ b/keama/tests/poolinroot6.err6
@@ -0,0 +1,7 @@
+# DHCPv6 pool declaration in root config
+
+# DHCPv6 pool declaration must be in a shared-network or subnet declaration
+pool6 {
+ range6 2001::/64;
+}
+
diff --git a/keama/tests/poolinroot6.msg b/keama/tests/poolinroot6.msg
new file mode 100644
index 00000000..5f48f13d
--- /dev/null
+++ b/keama/tests/poolinroot6.msg
@@ -0,0 +1 @@
+poolinroot6.err6 line 4: pool6 declared outside of network
diff --git a/keama/tests/preferred6.in6 b/keama/tests/preferred6.in6
new file mode 100644
index 00000000..9fd8519f
--- /dev/null
+++ b/keama/tests/preferred6.in6
@@ -0,0 +1,14 @@
+# preferred lifetime
+
+preferred-lifetime 1200;
+
+# embedded in pool
+subnet6 2001::/64 {
+ # silently overwritten
+ preferred-lifetime 1800;
+ pool6 {
+ preferred-lifetime 2400;
+ range6 2001::1000 2001::1fff;
+ }
+}
+
diff --git a/keama/tests/preferred6.out b/keama/tests/preferred6.out
new file mode 100644
index 00000000..88acf33d
--- /dev/null
+++ b/keama/tests/preferred6.out
@@ -0,0 +1,30 @@
+{
+ # preferred lifetime
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "preferred-lifetime": 1200,
+ "renew-timer": 600,
+ "rebind-timer": 960,
+ "subnet6": [
+ # embedded in pool
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "preferred-lifetime": 1800,
+ "renew-timer": 900,
+ "rebind-timer": 1440,
+// /// preferred-lifetime moved from an internal pool scope
+// /// Avoid to overwrite current value...
+// "preferred-lifetime": 2400,
+// "renew-timer": 1200,
+// "rebind-timer": 1920,
+ "pools": [
+ {
+ "pool": "2001::1000 - 2001::1fff"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/prefix0.err6 b/keama/tests/prefix0.err6
new file mode 100644
index 00000000..c10b6d15
--- /dev/null
+++ b/keama/tests/prefix0.err6
@@ -0,0 +1,9 @@
+# DHCPv6 (bad 128 bit length) prefix config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # range declaration
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: / 128;
+}
diff --git a/keama/tests/prefix0.msg b/keama/tests/prefix0.msg
new file mode 100644
index 00000000..73952817
--- /dev/null
+++ b/keama/tests/prefix0.msg
@@ -0,0 +1 @@
+prefix0.err6 line 8: networks have 0 to 128 bits (exclusive)
diff --git a/keama/tests/prefix128.err6 b/keama/tests/prefix128.err6
new file mode 100644
index 00000000..c10b6d15
--- /dev/null
+++ b/keama/tests/prefix128.err6
@@ -0,0 +1,9 @@
+# DHCPv6 (bad 128 bit length) prefix config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # range declaration
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: / 128;
+}
diff --git a/keama/tests/prefix128.msg b/keama/tests/prefix128.msg
new file mode 100644
index 00000000..ee874c74
--- /dev/null
+++ b/keama/tests/prefix128.msg
@@ -0,0 +1 @@
+prefix128.err6 line 8: networks have 0 to 128 bits (exclusive)
diff --git a/keama/tests/prefix6.in6 b/keama/tests/prefix6.in6
new file mode 100644
index 00000000..e5c01c90
--- /dev/null
+++ b/keama/tests/prefix6.in6
@@ -0,0 +1,9 @@
+# DHCPv6 prefix config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # range declaration
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: / 64;
+}
diff --git a/keama/tests/prefix6.out b/keama/tests/prefix6.out
new file mode 100644
index 00000000..e0fe3ea5
--- /dev/null
+++ b/keama/tests/prefix6.out
@@ -0,0 +1,32 @@
+{
+ # DHCPv6 prefix config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "option-data": [
+ # range declaration
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "pd-pools": [
+ {
+ "prefix": "2001:0:0:10::",
+ "delegated-len": 64,
+ "prefix-len": 60
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/prefix62.in6 b/keama/tests/prefix62.in6
new file mode 100644
index 00000000..408481a5
--- /dev/null
+++ b/keama/tests/prefix62.in6
@@ -0,0 +1,10 @@
+# DHCPv6 prefix config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # range declaration
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ prefix6 2001:0:0:1:: 2001:0:0:3:: / 64;
+ interface "en0";
+}
diff --git a/keama/tests/prefix62.out b/keama/tests/prefix62.out
new file mode 100644
index 00000000..0ce536a8
--- /dev/null
+++ b/keama/tests/prefix62.out
@@ -0,0 +1,36 @@
+{
+ # DHCPv6 prefix config
+ # subnet declaration
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "option-data": [
+ # range declaration
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "pd-pools": [
+// {
+// "prefix": "2001:0:0:1::",
+// "delegated-len": 64,
+// "prefix-highest": "2001:0:0:3::"
+// }
+ ],
+ "interface": "en0"
+ }
+ ],
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ }
+ }
+}
diff --git a/keama/tests/prefixinroot6.err6 b/keama/tests/prefixinroot6.err6
new file mode 100644
index 00000000..f1fd4a1e
--- /dev/null
+++ b/keama/tests/prefixinroot6.err6
@@ -0,0 +1,4 @@
+# DHCPv6 prefix declaration in root config
+
+# DHCPv6 prefix declaration must be in a subnet declaration
+prefix6 2001:: 2001:0:1:: / 64;
diff --git a/keama/tests/prefixinroot6.msg b/keama/tests/prefixinroot6.msg
new file mode 100644
index 00000000..8aa498f4
--- /dev/null
+++ b/keama/tests/prefixinroot6.msg
@@ -0,0 +1 @@
+prefixinroot6.err6 line 4: prefix6 declaration not allowed here.
diff --git a/keama/tests/qualifyingsuffix4.in4 b/keama/tests/qualifyingsuffix4.in4
new file mode 100644
index 00000000..cef53f4d
--- /dev/null
+++ b/keama/tests/qualifyingsuffix4.in4
@@ -0,0 +1,9 @@
+# ddns-domainname (aka qualifying-suffix)
+
+ddns-domainname ".biz";
+ddns-updates true;
+
+# embedded
+class "foo" {
+ ddns-domainname ".bar";
+}
diff --git a/keama/tests/qualifyingsuffix4.out b/keama/tests/qualifyingsuffix4.out
new file mode 100644
index 00000000..f014362d
--- /dev/null
+++ b/keama/tests/qualifyingsuffix4.out
@@ -0,0 +1,17 @@
+{
+ # ddns-domainname (aka qualifying-suffix)
+ "Dhcp4": {
+ "dhcp-ddns": {
+ "qualifying-suffix": ".biz",
+ "enable-updates": true
+ },
+ "client-classes": [
+ # embedded
+ {
+ "name": "foo"
+// /// Only global qualifying-suffix is supported
+// "qualifying-suffix": ".bar"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/qualifyingsuffix6.in6 b/keama/tests/qualifyingsuffix6.in6
new file mode 100644
index 00000000..3957eae2
--- /dev/null
+++ b/keama/tests/qualifyingsuffix6.in6
@@ -0,0 +1,8 @@
+# ddns-domainname (aka qualifying-suffix)
+
+ddns-domainname ".biz";
+
+# embedded
+class "foo" {
+ ddns-domainname ".bar";
+}
diff --git a/keama/tests/qualifyingsuffix6.out b/keama/tests/qualifyingsuffix6.out
new file mode 100644
index 00000000..44ae84a1
--- /dev/null
+++ b/keama/tests/qualifyingsuffix6.out
@@ -0,0 +1,17 @@
+{
+ # ddns-domainname (aka qualifying-suffix)
+ "Dhcp6": {
+ "dhcp-ddns": {
+ "enable-updates": false,
+ "qualifying-suffix": ".biz"
+ },
+ "client-classes": [
+ # embedded
+ {
+ "name": "foo"
+// /// Only global qualifying-suffix is supported
+// "qualifying-suffix": ".bar"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/range4.in4 b/keama/tests/range4.in4
new file mode 100644
index 00000000..f755a2eb
--- /dev/null
+++ b/keama/tests/range4.in4
@@ -0,0 +1,9 @@
+# DHCPv4 range config
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # range declaration
+ option domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ range 10.5.5.5 10.5.5.10;
+}
diff --git a/keama/tests/range4.out b/keama/tests/range4.out
new file mode 100644
index 00000000..94611f7e
--- /dev/null
+++ b/keama/tests/range4.out
@@ -0,0 +1,30 @@
+{
+ # DHCPv4 range config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "option-data": [
+ # range declaration
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "pools": [
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/range6.in6 b/keama/tests/range6.in6
new file mode 100644
index 00000000..45a0635a
--- /dev/null
+++ b/keama/tests/range6.in6
@@ -0,0 +1,11 @@
+# DHCPv6 range config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # range declaration
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ range6 2001::100 2001::200;
+ range6 2001::1000/116;
+ interface "en0";
+}
diff --git a/keama/tests/range6.out b/keama/tests/range6.out
new file mode 100644
index 00000000..1e506ab5
--- /dev/null
+++ b/keama/tests/range6.out
@@ -0,0 +1,37 @@
+{
+ # DHCPv6 range config
+ # subnet declaration
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "option-data": [
+ # range declaration
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "pools": [
+ {
+ "pool": "2001::100 - 2001::200"
+ },
+ {
+ "pool": "2001::1000/116"
+ }
+ ],
+ "interface": "en0"
+ }
+ ],
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ }
+ }
+}
diff --git a/keama/tests/range6in4.err4 b/keama/tests/range6in4.err4
new file mode 100644
index 00000000..af3630c0
--- /dev/null
+++ b/keama/tests/range6in4.err4
@@ -0,0 +1,5 @@
+# DHCPv6 pool declaration in DHCPv4 config
+
+# DHCPv6 pool declaration must be in DHCPv6 config
+range6 2001::/64;
+
diff --git a/keama/tests/range6in4.msg b/keama/tests/range6in4.msg
new file mode 100644
index 00000000..d27a8452
--- /dev/null
+++ b/keama/tests/range6in4.msg
@@ -0,0 +1 @@
+range6in4.err4 line 4: expecting a parameter or declaration
diff --git a/keama/tests/rangeinroot4.err4 b/keama/tests/rangeinroot4.err4
new file mode 100644
index 00000000..7a3d36f9
--- /dev/null
+++ b/keama/tests/rangeinroot4.err4
@@ -0,0 +1,4 @@
+# DHCPv4 range declaration in root config
+
+# DHCPv4 range declaration must be in a subnet declaration
+range 204.152.185.135 204.152.185.185;
diff --git a/keama/tests/rangeinroot4.msg b/keama/tests/rangeinroot4.msg
new file mode 100644
index 00000000..4ed397e4
--- /dev/null
+++ b/keama/tests/rangeinroot4.msg
@@ -0,0 +1 @@
+rangeinroot4.err4 line 4: range declaration not allowed here.
diff --git a/keama/tests/rangeinroot6.err6 b/keama/tests/rangeinroot6.err6
new file mode 100644
index 00000000..e8c5c65e
--- /dev/null
+++ b/keama/tests/rangeinroot6.err6
@@ -0,0 +1,5 @@
+# DHCPv6 range declaration in root config
+
+# DHCPv6 range declaration must be in a subnet declaration
+range6 2001::/64;
+
diff --git a/keama/tests/rangeinroot6.msg b/keama/tests/rangeinroot6.msg
new file mode 100644
index 00000000..969fe658
--- /dev/null
+++ b/keama/tests/rangeinroot6.msg
@@ -0,0 +1 @@
+rangeinroot6.err6 line 4: range6 declaration not allowed here.
diff --git a/keama/tests/reversedx6.in6 b/keama/tests/reversedx6.in6
new file mode 100644
index 00000000..ba762787
--- /dev/null
+++ b/keama/tests/reversedx6.in6
@@ -0,0 +1,9 @@
+# reverse data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# reduce literals
+class "literal" {
+ match if option dhcp6.client-data = reverse(2 & 2, "foobar");
+}
diff --git a/keama/tests/reversedx6.out b/keama/tests/reversedx6.out
new file mode 100644
index 00000000..f13efe95
--- /dev/null
+++ b/keama/tests/reversedx6.out
@@ -0,0 +1,15 @@
+{
+ # reverse data expression
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # reduce literals
+ {
+ "name": "literal",
+ /// from: match if (option dhcp6.client-data) = (reverse(2 & 2, 'foobar'))
+ "test": "option[45].hex == 'arobfo'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/runall.sh b/keama/tests/runall.sh
new file mode 100644
index 00000000..f8cf357e
--- /dev/null
+++ b/keama/tests/runall.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+#set -x
+
+cd "$(dirname "$0")"
+
+echo subdirs:
+/bin/sh samples/runall.sh
+
+echo tests:
+for t in *.err* *.in*
+do
+ echo `basename $t`
+ /bin/sh runone.sh $t
+done
diff --git a/keama/tests/runone.sh b/keama/tests/runone.sh
new file mode 100644
index 00000000..4838c973
--- /dev/null
+++ b/keama/tests/runone.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#set -x
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 test-name" >&2
+ exit 1
+fi
+
+file=$1
+
+cd "$(dirname "$0")"
+
+isin=$(expr $file : ".*\.in*")
+iserr=$(expr $file : ".*\.err*")
+if [ \( $isin -eq 0 \) -a \( $iserr -eq 0 \) ]; then
+ full=$file.in*
+ if [ ! -f $full ]; then
+ full=$file.err*
+ fi
+else
+ full=$file
+fi
+
+if [ ! -f $full ]; then
+ echo "can't find $file" >&2
+ exit 1
+fi
+
+errcase=$(expr $full : ".*\.err*")
+
+trail=
+if [ $errcase -eq 0 ]; then
+ trail=$(expr $full : ".*\.in\(.\)")
+else
+ trail=$(expr $full : ".*\.err\(.\)")
+fi
+
+options=""
+dual=0
+hook=0
+
+case $trail in
+ '') dual=1;;
+ 4) options="-4";;
+ 6) options="-6";;
+ F) options="-4 -r fatal";;
+ P) options="-4 -r pass";;
+ d) options="-4 -D";;
+ D) options="-6 -D";;
+ n) options="-4 -N";;
+ N) options="-6 -N";;
+ l) options="-4 -l ${HOOK:-/path/}"; hook=1;;
+ L) options="-6 -l ${HOOK:-/path/}"; hook=1;;
+ *) echo "unrecognized trail '$trail' in '$full'" >&2; exit 1;;
+esac
+
+if [ $errcase -ne 0 ]; then
+ base=$(basename $full .err$trail)
+else
+ if [ $dual -ne 0 ]; then
+ echo "required trail ([45FP]) in '$full'" >&2
+ exit 1
+ fi
+ base=$(basename $full .in$trail)
+fi
+
+out=/tmp/$base.out$$
+expected=""
+if [ $errcase -ne 0 ]; then
+ expected=$base.msg
+else
+ expected=$base.out
+fi
+
+if [ $errcase -ne 0 ]; then
+ if [ $dual -eq 1 ]; then
+ ../keama -4 -i $full >$out 2>&1
+ if [ $? -ne 255 ]; then
+ echo "$full -4 doesn't fail as expected" >&2
+ exit 1
+ fi
+ ../keama -6 -i $full >$out 2>&1
+ if [ $? -ne 255 ]; then
+ echo "$full -6 doesn't fail as expected" >&2
+ exit 1
+ fi
+ else
+ ../keama $options -i $full >$out 2>&1
+ if [ $? -ne 255 ]; then
+ echo "$full doesn't fail as expected" >&2
+ exit 1
+ fi
+ fi
+else
+ ../keama $options -i $full -o $out >&2
+ if [ $? -eq 255 ]; then
+ echo "$full raised an error" >&2
+ exit 1
+ fi
+fi
+
+if [ $hook -eq 1 ]; then
+ sed s,/path/,${HOOK:-/path/}, < ${expected}L > $expected
+fi
+
+if [ $errcase -ne 0 ]; then
+ cat $out | head -1 | diff --brief - $expected
+ if [ $? -ne 0 ]; then
+ echo "$full doesn't provide expected output" >&2
+ exit 1
+ fi
+else
+ diff --brief $out $expected
+ if [ $? -ne 0 ]; then
+ echo "$full doesn't provide expected output" >&2
+ exit 1
+ fi
+fi
diff --git a/keama/tests/samples/example.conf b/keama/tests/samples/example.conf
new file mode 100644
index 00000000..62b088dc
--- /dev/null
+++ b/keama/tests/samples/example.conf
@@ -0,0 +1,111 @@
+# dhcpd.conf
+#
+# Sample configuration file for ISC dhcpd
+#
+
+# option definitions common to all supported networks...
+option domain-name "example.org";
+#option domain-name-servers ns1.example.org, ns2.example.org;
+option domain-name-servers 10.35.0.1, 10.35.0.2;
+
+default-lease-time 600;
+max-lease-time 7200;
+
+# Use this to enble / disable dynamic dns updates globally.
+#ddns-update-style none;
+
+# If this DHCP server is the official DHCP server for the local
+# network, the authoritative directive should be uncommented.
+#authoritative;
+
+# Use this to send dhcp log messages to a different log file (you also
+# have to hack syslog.conf to complete the redirection).
+log-facility local7;
+
+# No service will be given on this subnet, but declaring it helps the
+# DHCP server to understand the network topology.
+
+subnet 10.152.187.0 netmask 255.255.255.0 {
+}
+
+# This is a very basic subnet declaration.
+
+subnet 10.254.239.0 netmask 255.255.255.224 {
+ range 10.254.239.10 10.254.239.20;
+# option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
+ option routers 10.254.239.1, 10.254.239.2;
+}
+
+# This declaration allows BOOTP clients to get dynamic addresses,
+# which we don't really recommend.
+
+subnet 10.254.239.32 netmask 255.255.255.224 {
+ range dynamic-bootp 10.254.239.40 10.254.239.60;
+ option broadcast-address 10.254.239.31;
+# option routers rtr-239-32-1.example.org;
+ option routers 10.254.239.33;
+}
+
+# A slightly different configuration for an internal subnet.
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.26 10.5.5.30;
+# option domain-name-servers ns1.internal.example.org;
+ option domain-name-servers 10.35.1.1;
+ option domain-name "internal.example.org";
+ option routers 10.5.5.1;
+ option broadcast-address 10.5.5.31;
+ default-lease-time 600;
+ max-lease-time 7200;
+}
+
+# Hosts which require special configuration options can be listed in
+# host statements. If no address is specified, the address will be
+# allocated dynamically (if possible), but the host-specific information
+# will still come from the host declaration.
+
+host passacaglia {
+ hardware ethernet 0:0:c0:5d:bd:95;
+ filename "vmunix.passacaglia";
+ server-name "toccata.example.com";
+}
+
+# Fixed IP addresses can also be specified for hosts. These addresses
+# should not also be listed as being available for dynamic assignment.
+# Hosts for which fixed IP addresses have been specified can boot using
+# BOOTP or DHCP. Hosts for which no fixed address is specified can only
+# be booted with DHCP, unless there is an address range on the subnet
+# to which a BOOTP client is connected which has the dynamic-bootp flag
+# set.
+host fantasia {
+ hardware ethernet 08:00:07:26:c0:a5;
+# fixed-address fantasia.example.com;
+ fixed-address 10.5.5.20;
+}
+
+# You can declare a class of clients and then do address allocation
+# based on that. The example below shows a case where all clients
+# in a certain class get addresses on the 10.17.224/24 subnet, and all
+# other clients get addresses on the 10.0.29/24 subnet.
+
+class "foo" {
+ match if substring (option vendor-class-identifier, 0, 4) = "SUNW";
+}
+
+shared-network 224-29 {
+ subnet 10.17.224.0 netmask 255.255.255.0 {
+# option routers rtr-224.example.org;
+ option routers 10.17.224.1;
+ }
+ subnet 10.0.29.0 netmask 255.255.255.0 {
+# option routers rtr-29.example.org;
+ option routers 10.0.29.1;
+ }
+ pool {
+ allow members of "foo";
+ range 10.17.224.10 10.17.224.250;
+ }
+ pool {
+ deny members of "foo";
+ range 10.0.29.10 10.0.29.230;
+ }
+}
diff --git a/keama/tests/samples/example.conf.orig b/keama/tests/samples/example.conf.orig
new file mode 100644
index 00000000..8b99f496
--- /dev/null
+++ b/keama/tests/samples/example.conf.orig
@@ -0,0 +1,104 @@
+# dhcpd.conf
+#
+# Sample configuration file for ISC dhcpd
+#
+
+# option definitions common to all supported networks...
+option domain-name "example.org";
+option domain-name-servers ns1.example.org, ns2.example.org;
+
+default-lease-time 600;
+max-lease-time 7200;
+
+# Use this to enble / disable dynamic dns updates globally.
+#ddns-update-style none;
+
+# If this DHCP server is the official DHCP server for the local
+# network, the authoritative directive should be uncommented.
+#authoritative;
+
+# Use this to send dhcp log messages to a different log file (you also
+# have to hack syslog.conf to complete the redirection).
+log-facility local7;
+
+# No service will be given on this subnet, but declaring it helps the
+# DHCP server to understand the network topology.
+
+subnet 10.152.187.0 netmask 255.255.255.0 {
+}
+
+# This is a very basic subnet declaration.
+
+subnet 10.254.239.0 netmask 255.255.255.224 {
+ range 10.254.239.10 10.254.239.20;
+ option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
+}
+
+# This declaration allows BOOTP clients to get dynamic addresses,
+# which we don't really recommend.
+
+subnet 10.254.239.32 netmask 255.255.255.224 {
+ range dynamic-bootp 10.254.239.40 10.254.239.60;
+ option broadcast-address 10.254.239.31;
+ option routers rtr-239-32-1.example.org;
+}
+
+# A slightly different configuration for an internal subnet.
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.26 10.5.5.30;
+ option domain-name-servers ns1.internal.example.org;
+ option domain-name "internal.example.org";
+ option routers 10.5.5.1;
+ option broadcast-address 10.5.5.31;
+ default-lease-time 600;
+ max-lease-time 7200;
+}
+
+# Hosts which require special configuration options can be listed in
+# host statements. If no address is specified, the address will be
+# allocated dynamically (if possible), but the host-specific information
+# will still come from the host declaration.
+
+host passacaglia {
+ hardware ethernet 0:0:c0:5d:bd:95;
+ filename "vmunix.passacaglia";
+ server-name "toccata.example.com";
+}
+
+# Fixed IP addresses can also be specified for hosts. These addresses
+# should not also be listed as being available for dynamic assignment.
+# Hosts for which fixed IP addresses have been specified can boot using
+# BOOTP or DHCP. Hosts for which no fixed address is specified can only
+# be booted with DHCP, unless there is an address range on the subnet
+# to which a BOOTP client is connected which has the dynamic-bootp flag
+# set.
+host fantasia {
+ hardware ethernet 08:00:07:26:c0:a5;
+ fixed-address fantasia.example.com;
+}
+
+# You can declare a class of clients and then do address allocation
+# based on that. The example below shows a case where all clients
+# in a certain class get addresses on the 10.17.224/24 subnet, and all
+# other clients get addresses on the 10.0.29/24 subnet.
+
+class "foo" {
+ match if substring (option vendor-class-identifier, 0, 4) = "SUNW";
+}
+
+shared-network 224-29 {
+ subnet 10.17.224.0 netmask 255.255.255.0 {
+ option routers rtr-224.example.org;
+ }
+ subnet 10.0.29.0 netmask 255.255.255.0 {
+ option routers rtr-29.example.org;
+ }
+ pool {
+ allow members of "foo";
+ range 10.17.224.10 10.17.224.250;
+ }
+ pool {
+ deny members of "foo";
+ range 10.0.29.10 10.0.29.230;
+ }
+}
diff --git a/keama/tests/samples/example.json b/keama/tests/samples/example.json
new file mode 100644
index 00000000..7123a158
--- /dev/null
+++ b/keama/tests/samples/example.json
@@ -0,0 +1,223 @@
+{
+ # dhcpd.conf
+ #
+ # Sample configuration file for ISC dhcpd
+ #
+ # option definitions common to all supported networks...
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-name",
+ "code": 15,
+ "data": "example.org"
+ },
+ #option domain-name-servers ns1.example.org, ns2.example.org;
+ {
+ "space": "dhcp4",
+ "name": "domain-name-servers",
+ "code": 6,
+ "data": "10.35.0.1, 10.35.0.2"
+ }
+ ],
+ "valid-lifetime": 600,
+ "max-valid-lifetime": 7200,
+// "config": [
+// /// log-facility is not supported
+// /// Please use the KEA_LOGGER_DESTINATION environment variable instead
+// {
+// "name": "log-facility",
+// "code": 44,
+// "value": "local7"
+// }
+// ],
+ "subnet4": [
+ # No service will be given on this subnet, but declaring it helps the
+ # DHCP server to understand the network topology.
+ {
+ "id": 1,
+ "subnet": "10.152.187.0/24"
+ },
+ # This is a very basic subnet declaration.
+ {
+ "id": 2,
+ "subnet": "10.254.239.0/27",
+ "pools": [
+ {
+ "pool": "10.254.239.10 - 10.254.239.20"
+ }
+ ],
+ "option-data": [
+ # option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "10.254.239.1, 10.254.239.2"
+ }
+ ]
+ },
+ # This declaration allows BOOTP clients to get dynamic addresses,
+ # which we don't really recommend.
+ {
+ "id": 3,
+ "subnet": "10.254.239.32/27",
+ "pools": [
+ {
+ "pool": "10.254.239.40 - 10.254.239.60"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "broadcast-address",
+ "code": 28,
+ "data": "10.254.239.31"
+ },
+ # option routers rtr-239-32-1.example.org;
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "10.254.239.33"
+ }
+ ]
+ },
+ # A slightly different configuration for an internal subnet.
+ {
+ "id": 4,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ {
+ "pool": "10.5.5.26 - 10.5.5.30"
+ }
+ ],
+ "option-data": [
+ # option domain-name-servers ns1.internal.example.org;
+ {
+ "space": "dhcp4",
+ "name": "domain-name-servers",
+ "code": 6,
+ "data": "10.35.1.1"
+ },
+ {
+ "space": "dhcp4",
+ "name": "domain-name",
+ "code": 15,
+ "data": "internal.example.org"
+ },
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "10.5.5.1"
+ },
+ {
+ "space": "dhcp4",
+ "name": "broadcast-address",
+ "code": 28,
+ "data": "10.5.5.31"
+ }
+ ],
+ "valid-lifetime": 600,
+ "max-valid-lifetime": 7200,
+ /// Host reservations without fixed addresses were put in the last declared subnet
+ /// Reference Kea #231
+ "reservations": [
+ # Hosts which require special configuration options can be listed in
+ # host statements. If no address is specified, the address will be
+ # allocated dynamically (if possible), but the host-specific information
+ # will still come from the host declaration.
+ {
+ "hostname": "passacaglia",
+ "hw-address": "00:00:c0:5d:bd:95",
+ "boot-file-name": "vmunix.passacaglia",
+ "server-hostname": "toccata.example.com"
+ },
+ # Fixed IP addresses can also be specified for hosts. These addresses
+ # should not also be listed as being available for dynamic assignment.
+ # Hosts for which fixed IP addresses have been specified can boot using
+ # BOOTP or DHCP. Hosts for which no fixed address is specified can only
+ # be booted with DHCP, unless there is an address range on the subnet
+ # to which a BOOTP client is connected which has the dynamic-bootp flag
+ # set.
+ {
+ "hostname": "fantasia",
+ "hw-address": "08:00:07:26:c0:a5",
+ "ip-address": "10.5.5.20"
+ }
+ ]
+ }
+ ],
+ "host-reservation-identifiers": [
+ "hw-address"
+ ],
+ "client-classes": [
+ # You can declare a class of clients and then do address allocation
+ # based on that. The example below shows a case where all clients
+ # in a certain class get addresses on the 10.17.224/24 subnet, and all
+ # other clients get addresses on the 10.0.29/24 subnet.
+ {
+ "name": "foo",
+ /// from: match if (substring(option dhcp.vendor-class-identifier, 0, 4)) = 'SUNW'
+ "test": "substring(option[60].hex,0,4) == 'SUNW'"
+ },
+ {
+ "name": "gen#_AND_#!foo#",
+ "test": "not member('foo')"
+ }
+ ],
+ /// Kea shared-networks are different, cf Kea #236
+ "shared-networks": [
+ {
+ "name": "224-29",
+ "subnet4": [
+ {
+ "id": 5,
+ "subnet": "10.17.224.0/24",
+ "option-data": [
+ # option routers rtr-224.example.org;
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "10.17.224.1"
+ }
+ ],
+ "pools": [
+ {
+ "pool": "10.17.224.10 - 10.17.224.250",
+ /// From:
+ /// allow foo
+ "client-class": "foo"
+ }
+ ]
+ },
+ {
+ "id": 6,
+ "subnet": "10.0.29.0/24",
+ "option-data": [
+ # option routers rtr-29.example.org;
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "10.0.29.1"
+ }
+ ],
+ "pools": [
+ {
+ "pool": "10.0.29.10 - 10.0.29.230",
+ /// From:
+ /// deny foo
+ "client-class": "gen#_AND_#!foo#"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/samples/runall.sh b/keama/tests/samples/runall.sh
new file mode 100644
index 00000000..bc41c7f8
--- /dev/null
+++ b/keama/tests/samples/runall.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+#set -x
+
+cd "$(dirname "$0")"
+
+for t in example simple test-a6 vmnet8
+do
+ echo $t
+ /bin/sh runone.sh $t
+done
diff --git a/keama/tests/samples/runone.sh b/keama/tests/samples/runone.sh
new file mode 100644
index 00000000..6526d798
--- /dev/null
+++ b/keama/tests/samples/runone.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+#set -x
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 config-name" >&2
+ exit 1
+fi
+
+file=$1
+
+cd "$(dirname "$0")"
+
+trail=$(expr $file : ".*6$")
+options=""
+if [ $trail -eq 0 ]; then
+ options="-4"
+else
+ options="-6"
+fi
+
+out=/tmp/$file.out$$
+
+../../keama $options -N -i $file.conf -o $out >&2
+status=$?
+if [ $status -eq 255 ]; then
+ echo "$file config raised an error" >&2
+ exit 1
+fi
+
+expected=$file.json
+
+diff --brief $out $expected
+if [ $? -ne 0 ]; then
+ echo "$file config doesn't provide expected output" >&2
+ exit 1
+fi
+
+exit $status
diff --git a/keama/tests/samples/simple.conf b/keama/tests/samples/simple.conf
new file mode 100644
index 00000000..76c1cfa5
--- /dev/null
+++ b/keama/tests/samples/simple.conf
@@ -0,0 +1,33 @@
+# -------------------------
+# dhcpd.conf
+#
+# Sample configuration file for ISC dhcpd
+#
+
+# option definitions common to all supported networks...
+option domain-name "example.org";
+# option domain-name-servers ns1.example.org, ns2.example.org;
+
+default-lease-time 1800;
+max-lease-time 7200;
+
+# We're going to be authoritative for the network we've
+# just created.
+
+authoritative;
+
+# No service will be given on this subnet, but we're telling
+# the DHCP server about it so it understands it's there and
+# not to hand out leases for it.
+
+subnet 10.14.8.195 netmask 255.255.255.0 {
+}
+
+# But we do want to hand out leases for the 192.168.1.0/24
+# network for purposes of this test..
+
+subnet 192.168.1.0 netmask 255.255.255.0 {
+ range 192.168.1.100 192.168.1.150;
+ option routers 192.168.1.1;
+}
+# -------------------------
diff --git a/keama/tests/samples/simple.json b/keama/tests/samples/simple.json
new file mode 100644
index 00000000..7cd02423
--- /dev/null
+++ b/keama/tests/samples/simple.json
@@ -0,0 +1,53 @@
+{
+ # -------------------------
+ # dhcpd.conf
+ #
+ # Sample configuration file for ISC dhcpd
+ #
+ # option definitions common to all supported networks...
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-name",
+ "code": 15,
+ "data": "example.org"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "max-valid-lifetime": 7200,
+ # We're going to be authoritative for the network we've
+ # just created.
+ "authoritative": true,
+ "subnet4": [
+ # No service will be given on this subnet, but we're telling
+ # the DHCP server about it so it understands it's there and
+ # not to hand out leases for it.
+ {
+ "id": 1,
+ "subnet": "10.14.8.195/24"
+ },
+ # But we do want to hand out leases for the 192.168.1.0/24
+ # network for purposes of this test..
+ {
+ "id": 2,
+ "subnet": "192.168.1.0/24",
+ "pools": [
+ {
+ "pool": "192.168.1.100 - 192.168.1.150"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "192.168.1.1"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/samples/test-a6.conf b/keama/tests/samples/test-a6.conf
new file mode 100644
index 00000000..9514c4aa
--- /dev/null
+++ b/keama/tests/samples/test-a6.conf
@@ -0,0 +1,75 @@
+#
+# Define the DHCPv6 option space.
+#
+# Option numbers are assigned by IANA:
+# http://www.iana.org/assignments/dhcpv6-parameters
+#
+option dhcp6.time-servers code 1040 = array of ip6-address;
+option dhcp6.time-offset code 1041 = signed integer 32;
+
+#
+# Define the DOCSIS option space.
+# TODO: DOCSIS oro definition
+#
+# this space is already known by Kea
+option space docsis code width 2 length width 2;
+option vsio.docsis code 4491 = encapsulate docsis;
+option docsis.tftp-servers code 32 = array of ip6-address;
+% option docsis.tftp-servers local;
+option docsis.cablelabs-configuration-file code 33 = text;
+% option docsis.cablelabs-configuration-file alias config-file;
+% option docsis.cablelabs-configuration-file local;
+option docsis.cablelabs-syslog-servers code 34 = array of ip6-address;
+% option docsis.cablelabs-syslog-servers alias syslog-servers;
+% option docsis.cablelabs-syslog-servers local;
+#option docsis.device-id code 36 = string;
+% option docsis.device-id code 36 = "X";
+% option docsis.device-id local;
+
+#
+# Declare some options.
+#
+option dhcp6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2;
+option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2;
+
+#
+# DNS server IP address to update dynamically
+#
+ddns-update-style interim;
+ddns-domainname "foo.com";
+
+#
+# Per-host settings.
+#
+host cablemodem-1 {
+ host-identifier option
+ dhcp6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6;
+ fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff;
+ ddns-domainname "bar.com";
+ option dhcp6.time-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option dhcp6.time-offset -14400; # -4 hours
+ option docsis.cablelabs-configuration-file "bootfile.cfg";
+ option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+}
+
+#host cablemodem-2 {
+# host-identifier option docsis.device-id 00:06:5B:50:99:F6;
+# option dhcp6.time-servers 3ffe:dddd:aaaa:aaaa::1,
+# 3ffe:dddd:aaaa:aaaa::2;
+# option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1,
+# 3ffe:dddd:aaaa:aaaa::2;
+# option dhcp6.time-offset -14400; # -4 hours
+# option docsis.cablelabs-configuration-file "bootfile.cfg";
+# option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+# 3ffe:aaaa:aaaa:aaaa::2;
+#}
+
+# XXX: for testing
+subnet6 3ffe:aaaa:aaaa:aaaa::/64 {
+}
+
+
diff --git a/keama/tests/samples/test-a6.conf.orig b/keama/tests/samples/test-a6.conf.orig
new file mode 100644
index 00000000..9fe42364
--- /dev/null
+++ b/keama/tests/samples/test-a6.conf.orig
@@ -0,0 +1,67 @@
+#
+# Define the DHCPv6 option space.
+#
+# Option numbers are assigned by IANA:
+# http://www.iana.org/assignments/dhcpv6-parameters
+#
+option dhcp6.time-servers code 40 = array of ip6-address;
+option dhcp6.time-offset code 41 = signed integer 32;
+
+#
+# Define the DOCSIS option space.
+# TODO: DOCSIS oro definition
+#
+option space docsis code width 2 length width 2;
+option vsio.docsis code 4491 = encapsulate docsis;
+option docsis.tftp-servers code 32 = array of ip6-address;
+option docsis.cablelabs-configuration-file code 33 = text;
+option docsis.cablelabs-syslog-servers code 34 = array of ip6-address;
+option docsis.device-id code 36 = string;
+
+#
+# Declare some options.
+#
+option dhcp6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2;
+option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2;
+
+#
+# DNS server IP address to update dynamically
+#
+ddns-update-style interim;
+ddns-domainname "foo.com";
+
+#
+# Per-host settings.
+#
+host cablemodem-1 {
+ host-identifier option
+ dhcp6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6;
+ fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff;
+ ddns-domainname "bar.com";
+ option dhcp6.time-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option dhcp6.time-offset -14400; # -4 hours
+ option docsis.cablelabs-configuration-file "bootfile.cfg";
+ option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+}
+
+#host cablemodem-2 {
+# host-identifier option docsis.device-id 00:06:5B:50:99:F6;
+# option dhcp6.time-servers 3ffe:dddd:aaaa:aaaa::1,
+# 3ffe:dddd:aaaa:aaaa::2;
+# option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1,
+# 3ffe:dddd:aaaa:aaaa::2;
+# option dhcp6.time-offset -14400; # -4 hours
+# option docsis.cablelabs-configuration-file "bootfile.cfg";
+# option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+# 3ffe:aaaa:aaaa:aaaa::2;
+#}
+
+# XXX: for testing
+subnet6 fe80::20c:29ff:fe42:820/128 {
+}
+
+
diff --git a/keama/tests/samples/test-a6.json b/keama/tests/samples/test-a6.json
new file mode 100644
index 00000000..8b768686
--- /dev/null
+++ b/keama/tests/samples/test-a6.json
@@ -0,0 +1,144 @@
+{
+ #
+ # Define the DHCPv6 option space.
+ #
+ # Option numbers are assigned by IANA:
+ # http://www.iana.org/assignments/dhcpv6-parameters
+ #
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "time-servers",
+ "code": 1040,
+ "array": true,
+ "type": "ipv6-address"
+ },
+ {
+ "space": "dhcp6",
+ "name": "time-offset",
+ "code": 1041,
+ "type": "int32"
+ }
+ ],
+ "option-data": [
+ #option docsis.device-id code 36 = string;
+ #
+ # Declare some options.
+ #
+ {
+ "space": "dhcp6",
+ "name": "time-servers",
+ "code": 1040,
+ "data": "3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2"
+ },
+ {
+ "space": "dhcp6",
+ "name": "vendor-opts",
+ "code": 17,
+ "data": "4491"
+ },
+ {
+ "space": "vendor-4491",
+ "name": "tftp-servers",
+ "code": 32,
+ "data": "3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2"
+ }
+ ],
+// /// Unsupported ddns-update-style interim
+// "ddns-update-style": "interim",
+ "dhcp-ddns": {
+ "enable-updates": true,
+ "qualifying-suffix": "foo.com"
+ },
+ "host-reservation-identifiers": [
+ "flex-id"
+ ],
+ /// The flexible host identifier is a premium feature
+ "hooks-libraries": [
+ {
+ /// Please update the path here
+ "library": "/path/libdhcp_flex_id.so",
+ "parameters": {
+ "identifier-expression": "option[1].hex"
+ }
+ }
+ ],
+ "subnet6": [
+ #host cablemodem-2 {
+ # host-identifier option docsis.device-id 00:06:5B:50:99:F6;
+ # option dhcp6.time-servers 3ffe:dddd:aaaa:aaaa::1,
+ # 3ffe:dddd:aaaa:aaaa::2;
+ # option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1,
+ # 3ffe:dddd:aaaa:aaaa::2;
+ # option dhcp6.time-offset -14400; # -4 hours
+ # option docsis.cablelabs-configuration-file "bootfile.cfg";
+ # option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+ # 3ffe:aaaa:aaaa:aaaa::2;
+ #}
+ # XXX: for testing
+ {
+ "id": 1,
+ "subnet": "3ffe:aaaa:aaaa:aaaa::/64",
+ "reservations": [
+ #
+ # Per-host settings.
+ #
+ {
+ "hostname": "cablemodem-1",
+ "flex-id": "000100010c00a14100065b5099f6",
+ "ip-addresses": [
+ "3ffe:aaaa:aaaa:aaaa::ffff"
+ ],
+// /// Only global qualifying-suffix is supported
+// "qualifying-suffix": "bar.com",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "time-servers",
+ "code": 1040,
+// "original-data": "3ffe:aaaa:aaaa:aaaa::1, \n\t\t\t\t 3ffe:aaaa:aaaa:aaaa::2",
+ "data": "3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2"
+ },
+ {
+ "space": "dhcp6",
+ "name": "vendor-opts",
+ "code": 17,
+ "data": "4491"
+ },
+ {
+ "space": "vendor-4491",
+ "name": "tftp-servers",
+ "code": 32,
+// "original-data": "3ffe:aaaa:aaaa:aaaa::1,\n\t\t\t\t 3ffe:aaaa:aaaa:aaaa::2",
+ "data": "3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2"
+ },
+ {
+ "space": "dhcp6",
+ "name": "time-offset",
+ "code": 1041,
+ "data": "-14400"
+ },
+ # -4 hours
+ {
+ "space": "vendor-4491",
+ "name": "config-file",
+ "code": 33,
+ "data": "bootfile.cfg"
+ },
+ {
+ "space": "vendor-4491",
+ "name": "syslog-servers",
+ "code": 34,
+// "original-data": "3ffe:aaaa:aaaa:aaaa::1,\n\t\t\t\t\t 3ffe:aaaa:aaaa:aaaa::2",
+ "data": "3ffe:aaaa:aaaa:aaaa::1, 3ffe:aaaa:aaaa:aaaa::2"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/samples/vmnet8.conf b/keama/tests/samples/vmnet8.conf
new file mode 100644
index 00000000..9484938e
--- /dev/null
+++ b/keama/tests/samples/vmnet8.conf
@@ -0,0 +1,43 @@
+# Configuration file for ISC 2.0 vmnet-dhcpd operating on vmnet8.
+#
+# This file was automatically generated by the VMware configuration program.
+# See Instructions below if you want to modify it.
+#
+# We set domain-name-servers to make some DHCP clients happy
+# (dhclient as configured in SuSE, TurboLinux, etc.).
+# We also supply a domain name to make pump (Red Hat 6.x) happy.
+#
+
+
+###### VMNET DHCP Configuration. Start of "DO NOT MODIFY SECTION" #####
+# Modification Instructions: This section of the configuration file contains
+# information generated by the configuration program. Do not modify this
+# section.
+# You are free to modify everything else. Also, this section must start
+# on a new line
+# This file will get backed up with a different name in the same directory
+# if this section is edited and you try to configure DHCP again.
+
+# Written at: 04/12/2017 14:00:17
+allow unknown-clients;
+default-lease-time 1800; # default is 30 minutes
+max-lease-time 7200; # default is 2 hours
+
+subnet 172.16.254.0 netmask 255.255.255.0 {
+ range 172.16.254.128 172.16.254.254;
+ option broadcast-address 172.16.254.255;
+ option domain-name-servers 172.16.254.2;
+ option domain-name localdomain;
+ default-lease-time 1800; # default is 30 minutes
+ max-lease-time 7200; # default is 2 hours
+ option netbios-name-servers 172.16.254.2;
+ option routers 172.16.254.2;
+}
+host vmnet8 {
+ hardware ethernet 00:50:56:C0:00:08;
+ fixed-address 172.16.254.1;
+ option domain-name-servers 0.0.0.0;
+# option domain-name "";
+ option routers 0.0.0.0;
+}
+####### VMNET DHCP Configuration. End of "DO NOT MODIFY SECTION" #######
diff --git a/keama/tests/samples/vmnet8.conf.orig b/keama/tests/samples/vmnet8.conf.orig
new file mode 100644
index 00000000..313deca7
--- /dev/null
+++ b/keama/tests/samples/vmnet8.conf.orig
@@ -0,0 +1,43 @@
+# Configuration file for ISC 2.0 vmnet-dhcpd operating on vmnet8.
+#
+# This file was automatically generated by the VMware configuration program.
+# See Instructions below if you want to modify it.
+#
+# We set domain-name-servers to make some DHCP clients happy
+# (dhclient as configured in SuSE, TurboLinux, etc.).
+# We also supply a domain name to make pump (Red Hat 6.x) happy.
+#
+
+
+###### VMNET DHCP Configuration. Start of "DO NOT MODIFY SECTION" #####
+# Modification Instructions: This section of the configuration file contains
+# information generated by the configuration program. Do not modify this
+# section.
+# You are free to modify everything else. Also, this section must start
+# on a new line
+# This file will get backed up with a different name in the same directory
+# if this section is edited and you try to configure DHCP again.
+
+# Written at: 04/12/2017 14:00:17
+allow unknown-clients;
+default-lease-time 1800; # default is 30 minutes
+max-lease-time 7200; # default is 2 hours
+
+subnet 172.16.254.0 netmask 255.255.255.0 {
+ range 172.16.254.128 172.16.254.254;
+ option broadcast-address 172.16.254.255;
+ option domain-name-servers 172.16.254.2;
+ option domain-name localdomain;
+ default-lease-time 1800; # default is 30 minutes
+ max-lease-time 7200; # default is 2 hours
+ option netbios-name-servers 172.16.254.2;
+ option routers 172.16.254.2;
+}
+host vmnet8 {
+ hardware ethernet 00:50:56:C0:00:08;
+ fixed-address 172.16.254.1;
+ option domain-name-servers 0.0.0.0;
+ option domain-name "";
+ option routers 0.0.0.0;
+}
+####### VMNET DHCP Configuration. End of "DO NOT MODIFY SECTION" #######
diff --git a/keama/tests/samples/vmnet8.json b/keama/tests/samples/vmnet8.json
new file mode 100644
index 00000000..9b207ff8
--- /dev/null
+++ b/keama/tests/samples/vmnet8.json
@@ -0,0 +1,105 @@
+{
+ # Configuration file for ISC 2.0 vmnet-dhcpd operating on vmnet8.
+ #
+ # This file was automatically generated by the VMware configuration program.
+ # See Instructions below if you want to modify it.
+ #
+ # We set domain-name-servers to make some DHCP clients happy
+ # (dhclient as configured in SuSE, TurboLinux, etc.).
+ # We also supply a domain name to make pump (Red Hat 6.x) happy.
+ #
+ ###### VMNET DHCP Configuration. Start of "DO NOT MODIFY SECTION" #####
+ # Modification Instructions: This section of the configuration file contains
+ # information generated by the configuration program. Do not modify this
+ # section.
+ # You are free to modify everything else. Also, this section must start
+ # on a new line
+ # This file will get backed up with a different name in the same directory
+ # if this section is edited and you try to configure DHCP again.
+ # Written at: 04/12/2017 14:00:17
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+// "statement": {
+// "config": {
+// "value": "allow",
+// "name": "boot-unknown-clients",
+// "code": 6
+// }
+// },
+ "valid-lifetime": 1800,
+ "max-valid-lifetime": 7200,
+ "subnet4": [
+ # default is 2 hours
+ {
+ "id": 1,
+ "subnet": "172.16.254.0/24",
+ "pools": [
+ {
+ "pool": "172.16.254.128 - 172.16.254.254"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "broadcast-address",
+ "code": 28,
+ "data": "172.16.254.255"
+ },
+ {
+ "space": "dhcp4",
+ "name": "domain-name-servers",
+ "code": 6,
+ "data": "172.16.254.2"
+ },
+ {
+ "space": "dhcp4",
+ "name": "domain-name",
+ "code": 15,
+ "data": "localdomain"
+ },
+ # default is 2 hours
+ {
+ "space": "dhcp4",
+ "name": "netbios-name-servers",
+ "code": 44,
+ "data": "172.16.254.2"
+ },
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "172.16.254.2"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "max-valid-lifetime": 7200,
+ "reservations": [
+ {
+ "hostname": "vmnet8",
+ "hw-address": "00:50:56:c0:00:08",
+ "ip-address": "172.16.254.1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-name-servers",
+ "code": 6,
+ "data": "0.0.0.0"
+ },
+ # option domain-name "";
+ {
+ "space": "dhcp4",
+ "name": "routers",
+ "code": 3,
+ "data": "0.0.0.0"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "host-reservation-identifiers": [
+ "hw-address"
+ ]
+ }
+}
diff --git a/keama/tests/share0.err b/keama/tests/share0.err
new file mode 100644
index 00000000..6b2184e4
--- /dev/null
+++ b/keama/tests/share0.err
@@ -0,0 +1,6 @@
+# bad (empty name)shared-network declaration config
+
+# shared-network declaration
+shared-network "" {
+
+}
diff --git a/keama/tests/share0.msg b/keama/tests/share0.msg
new file mode 100644
index 00000000..4296f4e8
--- /dev/null
+++ b/keama/tests/share0.msg
@@ -0,0 +1 @@
+share0.err line 4: zero-length shared network name
diff --git a/keama/tests/share2if.err b/keama/tests/share2if.err
new file mode 100644
index 00000000..2c40326d
--- /dev/null
+++ b/keama/tests/share2if.err
@@ -0,0 +1,7 @@
+# bad (2 interfaces)shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ interface "foo";
+ interface "bar";
+}
diff --git a/keama/tests/share2if.msg b/keama/tests/share2if.msg
new file mode 100644
index 00000000..bc4b9c10
--- /dev/null
+++ b/keama/tests/share2if.msg
@@ -0,0 +1 @@
+share2if.err line 6: A shared network can't be connected to two interfaces.
diff --git a/keama/tests/shareempty.err b/keama/tests/shareempty.err
new file mode 100644
index 00000000..2e9e3a12
--- /dev/null
+++ b/keama/tests/shareempty.err
@@ -0,0 +1,6 @@
+# bad (no subnet) shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ interface "foo";
+}
diff --git a/keama/tests/shareempty.msg b/keama/tests/shareempty.msg
new file mode 100644
index 00000000..fb74cc62
--- /dev/null
+++ b/keama/tests/shareempty.msg
@@ -0,0 +1 @@
+shareempty.err line 6: empty shared-network decl
diff --git a/keama/tests/shareinclass.err b/keama/tests/shareinclass.err
new file mode 100644
index 00000000..d68e724e
--- /dev/null
+++ b/keama/tests/shareinclass.err
@@ -0,0 +1,11 @@
+# shared-network declaration inside class declaration config
+
+# class declaration
+class "foobar" {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ # can't put a shared-network declaration here
+ shared-network "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/shareinclass.msg b/keama/tests/shareinclass.msg
new file mode 100644
index 00000000..656898c2
--- /dev/null
+++ b/keama/tests/shareinclass.msg
@@ -0,0 +1 @@
+shareinclass.err line 5: hardware address parameter not allowed here.
diff --git a/keama/tests/shareinhost.err b/keama/tests/shareinhost.err
new file mode 100644
index 00000000..93b049c7
--- /dev/null
+++ b/keama/tests/shareinhost.err
@@ -0,0 +1,11 @@
+# shared-network declaration inside host declaration config
+
+# host declaration
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ # can't put a shared-network declaration here
+ shared-network "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/shareinhost.msg b/keama/tests/shareinhost.msg
new file mode 100644
index 00000000..22da166d
--- /dev/null
+++ b/keama/tests/shareinhost.msg
@@ -0,0 +1 @@
+shareinhost.err line 7: shared-network parameters not allowed here.
diff --git a/keama/tests/shareinshare.err b/keama/tests/shareinshare.err
new file mode 100644
index 00000000..68750613
--- /dev/null
+++ b/keama/tests/shareinshare.err
@@ -0,0 +1,10 @@
+# shared-network declaration inside shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ # can't put another shared-network declaration here
+ shared-network "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/shareinshare.msg b/keama/tests/shareinshare.msg
new file mode 100644
index 00000000..a2579e8e
--- /dev/null
+++ b/keama/tests/shareinshare.msg
@@ -0,0 +1 @@
+shareinshare.err line 6: shared-network parameters not allowed here.
diff --git a/keama/tests/shareinsubnet4.err4 b/keama/tests/shareinsubnet4.err4
new file mode 100644
index 00000000..ecc0d080
--- /dev/null
+++ b/keama/tests/shareinsubnet4.err4
@@ -0,0 +1,10 @@
+# shared-network declaration inside DHCPv4 subnet declaration config
+
+# subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # can't put a shared-network declaration here
+ shared-network "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/shareinsubnet4.msg b/keama/tests/shareinsubnet4.msg
new file mode 100644
index 00000000..c332da87
--- /dev/null
+++ b/keama/tests/shareinsubnet4.msg
@@ -0,0 +1 @@
+shareinsubnet4.err4 line 6: shared-network parameters not allowed here.
diff --git a/keama/tests/shareinsubnet6.err6 b/keama/tests/shareinsubnet6.err6
new file mode 100644
index 00000000..fec6f6d2
--- /dev/null
+++ b/keama/tests/shareinsubnet6.err6
@@ -0,0 +1,10 @@
+# shared-network declaration inside DHCPv6 subnet declaration config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # can't put a shared-network declaration here
+ shared-network "illegal" {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/shareinsubnet6.msg b/keama/tests/shareinsubnet6.msg
new file mode 100644
index 00000000..640e5323
--- /dev/null
+++ b/keama/tests/shareinsubnet6.msg
@@ -0,0 +1 @@
+shareinsubnet6.err6 line 6: shared-network parameters not allowed here.
diff --git a/keama/tests/sharenoname.err b/keama/tests/sharenoname.err
new file mode 100644
index 00000000..854a1728
--- /dev/null
+++ b/keama/tests/sharenoname.err
@@ -0,0 +1,6 @@
+# bad (no name)shared-network declaration config
+
+# shared-network declaration
+shared-network {
+
+}
diff --git a/keama/tests/sharenoname.msg b/keama/tests/sharenoname.msg
new file mode 100644
index 00000000..ad12f0d7
--- /dev/null
+++ b/keama/tests/sharenoname.msg
@@ -0,0 +1 @@
+sharenoname.err line 4: expecting a name for shared-network
diff --git a/keama/tests/shareone4.in4 b/keama/tests/shareone4.in4
new file mode 100644
index 00000000..ef0e15ac
--- /dev/null
+++ b/keama/tests/shareone4.in4
@@ -0,0 +1,22 @@
+# DHCPv4 one-subnet shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ # interface
+ interface "en0";
+ # option
+ option domain-search "example.com", "example.org";
+ # parameter
+ default-lease-time 1800;
+ # subnet declaration
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ # redefined parameter
+ default-lease-time 3600;
+ # another option
+ option ip-forwarding true;
+ }
+ # pool (must be after the subnet)
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+}
diff --git a/keama/tests/shareone4.out b/keama/tests/shareone4.out
new file mode 100644
index 00000000..56f5cbb7
--- /dev/null
+++ b/keama/tests/shareone4.out
@@ -0,0 +1,44 @@
+{
+ # DHCPv4 one-subnet shared-network declaration config
+ # shared-network declaration
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ "subnet4": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "valid-lifetime": 3600,
+ "option-data": [
+ # another option
+ {
+ "space": "dhcp4",
+ "name": "ip-forwarding",
+ "code": 19,
+ "data": "true"
+ },
+ # interface
+ # option
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "interface": "en0",
+ "pools": [
+ # pool (must be after the subnet)
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/shareone6.in6 b/keama/tests/shareone6.in6
new file mode 100644
index 00000000..11b03cb0
--- /dev/null
+++ b/keama/tests/shareone6.in6
@@ -0,0 +1,19 @@
+# DHCPv6 one-subnet shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ # interface
+ interface "en0";
+ # option
+ option dhcp6.domain-search "example.com", "example.org";
+ # parameter
+ default-lease-time 1800;
+ # subnet declaration
+ subnet6 2001::/64 {
+ # redefined parameter
+ default-lease-time 3600;
+ # pool
+ range6 2001::1000 2001::2000;
+ }
+ # tried another pool here but DHCPv6 pools are allowed only in subnets
+}
diff --git a/keama/tests/shareone6.out b/keama/tests/shareone6.out
new file mode 100644
index 00000000..6f8389c3
--- /dev/null
+++ b/keama/tests/shareone6.out
@@ -0,0 +1,37 @@
+{
+ # DHCPv6 one-subnet shared-network declaration config
+ # shared-network declaration
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ "subnet6": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "valid-lifetime": 3600,
+ "pools": [
+ {
+ # pool
+ "pool": "2001::1000 - 2001::2000"
+ }
+ ],
+ "interface": "en0",
+ "option-data": [
+ # interface
+ # option
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/sharepools4.in4 b/keama/tests/sharepools4.in4
new file mode 100644
index 00000000..b42a8481
--- /dev/null
+++ b/keama/tests/sharepools4.in4
@@ -0,0 +1,27 @@
+# DHCPv4 two pools and subnets shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ # interface
+ interface "en0";
+ # option
+ option domain-search "example.com", "example.org";
+ # parameter
+ default-lease-time 1800;
+ # subnet declaration
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ # redefined parameter
+ default-lease-time 3600;
+ # another option
+ option ip-forwarding true;
+ }
+ # second subnet declaration
+ subnet 10.10.10.0 netmask 255.255.255.224 { }
+ # pools at shared-network level
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+ pool {
+ range 10.10.10.5 10.10.10.10;
+ }
+}
diff --git a/keama/tests/sharepools4.out b/keama/tests/sharepools4.out
new file mode 100644
index 00000000..087ec5c8
--- /dev/null
+++ b/keama/tests/sharepools4.out
@@ -0,0 +1,63 @@
+{
+ # DHCPv4 two pools and subnets shared-network declaration config
+ # shared-network declaration
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ /// Kea shared-networks are different, cf Kea #236
+ "shared-networks": [
+ {
+ "name": "foobar",
+ "subnet4": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "valid-lifetime": 3600,
+ "option-data": [
+ # another option
+ {
+ "space": "dhcp4",
+ "name": "ip-forwarding",
+ "code": 19,
+ "data": "true"
+ }
+ ],
+ "pools": [
+ # pools at shared-network level
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ },
+ # second subnet declaration
+ {
+ "id": 2,
+ "subnet": "10.10.10.0/27",
+ "pools": [
+ {
+ "pool": "10.10.10.5 - 10.10.10.10"
+ }
+ ]
+ }
+ ],
+ "interface": "en0",
+ "option-data": [
+ # interface
+ # option
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800
+ }
+ ]
+ }
+}
diff --git a/keama/tests/sharetwo4.in4 b/keama/tests/sharetwo4.in4
new file mode 100644
index 00000000..fa9126fe
--- /dev/null
+++ b/keama/tests/sharetwo4.in4
@@ -0,0 +1,29 @@
+# DHCPv4 two subnets in shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ # interface
+ interface "en0";
+ # option
+ option domain-search "example.com", "example.org";
+ # parameter
+ default-lease-time 1800;
+ # subnet declaration
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ # redefined parameter
+ default-lease-time 3600;
+ # another option
+ option ip-forwarding true;
+ # pool inside the subnet
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+ }
+ # second subnet declaration
+ subnet 10.10.10.0 netmask 255.255.255.224 {
+ # pool inside the subnet
+ pool {
+ range 10.10.10.5 10.10.10.10;
+ }
+ }
+}
diff --git a/keama/tests/sharetwo4.out b/keama/tests/sharetwo4.out
new file mode 100644
index 00000000..71f7832e
--- /dev/null
+++ b/keama/tests/sharetwo4.out
@@ -0,0 +1,64 @@
+{
+ # DHCPv4 two subnets in shared-network declaration config
+ # shared-network declaration
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ /// Kea shared-networks are different, cf Kea #236
+ "shared-networks": [
+ {
+ "name": "foobar",
+ "subnet4": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "valid-lifetime": 3600,
+ "option-data": [
+ # another option
+ {
+ "space": "dhcp4",
+ "name": "ip-forwarding",
+ "code": 19,
+ "data": "true"
+ }
+ ],
+ "pools": [
+ # pool inside the subnet
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ]
+ },
+ # second subnet declaration
+ {
+ "id": 2,
+ "subnet": "10.10.10.0/27",
+ "pools": [
+ # pool inside the subnet
+ {
+ "pool": "10.10.10.5 - 10.10.10.10"
+ }
+ ]
+ }
+ ],
+ "interface": "en0",
+ "option-data": [
+ # interface
+ # option
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800
+ }
+ ]
+ }
+}
diff --git a/keama/tests/sharetwo6.in6 b/keama/tests/sharetwo6.in6
new file mode 100644
index 00000000..3c75aad5
--- /dev/null
+++ b/keama/tests/sharetwo6.in6
@@ -0,0 +1,24 @@
+# DHCPv6 two subnets in shared-network declaration config
+
+# shared-network declaration
+shared-network "foobar" {
+ # interface
+ interface "en0";
+ # option
+ option dhcp6.domain-search "example.com", "example.org";
+ # parameter
+ default-lease-time 1800;
+ # subnet declaration
+ subnet6 2001::/64 {
+ # redefined parameter
+ default-lease-time 3600;
+ # pool
+ range6 2001::1000 2001::2000;
+ }
+ # second subnet declaration
+ subnet6 2002::/64 {
+ pool6 {
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: /64;
+ }
+ }
+}
diff --git a/keama/tests/sharetwo6.out b/keama/tests/sharetwo6.out
new file mode 100644
index 00000000..23df4e7c
--- /dev/null
+++ b/keama/tests/sharetwo6.out
@@ -0,0 +1,56 @@
+{
+ # DHCPv6 two subnets in shared-network declaration config
+ # shared-network declaration
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ },
+ /// Kea shared-networks are different, cf Kea #236
+ "shared-networks": [
+ {
+ "name": "foobar",
+ "subnet6": [
+ # subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "valid-lifetime": 3600,
+ "pools": [
+ {
+ # pool
+ "pool": "2001::1000 - 2001::2000"
+ }
+ ]
+ },
+ # second subnet declaration
+ {
+ "id": 2,
+ "subnet": "2002::/64",
+ "pd-pools": [
+ {
+ "prefix": "2001:0:0:10::",
+ "delegated-len": 64,
+ "prefix-len": 60
+ }
+ ]
+ }
+ ],
+ "interface": "en0",
+ "option-data": [
+ # interface
+ # option
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800
+ }
+ ]
+ }
+}
diff --git a/keama/tests/sname4.notyet b/keama/tests/sname4.notyet
new file mode 100644
index 00000000..172b2acf
--- /dev/null
+++ b/keama/tests/sname4.notyet
@@ -0,0 +1,20 @@
+# server-name data expression
+# Kea has no server-name extractor in libeval
+
+# authoritative is mandatory
+authoritative;
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# by filename superclass
+class "byfn" {
+ match server-name;
+}
+
+subclass "byfn" "foobar" {
+ option domain-name-servers 10.5.5.1;
+}
+
+# raw
+option bootfile-name = concat(server-name, "/", option host-name);
diff --git a/keama/tests/spawning6.in6 b/keama/tests/spawning6.in6
new file mode 100644
index 00000000..0c33afe1
--- /dev/null
+++ b/keama/tests/spawning6.in6
@@ -0,0 +1,11 @@
+# spawning declaration config
+
+# options
+option dhcp6.mysystem code 1250 = text;
+option dhcp6.myversion code 1251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ spawn with option dhcp6.mysystem;
+ option dhcp6.myversion 1;
+}
diff --git a/keama/tests/spawning6.out b/keama/tests/spawning6.out
new file mode 100644
index 00000000..ce556d80
--- /dev/null
+++ b/keama/tests/spawning6.out
@@ -0,0 +1,37 @@
+{
+ # spawning declaration config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "mysystem",
+ "code": 1250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// Spawning classes are not supported by Kea
+ /// Reference Kea #248
+ /// spawn with: option dhcp6.mysystem
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subclass4.in4 b/keama/tests/subclass4.in4
new file mode 100644
index 00000000..bdae6632
--- /dev/null
+++ b/keama/tests/subclass4.in4
@@ -0,0 +1,23 @@
+# subclass declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ match option mysystem;
+ option myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" "version1";
+
+# option setting subclass declaration
+subclass "foobar" "version2" { option myversion 2; }
+
+# complex subclass declaration
+subclass "foobar" "version3" {
+ option myversion 3;
+ next-server 192.168.0.1;
+}
diff --git a/keama/tests/subclass4.out b/keama/tests/subclass4.out
new file mode 100644
index 00000000..e155d0de
--- /dev/null
+++ b/keama/tests/subclass4.out
@@ -0,0 +1,84 @@
+{
+ # subclass declaration config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "mysystem",
+ "code": 250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp.mysystem
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 'version1'
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version1'
+ "test": "option[250].hex == 'version1'"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'version2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "2"
+ }
+ ],
+ /// from: match option dhcp.mysystem
+ /// data: 'version2'
+ "test": "option[250].hex == 'version2'"
+ },
+ # complex subclass declaration
+ /// subclass selector 'version3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "3"
+ }
+ ],
+ "next-server": "192.168.0.1",
+ /// from: match option dhcp.mysystem
+ /// data: 'version3'
+ "test": "option[250].hex == 'version3'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subclass6.in6 b/keama/tests/subclass6.in6
new file mode 100644
index 00000000..41408180
--- /dev/null
+++ b/keama/tests/subclass6.in6
@@ -0,0 +1,23 @@
+# subclass declaration config
+
+# options
+option dhcp6.mysystem code 1250 = text;
+option dhcp6.myversion code 1251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ match option dhcp6.mysystem;
+ option dhcp6.myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" "version1";
+
+# option setting subclass declaration
+subclass "foobar" "version2" { option dhcp6.myversion 2; }
+
+# complex subclass declaration
+subclass "foobar" "version3" {
+ option dhcp6.myversion 3;
+ option dhcp6.rapid-commit;
+}
diff --git a/keama/tests/subclass6.out b/keama/tests/subclass6.out
new file mode 100644
index 00000000..3828d58d
--- /dev/null
+++ b/keama/tests/subclass6.out
@@ -0,0 +1,89 @@
+{
+ # subclass declaration config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "mysystem",
+ "code": 1250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp6.mysystem
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 'version1'
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version1'
+ "test": "option[1250].hex == 'version1'"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'version2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "2"
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version2'
+ "test": "option[1250].hex == 'version2'"
+ },
+ # complex subclass declaration
+ /// subclass selector 'version3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "3"
+ },
+ {
+ "space": "dhcp6",
+ "name": "rapid-commit",
+ "code": 14,
+ "data": ""
+ }
+ ],
+ /// from: match option dhcp6.mysystem
+ /// data: 'version3'
+ "test": "option[1250].hex == 'version3'"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subclassbinsel4.in4 b/keama/tests/subclassbinsel4.in4
new file mode 100644
index 00000000..95d5aa54
--- /dev/null
+++ b/keama/tests/subclassbinsel4.in4
@@ -0,0 +1,16 @@
+# subclass declaration config
+
+# options
+option myversion code 251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ match hardware;
+ option myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" 1:00:07:0E:36:48:19;
+
+# option setting subclass declaration
+subclass "foobar" 1:00:0B:FD:32:E6:FA { option myversion 2; }
diff --git a/keama/tests/subclassbinsel4.out b/keama/tests/subclassbinsel4.out
new file mode 100644
index 00000000..589afcb8
--- /dev/null
+++ b/keama/tests/subclassbinsel4.out
@@ -0,0 +1,61 @@
+{
+ # subclass declaration config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: hardware
+ {
+ "name": "foobar",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 0x0x0100070e364819
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "1"
+ }
+ ],
+ /// from: match hardware
+ /// data: 0x0100070e364819
+ "test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x0100070e364819"
+ },
+ # option setting subclass declaration
+ /// subclass selector 0x0x01000bfd32e6fa
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "data": "2"
+ }
+ ],
+ /// from: match hardware
+ /// data: 0x01000bfd32e6fa
+ "test": "concat(substring(pkt4.htype,-1,all), pkt4.mac) == 0x01000bfd32e6fa"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subclassbinsel6.in6 b/keama/tests/subclassbinsel6.in6
new file mode 100644
index 00000000..0a7e36d7
--- /dev/null
+++ b/keama/tests/subclassbinsel6.in6
@@ -0,0 +1,25 @@
+# subclass declaration config
+
+# options
+option dhcp6.hardware code 1250 = string;
+option dhcp6.myversion code 1251 = unsigned integer 16;
+
+# superclass declaration
+class "foobar" {
+ # no harware in DHCPv6
+ match option dhcp6.hardware;
+ option dhcp6.myversion 1;
+}
+
+# simple subclass declaration
+subclass "foobar" 1:00:07:0E:36:48:19;
+
+# option setting subclass declaration
+subclass "foobar" 1:00:0B:FD:32:E6:FA { option dhcp6.myversion 2; }
+
+# complex subclass declaration
+subclass "foobar" 1:00:02:B3:88:C5:27 {
+ option dhcp6.myversion 3;
+ lease limit 20;
+}
+
diff --git a/keama/tests/subclassbinsel6.out b/keama/tests/subclassbinsel6.out
new file mode 100644
index 00000000..42c96bb2
--- /dev/null
+++ b/keama/tests/subclassbinsel6.out
@@ -0,0 +1,88 @@
+{
+ # subclass declaration config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "hardware",
+ "code": 1250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "type": "uint16"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp6.hardware
+ {
+ "name": "foobar",
+ "option-data": [
+ # no harware in DHCPv6
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ]
+ },
+ # simple subclass declaration
+ /// subclass selector 0x0x0100070e364819
+ {
+ "name": "sub#foobar#0",
+ "option-data": [
+ # no harware in DHCPv6
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "1"
+ }
+ ],
+ /// from: match option dhcp6.hardware
+ /// data: 0x0100070e364819
+ "test": "option[1250].hex == 0x0100070e364819"
+ },
+ # option setting subclass declaration
+ /// subclass selector 0x0x01000bfd32e6fa
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "2"
+ }
+ ],
+ /// from: match option dhcp6.hardware
+ /// data: 0x01000bfd32e6fa
+ "test": "option[1250].hex == 0x01000bfd32e6fa"
+ },
+ # complex subclass declaration
+ /// subclass selector 0x0x010002b388c527
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "data": "3"
+ }
+ ],
+// /// Per-class limit is not supported by Kea
+// /// Reference Kea #237
+// "lease-limit": 20,
+ /// from: match option dhcp6.hardware
+ /// data: 0x010002b388c527
+ "test": "option[1250].hex == 0x010002b388c527"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subclassguard4.in4 b/keama/tests/subclassguard4.in4
new file mode 100644
index 00000000..9a22d356
--- /dev/null
+++ b/keama/tests/subclassguard4.in4
@@ -0,0 +1,24 @@
+# subclass with guard declaration config
+
+# options
+option mysystem code 250 = text;
+option myversion code 251 = unsigned integer 16;
+option mydescr code 252 = text;
+
+# superclass declaration
+class "foobar" {
+ match if option myversion = 0:1;
+ match option mysystem;
+}
+
+# simple subclass declaration
+subclass "foobar" "system1";
+
+# option setting subclass declaration
+subclass "foobar" "system2" { option mydescr "1.2"; }
+
+# complex subclass declaration
+subclass "foobar" "system3" {
+ option mydescr "1.3";
+ next-server 192.168.0.1;
+}
diff --git a/keama/tests/subclassguard4.out b/keama/tests/subclassguard4.out
new file mode 100644
index 00000000..910b5b93
--- /dev/null
+++ b/keama/tests/subclassguard4.out
@@ -0,0 +1,79 @@
+{
+ # subclass with guard declaration config
+ # options
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "dhcp4",
+ "name": "mysystem",
+ "code": 250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp4",
+ "name": "myversion",
+ "code": 251,
+ "type": "uint16"
+ },
+ {
+ "space": "dhcp4",
+ "name": "mydescr",
+ "code": 252,
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp.mysystem
+ {
+ "name": "foobar",
+ /// from: match if (option dhcp.myversion) = 0x0001
+ "test": "option[251].hex == 0x0001"
+ },
+ # simple subclass declaration
+ /// subclass selector 'system1'
+ {
+ "name": "sub#foobar#0",
+ /// from: match-if (option dhcp.myversion) = 0x0001
+ /// match: option dhcp.mysystem
+ /// data: 'system1'
+ "test": "(option[251].hex == 0x0001) and (option[250].hex == 'system1')"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'system2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "mydescr",
+ "code": 252,
+ "data": "1.2"
+ }
+ ],
+ /// from: match-if (option dhcp.myversion) = 0x0001
+ /// match: option dhcp.mysystem
+ /// data: 'system2'
+ "test": "(option[251].hex == 0x0001) and (option[250].hex == 'system2')"
+ },
+ # complex subclass declaration
+ /// subclass selector 'system3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "mydescr",
+ "code": 252,
+ "data": "1.3"
+ }
+ ],
+ "next-server": "192.168.0.1",
+ /// from: match-if (option dhcp.myversion) = 0x0001
+ /// match: option dhcp.mysystem
+ /// data: 'system3'
+ "test": "(option[251].hex == 0x0001) and (option[250].hex == 'system3')"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subclassguard6.in6 b/keama/tests/subclassguard6.in6
new file mode 100644
index 00000000..5c2119a6
--- /dev/null
+++ b/keama/tests/subclassguard6.in6
@@ -0,0 +1,24 @@
+# subclass with guard declaration config
+
+# options
+option dhcp6.mysystem code 1250 = text;
+option dhcp6.myversion code 1251 = unsigned integer 16;
+option dhcp6.mydescr code 1252 = text;
+
+# superclass declaration
+class "foobar" {
+ match if option dhcp6.myversion = 0:1;
+ match option dhcp6.mysystem;
+}
+
+# simple subclass declaration
+subclass "foobar" "system1";
+
+# option setting subclass declaration
+subclass "foobar" "system2" { option dhcp6.mydescr "1.2"; }
+
+# complex subclass declaration
+subclass "foobar" "system3" {
+ option dhcp6.mydescr "1.3";
+ option dhcp6.rapid-commit;
+}
diff --git a/keama/tests/subclassguard6.out b/keama/tests/subclassguard6.out
new file mode 100644
index 00000000..26a9d4dc
--- /dev/null
+++ b/keama/tests/subclassguard6.out
@@ -0,0 +1,84 @@
+{
+ # subclass with guard declaration config
+ # options
+ "Dhcp6": {
+ "option-def": [
+ {
+ "space": "dhcp6",
+ "name": "mysystem",
+ "code": 1250,
+ "type": "string"
+ },
+ {
+ "space": "dhcp6",
+ "name": "myversion",
+ "code": 1251,
+ "type": "uint16"
+ },
+ {
+ "space": "dhcp6",
+ "name": "mydescr",
+ "code": 1252,
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ # superclass declaration
+ /// match: option dhcp6.mysystem
+ {
+ "name": "foobar",
+ /// from: match if (option dhcp6.myversion) = 0x0001
+ "test": "option[1251].hex == 0x0001"
+ },
+ # simple subclass declaration
+ /// subclass selector 'system1'
+ {
+ "name": "sub#foobar#0",
+ /// from: match-if (option dhcp6.myversion) = 0x0001
+ /// match: option dhcp6.mysystem
+ /// data: 'system1'
+ "test": "(option[1251].hex == 0x0001) and (option[1250].hex == 'system1')"
+ },
+ # option setting subclass declaration
+ /// subclass selector 'system2'
+ {
+ "name": "sub#foobar#1",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "mydescr",
+ "code": 1252,
+ "data": "1.2"
+ }
+ ],
+ /// from: match-if (option dhcp6.myversion) = 0x0001
+ /// match: option dhcp6.mysystem
+ /// data: 'system2'
+ "test": "(option[1251].hex == 0x0001) and (option[1250].hex == 'system2')"
+ },
+ # complex subclass declaration
+ /// subclass selector 'system3'
+ {
+ "name": "sub#foobar#2",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "mydescr",
+ "code": 1252,
+ "data": "1.3"
+ },
+ {
+ "space": "dhcp6",
+ "name": "rapid-commit",
+ "code": 14,
+ "data": ""
+ }
+ ],
+ /// from: match-if (option dhcp6.myversion) = 0x0001
+ /// match: option dhcp6.mysystem
+ /// data: 'system3'
+ "test": "(option[1251].hex == 0x0001) and (option[1250].hex == 'system3')"
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subnet4.in4 b/keama/tests/subnet4.in4
new file mode 100644
index 00000000..9c9247a4
--- /dev/null
+++ b/keama/tests/subnet4.in4
@@ -0,0 +1,16 @@
+# DHCPv4 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1800;
+
+# DHCPv4 subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # at least one pool is required
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+ option domain-search "example.com", "example.org";
+ default-lease-time 3600;
+ ignore-client-uids false;
+}
+
diff --git a/keama/tests/subnet4.out b/keama/tests/subnet4.out
new file mode 100644
index 00000000..2b50c272
--- /dev/null
+++ b/keama/tests/subnet4.out
@@ -0,0 +1,33 @@
+{
+ # DHCPv4 subnet declaration config
+ # parameter which will be changed in subnet
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "subnet4": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # at least one pool is required
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 3600,
+ "match-client-id": true
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subnet42if.err4 b/keama/tests/subnet42if.err4
new file mode 100644
index 00000000..b65c5d72
--- /dev/null
+++ b/keama/tests/subnet42if.err4
@@ -0,0 +1,8 @@
+# bad (2 interfaces) DHCPv4 subnet declaration config
+
+# DHCPv4 subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ interface "foo";
+ interface "bar";
+}
+
diff --git a/keama/tests/subnet42if.msg b/keama/tests/subnet42if.msg
new file mode 100644
index 00000000..22cd15dc
--- /dev/null
+++ b/keama/tests/subnet42if.msg
@@ -0,0 +1 @@
+subnet42if.err4 line 6: A subnet can't be connected to two interfaces.
diff --git a/keama/tests/subnet4auth.in4 b/keama/tests/subnet4auth.in4
new file mode 100644
index 00000000..dd7750d7
--- /dev/null
+++ b/keama/tests/subnet4auth.in4
@@ -0,0 +1,18 @@
+# DHCPv4 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1800;
+
+# DHCPv4 subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ # at least one pool is required
+ pool {
+ range 10.5.5.5 10.5.5.10;
+ }
+ # authorize here
+ authoritative;
+ option domain-search "example.com", "example.org";
+ default-lease-time 3600;
+ ignore-client-uids false;
+}
+
diff --git a/keama/tests/subnet4auth.out b/keama/tests/subnet4auth.out
new file mode 100644
index 00000000..ad605779
--- /dev/null
+++ b/keama/tests/subnet4auth.out
@@ -0,0 +1,35 @@
+{
+ # DHCPv4 subnet declaration config
+ # parameter which will be changed in subnet
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "subnet4": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "10.5.5.0/27",
+ "pools": [
+ # at least one pool is required
+ {
+ "pool": "10.5.5.5 - 10.5.5.10"
+ }
+ ],
+ # authorize here
+ "authoritative": true,
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 3600,
+ "match-client-id": true
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subnet4badmask.err4 b/keama/tests/subnet4badmask.err4
new file mode 100644
index 00000000..979f5cbb
--- /dev/null
+++ b/keama/tests/subnet4badmask.err4
@@ -0,0 +1,7 @@
+# bad (bad netmask) DHCPv4 subnet declaration config
+
+# DHCPv4 subnet declaration
+subnet 10.5.5.0 netmask 255.255.255.96 {
+ default-lease-time 1800;
+}
+
diff --git a/keama/tests/subnet4badmask.msg b/keama/tests/subnet4badmask.msg
new file mode 100644
index 00000000..860aec2d
--- /dev/null
+++ b/keama/tests/subnet4badmask.msg
@@ -0,0 +1 @@
+subnet4badmask.err4 line 4: can't get a prefix from 10.5.5.0 mask 255.255.255.96
diff --git a/keama/tests/subnet4inclass.err4 b/keama/tests/subnet4inclass.err4
new file mode 100644
index 00000000..51ede4b0
--- /dev/null
+++ b/keama/tests/subnet4inclass.err4
@@ -0,0 +1,10 @@
+# DHCPv4 subnet declaration inside class declaration config
+
+# class declaration
+class "foobar" {
+ # can't put a DHCPv4 subnet declaration here
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/subnet4inclass.msg b/keama/tests/subnet4inclass.msg
new file mode 100644
index 00000000..eabc9e47
--- /dev/null
+++ b/keama/tests/subnet4inclass.msg
@@ -0,0 +1 @@
+subnet4inclass.err4 line 6: subnet declarations not allowed here.
diff --git a/keama/tests/subnet4inhost.err4 b/keama/tests/subnet4inhost.err4
new file mode 100644
index 00000000..266e6dc3
--- /dev/null
+++ b/keama/tests/subnet4inhost.err4
@@ -0,0 +1,11 @@
+# DHCPv4 subnet declaration inside host declaration config
+
+# host declaration
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ # can't put a DHCPv4 subnet declaration here
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/subnet4inhost.msg b/keama/tests/subnet4inhost.msg
new file mode 100644
index 00000000..49e7d18d
--- /dev/null
+++ b/keama/tests/subnet4inhost.msg
@@ -0,0 +1 @@
+subnet4inhost.err4 line 7: subnet declarations not allowed here.
diff --git a/keama/tests/subnet4nomask.err4 b/keama/tests/subnet4nomask.err4
new file mode 100644
index 00000000..42266d03
--- /dev/null
+++ b/keama/tests/subnet4nomask.err4
@@ -0,0 +1,7 @@
+# bad (no netmask) DHCPv4 subnet declaration config
+
+# DHCPv4 subnet declaration
+subnet 10.5.5.0/27 {
+ default-lease-time 1800;
+}
+
diff --git a/keama/tests/subnet4nomask.msg b/keama/tests/subnet4nomask.msg
new file mode 100644
index 00000000..2d786c10
--- /dev/null
+++ b/keama/tests/subnet4nomask.msg
@@ -0,0 +1 @@
+subnet4nomask.err4 line 4: Expecting netmask
diff --git a/keama/tests/subnet6.in6 b/keama/tests/subnet6.in6
new file mode 100644
index 00000000..ec834fd3
--- /dev/null
+++ b/keama/tests/subnet6.in6
@@ -0,0 +1,18 @@
+# DHCPv6 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1800;
+
+# DHCPv4 subnet declaration
+subnet6 2001::/64 {
+ # at least one pool is required
+ pool6 {
+ range6 2001::100 2001::200;
+ }
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 3600;
+ pool6 {
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: /64;
+ }
+}
+
diff --git a/keama/tests/subnet6.out b/keama/tests/subnet6.out
new file mode 100644
index 00000000..f3535931
--- /dev/null
+++ b/keama/tests/subnet6.out
@@ -0,0 +1,39 @@
+{
+ # DHCPv6 subnet declaration config
+ # parameter which will be changed in subnet
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "subnet6": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ # at least one pool is required
+ {
+ "pool": "2001::100 - 2001::200"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 3600,
+ "pd-pools": [
+ {
+ "prefix": "2001:0:0:10::",
+ "delegated-len": 64,
+ "prefix-len": 60
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subnet62if.err6 b/keama/tests/subnet62if.err6
new file mode 100644
index 00000000..37fdc3f9
--- /dev/null
+++ b/keama/tests/subnet62if.err6
@@ -0,0 +1,8 @@
+# bad (2 interfaces) DHCPv6 subnet declaration config
+
+# DHCPv6 subnet declaration
+subnet6 2001::/64 {
+ interface "foo";
+ interface "bar";
+}
+
diff --git a/keama/tests/subnet62if.msg b/keama/tests/subnet62if.msg
new file mode 100644
index 00000000..66f6b900
--- /dev/null
+++ b/keama/tests/subnet62if.msg
@@ -0,0 +1 @@
+subnet62if.err6 line 6: A subnet can't be connected to two interfaces.
diff --git a/keama/tests/subnet6auth.in6 b/keama/tests/subnet6auth.in6
new file mode 100644
index 00000000..a41ff6f4
--- /dev/null
+++ b/keama/tests/subnet6auth.in6
@@ -0,0 +1,21 @@
+# DHCPv6 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1800;
+
+# DHCPv4 subnet declaration
+subnet6 2001::/64 {
+ # at least one pool is required
+ pool6 {
+ range6 2001::100 2001::200;
+ }
+ # authorize here
+ authoritative;
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 3600;
+ interface "en0";
+ pool6 {
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: /64;
+ }
+}
+
diff --git a/keama/tests/subnet6auth.out b/keama/tests/subnet6auth.out
new file mode 100644
index 00000000..aa79d1df
--- /dev/null
+++ b/keama/tests/subnet6auth.out
@@ -0,0 +1,44 @@
+{
+ # DHCPv6 subnet declaration config
+ # parameter which will be changed in subnet
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "subnet6": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pools": [
+ # at least one pool is required
+ {
+ "pool": "2001::100 - 2001::200"
+ }
+ ],
+ "option-data": [
+ # authorize here
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 3600,
+ "interface": "en0",
+ "pd-pools": [
+ {
+ "prefix": "2001:0:0:10::",
+ "delegated-len": 64,
+ "prefix-len": 60
+ }
+ ]
+ }
+ ],
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ }
+ }
+}
diff --git a/keama/tests/subnet6inclass.err6 b/keama/tests/subnet6inclass.err6
new file mode 100644
index 00000000..52368cd8
--- /dev/null
+++ b/keama/tests/subnet6inclass.err6
@@ -0,0 +1,10 @@
+# DHCPv6 subnet declaration inside class declaration config
+
+# class declaration
+class "foobar" {
+ # can't put a DHCPv6 subnet declaration here
+ subnet6 2001::/64 {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/subnet6inclass.msg b/keama/tests/subnet6inclass.msg
new file mode 100644
index 00000000..e7360029
--- /dev/null
+++ b/keama/tests/subnet6inclass.msg
@@ -0,0 +1 @@
+subnet6inclass.err6 line 6: subnet declarations not allowed here.
diff --git a/keama/tests/subnet6inhost.err6 b/keama/tests/subnet6inhost.err6
new file mode 100644
index 00000000..914decd6
--- /dev/null
+++ b/keama/tests/subnet6inhost.err6
@@ -0,0 +1,11 @@
+# DHCPv6 subnet declaration inside host declaration config
+
+# host declaration
+host foobar {
+ hardware ethernet 00:0B:FD:32:E6:FA;
+ # can't put a DHCPv6 subnet declaration here
+ subnet6 2001::/64 {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/subnet6inhost.msg b/keama/tests/subnet6inhost.msg
new file mode 100644
index 00000000..bd49b89f
--- /dev/null
+++ b/keama/tests/subnet6inhost.msg
@@ -0,0 +1 @@
+subnet6inhost.err6 line 7: subnet declarations not allowed here.
diff --git a/keama/tests/subnet6multi.in6 b/keama/tests/subnet6multi.in6
new file mode 100644
index 00000000..a9974ba0
--- /dev/null
+++ b/keama/tests/subnet6multi.in6
@@ -0,0 +1,19 @@
+# DHCPv6 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1800;
+
+# DHCPv4 subnet declaration
+subnet6 2001::/64 {
+ # at least one pool is required
+ pool6 {
+ # the pool is shared between addresses and prefixes
+ range6 2001::100 2001::200;
+ range6 2001::1000 2001::2000;
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: /64;
+ prefix6 2001:0:0:80:: 2001:0:0:ff:: /64;
+ }
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 3600;
+}
+
diff --git a/keama/tests/subnet6multi.out b/keama/tests/subnet6multi.out
new file mode 100644
index 00000000..9a4defb6
--- /dev/null
+++ b/keama/tests/subnet6multi.out
@@ -0,0 +1,49 @@
+{
+ # DHCPv6 subnet declaration config
+ # parameter which will be changed in subnet
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "subnet6": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pd-pools": [
+ {
+ "prefix": "2001:0:0:10::",
+ "delegated-len": 64,
+ "prefix-len": 60
+ },
+ # at least one pool is required
+ {
+ "prefix": "2001:0:0:80::",
+ "delegated-len": 64,
+ "prefix-len": 57
+ }
+ ],
+ "pools": [
+ {
+ # the pool is shared between addresses and prefixes
+ "pool": "2001::100 - 2001::200"
+ },
+ # at least one pool is required
+ {
+ "pool": "2001::1000 - 2001::2000"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 3600
+ }
+ ]
+ }
+}
diff --git a/keama/tests/subnet6nolen.err6 b/keama/tests/subnet6nolen.err6
new file mode 100644
index 00000000..1a71ac79
--- /dev/null
+++ b/keama/tests/subnet6nolen.err6
@@ -0,0 +1,7 @@
+# bad (no length) DHCPv6 subnet declaration config
+
+# DHCPv6 subnet declaration
+subnet6 2001::/ {
+ default-lease-time 1800;
+}
+
diff --git a/keama/tests/subnet6nolen.msg b/keama/tests/subnet6nolen.msg
new file mode 100644
index 00000000..cc36f0e1
--- /dev/null
+++ b/keama/tests/subnet6nolen.msg
@@ -0,0 +1 @@
+subnet6nolen.err6 line 4: Expecting a number.
diff --git a/keama/tests/subnet6noslash.err6 b/keama/tests/subnet6noslash.err6
new file mode 100644
index 00000000..2083eab6
--- /dev/null
+++ b/keama/tests/subnet6noslash.err6
@@ -0,0 +1,7 @@
+# bad (no /) DHCPv6 subnet declaration config
+
+# DHCPv6 subnet declaration
+subnet6 2001:: {
+ default-lease-time 1800;
+}
+
diff --git a/keama/tests/subnet6noslash.msg b/keama/tests/subnet6noslash.msg
new file mode 100644
index 00000000..63fa81c6
--- /dev/null
+++ b/keama/tests/subnet6noslash.msg
@@ -0,0 +1 @@
+subnet6noslash.err6 line 4: Expecting a '/'.
diff --git a/keama/tests/subnet6one.in6 b/keama/tests/subnet6one.in6
new file mode 100644
index 00000000..7c0c3a0b
--- /dev/null
+++ b/keama/tests/subnet6one.in6
@@ -0,0 +1,18 @@
+# DHCPv6 subnet declaration config
+
+# parameter which will be changed in subnet
+default-lease-time 1800;
+
+# DHCPv4 subnet declaration
+subnet6 2001::/64 {
+ # at least one pool is required
+ pool6 {
+ # the pool is shared between addresses and prefixes
+ range6 2001::100 2001::200;
+ prefix6 2001:0:0:10:: 2001:0:0:1f:: /64;
+ }
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 3600;
+ interface "en0";
+}
+
diff --git a/keama/tests/subnet6one.out b/keama/tests/subnet6one.out
new file mode 100644
index 00000000..e1388c81
--- /dev/null
+++ b/keama/tests/subnet6one.out
@@ -0,0 +1,45 @@
+{
+ # DHCPv6 subnet declaration config
+ # parameter which will be changed in subnet
+ "Dhcp6": {
+ "valid-lifetime": 1800,
+ "subnet6": [
+ # DHCPv4 subnet declaration
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "pd-pools": [
+ # at least one pool is required
+ {
+ "prefix": "2001:0:0:10::",
+ "delegated-len": 64,
+ "prefix-len": 60
+ }
+ ],
+ "pools": [
+ # at least one pool is required
+ {
+ # the pool is shared between addresses and prefixes
+ "pool": "2001::100 - 2001::200"
+ }
+ ],
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 3600,
+ "interface": "en0"
+ }
+ ],
+ "interfaces-config": {
+ "interfaces": [
+ "en0"
+ ]
+ }
+ }
+}
diff --git a/keama/tests/subnetinsubnet4.err4 b/keama/tests/subnetinsubnet4.err4
new file mode 100644
index 00000000..c326c16d
--- /dev/null
+++ b/keama/tests/subnetinsubnet4.err4
@@ -0,0 +1,10 @@
+# DHCPv4 subnet declaration inside another subnet declaration config
+
+# DHCPv4 subnet declaration
+subnet 10.254.239.32 netmask 255.255.255.224 {
+ # can't put another subnet declaration here
+ subnet 10.5.5.0 netmask 255.255.255.224 {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/subnetinsubnet4.msg b/keama/tests/subnetinsubnet4.msg
new file mode 100644
index 00000000..710cffa7
--- /dev/null
+++ b/keama/tests/subnetinsubnet4.msg
@@ -0,0 +1 @@
+subnetinsubnet4.err4 line 6: subnet declarations not allowed here.
diff --git a/keama/tests/subnetinsubnet6.err6 b/keama/tests/subnetinsubnet6.err6
new file mode 100644
index 00000000..ac9a6097
--- /dev/null
+++ b/keama/tests/subnetinsubnet6.err6
@@ -0,0 +1,10 @@
+# DHCPv6 subnet declaration inside another subnet declaration config
+
+# DHCPv6 subnet declaration
+subnet6 2001:2::/64 {
+ # can't put another subnet declaration here
+ subnet6 2001::/64 {
+ default-lease-time 1800;
+ }
+}
+
diff --git a/keama/tests/subnetinsubnet6.msg b/keama/tests/subnetinsubnet6.msg
new file mode 100644
index 00000000..27f8f3fa
--- /dev/null
+++ b/keama/tests/subnetinsubnet6.msg
@@ -0,0 +1 @@
+subnetinsubnet6.err6 line 6: subnet declarations not allowed here.
diff --git a/keama/tests/substringdx4.in4 b/keama/tests/substringdx4.in4
new file mode 100644
index 00000000..ed0c9d35
--- /dev/null
+++ b/keama/tests/substringdx4.in4
@@ -0,0 +1,21 @@
+# substring data expression
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# use substring in a reductible match
+class "reductible" {
+ match substring(option host-name, 0, 3);
+}
+
+subclass "reductible" "www" { }
+
+# reduce literals too
+class "literal" {
+ match if option host-name = substring("www.example.com", 0, 3);
+}
+
+# raw
+option domain-name = substring(option domain-name, 4, 1000);
+
+
diff --git a/keama/tests/substringdx4.out b/keama/tests/substringdx4.out
new file mode 100644
index 00000000..75865424
--- /dev/null
+++ b/keama/tests/substringdx4.out
@@ -0,0 +1,49 @@
+{
+ # substring data expression
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # use substring in a reductible match
+ /// match: substring(option dhcp.host-name, 0, 3)
+ {
+ "name": "reductible"
+ },
+ /// subclass selector 'www'
+ {
+ "name": "sub#reductible#0",
+ /// from: match substring(option dhcp.host-name, 0, 3)
+ /// data: 'www'
+ "test": "substring(option[12].hex,0,3) == 'www'"
+ },
+ # reduce literals too
+ {
+ "name": "literal",
+ /// from: match if (option dhcp.host-name) = (substring('www.example.com', 0, 3))
+ "test": "option[12].hex == 'www'"
+ }
+ ],
+ "option-data": [
+// # raw
+// {
+// "space": "dhcp4",
+// "name": "domain-name",
+// "code": 15,
+// "csv-format": false,
+// "expression": {
+// "substring": {
+// "expression": {
+// "option": {
+// "universe": "dhcp",
+// "name": "domain-name",
+// "code": 15
+// }
+// },
+// "offset": 4,
+// "length": 1000
+// }
+// }
+// }
+ ]
+ }
+}
diff --git a/keama/tests/suffixdx4.in4 b/keama/tests/suffixdx4.in4
new file mode 100644
index 00000000..fc692d10
--- /dev/null
+++ b/keama/tests/suffixdx4.in4
@@ -0,0 +1,26 @@
+# suffix data expression
+# in fact ISC DHCP suffix can be reduced into Kea substring
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# use suffix in a reductible match
+class "reductible" {
+ match suffix(option host-name, 3);
+}
+
+subclass "reductible" "com" {
+ option domain-search "example.com";
+}
+
+subclass "reductible" "org" {
+ option domain-search "example.org";
+}
+
+# reduce literals too
+class "literal" {
+ match if option domain-name = suffix("www.example.com", 3);
+}
+
+# raw
+option domain-name = suffix(option domain-name, 3);
diff --git a/keama/tests/suffixdx4.out b/keama/tests/suffixdx4.out
new file mode 100644
index 00000000..12f045d6
--- /dev/null
+++ b/keama/tests/suffixdx4.out
@@ -0,0 +1,74 @@
+{
+ # suffix data expression
+ # in fact ISC DHCP suffix can be reduced into Kea substring
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800,
+ "client-classes": [
+ # use suffix in a reductible match
+ /// match: suffix(option dhcp.host-name, 3)
+ {
+ "name": "reductible"
+ },
+ /// subclass selector 'com'
+ {
+ "name": "sub#reductible#0",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.com\"",
+ "data": "example.com"
+ }
+ ],
+ /// from: match suffix(option dhcp.host-name, 3)
+ /// data: 'com'
+ "test": "substring(option[12].hex,-3,all) == 'com'"
+ },
+ /// subclass selector 'org'
+ {
+ "name": "sub#reductible#1",
+ "option-data": [
+ {
+ "space": "dhcp4",
+ "name": "domain-search",
+ "code": 119,
+// "original-data": "\"example.org\"",
+ "data": "example.org"
+ }
+ ],
+ /// from: match suffix(option dhcp.host-name, 3)
+ /// data: 'org'
+ "test": "substring(option[12].hex,-3,all) == 'org'"
+ },
+ # reduce literals too
+ {
+ "name": "literal",
+ /// from: match if (option dhcp.domain-name) = (suffix('www.example.com', 3))
+ "test": "option[15].hex == '.example.com'"
+ }
+ ],
+ "option-data": [
+// # raw
+// {
+// "space": "dhcp4",
+// "name": "domain-name",
+// "code": 15,
+// "csv-format": false,
+// "expression": {
+// "suffix": {
+// "expression": {
+// "option": {
+// "universe": "dhcp",
+// "name": "domain-name",
+// "code": 15
+// }
+// },
+// "length": 3
+// }
+// }
+// }
+ ]
+ }
+}
diff --git a/keama/tests/switchxsc4.in4 b/keama/tests/switchxsc4.in4
new file mode 100644
index 00000000..7ef6f2f3
--- /dev/null
+++ b/keama/tests/switchxsc4.in4
@@ -0,0 +1,18 @@
+# switch executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# a switch
+switch (option user-class) {
+case "accounting":
+ add "acct";
+ send ip-forwarding false;
+ default-lease-time 3600;
+ break;
+case "engineering":
+ allow booting;
+ log (debug, option host-name);
+ set foo = "bar";
+ break;
+}
diff --git a/keama/tests/switchxsc4.out b/keama/tests/switchxsc4.out
new file mode 100644
index 00000000..e45421d3
--- /dev/null
+++ b/keama/tests/switchxsc4.out
@@ -0,0 +1,79 @@
+{
+ # switch executable statement construct
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// # a switch
+// "statement": {
+// "switch": {
+// "condition": {
+// "option": {
+// "universe": "dhcp",
+// "name": "user-class",
+// "code": 77
+// }
+// },
+// "body": [
+// {
+// "case": "accounting"
+// },
+// {
+// "add-class": "acct"
+// },
+// {
+// /// Kea does not support option data set variants (send)
+// "option": {
+// "space": "dhcp4",
+// "name": "ip-forwarding",
+// "code": 19,
+// "data": "false"
+// }
+// },
+// {
+// "config": {
+// "name": "default-lease-time",
+// "code": 1,
+// "value": 3600
+// }
+// },
+// {
+// "break": null
+// },
+// {
+// "case": "engineering"
+// },
+// {
+// "config": {
+// "value": "allow",
+// "name": "allow-booting",
+// "code": 9
+// }
+// },
+// {
+// /// Kea does not support yet log statements
+// /// Reference Kea #234
+// "log": {
+// "priority": "debug",
+// "message": {
+// "option": {
+// "universe": "dhcp",
+// "name": "host-name",
+// "code": 12
+// }
+// }
+// }
+// },
+// {
+// "set": {
+// "name": "foo",
+// "value": "bar"
+// }
+// },
+// {
+// "break": null
+// }
+// ]
+// }
+// }
+ }
+}
diff --git a/keama/tests/switchxsc6.in6 b/keama/tests/switchxsc6.in6
new file mode 100644
index 00000000..a9c74f86
--- /dev/null
+++ b/keama/tests/switchxsc6.in6
@@ -0,0 +1,18 @@
+# switch executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# a switch
+switch (option dhcp6.remote-id) {
+case "accounting":
+ default dhcp6.bootfile-url foobar;
+ default-lease-time 3600;
+ unset foo;
+ break;
+case "engineering":
+ deny declines;
+ log (debug, "hello");
+ define foo (x) { return "world"; }
+ break;
+}
diff --git a/keama/tests/switchxsc6.out b/keama/tests/switchxsc6.out
new file mode 100644
index 00000000..b7fbd06c
--- /dev/null
+++ b/keama/tests/switchxsc6.out
@@ -0,0 +1,82 @@
+{
+ # switch executable statement construct
+ # empty configs are not accepted by Kea
+ "Dhcp6": {
+ "valid-lifetime": 1800
+// # a switch
+// "statement": {
+// "switch": {
+// "condition": {
+// "option": {
+// "universe": "dhcp6",
+// "name": "remote-id",
+// "code": 37
+// }
+// },
+// "body": [
+// {
+// "case": "accounting"
+// },
+// {
+// /// Kea does not support option data set variants (default)
+// "option": {
+// "space": "dhcp6",
+// "name": "bootfile-url",
+// "code": 59,
+// "data": "foobar"
+// }
+// },
+// {
+// "config": {
+// "name": "default-lease-time",
+// "code": 1,
+// "value": 3600
+// }
+// },
+// {
+// "unset": {
+// "name": "foo"
+// }
+// },
+// {
+// "break": null
+// },
+// {
+// "case": "engineering"
+// },
+// {
+// "config": {
+// "value": "deny",
+// "name": "declines",
+// "code": 29
+// }
+// },
+// {
+// /// Kea does not support yet log statements
+// /// Reference Kea #234
+// "log": {
+// "priority": "debug",
+// "message": "hello"
+// }
+// },
+// {
+// "define": {
+// "name": "foo",
+// "function": {
+// "arguments": "x",
+// "body": [
+// {
+// "return": "world"
+// }
+// ]
+// }
+// }
+// },
+// {
+// "break": null
+// }
+// ]
+// }
+// }
+ }
+}
diff --git a/keama/tests/tautology.err b/keama/tests/tautology.err
new file mode 100644
index 00000000..e1f506dd
--- /dev/null
+++ b/keama/tests/tautology.err
@@ -0,0 +1,9 @@
+# bad (tautology) class declaration config
+
+# class declaration
+class "tautology" {
+ # tautology
+ # note that true does not work as it is a variable reference
+ # and for the same reason quotes are needed (or one can use hexa)
+ match if "foo" = "foo";
+}
diff --git a/keama/tests/tautology.msg b/keama/tests/tautology.msg
new file mode 100644
index 00000000..80bf2feb
--- /dev/null
+++ b/keama/tests/tautology.msg
@@ -0,0 +1 @@
+tautology.err line 8: 'match if' with a constant boolean expression 'foo' = 'foo'
diff --git a/keama/tests/tautologyhexa.err b/keama/tests/tautologyhexa.err
new file mode 100644
index 00000000..69249d86
--- /dev/null
+++ b/keama/tests/tautologyhexa.err
@@ -0,0 +1,9 @@
+# bad (tautology) class declaration config
+
+# class declaration
+class "tautology" {
+ # tautology
+ # note that true does not work as it is a variable reference
+ # and for the same reason quotes are needed (or one can use hexa)
+ match if 12:34 = 56:78:9a;
+}
diff --git a/keama/tests/tautologyhexa.msg b/keama/tests/tautologyhexa.msg
new file mode 100644
index 00000000..7482482b
--- /dev/null
+++ b/keama/tests/tautologyhexa.msg
@@ -0,0 +1 @@
+tautologyhexa.err line 8: 'match if' with a constant boolean expression 0x1234 = 0x56789a
diff --git a/keama/tests/tautologysub.err b/keama/tests/tautologysub.err
new file mode 100644
index 00000000..08de2575
--- /dev/null
+++ b/keama/tests/tautologysub.err
@@ -0,0 +1,9 @@
+# bad (tautology) class declaration config
+
+# superclass declaration
+class "constant" {
+ match "foo";
+}
+
+# subclass declaration
+subclass "constant" "bar";
diff --git a/keama/tests/tautologysub.msg b/keama/tests/tautologysub.msg
new file mode 100644
index 00000000..d30f39a7
--- /dev/null
+++ b/keama/tests/tautologysub.msg
@@ -0,0 +1 @@
+tautologysub.err line 9: class matching rule evaluated to a constant boolean expression: 'foo' = 'bar'
diff --git a/keama/tests/temporary6.in6 b/keama/tests/temporary6.in6
new file mode 100644
index 00000000..dec7d99b
--- /dev/null
+++ b/keama/tests/temporary6.in6
@@ -0,0 +1,10 @@
+# DHCPv6 temporary (aka IA_TA) range config
+
+# subnet declaration
+subnet6 2001::/64 {
+ # range declaration
+ option dhcp6.domain-search "example.com", "example.org";
+ default-lease-time 1800;
+ range6 2001::100 temporary;
+ range6 2001::1000/116 temporary;
+}
diff --git a/keama/tests/temporary6.out b/keama/tests/temporary6.out
new file mode 100644
index 00000000..e7af7bba
--- /dev/null
+++ b/keama/tests/temporary6.out
@@ -0,0 +1,33 @@
+{
+ # DHCPv6 temporary (aka IA_TA) range config
+ # subnet declaration
+ /// This configuration declares some subnets but has no interfaces-config
+ /// Reference Kea #245
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001::/64",
+ "option-data": [
+ # range declaration
+ {
+ "space": "dhcp6",
+ "name": "domain-search",
+ "code": 24,
+// "original-data": "\"example.com\", \"example.org\"",
+ "data": "example.com, example.org"
+ }
+ ],
+ "valid-lifetime": 1800,
+ "pools": [
+// {
+// "pool": "2001::100/64 temporary"
+// }
+// {
+// "pool": "2001::1000/116 temporary"
+// }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/textarray.err b/keama/tests/textarray.err
new file mode 100644
index 00000000..7cecd7a0
--- /dev/null
+++ b/keama/tests/textarray.err
@@ -0,0 +1,7 @@
+# option definition config
+
+# options
+option space foobar;
+
+# array of text are forbidden
+option foobarstring-array code 1 = array of text;
diff --git a/keama/tests/textarray.msg b/keama/tests/textarray.msg
new file mode 100644
index 00000000..f1115cbe
--- /dev/null
+++ b/keama/tests/textarray.msg
@@ -0,0 +1 @@
+textarray.err line 7: arrays of text strings not yet supported.
diff --git a/keama/tests/unknownoption.err b/keama/tests/unknownoption.err
new file mode 100644
index 00000000..ce4aacbb
--- /dev/null
+++ b/keama/tests/unknownoption.err
@@ -0,0 +1,4 @@
+# unknown option config
+
+# unknown option
+option this-option-does-not-exist off;
diff --git a/keama/tests/unknownoption.msg b/keama/tests/unknownoption.msg
new file mode 100644
index 00000000..0fb2f1e9
--- /dev/null
+++ b/keama/tests/unknownoption.msg
@@ -0,0 +1 @@
+unknownoption.err line 4: unknown option dhcp.this-option-does-not-exist
diff --git a/keama/tests/unknownspace.err b/keama/tests/unknownspace.err
new file mode 100644
index 00000000..8165a972
--- /dev/null
+++ b/keama/tests/unknownspace.err
@@ -0,0 +1,4 @@
+# unknown option config
+
+# unknown option
+option this-space-does-not-exist.domain-search "example.com";
diff --git a/keama/tests/unknownspace.msg b/keama/tests/unknownspace.msg
new file mode 100644
index 00000000..f6e378af
--- /dev/null
+++ b/keama/tests/unknownspace.msg
@@ -0,0 +1 @@
+unknownspace.err line 4: no option space named this-space-does-not-exist.
diff --git a/keama/tests/userclass.err b/keama/tests/userclass.err
new file mode 100644
index 00000000..e8a3d27c
--- /dev/null
+++ b/keama/tests/userclass.err
@@ -0,0 +1,6 @@
+# user-class declaration config
+
+# user-class declaration
+user-class "foobar" {
+}
+
diff --git a/keama/tests/userclass.msg b/keama/tests/userclass.msg
new file mode 100644
index 00000000..2208500e
--- /dev/null
+++ b/keama/tests/userclass.msg
@@ -0,0 +1 @@
+userclass.err line 4: obsolete 'user-class' declaration
diff --git a/keama/tests/vendorclass.err b/keama/tests/vendorclass.err
new file mode 100644
index 00000000..81d1b4e3
--- /dev/null
+++ b/keama/tests/vendorclass.err
@@ -0,0 +1,6 @@
+# vendor-class declaration config
+
+# vendor-class declaration
+vendor-class "foobar" {
+}
+
diff --git a/keama/tests/vendorclass.msg b/keama/tests/vendorclass.msg
new file mode 100644
index 00000000..fc45fe66
--- /dev/null
+++ b/keama/tests/vendorclass.msg
@@ -0,0 +1 @@
+vendorclass.err line 4: obsolete 'vendor-class' declaration
diff --git a/keama/tests/vendorspace4.in4 b/keama/tests/vendorspace4.in4
new file mode 100644
index 00000000..98f9eada
--- /dev/null
+++ b/keama/tests/vendorspace4.in4
@@ -0,0 +1,11 @@
+# vendor option space config
+
+option space foo;
+option foo.bar code 1 = text;
+
+# class declaration
+class "foobar" {
+ match if option vendor-class-identifier = "foo";
+ vendor-option-space foo;
+ option foo.bar "foobar";
+}
diff --git a/keama/tests/vendorspace4.out b/keama/tests/vendorspace4.out
new file mode 100644
index 00000000..09feae6f
--- /dev/null
+++ b/keama/tests/vendorspace4.out
@@ -0,0 +1,41 @@
+{
+ # vendor option space config
+ "Dhcp4": {
+ "option-def": [
+ {
+ "space": "foo",
+ "name": "bar",
+ "code": 1,
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ # class declaration
+ {
+ "name": "foobar",
+ /// from: match if (option dhcp.vendor-class-identifier) = 'foo'
+ "test": "option[60].hex == 'foo'",
+ "option-def": [
+ {
+ "name": "vendor-encapsulated-options",
+ "code": 43,
+ "type": "empty",
+ "encapsulate": "foo"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "vendor-encapsulated-options",
+ "code": 43
+ },
+ {
+ "space": "foo",
+ "name": "bar",
+ "code": 1,
+ "data": "foobar"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/keama/tests/zone4.in4 b/keama/tests/zone4.in4
new file mode 100644
index 00000000..654e6c8e
--- /dev/null
+++ b/keama/tests/zone4.in4
@@ -0,0 +1,24 @@
+# zone executable statement construct
+
+# empty configs are not accepted by Kea
+default-lease-time 1800;
+
+# a zone
+zone example.com {
+ primary 10.5.5.1, 10.5.5.2;
+ secondary 10.10.10.1;
+ primary6 2001::1, 2001::2;
+ secondary6 2002::1;
+ key "mykey";
+}
+
+# a key;
+key "mykey" {
+ algorithm hmac-md5;
+ secret "somekeydata";
+}
+
+# another key (with bind 8 semi-colon)
+key example.com {
+ algorithm aes-gmac.dreams;
+};
diff --git a/keama/tests/zone4.out b/keama/tests/zone4.out
new file mode 100644
index 00000000..7565db4c
--- /dev/null
+++ b/keama/tests/zone4.out
@@ -0,0 +1,47 @@
+{
+ # zone executable statement construct
+ # empty configs are not accepted by Kea
+ "Dhcp4": {
+ "valid-lifetime": 1800
+// # a zone
+// "statement": {
+// "zone": {
+// "name": "example.com.",
+// "primary": [
+// "10.5.5.1",
+// "10.5.5.2"
+// ],
+// "secondary": [
+// "10.10.10.1"
+// ],
+// "primary6": [
+// "2001::1",
+// "2001::2"
+// ],
+// "secondary6": [
+// "2002::1"
+// ],
+// "key": "mykey"
+// }
+// }
+// # a key;
+// "statement": {
+// "tsig-keys": [
+// {
+// "name": "mykey",
+// "algorithm": "hmac-md5.SIG-ALG.REG.INT.",
+// "secret": "somekeydata"
+// }
+// ]
+// }
+// # another key (with bind 8 semi-colon)
+// "statement": {
+// "tsig-keys": [
+// {
+// "name": "example.com",
+// "algorithm": "aes-gmac.dreams."
+// }
+// ]
+// }
+ }
+}
diff --git a/omapip/Makefile.in b/omapip/Makefile.in
index 619434f5..79593536 100644
--- a/omapip/Makefile.in
+++ b/omapip/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -100,6 +100,7 @@ mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/includes/config.h
CONFIG_CLEAN_FILES = Makefile.am
CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
@@ -145,7 +146,6 @@ am_libomapi_a_OBJECTS = protocol.$(OBJEXT) buffer.$(OBJEXT) \
trace.$(OBJEXT) toisc.$(OBJEXT) iscprint.$(OBJEXT) \
isclib.$(OBJEXT)
libomapi_a_OBJECTS = $(am_libomapi_a_OBJECTS)
-PROGRAMS = $(noinst_PROGRAMS)
am_svtest_OBJECTS = test.$(OBJEXT)
svtest_OBJECTS = $(am_svtest_OBJECTS)
svtest_DEPENDENCIES = libomapi.a $(BINDLIBIRSDIR)/libirs.a \
@@ -165,7 +165,18 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/alloc.Po ./$(DEPDIR)/array.Po \
+ ./$(DEPDIR)/auth.Po ./$(DEPDIR)/buffer.Po \
+ ./$(DEPDIR)/connection.Po ./$(DEPDIR)/convert.Po \
+ ./$(DEPDIR)/dispatch.Po ./$(DEPDIR)/errwarn.Po \
+ ./$(DEPDIR)/generic.Po ./$(DEPDIR)/handle.Po \
+ ./$(DEPDIR)/hash.Po ./$(DEPDIR)/inet_addr.Po \
+ ./$(DEPDIR)/isclib.Po ./$(DEPDIR)/iscprint.Po \
+ ./$(DEPDIR)/listener.Po ./$(DEPDIR)/message.Po \
+ ./$(DEPDIR)/protocol.Po ./$(DEPDIR)/result.Po \
+ ./$(DEPDIR)/support.Po ./$(DEPDIR)/test.Po \
+ ./$(DEPDIR)/toisc.Po ./$(DEPDIR)/trace.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -370,8 +381,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -384,6 +395,9 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
Makefile.am: $(top_builddir)/config.status $(srcdir)/Makefile.am.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
install-libLIBRARIES: $(lib_LIBRARIES)
@$(NORMAL_INSTALL)
@list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
@@ -421,9 +435,6 @@ libomapi.a: $(libomapi_a_OBJECTS) $(libomapi_a_DEPENDENCIES) $(EXTRA_libomapi_a_
$(AM_V_AR)$(libomapi_a_AR) libomapi.a $(libomapi_a_OBJECTS) $(libomapi_a_LIBADD)
$(AM_V_at)$(RANLIB) libomapi.a
-clean-noinstPROGRAMS:
- -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
-
svtest$(EXEEXT): $(svtest_OBJECTS) $(svtest_DEPENDENCIES) $(EXTRA_svtest_DEPENDENCIES)
@rm -f svtest$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(svtest_OBJECTS) $(svtest_LDADD) $(LIBS)
@@ -434,28 +445,34 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/convert.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errwarn.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/handle.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet_addr.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isclib.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iscprint.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listener.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/result.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/toisc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/convert.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errwarn.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/handle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet_addr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isclib.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iscprint.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listener.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/result.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/toisc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -566,7 +583,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -598,7 +618,7 @@ distdir: $(DISTFILES)
done
check-am: all-am
check: check-am
-all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(MANS)
+all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(MANS)
installdirs:
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
@@ -639,7 +659,28 @@ clean-am: clean-generic clean-libLIBRARIES clean-noinstPROGRAMS \
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/alloc.Po
+ -rm -f ./$(DEPDIR)/array.Po
+ -rm -f ./$(DEPDIR)/auth.Po
+ -rm -f ./$(DEPDIR)/buffer.Po
+ -rm -f ./$(DEPDIR)/connection.Po
+ -rm -f ./$(DEPDIR)/convert.Po
+ -rm -f ./$(DEPDIR)/dispatch.Po
+ -rm -f ./$(DEPDIR)/errwarn.Po
+ -rm -f ./$(DEPDIR)/generic.Po
+ -rm -f ./$(DEPDIR)/handle.Po
+ -rm -f ./$(DEPDIR)/hash.Po
+ -rm -f ./$(DEPDIR)/inet_addr.Po
+ -rm -f ./$(DEPDIR)/isclib.Po
+ -rm -f ./$(DEPDIR)/iscprint.Po
+ -rm -f ./$(DEPDIR)/listener.Po
+ -rm -f ./$(DEPDIR)/message.Po
+ -rm -f ./$(DEPDIR)/protocol.Po
+ -rm -f ./$(DEPDIR)/result.Po
+ -rm -f ./$(DEPDIR)/support.Po
+ -rm -f ./$(DEPDIR)/test.Po
+ -rm -f ./$(DEPDIR)/toisc.Po
+ -rm -f ./$(DEPDIR)/trace.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -685,7 +726,28 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/alloc.Po
+ -rm -f ./$(DEPDIR)/array.Po
+ -rm -f ./$(DEPDIR)/auth.Po
+ -rm -f ./$(DEPDIR)/buffer.Po
+ -rm -f ./$(DEPDIR)/connection.Po
+ -rm -f ./$(DEPDIR)/convert.Po
+ -rm -f ./$(DEPDIR)/dispatch.Po
+ -rm -f ./$(DEPDIR)/errwarn.Po
+ -rm -f ./$(DEPDIR)/generic.Po
+ -rm -f ./$(DEPDIR)/handle.Po
+ -rm -f ./$(DEPDIR)/hash.Po
+ -rm -f ./$(DEPDIR)/inet_addr.Po
+ -rm -f ./$(DEPDIR)/isclib.Po
+ -rm -f ./$(DEPDIR)/iscprint.Po
+ -rm -f ./$(DEPDIR)/listener.Po
+ -rm -f ./$(DEPDIR)/message.Po
+ -rm -f ./$(DEPDIR)/protocol.Po
+ -rm -f ./$(DEPDIR)/result.Po
+ -rm -f ./$(DEPDIR)/support.Po
+ -rm -f ./$(DEPDIR)/test.Po
+ -rm -f ./$(DEPDIR)/toisc.Po
+ -rm -f ./$(DEPDIR)/trace.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -707,20 +769,20 @@ uninstall-man: uninstall-man3
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libLIBRARIES clean-noinstPROGRAMS cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am install-dvi \
- install-dvi-am install-exec install-exec-am install-html \
- install-html-am install-info install-info-am \
- install-libLIBRARIES install-man install-man3 install-pdf \
- install-pdf-am install-ps install-ps-am install-strip \
- installcheck installcheck-am installdirs maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am uninstall-libLIBRARIES uninstall-man \
- uninstall-man3
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLIBRARIES clean-noinstPROGRAMS \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLIBRARIES install-man install-man3 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLIBRARIES \
+ uninstall-man uninstall-man3
.PRECIOUS: Makefile
diff --git a/omapip/isclib.c b/omapip/isclib.c
index fe306b08..4aca9130 100644
--- a/omapip/isclib.c
+++ b/omapip/isclib.c
@@ -134,6 +134,35 @@ handle_signal(int sig, void (*handler)(int)) {
}
}
+/* Callback passed to isc_app_ctxonrun
+ *
+ * BIND9 context code will invoke this handler once the context has
+ * entered the running state. We use it to set a global marker so that
+ * we can tell if the context is running. Several of the isc_app_
+ * calls REQUIRE that the context is running and we need a way to
+ * know that.
+ *
+ * We also check to see if we received a shutdown signal prior to
+ * the context entering the run state. If we did, then we can just
+ * simply shut the context down now. This closes the relatively
+ * small window between start up and entering run via the call
+ * to dispatch().
+ *
+ */
+static void
+set_ctx_running(isc_task_t *task, isc_event_t *event) {
+ IGNORE_UNUSED(task);
+ dhcp_gbl_ctx.actx_running = ISC_TRUE;
+
+ if (shutdown_signal) {
+ // We got signaled shutdown before we entered running state.
+ // Now that we've reached running state, shut'er down.
+ isc_app_ctxsuspend(dhcp_gbl_ctx.actx);
+ }
+
+ isc_event_free(&event);
+}
+
isc_result_t
dhcp_context_create(int flags,
struct in_addr *local4,
@@ -141,6 +170,9 @@ dhcp_context_create(int flags,
isc_result_t result;
if ((flags & DHCP_CONTEXT_PRE_DB) != 0) {
+ dhcp_gbl_ctx.actx_started = ISC_FALSE;
+ dhcp_gbl_ctx.actx_running = ISC_FALSE;
+
/*
* Set up the error messages, this isn't the right place
* for this call but it is convienent for now.
@@ -204,15 +236,24 @@ dhcp_context_create(int flags,
if (result != ISC_R_SUCCESS)
goto cleanup;
- result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0, &dhcp_gbl_ctx.task);
+ result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0,
+ &dhcp_gbl_ctx.task);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = isc_app_ctxstart(dhcp_gbl_ctx.actx);
if (result != ISC_R_SUCCESS)
- return (result);
+ goto cleanup;
+
dhcp_gbl_ctx.actx_started = ISC_TRUE;
+ // Install the onrun callback.
+ result = isc_app_ctxonrun(dhcp_gbl_ctx.actx, dhcp_gbl_ctx.mctx,
+ dhcp_gbl_ctx.task, set_ctx_running,
+ dhcp_gbl_ctx.actx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
/* Not all OSs support suppressing SIGPIPE through socket
* options, so set the sigal action to be ignore. This allows
* broken connections to fail gracefully with EPIPE on writes */
@@ -335,19 +376,17 @@ isclib_make_dst_key(char *inname,
* @param signal signal code that we received
*/
void dhcp_signal_handler(int signal) {
- isc_appctx_t *ctx = dhcp_gbl_ctx.actx;
- int prev = shutdown_signal;
-
- if (prev != 0) {
+ if (shutdown_signal != 0) {
/* Already in shutdown. */
return;
}
+
/* Possible race but does it matter? */
shutdown_signal = signal;
- /* Use reload (aka suspend) for easier dispatch() reenter. */
- if (ctx && ctx->methods && ctx->methods->ctxsuspend) {
- (void) isc_app_ctxsuspend(ctx);
+ /* If the application context is running tell it to shut down */
+ if (dhcp_gbl_ctx.actx_running == ISC_TRUE) {
+ (void) isc_app_ctxsuspend(dhcp_gbl_ctx.actx);
}
}
diff --git a/relay/Makefile.in b/relay/Makefile.in
index 9e9e7bd2..1ad248d4 100644
--- a/relay/Makefile.in
+++ b/relay/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -118,7 +118,8 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dhcrelay.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -344,8 +345,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -409,7 +410,13 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcrelay.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcrelay.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -520,7 +527,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -592,7 +602,7 @@ clean: clean-am
clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/dhcrelay.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -638,7 +648,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/dhcrelay.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -660,15 +670,15 @@ uninstall-man: uninstall-man8
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \
- distclean-compile distclean-generic distclean-tags distdir dvi \
- dvi-am html html-am info info-am install install-am \
- install-data install-data-am install-dvi install-dvi-am \
- install-exec install-exec-am install-html install-html-am \
- install-info install-info-am install-man install-man8 \
- install-pdf install-pdf-am install-ps install-ps-am \
- install-sbinPROGRAMS install-strip installcheck \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-sbinPROGRAMS cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
index dd8e446d..09162100 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -1869,7 +1869,7 @@ process_down6(struct packet *packet) {
&global_scope, oc, MDL) ||
(relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
log_error("Can't evaluate relay-msg.");
- return;
+ goto cleanup;
}
msg = (const struct dhcpv6_packet *) relay_msg.data;
@@ -2076,6 +2076,9 @@ dhcp_set_control_state(control_object_state_t oldstate,
if (newstate != server_shutdown)
return ISC_R_SUCCESS;
+ /* Log shutdown on signal. */
+ log_info("Received signal %d, initiating shutdown.", shutdown_signal);
+
if (no_pid_file == ISC_FALSE)
(void) unlink(path_dhcrelay_pid);
diff --git a/server/Makefile.in b/server/Makefile.in
index 08e2774a..1a4171e2 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -136,7 +136,18 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dhcpd-bootp.Po \
+ ./$(DEPDIR)/dhcpd-class.Po ./$(DEPDIR)/dhcpd-confpars.Po \
+ ./$(DEPDIR)/dhcpd-db.Po ./$(DEPDIR)/dhcpd-ddns.Po \
+ ./$(DEPDIR)/dhcpd-dhcp.Po ./$(DEPDIR)/dhcpd-dhcpd.Po \
+ ./$(DEPDIR)/dhcpd-dhcpleasequery.Po \
+ ./$(DEPDIR)/dhcpd-dhcpv6.Po ./$(DEPDIR)/dhcpd-failover.Po \
+ ./$(DEPDIR)/dhcpd-ldap.Po ./$(DEPDIR)/dhcpd-ldap_casa.Po \
+ ./$(DEPDIR)/dhcpd-ldap_krb_helper.Po \
+ ./$(DEPDIR)/dhcpd-leasechain.Po ./$(DEPDIR)/dhcpd-mdb.Po \
+ ./$(DEPDIR)/dhcpd-mdb6.Po ./$(DEPDIR)/dhcpd-omapi.Po \
+ ./$(DEPDIR)/dhcpd-salloc.Po ./$(DEPDIR)/dhcpd-stables.Po
am__mv = mv -f
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -208,7 +219,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
+ distdir distdir-am
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -422,8 +433,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -487,25 +498,31 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-bootp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-class.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-confpars.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-db.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ddns.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpleasequery.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpv6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-failover.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_casa.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_krb_helper.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-leasechain.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-omapi.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-salloc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-stables.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-bootp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-class.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-confpars.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-db.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ddns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpleasequery.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpv6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-failover.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_casa.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_krb_helper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-leasechain.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-omapi.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-salloc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-stables.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -993,7 +1010,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -1091,7 +1111,25 @@ clean: clean-recursive
clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/dhcpd-bootp.Po
+ -rm -f ./$(DEPDIR)/dhcpd-class.Po
+ -rm -f ./$(DEPDIR)/dhcpd-confpars.Po
+ -rm -f ./$(DEPDIR)/dhcpd-db.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ddns.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcp.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcpd.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcpleasequery.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcpv6.Po
+ -rm -f ./$(DEPDIR)/dhcpd-failover.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ldap.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ldap_casa.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ldap_krb_helper.Po
+ -rm -f ./$(DEPDIR)/dhcpd-leasechain.Po
+ -rm -f ./$(DEPDIR)/dhcpd-mdb.Po
+ -rm -f ./$(DEPDIR)/dhcpd-mdb6.Po
+ -rm -f ./$(DEPDIR)/dhcpd-omapi.Po
+ -rm -f ./$(DEPDIR)/dhcpd-salloc.Po
+ -rm -f ./$(DEPDIR)/dhcpd-stables.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -1137,7 +1175,25 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/dhcpd-bootp.Po
+ -rm -f ./$(DEPDIR)/dhcpd-class.Po
+ -rm -f ./$(DEPDIR)/dhcpd-confpars.Po
+ -rm -f ./$(DEPDIR)/dhcpd-db.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ddns.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcp.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcpd.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcpleasequery.Po
+ -rm -f ./$(DEPDIR)/dhcpd-dhcpv6.Po
+ -rm -f ./$(DEPDIR)/dhcpd-failover.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ldap.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ldap_casa.Po
+ -rm -f ./$(DEPDIR)/dhcpd-ldap_krb_helper.Po
+ -rm -f ./$(DEPDIR)/dhcpd-leasechain.Po
+ -rm -f ./$(DEPDIR)/dhcpd-mdb.Po
+ -rm -f ./$(DEPDIR)/dhcpd-mdb6.Po
+ -rm -f ./$(DEPDIR)/dhcpd-omapi.Po
+ -rm -f ./$(DEPDIR)/dhcpd-salloc.Po
+ -rm -f ./$(DEPDIR)/dhcpd-stables.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -1160,21 +1216,22 @@ uninstall-man: uninstall-man5 uninstall-man8
.MAKE: $(am__recursive_targets) install-am install-strip
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-generic clean-sbinPROGRAMS cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-dist_sysconfDATA install-dvi install-dvi-am \
- install-exec install-exec-am install-html install-html-am \
- install-info install-info-am install-man install-man5 \
- install-man8 install-pdf install-pdf-am install-ps \
- install-ps-am install-sbinPROGRAMS install-strip installcheck \
- installcheck-am installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am uninstall-dist_sysconfDATA uninstall-man \
- uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dist_sysconfDATA \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-man install-man5 install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-dist_sysconfDATA uninstall-man uninstall-man5 \
+ uninstall-man8 uninstall-sbinPROGRAMS
.PRECIOUS: Makefile
diff --git a/server/class.c b/server/class.c
index 70904978..dc7737f2 100644
--- a/server/class.c
+++ b/server/class.c
@@ -174,7 +174,6 @@ int check_collection (packet, lease, collection)
}
data_string_copy (&nc -> hash_string, &data,
MDL);
- data_string_forget (&data, MDL);
if (!class -> hash)
class_new_hash(&class->hash,
SCLASS_HASH_SIZE, MDL);
@@ -186,6 +185,8 @@ int check_collection (packet, lease, collection)
classify (packet, nc);
class_dereference (&nc, MDL);
}
+
+ data_string_forget (&data, MDL);
}
}
return matched;
diff --git a/server/confpars.c b/server/confpars.c
index 7ad28d55..4f0b58ee 100644
--- a/server/confpars.c
+++ b/server/confpars.c
@@ -775,8 +775,11 @@ int parse_statement (cfile, group, type, host_decl, declaration)
et = (struct executable_statement *)0;
if (!parse_option_statement
(&et, cfile, 1, option,
- supersede_option_statement))
+ supersede_option_statement)) {
+ option_dereference(&option, MDL);
return declaration;
+ }
+
option_dereference(&option, MDL);
goto insert_statement;
} else
@@ -2048,12 +2051,15 @@ void parse_host_declaration (cfile, group)
unsigned len;
skip_token(&val, (unsigned *)0, cfile);
- data_string_forget (&host -> client_identifier, MDL);
-
if (host->client_identifier.len != 0) {
- parse_warn(cfile, "Host %s already has a "
- "client identifier.",
- host->name);
+ char buf[256];
+ print_hex_or_string(host->client_identifier.len,
+ host->client_identifier.data,
+ sizeof(buf) - 1, buf);
+ parse_warn(cfile,
+ "Host '%s' already has a uid '%s'",
+ host->name, buf);
+ skip_to_rbrace(cfile, 1);
break;
}
@@ -2795,6 +2801,7 @@ void parse_subnet_declaration (cfile, share)
if (token != NETMASK) {
parse_warn (cfile, "Expecting netmask");
skip_to_semi (cfile);
+ subnet_dereference (&subnet, MDL);
return;
}
@@ -2898,6 +2905,7 @@ parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) {
token = next_token(&val, NULL, cfile);
if (token != SLASH) {
parse_warn(cfile, "Expecting a '/'.");
+ subnet_dereference(&subnet, MDL);
skip_to_semi(cfile);
return;
}
@@ -2905,6 +2913,7 @@ parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) {
token = next_token(&val, NULL, cfile);
if (token != NUMBER) {
parse_warn(cfile, "Expecting a number.");
+ subnet_dereference(&subnet, MDL);
skip_to_semi(cfile);
return;
}
@@ -2914,12 +2923,14 @@ parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) {
(subnet->prefix_len > 128) ||
(*endp != '\0')) {
parse_warn(cfile, "Expecting a number between 0 and 128.");
+ subnet_dereference(&subnet, MDL);
skip_to_semi(cfile);
return;
}
if (!is_cidr_mask_valid(&subnet->net, subnet->prefix_len)) {
parse_warn(cfile, "New subnet mask too short.");
+ subnet_dereference(&subnet, MDL);
skip_to_semi(cfile);
return;
}
@@ -3578,6 +3589,11 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
if (token != EQUAL) {
parse_warn (cfile,
"expecting '=' in set statement.");
+ binding_value_dereference(&nv, MDL);
+ if (newbinding) {
+ dfree(binding->name, MDL);
+ dfree(binding, MDL);
+ }
goto badset;
}
}
@@ -3585,6 +3601,10 @@ int parse_lease_declaration (struct lease **lp, struct parse *cfile)
if (!parse_binding_value(cfile, nv)) {
binding_value_dereference(&nv, MDL);
lease_dereference(&lease, MDL);
+ if (newbinding) {
+ dfree(binding->name, MDL);
+ dfree(binding, MDL);
+ }
return 0;
}
@@ -4762,6 +4782,7 @@ parse_ia_na_declaration(struct parse *cfile) {
if (token != LBRACE) {
parse_warn(cfile, "corrupt lease file; expecting left brace");
skip_to_semi(cfile);
+ ia_dereference(&ia, MDL);
return;
}
@@ -5209,6 +5230,7 @@ parse_ia_ta_declaration(struct parse *cfile) {
if (token != LBRACE) {
parse_warn(cfile, "corrupt lease file; expecting left brace");
skip_to_semi(cfile);
+ ia_dereference(&ia, MDL);
return;
}
@@ -5646,6 +5668,7 @@ parse_ia_pd_declaration(struct parse *cfile) {
if (token != LBRACE) {
parse_warn(cfile, "corrupt lease file; expecting left brace");
skip_to_semi(cfile);
+ ia_dereference(&ia, MDL);
return;
}
diff --git a/server/ddns.c b/server/ddns.c
index 1bd72f6b..aecc3d38 100644
--- a/server/ddns.c
+++ b/server/ddns.c
@@ -1285,9 +1285,10 @@ ddns_update_lease_ptr(struct lease *lease,
file, line);
#endif
/*
- * never reached. update_lease_failed
- * calls log_fatal.
+ * not reached when update_lease_failed is called,
+ * it calls log_fatal.
*/
+ ipv6_pool_dereference(&pool, MDL);
return(ISC_R_FAILURE);
}
ipv6_pool_dereference(&pool, MDL);
diff --git a/server/dhcp.c b/server/dhcp.c
index 6d129ec9..16a4f924 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -1193,6 +1193,7 @@ void dhcpinform (packet, ms_nulltp)
if (d1.len != 4) {
log_info("%s: ignored (invalid subnet selection option).", msgbuf);
option_state_dereference(&options, MDL);
+ data_string_forget(&d1, MDL);
return;
}
@@ -1574,6 +1575,7 @@ void dhcpinform (packet, ms_nulltp)
option_state_dereference (&options, MDL);
if (subnet)
subnet_dereference (&subnet, MDL);
+ data_string_forget (&d1, MDL);
return;
}
@@ -3526,6 +3528,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
(const char *)d1.data, d1.len,
MDL)) {
log_error ("unknown option space %s.", d1.data);
+ data_string_forget (&d1, MDL);
return;
}
@@ -5370,13 +5373,12 @@ int locate_network (packet)
&global_scope, oc, MDL)) {
return 0;
}
- if (data.len == 0) {
- return 0;
- }
+
if (data.len != 4) {
data_string_forget (&data, MDL);
return 0;
}
+
ia.len = 4;
memcpy (ia.iabuf, data.data, 4);
data_string_forget (&data, MDL);
diff --git a/server/dhcpleasequery.c b/server/dhcpleasequery.c
index 7be07889..0f1d4f77 100644
--- a/server/dhcpleasequery.c
+++ b/server/dhcpleasequery.c
@@ -873,15 +873,9 @@ valid_query_msg(struct lq6_state *lq) {
exit:
if (!ret_val) {
- if (lq->client_id.len > 0) {
- data_string_forget(&lq->client_id, MDL);
- }
- if (lq->server_id.len > 0) {
- data_string_forget(&lq->server_id, MDL);
- }
- if (lq->lq_query.len > 0) {
- data_string_forget(&lq->lq_query, MDL);
- }
+ data_string_forget(&lq->client_id, MDL);
+ data_string_forget(&lq->server_id, MDL);
+ data_string_forget(&lq->lq_query, MDL);
}
return ret_val;
}
diff --git a/server/dhcpv6.c b/server/dhcpv6.c
index cde4f617..0ea05325 100644
--- a/server/dhcpv6.c
+++ b/server/dhcpv6.c
@@ -632,13 +632,9 @@ valid_client_msg(struct packet *packet, struct data_string *client_id) {
ret_val = 1;
exit:
- if (data.len > 0) {
- data_string_forget(&data, MDL);
- }
+ data_string_forget(&data, MDL);
if (!ret_val) {
- if (client_id->len > 0) {
- data_string_forget(client_id, MDL);
- }
+ data_string_forget(client_id, MDL);
}
return ret_val;
}
@@ -723,12 +719,8 @@ valid_client_resp(struct packet *packet,
exit:
if (!ret_val) {
- if (server_id->len > 0) {
- data_string_forget(server_id, MDL);
- }
- if (client_id->len > 0) {
- data_string_forget(client_id, MDL);
- }
+ data_string_forget(server_id, MDL);
+ data_string_forget(client_id, MDL);
}
return ret_val;
}
@@ -840,9 +832,7 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
exit:
if (!ret_val) {
- if (server_id->len > 0) {
- data_string_forget(server_id, MDL);
- }
+ data_string_forget(server_id, MDL);
}
return ret_val;
}
diff --git a/server/mdb.c b/server/mdb.c
index 052df676..ff8a707f 100644
--- a/server/mdb.c
+++ b/server/mdb.c
@@ -3,7 +3,7 @@
Server-specific in-memory database support. */
/*
- * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium
*
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -41,12 +41,12 @@ lease_id_hash_t *lease_hw_addr_hash;
/*
* We allow users to specify any option as a host identifier.
*
- * Any host is uniquely identified by the combination of
+ * Any host is uniquely identified by the combination of
* option type & option data.
*
- * We expect people will only use a few types of options as host
+ * We expect people will only use a few types of options as host
* identifier. Because of this, we store a list with an entry for
- * each option type. Each of these has a hash table, which contains
+ * each option type. Each of these has a hash table, which contains
* hash of the option data.
*
* For v6 we also include a relay count - this specifies which
@@ -87,7 +87,7 @@ isc_result_t enter_class(cd, dynamicp, commit)
class_dereference(&c, MDL);
return ISC_R_EXISTS;
}
-
+
/* Find the tail. */
for (c = collections -> classes;
c -> nic; c = c -> nic)
@@ -188,7 +188,7 @@ change_host_uid(struct host_decl *host, const char *uid, int len) {
}
}
- /*
+ /*
* Remove the old entry, if one exists.
*/
if (host->client_identifier.data != NULL) {
@@ -199,7 +199,7 @@ change_host_uid(struct host_decl *host, const char *uid, int len) {
data_string_forget(&host->client_identifier, MDL);
}
- /*
+ /*
* Set our new value.
*/
memset(&host->client_identifier, 0, sizeof(host->client_identifier));
@@ -213,7 +213,7 @@ change_host_uid(struct host_decl *host, const char *uid, int len) {
/*
* And add to hash.
*/
- host_hash_add(host_uid_hash, host->client_identifier.data,
+ host_hash_add(host_uid_hash, host->client_identifier.data,
host->client_identifier.len, host, MDL);
}
@@ -313,10 +313,32 @@ isc_result_t enter_host (hd, dynamicp, commit)
esp = NULL;
if (executable_statement_foreach (hd->group->statements,
find_uid_statement, &esp, 0)) {
- (void) evaluate_option_cache (&hd->client_identifier,
- NULL, NULL, NULL, NULL, NULL,
+ struct data_string cid;
+ memset(&cid, 0, sizeof(cid));
+ (void) evaluate_option_cache (&cid,
+ NULL, NULL, NULL, NULL, NULL,
&global_scope,
esp->data.option, MDL);
+
+ if (hd->client_identifier.len > 0 && cid.len > 0) {
+ char uid_buf[256];
+ char cid_buf[256];
+ print_hex_or_string(hd->client_identifier.len,
+ hd->client_identifier.data,
+ sizeof(uid_buf) - 1, uid_buf);
+
+ print_hex_or_string(cid.len, cid.data,
+ sizeof(cid_buf) - 1, cid_buf);
+
+ log_error ("Warning, host declaration '%s'"
+ " already has uid '%s',"
+ " ignoring dhcp-client-identifier '%s'",
+ hd->name, uid_buf, cid_buf);
+
+ data_string_forget(&cid, MDL);
+ } else {
+ memcpy(&hd->client_identifier, &cid, sizeof(cid));
+ }
}
/* If we got a client identifier, hash this entry by
@@ -379,9 +401,9 @@ isc_result_t enter_host (hd, dynamicp, commit)
log_fatal("No memory for host-identifier "
"option information.");
}
- option_reference(&h_id_info->option,
+ option_reference(&h_id_info->option,
hd->host_id_option, MDL);
- if (!host_new_hash(&h_id_info->values_hash,
+ if (!host_new_hash(&h_id_info->values_hash,
HOST_HASH_SIZE, MDL)) {
log_fatal("No memory for host-identifier "
"option hash.");
@@ -391,16 +413,16 @@ isc_result_t enter_host (hd, dynamicp, commit)
host_id_info = h_id_info;
}
- if (host_hash_lookup(&hp, h_id_info->values_hash,
+ if (host_hash_lookup(&hp, h_id_info->values_hash,
hd->host_id.data, hd->host_id.len, MDL)) {
- /*
- * If this option is already present, then add
+ /*
+ * If this option is already present, then add
* this host to the list in n_ipaddr, unless
* we have already done so previously.
*
* XXXSK: This seems scary to me, but I don't
- * fully understand how these are used.
- * Shouldn't there be multiple lists, or
+ * fully understand how these are used.
+ * Shouldn't there be multiple lists, or
* maybe we should just forbid duplicates?
*/
if (np == NULL) {
@@ -414,7 +436,7 @@ isc_result_t enter_host (hd, dynamicp, commit)
}
host_dereference(&hp, MDL);
} else {
- host_hash_add(h_id_info->values_hash,
+ host_hash_add(h_id_info->values_hash,
hd->host_id.data,
hd->host_id.len,
hd, MDL);
@@ -440,18 +462,18 @@ isc_result_t delete_class (cp, commit)
/* do the write first as we won't be leaving it in any data
structures, unlike the host objects */
-
+
if (commit) {
write_named_billing_class ((unsigned char *)cp->name, 0, cp);
if (!commit_leases ())
return ISC_R_IOERROR;
}
-
+
/*
* If this is a subclass remove it from the class's hash table
*/
if (cp->superclass) {
- class_hash_delete(cp->superclass->hash,
+ class_hash_delete(cp->superclass->hash,
(const char *)cp->hash_string.data,
cp->hash_string.len,
MDL);
@@ -634,7 +656,7 @@ int find_hosts_by_uid (struct host_decl **hp,
}
int
-find_hosts_by_option(struct host_decl **hp,
+find_hosts_by_option(struct host_decl **hp,
struct packet *packet,
struct option_state *opt_state,
const char *file, int line) {
@@ -649,9 +671,9 @@ find_hosts_by_option(struct host_decl **hp,
if ((found = find_client_in_ldap (hp, packet, opt_state, file, line)))
return found;
#endif
-
+
for (p = host_id_info; p != NULL; p = p->next) {
- relay_packet = packet;
+ relay_packet = packet;
relay_state = opt_state;
/* If this option block is for a relay (relays != 0)
@@ -675,20 +697,20 @@ find_hosts_by_option(struct host_decl **hp,
relay_state = relay_packet->options;
}
- oc = lookup_option(p->option->universe,
+ oc = lookup_option(p->option->universe,
relay_state, p->option->code);
if (oc != NULL) {
memset(&data, 0, sizeof(data));
if (!evaluate_option_cache(&data, relay_packet, NULL,
NULL, relay_state, NULL,
- &global_scope, oc,
+ &global_scope, oc,
MDL)) {
log_error("Error evaluating option cache");
return 0;
}
-
- found = host_hash_lookup(hp, p->values_hash,
+
+ found = host_hash_lookup(hp, p->values_hash,
data.data, data.len,
file, line);
@@ -949,8 +971,8 @@ int find_grouped_subnet (struct subnet **sp,
}
/* XXX: could speed up if everyone had a prefix length */
-int
-subnet_inner_than(const struct subnet *subnet,
+int
+subnet_inner_than(const struct subnet *subnet,
const struct subnet *scan,
int warnp) {
#if defined(DHCP4o6)
@@ -1026,7 +1048,7 @@ void enter_subnet (subnet)
}
subnet_reference (&subnets, subnet, MDL);
}
-
+
/* Enter a new shared network into the shared network list. */
void enter_shared_network (share)
@@ -1039,7 +1061,7 @@ void enter_shared_network (share)
}
shared_network_reference (&shared_networks, share, MDL);
}
-
+
void new_shared_network_interface (cfile, share, name)
struct parse *cfile;
struct shared_network *share;
@@ -1049,12 +1071,12 @@ void new_shared_network_interface (cfile, share, name)
isc_result_t status;
if (share -> interface) {
- parse_warn (cfile,
+ parse_warn (cfile,
"A subnet or shared network can't be connected %s",
"to two interfaces.");
return;
}
-
+
for (ip = interfaces; ip; ip = ip -> next)
if (!strcmp (ip -> name, name))
break;
@@ -1403,7 +1425,7 @@ int supersede_lease (comp, lease, commit, propogate, pimmediate, from_pool)
/* If this is the next lease that will timeout on the pool,
zap the old timeout and set the timeout on this pool to the
time that the lease's next event will happen.
-
+
We do not actually set the timeout unless commit is true -
we don't want to thrash the timer queue when reading the
lease database. Instead, the database code calls the
@@ -1516,7 +1538,7 @@ void make_binding_state_transition (struct lease *lease)
executable_statement_dereference
(&lease->on_star.on_expiry, MDL);
}
-
+
/* No sense releasing a lease after it's expired. */
if (lease->on_star.on_release)
executable_statement_dereference
@@ -1579,7 +1601,7 @@ void make_binding_state_transition (struct lease *lease)
executable_statement_dereference
(&lease->on_star.on_release, MDL);
}
-
+
/* A released lease can't expire. */
if (lease->on_star.on_expiry)
executable_statement_dereference
@@ -1947,7 +1969,7 @@ void pool_timer (vpool)
if (i == EXPIRED_LEASES)
continue;
}
-#endif
+#endif
lease_reference(&lease, LEASE_GET_FIRSTP(lptr[i]), MDL);
while (lease) {
@@ -2239,7 +2261,7 @@ hw_hash_add(struct lease *lease)
if ((lease->hardware_addr.hlen == 1) &&
(lease->hardware_addr.hbuf[0] == HTYPE_INFINIBAND))
return;
-
+
/* If it's not in the hash, just add it. */
if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
lease -> hardware_addr.hlen, MDL))
@@ -2429,12 +2451,12 @@ int write_leases ()
}
}
- /* XXXJAB this number doesn't include subclasses... */
+ /* XXXJAB this number doesn't include subclasses... */
log_info ("Wrote %d class decls to leases file.",
numclasseswritten);
}
-
-
+
+
/* Write all the dynamically-created group declarations. */
if (group_name_hash) {
num_written = 0;
@@ -2605,7 +2627,7 @@ void lease_insert(struct lease **lq, struct lease *comp)
* a re-queue), use that as a starting point for the insertion-sort.
*/
if ((server_starting & SS_QFOLLOW) && (lq == last_lq) &&
- (comp != last_insert_point) &&
+ (comp != last_insert_point) &&
(last_insert_point->sort_time <= comp->sort_time)) {
prev = last_insert_point;
lp = prev->next;
@@ -2655,7 +2677,7 @@ void lease_insert(struct lease **lq, struct lease *comp)
*/
int lease_enqueue (struct lease *comp)
{
- LEASE_STRUCT_PTR lq;
+ LEASE_STRUCT_PTR lq;
/* No queue to put it on? */
if (!comp -> pool)
@@ -2757,17 +2779,17 @@ lease_instantiate(const void *key, unsigned len, void *object)
* pool must have been formerly configured for failover and
* is now configured as standalone. This means we need to
* move the lease to FTS_FREE to make it available. */
- if ((lease->binding_state == FTS_BACKUP) &&
+ if ((lease->binding_state == FTS_BACKUP) &&
(lease->pool->failover_peer == NULL)) {
#else
/* We aren't compiled for failover, so just move to FTS_FREE */
- if (lease->binding_state == FTS_BACKUP) {
+ if (lease->binding_state == FTS_BACKUP) {
#endif
lease->binding_state = FTS_FREE;
lease->next_binding_state = FTS_FREE;
lease->rewind_binding_state = FTS_FREE;
}
-
+
/* Put the lease on the right queue. Failure to queue is probably
* due to a bogus binding state. In such a case, we claim success,
* so that later leases in a hash_foreach are processed, but we
diff --git a/server/mdb6.c b/server/mdb6.c
index 4afb3928..da7baf6e 100644
--- a/server/mdb6.c
+++ b/server/mdb6.c
@@ -1085,9 +1085,11 @@ create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
case D6O_IA_PD:
/* prefix */
log_error("create_lease6: prefix pool.");
+ data_string_forget(&ds, MDL);
return DHCP_R_INVALIDARG;
default:
log_error("create_lease6: untyped pool.");
+ data_string_forget(&ds, MDL);
return DHCP_R_INVALIDARG;
}
diff --git a/server/tests/Makefile.in b/server/tests/Makefile.in
index bc1c00b3..acd9b541 100644
--- a/server/tests/Makefile.in
+++ b/server/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -178,7 +178,18 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/bootp.Po ./$(DEPDIR)/class.Po \
+ ./$(DEPDIR)/confpars.Po ./$(DEPDIR)/db.Po ./$(DEPDIR)/ddns.Po \
+ ./$(DEPDIR)/dhcp.Po ./$(DEPDIR)/dhcpd.Po \
+ ./$(DEPDIR)/dhcpleasequery.Po ./$(DEPDIR)/dhcpv6.Po \
+ ./$(DEPDIR)/failover.Po ./$(DEPDIR)/hash_unittest.Po \
+ ./$(DEPDIR)/ldap.Po ./$(DEPDIR)/ldap_casa.Po \
+ ./$(DEPDIR)/leasechain.Po ./$(DEPDIR)/leaseq_unittest.Po \
+ ./$(DEPDIR)/load_bal_unittest.Po ./$(DEPDIR)/mdb.Po \
+ ./$(DEPDIR)/mdb6.Po ./$(DEPDIR)/mdb6_unittest.Po \
+ ./$(DEPDIR)/omapi.Po ./$(DEPDIR)/salloc.Po \
+ ./$(DEPDIR)/simple_unittest.Po ./$(DEPDIR)/stables.Po
am__mv = mv -f
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -224,7 +235,7 @@ am__recursive_targets = \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
+ distdir distdir-am
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -447,8 +458,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -489,29 +500,35 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bootp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/class.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/confpars.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddns.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpleasequery.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpv6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failover.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap_casa.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/leasechain.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/leaseq_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_bal_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdb.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdb6.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdb6_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/omapi.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/salloc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stables.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bootp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/class.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/confpars.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpleasequery.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpv6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failover.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap_casa.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/leasechain.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/leaseq_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_bal_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdb6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdb6_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/omapi.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/salloc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stables.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -878,7 +895,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -975,7 +995,29 @@ clean: clean-recursive
clean-am: clean-checkPROGRAMS clean-generic mostlyclean-am
distclean: distclean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/bootp.Po
+ -rm -f ./$(DEPDIR)/class.Po
+ -rm -f ./$(DEPDIR)/confpars.Po
+ -rm -f ./$(DEPDIR)/db.Po
+ -rm -f ./$(DEPDIR)/ddns.Po
+ -rm -f ./$(DEPDIR)/dhcp.Po
+ -rm -f ./$(DEPDIR)/dhcpd.Po
+ -rm -f ./$(DEPDIR)/dhcpleasequery.Po
+ -rm -f ./$(DEPDIR)/dhcpv6.Po
+ -rm -f ./$(DEPDIR)/failover.Po
+ -rm -f ./$(DEPDIR)/hash_unittest.Po
+ -rm -f ./$(DEPDIR)/ldap.Po
+ -rm -f ./$(DEPDIR)/ldap_casa.Po
+ -rm -f ./$(DEPDIR)/leasechain.Po
+ -rm -f ./$(DEPDIR)/leaseq_unittest.Po
+ -rm -f ./$(DEPDIR)/load_bal_unittest.Po
+ -rm -f ./$(DEPDIR)/mdb.Po
+ -rm -f ./$(DEPDIR)/mdb6.Po
+ -rm -f ./$(DEPDIR)/mdb6_unittest.Po
+ -rm -f ./$(DEPDIR)/omapi.Po
+ -rm -f ./$(DEPDIR)/salloc.Po
+ -rm -f ./$(DEPDIR)/simple_unittest.Po
+ -rm -f ./$(DEPDIR)/stables.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-local distclean-tags
@@ -1019,7 +1061,29 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/bootp.Po
+ -rm -f ./$(DEPDIR)/class.Po
+ -rm -f ./$(DEPDIR)/confpars.Po
+ -rm -f ./$(DEPDIR)/db.Po
+ -rm -f ./$(DEPDIR)/ddns.Po
+ -rm -f ./$(DEPDIR)/dhcp.Po
+ -rm -f ./$(DEPDIR)/dhcpd.Po
+ -rm -f ./$(DEPDIR)/dhcpleasequery.Po
+ -rm -f ./$(DEPDIR)/dhcpv6.Po
+ -rm -f ./$(DEPDIR)/failover.Po
+ -rm -f ./$(DEPDIR)/hash_unittest.Po
+ -rm -f ./$(DEPDIR)/ldap.Po
+ -rm -f ./$(DEPDIR)/ldap_casa.Po
+ -rm -f ./$(DEPDIR)/leasechain.Po
+ -rm -f ./$(DEPDIR)/leaseq_unittest.Po
+ -rm -f ./$(DEPDIR)/load_bal_unittest.Po
+ -rm -f ./$(DEPDIR)/mdb.Po
+ -rm -f ./$(DEPDIR)/mdb6.Po
+ -rm -f ./$(DEPDIR)/mdb6_unittest.Po
+ -rm -f ./$(DEPDIR)/omapi.Po
+ -rm -f ./$(DEPDIR)/salloc.Po
+ -rm -f ./$(DEPDIR)/simple_unittest.Po
+ -rm -f ./$(DEPDIR)/stables.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -1039,19 +1103,19 @@ uninstall-am:
.MAKE: $(am__recursive_targets) check-am install-am install-strip
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-checkPROGRAMS clean-generic cscopelist-am \
- ctags ctags-am distclean distclean-compile distclean-generic \
- distclean-local distclean-tags distdir dvi dvi-am html html-am \
- info info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-checkPROGRAMS \
+ clean-generic cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-local \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 44464af6..7e94dfd1 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -120,7 +120,8 @@ am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/t_api.Po ./$(DEPDIR)/t_api_dhcp.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -338,8 +339,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -369,8 +370,14 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -438,7 +445,10 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -508,7 +518,8 @@ clean: clean-am
clean-am: clean-checkLIBRARIES clean-generic mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/t_api.Po
+ -rm -f ./$(DEPDIR)/t_api_dhcp.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -554,7 +565,8 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/t_api.Po
+ -rm -f ./$(DEPDIR)/t_api_dhcp.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -574,7 +586,7 @@ uninstall-am:
.MAKE: check-am install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-checkLIBRARIES clean-generic cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-tags distdir dvi dvi-am html html-am info info-am \