diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-03-02 19:04:49 +0000 |
---|---|---|
committer | <> | 2015-05-08 15:30:59 +0000 |
commit | f800382616186a5d30e28d8b2c51e97a9a8360f2 (patch) | |
tree | 0d5270190548a37223d14b54383ce8a3d3af5302 /omapip | |
download | isc-dhcp-tarball-master.tar.gz |
Imported from /home/lorry/working-area/delta_isc-dhcp-tarball/dhcp-4.2.8.tar.gz.HEADdhcp-4.2.8master
Diffstat (limited to 'omapip')
-rw-r--r-- | omapip/Makefile.am | 14 | ||||
-rw-r--r-- | omapip/Makefile.in | 691 | ||||
-rw-r--r-- | omapip/alloc.c | 1165 | ||||
-rw-r--r-- | omapip/array.c | 156 | ||||
-rw-r--r-- | omapip/auth.c | 278 | ||||
-rw-r--r-- | omapip/buffer.c | 716 | ||||
-rw-r--r-- | omapip/connection.c | 1104 | ||||
-rw-r--r-- | omapip/convert.c | 179 | ||||
-rw-r--r-- | omapip/dispatch.c | 959 | ||||
-rw-r--r-- | omapip/errwarn.c | 363 | ||||
-rw-r--r-- | omapip/generic.c | 299 | ||||
-rw-r--r-- | omapip/handle.c | 302 | ||||
-rw-r--r-- | omapip/hash.c | 560 | ||||
-rw-r--r-- | omapip/inet_addr.c | 135 | ||||
-rw-r--r-- | omapip/isclib.c | 276 | ||||
-rw-r--r-- | omapip/iscprint.c | 539 | ||||
-rw-r--r-- | omapip/listener.c | 484 | ||||
-rw-r--r-- | omapip/message.c | 762 | ||||
-rw-r--r-- | omapip/omapi.3 | 249 | ||||
-rw-r--r-- | omapip/protocol.c | 1314 | ||||
-rw-r--r-- | omapip/result.c | 86 | ||||
-rw-r--r-- | omapip/support.c | 853 | ||||
-rw-r--r-- | omapip/test.c | 110 | ||||
-rw-r--r-- | omapip/toisc.c | 211 | ||||
-rw-r--r-- | omapip/trace.c | 719 |
25 files changed, 12524 insertions, 0 deletions
diff --git a/omapip/Makefile.am b/omapip/Makefile.am new file mode 100644 index 0000000..595950a --- /dev/null +++ b/omapip/Makefile.am @@ -0,0 +1,14 @@ +lib_LIBRARIES = libomapi.a +noinst_PROGRAMS = svtest + +libomapi_a_SOURCES = protocol.c buffer.c alloc.c result.c connection.c \ + errwarn.c listener.c dispatch.c generic.c support.c \ + handle.c message.c convert.c hash.c auth.c inet_addr.c \ + array.c trace.c toisc.c iscprint.c isclib.c + +man_MANS = omapi.3 +EXTRA_DIST = $(man_MANS) + +svtest_SOURCES = test.c +svtest_LDADD = libomapi.a ../bind/lib/libdns.a ../bind/lib/libisc.a + diff --git a/omapip/Makefile.in b/omapip/Makefile.in new file mode 100644 index 0000000..663dd49 --- /dev/null +++ b/omapip/Makefile.in @@ -0,0 +1,691 @@ +# Makefile.in generated by automake 1.14 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ +noinst_PROGRAMS = svtest$(EXEEXT) +subdir = omapip +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp +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) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/includes/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +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; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" +LIBRARIES = $(lib_LIBRARIES) +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libomapi_a_AR = $(AR) $(ARFLAGS) +libomapi_a_LIBADD = +am_libomapi_a_OBJECTS = protocol.$(OBJEXT) buffer.$(OBJEXT) \ + alloc.$(OBJEXT) result.$(OBJEXT) connection.$(OBJEXT) \ + errwarn.$(OBJEXT) listener.$(OBJEXT) dispatch.$(OBJEXT) \ + generic.$(OBJEXT) support.$(OBJEXT) handle.$(OBJEXT) \ + message.$(OBJEXT) convert.$(OBJEXT) hash.$(OBJEXT) \ + auth.$(OBJEXT) inet_addr.$(OBJEXT) array.$(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 ../bind/lib/libdns.a \ + ../bind/lib/libisc.a +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__depfiles_maybe = depfiles +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 = $(libomapi_a_SOURCES) $(svtest_SOURCES) +DIST_SOURCES = $(libomapi_a_SOURCES) $(svtest_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +man3dir = $(mandir)/man3 +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 +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +ATF_BIN = @ATF_BIN@ +ATF_CFLAGS = @ATF_CFLAGS@ +ATF_LDFLAGS = @ATF_LDFLAGS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +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@ +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@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +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@ +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@ +lib_LIBRARIES = libomapi.a +libomapi_a_SOURCES = protocol.c buffer.c alloc.c result.c connection.c \ + errwarn.c listener.c dispatch.c generic.c support.c \ + handle.c message.c convert.c hash.c auth.c inet_addr.c \ + array.c trace.c toisc.c iscprint.c isclib.c + +man_MANS = omapi.3 +EXTRA_DIST = $(man_MANS) +svtest_SOURCES = test.c +svtest_LDADD = libomapi.a ../bind/lib/libdns.a ../bind/lib/libisc.a +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 omapip/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign omapip/Makefile +.PRECIOUS: 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__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + 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-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) + +libomapi.a: $(libomapi_a_OBJECTS) $(libomapi_a_DEPENDENCIES) $(EXTRA_libomapi_a_DEPENDENCIES) + $(AM_V_at)-rm -f 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) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +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@ + +.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-man3: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man3dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man3dir)" || 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 '/\.3[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,^[^3][0-9a-z]*$$,3,;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)$(man3dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$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)$(man3dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \ + done; } + +uninstall-man3: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man3dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.3[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man3dir)'; $(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: $(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 $(LIBRARIES) $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)"; 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-libLIBRARIES clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -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-libLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man3 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -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-libLIBRARIES uninstall-man + +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 + + +# 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/omapip/alloc.c b/omapip/alloc.c new file mode 100644 index 0000000..a70fe21 --- /dev/null +++ b/omapip/alloc.c @@ -0,0 +1,1165 @@ +/* alloc.c + + Functions supporting memory allocation for the object management + protocol... */ + +/* + * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +struct dmalloc_preamble *dmalloc_list; +unsigned long dmalloc_outstanding; +unsigned long dmalloc_longterm; +unsigned long dmalloc_generation; +unsigned long dmalloc_cutoff_generation; +#endif + +#if defined (DEBUG_RC_HISTORY) +struct rc_history_entry rc_history [RC_HISTORY_MAX]; +int rc_history_index; +int rc_history_count; +#endif + +#if defined (DEBUG_RC_HISTORY) +static void print_rc_hist_entry (int); +#endif + +void * +dmalloc(unsigned size, const char *file, int line) { + unsigned char *foo; + unsigned len; + void **bar; +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + int i; + struct dmalloc_preamble *dp; +#endif + + len = size + DMDSIZE; + if (len < size) + return NULL; + + foo = malloc(len); + + if (!foo) + return NULL; + bar = (void *)(foo + DMDOFFSET); + memset (bar, 0, size); + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + dp = (struct dmalloc_preamble *)foo; + dp -> prev = dmalloc_list; + if (dmalloc_list) + dmalloc_list -> next = dp; + dmalloc_list = dp; + dp -> next = (struct dmalloc_preamble *)0; + dp -> size = size; + dp -> file = file; + dp -> line = line; + dp -> generation = dmalloc_generation++; + dmalloc_outstanding += size; + for (i = 0; i < DMLFSIZE; i++) + dp -> low_fence [i] = + (((unsigned long) + (&dp -> low_fence [i])) % 143) + 113; + for (i = DMDOFFSET; i < DMDSIZE; i++) + foo [i + size] = + (((unsigned long) + (&foo [i + size])) % 143) + 113; +#if defined (DEBUG_MALLOC_POOL_EXHAUSTIVELY) + /* Check _every_ entry in the pool! Very expensive. */ + for (dp = dmalloc_list; dp; dp = dp -> prev) { + for (i = 0; i < DMLFSIZE; i++) { + if (dp -> low_fence [i] != + (((unsigned long) + (&dp -> low_fence [i])) % 143) + 113) + { + log_error ("malloc fence modified: %s(%d)", + dp -> file, dp -> line); + abort (); + } + } + foo = (unsigned char *)dp; + for (i = DMDOFFSET; i < DMDSIZE; i++) { + if (foo [i + dp -> size] != + (((unsigned long) + (&foo [i + dp -> size])) % 143) + 113) { + log_error ("malloc fence modified: %s(%d)", + dp -> file, dp -> line); + abort (); + } + } + } +#endif +#endif +#ifdef DEBUG_REFCNT_DMALLOC_FREE + rc_register (file, line, 0, foo + DMDOFFSET, 1, 0, RC_MALLOC); +#endif + return bar; +} + +void +dfree(void *ptr, const char *file, int line) { + if (!ptr) { + log_error ("dfree %s(%d): free on null pointer.", file, line); + return; + } +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + { + unsigned char *bar = ptr; + struct dmalloc_preamble *dp, *cur; + int i; + bar -= DMDOFFSET; + cur = (struct dmalloc_preamble *)bar; + for (dp = dmalloc_list; dp; dp = dp -> prev) + if (dp == cur) + break; + if (!dp) { + log_error ("%s(%d): freeing unknown memory: %lx", + file, line, (unsigned long)cur); + abort (); + } + if (dp -> prev) + dp -> prev -> next = dp -> next; + if (dp -> next) + dp -> next -> prev = dp -> prev; + if (dp == dmalloc_list) + dmalloc_list = dp -> prev; + if (dp -> generation >= dmalloc_cutoff_generation) + dmalloc_outstanding -= dp -> size; + else + dmalloc_longterm -= dp -> size; + + for (i = 0; i < DMLFSIZE; i++) { + if (dp -> low_fence [i] != + (((unsigned long) + (&dp -> low_fence [i])) % 143) + 113) + { + log_error ("malloc fence modified: %s(%d)", + dp -> file, dp -> line); + abort (); + } + } + for (i = DMDOFFSET; i < DMDSIZE; i++) { + if (bar [i + dp -> size] != + (((unsigned long) + (&bar [i + dp -> size])) % 143) + 113) { + log_error ("malloc fence modified: %s(%d)", + dp -> file, dp -> line); + abort (); + } + } + ptr = bar; + } +#endif +#ifdef DEBUG_REFCNT_DMALLOC_FREE + rc_register (file, line, + 0, (unsigned char *)ptr + DMDOFFSET, 0, 1, RC_MALLOC); +#endif + free (ptr); +} + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +/* For allocation functions that keep their own free lists, we want to + account for the reuse of the memory. */ + +void +dmalloc_reuse(void *foo, const char *file, int line, int justref) { + struct dmalloc_preamble *dp; + + /* Get the pointer to the dmalloc header. */ + dp = foo; + dp--; + + /* If we just allocated this and are now referencing it, this + function would almost be a no-op, except that it would + increment the generation count needlessly. So just return + in this case. */ + if (dp -> generation == dmalloc_generation) + return; + + /* If this is longterm data, and we just made reference to it, + don't put it on the short-term list or change its name - + we don't need to know about this. */ + if (dp -> generation < dmalloc_cutoff_generation && justref) + return; + + /* Take it out of the place in the allocated list where it was. */ + if (dp -> prev) + dp -> prev -> next = dp -> next; + if (dp -> next) + dp -> next -> prev = dp -> prev; + if (dp == dmalloc_list) + dmalloc_list = dp -> prev; + + /* Account for its removal. */ + if (dp -> generation >= dmalloc_cutoff_generation) + dmalloc_outstanding -= dp -> size; + else + dmalloc_longterm -= dp -> size; + + /* Now put it at the head of the list. */ + dp -> prev = dmalloc_list; + if (dmalloc_list) + dmalloc_list -> next = dp; + dmalloc_list = dp; + dp -> next = (struct dmalloc_preamble *)0; + + /* Change the reference location information. */ + dp -> file = file; + dp -> line = line; + + /* Increment the generation. */ + dp -> generation = dmalloc_generation++; + + /* Account for it. */ + dmalloc_outstanding += dp -> size; +} + +void dmalloc_dump_outstanding () +{ + static unsigned long dmalloc_cutoff_point; + struct dmalloc_preamble *dp; +#if defined(DEBUG_MALLOC_POOL) + unsigned char *foo; + int i; +#endif + + if (!dmalloc_cutoff_point) + dmalloc_cutoff_point = dmalloc_cutoff_generation; + for (dp = dmalloc_list; dp; dp = dp -> prev) { + if (dp -> generation <= dmalloc_cutoff_point) + break; +#if defined (DEBUG_MALLOC_POOL) + for (i = 0; i < DMLFSIZE; i++) { + if (dp -> low_fence [i] != + (((unsigned long) + (&dp -> low_fence [i])) % 143) + 113) + { + log_error ("malloc fence modified: %s(%d)", + dp -> file, dp -> line); + abort (); + } + } + foo = (unsigned char *)dp; + for (i = DMDOFFSET; i < DMDSIZE; i++) { + if (foo [i + dp -> size] != + (((unsigned long) + (&foo [i + dp -> size])) % 143) + 113) { + log_error ("malloc fence modified: %s(%d)", + dp -> file, dp -> line); + abort (); + } + } +#endif +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + /* Don't count data that's actually on a free list + somewhere. */ + if (dp -> file) { +#if defined (DEBUG_RC_HISTORY) + int i, count, inhistory = 0, noted = 0; + + /* If we have the info, see if this is actually + new garbage. */ + if (rc_history_count < RC_HISTORY_MAX) { + count = rc_history_count; + } else + count = RC_HISTORY_MAX; + i = rc_history_index - 1; + if (i < 0) + i += RC_HISTORY_MAX; + + do { + if (rc_history [i].addr == dp + 1) { + inhistory = 1; + if (!noted) { + log_info (" %s(%d): %ld", dp -> file, + dp -> line, (long) dp -> size); + noted = 1; + } + print_rc_hist_entry (i); + if (!rc_history [i].refcnt) + break; + } + if (--i < 0) + i = RC_HISTORY_MAX - 1; + } while (count--); + if (!inhistory) +#endif + log_info (" %s(%d): %ld", + dp -> file, dp -> line, + (long) dp -> size); + } +#endif + } + if (dmalloc_list) + dmalloc_cutoff_point = dmalloc_list -> generation; +} +#endif /* DEBUG_MEMORY_LEAKAGE || DEBUG_MALLOC_POOL */ + +#if defined (DEBUG_RC_HISTORY) +static void print_rc_hist_entry (int i) +{ + log_info (" referenced by %s(%d)[%lx]: addr = %lx refcnt = %x", + rc_history [i].file, rc_history [i].line, + (unsigned long)rc_history [i].reference, + (unsigned long)rc_history [i].addr, + rc_history [i].refcnt); +} + +void dump_rc_history (void *addr) +{ + int i; + + i = rc_history_index; + if (!rc_history [i].file) + i = 0; + else if (rc_history_count < RC_HISTORY_MAX) { + i -= rc_history_count; + if (i < 0) + i += RC_HISTORY_MAX; + } + rc_history_count = 0; + + while (rc_history [i].file) { + if (!addr || addr == rc_history [i].addr) + print_rc_hist_entry (i); + ++i; + if (i == RC_HISTORY_MAX) + i = 0; + if (i == rc_history_index) + break; + } +} +void rc_history_next (int d) +{ +#if defined (RC_HISTORY_COMPRESSION) + int i, j = 0, m, n = 0; + void *ap, *rp; + + /* If we are decreasing the reference count, try to find the + entry where the reference was made and eliminate it; then + we can also eliminate this reference. */ + if (d) { + m = rc_history_index - 1000; + if (m < -1) + m = -1; + ap = rc_history [rc_history_index].addr; + rp = rc_history [rc_history_index].reference; + for (i = rc_history_index - 1; i > m; i--) { + if (rc_history [i].addr == ap) { + if (rc_history [i].reference == rp) { + if (n > 10) { + for (n = i; n <= rc_history_index; n++) + print_rc_hist_entry (n); + n = 11; + } + memmove (&rc_history [i], + &rc_history [i + 1], + (unsigned)((rc_history_index - i) * + sizeof (struct rc_history_entry))); + --rc_history_count; + --rc_history_index; + for (j = i; j < rc_history_count; j++) { + if (rc_history [j].addr == ap) + --rc_history [j].refcnt; + } + if (n > 10) { + for (n = i; n <= rc_history_index; n++) + print_rc_hist_entry (n); + n = 11; + exit (0); + } + return; + } + } + } + } +#endif + if (++rc_history_index == RC_HISTORY_MAX) + rc_history_index = 0; + ++rc_history_count; +} +#endif /* DEBUG_RC_HISTORY */ + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +struct caller { + struct dmalloc_preamble *dp; + int count; +}; + +static int dmalloc_find_entry (struct dmalloc_preamble *dp, + struct caller *array, + int min, int max) +{ + int middle; + + middle = (min + max) / 2; + if (middle == min) + return middle; + if (array [middle].dp -> file == dp -> file) { + if (array [middle].dp -> line == dp -> line) + return middle; + else if (array [middle].dp -> line < dp -> line) + return dmalloc_find_entry (dp, array, middle, max); + else + return dmalloc_find_entry (dp, array, 0, middle); + } else if (array [middle].dp -> file < dp -> file) + return dmalloc_find_entry (dp, array, middle, max); + else + return dmalloc_find_entry (dp, array, 0, middle); +} + +void omapi_print_dmalloc_usage_by_caller () +{ + struct dmalloc_preamble *dp; + int ccur, cmax, i; + struct caller cp [1024]; + + cmax = 1024; + ccur = 0; + + memset (cp, 0, sizeof cp); + for (dp = dmalloc_list; dp; dp = dp -> prev) { + i = dmalloc_find_entry (dp, cp, 0, ccur); + if ((i == ccur || + cp [i].dp -> file != dp -> file || + cp [i].dp -> line != dp -> line) && + ccur == cmax) { + log_error ("no space for memory usage summary."); + return; + } + if (i == ccur) { + cp [ccur++].dp = dp; + cp [i].count = 1; + } else if (cp [i].dp -> file < dp -> file || + (cp [i].dp -> file == dp -> file && + cp [i].dp -> line < dp -> line)) { + if (i + 1 != ccur) + memmove (cp + i + 2, cp + i + 1, + (ccur - i) * sizeof *cp); + cp [i + 1].dp = dp; + cp [i + 1].count = 1; + ccur++; + } else if (cp [i].dp -> file != dp -> file || + cp [i].dp -> line != dp -> line) { + memmove (cp + i + 1, + cp + i, (ccur - i) * sizeof *cp); + cp [i].dp = dp; + cp [i].count = 1; + ccur++; + } else + cp [i].count++; +#if 0 + printf ("%d\t%s:%d\n", i, dp -> file, dp -> line); + dump_rc_history (dp + 1); +#endif + } + for (i = 0; i < ccur; i++) { + printf ("%d\t%s:%d\t%d\n", i, + cp [i].dp -> file, cp [i].dp -> line, cp [i].count); +#if defined(DUMP_RC_HISTORY) + dump_rc_history (cp [i].dp + 1); +#endif + } +} +#endif /* DEBUG_MEMORY_LEAKAGE || DEBUG_MALLOC_POOL */ + +isc_result_t omapi_object_allocate (omapi_object_t **o, + omapi_object_type_t *type, + size_t size, + const char *file, int line) +{ + size_t tsize; + omapi_object_t *foo; + isc_result_t status; + + if (type -> allocator) { + foo = (omapi_object_t *)0; + status = (*type -> allocator) (&foo, file, line); + tsize = type -> size; + } else { + status = ISC_R_NOMEMORY; + tsize = 0; + } + + if (status == ISC_R_NOMEMORY) { + if (type -> sizer) + tsize = (*type -> sizer) (size); + else + tsize = type -> size; + + /* Sanity check. */ + if (tsize < sizeof (omapi_object_t)) + return DHCP_R_INVALIDARG; + + foo = dmalloc (tsize, file, line); + if (!foo) + return ISC_R_NOMEMORY; + } + + status = omapi_object_initialize (foo, type, size, tsize, file, line); + if (status != ISC_R_SUCCESS) { + if (type -> freer) + (*type -> freer) (foo, file, line); + else + dfree (foo, file, line); + return status; + } + return omapi_object_reference (o, foo, file, line); +} + +isc_result_t omapi_object_initialize (omapi_object_t *o, + omapi_object_type_t *type, + size_t usize, size_t psize, + const char *file, int line) +{ + memset (o, 0, psize); + o -> type = type; + if (type -> initialize) + (*type -> initialize) (o, file, line); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_object_reference (omapi_object_t **r, + omapi_object_t *h, + const char *file, int line) +{ + if (!h || !r) + return DHCP_R_INVALIDARG; + + if (*r) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): reference store into non-null pointer!", + file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + rc_register (file, line, r, h, h -> refcnt, 0, h -> type -> rc_flag); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_object_dereference (omapi_object_t **h, + const char *file, int line) +{ + int outer_reference = 0; + int inner_reference = 0; + int handle_reference = 0; + int extra_references; + omapi_object_t *p, *hp; + + if (!h) + return DHCP_R_INVALIDARG; + + if (!*h) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of pointer with refcnt of zero!", + file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*h); +#endif + abort (); +#else + *h = 0; + return DHCP_R_INVALIDARG; +#endif + } + + /* See if this object's inner object refers to it, but don't + count this as a reference if we're being asked to free the + reference from the inner object. */ + if ((*h) -> inner && (*h) -> inner -> outer && + h != &((*h) -> inner -> outer)) + inner_reference = 1; + + /* Ditto for the outer object. */ + if ((*h) -> outer && (*h) -> outer -> inner && + h != &((*h) -> outer -> inner)) + outer_reference = 1; + + /* Ditto for the outer object. The code below assumes that + the only reason we'd get a dereference from the handle + table is if this function does it - otherwise we'd have to + traverse the handle table to find the address where the + reference is stored and compare against that, and we don't + want to do that if we can avoid it. */ + if ((*h) -> handle) + handle_reference = 1; + + /* If we are getting rid of the last reference other than + references to inner and outer objects, or from the handle + table, then we must examine all the objects in either + direction to see if they hold any non-inner, non-outer, + non-handle-table references. If not, we need to free the + entire chain of objects. */ + if ((*h) -> refcnt == + inner_reference + outer_reference + handle_reference + 1) { + if (inner_reference || outer_reference || handle_reference) { + /* XXX we could check for a reference from the + handle table here. */ + extra_references = 0; + for (p = (*h) -> inner; + p && !extra_references; p = p -> inner) { + extra_references += p -> refcnt; + if (p -> inner && p -> inner -> outer == p) + --extra_references; + if (p -> outer) + --extra_references; + if (p -> handle) + --extra_references; + } + for (p = (*h) -> outer; + p && !extra_references; p = p -> outer) { + extra_references += p -> refcnt; + if (p -> outer && p -> outer -> inner == p) + --extra_references; + if (p -> inner) + --extra_references; + if (p -> handle) + --extra_references; + } + } else + extra_references = 0; + + if (!extra_references) { + hp = *h; + *h = 0; + hp -> refcnt--; + if (inner_reference) + omapi_object_dereference + (&hp -> inner, file, line); + if (outer_reference) + omapi_object_dereference + (&hp -> outer, file, line); +/* if (!hp -> type -> freer) */ + rc_register (file, line, h, hp, + 0, 1, hp -> type -> rc_flag); + if (handle_reference) { + if (omapi_handle_clear(hp->handle) != + ISC_R_SUCCESS) { + log_debug("Attempt to clear null " + "handle pointer"); + } + } + if (hp -> type -> destroy) + (*(hp -> type -> destroy)) (hp, file, line); + if (hp -> type -> freer) + (hp -> type -> freer (hp, file, line)); + else + dfree (hp, file, line); + } else { + (*h) -> refcnt--; +/* if (!(*h) -> type -> freer) */ + rc_register (file, line, + h, *h, (*h) -> refcnt, 1, + (*h) -> type -> rc_flag); + } + } else { + (*h) -> refcnt--; +/* if (!(*h) -> type -> freer) */ + rc_register (file, line, h, *h, (*h) -> refcnt, 1, + (*h) -> type -> rc_flag); + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_buffer_new (omapi_buffer_t **h, + const char *file, int line) +{ + omapi_buffer_t *t; + isc_result_t status; + + t = (omapi_buffer_t *)dmalloc (sizeof *t, file, line); + if (!t) + return ISC_R_NOMEMORY; + memset (t, 0, sizeof *t); + status = omapi_buffer_reference (h, t, file, line); + if (status != ISC_R_SUCCESS) + dfree (t, file, line); + (*h) -> head = sizeof ((*h) -> buf) - 1; + return status; +} + +isc_result_t omapi_buffer_reference (omapi_buffer_t **r, + omapi_buffer_t *h, + const char *file, int line) +{ + if (!h || !r) + return DHCP_R_INVALIDARG; + + if (*r) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): reference store into non-null pointer!", + file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_buffer_dereference (omapi_buffer_t **h, + const char *file, int line) +{ + if (!h) + return DHCP_R_INVALIDARG; + + if (!*h) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of pointer with refcnt of zero!", + file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*h); +#endif + abort (); +#else + *h = 0; + return DHCP_R_INVALIDARG; +#endif + } + + --(*h) -> refcnt; + rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); + if ((*h) -> refcnt == 0) + dfree (*h, file, line); + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_typed_data_new (const char *file, int line, + omapi_typed_data_t **t, + omapi_datatype_t type, ...) +{ + va_list l; + omapi_typed_data_t *new; + unsigned len; + unsigned val = 0; + int intval = 0; + char *s = NULL; + isc_result_t status; + omapi_object_t *obj = NULL; + + va_start (l, type); + + switch (type) { + case omapi_datatype_int: + len = OMAPI_TYPED_DATA_INT_LEN; + intval = va_arg (l, int); + break; + case omapi_datatype_string: + s = va_arg (l, char *); + val = strlen (s); + len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val; + if (len < val) { + va_end(l); + return DHCP_R_INVALIDARG; + } + break; + case omapi_datatype_data: + val = va_arg (l, unsigned); + len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val; + if (len < val) { + va_end(l); + return DHCP_R_INVALIDARG; + } + break; + case omapi_datatype_object: + len = OMAPI_TYPED_DATA_OBJECT_LEN; + obj = va_arg (l, omapi_object_t *); + break; + default: + va_end (l); + return DHCP_R_INVALIDARG; + } + va_end (l); + + new = dmalloc (len, file, line); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, len); + + switch (type) { + case omapi_datatype_int: + new -> u.integer = intval; + break; + case omapi_datatype_string: + memcpy (new -> u.buffer.value, s, val); + new -> u.buffer.len = val; + break; + case omapi_datatype_data: + new -> u.buffer.len = val; + break; + case omapi_datatype_object: + status = omapi_object_reference (&new -> u.object, obj, + file, line); + if (status != ISC_R_SUCCESS) { + dfree (new, file, line); + return status; + } + break; + } + new -> type = type; + + return omapi_typed_data_reference (t, new, file, line); +} + +isc_result_t omapi_typed_data_reference (omapi_typed_data_t **r, + omapi_typed_data_t *h, + const char *file, int line) +{ + if (!h || !r) + return DHCP_R_INVALIDARG; + + if (*r) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): reference store into non-null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **h, + const char *file, int line) +{ + if (!h) + return DHCP_R_INVALIDARG; + + if (!*h) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of pointer with refcnt of zero!", + file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*h); +#endif + abort (); +#else + *h = 0; + return DHCP_R_INVALIDARG; +#endif + } + + --((*h) -> refcnt); + rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); + if ((*h) -> refcnt <= 0 ) { + switch ((*h) -> type) { + case omapi_datatype_int: + case omapi_datatype_string: + case omapi_datatype_data: + default: + break; + case omapi_datatype_object: + omapi_object_dereference (&(*h) -> u.object, + file, line); + break; + } + dfree (*h, file, line); + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_data_string_new (omapi_data_string_t **d, unsigned len, + const char *file, int line) +{ + omapi_data_string_t *new; + unsigned nlen; + + nlen = OMAPI_DATA_STRING_EMPTY_SIZE + len; + if (nlen < len) + return DHCP_R_INVALIDARG; + new = dmalloc (nlen, file, line); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, OMAPI_DATA_STRING_EMPTY_SIZE); + new -> len = len; + return omapi_data_string_reference (d, new, file, line); +} + +isc_result_t omapi_data_string_reference (omapi_data_string_t **r, + omapi_data_string_t *h, + const char *file, int line) +{ + if (!h || !r) + return DHCP_R_INVALIDARG; + + if (*r) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): reference store into non-null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_data_string_dereference (omapi_data_string_t **h, + const char *file, int line) +{ + if (!h) + return DHCP_R_INVALIDARG; + + if (!*h) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of pointer with refcnt of zero!", + file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*h); +#endif + abort (); +#else + *h = 0; + return DHCP_R_INVALIDARG; +#endif + } + + --((*h) -> refcnt); + rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); + if ((*h) -> refcnt <= 0 ) { + dfree (*h, file, line); + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_value_new (omapi_value_t **d, + const char *file, int line) +{ + omapi_value_t *new; + + new = dmalloc (sizeof *new, file, line); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, sizeof *new); + return omapi_value_reference (d, new, file, line); +} + +isc_result_t omapi_value_reference (omapi_value_t **r, + omapi_value_t *h, + const char *file, int line) +{ + if (!h || !r) + return DHCP_R_INVALIDARG; + + if (*r) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): reference store into non-null pointer!", + file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_value_dereference (omapi_value_t **h, + const char *file, int line) +{ + if (!h) + return DHCP_R_INVALIDARG; + + if (!*h) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of pointer with refcnt of zero!", + file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*h); +#endif + abort (); +#else + *h = 0; + return DHCP_R_INVALIDARG; +#endif + } + + --((*h) -> refcnt); + rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); + if ((*h) -> refcnt == 0) { + if ((*h) -> name) + omapi_data_string_dereference (&(*h) -> name, + file, line); + if ((*h) -> value) + omapi_typed_data_dereference (&(*h) -> value, + file, line); + dfree (*h, file, line); + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_addr_list_new (omapi_addr_list_t **d, unsigned count, + const char *file, int line) +{ + omapi_addr_list_t *new; + + new = dmalloc ((count * sizeof (omapi_addr_t)) + + sizeof (omapi_addr_list_t), file, line); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, ((count * sizeof (omapi_addr_t)) + + sizeof (omapi_addr_list_t))); + new -> count = count; + new -> addresses = (omapi_addr_t *)(new + 1); + return omapi_addr_list_reference (d, new, file, line); +} + +isc_result_t omapi_addr_list_reference (omapi_addr_list_t **r, + omapi_addr_list_t *h, + const char *file, int line) +{ + if (!h || !r) + return DHCP_R_INVALIDARG; + + if (*r) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): reference store into non-null pointer!", + file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_addr_list_dereference (omapi_addr_list_t **h, + const char *file, int line) +{ + if (!h) + return DHCP_R_INVALIDARG; + + if (!*h) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of null pointer!", file, line); + abort (); +#else + return DHCP_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (POINTER_DEBUG) + log_error ("%s(%d): dereference of pointer with zero refcnt!", + file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (*h); +#endif + abort (); +#else + *h = 0; + return DHCP_R_INVALIDARG; +#endif + } + + --((*h) -> refcnt); + rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC); + if ((*h) -> refcnt <= 0 ) { + dfree (*h, file, line); + } + *h = 0; + return ISC_R_SUCCESS; +} + diff --git a/omapip/array.c b/omapip/array.c new file mode 100644 index 0000000..5e67d27 --- /dev/null +++ b/omapip/array.c @@ -0,0 +1,156 @@ +/* listener.c + + Subroutines that support the omapi extensible array type. */ + +/* + * Copyright (c) 2004-2007,2009,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +/* Allocate a new extensible array. */ + +isc_result_t omapi_array_allocate (omapi_array_t **array, + omapi_array_ref_t ref, + omapi_array_deref_t deref, + const char *file, int line) +{ + omapi_array_t *aptr; + + if (!array || *array) + return DHCP_R_INVALIDARG; + aptr = dmalloc (sizeof (omapi_array_t),file, line); + if (!aptr) + return ISC_R_NOMEMORY; + *array = aptr; + aptr -> ref = ref; + aptr -> deref = deref; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_array_free (omapi_array_t **array, + const char *file, int line) +{ + omapi_array_t *aptr; + int i; + + if (!array || !*array) + return DHCP_R_INVALIDARG; + aptr = *array; + for (i = 0; i < aptr -> count; i++) + if (aptr -> data [i] && aptr -> deref) + (*aptr -> deref) (&aptr -> data [i], file, line); + dfree (aptr -> data, MDL); + dfree (aptr, MDL); + *array = (omapi_array_t *)0; + return ISC_R_SUCCESS; +} + +/* Extend the size of the array by one entry (we may allocate more than that) + and store the specified value in the new array element. */ + +isc_result_t omapi_array_extend (omapi_array_t *array, char *ptr, + int *index, const char *file, int line) +{ + isc_result_t status; + int new = array -> count; + status = omapi_array_set (array, ptr, new, file, line); + if (index && status == ISC_R_SUCCESS) + *index = new; + return status; +} + +/* Set a value in the specified array, extending it if necessary. */ + +isc_result_t omapi_array_set (omapi_array_t *array, void *ptr, int index, + const char *file, int line) +{ + char **newbuf; + int delta; + isc_result_t status; + + if (!array) + return DHCP_R_INVALIDARG; + if (!ptr) + return DHCP_R_INVALIDARG; + if (index < 0) + return DHCP_R_INVALIDARG; + + /* If the proposed index is larger than the current available + space in the array, make more space in the array. */ + if (array -> max <= index) { + delta = index - array -> max + 10; + newbuf = dmalloc ((array -> max + delta) * sizeof (char *), + file, line); + if (!newbuf) + return ISC_R_NOMEMORY; + /* Zero the new elements. */ + memset (&newbuf [array -> max], 0, (sizeof (char *)) * delta); + array -> max += delta; + /* Copy the old array data into the new buffer. */ + if (array -> data) { + memcpy (newbuf, + array -> data, array -> count * sizeof (char *)); + dfree (array -> data, file, line); + } + array -> data = newbuf; + } else { + /* If there's already data there, and this is an array + of references, dereference what's there. */ + if (array -> data [index]) { + status = ((*array -> deref) (&array -> data [index], + file, line)); + + if (status != ISC_R_SUCCESS) + return status; + } + } + + /* Store the pointer using the referencer function. We have + either just memset this to zero or dereferenced what was + there previously, so there is no need to do anything if the + pointer we have been asked to store is null. */ + if (ptr) { + status = (*array -> ref) (&array -> data [index], ptr, + file, line); + if (status != ISC_R_SUCCESS) + return status; + } + if (index >= array -> count) + array -> count = index + 1; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_array_lookup (char **ptr, omapi_array_t *array, int index, + const char *file, int line) +{ + if (!array || !ptr || *ptr || index < 0 || index >= array -> count) + return DHCP_R_INVALIDARG; + if (array -> data [index]) + return (*array -> ref) (ptr, + array -> data [index], file, line); + return ISC_R_NOTFOUND; +} + diff --git a/omapip/auth.c b/omapip/auth.c new file mode 100644 index 0000000..1f400d0 --- /dev/null +++ b/omapip/auth.c @@ -0,0 +1,278 @@ +/* auth.c + + Subroutines having to do with authentication. */ + +/* + * Copyright (c) 2009-2010,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +OMAPI_OBJECT_ALLOC (omapi_auth_key, omapi_auth_key_t, omapi_type_auth_key) +typedef struct hash omapi_auth_hash_t; +HASH_FUNCTIONS_DECL (omapi_auth_key, const char *, + omapi_auth_key_t, omapi_auth_hash_t) +omapi_auth_hash_t *auth_key_hash; +HASH_FUNCTIONS (omapi_auth_key, const char *, omapi_auth_key_t, + omapi_auth_hash_t, + omapi_auth_key_reference, omapi_auth_key_dereference, + do_case_hash) + +isc_result_t omapi_auth_key_new (omapi_auth_key_t **o, const char *file, + int line) +{ + return omapi_auth_key_allocate (o, file, line); +} + +isc_result_t omapi_auth_key_destroy (omapi_object_t *h, + const char *file, int line) +{ + omapi_auth_key_t *a; + + if (h->type != omapi_type_auth_key) + return DHCP_R_INVALIDARG; + a = (omapi_auth_key_t *)h; + + if (auth_key_hash != NULL) + omapi_auth_key_hash_delete(auth_key_hash, a->name, 0, MDL); + + if (a->name != NULL) + dfree(a->name, MDL); + if (a->algorithm != NULL) + dfree(a->algorithm, MDL); + if (a->key != NULL) + omapi_data_string_dereference(&a->key, MDL); + if (a->tsec_key != NULL) + dns_tsec_destroy(&a->tsec_key); + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_auth_key_enter (omapi_auth_key_t *a) +{ + omapi_auth_key_t *tk; + isc_result_t status; + dst_key_t *dstkey; + + if (a -> type != omapi_type_auth_key) + return DHCP_R_INVALIDARG; + + tk = (omapi_auth_key_t *)0; + if (auth_key_hash) { + omapi_auth_key_hash_lookup (&tk, auth_key_hash, + a -> name, 0, MDL); + if (tk == a) { + omapi_auth_key_dereference (&tk, MDL); + return ISC_R_SUCCESS; + } + if (tk) { + omapi_auth_key_hash_delete (auth_key_hash, + tk -> name, 0, MDL); + omapi_auth_key_dereference (&tk, MDL); + } + } else { + if (!omapi_auth_key_new_hash(&auth_key_hash, + KEY_HASH_SIZE, MDL)) + return ISC_R_NOMEMORY; + } + + /* + * If possible create a tsec structure for this key, + * if we can't create the structure we put out a warning + * and continue. + */ + status = isclib_make_dst_key(a->name, a->algorithm, + a->key->value, a->key->len, + &dstkey); + if (status == ISC_R_SUCCESS) { + status = dns_tsec_create(dhcp_gbl_ctx.mctx, dns_tsectype_tsig, + dstkey, &a->tsec_key); + dst_key_free(&dstkey); + } + if (status != ISC_R_SUCCESS) + log_error("Unable to create tsec structure for %s", a->name); + + omapi_auth_key_hash_add (auth_key_hash, a -> name, 0, a, MDL); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_auth_key_lookup_name (omapi_auth_key_t **a, + const char *name) +{ + if (!auth_key_hash) + return ISC_R_NOTFOUND; + if (!omapi_auth_key_hash_lookup (a, auth_key_hash, name, 0, MDL)) + return ISC_R_NOTFOUND; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_auth_key_lookup (omapi_object_t **h, + omapi_object_t *id, + omapi_object_t *ref) +{ + isc_result_t status; + omapi_value_t *name = (omapi_value_t *)0; + omapi_value_t *algorithm = (omapi_value_t *)0; + + if (!auth_key_hash) + return ISC_R_NOTFOUND; + + if (!ref) + return DHCP_R_NOKEYS; + + status = omapi_get_value_str (ref, id, "name", &name); + if (status != ISC_R_SUCCESS) + return status; + + if ((name -> value -> type != omapi_datatype_string) && + (name -> value -> type != omapi_datatype_data)) { + omapi_value_dereference (&name, MDL); + return ISC_R_NOTFOUND; + } + + status = omapi_get_value_str (ref, id, "algorithm", &algorithm); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (&name, MDL); + return status; + } + + if ((algorithm -> value -> type != omapi_datatype_string) && + (algorithm -> value -> type != omapi_datatype_data)) { + omapi_value_dereference (&name, MDL); + omapi_value_dereference (&algorithm, MDL); + return ISC_R_NOTFOUND; + } + + + if (!omapi_auth_key_hash_lookup ((omapi_auth_key_t **)h, auth_key_hash, + (const char *) + name -> value -> u.buffer.value, + name -> value -> u.buffer.len, MDL)) { + omapi_value_dereference (&name, MDL); + omapi_value_dereference (&algorithm, MDL); + return ISC_R_NOTFOUND; + } + + if (omapi_td_strcasecmp (algorithm -> value, + ((omapi_auth_key_t *)*h) -> algorithm) != 0) { + omapi_value_dereference (&name, MDL); + omapi_value_dereference (&algorithm, MDL); + omapi_object_dereference (h, MDL); + return ISC_R_NOTFOUND; + } + + omapi_value_dereference (&name, MDL); + omapi_value_dereference (&algorithm, MDL); + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_auth_key_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *h) +{ + omapi_auth_key_t *a; + isc_result_t status; + + if (h -> type != omapi_type_auth_key) + return DHCP_R_INVALIDARG; + a = (omapi_auth_key_t *)h; + + /* Write only the name and algorithm -- not the secret! */ + if (a -> name) { + status = omapi_connection_put_name (c, "name"); + if (status != ISC_R_SUCCESS) + return status; + status = omapi_connection_put_string (c, a -> name); + if (status != ISC_R_SUCCESS) + return status; + } + if (a -> algorithm) { + status = omapi_connection_put_name (c, "algorithm"); + if (status != ISC_R_SUCCESS) + return status; + status = omapi_connection_put_string (c, a -> algorithm); + if (status != ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_auth_key_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_auth_key_t *a; + isc_result_t status; + + if (h -> type != omapi_type_auth_key) + return ISC_R_UNEXPECTED; + a = (omapi_auth_key_t *)h; + + if (omapi_ds_strcmp (name, "name") == 0) { + if (a -> name) + return omapi_make_string_value + (value, name, a -> name, MDL); + else + return ISC_R_NOTFOUND; + } else if (omapi_ds_strcmp (name, "key") == 0) { + if (a -> key) { + status = omapi_value_new (value, MDL); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference + (&(*value) -> name, name, MDL); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (value, MDL); + return status; + } + + status = omapi_typed_data_new (MDL, &(*value) -> value, + omapi_datatype_data, + a -> key -> len); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (value, MDL); + return status; + } + + memcpy ((*value) -> value -> u.buffer.value, + a -> key -> value, a -> key -> len); + return ISC_R_SUCCESS; + } else + return ISC_R_NOTFOUND; + } else if (omapi_ds_strcmp (name, "algorithm") == 0) { + if (a -> algorithm) + return omapi_make_string_value + (value, name, a -> algorithm, MDL); + else + return ISC_R_NOTFOUND; + } + + return ISC_R_SUCCESS; +} diff --git a/omapip/buffer.c b/omapip/buffer.c new file mode 100644 index 0000000..36bc8b9 --- /dev/null +++ b/omapip/buffer.c @@ -0,0 +1,716 @@ +/* buffer.c + + Buffer access functions for the object management protocol... */ + +/* + * Copyright (c) 2009,2012-2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include <errno.h> + +#if defined (TRACING) +static void trace_connection_input_input (trace_type_t *, unsigned, char *); +static void trace_connection_input_stop (trace_type_t *); +static void trace_connection_output_input (trace_type_t *, unsigned, char *); +static void trace_connection_output_stop (trace_type_t *); +static trace_type_t *trace_connection_input; +static trace_type_t *trace_connection_output; +static isc_result_t omapi_connection_reader_trace (omapi_object_t *, + unsigned, char *, + unsigned *); +extern omapi_array_t *omapi_connections; + +void omapi_buffer_trace_setup () +{ + trace_connection_input = + trace_type_register ("connection-input", + (void *)0, + trace_connection_input_input, + trace_connection_input_stop, MDL); + trace_connection_output = + trace_type_register ("connection-output", + (void *)0, + trace_connection_output_input, + trace_connection_output_stop, MDL); +} + +static void trace_connection_input_input (trace_type_t *ttype, + unsigned length, char *buf) +{ + unsigned left, taken, cc = 0; + char *s; + int32_t connect_index; + isc_result_t status; + omapi_connection_object_t *c = (omapi_connection_object_t *)0; + + memcpy (&connect_index, buf, sizeof connect_index); + connect_index = ntohl (connect_index); + + omapi_array_foreach_begin (omapi_connections, + omapi_connection_object_t, lp) { + if (lp -> index == ntohl (connect_index)) { + omapi_connection_reference (&c, lp, MDL); + omapi_connection_dereference (&lp, MDL); + break; + } + } omapi_array_foreach_end (omapi_connections, + omapi_connection_object_t, lp); + + if (!c) { + log_error ("trace connection input: no connection index %ld", + (long int)connect_index); + return; + } + + s = buf + sizeof connect_index; + left = length - sizeof connect_index; + + while (left) { + taken = 0; + status = omapi_connection_reader_trace ((omapi_object_t *)c, + left, s, &taken); + if (status != ISC_R_SUCCESS) { + log_error ("trace connection input: %s", + isc_result_totext (status)); + break; + } + if (!taken) { + if (cc > 0) { + log_error ("trace connection_input: %s", + "input is not being consumed."); + break; + } + cc++; + } else { + cc = 0; + left -= taken; + } + } + omapi_connection_dereference (&c, MDL); +} + +static void trace_connection_input_stop (trace_type_t *ttype) { } + +static void trace_connection_output_input (trace_type_t *ttype, + unsigned length, char *buf) +{ + /* We *could* check to see if the output is correct, but for now + we aren't going to do that. */ +} + +static void trace_connection_output_stop (trace_type_t *ttype) { } + +#endif + +/* Make sure that at least len bytes are in the input buffer, and if not, + read enough bytes to make up the difference. */ + +isc_result_t omapi_connection_reader (omapi_object_t *h) +{ +#if defined (TRACING) + return omapi_connection_reader_trace (h, 0, (char *)0, (unsigned *)0); +} + +static isc_result_t omapi_connection_reader_trace (omapi_object_t *h, + unsigned stuff_len, + char *stuff_buf, + unsigned *stuff_taken) +{ +#endif + omapi_buffer_t *buffer; + isc_result_t status; + unsigned read_len; + int read_status; + omapi_connection_object_t *c; + unsigned bytes_to_read; + + if (!h || h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + /* See if there are enough bytes. */ + if (c -> in_bytes >= OMAPI_BUF_SIZE - 1 && + c -> in_bytes > c -> bytes_needed) + return ISC_R_SUCCESS; + + + if (c -> inbufs) { + for (buffer = c -> inbufs; buffer -> next; + buffer = buffer -> next) + ; + if (!BUFFER_BYTES_FREE (buffer)) { + status = omapi_buffer_new (&buffer -> next, MDL); + if (status != ISC_R_SUCCESS) + return status; + buffer = buffer -> next; + } + } else { + status = omapi_buffer_new (&c -> inbufs, MDL); + if (status != ISC_R_SUCCESS) + return status; + buffer = c -> inbufs; + } + + bytes_to_read = BUFFER_BYTES_FREE (buffer); + + while (bytes_to_read) { + if (buffer -> tail > buffer -> head) + read_len = sizeof (buffer -> buf) - buffer -> tail; + else + read_len = buffer -> head - buffer -> tail; + +#if defined (TRACING) + if (trace_playback()) { + if (stuff_len) { + if (read_len > stuff_len) + read_len = stuff_len; + if (stuff_taken) + *stuff_taken += read_len; + memcpy (&buffer -> buf [buffer -> tail], + stuff_buf, read_len); + stuff_len -= read_len; + stuff_buf += read_len; + read_status = read_len; + } else { + break; + } + } else +#endif + { + read_status = read (c -> socket, + &buffer -> buf [buffer -> tail], + read_len); + } + if (read_status < 0) { + if (errno == EWOULDBLOCK) + break; + else if (errno == EIO) + return ISC_R_IOERROR; + else if (errno == EINVAL) + return DHCP_R_INVALIDARG; + else if (errno == ECONNRESET) { + omapi_disconnect (h, 1); + return ISC_R_SHUTTINGDOWN; + } else + return ISC_R_UNEXPECTED; + } + + /* If we got a zero-length read, as opposed to EWOULDBLOCK, + the remote end closed the connection. */ + if (read_status == 0) { + omapi_disconnect (h, 0); + return ISC_R_SHUTTINGDOWN; + } +#if defined (TRACING) + if (trace_record ()) { + trace_iov_t iov [2]; + int32_t connect_index; + + connect_index = htonl (c -> index); + + iov [0].buf = (char *)&connect_index; + iov [0].len = sizeof connect_index; + iov [1].buf = &buffer -> buf [buffer -> tail]; + iov [1].len = read_status; + + status = (trace_write_packet_iov + (trace_connection_input, 2, iov, MDL)); + if (status != ISC_R_SUCCESS) { + trace_stop (); + log_error ("trace connection input: %s", + isc_result_totext (status)); + } + } +#endif + buffer -> tail += read_status; + c -> in_bytes += read_status; + if (buffer -> tail == sizeof buffer -> buf) + buffer -> tail = 0; + if (read_status < read_len) + break; + bytes_to_read -= read_status; + } + + if (c -> bytes_needed <= c -> in_bytes) { + omapi_signal (h, "ready", c); + } + return ISC_R_SUCCESS; +} + +/* Put some bytes into the output buffer for a connection. */ + +isc_result_t omapi_connection_copyin (omapi_object_t *h, + const unsigned char *bufp, + unsigned len) +{ + omapi_buffer_t *buffer; + isc_result_t status; + int bytes_copied = 0; + unsigned copy_len; + int sig_flags = SIG_MODE_UPDATE; + omapi_connection_object_t *c; + + /* no need to verify len as it's unsigned */ + if (!h || h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + /* If the connection is closed, return an error if the caller + tries to copy in. */ + if (c -> state == omapi_connection_disconnecting || + c -> state == omapi_connection_closed) + return ISC_R_NOTCONNECTED; + + if (c -> outbufs) { + for (buffer = c -> outbufs; + buffer -> next; buffer = buffer -> next) + ; + } else { + status = omapi_buffer_new (&c -> outbufs, MDL); + if (status != ISC_R_SUCCESS) + goto leave; + buffer = c -> outbufs; + } + + while (bytes_copied < len) { + /* If there is no space available in this buffer, + allocate a new one. */ + if (!BUFFER_BYTES_FREE (buffer)) { + status = (omapi_buffer_new (&buffer -> next, MDL)); + if (status != ISC_R_SUCCESS) + goto leave; + buffer = buffer -> next; + } + + if (buffer -> tail > buffer -> head) + copy_len = sizeof (buffer -> buf) - buffer -> tail; + else + copy_len = buffer -> head - buffer -> tail; + + if (copy_len > (len - bytes_copied)) + copy_len = len - bytes_copied; + + if (c -> out_key) { + if (!c -> out_context) + sig_flags |= SIG_MODE_INIT; + status = omapi_connection_sign_data + (sig_flags, c -> out_key, &c -> out_context, + &bufp [bytes_copied], copy_len, + (omapi_typed_data_t **)0); + if (status != ISC_R_SUCCESS) + goto leave; + } + + memcpy (&buffer -> buf [buffer -> tail], + &bufp [bytes_copied], copy_len); + buffer -> tail += copy_len; + c -> out_bytes += copy_len; + bytes_copied += copy_len; + if (buffer -> tail == sizeof buffer -> buf) + buffer -> tail = 0; + } + + status = ISC_R_SUCCESS; + + leave: + /* + * If we have any bytes to send and we have a proper io object + * inform the socket code that we would like to know when we + * can send more bytes. + */ + if (c->out_bytes != 0) { + if ((c->outer != NULL) && + (c->outer->type == omapi_type_io_object)) { + omapi_io_object_t *io = (omapi_io_object_t *)c->outer; + isc_socket_fdwatchpoke(io->fd, + ISC_SOCKFDWATCH_WRITE); + } + } + + return (status); +} + +/* Copy some bytes from the input buffer, and advance the input buffer + pointer beyond the bytes copied out. */ + +isc_result_t omapi_connection_copyout (unsigned char *buf, + omapi_object_t *h, + unsigned size) +{ + unsigned bytes_remaining; + unsigned bytes_this_copy; + unsigned first_byte; + omapi_buffer_t *buffer; + unsigned char *bufp; + int sig_flags = SIG_MODE_UPDATE; + omapi_connection_object_t *c; + isc_result_t status; + + if (!h || h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (size > c -> in_bytes) + return ISC_R_NOMORE; + bufp = buf; + bytes_remaining = size; + buffer = c -> inbufs; + + while (bytes_remaining) { + if (!buffer) + return ISC_R_UNEXPECTED; + if (BYTES_IN_BUFFER (buffer)) { + if (buffer -> head == (sizeof buffer -> buf) - 1) + first_byte = 0; + else + first_byte = buffer -> head + 1; + + if (first_byte > buffer -> tail) { + bytes_this_copy = (sizeof buffer -> buf - + first_byte); + } else { + bytes_this_copy = + buffer -> tail - first_byte; + } + if (bytes_this_copy > bytes_remaining) + bytes_this_copy = bytes_remaining; + if (bufp) { + if (c -> in_key) { + if (!c -> in_context) + sig_flags |= SIG_MODE_INIT; + status = omapi_connection_sign_data + (sig_flags, + c -> in_key, + &c -> in_context, + (unsigned char *) + &buffer -> buf [first_byte], + bytes_this_copy, + (omapi_typed_data_t **)0); + if (status != ISC_R_SUCCESS) + return status; + } + + memcpy (bufp, &buffer -> buf [first_byte], + bytes_this_copy); + bufp += bytes_this_copy; + } + bytes_remaining -= bytes_this_copy; + buffer -> head = first_byte + bytes_this_copy - 1; + c -> in_bytes -= bytes_this_copy; + } + + if (!BYTES_IN_BUFFER (buffer)) + buffer = buffer -> next; + } + + /* Get rid of any input buffers that we emptied. */ + buffer = (omapi_buffer_t *)0; + while (c -> inbufs && + !BYTES_IN_BUFFER (c -> inbufs)) { + if (c -> inbufs -> next) { + omapi_buffer_reference (&buffer, + c -> inbufs -> next, MDL); + omapi_buffer_dereference (&c -> inbufs -> next, MDL); + } + omapi_buffer_dereference (&c -> inbufs, MDL); + if (buffer) { + omapi_buffer_reference + (&c -> inbufs, buffer, MDL); + omapi_buffer_dereference (&buffer, MDL); + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_writer (omapi_object_t *h) +{ + unsigned bytes_this_write; + int bytes_written; + unsigned first_byte; + omapi_buffer_t *buffer; + omapi_connection_object_t *c; + + if (!h || h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + /* Already flushed... */ + if (!c -> out_bytes) + return ISC_R_SUCCESS; + + buffer = c -> outbufs; + + while (c -> out_bytes) { + if (!buffer) + return ISC_R_UNEXPECTED; + if (BYTES_IN_BUFFER (buffer)) { + if (buffer -> head == (sizeof buffer -> buf) - 1) + first_byte = 0; + else + first_byte = buffer -> head + 1; + + if (first_byte > buffer -> tail) { + bytes_this_write = (sizeof buffer -> buf - + first_byte); + } else { + bytes_this_write = + buffer -> tail - first_byte; + } + bytes_written = write (c -> socket, + &buffer -> buf [first_byte], + bytes_this_write); + /* If the write failed with EWOULDBLOCK or we wrote + zero bytes, a further write would block, so we have + flushed as much as we can for now. Other errors + are really errors. */ + if (bytes_written < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + return ISC_R_INPROGRESS; + else if (errno == EPIPE) + return ISC_R_NOCONN; +#ifdef EDQUOT + else if (errno == EFBIG || errno == EDQUOT) +#else + else if (errno == EFBIG) +#endif + return ISC_R_NORESOURCES; + else if (errno == ENOSPC) + return ISC_R_NOSPACE; + else if (errno == EIO) + return ISC_R_IOERROR; + else if (errno == EINVAL) + return DHCP_R_INVALIDARG; + else if (errno == ECONNRESET) + return ISC_R_SHUTTINGDOWN; + else + return ISC_R_UNEXPECTED; + } + if (bytes_written == 0) + return ISC_R_INPROGRESS; + +#if defined (TRACING) + if (trace_record ()) { + isc_result_t status; + trace_iov_t iov [2]; + int32_t connect_index; + + connect_index = htonl (c -> index); + + iov [0].buf = (char *)&connect_index; + iov [0].len = sizeof connect_index; + iov [1].buf = &buffer -> buf [buffer -> tail]; + iov [1].len = bytes_written; + + status = (trace_write_packet_iov + (trace_connection_input, 2, iov, + MDL)); + if (status != ISC_R_SUCCESS) { + trace_stop (); + log_error ("trace %s output: %s", + "connection", + isc_result_totext (status)); + } + } +#endif + + buffer -> head = first_byte + bytes_written - 1; + c -> out_bytes -= bytes_written; + + /* If we didn't finish out the write, we filled the + O.S. output buffer and a further write would block, + so stop trying to flush now. */ + if (bytes_written != bytes_this_write) + return ISC_R_INPROGRESS; + } + + if (!BYTES_IN_BUFFER (buffer)) + buffer = buffer -> next; + } + + /* Get rid of any output buffers we emptied. */ + buffer = (omapi_buffer_t *)0; + while (c -> outbufs && + !BYTES_IN_BUFFER (c -> outbufs)) { + if (c -> outbufs -> next) { + omapi_buffer_reference (&buffer, + c -> outbufs -> next, MDL); + omapi_buffer_dereference (&c -> outbufs -> next, MDL); + } + omapi_buffer_dereference (&c -> outbufs, MDL); + if (buffer) { + omapi_buffer_reference (&c -> outbufs, buffer, MDL); + omapi_buffer_dereference (&buffer, MDL); + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_get_uint32 (omapi_object_t *c, + u_int32_t *result) +{ + u_int32_t inbuf; + isc_result_t status; + + status = omapi_connection_copyout ((unsigned char *)&inbuf, + c, sizeof inbuf); + if (status != ISC_R_SUCCESS) + return status; + + *result = ntohl (inbuf); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_put_uint32 (omapi_object_t *c, + u_int32_t value) +{ + u_int32_t inbuf; + + inbuf = htonl (value); + + return omapi_connection_copyin (c, (unsigned char *)&inbuf, + sizeof inbuf); +} + +isc_result_t omapi_connection_get_uint16 (omapi_object_t *c, + u_int16_t *result) +{ + u_int16_t inbuf; + isc_result_t status; + + status = omapi_connection_copyout ((unsigned char *)&inbuf, + c, sizeof inbuf); + if (status != ISC_R_SUCCESS) + return status; + + *result = ntohs (inbuf); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_put_uint16 (omapi_object_t *c, + u_int32_t value) +{ + u_int16_t inbuf; + + inbuf = htons (value); + + return omapi_connection_copyin (c, (unsigned char *)&inbuf, + sizeof inbuf); +} + +isc_result_t omapi_connection_write_typed_data (omapi_object_t *c, + omapi_typed_data_t *data) +{ + isc_result_t status; + omapi_handle_t handle; + + /* Null data is valid. */ + if (!data) + return omapi_connection_put_uint32 (c, 0); + + switch (data -> type) { + case omapi_datatype_int: + status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_put_uint32 (c, ((u_int32_t) + (data -> u.integer))); + + case omapi_datatype_string: + case omapi_datatype_data: + status = omapi_connection_put_uint32 (c, data -> u.buffer.len); + if (status != ISC_R_SUCCESS) + return status; + if (data -> u.buffer.len) + return omapi_connection_copyin + (c, data -> u.buffer.value, + data -> u.buffer.len); + return ISC_R_SUCCESS; + + case omapi_datatype_object: + if (data -> u.object) { + status = omapi_object_handle (&handle, + data -> u.object); + if (status != ISC_R_SUCCESS) + return status; + } else + handle = 0; + status = omapi_connection_put_uint32 (c, sizeof handle); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_put_uint32 (c, handle); + + } + return DHCP_R_INVALIDARG; +} + +isc_result_t omapi_connection_put_name (omapi_object_t *c, const char *name) +{ + isc_result_t status; + unsigned len = strlen (name); + + status = omapi_connection_put_uint16 (c, len); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_copyin (c, (const unsigned char *)name, len); +} + +isc_result_t omapi_connection_put_string (omapi_object_t *c, + const char *string) +{ + isc_result_t status; + unsigned len; + + if (string) + len = strlen (string); + else + len = 0; + + status = omapi_connection_put_uint32 (c, len); + if (status != ISC_R_SUCCESS) + return status; + if (len) + return omapi_connection_copyin + (c, (const unsigned char *)string, len); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h) +{ + isc_result_t status; + omapi_handle_t handle; + + if (h) { + status = omapi_object_handle (&handle, h); + if (status != ISC_R_SUCCESS) + return status; + } else + handle = 0; /* The null handle. */ + status = omapi_connection_put_uint32 (c, sizeof handle); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_put_uint32 (c, handle); +} diff --git a/omapip/connection.c b/omapip/connection.c new file mode 100644 index 0000000..58b08c6 --- /dev/null +++ b/omapip/connection.c @@ -0,0 +1,1104 @@ +/* connection.c + + Subroutines for dealing with connections. */ + +/* + * Copyright (c) 2009-2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <errno.h> + +#if defined (TRACING) +static void trace_connect_input (trace_type_t *, unsigned, char *); +static void trace_connect_stop (trace_type_t *); +static void trace_disconnect_input (trace_type_t *, unsigned, char *); +static void trace_disconnect_stop (trace_type_t *); +trace_type_t *trace_connect; +trace_type_t *trace_disconnect; +extern omapi_array_t *trace_listeners; +#endif +static isc_result_t omapi_connection_connect_internal (omapi_object_t *); + +OMAPI_OBJECT_ALLOC (omapi_connection, + omapi_connection_object_t, omapi_type_connection) + +isc_result_t omapi_connect (omapi_object_t *c, + const char *server_name, + unsigned port) +{ + struct hostent *he; + unsigned i, hix; + omapi_addr_list_t *addrs = (omapi_addr_list_t *)0; + struct in_addr foo; + isc_result_t status; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_connect(%s, port=%d)", server_name, port); +#endif + + if (!inet_aton (server_name, &foo)) { + /* If we didn't get a numeric address, try for a domain + name. It's okay for this call to block. */ + he = gethostbyname (server_name); + if (!he) + return DHCP_R_HOSTUNKNOWN; + for (i = 0; he -> h_addr_list [i]; i++) + ; + if (i == 0) + return DHCP_R_HOSTUNKNOWN; + hix = i; + + status = omapi_addr_list_new (&addrs, hix, MDL); + if (status != ISC_R_SUCCESS) + return status; + for (i = 0; i < hix; i++) { + addrs -> addresses [i].addrtype = he -> h_addrtype; + addrs -> addresses [i].addrlen = he -> h_length; + memcpy (addrs -> addresses [i].address, + he -> h_addr_list [i], + (unsigned)he -> h_length); + addrs -> addresses [i].port = port; + } + } else { + status = omapi_addr_list_new (&addrs, 1, MDL); + if (status != ISC_R_SUCCESS) + return status; + addrs -> addresses [0].addrtype = AF_INET; + addrs -> addresses [0].addrlen = sizeof foo; + memcpy (addrs -> addresses [0].address, &foo, sizeof foo); + addrs -> addresses [0].port = port; + } + status = omapi_connect_list (c, addrs, (omapi_addr_t *)0); + omapi_addr_list_dereference (&addrs, MDL); + return status; +} + +isc_result_t omapi_connect_list (omapi_object_t *c, + omapi_addr_list_t *remote_addrs, + omapi_addr_t *local_addr) +{ + isc_result_t status; + omapi_connection_object_t *obj; + int flag; + struct sockaddr_in local_sin; + + obj = (omapi_connection_object_t *)0; + status = omapi_connection_allocate (&obj, MDL); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj, + MDL); + if (status != ISC_R_SUCCESS) { + omapi_connection_dereference (&obj, MDL); + return status; + } + status = omapi_object_reference (&obj -> inner, c, MDL); + if (status != ISC_R_SUCCESS) { + omapi_connection_dereference (&obj, MDL); + return status; + } + + /* Store the address list on the object. */ + omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL); + obj -> cptr = 0; + obj -> state = omapi_connection_unconnected; + +#if defined (TRACING) + /* If we're playing back, don't actually try to connect - just leave + the object available for a subsequent connect or disconnect. */ + if (!trace_playback ()) { +#endif + /* Create a socket on which to communicate. */ + obj -> socket = + socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (obj -> socket < 0) { + omapi_connection_dereference (&obj, MDL); + if (errno == EMFILE || errno == ENFILE + || errno == ENOBUFS) + return ISC_R_NORESOURCES; + return ISC_R_UNEXPECTED; + } + + /* Set up the local address, if any. */ + if (local_addr) { + /* Only do TCPv4 so far. */ + if (local_addr -> addrtype != AF_INET) { + omapi_connection_dereference (&obj, MDL); + return DHCP_R_INVALIDARG; + } + local_sin.sin_port = htons (local_addr -> port); + memcpy (&local_sin.sin_addr, + local_addr -> address, + local_addr -> addrlen); +#if defined (HAVE_SA_LEN) + local_sin.sin_len = sizeof local_addr; +#endif + local_sin.sin_family = AF_INET; + memset (&local_sin.sin_zero, 0, + sizeof local_sin.sin_zero); + + if (bind (obj -> socket, (struct sockaddr *)&local_sin, + sizeof local_sin) < 0) { + omapi_connection_object_t **objp = &obj; + omapi_object_t **o = (omapi_object_t **)objp; + omapi_object_dereference(o, MDL); + if (errno == EADDRINUSE) + return ISC_R_ADDRINUSE; + if (errno == EADDRNOTAVAIL) + return ISC_R_ADDRNOTAVAIL; + if (errno == EACCES) + return ISC_R_NOPERM; + return ISC_R_UNEXPECTED; + } + obj -> local_addr = local_sin; + } + +#if defined(F_SETFD) + if (fcntl (obj -> socket, F_SETFD, 1) < 0) { + close (obj -> socket); + omapi_connection_dereference (&obj, MDL); + return ISC_R_UNEXPECTED; + } +#endif + + /* Set the SO_REUSEADDR flag (this should not fail). */ + flag = 1; + if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&flag, sizeof flag) < 0) { + omapi_connection_dereference (&obj, MDL); + return ISC_R_UNEXPECTED; + } + + /* Set the file to nonblocking mode. */ + if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { + omapi_connection_dereference (&obj, MDL); + return ISC_R_UNEXPECTED; + } + +#ifdef SO_NOSIGPIPE + /* + * If available stop the OS from killing our + * program on a SIGPIPE failure + */ + flag = 1; + if (setsockopt(obj->socket, SOL_SOCKET, SO_NOSIGPIPE, + (char *)&flag, sizeof(flag)) < 0) { + omapi_connection_dereference (&obj, MDL); + return ISC_R_UNEXPECTED; + } +#endif + + status = (omapi_register_io_object + ((omapi_object_t *)obj, + 0, omapi_connection_writefd, + 0, omapi_connection_connect, + omapi_connection_reaper)); + if (status != ISC_R_SUCCESS) + goto out; + status = omapi_connection_connect_internal ((omapi_object_t *) + obj); + /* + * inprogress is the same as success but used + * to indicate to the dispatch code that we should + * mark the socket as requiring more attention. + * Routines calling this function should handle + * success properly. + */ + if (status == ISC_R_INPROGRESS) { + status = ISC_R_SUCCESS; + } +#if defined (TRACING) + } + omapi_connection_register (obj, MDL); +#endif + + out: + omapi_connection_dereference (&obj, MDL); + return status; +} + +#if defined (TRACING) +omapi_array_t *omapi_connections; + +OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t) + +void omapi_connection_trace_setup (void) { + trace_connect = trace_type_register ("connect", (void *)0, + trace_connect_input, + trace_connect_stop, MDL); + trace_disconnect = trace_type_register ("disconnect", (void *)0, + trace_disconnect_input, + trace_disconnect_stop, MDL); +} + +void omapi_connection_register (omapi_connection_object_t *obj, + const char *file, int line) +{ + isc_result_t status; + trace_iov_t iov [6]; + int iov_count = 0; + int32_t connect_index, listener_index; + static int32_t index; + + if (!omapi_connections) { + status = omapi_connection_array_allocate (&omapi_connections, + file, line); + if (status != ISC_R_SUCCESS) + return; + } + + status = omapi_connection_array_extend (omapi_connections, obj, + (int *)0, file, line); + if (status != ISC_R_SUCCESS) { + obj -> index = -1; + return; + } + +#if defined (TRACING) + if (trace_record ()) { + /* Connection registration packet: + + int32_t index + int32_t listener_index [-1 means no listener] + u_int16_t remote_port + u_int16_t local_port + u_int32_t remote_addr + u_int32_t local_addr */ + + connect_index = htonl (index); + index++; + if (obj -> listener) + listener_index = htonl (obj -> listener -> index); + else + listener_index = htonl (-1); + iov [iov_count].buf = (char *)&connect_index; + iov [iov_count++].len = sizeof connect_index; + iov [iov_count].buf = (char *)&listener_index; + iov [iov_count++].len = sizeof listener_index; + iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port; + iov [iov_count++].len = sizeof obj -> remote_addr.sin_port; + iov [iov_count].buf = (char *)&obj -> local_addr.sin_port; + iov [iov_count++].len = sizeof obj -> local_addr.sin_port; + iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr; + iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr; + iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr; + iov [iov_count++].len = sizeof obj -> local_addr.sin_addr; + + status = trace_write_packet_iov (trace_connect, + iov_count, iov, file, line); + } +#endif +} + +static void trace_connect_input (trace_type_t *ttype, + unsigned length, char *buf) +{ + struct sockaddr_in remote, local; + int32_t connect_index, listener_index; + char *s = buf; + omapi_connection_object_t *obj; + isc_result_t status; + int i; + + if (length != ((sizeof connect_index) + + (sizeof remote.sin_port) + + (sizeof remote.sin_addr)) * 2) { + log_error ("Trace connect: invalid length %d", length); + return; + } + + memset (&remote, 0, sizeof remote); + memset (&local, 0, sizeof local); + memcpy (&connect_index, s, sizeof connect_index); + s += sizeof connect_index; + memcpy (&listener_index, s, sizeof listener_index); + s += sizeof listener_index; + memcpy (&remote.sin_port, s, sizeof remote.sin_port); + s += sizeof remote.sin_port; + memcpy (&local.sin_port, s, sizeof local.sin_port); + s += sizeof local.sin_port; + memcpy (&remote.sin_addr, s, sizeof remote.sin_addr); + s += sizeof remote.sin_addr; + memcpy (&local.sin_addr, s, sizeof local.sin_addr); + s += sizeof local.sin_addr; + POST(s); + + connect_index = ntohl (connect_index); + listener_index = ntohl (listener_index); + + /* If this was a connect to a listener, then we just slap together + a new connection. */ + if (listener_index != -1) { + omapi_listener_object_t *listener; + listener = (omapi_listener_object_t *)0; + omapi_array_foreach_begin (trace_listeners, + omapi_listener_object_t, lp) { + if (lp -> address.sin_port == local.sin_port) { + omapi_listener_reference (&listener, lp, MDL); + omapi_listener_dereference (&lp, MDL); + break; + } + } omapi_array_foreach_end (trace_listeners, + omapi_listener_object_t, lp); + if (!listener) { + log_error ("%s%ld, addr %s, port %d", + "Spurious traced listener connect - index ", + (long int)listener_index, + inet_ntoa (local.sin_addr), + ntohs (local.sin_port)); + return; + } + obj = (omapi_connection_object_t *)0; + status = omapi_listener_connect (&obj, listener, -1, &remote); + if (status != ISC_R_SUCCESS) { + log_error ("traced listener connect: %s", + isc_result_totext (status)); + } + if (obj) + omapi_connection_dereference (&obj, MDL); + omapi_listener_dereference (&listener, MDL); + return; + } + + /* Find the matching connect object, if there is one. */ + omapi_array_foreach_begin (omapi_connections, + omapi_connection_object_t, lp) { + for (i = 0; (lp->connect_list && + i < lp->connect_list->count); i++) { + if (!memcmp (&remote.sin_addr, + &lp->connect_list->addresses[i].address, + sizeof remote.sin_addr) && + (ntohs (remote.sin_port) == + lp->connect_list->addresses[i].port)) { + lp->state = omapi_connection_connected; + lp->remote_addr = remote; + lp->remote_addr.sin_family = AF_INET; + omapi_addr_list_dereference(&lp->connect_list, MDL); + lp->index = connect_index; + status = omapi_signal_in((omapi_object_t *)lp, + "connect"); + omapi_connection_dereference (&lp, MDL); + return; + } + } + } omapi_array_foreach_end (omapi_connections, + omapi_connection_object_t, lp); + + log_error ("Spurious traced connect - index %ld, addr %s, port %d", + (long int)connect_index, inet_ntoa (remote.sin_addr), + ntohs (remote.sin_port)); + return; +} + +static void trace_connect_stop (trace_type_t *ttype) { } + +static void trace_disconnect_input (trace_type_t *ttype, + unsigned length, char *buf) +{ + int32_t *index; + if (length != sizeof *index) { + log_error ("trace disconnect: wrong length %d", length); + return; + } + + index = (int32_t *)buf; + + omapi_array_foreach_begin (omapi_connections, + omapi_connection_object_t, lp) { + if (lp -> index == ntohl (*index)) { + omapi_disconnect ((omapi_object_t *)lp, 1); + omapi_connection_dereference (&lp, MDL); + return; + } + } omapi_array_foreach_end (omapi_connections, + omapi_connection_object_t, lp); + + log_error ("trace disconnect: no connection matching index %ld", + (long int)ntohl (*index)); +} + +static void trace_disconnect_stop (trace_type_t *ttype) { } +#endif + +/* Disconnect a connection object from the remote end. If force is nonzero, + close the connection immediately. Otherwise, shut down the receiving end + but allow any unsent data to be sent before actually closing the socket. */ + +isc_result_t omapi_disconnect (omapi_object_t *h, + int force) +{ + omapi_connection_object_t *c; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_disconnect(%s)", force ? "force" : ""); +#endif + + c = (omapi_connection_object_t *)h; + if (c -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + +#if defined (TRACING) + if (trace_record ()) { + isc_result_t status; + int32_t index; + + index = htonl (c -> index); + status = trace_write_packet (trace_disconnect, + sizeof index, (char *)&index, + MDL); + if (status != ISC_R_SUCCESS) { + trace_stop (); + log_error ("trace_write_packet: %s", + isc_result_totext (status)); + } + } + if (!trace_playback ()) { +#endif + if (!force) { + /* If we're already disconnecting, we don't have to do + anything. */ + if (c -> state == omapi_connection_disconnecting) + return ISC_R_SUCCESS; + + /* Try to shut down the socket - this sends a FIN to + the remote end, so that it won't send us any more + data. If the shutdown succeeds, and we still + have bytes left to write, defer closing the socket + until that's done. */ + if (!shutdown (c -> socket, SHUT_RD)) { + if (c -> out_bytes > 0) { + c -> state = + omapi_connection_disconnecting; + return ISC_R_SUCCESS; + } + } + } + close (c -> socket); +#if defined (TRACING) + } +#endif + c -> state = omapi_connection_closed; + +#if 0 + /* + * Disconnecting from the I/O object seems incorrect as it doesn't + * cause the I/O object to be cleaned and released. Previous to + * using the isc socket library this wouldn't have caused a problem + * with the socket library we would have a reference to a closed + * socket. Instead we now do an unregister to properly free the + * I/O object. + */ + + /* Disconnect from I/O object, if any. */ + if (h -> outer) { + if (h -> outer -> inner) + omapi_object_dereference (&h -> outer -> inner, MDL); + omapi_object_dereference (&h -> outer, MDL); + } +#else + if (h->outer) { + omapi_unregister_io_object(h); + } +#endif + + /* If whatever created us registered a signal handler, send it + a disconnect signal. */ + omapi_signal (h, "disconnect", h); + + /* Disconnect from protocol object, if any. */ + if (h->inner != NULL) { + if (h->inner->outer != NULL) { + omapi_object_dereference(&h->inner->outer, MDL); + } + omapi_object_dereference(&h->inner, MDL); + } + + /* XXX: the code to free buffers should be in the dereference + function, but there is no special-purpose function to + dereference connections, so these just get leaked */ + /* Free any buffers */ + if (c->inbufs != NULL) { + omapi_buffer_dereference(&c->inbufs, MDL); + } + c->in_bytes = 0; + if (c->outbufs != NULL) { + omapi_buffer_dereference(&c->outbufs, MDL); + } + c->out_bytes = 0; + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes) +{ + omapi_connection_object_t *c; + + if (h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + c -> bytes_needed = bytes; + if (c -> bytes_needed <= c -> in_bytes) { + return ISC_R_SUCCESS; + } + return DHCP_R_NOTYET; +} + +/* Return the socket on which the dispatcher should wait for readiness + to read, for a connection object. */ +int omapi_connection_readfd (omapi_object_t *h) +{ + omapi_connection_object_t *c; + if (h -> type != omapi_type_connection) + return -1; + c = (omapi_connection_object_t *)h; + if (c -> state != omapi_connection_connected) + return -1; + return c -> socket; +} + +/* + * Return the socket on which the dispatcher should wait for readiness + * to write, for a connection object. When bytes are buffered we should + * also poke the dispatcher to tell it to start or re-start watching the + * socket. + */ +int omapi_connection_writefd (omapi_object_t *h) +{ + omapi_connection_object_t *c; + if (h -> type != omapi_type_connection) + return -1; + c = (omapi_connection_object_t *)h; + return c->socket; +} + +isc_result_t omapi_connection_connect (omapi_object_t *h) +{ + isc_result_t status; + + /* + * We use the INPROGRESS status to indicate that + * we want more from the socket. In this case we + * have now connected and are trying to write to + * the socket for the first time. For the signaling + * code this is the same as a SUCCESS so we don't + * pass it on as a signal. + */ + status = omapi_connection_connect_internal (h); + if (status == ISC_R_INPROGRESS) + return ISC_R_INPROGRESS; + + if (status != ISC_R_SUCCESS) + omapi_signal (h, "status", status); + + return ISC_R_SUCCESS; +} + +static isc_result_t omapi_connection_connect_internal (omapi_object_t *h) +{ + int error = 0; + omapi_connection_object_t *c; + socklen_t sl; + isc_result_t status; + + if (h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (c -> state == omapi_connection_connecting) { + sl = sizeof error; + if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR, + (char *)&error, &sl) < 0) { + omapi_disconnect (h, 1); + return ISC_R_SUCCESS; + } + if (!error) + c -> state = omapi_connection_connected; + } + if (c -> state == omapi_connection_connecting || + c -> state == omapi_connection_unconnected) { + if (c -> cptr >= c -> connect_list -> count) { + switch (error) { + case ECONNREFUSED: + status = ISC_R_CONNREFUSED; + break; + case ENETUNREACH: + status = ISC_R_NETUNREACH; + break; + default: + status = uerr2isc (error); + break; + } + omapi_disconnect (h, 1); + return status; + } + + if (c -> connect_list -> addresses [c -> cptr].addrtype != + AF_INET) { + omapi_disconnect (h, 1); + return DHCP_R_INVALIDARG; + } + + memcpy (&c -> remote_addr.sin_addr, + &c -> connect_list -> addresses [c -> cptr].address, + sizeof c -> remote_addr.sin_addr); + c -> remote_addr.sin_family = AF_INET; + c -> remote_addr.sin_port = + htons (c -> connect_list -> addresses [c -> cptr].port); +#if defined (HAVE_SA_LEN) + c -> remote_addr.sin_len = sizeof c -> remote_addr; +#endif + memset (&c -> remote_addr.sin_zero, 0, + sizeof c -> remote_addr.sin_zero); + ++c -> cptr; + + error = connect (c -> socket, + (struct sockaddr *)&c -> remote_addr, + sizeof c -> remote_addr); + if (error < 0) { + error = errno; + if (error != EINPROGRESS) { + omapi_disconnect (h, 1); + switch (error) { + case ECONNREFUSED: + status = ISC_R_CONNREFUSED; + break; + case ENETUNREACH: + status = ISC_R_NETUNREACH; + break; + default: + status = uerr2isc (error); + break; + } + return status; + } + c -> state = omapi_connection_connecting; + return DHCP_R_INCOMPLETE; + } + c -> state = omapi_connection_connected; + } + + /* I don't know why this would fail, so I'm tempted not to test + the return value. */ + sl = sizeof (c -> local_addr); + if (getsockname (c -> socket, + (struct sockaddr *)&c -> local_addr, &sl) < 0) { + } + + /* Reregister with the I/O object. If we don't already have an + I/O object this turns into a register call, otherwise we simply + modify the pointers in the I/O object. */ + + status = omapi_reregister_io_object (h, + omapi_connection_readfd, + omapi_connection_writefd, + omapi_connection_reader, + omapi_connection_writer, + omapi_connection_reaper); + + if (status != ISC_R_SUCCESS) { + omapi_disconnect (h, 1); + return status; + } + + omapi_signal_in (h, "connect"); + omapi_addr_list_dereference (&c -> connect_list, MDL); + return ISC_R_INPROGRESS; +} + +/* Reaper function for connection - if the connection is completely closed, + reap it. If it's in the disconnecting state, there were bytes left + to write when the user closed it, so if there are now no bytes left to + write, we can close it. */ +isc_result_t omapi_connection_reaper (omapi_object_t *h) +{ + omapi_connection_object_t *c; + + if (h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + + c = (omapi_connection_object_t *)h; + if (c -> state == omapi_connection_disconnecting && + c -> out_bytes == 0) { +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_connection_reaper(): disconnect"); +#endif + omapi_disconnect (h, 1); + } + if (c -> state == omapi_connection_closed) { +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_connection_reaper(): closed"); +#endif + return ISC_R_NOTCONNECTED; + } + return ISC_R_SUCCESS; +} + +static isc_result_t make_dst_key (dst_key_t **dst_key, omapi_object_t *a) { + omapi_value_t *name = (omapi_value_t *)0; + omapi_value_t *algorithm = (omapi_value_t *)0; + omapi_value_t *key = (omapi_value_t *)0; + char *name_str = NULL; + isc_result_t status = ISC_R_SUCCESS; + + if (status == ISC_R_SUCCESS) + status = omapi_get_value_str + (a, (omapi_object_t *)0, "name", &name); + + if (status == ISC_R_SUCCESS) + status = omapi_get_value_str + (a, (omapi_object_t *)0, "algorithm", &algorithm); + + if (status == ISC_R_SUCCESS) + status = omapi_get_value_str + (a, (omapi_object_t *)0, "key", &key); + + if (status == ISC_R_SUCCESS) { + if ((algorithm->value->type != omapi_datatype_data && + algorithm->value->type != omapi_datatype_string) || + strncasecmp((char *)algorithm->value->u.buffer.value, + NS_TSIG_ALG_HMAC_MD5 ".", + algorithm->value->u.buffer.len) != 0) { + status = DHCP_R_INVALIDARG; + } + } + + if (status == ISC_R_SUCCESS) { + name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL); + if (!name_str) + status = ISC_R_NOMEMORY; + } + + if (status == ISC_R_SUCCESS) { + memcpy (name_str, + name -> value -> u.buffer.value, + name -> value -> u.buffer.len); + name_str [name -> value -> u.buffer.len] = 0; + + status = isclib_make_dst_key(name_str, + DHCP_HMAC_MD5_NAME, + key->value->u.buffer.value, + key->value->u.buffer.len, + dst_key); + + if (*dst_key == NULL) + status = ISC_R_NOMEMORY; + } + + if (name_str) + dfree (name_str, MDL); + if (key) + omapi_value_dereference (&key, MDL); + if (algorithm) + omapi_value_dereference (&algorithm, MDL); + if (name) + omapi_value_dereference (&name, MDL); + + return status; +} + +isc_result_t omapi_connection_sign_data (int mode, + dst_key_t *key, + void **context, + const unsigned char *data, + const unsigned len, + omapi_typed_data_t **result) +{ + omapi_typed_data_t *td = (omapi_typed_data_t *)0; + isc_result_t status; + dst_context_t **dctx = (dst_context_t **)context; + + /* Create the context for the dst module */ + if (mode & SIG_MODE_INIT) { + status = dst_context_create(key, dhcp_gbl_ctx.mctx, dctx); + if (status != ISC_R_SUCCESS) { + return status; + } + } + + /* If we have any data add it to the context */ + if (len != 0) { + isc_region_t region; + region.base = (unsigned char *)data; + region.length = len; + dst_context_adddata(*dctx, ®ion); + } + + /* Finish the signature and clean up the context */ + if (mode & SIG_MODE_FINAL) { + unsigned int sigsize; + isc_buffer_t sigbuf; + + status = dst_key_sigsize(key, &sigsize); + if (status != ISC_R_SUCCESS) { + goto cleanup; + } + + status = omapi_typed_data_new (MDL, &td, + omapi_datatype_data, + sigsize); + if (status != ISC_R_SUCCESS) { + goto cleanup; + } + + isc_buffer_init(&sigbuf, td->u.buffer.value, td->u.buffer.len); + status = dst_context_sign(*dctx, &sigbuf); + if (status != ISC_R_SUCCESS) { + goto cleanup; + } + + if (result) { + omapi_typed_data_reference (result, td, MDL); + } + + cleanup: + /* We are done with the context and the td. On success + * the td is now referenced from result, on failure we + * don't need it any more */ + if (td) { + omapi_typed_data_dereference (&td, MDL); + } + dst_context_destroy(dctx); + return status; + } + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_output_auth_length (omapi_object_t *h, + unsigned *l) +{ + omapi_connection_object_t *c; + + if (h->type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (c->out_key == NULL) + return ISC_R_NOTFOUND; + + return(dst_key_sigsize(c->out_key, l)); +} + +isc_result_t omapi_connection_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_connection_object_t *c; + isc_result_t status; + + if (h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (omapi_ds_strcmp (name, "input-authenticator") == 0) { + if (value && value -> type != omapi_datatype_object) + return DHCP_R_INVALIDARG; + + if (c -> in_context) { + omapi_connection_sign_data (SIG_MODE_FINAL, + c -> in_key, + &c -> in_context, + 0, 0, + (omapi_typed_data_t **) 0); + } + + if (c->in_key != NULL) { + dst_key_free(&c->in_key); + } + + if (value) { + status = make_dst_key (&c -> in_key, + value -> u.object); + if (status != ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; + } + else if (omapi_ds_strcmp (name, "output-authenticator") == 0) { + if (value && value -> type != omapi_datatype_object) + return DHCP_R_INVALIDARG; + + if (c -> out_context) { + omapi_connection_sign_data (SIG_MODE_FINAL, + c -> out_key, + &c -> out_context, + 0, 0, + (omapi_typed_data_t **) 0); + } + + if (c->out_key != NULL) { + dst_key_free(&c->out_key); + } + + if (value) { + status = make_dst_key (&c -> out_key, + value -> u.object); + if (status != ISC_R_SUCCESS) + return status; + } + + return ISC_R_SUCCESS; + } + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_connection_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_connection_object_t *c; + omapi_typed_data_t *td = (omapi_typed_data_t *)0; + isc_result_t status; + unsigned int sigsize; + + if (h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (omapi_ds_strcmp (name, "input-signature") == 0) { + if (!c -> in_key || !c -> in_context) + return ISC_R_NOTFOUND; + + status = omapi_connection_sign_data (SIG_MODE_FINAL, + c -> in_key, + &c -> in_context, + 0, 0, &td); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_make_value (value, name, td, MDL); + omapi_typed_data_dereference (&td, MDL); + return status; + + } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) { + if (c->in_key == NULL) + return ISC_R_NOTFOUND; + + status = dst_key_sigsize(c->in_key, &sigsize); + if (status != ISC_R_SUCCESS) { + return(status); + } + + return omapi_make_int_value(value, name, sigsize, MDL); + + } else if (omapi_ds_strcmp (name, "output-signature") == 0) { + if (!c -> out_key || !c -> out_context) + return ISC_R_NOTFOUND; + + status = omapi_connection_sign_data (SIG_MODE_FINAL, + c -> out_key, + &c -> out_context, + 0, 0, &td); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_make_value (value, name, td, MDL); + omapi_typed_data_dereference (&td, MDL); + return status; + + } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) { + if (c->out_key == NULL) + return ISC_R_NOTFOUND; + + + status = dst_key_sigsize(c->out_key, &sigsize); + if (status != ISC_R_SUCCESS) { + return(status); + } + + return omapi_make_int_value(value, name, sigsize, MDL); + } + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_connection_destroy (omapi_object_t *h, + const char *file, int line) +{ + omapi_connection_object_t *c; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_connection_destroy()"); +#endif + + if (h -> type != omapi_type_connection) + return ISC_R_UNEXPECTED; + c = (omapi_connection_object_t *)(h); + if (c -> state == omapi_connection_connected) + omapi_disconnect (h, 1); + if (c -> listener) + omapi_listener_dereference (&c -> listener, file, line); + if (c -> connect_list) + omapi_addr_list_dereference (&c -> connect_list, file, line); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_connection_signal_handler(%s)", name); +#endif + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_connection_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *m) +{ + if (m -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + + if (m -> inner && m -> inner -> type -> stuff_values) + return (*(m -> inner -> type -> stuff_values)) (c, id, + m -> inner); + return ISC_R_SUCCESS; +} diff --git a/omapip/convert.c b/omapip/convert.c new file mode 100644 index 0000000..3482a9a --- /dev/null +++ b/omapip/convert.c @@ -0,0 +1,179 @@ +/* convert.c + + Safe copying of option values into and out of the option buffer, which + can't be assumed to be aligned. */ + +/* + * Copyright (c) 2004,2007,2009,2014 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/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +u_int32_t getULong (buf) + const unsigned char *buf; +{ + u_int32_t ibuf; + + memcpy (&ibuf, buf, sizeof (u_int32_t)); + return ntohl (ibuf); +} + +int32_t getLong (buf) + const unsigned char *buf; +{ + int32_t ibuf; + + memcpy (&ibuf, buf, sizeof (int32_t)); + return ntohl (ibuf); +} + +u_int32_t getUShort (buf) + const unsigned char *buf; +{ + unsigned short ibuf; + + memcpy (&ibuf, buf, sizeof (u_int16_t)); + return ntohs (ibuf); +} + +int32_t getShort (buf) + const unsigned char *buf; +{ + short ibuf; + + memcpy (&ibuf, buf, sizeof (int16_t)); + return ntohs (ibuf); +} + +void putULong (obuf, val) + unsigned char *obuf; + u_int32_t val; +{ + u_int32_t tmp = htonl (val); + memcpy (obuf, &tmp, sizeof tmp); +} + +void putLong (obuf, val) + unsigned char *obuf; + int32_t val; +{ + int32_t tmp = htonl (val); + memcpy (obuf, &tmp, sizeof tmp); +} + +void putUShort (obuf, val) + unsigned char *obuf; + u_int32_t val; +{ + u_int16_t tmp = htons (val); + memcpy (obuf, &tmp, sizeof tmp); +} + +void putShort (obuf, val) + unsigned char *obuf; + int32_t val; +{ + int16_t tmp = htons (val); + memcpy (obuf, &tmp, sizeof tmp); +} + +void putUChar (obuf, val) + unsigned char *obuf; + u_int32_t val; +{ + *obuf = val; +} + +u_int32_t getUChar (obuf) + const unsigned char *obuf; +{ + return obuf [0]; +} + +int converted_length (buf, base, width) + const unsigned char *buf; + unsigned int base; + unsigned int width; +{ + u_int32_t number; + u_int32_t column; + int power = 1; + u_int32_t newcolumn = base; + + if (base > 16) + return 0; + + if (width == 1) + number = getUChar (buf); + else if (width == 2) + number = getUShort (buf); + else if (width == 4) + number = getULong (buf); + else + return 0; + + do { + column = newcolumn; + + if (number < column) + return power; + power++; + newcolumn = column * base; + /* If we wrap around, it must be the next power of two up. */ + } while (newcolumn > column); + + return power; +} + +int binary_to_ascii (outbuf, inbuf, base, width) + unsigned char *outbuf; + const unsigned char *inbuf; + unsigned int base; + unsigned int width; +{ + u_int32_t number; + static char h2a [] = "0123456789abcdef"; + int power = converted_length (inbuf, base, width); + int i; + + if (base > 16) + return 0; + + if (width == 1) + number = getUChar (inbuf); + else if (width == 2) + number = getUShort (inbuf); + else if (width == 4) + number = getULong (inbuf); + else + return 0; + + for (i = power - 1 ; i >= 0; i--) { + outbuf [i] = h2a [number % base]; + number /= base; + } + + return power; +} diff --git a/omapip/dispatch.c b/omapip/dispatch.c new file mode 100644 index 0000000..be3918b --- /dev/null +++ b/omapip/dispatch.c @@ -0,0 +1,959 @@ +/* dispatch.c + + I/O dispatcher. */ + +/* + * Copyright (c) 2004,2007-2009,2013-2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include <sys/time.h> + +static omapi_io_object_t omapi_io_states; +struct timeval cur_tv; + +struct eventqueue *rw_queue_empty; + +OMAPI_OBJECT_ALLOC (omapi_io, + omapi_io_object_t, omapi_type_io_object) +OMAPI_OBJECT_ALLOC (omapi_waiter, + omapi_waiter_object_t, omapi_type_waiter) + +void +register_eventhandler(struct eventqueue **queue, void (*handler)(void *)) +{ + struct eventqueue *t, *q; + + /* traverse to end of list */ + t = NULL; + for (q = *queue ; q ; q = q->next) { + if (q->handler == handler) + return; /* handler already registered */ + t = q; + } + + q = ((struct eventqueue *)dmalloc(sizeof(struct eventqueue), MDL)); + if (!q) + log_fatal("register_eventhandler: no memory!"); + memset(q, 0, sizeof *q); + if (t) + t->next = q; + else + *queue = q; + q->handler = handler; + return; +} + +void +unregister_eventhandler(struct eventqueue **queue, void (*handler)(void *)) +{ + struct eventqueue *t, *q; + + /* traverse to end of list */ + t= NULL; + for (q = *queue ; q ; q = q->next) { + if (q->handler == handler) { + if (t) + t->next = q->next; + else + *queue = q->next; + dfree(q, MDL); /* Don't access q after this!*/ + break; + } + t = q; + } + return; +} + +void +trigger_event(struct eventqueue **queue) +{ + struct eventqueue *q; + + for (q=*queue ; q ; q=q->next) { + if (q->handler) + (*q->handler)(NULL); + } +} + +/* + * Callback routine to connect the omapi I/O object and socket with + * the isc socket code. The isc socket code will call this routine + * which will then call the correct local routine to process the bytes. + * + * Currently we are always willing to read more data, this should be modified + * so that on connections we don't read more if we already have enough. + * + * If we have more bytes to write we ask the library to call us when + * we can write more. If we indicate we don't have more to write we need + * to poke the library via isc_socket_fdwatchpoke. + */ + +/* + * sockdelete indicates if we are deleting the socket or leaving it in place + * 1 is delete, 0 is leave in place + */ +#define SOCKDELETE 1 +int +omapi_iscsock_cb(isc_task_t *task, + isc_socket_t *socket, + void *cbarg, + int flags) +{ + omapi_io_object_t *obj; + isc_result_t status; + + /* Get the current time... */ + gettimeofday (&cur_tv, (struct timezone *)0); + + /* isc socket stuff */ +#if SOCKDELETE + /* + * walk through the io states list, if our object is on there + * service it. if not ignore it. + */ + for (obj = omapi_io_states.next; + (obj != NULL) && (obj->next != NULL); + obj = obj->next) { + if (obj == cbarg) + break; + } + if (obj == NULL) { + return(0); + } +#else + /* Not much to be done if we have the wrong type of object. */ + if (((omapi_object_t *)cbarg) -> type != omapi_type_io_object) { + log_fatal ("Incorrect object type, must be of type io_object"); + } + obj = (omapi_io_object_t *)cbarg; + + /* + * If the object is marked as closed don't try and process + * anything just indicate that we don't want any more. + * + * This should be a temporary fix until we arrange to properly + * close the socket. + */ + if (obj->closed == ISC_TRUE) { + return(0); + } +#endif + + if ((flags == ISC_SOCKFDWATCH_READ) && + (obj->reader != NULL) && + (obj->inner != NULL)) { + status = obj->reader(obj->inner); + /* + * If we are shutting down (basically tried to + * read and got no bytes) we don't need to try + * again. + */ + if (status == ISC_R_SHUTTINGDOWN) + return (0); + /* Otherwise We always ask for more when reading */ + return (1); + } else if ((flags == ISC_SOCKFDWATCH_WRITE) && + (obj->writer != NULL) && + (obj->inner != NULL)) { + status = obj->writer(obj->inner); + /* If the writer has more to write they should return + * ISC_R_INPROGRESS */ + if (status == ISC_R_INPROGRESS) { + return (1); + } + } + + /* + * We get here if we either had an error (inconsistent + * structures etc) or no more to write, tell the socket + * lib we don't have more to do right now. + */ + return (0); +} + +/* Register an I/O handle so that we can do asynchronous I/O on it. */ + +isc_result_t omapi_register_io_object (omapi_object_t *h, + int (*readfd) (omapi_object_t *), + int (*writefd) (omapi_object_t *), + isc_result_t (*reader) + (omapi_object_t *), + isc_result_t (*writer) + (omapi_object_t *), + isc_result_t (*reaper) + (omapi_object_t *)) +{ + isc_result_t status; + omapi_io_object_t *obj, *p; + int fd_flags = 0, fd = 0; + + /* omapi_io_states is a static object. If its reference count + is zero, this is the first I/O handle to be registered, so + we need to initialize it. Because there is no inner or outer + pointer on this object, and we're setting its refcnt to 1, it + will never be freed. */ + if (!omapi_io_states.refcnt) { + omapi_io_states.refcnt = 1; + omapi_io_states.type = omapi_type_io_object; + } + + obj = (omapi_io_object_t *)0; + status = omapi_io_allocate (&obj, MDL); + if (status != ISC_R_SUCCESS) + return status; + obj->closed = ISC_FALSE; /* mark as open */ + + status = omapi_object_reference (&obj -> inner, h, MDL); + if (status != ISC_R_SUCCESS) { + omapi_io_dereference (&obj, MDL); + return status; + } + + status = omapi_object_reference (&h -> outer, + (omapi_object_t *)obj, MDL); + if (status != ISC_R_SUCCESS) { + omapi_io_dereference (&obj, MDL); + return status; + } + + /* + * Attach the I/O object to the isc socket library via the + * fdwatch function. This allows the socket library to watch + * over a socket that we built. If there are both a read and + * a write socket we asssume they are the same socket. + */ + + if (readfd) { + fd_flags |= ISC_SOCKFDWATCH_READ; + fd = readfd(h); + } + + if (writefd) { + fd_flags |= ISC_SOCKFDWATCH_WRITE; + fd = writefd(h); + } + + if (fd_flags != 0) { + status = isc_socket_fdwatchcreate(dhcp_gbl_ctx.socketmgr, + fd, fd_flags, + omapi_iscsock_cb, + obj, + dhcp_gbl_ctx.task, + &obj->fd); + if (status != ISC_R_SUCCESS) { + log_error("Unable to register fd with library %s", + isc_result_totext(status)); + + /*sar*/ + /* is this the cleanup we need? */ + omapi_object_dereference(&h->outer, MDL); + omapi_io_dereference (&obj, MDL); + return (status); + } + } + + + /* Find the last I/O state, if there are any. */ + for (p = omapi_io_states.next; + p && p -> next; p = p -> next) + ; + if (p) + omapi_io_reference (&p -> next, obj, MDL); + else + omapi_io_reference (&omapi_io_states.next, obj, MDL); + + obj -> readfd = readfd; + obj -> writefd = writefd; + obj -> reader = reader; + obj -> writer = writer; + obj -> reaper = reaper; + + omapi_io_dereference(&obj, MDL); + return ISC_R_SUCCESS; +} + +/* + * ReRegister an I/O handle so that we can do asynchronous I/O on it. + * If the handle doesn't exist we call the register routine to build it. + * If it does exist we change the functions associated with it, and + * repoke the fd code to make it happy. Neither the objects nor the + * fd are allowed to have changed. + */ + +isc_result_t omapi_reregister_io_object (omapi_object_t *h, + int (*readfd) (omapi_object_t *), + int (*writefd) (omapi_object_t *), + isc_result_t (*reader) + (omapi_object_t *), + isc_result_t (*writer) + (omapi_object_t *), + isc_result_t (*reaper) + (omapi_object_t *)) +{ + omapi_io_object_t *obj; + int fd_flags = 0; + + if ((!h -> outer) || (h -> outer -> type != omapi_type_io_object)) { + /* + * If we don't have an object or if the type isn't what + * we expect do the normal registration (which will overwrite + * an incorrect type, that's what we did historically, may + * want to change that) + */ + return (omapi_register_io_object (h, readfd, writefd, + reader, writer, reaper)); + } + + /* We have an io object of the correct type, try to update it */ + /*sar*/ + /* Should we validate that the fd matches the previous one? + * It's suppossed to, that's a requirement, don't bother yet */ + + obj = (omapi_io_object_t *)h->outer; + + obj->readfd = readfd; + obj->writefd = writefd; + obj->reader = reader; + obj->writer = writer; + obj->reaper = reaper; + + if (readfd) { + fd_flags |= ISC_SOCKFDWATCH_READ; + } + + if (writefd) { + fd_flags |= ISC_SOCKFDWATCH_WRITE; + } + + isc_socket_fdwatchpoke(obj->fd, fd_flags); + + return (ISC_R_SUCCESS); +} + +isc_result_t omapi_unregister_io_object (omapi_object_t *h) +{ + omapi_io_object_t *obj, *ph; +#if SOCKDELETE + omapi_io_object_t *p, *last; +#endif + + if (!h -> outer || h -> outer -> type != omapi_type_io_object) + return DHCP_R_INVALIDARG; + obj = (omapi_io_object_t *)h -> outer; + ph = (omapi_io_object_t *)0; + omapi_io_reference (&ph, obj, MDL); + +#if SOCKDELETE + /* + * For now we leave this out. We can't clean up the isc socket + * structure cleanly yet so we need to leave the io object in place. + * By leaving it on the io states list we avoid it being freed. + * We also mark it as closed to avoid using it. + */ + + /* remove from the list of I/O states */ + last = &omapi_io_states; + for (p = omapi_io_states.next; p; p = p -> next) { + if (p == obj) { + omapi_io_dereference (&last -> next, MDL); + omapi_io_reference (&last -> next, p -> next, MDL); + break; + } + last = p; + } + if (obj -> next) + omapi_io_dereference (&obj -> next, MDL); +#endif + + if (obj -> outer) { + if (obj -> outer -> inner == (omapi_object_t *)obj) + omapi_object_dereference (&obj -> outer -> inner, + MDL); + omapi_object_dereference (&obj -> outer, MDL); + } + omapi_object_dereference (&obj -> inner, MDL); + omapi_object_dereference (&h -> outer, MDL); + +#if SOCKDELETE + /* remove isc socket associations */ + if (obj->fd != NULL) { + isc_socket_cancel(obj->fd, dhcp_gbl_ctx.task, + ISC_SOCKCANCEL_ALL); + isc_socket_detach(&obj->fd); + } +#else + obj->closed = ISC_TRUE; +#endif + + omapi_io_dereference (&ph, MDL); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_dispatch (struct timeval *t) +{ + return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states, + t); +} + +isc_result_t omapi_wait_for_completion (omapi_object_t *object, + struct timeval *t) +{ + isc_result_t status; + omapi_waiter_object_t *waiter; + omapi_object_t *inner; + + if (object) { + waiter = (omapi_waiter_object_t *)0; + status = omapi_waiter_allocate (&waiter, MDL); + if (status != ISC_R_SUCCESS) + return status; + + /* Paste the waiter object onto the inner object we're + waiting on. */ + for (inner = object; inner -> inner; inner = inner -> inner) + ; + + status = omapi_object_reference (&waiter -> outer, inner, MDL); + if (status != ISC_R_SUCCESS) { + omapi_waiter_dereference (&waiter, MDL); + return status; + } + + status = omapi_object_reference (&inner -> inner, + (omapi_object_t *)waiter, + MDL); + if (status != ISC_R_SUCCESS) { + omapi_waiter_dereference (&waiter, MDL); + return status; + } + } else + waiter = (omapi_waiter_object_t *)0; + + do { + status = omapi_one_dispatch ((omapi_object_t *)waiter, t); + if (status != ISC_R_SUCCESS) + return status; + } while (!waiter || !waiter -> ready); + + if (waiter -> outer) { + if (waiter -> outer -> inner) { + omapi_object_dereference (&waiter -> outer -> inner, + MDL); + if (waiter -> inner) + omapi_object_reference + (&waiter -> outer -> inner, + waiter -> inner, MDL); + } + omapi_object_dereference (&waiter -> outer, MDL); + } + if (waiter -> inner) + omapi_object_dereference (&waiter -> inner, MDL); + + status = waiter -> waitstatus; + omapi_waiter_dereference (&waiter, MDL); + return status; +} + +isc_result_t omapi_one_dispatch (omapi_object_t *wo, + struct timeval *t) +{ + fd_set r, w, x, rr, ww, xx; + int max = 0; + int count; + int desc; + struct timeval now, to; + omapi_io_object_t *io, *prev, *next; + omapi_waiter_object_t *waiter; + omapi_object_t *tmp = (omapi_object_t *)0; + + if (!wo || wo -> type != omapi_type_waiter) + waiter = (omapi_waiter_object_t *)0; + else + waiter = (omapi_waiter_object_t *)wo; + + FD_ZERO (&x); + + /* First, see if the timeout has expired, and if so return. */ + if (t) { + gettimeofday (&now, (struct timezone *)0); + cur_tv.tv_sec = now.tv_sec; + cur_tv.tv_usec = now.tv_usec; + if (now.tv_sec > t -> tv_sec || + (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec)) + return ISC_R_TIMEDOUT; + + /* We didn't time out, so figure out how long until + we do. */ + to.tv_sec = t -> tv_sec - now.tv_sec; + to.tv_usec = t -> tv_usec - now.tv_usec; + if (to.tv_usec < 0) { + to.tv_usec += 1000000; + to.tv_sec--; + } + + /* It is possible for the timeout to get set larger than + the largest time select() is willing to accept. + Restricting the timeout to a maximum of one day should + work around this. -DPN. (Ref: Bug #416) */ + if (to.tv_sec > (60 * 60 * 24)) + to.tv_sec = 60 * 60 * 24; + } + + /* If the object we're waiting on has reached completion, + return now. */ + if (waiter && waiter -> ready) + return ISC_R_SUCCESS; + + again: + /* If we have no I/O state, we can't proceed. */ + if (!(io = omapi_io_states.next)) + return ISC_R_NOMORE; + + /* Set up the read and write masks. */ + FD_ZERO (&r); + FD_ZERO (&w); + + for (; io; io = io -> next) { + /* Check for a read socket. If we shouldn't be + trying to read for this I/O object, either there + won't be a readfd function, or it'll return -1. */ + if (io -> readfd && io -> inner && + (desc = (*(io -> readfd)) (io -> inner)) >= 0) { + FD_SET (desc, &r); + if (desc > max) + max = desc; + } + + /* Same deal for write fdets. */ + if (io -> writefd && io -> inner && + (desc = (*(io -> writefd)) (io -> inner)) >= 0) { + FD_SET (desc, &w); + if (desc > max) + max = desc; + } + } + + /* poll if all reader are dry */ + now.tv_sec = 0; + now.tv_usec = 0; + rr=r; + ww=w; + xx=x; + + /* poll once */ + count = select(max + 1, &r, &w, &x, &now); + if (!count) { + /* We are dry now */ + trigger_event(&rw_queue_empty); + /* Wait for a packet or a timeout... XXX */ + r = rr; + w = ww; + x = xx; + count = select(max + 1, &r, &w, &x, t ? &to : NULL); + } + + /* Get the current time... */ + gettimeofday (&cur_tv, (struct timezone *)0); + + /* We probably have a bad file descriptor. Figure out which one. + When we find it, call the reaper function on it, which will + maybe make it go away, and then try again. */ + if (count < 0) { + struct timeval t0; + omapi_io_object_t *prev = (omapi_io_object_t *)0; + io = (omapi_io_object_t *)0; + if (omapi_io_states.next) + omapi_io_reference (&io, omapi_io_states.next, MDL); + + while (io) { + omapi_object_t *obj; + FD_ZERO (&r); + FD_ZERO (&w); + t0.tv_sec = t0.tv_usec = 0; + + if (io -> readfd && io -> inner && + (desc = (*(io -> readfd)) (io -> inner)) >= 0) { + FD_SET (desc, &r); + count = select (desc + 1, &r, &w, &x, &t0); + bogon: + if (count < 0) { + log_error ("Bad descriptor %d.", desc); + for (obj = (omapi_object_t *)io; + obj -> outer; + obj = obj -> outer) + ; + for (; obj; obj = obj -> inner) { + omapi_value_t *ov; + int len; + const char *s; + ov = (omapi_value_t *)0; + omapi_get_value_str (obj, + (omapi_object_t *)0, + "name", &ov); + if (ov && ov -> value && + (ov -> value -> type == + omapi_datatype_string)) { + s = (char *) + ov -> value -> u.buffer.value; + len = ov -> value -> u.buffer.len; + } else { + s = ""; + len = 0; + } + log_error ("Object %lx %s%s%.*s", + (unsigned long)obj, + obj -> type -> name, + len ? " " : "", + len, s); + if (len) + omapi_value_dereference (&ov, MDL); + } + (*(io -> reaper)) (io -> inner); + if (prev) { + omapi_io_dereference (&prev -> next, MDL); + if (io -> next) + omapi_io_reference (&prev -> next, + io -> next, MDL); + } else { + omapi_io_dereference + (&omapi_io_states.next, MDL); + if (io -> next) + omapi_io_reference + (&omapi_io_states.next, + io -> next, MDL); + } + omapi_io_dereference (&io, MDL); + goto again; + } + } + + FD_ZERO (&r); + FD_ZERO (&w); + t0.tv_sec = t0.tv_usec = 0; + + /* Same deal for write fdets. */ + if (io -> writefd && io -> inner && + (desc = (*(io -> writefd)) (io -> inner)) >= 0) { + FD_SET (desc, &w); + count = select (desc + 1, &r, &w, &x, &t0); + if (count < 0) + goto bogon; + } + if (prev) + omapi_io_dereference (&prev, MDL); + omapi_io_reference (&prev, io, MDL); + omapi_io_dereference (&io, MDL); + if (prev -> next) + omapi_io_reference (&io, prev -> next, MDL); + } + if (prev) + omapi_io_dereference (&prev, MDL); + + } + + for (io = omapi_io_states.next; io; io = io -> next) { + if (!io -> inner) + continue; + omapi_object_reference (&tmp, io -> inner, MDL); + /* Check for a read descriptor, and if there is one, + see if we got input on that socket. */ + if (io -> readfd && + (desc = (*(io -> readfd)) (tmp)) >= 0) { + if (FD_ISSET (desc, &r)) + ((*(io -> reader)) (tmp)); + } + + /* Same deal for write descriptors. */ + if (io -> writefd && + (desc = (*(io -> writefd)) (tmp)) >= 0) + { + if (FD_ISSET (desc, &w)) + ((*(io -> writer)) (tmp)); + } + omapi_object_dereference (&tmp, MDL); + } + + /* Now check for I/O handles that are no longer valid, + and remove them from the list. */ + prev = NULL; + io = NULL; + if (omapi_io_states.next != NULL) { + omapi_io_reference(&io, omapi_io_states.next, MDL); + } + while (io != NULL) { + if ((io->inner == NULL) || + ((io->reaper != NULL) && + ((io->reaper)(io->inner) != ISC_R_SUCCESS))) + { + + omapi_io_object_t *tmp = NULL; + /* Save a reference to the next + pointer, if there is one. */ + if (io->next != NULL) { + omapi_io_reference(&tmp, io->next, MDL); + omapi_io_dereference(&io->next, MDL); + } + if (prev != NULL) { + omapi_io_dereference(&prev->next, MDL); + if (tmp != NULL) + omapi_io_reference(&prev->next, + tmp, MDL); + } else { + omapi_io_dereference(&omapi_io_states.next, + MDL); + if (tmp != NULL) + omapi_io_reference + (&omapi_io_states.next, + tmp, MDL); + else + omapi_signal_in( + (omapi_object_t *) + &omapi_io_states, + "ready"); + } + if (tmp != NULL) + omapi_io_dereference(&tmp, MDL); + + } else { + + if (prev != NULL) { + omapi_io_dereference(&prev, MDL); + } + omapi_io_reference(&prev, io, MDL); + } + + /* + * Equivalent to: + * io = io->next + * But using our reference counting voodoo. + */ + next = NULL; + if (io->next != NULL) { + omapi_io_reference(&next, io->next, MDL); + } + omapi_io_dereference(&io, MDL); + if (next != NULL) { + omapi_io_reference(&io, next, MDL); + omapi_io_dereference(&next, MDL); + } + } + if (prev != NULL) { + omapi_io_dereference(&prev, MDL); + } + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_io_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_io_object) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_io_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_io_object) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +/* omapi_io_destroy (object, MDL); + * + * Find the requested IO [object] and remove it from the list of io + * states, causing the cleanup functions to destroy it. Note that we must + * hold a reference on the object while moving its ->next reference and + * removing the reference in the chain to the target object...otherwise it + * may be cleaned up from under us. + */ +isc_result_t omapi_io_destroy (omapi_object_t *h, const char *file, int line) +{ + omapi_io_object_t *obj = NULL, *p, *last = NULL, **holder; + + if (h -> type != omapi_type_io_object) + return DHCP_R_INVALIDARG; + + /* remove from the list of I/O states */ + for (p = omapi_io_states.next; p; p = p -> next) { + if (p == (omapi_io_object_t *)h) { + omapi_io_reference (&obj, p, MDL); + + if (last) + holder = &last -> next; + else + holder = &omapi_io_states.next; + + omapi_io_dereference (holder, MDL); + + if (obj -> next) { + omapi_io_reference (holder, obj -> next, MDL); + omapi_io_dereference (&obj -> next, MDL); + } + + return omapi_io_dereference (&obj, MDL); + } + last = p; + } + + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_io_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_io_object) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_io_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *i) +{ + if (i -> type != omapi_type_io_object) + return DHCP_R_INVALIDARG; + + if (i -> inner && i -> inner -> type -> stuff_values) + return (*(i -> inner -> type -> stuff_values)) (c, id, + i -> inner); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_waiter_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + omapi_waiter_object_t *waiter; + + if (h -> type != omapi_type_waiter) + return DHCP_R_INVALIDARG; + + if (!strcmp (name, "ready")) { + waiter = (omapi_waiter_object_t *)h; + waiter -> ready = 1; + waiter -> waitstatus = ISC_R_SUCCESS; + return ISC_R_SUCCESS; + } + + if (!strcmp(name, "status")) { + waiter = (omapi_waiter_object_t *)h; + waiter->ready = 1; + waiter->waitstatus = va_arg(ap, isc_result_t); + return ISC_R_SUCCESS; + } + + if (!strcmp (name, "disconnect")) { + waiter = (omapi_waiter_object_t *)h; + waiter -> ready = 1; + waiter -> waitstatus = DHCP_R_CONNRESET; + return ISC_R_SUCCESS; + } + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +/** @brief calls a given function on every object + * + * @param func function to be called + * @param p parameter to be passed to each function instance + * + * @return result (ISC_R_SUCCESS if successful, error code otherwise) + */ +isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *, + void *), + void *p) +{ + omapi_io_object_t *io = NULL; + isc_result_t status; + omapi_io_object_t *next = NULL; + + /* + * This just calls func on every inner object on the list. It would + * be much simpler in general case, but one of the operations could be + * release of the objects. Therefore we need to ref count the io and + * io->next pointers. + */ + + if (omapi_io_states.next) { + omapi_object_reference((omapi_object_t**)&io, + (omapi_object_t*)omapi_io_states.next, + MDL); + } + + while(io) { + /* If there's a next object, save it */ + if (io->next) { + omapi_object_reference((omapi_object_t**)&next, + (omapi_object_t*)io->next, MDL); + } + if (io->inner) { + status = (*func) (io->inner, p); + if (status != ISC_R_SUCCESS) { + /* Something went wrong. Let's stop using io & next pointer + * and bail out */ + omapi_object_dereference((omapi_object_t**)&io, MDL); + if (next) { + omapi_object_dereference((omapi_object_t**)&next, MDL); + } + return status; + } + } + /* Update the io pointer and free the next pointer */ + omapi_object_dereference((omapi_object_t**)&io, MDL); + if (next) { + omapi_object_reference((omapi_object_t**)&io, + (omapi_object_t*)next, + MDL); + omapi_object_dereference((omapi_object_t**)&next, MDL); + } + } + + /* + * The only way to get here is when next is NULL. There's no need + * to dereference it. + */ + return ISC_R_SUCCESS; +} diff --git a/omapip/errwarn.c b/omapip/errwarn.c new file mode 100644 index 0000000..d68af04 --- /dev/null +++ b/omapip/errwarn.c @@ -0,0 +1,363 @@ +/* errwarn.c + + Errors and warnings... */ + +/* + * Copyright (c) 1995 RadioMail Corporation. + * Copyright (c) 2004,2007,2009,2014 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/ + * + * This software was written for RadioMail Corporation by Ted Lemon + * under a contract with Vixie Enterprises. Further modifications have + * been made for Internet Systems Consortium under a contract + * with Vixie Laboratories. + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include <errno.h> +#include <syslog.h> + +#ifdef DEBUG +int log_perror = -1; +#else +int log_perror = 1; +#endif +void (*log_cleanup) (void); + +#define CVT_BUF_MAX 1023 +static char mbuf [CVT_BUF_MAX + 1]; +static char fbuf [CVT_BUF_MAX + 1]; + +/* Log an error message, then exit... */ + +void log_fatal (const char * fmt, ... ) +{ + va_list list; + + do_percentm (fbuf, fmt); + + /* %Audit% This is log output. %2004.06.17,Safe% + * If we truncate we hope the user can get a hint from the log. + */ + va_start (list, fmt); + vsnprintf (mbuf, sizeof mbuf, fbuf, list); + va_end (list); + +#ifndef DEBUG + syslog (LOG_ERR, "%s", mbuf); +#endif + + /* Also log it to stderr? */ + if (log_perror) { + IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); + IGNORE_RET (write (STDERR_FILENO, "\n", 1)); + } + +#if !defined (NOMINUM) + log_error ("%s", ""); + log_error ("If you did not get this software from ftp.isc.org, please"); + log_error ("get the latest from ftp.isc.org and install that before"); + log_error ("requesting help."); + log_error ("%s", ""); + log_error ("If you did get this software from ftp.isc.org and have not"); + log_error ("yet read the README, please read it before requesting help."); + log_error ("If you intend to request help from the dhcp-bugs at isc.org"); + log_error ("mailing list, please read the section on the README about"); + log_error ("submitting bug reports and requests for help."); + log_error ("%s", ""); + log_error ("Please do not under any circumstances send requests for"); + log_error ("help directly to the authors of this software - please"); + log_error ("send them to the appropriate mailing list as described in"); + log_error ("the README file."); + log_error ("%s", ""); + log_error ("exiting."); +#endif + if (log_cleanup) + (*log_cleanup) (); + exit (1); +} + +/* Log an error message... */ + +int log_error (const char * fmt, ...) +{ + va_list list; + + do_percentm (fbuf, fmt); + + /* %Audit% This is log output. %2004.06.17,Safe% + * If we truncate we hope the user can get a hint from the log. + */ + va_start (list, fmt); + vsnprintf (mbuf, sizeof mbuf, fbuf, list); + va_end (list); + +#ifndef DEBUG + syslog (LOG_ERR, "%s", mbuf); +#endif + + if (log_perror) { + IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); + IGNORE_RET (write (STDERR_FILENO, "\n", 1)); + } + + return 0; +} + +/* Log a note... */ + +int log_info (const char *fmt, ...) +{ + va_list list; + + do_percentm (fbuf, fmt); + + /* %Audit% This is log output. %2004.06.17,Safe% + * If we truncate we hope the user can get a hint from the log. + */ + va_start (list, fmt); + vsnprintf (mbuf, sizeof mbuf, fbuf, list); + va_end (list); + +#ifndef DEBUG + syslog (LOG_INFO, "%s", mbuf); +#endif + + if (log_perror) { + IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); + IGNORE_RET (write (STDERR_FILENO, "\n", 1)); + } + + return 0; +} + +/* Log a debug message... */ + +int log_debug (const char *fmt, ...) +{ + va_list list; + + do_percentm (fbuf, fmt); + + /* %Audit% This is log output. %2004.06.17,Safe% + * If we truncate we hope the user can get a hint from the log. + */ + va_start (list, fmt); + vsnprintf (mbuf, sizeof mbuf, fbuf, list); + va_end (list); + +#ifndef DEBUG + syslog (LOG_DEBUG, "%s", mbuf); +#endif + + if (log_perror) { + IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf))); + IGNORE_RET (write (STDERR_FILENO, "\n", 1)); + } + + return 0; +} + +/* Find %m in the input string and substitute an error message string. */ + +void do_percentm (obuf, ibuf) + char *obuf; + const char *ibuf; +{ + const char *s = ibuf; + char *p = obuf; + int infmt = 0; + const char *m; + int len = 0; + + while (*s) { + if (infmt) { + if (*s == 'm') { +#ifndef __CYGWIN32__ + m = strerror (errno); +#else + m = pWSAError (); +#endif + if (!m) + m = "<unknown error>"; + len += strlen (m); + if (len > CVT_BUF_MAX) + goto out; + strcpy (p - 1, m); + p += strlen (p); + ++s; + } else { + if (++len > CVT_BUF_MAX) + goto out; + *p++ = *s++; + } + infmt = 0; + } else { + if (*s == '%') + infmt = 1; + if (++len > CVT_BUF_MAX) + goto out; + *p++ = *s++; + } + } + out: + *p = 0; +} + +#ifdef NO_STRERROR +char *strerror (err) + int err; +{ + extern char *sys_errlist []; + extern int sys_nerr; + static char errbuf [128]; + + if (err < 0 || err >= sys_nerr) { + sprintf (errbuf, "Error %d", err); + return errbuf; + } + return sys_errlist [err]; +} +#endif /* NO_STRERROR */ + +#ifdef _WIN32 +char *pWSAError () +{ + int err = WSAGetLastError (); + + switch (err) + { + case WSAEACCES: + return "Permission denied"; + case WSAEADDRINUSE: + return "Address already in use"; + case WSAEADDRNOTAVAIL: + return "Cannot assign requested address"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEALREADY: + return "Operation already in progress"; + case WSAECONNABORTED: + return "Software caused connection abort"; + case WSAECONNREFUSED: + return "Connection refused"; + case WSAECONNRESET: + return "Connection reset by peer"; + case WSAEDESTADDRREQ: + return "Destination address required"; + case WSAEFAULT: + return "Bad address"; + case WSAEHOSTDOWN: + return "Host is down"; + case WSAEHOSTUNREACH: + return "No route to host"; + case WSAEINPROGRESS: + return "Operation now in progress"; + case WSAEINTR: + return "Interrupted function call"; + case WSAEINVAL: + return "Invalid argument"; + case WSAEISCONN: + return "Socket is already connected"; + case WSAEMFILE: + return "Too many open files"; + case WSAEMSGSIZE: + return "Message too long"; + case WSAENETDOWN: + return "Network is down"; + case WSAENETRESET: + return "Network dropped connection on reset"; + case WSAENETUNREACH: + return "Network is unreachable"; + case WSAENOBUFS: + return "No buffer space available"; + case WSAENOPROTOOPT: + return "Bad protocol option"; + case WSAENOTCONN: + return "Socket is not connected"; + case WSAENOTSOCK: + return "Socket operation on non-socket"; + case WSAEOPNOTSUPP: + return "Operation not supported"; + case WSAEPFNOSUPPORT: + return "Protocol family not supported"; + case WSAEPROCLIM: + return "Too many processes"; + case WSAEPROTONOSUPPORT: + return "Protocol not supported"; + case WSAEPROTOTYPE: + return "Protocol wrong type for socket"; + case WSAESHUTDOWN: + return "Cannot send after socket shutdown"; + case WSAESOCKTNOSUPPORT: + return "Socket type not supported"; + case WSAETIMEDOUT: + return "Connection timed out"; + case WSAEWOULDBLOCK: + return "Resource temporarily unavailable"; + case WSAHOST_NOT_FOUND: + return "Host not found"; +#if 0 + case WSA_INVALID_HANDLE: + return "Specified event object handle is invalid"; + case WSA_INVALID_PARAMETER: + return "One or more parameters are invalid"; + case WSAINVALIDPROCTABLE: + return "Invalid procedure table from service provider"; + case WSAINVALIDPROVIDER: + return "Invalid service provider version number"; + case WSA_IO_PENDING: + return "Overlapped operations will complete later"; + case WSA_IO_INCOMPLETE: + return "Overlapped I/O event object not in signaled state"; + case WSA_NOT_ENOUGH_MEMORY: + return "Insufficient memory available"; +#endif + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performer"; + case WSANO_DATA: + return "Valid name, no data record of requested type"; + case WSANO_RECOVERY: + return "This is a non-recoverable error"; +#if 0 + case WSAPROVIDERFAILEDINIT: + return "Unable to initialize a service provider"; + case WSASYSCALLFAILURE: + return "System call failure"; +#endif + case WSASYSNOTREADY: + return "Network subsystem is unavailable"; + case WSATRY_AGAIN: + return "Non-authoritative host not found"; + case WSAVERNOTSUPPORTED: + return "WINSOCK.DLL version out of range"; + case WSAEDISCON: + return "Graceful shutdown in progress"; +#if 0 + case WSA_OPERATION_ABORTED: + return "Overlapped operation aborted"; +#endif + } + return "Unknown WinSock error"; +} +#endif /* _WIN32 */ diff --git a/omapip/generic.c b/omapip/generic.c new file mode 100644 index 0000000..6dee801 --- /dev/null +++ b/omapip/generic.c @@ -0,0 +1,299 @@ +/* generic.c + + Subroutines that support the generic object. */ + +/* + * Copyright (c) 2004-2007,2009,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +OMAPI_OBJECT_ALLOC (omapi_generic, + omapi_generic_object_t, omapi_type_generic) + +isc_result_t omapi_generic_new (omapi_object_t **gen, + const char *file, int line) +{ + /* Backwards compatibility. */ + return omapi_generic_allocate ((omapi_generic_object_t **)gen, + file, line); +} + +isc_result_t omapi_generic_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_generic_object_t *g; + omapi_value_t *new; + omapi_value_t **va; + u_int8_t *ca; + int vm_new; + int i, vfree = -1; + isc_result_t status; + + if (h -> type != omapi_type_generic) + return DHCP_R_INVALIDARG; + g = (omapi_generic_object_t *)h; + + /* See if there's already a value with this name attached to + the generic object, and if so, replace the current value + with the new one. */ + for (i = 0; i < g -> nvalues; i++) { + if (!omapi_data_string_cmp (name, g -> values [i] -> name)) { + /* There's an inconsistency here: the standard + behaviour of a set_values method when + passed a matching name and a null value is + to delete the value associated with that + name (where possible). In the generic + object, we remember the name/null pair, + because generic objects are generally used + to pass messages around, and this is the + way that remote entities delete values from + local objects. If the get_value method of + a generic object is called for a name that + maps to a name/null pair, ISC_R_NOTFOUND is + returned. */ + new = (omapi_value_t *)0; + status = (omapi_value_new (&new, MDL)); + if (status != ISC_R_SUCCESS) + return status; + omapi_data_string_reference (&new -> name, name, MDL); + if (value) + omapi_typed_data_reference (&new -> value, + value, MDL); + + omapi_value_dereference (&(g -> values [i]), MDL); + status = (omapi_value_reference + (&(g -> values [i]), new, MDL)); + omapi_value_dereference (&new, MDL); + g -> changed [i] = 1; + return status; + } + /* Notice a free slot if we pass one. */ + else if (vfree == -1 && !g -> values [i]) + vfree = i; + } + + /* If the name isn't already attached to this object, see if an + inner object has it. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status != ISC_R_NOTFOUND) + return status; + } + + /* Okay, so it's a value that no inner object knows about, and + (implicitly, since the outer object set_value method would + have called this object's set_value method) it's an object that + no outer object knows about, it's this object's responsibility + to remember it - that's what generic objects do. */ + + /* Arrange for there to be space for the pointer to the new + name/value pair if necessary: */ + if (vfree == -1) { + vfree = g -> nvalues; + if (vfree == g -> va_max) { + if (g -> va_max) + vm_new = 2 * g -> va_max; + else + vm_new = 10; + va = dmalloc (vm_new * sizeof *va, MDL); + if (!va) + return ISC_R_NOMEMORY; + ca = dmalloc (vm_new * sizeof *ca, MDL); + if (!ca) { + dfree (va, MDL); + return ISC_R_NOMEMORY; + } + if (g -> va_max) { + memcpy (va, g -> values, + g -> va_max * sizeof *va); + memcpy (ca, g -> changed, + g -> va_max * sizeof *ca); + } + memset (va + g -> va_max, 0, + (vm_new - g -> va_max) * sizeof *va); + memset (ca + g -> va_max, 0, + (vm_new - g -> va_max) * sizeof *ca); + if (g -> values) + dfree (g -> values, MDL); + if (g -> changed) + dfree (g -> changed, MDL); + g -> values = va; + g -> changed = ca; + g -> va_max = vm_new; + } + } + status = omapi_value_new (&g -> values [vfree], MDL); + if (status != ISC_R_SUCCESS) + return status; + omapi_data_string_reference (&g -> values [vfree] -> name, + name, MDL); + if (value) + omapi_typed_data_reference + (&g -> values [vfree] -> value, value, MDL); + g -> changed [vfree] = 1; + if (vfree == g -> nvalues) + g -> nvalues++; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_generic_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + int i; + omapi_generic_object_t *g; + + if (h -> type != omapi_type_generic) + return DHCP_R_INVALIDARG; + g = (omapi_generic_object_t *)h; + + /* Look up the specified name in our list of objects. */ + for (i = 0; i < g -> nvalues; i++) { + if (!g -> values[i]) + continue; + if (!omapi_data_string_cmp (name, g -> values [i] -> name)) { + /* If this is a name/null value pair, this is the + same as if there were no value that matched + the specified name, so return ISC_R_NOTFOUND. */ + if (!g -> values [i] -> value) + return ISC_R_NOTFOUND; + /* Otherwise, return the name/value pair. */ + return omapi_value_reference (value, + g -> values [i], MDL); + } + } + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_generic_destroy (omapi_object_t *h, + const char *file, int line) +{ + omapi_generic_object_t *g; + int i; + + if (h -> type != omapi_type_generic) + return ISC_R_UNEXPECTED; + g = (omapi_generic_object_t *)h; + + if (g -> values) { + for (i = 0; i < g -> nvalues; i++) { + if (g -> values [i]) + omapi_value_dereference (&g -> values [i], + file, line); + } + dfree (g -> values, file, line); + dfree (g -> changed, file, line); + g -> values = (omapi_value_t **)0; + g -> changed = (u_int8_t *)0; + g -> va_max = 0; + } + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_generic_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_generic) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_generic_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *g) +{ + omapi_generic_object_t *src; + int i; + isc_result_t status; + + if (g -> type != omapi_type_generic) + return DHCP_R_INVALIDARG; + src = (omapi_generic_object_t *)g; + + for (i = 0; i < src -> nvalues; i++) { + if (src -> values [i] && src -> values [i] -> name -> len && + src -> changed [i]) { + status = (omapi_connection_put_uint16 + (c, src -> values [i] -> name -> len)); + if (status != ISC_R_SUCCESS) + return status; + status = (omapi_connection_copyin + (c, src -> values [i] -> name -> value, + src -> values [i] -> name -> len)); + if (status != ISC_R_SUCCESS) + return status; + + status = (omapi_connection_write_typed_data + (c, src -> values [i] -> value)); + if (status != ISC_R_SUCCESS) + return status; + } + } + + if (g -> inner && g -> inner -> type -> stuff_values) + return (*(g -> inner -> type -> stuff_values)) (c, id, + g -> inner); + return ISC_R_SUCCESS; +} + +/* Clear the changed flags on the object. This has the effect that if + generic_stuff is called, any attributes that still have a cleared changed + flag aren't sent to the peer. This also deletes any values that are + null, presuming that these have now been properly handled. */ + +isc_result_t omapi_generic_clear_flags (omapi_object_t *o) +{ + int i; + omapi_generic_object_t *g; + + if (o -> type != omapi_type_generic) + return DHCP_R_INVALIDARG; + g = (omapi_generic_object_t *)o; + + for (i = 0; i < g -> nvalues; i++) { + g -> changed [i] = 0; + if (g -> values [i] && + !g -> values [i] -> value) + omapi_value_dereference (&g -> values [i], MDL); + } + return ISC_R_SUCCESS; +} diff --git a/omapip/handle.c b/omapip/handle.c new file mode 100644 index 0000000..cae5ec2 --- /dev/null +++ b/omapip/handle.c @@ -0,0 +1,302 @@ +/* handle.c + + Functions for maintaining handles on objects. */ + +/* + * Copyright (c) 2009-2010,2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +/* The handle table is a hierarchical tree designed for quick mapping + of handle identifiers to objects. Objects contain their own handle + identifiers if they have them, so the reverse mapping is also + quick. The hierarchy is made up of table objects, each of which + has 120 entries, a flag indicating whether the table is a leaf + table or an indirect table, the handle of the first object covered + by the table and the first object after that that's *not* covered + by the table, a count of how many objects of either type are + currently stored in the table, and an array of 120 entries pointing + either to objects or tables. + + When we go to add an object to the table, we look to see if the + next object handle to be assigned is covered by the outermost + table. If it is, we find the place within that table where the + next handle should go, and if necessary create additional nodes in + the tree to contain the new handle. The pointer to the object is + then stored in the correct position. + + Theoretically, we could have some code here to free up handle + tables as they go out of use, but by and large handle tables won't + go out of use, so this is being skipped for now. It shouldn't be + too hard to implement in the future if there's a different + application. */ + +omapi_handle_table_t *omapi_handle_table; +omapi_handle_t omapi_next_handle = 1; /* Next handle to be assigned. */ + +#define FIND_HAND 0 +#define CLEAR_HAND 1 + +static isc_result_t omapi_handle_lookup_in (omapi_object_t **, + omapi_handle_t, + omapi_handle_table_t *, + int); +static isc_result_t omapi_object_handle_in_table (omapi_handle_t, + omapi_handle_table_t *, + omapi_object_t *); +static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **); + +isc_result_t omapi_object_handle (omapi_handle_t *h, omapi_object_t *o) +{ + isc_result_t status; + + if (o -> handle) { + *h = o -> handle; + return ISC_R_SUCCESS; + } + + if (!omapi_handle_table) { + omapi_handle_table = dmalloc (sizeof *omapi_handle_table, MDL); + if (!omapi_handle_table) + return ISC_R_NOMEMORY; + memset (omapi_handle_table, 0, sizeof *omapi_handle_table); + omapi_handle_table -> first = 0; + omapi_handle_table -> limit = OMAPI_HANDLE_TABLE_SIZE; + omapi_handle_table -> leafp = 1; + } + + /* If this handle doesn't fit in the outer table, we need to + make a new outer table. This is a while loop in case for + some reason we decide to do disjoint handle allocation, + where the next level of indirection still isn't big enough + to enclose the next handle ID. */ + + while (omapi_next_handle >= omapi_handle_table -> limit) { + omapi_handle_table_t *new; + + new = dmalloc (sizeof *new, MDL); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, sizeof *new); + new -> first = 0; + new -> limit = (omapi_handle_table -> limit * + OMAPI_HANDLE_TABLE_SIZE); + new -> leafp = 0; + new -> children [0].table = omapi_handle_table; + omapi_handle_table = new; + } + + /* Try to cram this handle into the existing table. */ + status = omapi_object_handle_in_table (omapi_next_handle, + omapi_handle_table, o); + /* If it worked, return the next handle and increment it. */ + if (status == ISC_R_SUCCESS) { + *h = omapi_next_handle; + omapi_next_handle++; + return ISC_R_SUCCESS; + } + if (status != ISC_R_NOSPACE) + return status; + + status = omapi_handle_table_enclose (&omapi_handle_table); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_handle_in_table (omapi_next_handle, + omapi_handle_table, o); + if (status != ISC_R_SUCCESS) + return status; + *h = omapi_next_handle; + omapi_next_handle++; + + return ISC_R_SUCCESS; +} + +static isc_result_t omapi_object_handle_in_table (omapi_handle_t h, + omapi_handle_table_t *table, + omapi_object_t *o) +{ + omapi_handle_table_t *inner; + omapi_handle_t scale, index; + isc_result_t status; + + if (table -> first > h || table -> limit <= h) + return ISC_R_NOSPACE; + + /* If this is a leaf table, just stash the object in the + appropriate place. */ + if (table -> leafp) { + status = (omapi_object_reference + (&table -> children [h - table -> first].object, + o, MDL)); + if (status != ISC_R_SUCCESS) + return status; + o -> handle = h; + return ISC_R_SUCCESS; + } + + /* Scale is the number of handles represented by each child of this + table. For a leaf table, scale would be 1. For a first level + of indirection, 120. For a second, 120 * 120. Et cetera. */ + scale = (table -> limit - table -> first) / OMAPI_HANDLE_TABLE_SIZE; + + /* So the next most direct table from this one that contains the + handle must be the subtable of this table whose index into this + table's array of children is the handle divided by the scale. */ + index = (h - table -> first) / scale; + inner = table -> children [index].table; + + /* If there is no more direct table than this one in the slot + we came up with, make one. */ + if (!inner) { + inner = dmalloc (sizeof *inner, MDL); + if (!inner) + return ISC_R_NOMEMORY; + memset (inner, 0, sizeof *inner); + inner -> first = index * scale + table -> first; + inner -> limit = inner -> first + scale; + if (scale == OMAPI_HANDLE_TABLE_SIZE) + inner -> leafp = 1; + table -> children [index].table = inner; + } + + status = omapi_object_handle_in_table (h, inner, o); + if (status == ISC_R_NOSPACE) { + status = (omapi_handle_table_enclose + (&table -> children [index].table)); + if (status != ISC_R_SUCCESS) + return status; + + return omapi_object_handle_in_table + (h, table -> children [index].table, o); + } + return status; +} + +static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **table) +{ + omapi_handle_table_t *inner = *table; + omapi_handle_table_t *new; + int index, base, scale; + + /* The scale of the table we're enclosing is going to be the + difference between its "first" and "limit" members. So the + scale of the table enclosing it is going to be that multiplied + by the table size. */ + scale = (inner -> first - inner -> limit) * OMAPI_HANDLE_TABLE_SIZE; + + /* The range that the enclosing table covers is going to be + the result of subtracting the remainder of dividing the + enclosed table's first entry number by the enclosing + table's scale. If handle IDs are being allocated + sequentially, the enclosing table's "first" value will be + the same as the enclosed table's "first" value. */ + base = inner -> first - inner -> first % scale; + + /* The index into the enclosing table at which the enclosed table + will be stored is going to be the difference between the "first" + value of the enclosing table and the enclosed table - zero, if + we are allocating sequentially. */ + index = (base - inner -> first) / OMAPI_HANDLE_TABLE_SIZE; + + new = dmalloc (sizeof *new, MDL); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, sizeof *new); + new -> first = base; + new -> limit = base + scale; + if (scale == OMAPI_HANDLE_TABLE_SIZE) + new -> leafp = 0; + new -> children [index].table = inner; + *table = new; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_handle_lookup (omapi_object_t **o, omapi_handle_t h) +{ + return(omapi_handle_lookup_in(o, h, omapi_handle_table, FIND_HAND)); +} + +static isc_result_t omapi_handle_lookup_in (omapi_object_t **o, + omapi_handle_t h, + omapi_handle_table_t *table, + int op) +{ + omapi_handle_t scale, index; + + if (!table || table->first > h || table->limit <= h) + return(ISC_R_NOTFOUND); + + /* If this is a leaf table, just grab the object. */ + if (table->leafp) { + /* Not there? */ + if (!table->children[h - table->first].object) + return(ISC_R_NOTFOUND); + if (op == CLEAR_HAND) { + table->children[h - table->first].object = NULL; + return(ISC_R_SUCCESS); + } else { + return(omapi_object_reference + (o, table->children[h - table->first].object, + MDL)); + } + } + + /* Scale is the number of handles represented by each child of this + table. For a leaf table, scale would be 1. For a first level + of indirection, 120. For a second, 120 * 120. Et cetera. */ + scale = (table->limit - table->first) / OMAPI_HANDLE_TABLE_SIZE; + + /* So the next most direct table from this one that contains the + handle must be the subtable of this table whose index into this + table's array of children is the handle divided by the scale. */ + index = (h - table->first) / scale; + + return(omapi_handle_lookup_in(o, h, table->children[index].table, op)); +} + +/* For looking up objects based on handles that have been sent on the wire. */ +isc_result_t omapi_handle_td_lookup (omapi_object_t **obj, + omapi_typed_data_t *handle) +{ + omapi_handle_t h; + + if (handle->type == omapi_datatype_int) + h = handle->u.integer; + else if (handle->type == omapi_datatype_data && + handle->u.buffer.len == sizeof h) { + memcpy(&h, handle->u.buffer.value, sizeof h); + h = ntohl(h); + } else + return(DHCP_R_INVALIDARG); + return(omapi_handle_lookup(obj, h)); +} + +isc_result_t omapi_handle_clear(omapi_handle_t h) +{ + return(omapi_handle_lookup_in(NULL, h, omapi_handle_table, CLEAR_HAND)); +} diff --git a/omapip/hash.c b/omapip/hash.c new file mode 100644 index 0000000..b75d102 --- /dev/null +++ b/omapip/hash.c @@ -0,0 +1,560 @@ +/* hash.c + + Routines for manipulating hash tables... */ + +/* + * Copyright (c) 2009-2010,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include <limits.h> +#include <ctype.h> + +static unsigned +find_length(const void *key, + unsigned (*do_hash)(const void *, unsigned, unsigned)) +{ + if (do_hash == do_case_hash || do_hash == do_string_hash) + return strlen((const char *)key); + if (do_hash == do_number_hash) + return sizeof(unsigned); + if (do_hash == do_ip4_hash) + return 4; + + log_debug("Unexpected hash function at %s:%d.", MDL); + /* + * If we get a hash function we don't specifically expect + * return a length of 0, this covers the case where a client + * id has a length of 0. + */ + return 0; +} + +int new_hash_table (tp, count, file, line) + struct hash_table **tp; + unsigned count; + const char *file; + int line; +{ + struct hash_table *rval; + unsigned extra; + + if (!tp) { + log_error ("%s(%d): new_hash_table called with null pointer.", + file, line); +#if defined (POINTER_DEBUG) + abort (); +#endif + return 0; + } + if (*tp) { + log_error ("%s(%d): non-null target for new_hash_table.", + file, line); +#if defined (POINTER_DEBUG) + abort (); +#endif + } + + /* There is one hash bucket in the structure. Allocate extra + * memory beyond the end of the structure to fulfill the requested + * count ("count - 1"). Do not let there be less than one. + */ + if (count <= 1) + extra = 0; + else + extra = count - 1; + + rval = dmalloc(sizeof(struct hash_table) + + (extra * sizeof(struct hash_bucket *)), file, line); + if (!rval) + return 0; + rval -> hash_count = count; + *tp = rval; + return 1; +} + +void free_hash_table (tp, file, line) + struct hash_table **tp; + const char *file; + int line; +{ + struct hash_table *ptr = *tp; + +#if defined (DEBUG_MEMORY_LEAKAGE) || \ + defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + int i; + struct hash_bucket *hbc, *hbn = (struct hash_bucket *)0; + + for (i = 0; i < ptr -> hash_count; i++) { + for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) { + hbn = hbc -> next; + if (ptr -> dereferencer && hbc -> value) + (*ptr -> dereferencer) (&hbc -> value, MDL); + } + for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) { + hbn = hbc -> next; + free_hash_bucket (hbc, MDL); + } + ptr -> buckets [i] = (struct hash_bucket *)0; + } +#endif + + dfree((void *)ptr, MDL); + *tp = (struct hash_table *)0; +} + +struct hash_bucket *free_hash_buckets; + +#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +struct hash_bucket *hash_bucket_hunks; + +void relinquish_hash_bucket_hunks () +{ + struct hash_bucket *c, *n, **p; + + /* Account for all the hash buckets on the free list. */ + p = &free_hash_buckets; + for (c = free_hash_buckets; c; c = c -> next) { + for (n = hash_bucket_hunks; n; n = n -> next) { + if (c > n && c < n + 127) { + *p = c -> next; + n -> len++; + break; + } + } + /* If we didn't delete the hash bucket from the free list, + advance the pointer. */ + if (!n) + p = &c -> next; + } + + for (c = hash_bucket_hunks; c; c = n) { + n = c -> next; + if (c -> len != 126) { + log_info ("hashbucket %lx hash_buckets %d free %u", + (unsigned long)c, 127, c -> len); + } + dfree (c, MDL); + } +} +#endif + +struct hash_bucket *new_hash_bucket (file, line) + const char *file; + int line; +{ + struct hash_bucket *rval; + int i = 0; + if (!free_hash_buckets) { + rval = dmalloc (127 * sizeof (struct hash_bucket), + file, line); + if (!rval) + return rval; +# if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) + rval -> next = hash_bucket_hunks; + hash_bucket_hunks = rval; + hash_bucket_hunks -> len = 0; + i++; + rval++; +#endif + for (; i < 127; i++) { + rval -> next = free_hash_buckets; + free_hash_buckets = rval; + rval++; + } + } + rval = free_hash_buckets; + free_hash_buckets = rval -> next; + return rval; +} + +void free_hash_bucket (ptr, file, line) + struct hash_bucket *ptr; + const char *file; + int line; +{ +#if defined (DEBUG_MALLOC_POOL) + struct hash_bucket *hp; + + for (hp = free_hash_buckets; hp; hp = hp -> next) { + if (hp == ptr) { + log_error ("hash bucket freed twice!"); + abort (); + } + } +#endif + ptr -> next = free_hash_buckets; + free_hash_buckets = ptr; +} + +int new_hash(struct hash_table **rp, + hash_reference referencer, + hash_dereference dereferencer, + unsigned hsize, + unsigned (*hasher)(const void *, unsigned, unsigned), + const char *file, int line) +{ + if (hsize == 0) + hsize = DEFAULT_HASH_SIZE; + + if (!new_hash_table (rp, hsize, file, line)) + return 0; + + memset ((*rp)->buckets, 0, hsize * sizeof(struct hash_bucket *)); + + (*rp)->referencer = referencer; + (*rp)->dereferencer = dereferencer; + (*rp)->do_hash = hasher; + + if (hasher == do_case_hash) + (*rp)->cmp = casecmp; + else + (*rp)->cmp = memcmp; + + return 1; +} + +unsigned +do_case_hash(const void *name, unsigned len, unsigned size) +{ + register unsigned accum = 0; + register const unsigned char *s = name; + int i = len; + register unsigned c; + + while (i--) { + /* Make the hash case-insensitive. */ + c = *s++; + if (isascii(c)) + c = tolower(c); + + /* Add the character in... */ + accum = (accum << 1) + c; + + /* Add carry back in... */ + while (accum > 65535) { + accum = (accum & 65535) + (accum >> 16); + } + + } + return accum % size; +} + +unsigned +do_string_hash(const void *name, unsigned len, unsigned size) +{ + register unsigned accum = 0; + register const unsigned char *s = (const unsigned char *)name; + int i = len; + + while (i--) { + /* Add the character in... */ + accum = (accum << 1) + *s++; + + /* Add carry back in... */ + while (accum > 65535) { + accum = (accum & 65535) + (accum >> 16); + } + } + return accum % size; +} + +/* Client identifiers are generally 32-bits of ordinary + * non-randomness followed by 24-bits of unordinary randomness. + * So, end-align in 24-bit chunks, and xor any preceding data + * just to mix it up a little. + */ +unsigned +do_id_hash(const void *name, unsigned len, unsigned size) +{ + register unsigned accum = 0; + register const unsigned char *s = (const unsigned char *)name; + const unsigned char *end = s + len; + + if (len == 0) + return 0; + + /* + * The switch handles our starting conditions, then we hash the + * remaining bytes in groups of 3 + */ + + switch (len % 3) { + case 0: + break; + case 2: + accum ^= *s++ << 8; + case 1: + accum ^= *s++; + break; + } + + while (s < end) { + accum ^= *s++ << 16; + accum ^= *s++ << 8; + accum ^= *s++; + } + + return accum % size; +} + +unsigned +do_number_hash(const void *key, unsigned len, unsigned size) +{ + register unsigned number = *((const unsigned *)key); + + return number % size; +} + +unsigned +do_ip4_hash(const void *key, unsigned len, unsigned size) +{ + u_int32_t number; + + memcpy(&number, key, 4); + + number = ntohl(number); + + return number % size; +} + +unsigned char * +hash_report(struct hash_table *table) +{ + static unsigned char retbuf[sizeof("Contents/Size (%): " + "2147483647/2147483647 " + "(2147483647%). " + "Min/max: 2147483647/2147483647")]; + unsigned curlen, pct, contents=0, minlen=UINT_MAX, maxlen=0; + unsigned i; + struct hash_bucket *bp; + + if (table == NULL) + return (unsigned char *) "No table."; + + if (table->hash_count == 0) + return (unsigned char *) "Invalid hash table."; + + for (i = 0 ; i < table->hash_count ; i++) { + curlen = 0; + + bp = table->buckets[i]; + while (bp != NULL) { + curlen++; + bp = bp->next; + } + + if (curlen < minlen) + minlen = curlen; + if (curlen > maxlen) + maxlen = curlen; + + contents += curlen; + } + + if (contents >= (UINT_MAX / 100)) + pct = contents / ((table->hash_count / 100) + 1); + else + pct = (contents * 100) / table->hash_count; + + if (contents > 2147483647 || + table->hash_count > 2147483647 || + pct > 2147483647 || + minlen > 2147483647 || + maxlen > 2147483647) + return (unsigned char *) "Report out of range for display."; + + sprintf((char *)retbuf, + "Contents/Size (%%): %u/%u (%u%%). Min/max: %u/%u", + contents, table->hash_count, pct, minlen, maxlen); + + return retbuf; +} + +void add_hash (table, key, len, pointer, file, line) + struct hash_table *table; + unsigned len; + const void *key; + hashed_object_t *pointer; + const char *file; + int line; +{ + int hashno; + struct hash_bucket *bp; + void *foo; + + if (!table) + return; + + if (!len) + len = find_length(key, table->do_hash); + + hashno = (*table->do_hash)(key, len, table->hash_count); + bp = new_hash_bucket (file, line); + + if (!bp) { + log_error ("Can't add entry to hash table: no memory."); + return; + } + bp -> name = key; + if (table -> referencer) { + foo = &bp -> value; + (*(table -> referencer)) (foo, pointer, file, line); + } else + bp -> value = pointer; + bp -> next = table -> buckets [hashno]; + bp -> len = len; + table -> buckets [hashno] = bp; +} + +void delete_hash_entry (table, key, len, file, line) + struct hash_table *table; + unsigned len; + const void *key; + const char *file; + int line; +{ + int hashno; + struct hash_bucket *bp, *pbp = (struct hash_bucket *)0; + void *foo; + + if (!table) + return; + + if (!len) + len = find_length(key, table->do_hash); + + hashno = (*table->do_hash)(key, len, table->hash_count); + + /* Go through the list looking for an entry that matches; + if we find it, delete it. */ + for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { + if ((!bp -> len && + !strcmp ((const char *)bp->name, key)) || + (bp -> len == len && + !(table -> cmp)(bp->name, key, len))) { + if (pbp) { + pbp -> next = bp -> next; + } else { + table -> buckets [hashno] = bp -> next; + } + if (bp -> value && table -> dereferencer) { + foo = &bp -> value; + (*(table -> dereferencer)) (foo, file, line); + } + free_hash_bucket (bp, file, line); + break; + } + pbp = bp; /* jwg, 9/6/96 - nice catch! */ + } +} + +int hash_lookup (vp, table, key, len, file, line) + hashed_object_t **vp; + struct hash_table *table; + const void *key; + unsigned len; + const char *file; + int line; +{ + int hashno; + struct hash_bucket *bp; + + if (!table) + return 0; + if (!len) + len = find_length(key, table->do_hash); + + if (*vp != NULL) { + log_fatal("Internal inconsistency: storage value has not been " + "initialized to zero (from %s:%d).", file, line); + } + + hashno = (*table->do_hash)(key, len, table->hash_count); + + for (bp = table -> buckets [hashno]; bp; bp = bp -> next) { + if (len == bp -> len + && !(*table->cmp)(bp->name, key, len)) { + if (table -> referencer) + (*table -> referencer) (vp, bp -> value, + file, line); + else + *vp = bp -> value; + return 1; + } + } + return 0; +} + +int hash_foreach (struct hash_table *table, hash_foreach_func func) +{ + int i; + struct hash_bucket *bp, *next; + int count = 0; + + if (!table) + return 0; + + for (i = 0; i < table -> hash_count; i++) { + bp = table -> buckets [i]; + while (bp) { + next = bp -> next; + if ((*func)(bp->name, bp->len, bp->value) + != ISC_R_SUCCESS) + return count; + bp = next; + count++; + } + } + return count; +} + +int casecmp (const void *v1, const void *v2, size_t len) +{ + size_t i; + const unsigned char *s = v1; + const unsigned char *t = v2; + + for (i = 0; i < len; i++) + { + int c1, c2; + if (isascii(s[i])) + c1 = tolower(s[i]); + else + c1 = s[i]; + + if (isascii(t[i])) + c2 = tolower(t[i]); + else + c2 = t[i]; + + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } + return 0; +} diff --git a/omapip/inet_addr.c b/omapip/inet_addr.c new file mode 100644 index 0000000..611d9ee --- /dev/null +++ b/omapip/inet_addr.c @@ -0,0 +1,135 @@ +/* $NetBSD: inet_addr.c,v 1.6 1996/02/02 15:22:23 mrg Exp $ */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "dhcpd.h" + +#include "omapip/omapip_p.h" + +#ifdef NEED_INET_ATON +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(cp, addr) + const char *cp; + struct in_addr *addr; +{ + register u_long val; + register int base, n; + register char c; + u_int parts[4]; + register u_int *pp = parts; + + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, other=decimal. + */ + val = 0; base = 10; + if (*cp == '0') { + if (*++cp == 'x' || *cp == 'X') + base = 16, cp++; + else + base = 8; + } + while ((c = *cp) != '\0') { + if (isascii(c) && isdigit((int)c)) { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isascii(c) && isxdigit((int)c)) { + val = (val << 4) + + (c + 10 - (islower((int)c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xff) + return (0); + *pp++ = val, cp++; + } else + break; + } + /* + * Check for trailing characters. + */ + if (*cp && (!isascii(*cp) || !isspace((int)*cp))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} +#endif diff --git a/omapip/isclib.c b/omapip/isclib.c new file mode 100644 index 0000000..ce42e45 --- /dev/null +++ b/omapip/isclib.c @@ -0,0 +1,276 @@ +/* + * Copyright(c) 2009-2010,2013-2014 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/ + * + */ + +/*Trying to figure out what we need to define to get things to work. + It looks like we want/need the export library but need the fdwatchcommand + which may be a problem */ + +#include "dhcpd.h" + +#include <sys/time.h> +#include <signal.h> + +dhcp_context_t dhcp_gbl_ctx; +int shutdown_signal = 0; + +void +isclib_cleanup(void) +{ +#if defined (NSUPDATE) + if (dhcp_gbl_ctx.dnsclient != NULL) + dns_client_destroy((dns_client_t **)&dhcp_gbl_ctx.dnsclient); +#endif + + if (dhcp_gbl_ctx.task != NULL) { + isc_task_shutdown(dhcp_gbl_ctx.task); + isc_task_detach(&dhcp_gbl_ctx.task); + } + + if (dhcp_gbl_ctx.timermgr != NULL) + isc_timermgr_destroy(&dhcp_gbl_ctx.timermgr); + + if (dhcp_gbl_ctx.socketmgr != NULL) + isc_socketmgr_destroy(&dhcp_gbl_ctx.socketmgr); + + if (dhcp_gbl_ctx.taskmgr != NULL) + isc_taskmgr_destroy(&dhcp_gbl_ctx.taskmgr); + + if (dhcp_gbl_ctx.actx_started != ISC_FALSE) { + isc_app_ctxfinish(dhcp_gbl_ctx.actx); + dhcp_gbl_ctx.actx_started = ISC_FALSE; + } + + if (dhcp_gbl_ctx.actx != NULL) + isc_appctx_destroy(&dhcp_gbl_ctx.actx); + + if (dhcp_gbl_ctx.mctx != NULL) + isc_mem_detach(&dhcp_gbl_ctx.mctx); + + return; +} + +/* Installs a handler for a signal using sigaction */ +static void +handle_signal(int sig, void (*handler)(int)) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigfillset(&sa.sa_mask); + if (sigaction(sig, &sa, NULL) != 0) { + log_debug("handle_signal() failed for signal %d error: %s", + sig, strerror(errno)); + } +} + +isc_result_t +dhcp_context_create(void) { + isc_result_t result; + + /* + * Set up the error messages, this isn't the right place + * for this call but it is convienent for now. + */ + result = dhcp_result_register(); + if (result != ISC_R_SUCCESS) { + log_fatal("register_table() %s: %u", "failed", result); + } + + memset(&dhcp_gbl_ctx, 0, sizeof (dhcp_gbl_ctx)); + + isc_lib_register(); + + /* get the current time for use as the random seed */ + gettimeofday(&cur_tv, (struct timezone *)0); + isc_random_seed(cur_tv.tv_sec); + + /* we need to create the memory context before + * the lib inits in case we aren't doing NSUPDATE + * in which case dst needs a memory context + */ + result = isc_mem_create(0, 0, &dhcp_gbl_ctx.mctx); + if (result != ISC_R_SUCCESS) + goto cleanup; + +#if defined (NSUPDATE) + result = dns_lib_init(); + if (result != ISC_R_SUCCESS) + goto cleanup; +#else + /* The dst library is inited as part of dns_lib_init, we don't + * need it if NSUPDATE is enabled */ + result = dst_lib_init(dhcp_gbl_ctx.mctx, NULL, 0); + if (result != ISC_R_SUCCESS) + goto cleanup; +#endif + + result = isc_appctx_create(dhcp_gbl_ctx.mctx, &dhcp_gbl_ctx.actx); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = isc_app_ctxstart(dhcp_gbl_ctx.actx); + if (result != ISC_R_SUCCESS) + return (result); + dhcp_gbl_ctx.actx_started = ISC_TRUE; + + /* 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 */ + handle_signal(SIGPIPE, SIG_IGN); + + result = isc_taskmgr_createinctx(dhcp_gbl_ctx.mctx, + dhcp_gbl_ctx.actx, + 1, 0, + &dhcp_gbl_ctx.taskmgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = isc_socketmgr_createinctx(dhcp_gbl_ctx.mctx, + dhcp_gbl_ctx.actx, + &dhcp_gbl_ctx.socketmgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = isc_timermgr_createinctx(dhcp_gbl_ctx.mctx, + dhcp_gbl_ctx.actx, + &dhcp_gbl_ctx.timermgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0, &dhcp_gbl_ctx.task); + if (result != ISC_R_SUCCESS) + goto cleanup; + +#if defined (NSUPDATE) + result = dns_client_createx(dhcp_gbl_ctx.mctx, + dhcp_gbl_ctx.actx, + dhcp_gbl_ctx.taskmgr, + dhcp_gbl_ctx.socketmgr, + dhcp_gbl_ctx.timermgr, + 0, + &dhcp_gbl_ctx.dnsclient); + if (result != ISC_R_SUCCESS) + goto cleanup; +#endif + return(ISC_R_SUCCESS); + + cleanup: + /* + * Currently we don't try and cleanup, just return an error + * expecting that our caller will log the error and exit. + */ + + return(result); +} + +/* + * Convert a string name into the proper structure for the isc routines + * + * Previously we allowed names without a trailing '.' however the current + * dns and dst code requires the names to end in a period. If the + * name doesn't have a trailing period add one as part of creating + * the dns name. + */ + +isc_result_t +dhcp_isc_name(unsigned char *namestr, + dns_fixedname_t *namefix, + dns_name_t **name) +{ + size_t namelen; + isc_buffer_t b; + isc_result_t result; + + namelen = strlen((char *)namestr); + isc_buffer_init(&b, namestr, namelen); + isc_buffer_add(&b, namelen); + dns_fixedname_init(namefix); + *name = dns_fixedname_name(namefix); + result = dns_name_fromtext(*name, &b, dns_rootname, 0, NULL); + isc_buffer_invalidate(&b); + return(result); +} + +isc_result_t +isclib_make_dst_key(char *inname, + char *algorithm, + unsigned char *secret, + int length, + dst_key_t **dstkey) +{ + isc_result_t result; + dns_name_t *name; + dns_fixedname_t name0; + isc_buffer_t b; + unsigned int algorithm_code; + + isc_buffer_init(&b, secret, length); + isc_buffer_add(&b, length); + + if (strcasecmp(algorithm, DHCP_HMAC_MD5_NAME) == 0) { + algorithm_code = DST_ALG_HMACMD5; + } else if (strcasecmp(algorithm, DHCP_HMAC_SHA1_NAME) == 0) { + algorithm_code = DST_ALG_HMACSHA1; + } else if (strcasecmp(algorithm, DHCP_HMAC_SHA224_NAME) == 0) { + algorithm_code = DST_ALG_HMACSHA224; + } else if (strcasecmp(algorithm, DHCP_HMAC_SHA256_NAME) == 0) { + algorithm_code = DST_ALG_HMACSHA256; + } else if (strcasecmp(algorithm, DHCP_HMAC_SHA384_NAME) == 0) { + algorithm_code = DST_ALG_HMACSHA384; + } else if (strcasecmp(algorithm, DHCP_HMAC_SHA512_NAME) == 0) { + algorithm_code = DST_ALG_HMACSHA512; + } else { + return(DHCP_R_INVALIDARG); + } + + result = dhcp_isc_name((unsigned char *)inname, &name0, &name); + if (result != ISC_R_SUCCESS) { + return(result); + } + + return(dst_key_frombuffer(name, algorithm_code, DNS_KEYOWNER_ENTITY, + DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, + &b, dhcp_gbl_ctx.mctx, dstkey)); +} + +/** + * signal handler that initiates server shutdown + * + * @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) { + /* 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); + } +} diff --git a/omapip/iscprint.c b/omapip/iscprint.c new file mode 100644 index 0000000..dd75e9f --- /dev/null +++ b/omapip/iscprint.c @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: iscprint.c,v 1.2 2005/03/17 20:30:41 dhankins Exp $ */ + +#include "dhcpd.h" + +#ifdef NO_SNPRINTF + +#ifndef LINT +static char copyright[] = +"$Id: iscprint.c,v 1.2 2005/03/17 20:30:41 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium, Inc. All rights reserved."; +#endif + +#define INSIST(cond) REQUIRE(cond) +#define REQUIRE(cond) if (!(cond)) { return 0; } + +/* + * Return length of string that would have been written if not truncated. + */ + +int +isc_print_snprintf(char *str, size_t size, const char *format, ...) { + va_list ap; + int ret; + + va_start(ap, format); + ret = vsnprintf(str, size, format, ap); + va_end(ap); + return (ret); +} + +/* + * Return length of string that would have been written if not truncated. + */ + +int +isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { + int h; + int l; + int q; + int alt; + int zero; + int left; + int plus; + int space; + int neg; + isc_int64_t tmpi; + isc_uint64_t tmpui; + unsigned long width; + unsigned long precision; + unsigned int length; + char buf[1024]; + char c; + void *v; + char *save = str; + const char *cp; + const char *head; + int count = 0; + int pad; + int zeropad; + int dot; + double dbl; +#ifdef HAVE_LONG_DOUBLE + long double ldbl; +#endif + char fmt[32]; + + INSIST(str != NULL); + INSIST(format != NULL); + + while (*format != '\0') { + if (*format != '%') { + if (size > 1) { + *str++ = *format; + size--; + } + count++; + format++; + continue; + } + format++; + + /* + * Reset flags. + */ + dot = neg = space = plus = left = zero = alt = h = l = q = 0; + width = precision = 0; + head = ""; + length = pad = zeropad = 0; + + do { + if (*format == '#') { + alt = 1; + format++; + } else if (*format == '-') { + left = 1; + zero = 0; + format++; + } else if (*format == ' ') { + if (!plus) + space = 1; + format++; + } else if (*format == '+') { + plus = 1; + space = 0; + format++; + } else if (*format == '0') { + if (!left) + zero = 1; + format++; + } else + break; + } while (1); + + /* + * Width. + */ + if (*format == '*') { + width = va_arg(ap, int); + format++; + } else if (isdigit((unsigned char)*format)) { + char *e; + width = strtoul(format, &e, 10); + format = e; + } + + /* + * Precision. + */ + if (*format == '.') { + format++; + dot = 1; + if (*format == '*') { + precision = va_arg(ap, int); + format++; + } else if (isdigit((unsigned char)*format)) { + char *e; + precision = strtoul(format, &e, 10); + format = e; + } + } + + switch (*format) { + case '\0': + continue; + case '%': + if (size > 1) { + *str++ = *format; + size--; + } + count++; + break; + case 'q': + q = 1; + format++; + goto doint; + case 'h': + h = 1; + format++; + goto doint; + case 'l': + l = 1; + format++; + if (*format == 'l') { + q = 1; + format++; + } + goto doint; + case 'n': + case 'i': + case 'd': + case 'o': + case 'u': + case 'x': + case 'X': + doint: + if (precision != 0) + zero = 0; + switch (*format) { + case 'n': + if (h) { + short int *p; + p = va_arg(ap, short *); + REQUIRE(p != NULL); + *p = str - save; + } else if (l) { + long int *p; + p = va_arg(ap, long *); + REQUIRE(p != NULL); + *p = str - save; + } else { + int *p; + p = va_arg(ap, int *); + REQUIRE(p != NULL); + *p = str - save; + } + break; + case 'i': + case 'd': + if (q) + tmpi = va_arg(ap, isc_int64_t); + else if (l) + tmpi = va_arg(ap, long int); + else + tmpi = va_arg(ap, int); + if (tmpi < 0) { + head = "-"; + tmpui = -tmpi; + } else { + if (plus) + head = "+"; + else if (space) + head = " "; + else + head = ""; + tmpui = tmpi; + } + sprintf(buf, "%u", tmpui); + goto printint; + case 'o': + if (q) + tmpui = va_arg(ap, isc_uint64_t); + else if (l) + tmpui = va_arg(ap, long int); + else + tmpui = va_arg(ap, int); + sprintf(buf, alt ? "%#o" + : "%o", tmpui); + goto printint; + case 'u': + if (q) + tmpui = va_arg(ap, isc_uint64_t); + else if (l) + tmpui = va_arg(ap, unsigned long int); + else + tmpui = va_arg(ap, unsigned int); + sprintf(buf, "%u", tmpui); + goto printint; + case 'x': + if (q) + tmpui = va_arg(ap, isc_uint64_t); + else if (l) + tmpui = va_arg(ap, unsigned long int); + else + tmpui = va_arg(ap, unsigned int); + if (alt) { + head = "0x"; + if (precision > 2) + precision -= 2; + } + sprintf(buf, "%x", tmpui); + goto printint; + case 'X': + if (q) + tmpui = va_arg(ap, isc_uint64_t); + else if (l) + tmpui = va_arg(ap, unsigned long int); + else + tmpui = va_arg(ap, unsigned int); + if (alt) { + head = "0X"; + if (precision > 2) + precision -= 2; + } + sprintf(buf, "%X", tmpui); + goto printint; + printint: + if (precision != 0 || width != 0) { + length = strlen(buf); + if (length < precision) + zeropad = precision - length; + else if (length < width && zero) + zeropad = width - length; + if (width != 0) { + pad = width - length - + zeropad - strlen(head); + if (pad < 0) + pad = 0; + } + } + count += strlen(head) + strlen(buf) + pad + + zeropad; + if (!left) { + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + } + cp = head; + while (*cp != '\0' && size > 1) { + *str++ = *cp++; + size--; + } + while (zeropad > 0 && size > 1) { + *str++ = '0'; + size--; + zeropad--; + } + cp = buf; + while (*cp != '\0' && size > 1) { + *str++ = *cp++; + size--; + } + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + break; + default: + break; + } + break; + case 's': + cp = va_arg(ap, char *); + REQUIRE(cp != NULL); + + if (precision != 0) { + /* + * cp need not be NULL terminated. + */ + const char *tp; + unsigned long n; + + n = precision; + tp = cp; + while (n != 0 && *tp != '\0') + n--, tp++; + length = precision - n; + } else { + length = strlen(cp); + } + if (width != 0) { + pad = width - length; + if (pad < 0) + pad = 0; + } + count += pad + length; + if (!left) + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + if (precision != 0) + while (precision > 0 && *cp != '\0' && + size > 1) { + *str++ = *cp++; + size--; + precision--; + } + else + while (*cp != '\0' && size > 1) { + *str++ = *cp++; + size--; + } + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + break; + case 'c': + c = va_arg(ap, int); + if (width > 0) { + count += width; + width--; + if (left) { + *str++ = c; + size--; + } + while (width-- > 0 && size > 1) { + *str++ = ' '; + size--; + } + if (!left && size > 1) { + *str++ = c; + size--; + } + } else { + count++; + if (size > 1) { + *str++ = c; + size--; + } + } + break; + case 'p': + v = va_arg(ap, void *); + sprintf(buf, "%p", v); + length = strlen(buf); + if (precision > length) + zeropad = precision - length; + if (width > 0) { + pad = width - length - zeropad; + if (pad < 0) + pad = 0; + } + count += length + pad + zeropad; + if (!left) + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + cp = buf; + if (zeropad > 0 && buf[0] == '0' && + (buf[1] == 'x' || buf[1] == 'X')) { + if (size > 1) { + *str++ = *cp++; + size--; + } + if (size > 1) { + *str++ = *cp++; + size--; + } + while (zeropad > 0 && size > 1) { + *str++ = '0'; + size--; + zeropad--; + } + } + while (*cp != '\0' && size > 1) { + *str++ = *cp++; + size--; + } + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + break; + case 'D': /*deprecated*/ + INSIST("use %ld instead of %D" == NULL); + case 'O': /*deprecated*/ + INSIST("use %lo instead of %O" == NULL); + case 'U': /*deprecated*/ + INSIST("use %lu instead of %U" == NULL); + + case 'L': +#ifdef HAVE_LONG_DOUBLE + l = 1; +#else + INSIST("long doubles are not supported" == NULL); +#endif + /*FALLTHROUGH*/ + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!dot) + precision = 6; + /* + * IEEE floating point. + * MIN 2.2250738585072014E-308 + * MAX 1.7976931348623157E+308 + * VAX floating point has a smaller range than IEEE. + * + * precisions > 324 don't make much sense. + * if we cap the precision at 512 we will not + * overflow buf. + */ + if (precision > 512) + precision = 512; + sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", + plus ? "+" : space ? " " : "", + precision, l ? "L" : "", *format); + switch (*format) { + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': +#ifdef HAVE_LONG_DOUBLE + if (l) { + ldbl = va_arg(ap, long double); + sprintf(buf, fmt, ldbl); + } else +#endif + { + dbl = va_arg(ap, double); + sprintf(buf, fmt, dbl); + } + length = strlen(buf); + if (width > 0) { + pad = width - length; + if (pad < 0) + pad = 0; + } + count += length + pad; + if (!left) + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + cp = buf; + while (*cp != ' ' && size > 1) { + *str++ = *cp++; + size--; + } + while (pad > 0 && size > 1) { + *str++ = ' '; + size--; + pad--; + } + break; + default: + continue; + } + break; + default: + continue; + } + format++; + } + if (size > 0) + *str = '\0'; + return (count); +} + +#endif diff --git a/omapip/listener.c b/omapip/listener.c new file mode 100644 index 0000000..8bdcdbd --- /dev/null +++ b/omapip/listener.c @@ -0,0 +1,484 @@ +/* listener.c + + Subroutines that support the generic listener object. */ + +/* + * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include <errno.h> + +#if defined (TRACING) +omapi_array_t *trace_listeners; +static void trace_listener_accept_input (trace_type_t *, unsigned, char *); +static void trace_listener_remember (omapi_listener_object_t *, + const char *, int); +static void trace_listener_accept_stop (trace_type_t *); +trace_type_t *trace_listener_accept; +#endif + +OMAPI_OBJECT_ALLOC (omapi_listener, + omapi_listener_object_t, omapi_type_listener) + +isc_result_t omapi_listen (omapi_object_t *h, + unsigned port, + int max) +{ + omapi_addr_t addr; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_listen(port=%d, max=%d)", port, max); +#endif + + addr.addrtype = AF_INET; + addr.addrlen = sizeof (struct in_addr); + memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */ + addr.port = port; + + return omapi_listen_addr (h, &addr, max); +} + +isc_result_t omapi_listen_addr (omapi_object_t *h, + omapi_addr_t *addr, + int max) +{ + isc_result_t status; + omapi_listener_object_t *obj; + int i; + + /* Currently only support IPv4 addresses. */ + if (addr->addrtype != AF_INET) + return DHCP_R_INVALIDARG; + + /* Get the handle. */ + obj = (omapi_listener_object_t *)0; + status = omapi_listener_allocate (&obj, MDL); + if (status != ISC_R_SUCCESS) + /* + * we could simply return here but by going to + * error_exit we keep the code check tools happy + * without removing the NULL check on obj at + * the exit, which we could skip curently but + * might want in the future. + */ + goto error_exit; + obj->socket = -1; + + /* Connect this object to the inner object. */ + status = omapi_object_reference (&h -> outer, + (omapi_object_t *)obj, MDL); + if (status != ISC_R_SUCCESS) + goto error_exit; + status = omapi_object_reference (&obj -> inner, h, MDL); + if (status != ISC_R_SUCCESS) + goto error_exit; + + /* Set up the address on which we will listen... */ + obj -> address.sin_port = htons (addr -> port); + memcpy (&obj -> address.sin_addr, + addr -> address, sizeof obj -> address.sin_addr); +#if defined (HAVE_SA_LEN) + obj -> address.sin_len = + sizeof (struct sockaddr_in); +#endif + obj -> address.sin_family = AF_INET; + memset (&(obj -> address.sin_zero), 0, + sizeof obj -> address.sin_zero); + +#if defined (TRACING) + /* If we're playing back a trace file, we remember the object + on the trace listener queue. */ + if (trace_playback ()) { + trace_listener_remember (obj, MDL); + } else { +#endif + /* Create a socket on which to listen. */ + obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (obj->socket == -1) { + if (errno == EMFILE + || errno == ENFILE || errno == ENOBUFS) + status = ISC_R_NORESOURCES; + else + status = ISC_R_UNEXPECTED; + goto error_exit; + } + +#if defined (HAVE_SETFD) + if (fcntl (obj -> socket, F_SETFD, 1) < 0) { + status = ISC_R_UNEXPECTED; + goto error_exit; + } +#endif + + /* Set the REUSEADDR option so that we don't fail to start if + we're being restarted. */ + i = 1; + if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&i, sizeof i) < 0) { + status = ISC_R_UNEXPECTED; + goto error_exit; + } + + /* Try to bind to the wildcard address using the port number + we were given. */ + i = bind (obj -> socket, + (struct sockaddr *)&obj -> address, + sizeof obj -> address); + if (i < 0) { + if (errno == EADDRINUSE) + status = ISC_R_ADDRNOTAVAIL; + else if (errno == EPERM) + status = ISC_R_NOPERM; + else + status = ISC_R_UNEXPECTED; + goto error_exit; + } + + /* Now tell the kernel to listen for connections. */ + if (listen (obj -> socket, max)) { + status = ISC_R_UNEXPECTED; + goto error_exit; + } + + if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { + status = ISC_R_UNEXPECTED; + goto error_exit; + } + + status = omapi_register_io_object ((omapi_object_t *)obj, + omapi_listener_readfd, 0, + omapi_accept, 0, 0); +#if defined (TRACING) + } +#endif + + omapi_listener_dereference (&obj, MDL); + return status; + +error_exit: + if (obj != NULL) { + if (h->outer == (omapi_object_t *)obj) { + omapi_object_dereference((omapi_object_t **)&h->outer, + MDL); + } + if (obj->inner == h) { + omapi_object_dereference((omapi_object_t **)&obj->inner, + MDL); + } + if (obj->socket != -1) { + close(obj->socket); + } + omapi_listener_dereference(&obj, MDL); + } + return status; +} + +/* Return the socket on which the dispatcher should wait for readiness + to read, for a listener object. */ +int omapi_listener_readfd (omapi_object_t *h) +{ + omapi_listener_object_t *l; + + if (h -> type != omapi_type_listener) + return -1; + l = (omapi_listener_object_t *)h; + + return l -> socket; +} + +/* Reader callback for a listener object. Accept an incoming connection. */ +isc_result_t omapi_accept (omapi_object_t *h) +{ + isc_result_t status; + socklen_t len; + omapi_connection_object_t *obj; + omapi_listener_object_t *listener; + struct sockaddr_in addr; + int socket; + + if (h -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + listener = (omapi_listener_object_t *)h; + + /* Accept the connection. */ + len = sizeof addr; + socket = accept (listener -> socket, + ((struct sockaddr *)&(addr)), &len); + if (socket < 0) { + if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) + return ISC_R_NORESOURCES; + return ISC_R_UNEXPECTED; + } + +#if defined (TRACING) + /* If we're recording a trace, remember the connection. */ + if (trace_record ()) { + trace_iov_t iov [3]; + iov [0].buf = (char *)&addr.sin_port; + iov [0].len = sizeof addr.sin_port; + iov [1].buf = (char *)&addr.sin_addr; + iov [1].len = sizeof addr.sin_addr; + iov [2].buf = (char *)&listener -> address.sin_port; + iov [2].len = sizeof listener -> address.sin_port; + trace_write_packet_iov (trace_listener_accept, + 3, iov, MDL); + } +#endif + + obj = (omapi_connection_object_t *)0; + status = omapi_listener_connect (&obj, listener, socket, &addr); + if (status != ISC_R_SUCCESS) { + close (socket); + return status; + } + + status = omapi_register_io_object ((omapi_object_t *)obj, + omapi_connection_readfd, + omapi_connection_writefd, + omapi_connection_reader, + omapi_connection_writer, + omapi_connection_reaper); + + /* Lose our reference to the connection, so it'll be gc'd when it's + reaped. */ + omapi_connection_dereference (&obj, MDL); + if (status != ISC_R_SUCCESS) + omapi_disconnect ((omapi_object_t *)(obj), 1); + return status; +} + +isc_result_t omapi_listener_connect (omapi_connection_object_t **obj, + omapi_listener_object_t *listener, + int socket, + struct sockaddr_in *remote_addr) +{ + isc_result_t status; + omapi_object_t *h = (omapi_object_t *)listener; + omapi_addr_t addr; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_accept()"); +#endif + + /* Get the handle. */ + status = omapi_connection_allocate (obj, MDL); + if (status != ISC_R_SUCCESS) + return status; + + (*obj) -> state = omapi_connection_connected; + (*obj) -> remote_addr = *remote_addr; + (*obj) -> socket = socket; + + /* Verify that this host is allowed to connect. */ + if (listener -> verify_addr) { + addr.addrtype = AF_INET; + addr.addrlen = sizeof (remote_addr -> sin_addr); + memcpy (addr.address, &remote_addr -> sin_addr, + sizeof (remote_addr -> sin_addr)); + addr.port = ntohs(remote_addr -> sin_port); + + status = (listener -> verify_addr) (h, &addr); + if (status != ISC_R_SUCCESS) { + omapi_disconnect ((omapi_object_t *)(*obj), 1); + omapi_connection_dereference (obj, MDL); + return status; + } + } + + omapi_listener_reference (&(*obj) -> listener, listener, MDL); +#if defined (TRACING) + omapi_connection_register (*obj, MDL); +#endif + status = omapi_signal (h, "connect", (*obj)); + return status; +} + +#if defined (TRACING) +OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t) + +void omapi_listener_trace_setup (void) { + trace_listener_accept = + trace_type_register ("listener-accept", (void *)0, + trace_listener_accept_input, + trace_listener_accept_stop, MDL); +} + +static void trace_listener_remember (omapi_listener_object_t *obj, + const char *file, int line) +{ + isc_result_t status; + if (!trace_listeners) { + status = omapi_listener_array_allocate (&trace_listeners, + file, line); + if (status != ISC_R_SUCCESS) { + foo: + log_error ("trace_listener_remember: %s", + isc_result_totext (status)); + return; + } + } + status = omapi_listener_array_extend (trace_listeners, obj, + &obj -> index, MDL); + if (status != ISC_R_SUCCESS) + goto foo; +} + +static void trace_listener_accept_input (trace_type_t *ttype, + unsigned length, char *buf) +{ + struct in_addr *addr; + u_int16_t *remote_port; + u_int16_t *local_port; + omapi_connection_object_t *obj; + isc_result_t status; + struct sockaddr_in remote_addr; + + addr = (struct in_addr *)buf; + remote_port = (u_int16_t *)(addr + 1); + local_port = remote_port + 1; + + memset (&remote_addr, 0, sizeof remote_addr); + remote_addr.sin_addr = *addr; + remote_addr.sin_port = *remote_port; + + omapi_array_foreach_begin (trace_listeners, + omapi_listener_object_t, lp) { + if (lp -> address.sin_port == *local_port) { + obj = (omapi_connection_object_t *)0; + status = omapi_listener_connect (&obj, + lp, 0, &remote_addr); + if (status != ISC_R_SUCCESS) { + log_error("%s:%d: OMAPI: Failed to connect " + "a listener.", MDL); + } + omapi_listener_dereference (&lp, MDL); + return; + } + } omapi_array_foreach_end (trace_listeners, + omapi_listener_object_t, lp); + log_error ("trace_listener_accept: %s from %s/%d to port %d", + "unexpected connect", + inet_ntoa (*addr), *remote_port, *local_port); +} + +static void trace_listener_accept_stop (trace_type_t *ttype) { } + + +#endif + +isc_result_t omapi_listener_configure_security (omapi_object_t *h, + isc_result_t (*verify_addr) + (omapi_object_t *, + omapi_addr_t *)) +{ + omapi_listener_object_t *l; + + if (h -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + l = (omapi_listener_object_t *)h; + + l -> verify_addr = verify_addr; + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_listener_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_listener_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_listener_destroy (omapi_object_t *h, + const char *file, int line) +{ + omapi_listener_object_t *l; + + if (h -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + l = (omapi_listener_object_t *)h; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_listener_destroy()"); +#endif + + if (l -> socket != -1) { + close (l -> socket); + l -> socket = -1; + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_listener_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_listener_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *l) +{ + if (l -> type != omapi_type_listener) + return DHCP_R_INVALIDARG; + + if (l -> inner && l -> inner -> type -> stuff_values) + return (*(l -> inner -> type -> stuff_values)) (c, id, + l -> inner); + return ISC_R_SUCCESS; +} + diff --git a/omapip/message.c b/omapip/message.c new file mode 100644 index 0000000..59ccdc2 --- /dev/null +++ b/omapip/message.c @@ -0,0 +1,762 @@ +/* message.c + + Subroutines for dealing with message objects. */ + +/* + * Copyright (c) 2004,2007,2009,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +OMAPI_OBJECT_ALLOC (omapi_message, + omapi_message_object_t, omapi_type_message) + +omapi_message_object_t *omapi_registered_messages; + +isc_result_t omapi_message_new (omapi_object_t **o, const char *file, int line) +{ + omapi_message_object_t *m; + omapi_object_t *g; + isc_result_t status; + + m = (omapi_message_object_t *)0; + status = omapi_message_allocate (&m, file, line); + if (status != ISC_R_SUCCESS) + return status; + + g = (omapi_object_t *)0; + status = omapi_generic_new (&g, file, line); + if (status != ISC_R_SUCCESS) { + dfree (m, file, line); + return status; + } + status = omapi_object_reference (&m -> inner, g, file, line); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, file, line); + omapi_object_dereference (&g, file, line); + return status; + } + status = omapi_object_reference (&g -> outer, + (omapi_object_t *)m, file, line); + + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, file, line); + omapi_object_dereference (&g, file, line); + return status; + } + + status = omapi_object_reference (o, (omapi_object_t *)m, file, line); + omapi_message_dereference (&m, file, line); + omapi_object_dereference (&g, file, line); + if (status != ISC_R_SUCCESS) + return status; + + return status; +} + +isc_result_t omapi_message_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_message_object_t *m; + isc_result_t status; + + if (h -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + m = (omapi_message_object_t *)h; + + /* Can't set authlen. */ + + /* Can set authenticator, but the value must be typed data. */ + if (!omapi_ds_strcmp (name, "authenticator")) { + if (m -> authenticator) + omapi_typed_data_dereference (&m -> authenticator, + MDL); + omapi_typed_data_reference (&m -> authenticator, value, MDL); + return ISC_R_SUCCESS; + + } else if (!omapi_ds_strcmp (name, "object")) { + if (value -> type != omapi_datatype_object) + return DHCP_R_INVALIDARG; + if (m -> object) + omapi_object_dereference (&m -> object, MDL); + omapi_object_reference (&m -> object, value -> u.object, MDL); + return ISC_R_SUCCESS; + + } else if (!omapi_ds_strcmp (name, "notify-object")) { + if (value -> type != omapi_datatype_object) + return DHCP_R_INVALIDARG; + if (m -> notify_object) + omapi_object_dereference (&m -> notify_object, MDL); + omapi_object_reference (&m -> notify_object, + value -> u.object, MDL); + return ISC_R_SUCCESS; + + /* Can set authid, but it has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "authid")) { + if (value -> type != omapi_datatype_int) + return DHCP_R_INVALIDARG; + m -> authid = value -> u.integer; + return ISC_R_SUCCESS; + + /* Can set op, but it has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "op")) { + if (value -> type != omapi_datatype_int) + return DHCP_R_INVALIDARG; + m -> op = value -> u.integer; + return ISC_R_SUCCESS; + + /* Handle also has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "handle")) { + if (value -> type != omapi_datatype_int) + return DHCP_R_INVALIDARG; + m -> h = value -> u.integer; + return ISC_R_SUCCESS; + + /* Transaction ID has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "id")) { + if (value -> type != omapi_datatype_int) + return DHCP_R_INVALIDARG; + m -> id = value -> u.integer; + return ISC_R_SUCCESS; + + /* Remote transaction ID has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "rid")) { + if (value -> type != omapi_datatype_int) + return DHCP_R_INVALIDARG; + m -> rid = value -> u.integer; + return ISC_R_SUCCESS; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_message_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_message_object_t *m; + if (h -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + m = (omapi_message_object_t *)h; + + /* Look for values that are in the message data structure. */ + if (!omapi_ds_strcmp (name, "authlen")) + return omapi_make_int_value (value, name, (int)m -> authlen, + MDL); + else if (!omapi_ds_strcmp (name, "authenticator")) { + if (m -> authenticator) + return omapi_make_value (value, name, + m -> authenticator, MDL); + else + return ISC_R_NOTFOUND; + } else if (!omapi_ds_strcmp (name, "authid")) { + return omapi_make_int_value (value, + name, (int)m -> authid, MDL); + } else if (!omapi_ds_strcmp (name, "op")) { + return omapi_make_int_value (value, name, (int)m -> op, MDL); + } else if (!omapi_ds_strcmp (name, "handle")) { + return omapi_make_int_value (value, name, (int)m -> h, MDL); + } else if (!omapi_ds_strcmp (name, "id")) { + return omapi_make_int_value (value, name, (int)m -> id, MDL); + } else if (!omapi_ds_strcmp (name, "rid")) { + return omapi_make_int_value (value, name, (int)m -> rid, MDL); + } + + /* See if there's an inner object that has the value. */ + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_message_destroy (omapi_object_t *h, + const char *file, int line) +{ + omapi_message_object_t *m; + if (h -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + m = (omapi_message_object_t *)h; + if (m -> authenticator) { + omapi_typed_data_dereference (&m -> authenticator, file, line); + } + if (!m -> prev && omapi_registered_messages != m) + omapi_message_unregister (h); + if (m -> id_object) + omapi_object_dereference (&m -> id_object, file, line); + if (m -> object) + omapi_object_dereference (&m -> object, file, line); + if (m -> notify_object) + omapi_object_dereference (&m -> notify_object, file, line); + if (m -> protocol_object) + omapi_protocol_dereference (&m -> protocol_object, file, line); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_message_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + omapi_message_object_t *m; + if (h -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + m = (omapi_message_object_t *)h; + + if (!strcmp (name, "status")) { + if (m -> notify_object && + m -> notify_object -> type -> signal_handler) + return ((m -> notify_object -> type -> signal_handler)) + (m -> notify_object, name, ap); + else if (m -> object && m -> object -> type -> signal_handler) + return ((m -> object -> type -> signal_handler)) + (m -> object, name, ap); + } + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_message_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *m) +{ + if (m -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + + if (m -> inner && m -> inner -> type -> stuff_values) + return (*(m -> inner -> type -> stuff_values)) (c, id, + m -> inner); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_message_register (omapi_object_t *mo) +{ + omapi_message_object_t *m; + + if (mo -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + m = (omapi_message_object_t *)mo; + + /* Already registered? */ + if (m -> prev || m -> next || omapi_registered_messages == m) + return DHCP_R_INVALIDARG; + + if (omapi_registered_messages) { + omapi_object_reference + ((omapi_object_t **)&m -> next, + (omapi_object_t *)omapi_registered_messages, MDL); + omapi_object_reference + ((omapi_object_t **)&omapi_registered_messages -> prev, + (omapi_object_t *)m, MDL); + omapi_object_dereference + ((omapi_object_t **)&omapi_registered_messages, MDL); + } + omapi_object_reference + ((omapi_object_t **)&omapi_registered_messages, + (omapi_object_t *)m, MDL); + return ISC_R_SUCCESS;; +} + +isc_result_t omapi_message_unregister (omapi_object_t *mo) +{ + omapi_message_object_t *m; + omapi_message_object_t *n; + + if (mo -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + m = (omapi_message_object_t *)mo; + + /* Not registered? */ + if (!m -> prev && omapi_registered_messages != m) + return DHCP_R_INVALIDARG; + + n = (omapi_message_object_t *)0; + if (m -> next) { + omapi_object_reference ((omapi_object_t **)&n, + (omapi_object_t *)m -> next, MDL); + omapi_object_dereference ((omapi_object_t **)&m -> next, MDL); + omapi_object_dereference ((omapi_object_t **)&n -> prev, MDL); + } + if (m -> prev) { + omapi_message_object_t *tmp = (omapi_message_object_t *)0; + omapi_object_reference ((omapi_object_t **)&tmp, + (omapi_object_t *)m -> prev, MDL); + omapi_object_dereference ((omapi_object_t **)&m -> prev, MDL); + if (tmp -> next) + omapi_object_dereference + ((omapi_object_t **)&tmp -> next, MDL); + if (n) + omapi_object_reference + ((omapi_object_t **)&tmp -> next, + (omapi_object_t *)n, MDL); + omapi_object_dereference ((omapi_object_t **)&tmp, MDL); + } else { + omapi_object_dereference + ((omapi_object_t **)&omapi_registered_messages, MDL); + if (n) + omapi_object_reference + ((omapi_object_t **)&omapi_registered_messages, + (omapi_object_t *)n, MDL); + } + if (n) + omapi_object_dereference ((omapi_object_t **)&n, MDL); + return ISC_R_SUCCESS; +} + +#ifdef DEBUG_PROTOCOL +static const char *omapi_message_op_name(int op) { + switch (op) { + case OMAPI_OP_OPEN: return "OMAPI_OP_OPEN"; + case OMAPI_OP_REFRESH: return "OMAPI_OP_REFRESH"; + case OMAPI_OP_UPDATE: return "OMAPI_OP_UPDATE"; + case OMAPI_OP_STATUS: return "OMAPI_OP_STATUS"; + case OMAPI_OP_DELETE: return "OMAPI_OP_DELETE"; + case OMAPI_OP_NOTIFY: return "OMAPI_OP_NOTIFY"; + default: return "(unknown op)"; + } +} +#endif + +static isc_result_t +omapi_message_process_internal (omapi_object_t *, omapi_object_t *); + +isc_result_t omapi_message_process (omapi_object_t *mo, omapi_object_t *po) +{ + isc_result_t status; +#if defined (DEBUG_MEMORY_LEAKAGE) && 0 + unsigned long previous_outstanding = dmalloc_outstanding; +#endif + + status = omapi_message_process_internal (mo, po); + +#if defined (DEBUG_MEMORY_LEAKAGE) && 0 + log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term", + dmalloc_generation, + dmalloc_outstanding - previous_outstanding, + dmalloc_outstanding, dmalloc_longterm); +#endif +#if defined (DEBUG_MEMORY_LEAKAGE) && 0 + dmalloc_dump_outstanding (); +#endif +#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) && 0 + dump_rc_history (); +#endif + + return status; +} + +static isc_result_t +omapi_message_process_internal (omapi_object_t *mo, omapi_object_t *po) +{ + omapi_message_object_t *message, *m; + omapi_object_t *object = (omapi_object_t *)0; + omapi_value_t *tv = (omapi_value_t *)0; + unsigned long create, update, exclusive; + unsigned long wsi; + isc_result_t status, waitstatus; + omapi_object_type_t *type; + + if (mo -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + message = (omapi_message_object_t *)mo; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_message_process(): " + "op=%s handle=%#x id=%#x rid=%#x", + omapi_message_op_name (message -> op), + message -> h, message -> id, message -> rid); +#endif + + if (message -> rid) { + for (m = omapi_registered_messages; m; m = m -> next) + if (m -> id == message -> rid) + break; + /* If we don't have a real message corresponding to + the message ID to which this message claims it is a + response, something's fishy. */ + if (!m) + return ISC_R_NOTFOUND; + /* The authenticator on responses must match the initial + message. */ + if (message -> authid != m -> authid) + return ISC_R_NOTFOUND; + } else { + m = (omapi_message_object_t *)0; + + /* All messages must have an authenticator, with the exception + of messages that are opening a new authenticator. */ + if (omapi_protocol_authenticated(po) && + !message->id_object && + message->op != OMAPI_OP_OPEN) { + return omapi_protocol_send_status + (po, message->id_object, DHCP_R_NOKEYS, + message->id, "No authenticator on message"); + } + } + + switch (message -> op) { + case OMAPI_OP_OPEN: + if (m) { + return omapi_protocol_send_status + (po, message->id_object, DHCP_R_INVALIDARG, + message->id, "OPEN can't be a response"); + } + + /* Get the type of the requested object, if one was + specified. */ + status = omapi_get_value_str (mo, message -> id_object, + "type", &tv); + if (status == ISC_R_SUCCESS && + (tv -> value -> type == omapi_datatype_data || + tv -> value -> type == omapi_datatype_string)) { + for (type = omapi_object_types; + type; type = type -> next) + if (!omapi_td_strcmp (tv -> value, + type -> name)) + break; + } else + type = (omapi_object_type_t *)0; + if (tv) + omapi_value_dereference (&tv, MDL); + + /* If this object had no authenticator, the requested object + must be an authenticator object. */ + if (omapi_protocol_authenticated(po) && + !message->id_object && + type != omapi_type_auth_key) { + return omapi_protocol_send_status + (po, message->id_object, DHCP_R_NOKEYS, + message->id, "No authenticator on message"); + } + + /* Get the create flag. */ + status = omapi_get_value_str (mo, message -> id_object, + "create", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&create, tv -> value); + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "invalid create flag value"); + } + } else + create = 0; + + /* Get the update flag. */ + status = omapi_get_value_str (mo, message -> id_object, + "update", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&update, tv -> value); + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "invalid update flag value"); + } + } else + update = 0; + + /* Get the exclusive flag. */ + status = omapi_get_value_str (mo, message -> id_object, + "exclusive", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&exclusive, tv -> value); + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "invalid exclusive flag value"); + } + } else + exclusive = 0; + + /* If we weren't given a type, look the object up with + the handle. */ + if (!type) { + if (create) { + return omapi_protocol_send_status + (po, message->id_object, + DHCP_R_INVALIDARG, + message->id, + "type required on create"); + } + goto refresh; + } + + /* If the type doesn't provide a lookup method, we can't + look up the object. */ + if (!type -> lookup) { + return omapi_protocol_send_status + (po, message -> id_object, + ISC_R_NOTIMPLEMENTED, message -> id, + "unsearchable object type"); + } + + status = (*(type -> lookup)) (&object, message -> id_object, + message -> object); + + if (status != ISC_R_SUCCESS && + status != ISC_R_NOTFOUND && + status != DHCP_R_NOKEYS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "object lookup failed"); + } + + /* If we didn't find the object and we aren't supposed to + create it, return an error. */ + if (status == ISC_R_NOTFOUND && !create) { + return omapi_protocol_send_status + (po, message -> id_object, + ISC_R_NOTFOUND, message -> id, + "no object matches specification"); + } + + /* If we found an object, we're supposed to be creating an + object, and we're not supposed to have found an object, + return an error. */ + if (status == ISC_R_SUCCESS && create && exclusive) { + omapi_object_dereference (&object, MDL); + return omapi_protocol_send_status + (po, message -> id_object, + ISC_R_EXISTS, message -> id, + "specified object already exists"); + } + + /* If we're creating the object, do it now. */ + if (!object) { + status = omapi_object_create (&object, + message -> id_object, + type); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "can't create new object"); + } + } + + /* If we're updating it, do so now. */ + if (create || update) { + /* This check does not belong here. */ + if (object -> type == omapi_type_auth_key) { + omapi_object_dereference (&object, MDL); + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "can't update object"); + } + + status = omapi_object_update (object, + message -> id_object, + message -> object, + message -> h); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&object, MDL); + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "can't update object"); + } + } + + /* If this is an authenticator object, add it to the active + set for the connection. */ + if (object -> type == omapi_type_auth_key) { + omapi_handle_t handle; + status = omapi_object_handle (&handle, object); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&object, MDL); + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "can't select authenticator"); + } + + status = omapi_protocol_add_auth (po, object, handle); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&object, MDL); + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "can't select authenticator"); + } + } + + /* Now send the new contents of the object back in + response. */ + goto send; + + case OMAPI_OP_REFRESH: + refresh: + status = omapi_handle_lookup (&object, message -> h); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "no matching handle"); + } + send: + status = omapi_protocol_send_update (po, message -> id_object, + message -> id, object); + omapi_object_dereference (&object, MDL); + return status; + + case OMAPI_OP_UPDATE: + if (m && m -> object) { + status = omapi_object_reference (&object, m -> object, + MDL); + } else { + status = omapi_handle_lookup (&object, message -> h); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "no matching handle"); + } + } + + if (object -> type == omapi_type_auth_key || + (object -> inner && + object -> inner -> type == omapi_type_auth_key)) { + if (!m) { + omapi_object_dereference (&object, MDL); + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "cannot update authenticator"); + } + + status = omapi_protocol_add_auth (po, object, + message -> h); + } else { + status = omapi_object_update (object, + message -> id_object, + message -> object, + message -> h); + } + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&object, MDL); + if (!message -> rid) + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "can't update object"); + if (m) + omapi_signal ((omapi_object_t *)m, + "status", status, + (omapi_typed_data_t *)0); + return ISC_R_SUCCESS; + } + if (!message -> rid) + status = omapi_protocol_send_status + (po, message -> id_object, ISC_R_SUCCESS, + message -> id, (char *)0); + if (m) { + omapi_signal ((omapi_object_t *)m, + "status", ISC_R_SUCCESS, + (omapi_typed_data_t *)0); + omapi_message_unregister ((omapi_object_t *)m); + } + + omapi_object_dereference (&object, MDL); + + return status; + + case OMAPI_OP_NOTIFY: + return omapi_protocol_send_status + (po, message -> id_object, ISC_R_NOTIMPLEMENTED, + message -> id, "notify not implemented yet"); + + case OMAPI_OP_STATUS: + /* The return status of a request. */ + if (!m) + return ISC_R_UNEXPECTED; + + /* Get the wait status. */ + status = omapi_get_value_str (mo, message -> id_object, + "result", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&wsi, tv -> value); + waitstatus = wsi; + omapi_value_dereference (&tv, MDL); + if (status != ISC_R_SUCCESS) + waitstatus = ISC_R_UNEXPECTED; + } else + waitstatus = ISC_R_UNEXPECTED; + + status = omapi_get_value_str (mo, message -> id_object, + "message", &tv); + omapi_signal ((omapi_object_t *)m, "status", waitstatus, tv); + if (status == ISC_R_SUCCESS) + omapi_value_dereference (&tv, MDL); + + omapi_message_unregister((omapi_object_t *)m); + + return ISC_R_SUCCESS; + + case OMAPI_OP_DELETE: + status = omapi_handle_lookup (&object, message -> h); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, message -> id_object, + status, message -> id, + "no matching handle"); + } + + if (!object -> type -> remove) + return omapi_protocol_send_status + (po, message -> id_object, + ISC_R_NOTIMPLEMENTED, message -> id, + "no remove method for object"); + + status = (*(object -> type -> remove)) (object, + message -> id_object); + omapi_object_dereference (&object, MDL); + + return omapi_protocol_send_status (po, message -> id_object, + status, message -> id, + (char *)0); + } + return ISC_R_NOTIMPLEMENTED; +} diff --git a/omapip/omapi.3 b/omapip/omapi.3 new file mode 100644 index 0000000..8fa8105 --- /dev/null +++ b/omapip/omapi.3 @@ -0,0 +1,249 @@ +.\" omapi.3 +.\" +.\" Copyright (c) 2009-2010,2014 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (c) 2000-2003 by Internet Software Consortium +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Internet Systems Consortium, Inc. +.\" 950 Charter Street +.\" Redwood City, CA 94063 +.\" <info@isc.org> +.\" https://www.isc.org/ +.\" +.\" This software has been written for Internet Systems Consortium +.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. +.\" +.\" Support and other services are available for ISC products - see +.\" https://www.isc.org for more information or to learn more about ISC. +.\" +.TH omapi 3 +.SH NAME +OMAPI - Object Management Application Programming Interface +.SH DESCRIPTION +.PP +OMAPI is an programming layer designed for controlling remote +applications, and for querying them for their state. It is currently +used by the ISC DHCP server and this outline addresses the parts of +OMAPI appropriate to the clients of DHCP server. It does this by also +describing the use of a thin API layered on top of OMAPI called +\'dhcpctl\' +.PP +OMAPI uses TCP/IP as the transport for server communication, and +security can be imposed by having the client and server +cryptographically sign messages using a shared secret. +.PP +dhcpctl works by presenting the client with handles to objects that +act as surrogates for the real objects in the server. For example a +client will create a handle for a lease object, and will request the +server to fill the lease handle's state. The client application can +then pull details such as the lease expiration time from the lease +handle. +.PP +Modifications can be made to the server state by creating handles to +new objects, or by modifying attributes of handles to existing +objects, and then instructing the server to update itself according to +the changes made. +.SH USAGE +.PP +The client application must always call dhcpctl_initialize() before +making calls to any other dhcpctl functions. This initializes +various internal data structures. +.PP +To create the connection to the server the client must use +dhcpctl_connect() function. As well as making the physical connection +it will also set up the connection data structures to do +authentication on each message, if that is required. +.PP +All the dhcpctl functions return an integer value of type +isc_result_t. A successful call will yield a result of +ISC_R_SUCCESS. If the call fails for a reason local to the client +(e.g. insufficient local memory, or invalid arguments to the call) +then the return value of the dhcpctl function will show that. If the +call succeeds but the server couldn't process the request the error +value from the server is returned through another way, shown below. +.PP +The easiest way to understand dhcpctl is to see it in action. The +following program is fully functional, but almost all error checking +has been removed to make is shorter and easier to understand. This +program will query the server running on the localhost for the details +of the lease for IP address 10.0.0.101. It will then print out the time +the lease ends. +.PP +.nf + #include <stdarg.h> + #include <sys/time.h> + #include <sys/socket.h> + #include <stdio.h> + #include <netinet/in.h> + + #include <isc/result.h> + #include <dhcpctl/dhcpctl.h> + + int main (int argc, char **argv) { + dhcpctl_data_string ipaddrstring = NULL; + dhcpctl_data_string value = NULL; +.fi +.PP +All modifications of handles and all accesses of handle data happen +via dhcpctl_data_string objects. +.PP +.nf + dhcpctl_handle connection = NULL; + dhcpctl_handle lease = NULL; + isc_result_t waitstatus; + struct in_addr convaddr; + time_t thetime; + + dhcpctl_initialize (); +.fi +.PP +Required first step. +.PP +.nf + dhcpctl_connect (&connection, "127.0.0.1", + 7911, 0); +.fi +.PP +Sets up the connection to the server. The server normally listens on +port 7911 unless configured to do otherwise. +.PP +.nf + dhcpctl_new_object (&lease, connection, + "lease"); +.fi +.PP +Here we create a handle to a lease. This call just sets up local data +structure. The server hasn't yet made any association between the +client's data structure and any lease it has. +.PP +.nf + memset (&ipaddrstring, 0, sizeof + ipaddrstring); + + inet_pton(AF_INET, "10.0.0.101", + &convaddr); + + omapi_data_string_new (&ipaddrstring, + 4, MDL); +.fi +.PP +Create a new data string to storing in the handle. +.PP +.nf + memcpy(ipaddrstring->value, &convaddr.s_addr, 4); + + dhcpctl_set_value (lease, ipaddrstring, + "ip-address"); +.fi +.PP +We're setting the ip-address attribute of the lease handle to the +given address. We've not set any other attributes so when the server +makes the association the ip address will be all it uses to look up +the lease in its tables. +.PP +.nf + dhcpctl_open_object (lease, connection, 0); +.fi +.PP +Here we prime the connection with the request to look up the lease in +the server and fill up the local handle with the attributes the server +will send over in its answer. +.PP +.nf + dhcpctl_wait_for_completion (lease, + &waitstatus); +.fi +.PP +This call causes the message to get sent to the server (the message to +look up the lease and send back the attribute values in the +answer). The value in the variable waitstatus when the function +returns will be the result from the server. If the message could +not be processed properly by the server then the error will be +reflected here. +.PP +.nf + if (waitstatus != ISC_R_SUCCESS) { + /* server not authoritative */ + exit (0); + } + + dhcpctl_data_string_dereference(&ipaddrstring, + MDL); +.fi +.PP +Clean-up memory we no longer need. +.PP +.nf + dhcpctl_get_value (&value, lease, "ends"); +.fi +.PP +Get the attribute named ``ends'' from the lease handle. This is a +4-byte integer of the time (in unix epoch seconds) that the lease +will expire. +.PP +.nf + + memcpy(&thetime, value->value, value->len); + dhcpctl_data_string_dereference(&value, MDL); + + fprintf (stdout, "ending time is %s", + ctime(&thetime)); + } + +.fi +.SH AUTHENTICATION +If the server demands authenticated connections then before opening +the connection the user must call dhcpctl_new_authenticator. +.PP +.nf + dhcpctl_handle authenticator = NULL; + const char *keyname = "a-key-name"; + const char *algorithm = "hmac-md5"; + const char *secret = "a-shared-secret"; + + dhcpctl_new_authenticator (&authenticator, + keyname, + algorithm, + secret, + strlen(secret) + 1); +.fi +.PP +The keyname, algorithm and must all match what is specified in the server's +dhcpd.conf file, excepting that the secret should appear in \'raw\' form, not +in base64 as it would in dhcpd.conf: +.PP +.nf + key "a-key-name" { + algorithm hmac-md5; + secret "a-shared-secret"; + }; + + # Set the omapi-key value to use + # authenticated connections + omapi-key a-key-name; +.fi +.PP +The authenticator handle that is created by the call to +dhcpctl_new_authenticator must be given as the last (the 4th) argument +to the call to dhcpctl_connect(). All messages will then be signed +with the given secret string using the specified algorithm. +.SH SEE ALSO +dhcpctl(3), omshell(1), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5). +.SH AUTHOR +.B omapi +is maintained by ISC. To learn more about Internet Systems Consortium, +see +.B https://www.isc.org + diff --git a/omapip/protocol.c b/omapip/protocol.c new file mode 100644 index 0000000..908bcce --- /dev/null +++ b/omapip/protocol.c @@ -0,0 +1,1314 @@ +/* protocol.c + + Functions supporting the object management protocol... */ + +/* + * Copyright (c) 2009,2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +OMAPI_OBJECT_ALLOC (omapi_protocol, omapi_protocol_object_t, + omapi_type_protocol) +OMAPI_OBJECT_ALLOC (omapi_protocol_listener, omapi_protocol_listener_object_t, + omapi_type_protocol_listener) + +isc_result_t omapi_protocol_connect (omapi_object_t *h, + const char *server_name, + unsigned port, + omapi_object_t *a) +{ + isc_result_t rstatus, status; + omapi_protocol_object_t *obj; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_protocol_connect(%s port=%d)", server_name, port); +#endif + + obj = (omapi_protocol_object_t *)0; + status = omapi_protocol_allocate (&obj, MDL); + if (status != ISC_R_SUCCESS) + return status; + + rstatus = omapi_connect ((omapi_object_t *)obj, server_name, port); + if (rstatus != ISC_R_SUCCESS && rstatus != DHCP_R_INCOMPLETE) { + omapi_protocol_dereference (&obj, MDL); + return rstatus; + } + status = omapi_object_reference (&h -> outer, + (omapi_object_t *)obj, MDL); + if (status != ISC_R_SUCCESS) { + omapi_protocol_dereference (&obj, MDL); + return status; + } + status = omapi_object_reference (&obj -> inner, h, MDL); + if (status != ISC_R_SUCCESS) { + omapi_protocol_dereference (&obj, MDL); + return status; + } + + /* If we were passed a default authenticator, store it now. We'll + open it once we're connected. */ + if (a) { + obj -> default_auth = + dmalloc (sizeof(omapi_remote_auth_t), MDL); + if (!obj -> default_auth) { + omapi_protocol_dereference (&obj, MDL); + return ISC_R_NOMEMORY; + } + + obj -> default_auth -> next = (omapi_remote_auth_t *)0; + status = omapi_object_reference (&obj -> default_auth -> a, + a, MDL); + if (status != ISC_R_SUCCESS) { + dfree (obj -> default_auth, MDL); + omapi_protocol_dereference (&obj, MDL); + return status; + } + + obj -> insecure = 0; + rstatus = DHCP_R_INCOMPLETE; + } else { + obj -> insecure = 1; +#if 0 + status = ISC_R_SUCCESS; +#endif + } + + omapi_protocol_dereference (&obj, MDL); + return rstatus; +} + +/* Send the protocol introduction message. */ +isc_result_t omapi_protocol_send_intro (omapi_object_t *h, + unsigned ver, + unsigned hsize) +{ + isc_result_t status; + omapi_protocol_object_t *p; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_protocol_send_intro()"); +#endif + + if (h -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)h; + + if (!h -> outer || h -> outer -> type != omapi_type_connection) + return ISC_R_NOTCONNECTED; + + status = omapi_connection_put_uint32 (h -> outer, ver); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_connection_put_uint32 (h -> outer, hsize); + + if (status != ISC_R_SUCCESS) + return status; + + /* Require the other end to send an intro - this kicks off the + protocol input state machine. */ + p -> state = omapi_protocol_intro_wait; + status = omapi_connection_require (h -> outer, 8); + if (status != ISC_R_SUCCESS && status != DHCP_R_NOTYET) + return status; + + /* Make up an initial transaction ID for this connection. */ + p -> next_xid = random (); + return ISC_R_SUCCESS; +} + +#ifdef DEBUG_PROTOCOL +extern const char *omapi_message_op_name(int); +#endif /* DEBUG_PROTOCOL */ + +isc_result_t omapi_protocol_send_message (omapi_object_t *po, + omapi_object_t *id, + omapi_object_t *mo, + omapi_object_t *omo) +{ + omapi_protocol_object_t *p; + omapi_object_t *c; + omapi_message_object_t *m, *om; + omapi_remote_auth_t *ra; + omapi_value_t *signature; + isc_result_t status; + unsigned auth_len; + + if (po -> type != omapi_type_protocol || + !po -> outer || po -> outer -> type != omapi_type_connection || + mo -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + if (omo && omo -> type != omapi_type_message) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)po; + c = (omapi_object_t *)(po -> outer); + m = (omapi_message_object_t *)mo; + om = (omapi_message_object_t *)omo; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_protocol_send_message(): " + "op=%s handle=%#lx id=%#lx rid=%#lx", + omapi_message_op_name (m->op), + (long)(m -> object ? m -> object -> handle : m -> handle), + (long)p -> next_xid, (long)m -> rid); +#endif + + /* Find the authid to use for this message. */ + if (id) { + for (ra = p -> remote_auth_list; ra; ra = ra -> next) { + if (ra -> a == id) { + break; + } + } + + if (!ra) + return DHCP_R_KEY_UNKNOWN; + } else if (p -> remote_auth_list) { + ra = p -> default_auth; + } else { + ra = (omapi_remote_auth_t *)0; + } + + if (ra) { + m -> authid = ra -> remote_handle; + status = omapi_object_reference (&m -> id_object, + ra -> a, MDL); + if (status != ISC_R_SUCCESS) + return status; + } + + /* Write the ID of the authentication key we're using. */ + status = omapi_connection_put_uint32 (c, ra ? ra -> remote_handle : 0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Activate the authentication key on the connection. */ + auth_len = 0; + if (ra) { + status = omapi_set_object_value (c, (omapi_object_t *)0, + "output-authenticator", + ra -> a); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + status = omapi_connection_output_auth_length (c, &auth_len); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + + /* Write the authenticator length */ + status = omapi_connection_put_uint32 (c, auth_len); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the opcode. */ + status = omapi_connection_put_uint32 (c, m -> op); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the handle. If we've been given an explicit handle, use + that. Otherwise, use the handle of the object we're sending. + The caller is responsible for arranging for one of these handles + to be set (or not). */ + status = omapi_connection_put_uint32 (c, (m -> h + ? m -> h + : (m -> object + ? m -> object -> handle + : 0))); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Set and write the transaction ID. */ + m -> id = p -> next_xid++; + status = omapi_connection_put_uint32 (c, m -> id); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the transaction ID of the message to which this is a + response, if there is such a message. */ + status = omapi_connection_put_uint32 (c, om ? om -> id : m -> rid); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Stuff out the name/value pairs specific to this message. */ + status = omapi_stuff_values (c, id, (omapi_object_t *)m); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the zero-length name that terminates the list of name/value + pairs specific to the message. */ + status = omapi_connection_put_uint16 (c, 0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Stuff out all the published name/value pairs in the object that's + being sent in the message, if there is one. */ + if (m -> object) { + status = omapi_stuff_values (c, id, m -> object); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + + /* Write the zero-length name that terminates the list of name/value + pairs for the associated object. */ + status = omapi_connection_put_uint16 (c, 0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + if (ra) { + /* Calculate the message signature. */ + signature = (omapi_value_t *)0; + status = omapi_get_value_str (c, (omapi_object_t *)0, + "output-signature", &signature); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the authenticator... */ + status = (omapi_connection_copyin + (c, signature -> value -> u.buffer.value, + signature -> value -> u.buffer.len)); + omapi_value_dereference (&signature, MDL); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Dectivate the authentication key on the connection. */ + status = omapi_set_value_str (c, (omapi_object_t *)0, + "output-authenticator", + (omapi_typed_data_t *)0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + + if (!omo) { + omapi_protocol_reference (&m -> protocol_object, p, MDL); + } + return ISC_R_SUCCESS; +} + + +isc_result_t omapi_protocol_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + isc_result_t status; + omapi_protocol_object_t *p; + omapi_object_t *c; + omapi_message_object_t *m; + omapi_value_t *signature = NULL; + u_int16_t nlen; + u_int32_t vlen; + u_int32_t th; +#if defined (DEBUG_MEMORY_LEAKAGE) + unsigned long previous_outstanding = 0xDEADBEEF; + unsigned long connect_outstanding = 0xDEADBEEF; +#endif + + if (h -> type != omapi_type_protocol) { + /* XXX shouldn't happen. Put an assert here? */ + return ISC_R_UNEXPECTED; + } + p = (omapi_protocol_object_t *)h; + + if (!strcmp (name, "connect")) { +#if defined (DEBUG_MEMORY_LEAKAGE) + connect_outstanding = dmalloc_outstanding; +#endif + /* Send the introductory message. */ + status = omapi_protocol_send_intro + (h, OMAPI_PROTOCOL_VERSION, + sizeof (omapi_protocol_header_t)); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (p -> outer, 1); + return status; + } + return ISC_R_SUCCESS; + } + + /* Should only receive these when opening the initial authenticator. */ + if (!strcmp (name, "status")) { + status = va_arg (ap, isc_result_t); + if (status != ISC_R_SUCCESS) { + omapi_signal_in (h -> inner, "status", status, + (omapi_object_t *)0); + omapi_disconnect (p -> outer, 1); + return status; + } else { + return omapi_signal_in (h -> inner, "ready"); + } + } + + /* If we get a disconnect, dump memory usage. */ + if (!strcmp (name, "disconnect")) { +#if defined (DEBUG_MEMORY_LEAKAGE) + if (connect_outstanding != 0xDEADBEEF) { + log_info ("generation %ld: %ld new, %ld outstanding, %ld%s", + dmalloc_generation, + dmalloc_outstanding - previous_outstanding, + dmalloc_outstanding, dmalloc_longterm, " long-term"); + } +#endif +#if defined (DEBUG_MEMORY_LEAKAGE) + dmalloc_dump_outstanding (); +#endif +#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) + dump_rc_history (h); +#endif + for (m = omapi_registered_messages; m; m = m -> next) { + if (m -> protocol_object == p) { + if (m -> object) + omapi_signal (m -> object, "disconnect"); + } + } + + /* XXX */ + return ISC_R_SUCCESS; + } + + /* Not a signal we recognize? */ + if (strcmp (name, "ready")) { + if (p -> inner && p -> inner -> type -> signal_handler) + return (*(p -> inner -> type -> signal_handler)) (h, + name, + ap); + return ISC_R_NOTFOUND; + } + + if (!p -> outer || p -> outer -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + c = p -> outer; + + /* We get here because we requested that we be woken up after + some number of bytes were read, and that number of bytes + has in fact been read. */ + switch (p -> state) { + case omapi_protocol_intro_wait: + /* Get protocol version and header size in network + byte order. */ + omapi_connection_get_uint32 (c, &p -> protocol_version); + omapi_connection_get_uint32 (c, &p -> header_size); + + /* We currently only support the current protocol version. */ + if (p -> protocol_version != OMAPI_PROTOCOL_VERSION) { + omapi_disconnect (c, 1); + return DHCP_R_VERSIONMISMATCH; + } + + if (p -> header_size < sizeof (omapi_protocol_header_t)) { + omapi_disconnect (c, 1); + return DHCP_R_PROTOCOLERROR; + } + + if (p -> default_auth) { + status = omapi_protocol_send_open + (h, (omapi_object_t *)0, "authenticator", + p -> default_auth -> a, + OMAPI_NOTIFY_PROTOCOL); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } else { + status = omapi_signal_in (h -> inner, "ready"); + } + + to_header_wait: + /* The next thing we're expecting is a message header. */ + p -> state = omapi_protocol_header_wait; + + /* Register a need for the number of bytes in a + header, and if we already have that many, process + them immediately. */ + if ((omapi_connection_require (c, p -> header_size)) != + ISC_R_SUCCESS) + break; + /* If we already have the data, fall through. */ + + case omapi_protocol_header_wait: +#if defined (DEBUG_MEMORY_LEAKAGE) + if (previous_outstanding != 0xDEADBEEF) { + log_info ("%s %ld: %ld new, %ld outstanding, %ld%s", + "generation", dmalloc_generation, + dmalloc_outstanding - previous_outstanding, + dmalloc_outstanding, dmalloc_longterm, + " long-term"); +#endif +#if (defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL)) + dmalloc_dump_outstanding (); +#endif +#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) + dump_rc_history (h); +#endif +#if defined (DEBUG_MEMORY_LEAKAGE) + } + previous_outstanding = dmalloc_outstanding; +#endif + status = omapi_message_new ((omapi_object_t **)&p -> message, + MDL); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + p -> verify_result = ISC_R_SUCCESS; + + /* Swap in the header... */ + omapi_connection_get_uint32 (c, &p -> message -> authid); + + /* Bind the authenticator to the message object. */ + if (p -> message -> authid) { + status = (omapi_protocol_lookup_auth + (&p -> message -> id_object, h, + p -> message -> authid)); + if (status != ISC_R_SUCCESS) + p -> verify_result = status; + + /* Activate the authentication key. */ + status = omapi_set_object_value + (c, (omapi_object_t *)0, "input-authenticator", + p -> message -> id_object); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + + omapi_connection_get_uint32 (c, &p -> message -> authlen); + omapi_connection_get_uint32 (c, &p -> message -> op); + omapi_connection_get_uint32 (c, &th); + p -> message -> h = th; + omapi_connection_get_uint32 (c, &p -> message -> id); + omapi_connection_get_uint32 (c, &p -> message -> rid); + + /* If there was any extra header data, skip over it. */ + if (p -> header_size > sizeof (omapi_protocol_header_t)) { + omapi_connection_copyout + (0, c, (p -> header_size - + sizeof (omapi_protocol_header_t))); + } + + /* XXX must compute partial signature across the + XXX preceding bytes. Also, if authenticator + specifies encryption as well as signing, we may + have to decrypt the data on the way in. */ + + /* First we read in message-specific values, then object + values. */ + p -> reading_message_values = 1; + + need_name_length: + /* The next thing we're expecting is length of the + first name. */ + p -> state = omapi_protocol_name_length_wait; + + /* Wait for a 16-bit length. */ + if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_name_length_wait: + omapi_connection_get_uint16 (c, &nlen); + /* A zero-length name means that we're done reading name+value + pairs. */ + if (nlen == 0) { + /* If we've already read in the object, we are + done reading the message, but if we've just + finished reading in the values associated + with the message, we need to read the + object. */ + if (p -> reading_message_values) { + p -> reading_message_values = 0; + goto need_name_length; + } + + /* If the authenticator length is zero, there's no + signature to read in, so go straight to processing + the message. */ + if (p -> message -> authlen == 0) + goto message_done; + + /* The next thing we're expecting is the + message signature. */ + p -> state = omapi_protocol_signature_wait; + + /* Wait for the number of bytes specified for + the authenticator. If we already have it, + go read it in. */ + if (omapi_connection_require + (c, p -> message -> authlen) == ISC_R_SUCCESS) + goto signature_wait; + break; + } + + /* Allocate a buffer for the name. */ + status = (omapi_data_string_new (&p -> name, nlen, MDL)); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + p -> state = omapi_protocol_name_wait; + if (omapi_connection_require (c, nlen) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_name_wait: + omapi_connection_copyout (p -> name -> value, c, + p -> name -> len); + /* Wait for a 32-bit length. */ + p -> state = omapi_protocol_value_length_wait; + if ((omapi_connection_require (c, 4)) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_value_length_wait: + omapi_connection_get_uint32 (c, &vlen); + + /* Zero-length values are allowed - if we get one, we + don't have to read any data for the value - just + get the next one, if there is a next one. */ + if (!vlen) + goto insert_new_value; + + status = omapi_typed_data_new (MDL, &p -> value, + omapi_datatype_data, + vlen); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + + p -> state = omapi_protocol_value_wait; + if (omapi_connection_require (c, vlen) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_value_wait: + omapi_connection_copyout (p -> value -> u.buffer.value, c, + p -> value -> u.buffer.len); + + insert_new_value: + if (p -> reading_message_values) { + status = (omapi_set_value + ((omapi_object_t *)p -> message, + p -> message -> id_object, + p -> name, p -> value)); + } else { + if (!p -> message -> object) { + /* We need a generic object to hang off of the + incoming message. */ + status = (omapi_generic_new + (&p -> message -> object, MDL)); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + status = (omapi_set_value + ((omapi_object_t *)p -> message -> object, + p -> message -> id_object, + p -> name, p -> value)); + } + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + omapi_data_string_dereference (&p -> name, MDL); + if (p -> value) + omapi_typed_data_dereference (&p -> value, MDL); + goto need_name_length; + + signature_wait: + case omapi_protocol_signature_wait: + if (p -> message -> id_object) { + /* Compute the signature of the message. */ + status = omapi_get_value_str (c, (omapi_object_t *)0, + "input-signature", + &signature); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Disable the authentication key on the connection. */ + status = omapi_set_value_str (c, (omapi_object_t *)0, + "input-authenticator", + (omapi_typed_data_t *)0); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (&signature, MDL); + omapi_disconnect (c, 1); + return status; + } + } + + /* Read the authenticator. */ + status = omapi_typed_data_new (MDL, + &p -> message -> authenticator, + omapi_datatype_data, + p -> message -> authlen); + + if (status != ISC_R_SUCCESS) { + if (signature != NULL) { + omapi_value_dereference (&signature, MDL); + } + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + omapi_connection_copyout + (p -> message -> authenticator -> u.buffer.value, c, + p -> message -> authlen); + + /* Verify the signature. */ + if (p -> message -> id_object && + ((signature -> value -> u.buffer.len != + p -> message -> authlen) || + (memcmp (signature -> value -> u.buffer.value, + p -> message -> authenticator -> u.buffer.value, + p -> message -> authlen) != 0))) { + /* Invalid signature. */ + p->verify_result = DHCP_R_INVALIDKEY; + } + + if (signature != NULL) { + omapi_value_dereference (&signature, MDL); + } + + /* Process the message. */ + message_done: + if (p -> verify_result != ISC_R_SUCCESS) { + status = omapi_protocol_send_status + (h, (omapi_object_t *)0, p -> verify_result, + p -> message -> id, (char *)0); + } else { + status = omapi_message_process + ((omapi_object_t *)p -> message, h); + } + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + + omapi_message_dereference (&p -> message, MDL); +#if defined (DEBUG_MEMORY_LEAKAGE) + log_info ("generation %ld: %ld new, %ld outstanding, %ld%s", + dmalloc_generation, + dmalloc_outstanding - previous_outstanding, + dmalloc_outstanding, dmalloc_longterm, " long-term"); +#endif +#if (defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL)) + dmalloc_dump_outstanding (); +#endif +#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) + dump_rc_history (h); +#endif +#if defined (DEBUG_MEMORY_LEAKAGE) + previous_outstanding = 0xDEADBEEF; +#endif + /* Now wait for the next message. */ + goto to_header_wait; + + default: + /* XXX should never get here. Assertion? */ + break; + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_protocol_add_auth (omapi_object_t *po, + omapi_object_t *ao, + omapi_handle_t handle) +{ + omapi_protocol_object_t *p; + omapi_remote_auth_t *r; + isc_result_t status; + + if (ao -> type != omapi_type_auth_key && + (!ao -> inner || ao -> inner -> type != omapi_type_auth_key)) + return DHCP_R_INVALIDARG; + + if (po -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)po; + +#ifdef DEBUG_PROTOCOL + log_debug ("omapi_protocol_add_auth(name=%s)", + ((omapi_auth_key_t *)ao) -> name); +#endif + + if (p -> verify_auth) { + status = (p -> verify_auth) (po, (omapi_auth_key_t *)ao); + if (status != ISC_R_SUCCESS) + return status; + } + + /* If omapi_protocol_connect() was called with a default + authenticator, p -> default_auth will already be set, + but p -> remote_auth_list will not yet be initialized. */ + if (p -> default_auth && !p -> remote_auth_list) { + if (p -> default_auth -> a != ao) { + /* Something just went horribly wrong. */ + omapi_disconnect (p -> outer, 1); + return ISC_R_UNEXPECTED; + } + + p -> remote_auth_list = p -> default_auth; + p -> default_auth -> remote_handle = handle; + + return omapi_signal_in (p -> inner, "ready"); + } + + r = dmalloc (sizeof(*r), MDL); + if (!r) + return ISC_R_NOMEMORY; + + status = omapi_object_reference (&r -> a, ao, MDL); + if (status != ISC_R_SUCCESS) { + dfree (r, MDL); + return status; + } + + r -> remote_handle = handle; + r -> next = p -> remote_auth_list; + p -> remote_auth_list = r; + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_protocol_lookup_auth (omapi_object_t **a, + omapi_object_t *po, + omapi_handle_t handle) +{ + omapi_protocol_object_t *p; + omapi_remote_auth_t *r; + + if (po -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)po; + + for (r = p -> remote_auth_list; r; r = r -> next) + if (r -> remote_handle == handle) + return omapi_object_reference (a, r -> a, MDL); + + return DHCP_R_KEY_UNKNOWN; +} + +isc_result_t omapi_protocol_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_protocol_object_t *p; + omapi_remote_auth_t *r; + + if (h -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)h; + + if (omapi_ds_strcmp (name, "default-authenticator") == 0) { + if (!value || value -> type != omapi_datatype_object) + return DHCP_R_INVALIDARG; + + if (!value -> u.object) { + p -> default_auth = (omapi_remote_auth_t *)0; + } else { + for (r = p -> remote_auth_list; r; r = r -> next) + if (r -> a == value -> u.object) + break; + + if (!r) + return DHCP_R_KEY_UNKNOWN; + + p -> default_auth = r; + } + + return ISC_R_SUCCESS; + } + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_protocol_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_protocol_object_t *p; + + if (h -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)h; + + if (omapi_ds_strcmp (name, "default-authenticator") == 0) { + if (!p -> default_auth) + return ISC_R_NOTFOUND; + + return omapi_make_object_value (value, name, + p -> default_auth -> a, MDL); + } + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_protocol_destroy (omapi_object_t *h, + const char *file, int line) +{ + omapi_protocol_object_t *p; + if (h -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_object_t *)h; + if (p -> message) + omapi_message_dereference (&p -> message, file, line); + + /* This will happen if: 1) A default authenticator is supplied to + omapi_protocol_connect(), and 2) something goes wrong before + the authenticator can be opened. */ + if (p -> default_auth && !p -> remote_auth_list) + dfree (p -> default_auth, file, line); + + while (p -> remote_auth_list) { + omapi_remote_auth_t *r = p -> remote_auth_list; + p -> remote_auth_list = p -> remote_auth_list -> next; + omapi_object_dereference (&r -> a, file, line); + dfree (r, file, line); + } + return ISC_R_SUCCESS; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_protocol_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *p) +{ + if (p -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + + if (p -> inner && p -> inner -> type -> stuff_values) + return (*(p -> inner -> type -> stuff_values)) (c, id, + p -> inner); + return ISC_R_SUCCESS; +} + +/* Returns a boolean indicating whether this protocol requires that + messages be authenticated or not. */ + +isc_boolean_t omapi_protocol_authenticated (omapi_object_t *h) +{ + if (h -> type != omapi_type_protocol) + return isc_boolean_false; + if (((omapi_protocol_object_t *)h) -> insecure) + return isc_boolean_false; + else + return isc_boolean_true; +} + +/* Sets the address and authenticator verification callbacks. The handle + is to a listener object, not a protocol object. */ + +isc_result_t omapi_protocol_configure_security (omapi_object_t *h, + isc_result_t (*verify_addr) + (omapi_object_t *, + omapi_addr_t *), + isc_result_t (*verify_auth) + (omapi_object_t *, + omapi_auth_key_t *)) +{ + omapi_protocol_listener_object_t *l; + + if (h -> outer && h -> outer -> type == omapi_type_protocol_listener) + h = h -> outer; + + if (h -> type != omapi_type_protocol_listener) + return DHCP_R_INVALIDARG; + l = (omapi_protocol_listener_object_t *)h; + + l -> verify_auth = verify_auth; + l -> insecure = 0; + + if (h -> outer != NULL) { + return omapi_listener_configure_security (h -> outer, verify_addr); + } else { + return DHCP_R_INVALIDARG; + } +} + + +/* Set up a listener for the omapi protocol. The handle stored points to + a listener object, not a protocol object. */ + +isc_result_t omapi_protocol_listen (omapi_object_t *h, + unsigned port, + int max) +{ + isc_result_t status; + omapi_protocol_listener_object_t *obj; + + obj = (omapi_protocol_listener_object_t *)0; + status = omapi_protocol_listener_allocate (&obj, MDL); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_reference (&h -> outer, + (omapi_object_t *)obj, MDL); + if (status != ISC_R_SUCCESS) { + omapi_protocol_listener_dereference (&obj, MDL); + return status; + } + status = omapi_object_reference (&obj -> inner, h, MDL); + if (status != ISC_R_SUCCESS) { + omapi_protocol_listener_dereference (&obj, MDL); + return status; + } + + /* What a terrible default. */ + obj -> insecure = 1; + + status = omapi_listen ((omapi_object_t *)obj, port, max); + omapi_protocol_listener_dereference (&obj, MDL); + return status; +} + +/* Signal handler for protocol listener - if we get a connect signal, + create a new protocol connection, otherwise pass the signal down. */ + +isc_result_t omapi_protocol_listener_signal (omapi_object_t *o, + const char *name, va_list ap) +{ + isc_result_t status; + omapi_object_t *c; + omapi_protocol_object_t *obj; + omapi_protocol_listener_object_t *p; + + if (!o || o -> type != omapi_type_protocol_listener) + return DHCP_R_INVALIDARG; + p = (omapi_protocol_listener_object_t *)o; + + /* Not a signal we recognize? */ + if (strcmp (name, "connect")) { + if (p -> inner && p -> inner -> type -> signal_handler) + return (*(p -> inner -> type -> signal_handler)) + (p -> inner, name, ap); + return ISC_R_NOTFOUND; + } + + c = va_arg (ap, omapi_object_t *); + if (!c || c -> type != omapi_type_connection) + return DHCP_R_INVALIDARG; + + obj = (omapi_protocol_object_t *)0; + status = omapi_protocol_allocate (&obj, MDL); + if (status != ISC_R_SUCCESS) + return status; + + obj -> verify_auth = p -> verify_auth; + obj -> insecure = p -> insecure; + + status = omapi_object_reference (&obj -> outer, c, MDL); + if (status != ISC_R_SUCCESS) { + lose: + omapi_protocol_dereference (&obj, MDL); + omapi_disconnect (c, 1); + return status; + } + + status = omapi_object_reference (&c -> inner, + (omapi_object_t *)obj, MDL); + if (status != ISC_R_SUCCESS) + goto lose; + + /* Send the introductory message. */ + status = omapi_protocol_send_intro ((omapi_object_t *)obj, + OMAPI_PROTOCOL_VERSION, + sizeof (omapi_protocol_header_t)); + if (status != ISC_R_SUCCESS) + goto lose; + + omapi_protocol_dereference (&obj, MDL); + return status; +} + +isc_result_t omapi_protocol_listener_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_protocol_listener) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_protocol_listener_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_protocol_listener) + return DHCP_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> get_value) + return (*(h -> inner -> type -> get_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_protocol_listener_destroy (omapi_object_t *h, + const char *file, int line) +{ + if (h -> type != omapi_type_protocol_listener) + return DHCP_R_INVALIDARG; + return ISC_R_SUCCESS; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_protocol_listener_stuff (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *p) +{ + if (p -> type != omapi_type_protocol_listener) + return DHCP_R_INVALIDARG; + + if (p -> inner && p -> inner -> type -> stuff_values) + return (*(p -> inner -> type -> stuff_values)) (c, id, + p -> inner); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_protocol_send_status (omapi_object_t *po, + omapi_object_t *id, + isc_result_t waitstatus, + unsigned rid, const char *msg) +{ + isc_result_t status; + omapi_message_object_t *message = (omapi_message_object_t *)0; + omapi_object_t *mo; + + if (po -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + + status = omapi_message_new ((omapi_object_t **)&message, MDL); + if (status != ISC_R_SUCCESS) + return status; + mo = (omapi_object_t *)message; + + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "op", OMAPI_OP_STATUS); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "rid", (int)rid); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "result", (int)waitstatus); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + + /* If a message has been provided, send it. */ + if (msg) { + status = omapi_set_string_value (mo, (omapi_object_t *)0, + "message", msg); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + } + + status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0); + omapi_message_dereference (&message, MDL); + return status; +} + +/* The OMAPI_NOTIFY_PROTOCOL flag will cause the notify-object for the + message to be set to the protocol object. This is used when opening + the default authenticator. */ + +isc_result_t omapi_protocol_send_open (omapi_object_t *po, + omapi_object_t *id, + const char *type, + omapi_object_t *object, + unsigned flags) +{ + isc_result_t status; + omapi_message_object_t *message = (omapi_message_object_t *)0; + omapi_object_t *mo; + + if (po -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + + status = omapi_message_new ((omapi_object_t **)&message, MDL); + mo = (omapi_object_t *)message; + + if (status == ISC_R_SUCCESS) + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "op", OMAPI_OP_OPEN); + + if (status == ISC_R_SUCCESS) + status = omapi_set_object_value (mo, (omapi_object_t *)0, + "object", object); + + if ((flags & OMAPI_CREATE) && (status == ISC_R_SUCCESS)) + status = omapi_set_boolean_value (mo, (omapi_object_t *)0, + "create", 1); + + if ((flags & OMAPI_UPDATE) && (status == ISC_R_SUCCESS)) + status = omapi_set_boolean_value (mo, (omapi_object_t *)0, + "update", 1); + + if ((flags & OMAPI_EXCL) && (status == ISC_R_SUCCESS)) + status = omapi_set_boolean_value (mo, (omapi_object_t *)0, + "exclusive", 1); + + if ((flags & OMAPI_NOTIFY_PROTOCOL) && (status == ISC_R_SUCCESS)) + status = omapi_set_object_value (mo, (omapi_object_t *)0, + "notify-object", po); + + if (type && (status == ISC_R_SUCCESS)) + status = omapi_set_string_value (mo, (omapi_object_t *)0, + "type", type); + + if (status == ISC_R_SUCCESS) + status = omapi_message_register (mo); + + if (status == ISC_R_SUCCESS) { + status = omapi_protocol_send_message (po, id, mo, + (omapi_object_t *)0); + if (status != ISC_R_SUCCESS) + omapi_message_unregister (mo); + } + + if (message) + omapi_message_dereference (&message, MDL); + + return status; +} + +isc_result_t omapi_protocol_send_update (omapi_object_t *po, + omapi_object_t *id, + unsigned rid, + omapi_object_t *object) +{ + isc_result_t status; + omapi_message_object_t *message = (omapi_message_object_t *)0; + omapi_object_t *mo; + + if (po -> type != omapi_type_protocol) + return DHCP_R_INVALIDARG; + + status = omapi_message_new ((omapi_object_t **)&message, MDL); + if (status != ISC_R_SUCCESS) + return status; + mo = (omapi_object_t *)message; + + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "op", OMAPI_OP_UPDATE); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + + if (rid) { + omapi_handle_t handle; + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "rid", (int)rid); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + + status = omapi_object_handle (&handle, object); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + status = omapi_set_int_value (mo, (omapi_object_t *)0, + "handle", (int)handle); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + } + + status = omapi_set_object_value (mo, (omapi_object_t *)0, + "object", object); + if (status != ISC_R_SUCCESS) { + omapi_message_dereference (&message, MDL); + return status; + } + + status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0); + omapi_message_dereference (&message, MDL); + return status; +} diff --git a/omapip/result.c b/omapip/result.c new file mode 100644 index 0000000..383279f --- /dev/null +++ b/omapip/result.c @@ -0,0 +1,86 @@ +/* result.c + */ + +/* + * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * 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. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +/* + * In the previous code the results started at 36 + * rather than ISC_RESULTCLASS_DHCP + 0 + * ISC_R_NOTCONNECTED was + 4 (40), it has been superseeded by the isc version + */ + +static const char *text[DHCP_R_NRESULTS] = { + "host unknown", /* 0 */ + "protocol version mismatch", /* 1 */ + "protocol error", /* 2 */ + "invalid argument", /* 3 */ + "data not yet available", /* 4 */ + "object unchanged", /* 5 */ + "more than one object matches key", /* 6 */ + "key conflict", /* 7 */ + "parse error(s) occurred", /* 8 */ + "no key specified", /* 9 */ + "zone TSIG key not known", /* 10 */ + "invalid TSIG key", /* 11 */ + "operation in progress", /* 12 */ + "DNS format error", /* 13 */ + "DNS server failed", /* 14 */ + "no such domain", /* 15 */ + "not implemented", /* 16 */ + "refused", /* 17 */ + "domain already exists", /* 18 */ + "RRset already exists", /* 19 */ + "no such RRset", /* 20 */ + "not authorized", /* 21 */ + "not a zone", /* 22 */ + "bad DNS signature", /* 23 */ + "bad DNS key", /* 24 */ + "clock skew too great", /* 25 */ + "no root zone", /* 26 */ + "destination address required", /* 27 */ + "cross-zone update", /* 28 */ + "no TSIG signature", /* 29 */ + "not equal", /* 30 */ + "connection reset by peer", /* 31 */ + "unknown attribute" /* 32 */ +}; + +#define DHCP_RESULT_RESULTSET 2 +#define DHCP_RESULT_UNAVAILABLESET 3 + +// This is a placeholder as we don't allow for external message catalogs yet +isc_msgcat_t * dhcp_msgcat = NULL; + +isc_result_t +dhcp_result_register(void) { + isc_result_t result; + + result = isc_result_register(ISC_RESULTCLASS_DHCP, DHCP_R_NRESULTS, + text, dhcp_msgcat, DHCP_RESULT_RESULTSET); + + return(result); +} diff --git a/omapip/support.c b/omapip/support.c new file mode 100644 index 0000000..48592d4 --- /dev/null +++ b/omapip/support.c @@ -0,0 +1,853 @@ +/* support.c + + Subroutines providing general support for objects. */ + +/* + * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> + +omapi_object_type_t *omapi_type_connection; +omapi_object_type_t *omapi_type_listener; +omapi_object_type_t *omapi_type_io_object; +omapi_object_type_t *omapi_type_datagram; +omapi_object_type_t *omapi_type_generic; +omapi_object_type_t *omapi_type_protocol; +omapi_object_type_t *omapi_type_protocol_listener; +omapi_object_type_t *omapi_type_waiter; +omapi_object_type_t *omapi_type_remote; +omapi_object_type_t *omapi_type_message; +omapi_object_type_t *omapi_type_auth_key; + +omapi_object_type_t *omapi_object_types; +int omapi_object_type_count; + +#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +void omapi_type_relinquish () +{ + omapi_object_type_t *t, *n; + + for (t = omapi_object_types; t; t = n) { + n = t -> next; + dfree (t, MDL); + } + omapi_object_types = (omapi_object_type_t *)0; +} +#endif + +isc_result_t omapi_init (void) +{ + isc_result_t status; + + /* Register all the standard object types... */ + status = omapi_object_type_register (&omapi_type_connection, + "connection", + omapi_connection_set_value, + omapi_connection_get_value, + omapi_connection_destroy, + omapi_connection_signal_handler, + omapi_connection_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof + (omapi_connection_object_t), 0, + RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_listener, + "listener", + omapi_listener_set_value, + omapi_listener_get_value, + omapi_listener_destroy, + omapi_listener_signal_handler, + omapi_listener_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_listener_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_io_object, + "io", + omapi_io_set_value, + omapi_io_get_value, + omapi_io_destroy, + omapi_io_signal_handler, + omapi_io_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_io_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_generic, + "generic", + omapi_generic_set_value, + omapi_generic_get_value, + omapi_generic_destroy, + omapi_generic_signal_handler, + omapi_generic_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_generic_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_protocol, + "protocol", + omapi_protocol_set_value, + omapi_protocol_get_value, + omapi_protocol_destroy, + omapi_protocol_signal_handler, + omapi_protocol_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_protocol_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = (omapi_object_type_register + (&omapi_type_protocol_listener, "protocol-listener", + omapi_protocol_listener_set_value, + omapi_protocol_listener_get_value, + omapi_protocol_listener_destroy, + omapi_protocol_listener_signal, + omapi_protocol_listener_stuff, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_protocol_listener_object_t), 0, RC_MISC)); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_message, + "message", + omapi_message_set_value, + omapi_message_get_value, + omapi_message_destroy, + omapi_message_signal_handler, + omapi_message_stuff_values, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_message_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_waiter, + "waiter", + 0, + 0, + 0, + omapi_waiter_signal_handler, 0, + 0, 0, 0, 0, 0, 0, + sizeof (omapi_waiter_object_t), + 0, RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_auth_key, + "authenticator", + 0, + omapi_auth_key_get_value, + omapi_auth_key_destroy, + 0, + omapi_auth_key_stuff_values, + omapi_auth_key_lookup, + 0, 0, 0, 0, 0, + sizeof (omapi_auth_key_t), 0, + RC_MISC); + if (status != ISC_R_SUCCESS) + return status; + +#if defined (TRACING) + omapi_listener_trace_setup (); + omapi_connection_trace_setup (); + omapi_buffer_trace_setup (); +#endif + + /* This seems silly, but leave it. */ + return ISC_R_SUCCESS; +} + +isc_result_t omapi_object_type_register (omapi_object_type_t **type, + const char *name, + isc_result_t (*set_value) + (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *), + isc_result_t (*get_value) + (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **), + isc_result_t (*destroy) + (omapi_object_t *, + const char *, int), + isc_result_t (*signal_handler) + (omapi_object_t *, + const char *, va_list), + isc_result_t (*stuff_values) + (omapi_object_t *, + omapi_object_t *, + omapi_object_t *), + isc_result_t (*lookup) + (omapi_object_t **, + omapi_object_t *, + omapi_object_t *), + isc_result_t (*create) + (omapi_object_t **, + omapi_object_t *), + isc_result_t (*remove) + (omapi_object_t *, + omapi_object_t *), + isc_result_t (*freer) + (omapi_object_t *, + const char *, int), + isc_result_t (*allocator) + (omapi_object_t **, + const char *, int), + isc_result_t (*sizer) (size_t), + size_t size, + isc_result_t (*initialize) + (omapi_object_t *, + const char *, int), + int rc_flag) +{ + omapi_object_type_t *t; + + t = dmalloc (sizeof *t, MDL); + if (!t) + return ISC_R_NOMEMORY; + memset (t, 0, sizeof *t); + + t -> name = name; + t -> set_value = set_value; + t -> get_value = get_value; + t -> destroy = destroy; + t -> signal_handler = signal_handler; + t -> stuff_values = stuff_values; + t -> lookup = lookup; + t -> create = create; + t -> remove = remove; + t -> next = omapi_object_types; + t -> sizer = sizer; + t -> size = size; + t -> freer = freer; + t -> allocator = allocator; + t -> initialize = initialize; + t -> rc_flag = rc_flag; + omapi_object_types = t; + if (type) + *type = t; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_signal (omapi_object_t *handle, const char *name, ...) +{ + va_list ap; + omapi_object_t *outer; + isc_result_t status; + + va_start (ap, name); + for (outer = handle; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> signal_handler) + status = (*(outer -> type -> signal_handler)) (outer, + name, ap); + else + status = ISC_R_NOTFOUND; + va_end (ap); + return status; +} + +isc_result_t omapi_signal_in (omapi_object_t *handle, const char *name, ...) +{ + va_list ap; + isc_result_t status; + + if (!handle) + return ISC_R_NOTFOUND; + va_start (ap, name); + + if (handle -> type -> signal_handler) + status = (*(handle -> type -> signal_handler)) (handle, + name, ap); + else + status = ISC_R_NOTFOUND; + va_end (ap); + return status; +} + +isc_result_t omapi_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_object_t *outer; + isc_result_t status; + +#if defined (DEBUG) + if (!value) { + log_info ("omapi_set_value (%.*s, NULL)", + (int)name -> len, name -> value); + } else if (value -> type == omapi_datatype_int) { + log_info ("omapi_set_value (%.*s, %ld)", + (int)name -> len, name -> value, + (long)value -> u.integer); + } else if (value -> type == omapi_datatype_string) { + log_info ("omapi_set_value (%.*s, %.*s)", + (int)name -> len, name -> value, + (int)value -> u.buffer.len, value -> u.buffer.value); + } else if (value -> type == omapi_datatype_data) { + log_info ("omapi_set_value (%.*s, %ld %lx)", + (int)name -> len, name -> value, + (long)value -> u.buffer.len, + (unsigned long)value -> u.buffer.value); + } else if (value -> type == omapi_datatype_object) { + log_info ("omapi_set_value (%.*s, %s)", + (int)name -> len, name -> value, + value -> u.object + ? (value -> u.object -> type + ? value -> u.object -> type -> name + : "(unknown object)") + : "(unknown object)"); + } +#endif + + for (outer = h; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> set_value) + status = (*(outer -> type -> set_value)) (outer, + id, name, value); + else + status = ISC_R_NOTFOUND; +#if defined (DEBUG) + log_info (" ==> %s", isc_result_totext (status)); +#endif + return status; +} + +isc_result_t omapi_set_value_str (omapi_object_t *h, + omapi_object_t *id, + const char *name, + omapi_typed_data_t *value) +{ + omapi_data_string_t *nds; + isc_result_t status; + + nds = (omapi_data_string_t *)0; + status = omapi_data_string_new (&nds, strlen (name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (nds -> value, name, strlen (name)); + + status = omapi_set_value (h, id, nds, value); + omapi_data_string_dereference (&nds, MDL); + return status; +} + +isc_result_t omapi_set_boolean_value (omapi_object_t *h, omapi_object_t *id, + const char *name, int value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + + status = omapi_data_string_new (&n, strlen (name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, MDL); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +isc_result_t omapi_set_int_value (omapi_object_t *h, omapi_object_t *id, + const char *name, int value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + + status = omapi_data_string_new (&n, strlen (name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, MDL); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +isc_result_t omapi_set_object_value (omapi_object_t *h, omapi_object_t *id, + const char *name, omapi_object_t *value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + + status = omapi_data_string_new (&n, strlen (name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_object, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, MDL); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +isc_result_t omapi_set_string_value (omapi_object_t *h, omapi_object_t *id, + const char *name, const char *value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + + status = omapi_data_string_new (&n, strlen (name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, MDL); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, MDL); + omapi_typed_data_dereference (&tv, MDL); + return status; +} + +isc_result_t omapi_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_object_t *outer; + + for (outer = h; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> get_value) + return (*(outer -> type -> get_value)) (outer, + id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_get_value_str (omapi_object_t *h, + omapi_object_t *id, + const char *name, + omapi_value_t **value) +{ + omapi_object_t *outer; + omapi_data_string_t *nds; + isc_result_t status; + + nds = (omapi_data_string_t *)0; + status = omapi_data_string_new (&nds, strlen (name), MDL); + if (status != ISC_R_SUCCESS) + return status; + memcpy (nds -> value, name, strlen (name)); + + for (outer = h; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> get_value) + status = (*(outer -> type -> get_value)) (outer, + id, nds, value); + else + status = ISC_R_NOTFOUND; + omapi_data_string_dereference (&nds, MDL); + return status; +} + +isc_result_t omapi_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *o) +{ + omapi_object_t *outer; + + for (outer = o; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> stuff_values) + return (*(outer -> type -> stuff_values)) (c, id, outer); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_object_create (omapi_object_t **obj, omapi_object_t *id, + omapi_object_type_t *type) +{ + if (!type -> create) + return ISC_R_NOTIMPLEMENTED; + return (*(type -> create)) (obj, id); +} + +isc_result_t omapi_object_update (omapi_object_t *obj, omapi_object_t *id, + omapi_object_t *src, omapi_handle_t handle) +{ + omapi_generic_object_t *gsrc; + isc_result_t status; + int i; + + if (!src) + return DHCP_R_INVALIDARG; + if (src -> type != omapi_type_generic) + return ISC_R_NOTIMPLEMENTED; + gsrc = (omapi_generic_object_t *)src; + for (i = 0; i < gsrc -> nvalues; i++) { + status = omapi_set_value (obj, id, + gsrc -> values [i] -> name, + gsrc -> values [i] -> value); + if (status != ISC_R_SUCCESS && status != DHCP_R_UNCHANGED) + return status; + } + + /* + * For now ignore the return value. I'm not sure if we want to + * generate an error if we can't set the handle value. If we + * do add a check we probably should allow unchanged and notfound + */ + if (handle) + (void) omapi_set_int_value (obj, id, "remote-handle", (int)handle); + status = omapi_signal (obj, "updated"); + if (status != ISC_R_NOTFOUND) + return status; + return ISC_R_SUCCESS; +} + +int omapi_data_string_cmp (omapi_data_string_t *s1, omapi_data_string_t *s2) +{ + unsigned len; + int rv; + + if (s1 -> len > s2 -> len) + len = s2 -> len; + else + len = s1 -> len; + rv = memcmp (s1 -> value, s2 -> value, len); + if (rv) + return rv; + if (s1 -> len > s2 -> len) + return 1; + else if (s1 -> len < s2 -> len) + return -1; + return 0; +} + +int omapi_ds_strcmp (omapi_data_string_t *s1, const char *s2) +{ + unsigned len, slen; + int rv; + + slen = strlen (s2); + if (slen > s1 -> len) + len = s1 -> len; + else + len = slen; + rv = memcmp (s1 -> value, s2, len); + if (rv) + return rv; + if (s1 -> len > slen) + return 1; + else if (s1 -> len < slen) + return -1; + return 0; +} + +int omapi_td_strcmp (omapi_typed_data_t *s1, const char *s2) +{ + unsigned len, slen; + int rv; + + /* If the data type is not compatible, never equal. */ + if (s1 -> type != omapi_datatype_data && + s1 -> type != omapi_datatype_string) + return -1; + + slen = strlen (s2); + if (slen > s1 -> u.buffer.len) + len = s1 -> u.buffer.len; + else + len = slen; + rv = memcmp (s1 -> u.buffer.value, s2, len); + if (rv) + return rv; + if (s1 -> u.buffer.len > slen) + return 1; + else if (s1 -> u.buffer.len < slen) + return -1; + return 0; +} + +int omapi_td_strcasecmp (omapi_typed_data_t *s1, const char *s2) +{ + unsigned len, slen; + int rv; + + /* If the data type is not compatible, never equal. */ + if (s1 -> type != omapi_datatype_data && + s1 -> type != omapi_datatype_string) + return -1; + + slen = strlen (s2); + if (slen > s1 -> u.buffer.len) + len = s1 -> u.buffer.len; + else + len = slen; + rv = casecmp (s1 -> u.buffer.value, s2, len); + if (rv) + return rv; + if (s1 -> u.buffer.len > slen) + return 1; + else if (s1 -> u.buffer.len < slen) + return -1; + return 0; +} + +isc_result_t omapi_make_value (omapi_value_t **vp, + omapi_data_string_t *name, + omapi_typed_data_t *value, + const char *file, int line) +{ + isc_result_t status; + + status = omapi_value_new (vp, file, line); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, + name, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + if (value) { + status = omapi_typed_data_reference (&(*vp) -> value, + value, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_const_value (omapi_value_t **vp, + omapi_data_string_t *name, + const unsigned char *value, + unsigned len, + const char *file, int line) +{ + isc_result_t status; + + status = omapi_value_new (vp, file, line); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, + name, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + if (value) { + status = omapi_typed_data_new (file, line, &(*vp) -> value, + omapi_datatype_data, len); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + memcpy ((*vp) -> value -> u.buffer.value, value, len); + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_int_value (omapi_value_t **vp, + omapi_data_string_t *name, + int value, const char *file, int line) +{ + isc_result_t status; + + status = omapi_value_new (vp, file, line); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, + name, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + status = omapi_typed_data_new (file, line, &(*vp) -> value, + omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_uint_value (omapi_value_t **vp, + omapi_data_string_t *name, + unsigned int value, + const char *file, int line) +{ + return omapi_make_int_value (vp, name, (int)value, file, line); +} + +isc_result_t omapi_make_object_value (omapi_value_t **vp, + omapi_data_string_t *name, + omapi_object_t *value, + const char *file, int line) +{ + isc_result_t status; + + status = omapi_value_new (vp, file, line); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, + name, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + + if (value) { + status = omapi_typed_data_new (file, line, &(*vp) -> value, + omapi_datatype_object, value); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + } + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_handle_value (omapi_value_t **vp, + omapi_data_string_t *name, + omapi_object_t *value, + const char *file, int line) +{ + isc_result_t status; + + status = omapi_value_new (vp, file, line); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, + name, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + if (value) { + status = omapi_typed_data_new (file, line, &(*vp) -> value, + omapi_datatype_int); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + status = (omapi_object_handle + ((omapi_handle_t *)&(*vp) -> value -> u.integer, + value)); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_string_value (omapi_value_t **vp, + omapi_data_string_t *name, + const char *value, + const char *file, int line) +{ + isc_result_t status; + + status = omapi_value_new (vp, file, line); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, + name, file, line); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + if (value) { + status = omapi_typed_data_new (file, line, &(*vp) -> value, + omapi_datatype_string, value); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, file, line); + return status; + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_get_int_value (unsigned long *v, omapi_typed_data_t *t) +{ + u_int32_t rv; + + if (t -> type == omapi_datatype_int) { + *v = t -> u.integer; + return ISC_R_SUCCESS; + } else if (t -> type == omapi_datatype_string || + t -> type == omapi_datatype_data) { + if (t -> u.buffer.len != sizeof (rv)) + return DHCP_R_INVALIDARG; + memcpy (&rv, t -> u.buffer.value, sizeof rv); + *v = ntohl (rv); + return ISC_R_SUCCESS; + } + return DHCP_R_INVALIDARG; +} diff --git a/omapip/test.c b/omapip/test.c new file mode 100644 index 0000000..9183108 --- /dev/null +++ b/omapip/test.c @@ -0,0 +1,110 @@ +/* test.c + + Test code for omapip... */ + +/* + * Copyright (c) 2009-2010,2013-2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "config.h" + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <omapip/result.h> +#include <sys/time.h> +#include <omapip/omapip.h> +#include <omapip/isclib.h> + +int main (int argc, char **argv) +{ + omapi_object_t *listener = (omapi_object_t*)0; + omapi_object_t *connection = (omapi_object_t*)0; + isc_result_t status; + + status = dhcp_context_create(); + if (status != ISC_R_SUCCESS) { + fprintf(stderr, "Can't initialize context: %s\n", + isc_result_totext(status)); + exit(1); + } + + status = omapi_init (); + if (status != ISC_R_SUCCESS) { + fprintf(stderr, "omapi_init failed: %s\n", + isc_result_totext(status)); + exit(1); + } + + if (argc > 1 && !strcmp (argv [1], "listen")) { + if (argc < 3) { + fprintf (stderr, "Usage: test listen port\n"); + exit (1); + } + status = omapi_generic_new (&listener, MDL); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_generic_new: %s\n", + isc_result_totext (status)); + exit (1); + } + status = omapi_protocol_listen (listener, + (unsigned)atoi (argv [2]), 1); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_listen: %s\n", + isc_result_totext (status)); + exit (1); + } + omapi_dispatch (0); + } else if (argc > 1 && !strcmp (argv [1], "connect")) { + if (argc < 4) { + fprintf (stderr, "Usage: test listen address port\n"); + exit (1); + } + status = omapi_generic_new (&connection, MDL); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_generic_new: %s\n", + isc_result_totext (status)); + exit (1); + } + status = omapi_protocol_connect (connection, + argv [2], + (unsigned)atoi (argv [3]), 0); + fprintf (stderr, "connect: %s\n", isc_result_totext (status)); + if (status != ISC_R_SUCCESS) + exit (1); + status = omapi_wait_for_completion (connection, 0); + fprintf (stderr, "completion: %s\n", + isc_result_totext (status)); + if (status != ISC_R_SUCCESS) + exit (1); + /* ... */ + } else { + fprintf (stderr, "Usage: test [listen | connect] ...\n"); + exit (1); + } + + return 0; +} diff --git a/omapip/toisc.c b/omapip/toisc.c new file mode 100644 index 0000000..07aa251 --- /dev/null +++ b/omapip/toisc.c @@ -0,0 +1,211 @@ +/* toisc.c + + Convert non-ISC result codes to ISC result codes. */ + +/* + * Copyright (c) 2004,2007,2009,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" + +#include <omapip/omapip_p.h> +#include "arpa/nameser.h" +#include "minires.h" + +#include <errno.h> + +isc_result_t uerr2isc (int err) +{ + switch (err) { + case EPERM: + return ISC_R_NOPERM; + + case ENOENT: + return ISC_R_NOTFOUND; + + case ESRCH: + return ISC_R_NOTFOUND; + + case EIO: + return ISC_R_IOERROR; + + case ENXIO: + return ISC_R_NOTFOUND; + + case E2BIG: + return ISC_R_NOSPACE; + + case ENOEXEC: + return DHCP_R_FORMERR; + + case ECHILD: + return ISC_R_NOTFOUND; + + case ENOMEM: + return ISC_R_NOMEMORY; + + case EACCES: + return ISC_R_NOPERM; + + case EFAULT: + return DHCP_R_INVALIDARG; + + case EEXIST: + return ISC_R_EXISTS; + + case EINVAL: + return DHCP_R_INVALIDARG; + + case ENOTTY: + return DHCP_R_INVALIDARG; + + case EFBIG: + return ISC_R_NOSPACE; + + case ENOSPC: + return ISC_R_NOSPACE; + + case EROFS: + return ISC_R_NOPERM; + + case EMLINK: + return ISC_R_NOSPACE; + + case EPIPE: + return ISC_R_NOTCONNECTED; + + case EINPROGRESS: + return ISC_R_ALREADYRUNNING; + + case EALREADY: + return ISC_R_ALREADYRUNNING; + + case ENOTSOCK: + return ISC_R_INVALIDFILE; + + case EDESTADDRREQ: + return DHCP_R_DESTADDRREQ; + + case EMSGSIZE: + return ISC_R_NOSPACE; + + case EPROTOTYPE: + return DHCP_R_INVALIDARG; + + case ENOPROTOOPT: + return ISC_R_NOTIMPLEMENTED; + + case EPROTONOSUPPORT: + return ISC_R_NOTIMPLEMENTED; + + case ESOCKTNOSUPPORT: + return ISC_R_NOTIMPLEMENTED; + + case EOPNOTSUPP: + return ISC_R_NOTIMPLEMENTED; + + case EPFNOSUPPORT: + return ISC_R_NOTIMPLEMENTED; + + case EAFNOSUPPORT: + return ISC_R_NOTIMPLEMENTED; + + case EADDRINUSE: + return ISC_R_ADDRINUSE; + + case EADDRNOTAVAIL: + return ISC_R_ADDRNOTAVAIL; + + case ENETDOWN: + return ISC_R_NETDOWN; + + case ENETUNREACH: + return ISC_R_NETUNREACH; + + case ECONNABORTED: + return ISC_R_TIMEDOUT; + + case ECONNRESET: + return DHCP_R_CONNRESET; + + case ENOBUFS: + return ISC_R_NOSPACE; + + case EISCONN: + return ISC_R_ALREADYRUNNING; + + case ENOTCONN: + return ISC_R_NOTCONNECTED; + + case ESHUTDOWN: + return ISC_R_SHUTTINGDOWN; + + case ETIMEDOUT: + return ISC_R_TIMEDOUT; + + case ECONNREFUSED: + return ISC_R_CONNREFUSED; + + case EHOSTDOWN: + return ISC_R_HOSTDOWN; + + case EHOSTUNREACH: + return ISC_R_HOSTUNREACH; + +#ifdef EDQUOT + case EDQUOT: + return ISC_R_QUOTA; +#endif + +#ifdef EBADRPC + case EBADRPC: + return ISC_R_NOTIMPLEMENTED; +#endif + +#ifdef ERPCMISMATCH + case ERPCMISMATCH: + return DHCP_R_VERSIONMISMATCH; +#endif + +#ifdef EPROGMISMATCH + case EPROGMISMATCH: + return DHCP_R_VERSIONMISMATCH; +#endif + +#ifdef EAUTH + case EAUTH: + return DHCP_R_NOTAUTH; +#endif + +#ifdef ENEEDAUTH + case ENEEDAUTH: + return DHCP_R_NOTAUTH; +#endif + +#ifdef EOVERFLOW + case EOVERFLOW: + return ISC_R_NOSPACE; +#endif + } + return ISC_R_UNEXPECTED; +} diff --git a/omapip/trace.c b/omapip/trace.c new file mode 100644 index 0000000..f4115c1 --- /dev/null +++ b/omapip/trace.c @@ -0,0 +1,719 @@ +/* trace.c + + Subroutines that support tracing of OMAPI wire transactions and + provide a mechanism for programs using OMAPI to trace their own + transactions... */ + +/* + * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001-2003 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internet Systems Consortium, Inc. + * 950 Charter Street + * Redwood City, CA 94063 + * <info@isc.org> + * https://www.isc.org/ + * + */ + +#include "dhcpd.h" +#include <omapip/omapip_p.h> +#include <errno.h> + +#if defined (TRACING) +void (*trace_set_time_hook) (TIME); +static int tracing_stopped; +static int traceoutfile; +static int traceindex; +static trace_type_t **trace_types; +static int trace_type_count; +static int trace_type_max; +static trace_type_t *new_trace_types; +static FILE *traceinfile; +static tracefile_header_t tracefile_header; +static int trace_playback_flag; +trace_type_t trace_time_marker; + +#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) +extern omapi_array_t *trace_listeners; +extern omapi_array_t *omapi_connections; + +extern int errno; + +void trace_free_all () +{ + trace_type_t *tp; + int i; + tp = new_trace_types; + while (tp) { + new_trace_types = tp -> next; + if (tp -> name) { + dfree (tp -> name, MDL); + tp -> name = (char *)0; + } + dfree (tp, MDL); + tp = new_trace_types; + } + for (i = 0; i < trace_type_count; i++) { + if (trace_types [i]) { + if (trace_types [i] -> name) + dfree (trace_types [i] -> name, MDL); + dfree (trace_types [i], MDL); + } + } + dfree (trace_types, MDL); + trace_types = (trace_type_t **)0; + trace_type_count = trace_type_max = 0; + + omapi_array_free (&trace_listeners, MDL); + omapi_array_free (&omapi_connections, MDL); +} +#endif + +static isc_result_t trace_type_record (trace_type_t *, + unsigned, const char *, int); + +int trace_playback () +{ + return trace_playback_flag; +} + +int trace_record () +{ + if (traceoutfile && !tracing_stopped) + return 1; + return 0; +} + +isc_result_t trace_init (void (*set_time) (TIME), + const char *file, int line) +{ + trace_type_t *root_type; + static int root_setup = 0; + + if (root_setup) + return ISC_R_SUCCESS; + + trace_set_time_hook = set_time; + + root_type = trace_type_register ("trace-index-mapping", + (void *)0, trace_index_map_input, + trace_index_stop_tracing, file, line); + if (!root_type) + return ISC_R_UNEXPECTED; + if (new_trace_types == root_type) + new_trace_types = new_trace_types -> next; + root_type -> index = 0; + trace_type_stash (root_type); + + root_setup = 1; + return ISC_R_SUCCESS; +} + +isc_result_t trace_begin (const char *filename, + const char *file, int line) +{ + tracefile_header_t tfh; + int status; + trace_type_t *tptr, *next; + isc_result_t result; + + if (traceoutfile) { + log_error ("%s(%d): trace_begin called twice", + file, line); + return DHCP_R_INVALIDARG; + } + + traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600); + if (traceoutfile < 0 && errno == EEXIST) { + log_error ("WARNING: Overwriting trace file \"%s\"", filename); + traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC, + 0600); + } + + if (traceoutfile < 0) { + log_error ("%s(%d): trace_begin: %s: %m", + file, line, filename); + return ISC_R_UNEXPECTED; + } +#if defined (HAVE_SETFD) + if (fcntl (traceoutfile, F_SETFD, 1) < 0) + log_error ("Can't set close-on-exec on %s: %m", filename); +#endif + + tfh.magic = htonl (TRACEFILE_MAGIC); + tfh.version = htonl (TRACEFILE_VERSION); + tfh.hlen = htonl (sizeof (tracefile_header_t)); + tfh.phlen = htonl (sizeof (tracepacket_t)); + + status = write (traceoutfile, &tfh, sizeof tfh); + if (status < 0) { + log_error ("%s(%d): trace_begin write failed: %m", file, line); + return ISC_R_UNEXPECTED; + } else if (status != sizeof tfh) { + log_error ("%s(%d): trace_begin: short write (%d:%ld)", + file, line, status, (long)(sizeof tfh)); + trace_stop (); + return ISC_R_UNEXPECTED; + } + + /* Stash all the types that have already been set up. */ + if (new_trace_types) { + next = new_trace_types; + new_trace_types = (trace_type_t *)0; + for (tptr = next; tptr; tptr = next) { + next = tptr -> next; + if (tptr -> index != 0) { + result = (trace_type_record + (tptr, + strlen (tptr -> name), file, line)); + if (result != ISC_R_SUCCESS) + return status; + } + } + } + + return ISC_R_SUCCESS; +} + +isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length, + const char *buf, const char *file, int line) +{ + trace_iov_t iov; + + iov.buf = buf; + iov.len = length; + return trace_write_packet_iov (ttype, 1, &iov, file, line); +} + +isc_result_t trace_write_packet_iov (trace_type_t *ttype, + int count, trace_iov_t *iov, + const char *file, int line) +{ + tracepacket_t tmp; + int status; + int i; + int length; + + /* Really shouldn't get called here, but it may be hard to turn off + tracing midstream if the trace file write fails or something. */ + if (tracing_stopped) + return 0; + + if (!ttype) { + log_error ("%s(%d): trace_write_packet with null trace type", + file ? file : "<unknown file>", line); + return DHCP_R_INVALIDARG; + } + if (!traceoutfile) { + log_error ("%s(%d): trace_write_packet with no tracefile.", + file ? file : "<unknown file>", line); + return DHCP_R_INVALIDARG; + } + + /* Compute the total length of the iov. */ + length = 0; + for (i = 0; i < count; i++) + length += iov [i].len; + + /* We have to swap out the data, because it may be read back on a + machine of different endianness. */ + memset(&tmp, 0, sizeof(tmp)); + tmp.type_index = htonl (ttype -> index); + tmp.when = htonl (time ((time_t *)0)); /* XXX */ + tmp.length = htonl (length); + + status = write (traceoutfile, &tmp, sizeof tmp); + if (status < 0) { + log_error ("%s(%d): trace_write_packet write failed: %m", + file, line); + return ISC_R_UNEXPECTED; + } else if (status != sizeof tmp) { + log_error ("%s(%d): trace_write_packet: short write (%d:%ld)", + file, line, status, (long)(sizeof tmp)); + trace_stop (); + } + + for (i = 0; i < count; i++) { + status = write (traceoutfile, iov [i].buf, iov [i].len); + if (status < 0) { + log_error ("%s(%d): %s write failed: %m", + file, line, "trace_write_packet"); + return ISC_R_UNEXPECTED; + } else if (status != iov [i].len) { + log_error ("%s(%d): %s: short write (%d:%d)", + file, line, + "trace_write_packet", status, length); + trace_stop (); + } + } + + /* Write padding on the end of the packet to align the next + packet to an 8-byte boundary. This is in case we decide to + use mmap in some clever way later on. */ + if (length % 8) { + static char zero [] = { 0, 0, 0, 0, 0, 0, 0 }; + unsigned padl = 8 - (length % 8); + + status = write (traceoutfile, zero, padl); + if (status < 0) { + log_error ("%s(%d): trace_write_packet write failed: %m", + file, line); + return ISC_R_UNEXPECTED; + } else if (status != padl) { + log_error ("%s(%d): trace_write_packet: short write (%d:%d)", + file, line, status, padl); + trace_stop (); + } + } + + return ISC_R_SUCCESS; +} + +void trace_type_stash (trace_type_t *tptr) +{ + trace_type_t **vec; + int delta; + if (trace_type_max <= tptr -> index) { + delta = tptr -> index - trace_type_max + 10; + vec = dmalloc (((trace_type_max + delta) * + sizeof (trace_type_t *)), MDL); + if (!vec) + return; + memset (&vec [trace_type_max], 0, + (sizeof (trace_type_t *)) * delta); + trace_type_max += delta; + if (trace_types) { + memcpy (vec, trace_types, + trace_type_count * sizeof (trace_type_t *)); + dfree (trace_types, MDL); + } + trace_types = vec; + } + trace_types [tptr -> index] = tptr; + if (tptr -> index >= trace_type_count) + trace_type_count = tptr -> index + 1; +} + +trace_type_t *trace_type_register (const char *name, + void *baggage, + void (*have_packet) (trace_type_t *, + unsigned, char *), + void (*stop_tracing) (trace_type_t *), + const char *file, int line) +{ + trace_type_t *ttmp; + unsigned slen = strlen (name); + isc_result_t status; + + ttmp = dmalloc (sizeof *ttmp, file, line); + if (!ttmp) + return ttmp; + ttmp -> index = -1; + ttmp -> name = dmalloc (slen + 1, file, line); + if (!ttmp -> name) { + dfree (ttmp, file, line); + return (trace_type_t *)0; + } + strcpy (ttmp -> name, name); + ttmp -> have_packet = have_packet; + ttmp -> stop_tracing = stop_tracing; + + if (traceoutfile) { + status = trace_type_record (ttmp, slen, file, line); + if (status != ISC_R_SUCCESS) { + dfree (ttmp -> name, file, line); + dfree (ttmp, file, line); + return (trace_type_t *)0; + } + } else { + ttmp -> next = new_trace_types; + new_trace_types = ttmp; + } + + return ttmp; +} + +static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen, + const char *file, int line) +{ + trace_index_mapping_t *tim; + isc_result_t status; + + tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line); + if (!tim) + return ISC_R_NOMEMORY; + ttmp -> index = ++traceindex; + trace_type_stash (ttmp); + tim -> index = htonl (ttmp -> index); + memcpy (tim -> name, ttmp -> name, slen); + status = trace_write_packet (trace_types [0], + slen + TRACE_INDEX_MAPPING_SIZE, + (char *)tim, file, line); + dfree (tim, file, line); + return status; +} + +/* Stop all registered trace types from trying to trace. */ + +void trace_stop (void) +{ + int i; + + for (i = 0; i < trace_type_count; i++) + if (trace_types [i] -> stop_tracing) + (*(trace_types [i] -> stop_tracing)) + (trace_types [i]); + tracing_stopped = 1; +} + +void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf) +{ + trace_index_mapping_t *tmap; + unsigned len; + trace_type_t *tptr, **prev; + + if (length < TRACE_INDEX_MAPPING_SIZE) { + log_error ("short trace index mapping"); + return; + } + tmap = (trace_index_mapping_t *)buf; + + prev = &new_trace_types; + for (tptr = new_trace_types; tptr; tptr = tptr -> next) { + len = strlen (tptr -> name); + if (len == length - TRACE_INDEX_MAPPING_SIZE && + !memcmp (tptr -> name, tmap -> name, len)) { + tptr -> index = ntohl (tmap -> index); + trace_type_stash (tptr); + *prev = tptr -> next; + return; + } + prev = &tptr -> next; + } + + log_error ("No registered trace type for type name %.*s", + (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name); + return; +} + +void trace_index_stop_tracing (trace_type_t *ttype) { } + +void trace_replay_init (void) +{ + trace_playback_flag = 1; +} + +void trace_file_replay (const char *filename) +{ + tracepacket_t *tpkt = NULL; + int status; + char *buf = NULL; + unsigned buflen; + unsigned bufmax = 0; + trace_type_t *ttype = NULL; + isc_result_t result; + int len; + + traceinfile = fopen (filename, "r"); + if (!traceinfile) { + log_error("Can't open tracefile %s: %m", filename); + return; + } +#if defined (HAVE_SETFD) + if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0) + log_error("Can't set close-on-exec on %s: %m", filename); +#endif + status = fread(&tracefile_header, 1, + sizeof tracefile_header, traceinfile); + if (status < sizeof tracefile_header) { + if (ferror(traceinfile)) + log_error("Error reading trace file header: %m"); + else + log_error("Short read on trace file header: %d %ld.", + status, (long)(sizeof tracefile_header)); + goto out; + } + tracefile_header.magic = ntohl(tracefile_header.magic); + tracefile_header.version = ntohl(tracefile_header.version); + tracefile_header.hlen = ntohl(tracefile_header.hlen); + tracefile_header.phlen = ntohl(tracefile_header.phlen); + + if (tracefile_header.magic != TRACEFILE_MAGIC) { + log_error("%s: not a dhcp trace file.", filename); + goto out; + } + if (tracefile_header.version > TRACEFILE_VERSION) { + log_error ("tracefile version %ld > current %ld.", + (long int)tracefile_header.version, + (long int)TRACEFILE_VERSION); + goto out; + } + if (tracefile_header.phlen < sizeof *tpkt) { + log_error("tracefile packet size too small - %ld < %ld", + (long int)tracefile_header.phlen, + (long int)sizeof *tpkt); + goto out; + } + len = (sizeof tracefile_header) - tracefile_header.hlen; + if (len < 0) { + log_error("tracefile header size too small - %ld < %ld", + (long int)tracefile_header.hlen, + (long int)sizeof tracefile_header); + goto out; + } + if (len > 0) { + status = fseek(traceinfile, (long)len, SEEK_CUR); + if (status < 0) { + log_error("can't seek past header: %m"); + goto out; + } + } + + tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL); + if (tpkt == NULL) { + log_error ("can't allocate trace packet header."); + goto out; + } + + while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen, + &bufmax)) == ISC_R_SUCCESS) { + (*ttype->have_packet)(ttype, tpkt->length, buf); + ttype = NULL; + } + out: + fclose(traceinfile); + if (buf != NULL) + dfree(buf, MDL); + if (tpkt != NULL) + dfree(tpkt, MDL); +} + +/* Get the next packet from the file. If ttp points to a nonzero pointer + to a trace type structure, check the next packet to see if it's of the + expected type, and back off if not. */ + +isc_result_t trace_get_next_packet (trace_type_t **ttp, + tracepacket_t *tpkt, + char **buf, unsigned *buflen, + unsigned *bufmax) +{ + trace_type_t *ttype; + unsigned paylen; + int status, curposok = 0; + fpos_t curpos; + + while(1) { + curposok = 0; + status = fgetpos(traceinfile, &curpos); + if (status < 0) { + log_error("Can't save tracefile position: %m"); + } else { + curposok = 1; + } + + status = fread(tpkt, 1, (size_t)tracefile_header.phlen, + traceinfile); + if (status < tracefile_header.phlen) { + if (ferror(traceinfile)) + log_error("Error reading trace packet header: " + "%m"); + else if (status == 0) + return ISC_R_EOF; + else + log_error ("Short read on trace packet header:" + " %ld %ld.", + (long int)status, + (long int)tracefile_header.phlen); + return DHCP_R_PROTOCOLERROR; + } + + /* Swap the packet. */ + tpkt->type_index = ntohl(tpkt -> type_index); + tpkt->length = ntohl(tpkt -> length); + tpkt->when = ntohl(tpkt -> when); + + /* See if there's a handler for this packet type. */ + if (tpkt->type_index < trace_type_count && + trace_types[tpkt->type_index]) + ttype = trace_types[tpkt->type_index]; + else { + log_error ("Trace packet with unknown index %ld", + (long int)tpkt->type_index); + return DHCP_R_PROTOCOLERROR; + } + + /* + * Determine if we should try to expire any timer events. + * We do so if: + * we aren't looking for a specific type of packet + * we have a hook to use to update the timer + * the timestamp on the packet doesn't match the current time + * When we do so we rewind the file to the beginning of this + * packet and then try for a new packet. This allows + * any code triggered by a timeout to get the current packet + * while we get the next one. + */ + + if ((ttp != NULL) && (*ttp == NULL) && + (tpkt->when != cur_tv.tv_sec) && + (trace_set_time_hook != NULL)) { + if (curposok == 0) { + log_error("no curpos for fsetpos in " + "tracefile"); + return DHCP_R_PROTOCOLERROR; + } + + status = fsetpos(traceinfile, &curpos); + if (status < 0) { + log_error("fsetpos in tracefile failed: %m"); + return DHCP_R_PROTOCOLERROR; + } + + (*trace_set_time_hook) (tpkt->when); + continue; + } + break; + } + + /* If we were supposed to get a particular kind of packet, + check to see that we got the right kind. */ + if (ttp && *ttp && ttype != *ttp) { + log_error ("Read packet type %s when expecting %s", + ttype -> name, (*ttp) -> name); + status = fsetpos (traceinfile, &curpos); + if (status < 0) { + log_error ("fsetpos in tracefile failed: %m"); + return DHCP_R_PROTOCOLERROR; + } + return ISC_R_UNEXPECTEDTOKEN; + } + + paylen = tpkt -> length; + if (paylen % 8) + paylen += 8 - (tpkt -> length % 8); + + /* allocate a buffer if we need one or current buffer is too small */ + if ((*buf == NULL) || (paylen > (*bufmax))) { + if ((*buf)) + dfree ((*buf), MDL); + (*bufmax) = ((paylen + 1023) & ~1023U); + (*buf) = dmalloc ((*bufmax), MDL); + if (!(*buf)) { + log_error ("Can't allocate input buffer sized %d", + (*bufmax)); + return ISC_R_NOMEMORY; + } + } + + status = fread ((*buf), 1, paylen, traceinfile); + if (status < paylen) { + if (ferror (traceinfile)) + log_error ("Error reading trace payload: %m"); + else + log_error ("Short read on trace payload: %d %d.", + status, paylen); + return DHCP_R_PROTOCOLERROR; + } + + /* Store the actual length of the payload. */ + *buflen = tpkt -> length; + + if (ttp) + *ttp = ttype; + return ISC_R_SUCCESS; +} + +isc_result_t trace_get_packet (trace_type_t **ttp, + unsigned *buflen, char **buf) +{ + tracepacket_t *tpkt; + unsigned bufmax = 0; + isc_result_t status; + + if (!buf || *buf) + return DHCP_R_INVALIDARG; + + tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL); + if (!tpkt) { + log_error ("can't allocate trace packet header."); + return ISC_R_NOMEMORY; + } + + status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax); + + dfree (tpkt, MDL); + return status; +} + +/* Get a packet from the trace input file that contains a file with the + specified name. We don't hunt for the packet - it should be the next + packet in the tracefile. If it's not, or something else bad happens, + return an error code. */ + +isc_result_t trace_get_file (trace_type_t *ttype, + const char *filename, unsigned *len, char **buf) +{ + fpos_t curpos; + unsigned max = 0; + tracepacket_t *tpkt; + int status; + isc_result_t result; + + /* Disallow some obvious bogosities. */ + if (!buf || !len || *buf) + return DHCP_R_INVALIDARG; + + /* Save file position in case of filename mismatch. */ + status = fgetpos (traceinfile, &curpos); + if (status < 0) + log_error ("Can't save tracefile position: %m"); + + tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL); + if (!tpkt) { + log_error ("can't allocate trace packet header."); + return ISC_R_NOMEMORY; + } + + result = trace_get_next_packet (&ttype, tpkt, buf, len, &max); + /* done with tpkt, free it */ + dfree (tpkt, MDL); + if (result != ISC_R_SUCCESS) { + if (*buf) { + dfree (*buf, MDL); + *buf = NULL; + } + return result; + } + + /* Make sure the filename is right. */ + if (strcmp (filename, *buf)) { + log_error ("Read file %s when expecting %s", *buf, filename); + dfree (*buf, MDL); + *buf = NULL; + + status = fsetpos (traceinfile, &curpos); + if (status < 0) { + log_error ("fsetpos in tracefile failed: %m"); + return DHCP_R_PROTOCOLERROR; + } + return ISC_R_UNEXPECTEDTOKEN; + } + + return ISC_R_SUCCESS; +} +#endif /* TRACING */ |