diff --git a/.gitattributes b/.gitattributes index b86e4ef..8e31cf7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,6 +6,32 @@ /configure -text /configure.in -text /install-sh -text +libopm/.cvsignore -text +libopm/Makefile.am -text +libopm/Makefile.in -text +libopm/compat.c -text +libopm/compat.h -text +libopm/config.c -text +libopm/config.h -text +libopm/inet.c -text +libopm/inet.h -text +libopm/libopm.c -text +libopm/libopm.h -text +libopm/list.c -text +libopm/list.h -text +libopm/make -text +libopm/malloc.c -text +libopm/malloc.h -text +libopm/opm.h -text +libopm/opm_common.h -text +libopm/opm_error.h -text +libopm/opm_types.h -text +libopm/proxy.c -text +libopm/proxy.h -text +libopm/setup.h.in -text +libopm/snprintf.c -text +libopm/snprintf.h -text +libopm/test.c -text /modconfig.h.in -text /opsb.c -text /opsb.h -text diff --git a/libopm/.cvsignore b/libopm/.cvsignore new file mode 100644 index 0000000..b00cfec --- /dev/null +++ b/libopm/.cvsignore @@ -0,0 +1,10 @@ +*.sw[op] +Makefile +*.lo +*.la +setup.h +stamp-h* +.deps +.libs +test +test_debug diff --git a/libopm/Makefile.am b/libopm/Makefile.am new file mode 100644 index 0000000..db79348 --- /dev/null +++ b/libopm/Makefile.am @@ -0,0 +1,19 @@ +lib_LTLIBRARIES = libopm.la + +libopm_la_SOURCES = compat.c compat.h config.c config.h inet.c inet.h \ + libopm.c libopm.h list.c list.h malloc.c malloc.h opm_common.h \ + opm_error.h opm.h opm_types.h proxy.c proxy.h setup.h + +include_HEADERS = opm.h opm_error.h opm_types.h opm_common.h + +libopm_la_LIBADD = @ETR_SOCKET_LIBS@ @LTLIBOBJS@ + +noinst_PROGRAMS = test test_debug +test_SOURCES = test.c +test_LDADD = libopm.la compat.o @LIBOBJS@ + +# An easier-to-debug version of test + +test_debug_SOURCES = test.c +test_debug_LDADD = libopm.la compat.o @LIBOBJS@ +test_debug_LDFLAGS = -static diff --git a/libopm/Makefile.in b/libopm/Makefile.in new file mode 100644 index 0000000..8b6b232 --- /dev/null +++ b/libopm/Makefile.in @@ -0,0 +1,452 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# 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@ +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AS = @AS@ +AWK = @AWK@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOX_DIR_HTML = @DOX_DIR_HTML@ +DOX_DIR_LATEX = @DOX_DIR_LATEX@ +DOX_DIR_MAN = @DOX_DIR_MAN@ +ECHO = @ECHO@ +ETR_SOCKET_LIBS = @ETR_SOCKET_LIBS@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ +LNSL = @LNSL@ +LN_S = @LN_S@ +LSOCKET = @LSOCKET@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +OBJDUMP = @OBJDUMP@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ +VERSION = @VERSION@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +lib_LTLIBRARIES = libopm.la + +libopm_la_SOURCES = compat.c compat.h config.c config.h inet.c inet.h \ + libopm.c libopm.h list.c list.h malloc.c malloc.h opm_common.h \ + opm_error.h opm.h opm_types.h proxy.c proxy.h setup.h + + +include_HEADERS = opm.h opm_error.h opm_types.h opm_common.h + +libopm_la_LIBADD = @ETR_SOCKET_LIBS@ @LTLIBOBJS@ + +noinst_PROGRAMS = test test_debug +test_SOURCES = test.c +test_LDADD = libopm.la compat.o @LIBOBJS@ + + +# An easier-to-debug version of test +test_debug_SOURCES = test.c +test_debug_LDADD = libopm.la compat.o @LIBOBJS@ +test_debug_LDFLAGS = -static +subdir = src +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = setup.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(lib_LTLIBRARIES) + +libopm_la_LDFLAGS = +libopm_la_DEPENDENCIES = @LTLIBOBJS@ +am_libopm_la_OBJECTS = compat.lo config.lo inet.lo libopm.lo list.lo \ + malloc.lo proxy.lo +libopm_la_OBJECTS = $(am_libopm_la_OBJECTS) +noinst_PROGRAMS = test$(EXEEXT) test_debug$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) + +am_test_OBJECTS = test.$(OBJEXT) +test_OBJECTS = $(am_test_OBJECTS) +test_DEPENDENCIES = libopm.la compat.o @LIBOBJS@ +test_LDFLAGS = +am_test_debug_OBJECTS = test.$(OBJEXT) +test_debug_OBJECTS = $(am_test_debug_OBJECTS) +test_debug_DEPENDENCIES = libopm.la compat.o @LIBOBJS@ + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/snprintf.Plo $(DEPDIR)/snprintf.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/compat.Plo ./$(DEPDIR)/config.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/inet.Plo ./$(DEPDIR)/libopm.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/list.Plo ./$(DEPDIR)/malloc.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/proxy.Plo ./$(DEPDIR)/test.Po +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = $(libopm_la_SOURCES) $(test_SOURCES) \ + $(test_debug_SOURCES) +HEADERS = $(include_HEADERS) + +DIST_COMMON = $(include_HEADERS) Makefile.am Makefile.in setup.h.in \ + snprintf.c +SOURCES = $(libopm_la_SOURCES) $(test_SOURCES) $(test_debug_SOURCES) + +all: setup.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) + +setup.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/setup.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status src/setup.h + +$(srcdir)/setup.h.in: @MAINTAINER_MODE_TRUE@ $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOHEADER) + touch $(srcdir)/setup.h.in + +distclean-hdr: + -rm -f setup.h stamp-h1 +libLTLIBRARIES_INSTALL = $(INSTALL) +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libdir) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test -z "$dir" && dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libopm.la: $(libopm_la_OBJECTS) $(libopm_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libopm_la_LDFLAGS) $(libopm_la_OBJECTS) $(libopm_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +test$(EXEEXT): $(test_OBJECTS) $(test_DEPENDENCIES) + @rm -f test$(EXEEXT) + $(LINK) $(test_LDFLAGS) $(test_OBJECTS) $(test_LDADD) $(LIBS) +test_debug$(EXEEXT): $(test_debug_OBJECTS) $(test_debug_DEPENDENCIES) + @rm -f test_debug$(EXEEXT) + $(LINK) $(test_debug_LDFLAGS) $(test_debug_OBJECTS) $(test_debug_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/snprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) ./$(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$< +CCDEPMODE = @CCDEPMODE@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +includeHEADERS_INSTALL = $(INSTALL_HEADER) +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(includedir) + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(includeHEADERS_INSTALL) $$d$$p $(DESTDIR)$(includedir)/$$f"; \ + $(includeHEADERS_INSTALL) $$d$$p $(DESTDIR)$(includedir)/$$f; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(includedir)/$$f"; \ + rm -f $(DESTDIR)$(includedir)/$$f; \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: $(HEADERS) $(SOURCES) setup.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) setup.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) setup.h + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir) + +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_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-libLTLIBRARIES clean-libtool \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-hdr distclean-libtool \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: install-includeHEADERS + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-includeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstPROGRAMS \ + distclean distclean-compile distclean-depend distclean-generic \ + distclean-hdr distclean-libtool distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am \ + install-includeHEADERS install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool tags uninstall \ + uninstall-am uninstall-includeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + +# 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/libopm/compat.c b/libopm/compat.c new file mode 100644 index 0000000..b489c36 --- /dev/null +++ b/libopm/compat.c @@ -0,0 +1,70 @@ +/* vim: set shiftwidth=3 softtabstop=3 expandtab: */ + +/* + * Copyright (C) 2002 Andy Smith + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA + */ + +#include "setup.h" + +#include + +#ifdef STDC_HEADERS +# include +#endif + +#ifndef HAVE_INET_ATON +# include +#endif + +#include "compat.h" +#include "opm.h" + +RCSID("$Id: compat.c,v 1.3 2003/01/11 06:18:41 andy Exp $"); + +#ifndef HAVE_INET_ATON +/* + * An implementation of inet_aton for those systems that don't have it + * (Solaris, ...) + */ +int libopm_inet_aton(const char *cp, struct in_addr *inp) +{ + unsigned int a1, a2, a3, a4; + unsigned long ret; + + if (strcmp(cp, "255.255.255.255") == 0) { + inp->s_addr = (unsigned) -1; + return 0; + } + + if (sscanf(cp, "%u.%u.%u.%u", &a1, &a2, &a3, &a4) != 4 || + a1 > 255 || a2 > 255 || a3 > 255 || a4 > 255) { + return 0; + } + + ret = (a1 << 24) | (a2 << 16) | (a3 << 8) | a4; + + inp->s_addr = htonl(ret); + + if (inp->s_addr == (unsigned) -1) { + return 0; + } + return 1; +} +#endif diff --git a/libopm/compat.h b/libopm/compat.h new file mode 100644 index 0000000..33d1b9b --- /dev/null +++ b/libopm/compat.h @@ -0,0 +1,29 @@ +#ifndef COMPAT_H +#define COMPAT_H + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#ifndef HAVE_INET_ATON +#undef inet_aton +#define inet_aton libopm_inet_aton +extern int libopm_inet_aton(const char *cp, struct in_addr *inp); +#endif + +#ifndef HAVE_SNPRINTF +#undef snprintf +#define snprintf libopm_snprintf +#endif + +#ifndef HAVE_VSNPRINTF +#undef vsnprintf +#define vsnprintf libopm_vsnprintf +#endif + +#ifndef HAVE_INET_PTON +#undef inet_pton +#define inet_pton libopm_inet_pton +#endif + +#endif diff --git a/libopm/config.c b/libopm/config.c new file mode 100644 index 0000000..31ccca6 --- /dev/null +++ b/libopm/config.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2002 Erik Fears + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + * + */ + +#include "setup.h" + +#include "malloc.h" +#include "config.h" +#include "inet.h" +#include "opm_error.h" +#include "opm_types.h" +#include "opm_common.h" +#include "list.h" + +#ifdef STDC_HEADERS +# include +#endif + +RCSID("$Id: config.c,v 1.27 2003/06/20 04:18:44 andy Exp $"); + +static OPM_CONFIG_HASH_T HASH[] = { + {OPM_CONFIG_FD_LIMIT, OPM_TYPE_INT}, + {OPM_CONFIG_BIND_IP , OPM_TYPE_ADDRESS}, + {OPM_CONFIG_DNSBL_HOST, OPM_TYPE_STRING}, + {OPM_CONFIG_TARGET_STRING, OPM_TYPE_STRINGLIST}, + {OPM_CONFIG_SCAN_IP, OPM_TYPE_STRING}, + {OPM_CONFIG_SCAN_PORT, OPM_TYPE_INT}, + {OPM_CONFIG_MAX_READ, OPM_TYPE_INT}, + {OPM_CONFIG_TIMEOUT, OPM_TYPE_INT}, +}; + + +/* config_create + * + * Create an OPM_CONFIG_T struct, set default values and return it + * + * Parameters: + * None; + * + * Return: + * Pointer to allocated OPM_CONFIG_T struct + */ + +OPM_CONFIG_T *libopm_config_create() +{ + int num, i; + OPM_CONFIG_T *ret; + + num = sizeof(HASH) / sizeof(OPM_CONFIG_HASH_T); + + ret = MyMalloc(sizeof(OPM_CONFIG_T)); + ret->vars = MyMalloc(sizeof(void *) * num); + + + /* Set default config items. This in the future would be much better + if it could set realistic defaults for each individual config item. + + OPM_TYPE_INT = 0 + OPM_TYPE_STRING = "" + OPM_TYPE_ADDRESS = 0.0.0.0 + OPM_TYPE_STRINGLIST = empty list + */ + + for(i = 0; i < num; i++) + { + switch(libopm_config_gettype(i)) + { + case OPM_TYPE_INT: + ret->vars[i] = MyMalloc(sizeof(int)); + *(int *) ret->vars[i] = 0; + break; + + case OPM_TYPE_STRING: + (char *) ret->vars[i] = strdup(""); + break; + + case OPM_TYPE_ADDRESS: + (opm_sockaddr *) ret->vars[i] = MyMalloc(sizeof(opm_sockaddr)); + memset((opm_sockaddr *) ret->vars[i], 0, sizeof(opm_sockaddr)); + break; + + case OPM_TYPE_STRINGLIST: + (OPM_LIST_T *) ret->vars[i] = libopm_list_create(); + break; + default: + ret->vars[i] = NULL; + } + } + return ret; +} + + + + +/* config_free + * + * Free config structure and clean up + * + * Parameters: + * config: Structure to free/cleanup + * + * Return: + * None + */ + +void libopm_config_free(OPM_CONFIG_T *config) +{ + int num, i; + OPM_NODE_T *p, *next; + OPM_LIST_T *list; + + num = sizeof(HASH) / sizeof(OPM_CONFIG_HASH_T); + + for(i = 0; i < num; i++) + { + if(config->vars[i] == NULL) + continue; + + switch(libopm_config_gettype(i)) + { + case OPM_TYPE_STRINGLIST: + list = (OPM_LIST_T *) config->vars[i]; + LIST_FOREACH_SAFE(p, next, list->head) + { + MyFree(p->data); + MyFree(p); + } + break; + default: + MyFree(config->vars[i]); + break; + } + } + + MyFree(config->vars); + MyFree(config); +} + + + + +/* config_set + * + * Set configuration options on config struct. + * + * Parameters: + * config: Config struct to set parameters on + * key: Variable within the struct to set + * value: Address of value to set + * + * Return: + * 1: Variable was set + * 0: Some error occured + */ + +OPM_ERR_T libopm_config_set(OPM_CONFIG_T *config, int key, void *value) +{ + + int num; + OPM_NODE_T *node; + + num = sizeof(HASH) / sizeof(OPM_CONFIG_HASH_T); + + if(key < 0 || key >= num) + return OPM_ERR_BADKEY; /* Return appropriate error code eventually */ + + switch(libopm_config_gettype(key)) + { + case OPM_TYPE_STRING: + if((char *) config->vars[key] != NULL) + MyFree(config->vars[key]); + (char *) config->vars[key] = strdup((char *) value); + break; + + case OPM_TYPE_INT: + *(int *) config->vars[key] = *(int *) value; + break; + + case OPM_TYPE_ADDRESS: + if( inet_pton(AF_INET, (char *) value, &( ((opm_sockaddr *)config->vars[key])->sa4.sin_addr)) + <= 0) + return OPM_ERR_BADVALUE; /* return appropriate err code */ + break; + + case OPM_TYPE_STRINGLIST: + node = libopm_node_create(strdup((char *) value)); + libopm_list_add((OPM_LIST_T *) config->vars[key], node); + break; + + default: + return OPM_ERR_BADKEY; /* return appropriate err code */ + + } + + return OPM_SUCCESS; + +} + + + + +/* config_gettype + * + * Get type of key. + * + * Parameters: + * key: Key to get type of. + * + * Return: + * TYPE_? of key + */ + +int libopm_config_gettype(int key) +{ + int num, i; + + num = sizeof(HASH) / sizeof(OPM_CONFIG_HASH_T); + + for(i = 0; i < num; i++) + if(HASH[i].key == key) + return HASH[i].type; + + return 0; +} + +/* config + * + * Retrieve a specific config variable from + * an OPM_CONFIG_T struct. This is basically a + * wrapper to extracting the variable from the + * array. + * + * Parameters: + * config: Config struct to extract from + * key: Value to extract + * + * Return: + * -ADDRESS- to extracted value in array. This address + * will have to be cast on the return end to be any use. + */ + +void *libopm_config(OPM_CONFIG_T *config, int key) +{ + return config->vars[key]; +} diff --git a/libopm/config.h b/libopm/config.h new file mode 100644 index 0000000..2b6f0c6 --- /dev/null +++ b/libopm/config.h @@ -0,0 +1,20 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "libopm.h" + +typedef struct _opm_config_hash OPM_CONFIG_HASH_T; + +struct _opm_config_hash { + int key; + int type; +}; + +void libopm_config_free(OPM_CONFIG_T *); +void *libopm_config(OPM_CONFIG_T *, int); +int libopm_config_gettype(int); +OPM_CONFIG_T *libopm_config_create(void); +OPM_ERR_T libopm_config_set(OPM_CONFIG_T *, int , void *); + + +#endif /* CONFIG_H */ diff --git a/libopm/inet.c b/libopm/inet.c new file mode 100644 index 0000000..1fffad6 --- /dev/null +++ b/libopm/inet.c @@ -0,0 +1,534 @@ +/* +Copyright (C) 2002 by the past and present ircd coders, and others. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to + + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + +*/ + +/* + * This code is borrowed from ircd-hybrid version 7 + * -TimeMr14C + */ + +#include "setup.h" + +#include +#include +#include +#if STDC_HEADERS +# include +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include +#include +#include +#include + +#include "inet.h" +#include "opm.h" + +#ifndef INADDRSZ +#define INADDRSZ 4 +#endif + +#ifdef IPV6 +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 +#endif +#endif + + +#ifndef INT16SZ +#define INT16SZ 2 +#endif + +#ifdef IPV6 +#define HOSTIPLEN 53 +#else +#define HOSTIPLEN 15 +#endif + +RCSID("$Id: inet.c,v 1.7 2003/06/20 04:18:44 andy Exp $"); + +extern const unsigned char ToLowerTab[]; +#define ToLower(c) (ToLowerTab[(unsigned char)(c)]) + +/* + * From: Thomas Helvey + */ +#if 0 +static const char *IpQuadTab[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", + "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", + "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", + "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", + "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", + "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", + "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", + "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", + "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", + "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", + "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", + "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", + "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", + "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", + "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", + "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", + "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255" +}; +#endif + +/* + * inetntoa - in_addr to string + * changed name to remove collision possibility and + * so behaviour is guaranteed to take a pointer arg. + * -avalon 23/11/92 + * inet_ntoa -- returned the dotted notation of a given + * internet number + * argv 11/90). + * inet_ntoa -- its broken on some Ultrix/Dynix too. -avalon + * + * XXX - Again not used anywhere? + * -grifferz + */ + +#if 0 +static char *inetntoa(char *in) +{ + static char buf[16]; + register char *bufptr = buf; + register const unsigned char *a = (const unsigned char *) in; + register const char *n; + + n = IpQuadTab[*a++]; + while (*n) + *bufptr++ = *n++; + *bufptr++ = '.'; + n = IpQuadTab[*a++]; + while (*n) + *bufptr++ = *n++; + *bufptr++ = '.'; + n = IpQuadTab[*a++]; + while (*n) + *bufptr++ = *n++; + *bufptr++ = '.'; + n = IpQuadTab[*a]; + while (*n) + *bufptr++ = *n++; + *bufptr = '\0'; + return buf; +} +#endif + +/* + * Copyright (c) 1996-1999 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM 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. + */ + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +/* + * XXX - this does not seem to be used anywhere currently. + * -grifferz + */ +#if 0 +static const char *inet_ntop4(const u_char *src, char *dst, unsigned int size); +#endif + +#ifdef IPV6 +static const char *inet_ntop6(const u_char *src, char *dst, unsigned int size); +#endif + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + * + * XXX - this does not seem to be used anywhere currently. + * -grifferz + */ +#if 0 +static const char *inet_ntop4(const unsigned char *src, char *dst, unsigned int size) +{ + if (size < 15) + return NULL; + return strcpy(dst, inetntoa((char *) src)); +} +#endif + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +#ifdef IPV6 +static const char *inet_ntop6(const unsigned char *src, char *dst, unsigned int size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { + int base, len; + } best, cur; + u_int words[IN6ADDRSZ / INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < IN6ADDRSZ; i += 2) + words[i / 2] = (src[i] << 8) | src[i + 1]; + best.base = -1; + cur.base = -1; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += sprintf(tp, "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) + *tp++ = ':'; + + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + + if ((tp - tmp) > size) { + return (NULL); + } + return strcpy(dst, tmp); +} +#endif + +#if 0 + +/* + * This code doesn't seem to be used anywhere currently? + * -grifferz + */ + +/* char * + * inetntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char *inetntop(int af, const void *src, char *dst, unsigned int size) +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); +#ifdef IPV6 + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED((const struct in6_addr *) src) || + IN6_IS_ADDR_V4COMPAT((const struct in6_addr *) src)) + return (inet_ntop4 + ((unsigned char *) &((struct in6_addr *) src)->s6_addr[12], dst, size)); + else + return (inet_ntop6(src, dst, size)); + +#endif + default: + return (NULL); + } + /* NOTREACHED */ +} +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +/* int + * inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address wasn't valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * author: + * Paul Vixie, 1996. + */ + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ + +#ifndef HAVE_INET_PTON + +static int inet_pton4(src, dst) + const char *src; + unsigned char *dst; +{ +int saw_digit, octets, ch; +unsigned char tmp[INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + + if (ch >= '0' && ch <= '9') { +unsigned int new = *tp * 10 + (ch - '0'); + + if (new > 255) + return (0); + *tp = new; + if (!saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + memcpy(dst, tmp, INADDRSZ); + return (1); +} + +#ifdef IPV6 +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ + +static int inet_pton6(src, dst) + const char *src; + unsigned char *dst; +{ +static const char xdigits[] = "0123456789abcdef"; +unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; +const char *curtok; +int ch, saw_xdigit; +unsigned int val; + + tp = memset(tmp, '\0', IN6ADDRSZ); + endp = tp + IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = ToLower(*src++)) != '\0') { +const char *pch; + + pch = strchr(xdigits, ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } else if (*src == '\0') { + return (0); + } + if (tp + INT16SZ > endp) + return (0); + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + + if (*src != '\0' && ch == '.') { + if (((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { + tp += INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + } else + continue; + return (0); + } + if (saw_xdigit) { + if (tp + INT16SZ > endp) + return (0); + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ +const int n = tp - colonp; +int i; + + if (tp == endp) + return (0); + for (i = 1; i <= n; i++) { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, IN6ADDRSZ); + return (1); +} +#endif /* IPv6 */ + +int inet_pton(af, src, dst) + int af; + const char *src; + void *dst; +{ + switch (af) { + case AF_INET: + return (inet_pton4(src, dst)); +#ifdef IPV6 + case AF_INET6: + /* Somebody might have passed as an IPv4 address this is sick but it works */ + if (inet_pton4(src, dst)) { +char tmp[HOSTIPLEN]; + sprintf(tmp, "::ffff:%s", src); + return (inet_pton6(tmp, dst)); + } else + return (inet_pton6(src, dst)); +#endif /* IPv6 */ + default: + return (-1); + } + /* NOTREACHED */ +} + +#endif diff --git a/libopm/inet.h b/libopm/inet.h new file mode 100644 index 0000000..9f2324e --- /dev/null +++ b/libopm/inet.h @@ -0,0 +1,40 @@ +#ifndef INET_H +#define INET_H + +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +# include +#endif +#include + +#ifdef HAVE_SYS_POLL_H +# include +#endif + +#ifndef AF_INET6 +# define AF_INET6 10 +#endif + +typedef struct _opm_sockaddr opm_sockaddr; +typedef struct _opm_inaddr opm_inaddr; + +struct _opm_sockaddr { + struct sockaddr_in sa4; +}; + +struct _opm_inaddr { + struct in_addr in4; +}; + +#ifndef HAVE_INET_PTON +extern int inet_pton(int, const char *, void *); +#endif +extern const char *inetntop(int, const void *, char *, unsigned int); +extern struct hostent *opm_gethostbyname(const char *); + + +#endif /* INET_H */ diff --git a/libopm/libopm.c b/libopm/libopm.c new file mode 100644 index 0000000..c0211eb --- /dev/null +++ b/libopm/libopm.c @@ -0,0 +1,1432 @@ +/* + * Copyright (C) 2002-2003 Erik Fears + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + * + */ + +#include "setup.h" + +#include "config.h" +#include "libopm.h" +#include "malloc.h" +#include "opm_error.h" +#include "opm_types.h" +#include "opm_common.h" +#include "list.h" +#include "inet.h" +#include "proxy.h" + +#include +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#endif + +RCSID("$Id: libopm.c,v 1.80 2003/06/22 13:19:40 andy Exp $"); + +static OPM_PROTOCOL_CONFIG_T *libopm_protocol_config_create(void); +static void libopm_protocol_config_free(OPM_PROTOCOL_CONFIG_T *); + +/* + * XXX - does not appear to be used anywhere? + * -grifferz + */ +#if 0 +static OPM_PROTOCOL_T *libopm_protocol_create(void); +static void libopm_protocol_free(OPM_PROTOCOL_T *); +#endif + +static OPM_SCAN_T *libopm_scan_create(OPM_T *, OPM_REMOTE_T *); +static void libopm_scan_free(OPM_SCAN_T *); + +static OPM_CONNECTION_T *libopm_connection_create(void); +static void libopm_connection_free(OPM_CONNECTION_T *); + +static void libopm_check_establish(OPM_T *); +static void libopm_check_poll(OPM_T *); +static void libopm_check_closed(OPM_T *); +static void libopm_check_queue(OPM_T *); + +static void libopm_do_connect(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +static void libopm_do_readready(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +static void libopm_do_writeready(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +static void libopm_do_hup(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +static void libopm_do_read(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +static void libopm_do_openproxy(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); + +static void libopm_do_callback(OPM_T *, OPM_REMOTE_T *, int, int); + +static OPM_REMOTE_T *libopm_setup_remote(OPM_REMOTE_T *, OPM_CONNECTION_T *); + + +/* OPM_PROTOCOLS hash + * + * OPM_PPROTOCOLS hashes the protocol types (int) to functions + * which handle the protocol (sending/receiving protocol specific + * data). + * + */ + +static OPM_PROTOCOL_T OPM_PROTOCOLS[] = { + {OPM_TYPE_HTTP, libopm_proxy_http_write, NULL}, + {OPM_TYPE_SOCKS4, libopm_proxy_socks4_write, NULL}, + {OPM_TYPE_SOCKS5, libopm_proxy_socks5_write, NULL}, + {OPM_TYPE_ROUTER, libopm_proxy_router_write, NULL}, + {OPM_TYPE_WINGATE, libopm_proxy_wingate_write, NULL}, + {OPM_TYPE_HTTPPOST, libopm_proxy_httppost_write, NULL} +}; + + + + +/* opm_create + * + * Initialize a new scanner and return a pointer to it. + * + * Parameters: + * None + * + * Return + * Pointer to new OPM_T (scanner) + */ + +OPM_T *opm_create() +{ + int i; + OPM_T *ret; + + ret = MyMalloc(sizeof *ret); + + ret->config = libopm_config_create(); + ret->scans = libopm_list_create(); + ret->queue = libopm_list_create(); + ret->protocols = libopm_list_create(); + ret->fd_use = 0; + + /* Setup callbacks */ + ret->callbacks = MyMalloc(sizeof(OPM_CALLBACK_T) * CBLEN); + for(i = 0; i < CBLEN; i++) + { + ret->callbacks[i].func = NULL; + ret->callbacks[i].data = NULL; + } + + return ret; +} + + + + +/* opm_remote_create + * + * Create OPM_REMOTE_T struct, fill it with neccessary + * default values and return it to the client. + * + * Parameters: + * ip: IP of remote host + * + * Return: + * Address of OPM_REMOTE_T created + * + */ + +OPM_REMOTE_T *opm_remote_create(const char *ip) +{ + OPM_REMOTE_T *ret; + + + ret = MyMalloc(sizeof *ret); + + /* Do initializations */ + if(ip == NULL) + return NULL; + + ret->ip = (char*) strdup(ip); /* replace with custom strdup function */ + + ret->port = 0; + ret->protocol = 0; + ret->bytes_read = 0; + + ret->data = NULL; + + ret->protocols = libopm_list_create(); /* setup protocol list */ + + return ret; +} + + + +/* opm_remote_free + * + * Free OPM_REMOTE_T struct and cleanup + * + * Parameters: + * remote: Struct to free + * + * Return: + * None + */ + +void opm_remote_free(OPM_REMOTE_T *remote) +{ + + OPM_NODE_T *p, *next; + OPM_PROTOCOL_CONFIG_T *ppc; + + MyFree(remote->ip); + + LIST_FOREACH_SAFE(p, next, remote->protocols->head) + { + ppc = (OPM_PROTOCOL_CONFIG_T *) p->data; + + libopm_protocol_config_free(ppc); + libopm_list_remove(remote->protocols, p); + libopm_node_free(p); + } + + libopm_list_free(remote->protocols); + + MyFree(remote); +} + + + + +/* opm_callback + * Register scanner level callback + * + * Parameters + * scanner: scanner struct + * type: callback type + * Return: + * Error code + */ + +OPM_ERR_T opm_callback(OPM_T *scanner, int type, OPM_CALLBACK_FUNC *function, void *data) +{ + if(type < 0 || type >= (CBLEN + 1)) + return OPM_ERR_CBNOTFOUND; + + scanner->callbacks[type].func = function; + scanner->callbacks[type].data = data; + + return OPM_SUCCESS; +} + + + + +/* opm_free + * + * Free OPM_T (scanner) and cleanup + * + * Parameters: + * scanner: Address of OPM_T to cleanup + * + * Return: + * None + */ + +void opm_free(OPM_T *scanner) +{ + OPM_NODE_T *p, *next; + OPM_PROTOCOL_CONFIG_T *ppc; + OPM_SCAN_T *scan; + + libopm_config_free(scanner->config); + + LIST_FOREACH_SAFE(p, next, scanner->protocols->head) + { + ppc = (OPM_PROTOCOL_CONFIG_T *) p->data; + + libopm_protocol_config_free(ppc); + libopm_list_remove(scanner->protocols, p); + libopm_node_free(p); + } + + LIST_FOREACH_SAFE(p, next, scanner->scans->head) + { + scan = (OPM_SCAN_T *) p->data; + libopm_scan_free(scan); + libopm_list_remove(scanner->scans, p); + libopm_node_free(p); + } + + LIST_FOREACH_SAFE(p, next, scanner->queue->head) + { + scan = (OPM_SCAN_T *) p->data; + libopm_scan_free(scan); + libopm_list_remove(scanner->queue, p); + libopm_node_free(p); + } + + libopm_list_free(scanner->protocols); + libopm_list_free(scanner->scans); + libopm_list_free(scanner->queue); + + MyFree(scanner->callbacks); + MyFree(scanner); +} + + + + +/* opm_config + * + * Wrapper to config_set. Set configuration variables + * on the config struct. + * + * Parameters: + * scanner: OPM_T struct the config struct resides in + * key: Variable within the config struct to set + * value: Address of value to set variable (key) to + * + * Return: + * OPM_ERR_T containing error code + */ + +OPM_ERR_T opm_config(OPM_T *scanner, int key, void *value) +{ + return libopm_config_set((scanner->config), key, value); +} + + + + +/* opm_addtype + * + * Add a proxy type and port to the list of protocols + * a scanner will use. + * + * Parameters: + * scanner: pointer to scanner struct + * type: type of proxy to scan (used in hashing to the functions) + * port: port this specific type/protocol will scan on + * Return: + * OPM_SUCCESS: Successful protocol add + * OPM_ERR_BADPROTOCOL: Protocol is unknown + */ + +OPM_ERR_T opm_addtype(OPM_T *scanner, int type, unsigned short int port) +{ + unsigned int i; + + OPM_NODE_T *node; + OPM_PROTOCOL_CONFIG_T *protocol_config; + + for(i = 0; i < sizeof(OPM_PROTOCOLS) / sizeof(OPM_PROTOCOL_T); i++) + { + if(type == OPM_PROTOCOLS[i].type) + { + protocol_config = libopm_protocol_config_create(); + + protocol_config->type = &OPM_PROTOCOLS[i]; + protocol_config->port = port; + + node = libopm_node_create(protocol_config); + libopm_list_add(scanner->protocols, node); + + return OPM_SUCCESS; + + } + } + return OPM_ERR_BADPROTOCOL; +} + + + +/* opm_remote_addtype + * + * Add a proxy type and port to the list of protocols + * a scanner will use. + * + * Parameters: + * remote: pointer to scanner struct + * type: type of proxy to scan (used in hashing to the functions) + * port: port this specific type/protocol will scan on + * Return: + * OPM_SUCCESS: Successful protocol add + * OPM_ERR_BADPROTOCOL: Protocol is unknown + */ + +OPM_ERR_T opm_remote_addtype(OPM_REMOTE_T *remote, int type, unsigned short int port) +{ + unsigned int i; + + OPM_NODE_T *node; + OPM_PROTOCOL_CONFIG_T *protocol_config; + + for(i = 0; i < sizeof(OPM_PROTOCOLS) / sizeof(OPM_PROTOCOL_T); i++) + { + if(type == OPM_PROTOCOLS[i].type) + { + protocol_config = libopm_protocol_config_create(); + + protocol_config->type = &OPM_PROTOCOLS[i]; + protocol_config->port = port; + + node = libopm_node_create(protocol_config); + libopm_list_add(remote->protocols, node); + + return OPM_SUCCESS; + } + } + return OPM_ERR_BADPROTOCOL; +} + + + + +/* libopm_protocol_create + * + * Create OPM_PROTOCOL_T struct. + * + * Parameters: + * None + * Return: + * Pointer to new struct + * + * XXX - does not appear to be used anywhere? + * -grifferz + */ + +#if 0 +static OPM_PROTOCOL_T *libopm_protocol_create(void) +{ + OPM_PROTOCOL_T *ret; + ret = MyMalloc(sizeof(OPM_PROTOCOL_T)); + + ret->type = 0; + ret->write_function = NULL; + ret->read_function = NULL; + + return ret; +} +#endif + + +/* libopm_protocol_free + * + * Free an OPM_PROTOCOL_T struct. Assume that if + * format is not NULL, it is pointed to dynamically + * allocated memory and free it. + * + * Parameters: + * protocol: struct to free + * + * Return: + * None + * + * XXX - apparently no longer used? + * -grifferz + */ + +#if 0 +static void libopm_protocol_free(OPM_PROTOCOL_T *protocol) +{ + MyFree(protocol); +} +#endif + + + +/* libopm_protocol_config_create + * + * Allocate and return address of a new OPM_PROTOCOL_CONFIG_T + * + * Parameters: + * None + * + * Return: + * Address of new OPM_PROTOCOL_CONFIG_T + */ + +static OPM_PROTOCOL_CONFIG_T *libopm_protocol_config_create(void) +{ + OPM_PROTOCOL_CONFIG_T *ret; + ret = MyMalloc(sizeof *ret); + + return ret; +} + + + + +/* protocol_config_free + * + * Free OPM_PROTOCOL_CONFIG_T struct + * + * Parameters: + * protocol: struct to free + * + * Return: + * None + */ + +static void libopm_protocol_config_free(OPM_PROTOCOL_CONFIG_T *protocol) +{ + MyFree(protocol); +} + + + + +/* opm_scan + * + * Scan remote host. The opm_scan function takes an OPM_REMOTE_T + * struct, calculates the in_addr of the remote host, and creates + * a scan list based on protocols defined in the scanner. + * + * Parameters: + * scanner: Scanner to scan host on + * remote: OPM_REMOTE_T defining remote host + * + * Return: + * (to be written) + */ + +OPM_ERR_T opm_scan(OPM_T *scanner, OPM_REMOTE_T *remote) +{ + OPM_SCAN_T *scan; /* New scan for OPM_T */ + OPM_NODE_T *node; /* Node we'll add scan to + when we link it to scans */ + unsigned int fd_limit; + + fd_limit = *(int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT); + + if(LIST_SIZE(scanner->protocols) == 0 && + LIST_SIZE(remote->protocols) == 0) + { + return OPM_ERR_NOPROTOCOLS; + } + + scan = libopm_scan_create(scanner, remote); + + if(inet_pton(AF_INET, remote->ip, &(scan->addr.sa4.sin_addr) ) <= 0) + { + libopm_scan_free(scan); + return OPM_ERR_BADADDR; + } + + node = libopm_node_create(scan); + libopm_list_add(scanner->queue, node); + + return OPM_SUCCESS; +} + + + + +/* opm_end + * + * End a scan prematurely. + * + * Parameters: + * scanner: Scanner to end scan on + * remote: Pointer to remote struct to search for and end + * + * Return: + * No return. OPM_CALLBACK_END will still be called as normal. + */ + +void opm_end(OPM_T *scanner, OPM_REMOTE_T *remote) +{ + OPM_NODE_T *node1, *node2, *next1, *next2; + + OPM_SCAN_T *scan; + OPM_CONNECTION_T *conn; + + /* End active scans */ + opm_endscan(scanner, remote); + + /* Secondly remove all traces of it in the queue. Once removed we have to call + OPM_CALLBACK_END */ + + LIST_FOREACH_SAFE(node1, next1, scanner->queue->head) + { + scan = (OPM_SCAN_T *) node1->data; + if(scan->remote == remote) + { + /* Free all connections */ + LIST_FOREACH_SAFE(node2, next2, scan->connections->head) + { + + conn = (OPM_CONNECTION_T *) node2->data; + + libopm_list_remove(scan->connections, node2); + libopm_connection_free(conn); + libopm_node_free(node2); + continue; + } + + /* OPM_CALLBACK_END because check_closed normally handles this */ + libopm_do_callback(scanner, scan->remote, OPM_CALLBACK_END, 0); + + /* Free up the scan */ + libopm_list_remove(scanner->queue, node1); + libopm_scan_free(scan); + libopm_node_free(node1); + } + } +} + + + + +/* opm_endscan + * + * End a scan prematurely. Only end non-queued scans. This is useful + * because it only checks the active scan list (saving time), where + * opm_end checks both the scan and the possibly large queue. + * + * Parameters: + * scanner: Scanner to end scan on + * remote: Pointer to remote struct to search for and end + * + * Return: + * No return. OPM_CALLBACK_END will still be called as normal. + */ + +void opm_endscan(OPM_T *scanner, OPM_REMOTE_T *remote) +{ + OPM_NODE_T *node1, *node2; + + OPM_SCAN_T *scan; + OPM_CONNECTION_T *conn; + + /* + First check to see if it's in the queue, if it is set all connections closed + Next cycle of libopm_check_closed will take care of the garbage and handle + OPM_CALLBACK_END + */ + LIST_FOREACH(node1, scanner->scans->head) + { + scan = (OPM_SCAN_T *) node1->data; + + if(scan->remote == remote) + { + LIST_FOREACH(node2, scan->connections->head) + { + conn = (OPM_CONNECTION_T *) node2->data; + conn->state = OPM_STATE_CLOSED; + } + } + } +} + + + + +/* opm_active + + Return number of scans in a scanner left. + + Parameters: + scanner: Scanner to return active scans on + + Return: + Number of active scans, both queued and active. +*/ + +size_t opm_active(OPM_T *scanner) +{ + return LIST_SIZE(scanner->queue) + LIST_SIZE(scanner->scans); +} + + + +/* scan_create + * + * Create new OPM_SCAN_T struct + * + * Parameters: + * scanner: Scanner the scan is being created for. This + * is needed to get information on currently set + * protocols/config. + * + * remote: Remote host this scan will be scanning + * + * Return + * Address of new struct + */ +static OPM_SCAN_T *libopm_scan_create(OPM_T *scanner, OPM_REMOTE_T *remote) +{ + OPM_SCAN_T *ret; + OPM_CONNECTION_T *conn; + OPM_NODE_T *node, *p; + + ret = MyMalloc(sizeof *ret); + + ret->remote = remote; + ret->connections = libopm_list_create(); + + /* Setup list of connections, one for each protocol */ + LIST_FOREACH(p, scanner->protocols->head) + { + conn = libopm_connection_create(); + + conn->protocol = ((OPM_PROTOCOL_CONFIG_T *) p->data)->type; + conn->port = ((OPM_PROTOCOL_CONFIG_T *) p->data)->port; + + node = libopm_node_create(conn); + + libopm_list_add(ret->connections, node); + } + + /* Do the same for any specific protocols the remote struct might be configured + with */ + + LIST_FOREACH(p, remote->protocols->head) + { + conn = libopm_connection_create(); + + conn->protocol = ((OPM_PROTOCOL_CONFIG_T *) p->data)->type; + conn->port = ((OPM_PROTOCOL_CONFIG_T *) p->data)->port; + + node = libopm_node_create(conn); + + libopm_list_add(ret->connections, node); + } + + memset(&(ret->addr), 0, sizeof(opm_sockaddr)); + + return ret; +} + + + + +/* scan_free + * + * Free and cleanup OPM_SCAN_T struct + * + * Parametsr: + * scan: Scan struct to free + * + * Return: + * None + */ + +static void libopm_scan_free(OPM_SCAN_T *scan) +{ + OPM_NODE_T *p, *next; + OPM_CONNECTION_T *conn; + + LIST_FOREACH_SAFE(p, next, scan->connections->head) + { + conn = (OPM_CONNECTION_T *) p->data; + libopm_connection_free(conn); + + libopm_list_remove(scan->connections, p); + libopm_node_free(p); + } + libopm_list_free(scan->connections); + + MyFree(scan); +} + + + + +/* connection_create + * + * Allocate new OPM_CONNECTION_T + * + * Parameters: + * None + * + * Return: + * Address of new OPM_CONNECTION_T + */ + +static OPM_CONNECTION_T *libopm_connection_create(void) +{ + OPM_CONNECTION_T *ret; + ret = MyMalloc(sizeof *ret); + + ret->fd = 0; + ret->bytes_read = 0; + ret->readlen = 0; + ret->protocol = 0; + ret->port = 0; + + ret->state = OPM_STATE_UNESTABLISHED; + + return ret; +} + + + + +/* connection_free + * + * Free OPM_CONNECTION_T struct + * + * Parameters: + * conn: Address of struct to free + * + * Return: + * None + */ + +static void libopm_connection_free(OPM_CONNECTION_T *conn) +{ + MyFree(conn); +} + + +/* opm_cycle + * + * Perform tasks (called by client's loop) + * + * Parameters: + * None + * Return: + * None + */ + +void opm_cycle(OPM_T *scanner) +{ + libopm_check_queue(scanner); /* Move scans from the queue to the live scan list */ + libopm_check_establish(scanner); /* Make new connections if possible */ + libopm_check_poll(scanner); /* Poll connections for IO and proxy test */ + libopm_check_closed(scanner); /* Check for closed or timed out connections */ +} + + + + +/* check_queue + * + * Move scans from the queue to the live scan list as long as there is + * room. + * + * Parameters: + * scanner: Scanner to check queue on + * + * Return: + * None + */ + +static void libopm_check_queue(OPM_T *scanner) +{ + OPM_NODE_T *node; + OPM_SCAN_T *scan; + + unsigned int protocols, projected, fd_limit; + + if(LIST_SIZE(scanner->queue) == 0) + return; + + fd_limit = *(int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT); + + projected = scanner->fd_use; + + /* We want to keep the live scan list as small as possible, so only + move queued scans to the live list if they will not push above fd_limit */ + while(LIST_SIZE(scanner->queue) > 0) + { + + /* Grab the top scan */ + scan = (OPM_SCAN_T *) scanner->queue->head->data; + protocols = LIST_SIZE(scan->connections); + + /* Check if it will fit in the live scan list */ + if((protocols + projected) > fd_limit) + break; + + /* Scans on the top of the queue were added first, swap the head off the + top of the queue and add it to the tail of the live scan list */ + node = libopm_list_remove(scanner->queue, scanner->queue->head); + libopm_list_add(scanner->scans, node); + projected += protocols; + } + +} + + + + +/* check_establish + * + * Make new connections if there are free file descriptors and connections + * to be made. + * + * Parameters: + * scanner: Scanner to check for establish on + * Return: + * None + */ + +static void libopm_check_establish(OPM_T *scanner) +{ + OPM_NODE_T *node1, *node2; + OPM_SCAN_T *scan; + OPM_CONNECTION_T *conn; + + unsigned int fd_limit; + + if(LIST_SIZE(scanner->scans) == 0) + return; + + fd_limit = *(int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT); + + if(scanner->fd_use >= fd_limit) + return; + + LIST_FOREACH(node1, scanner->scans->head) + { + scan = (OPM_SCAN_T *) node1->data; + LIST_FOREACH(node2, scan->connections->head) + { + /* Only scan if we have free file descriptors */ + if(scanner->fd_use >= fd_limit) + return; + + conn = (OPM_CONNECTION_T *) node2->data; + if(conn->state == OPM_STATE_UNESTABLISHED) + libopm_do_connect(scanner, scan, conn); + } + } +} + + + + +/* check_closed + * + * Check for connections which have timed out or are + * closed. Connections timed out still need to be closed. + * + * Remove the connection from the list of connections, free + * the connection struct and free the list node. Then if this is + * the last connection of the scan, consider the scan completed and + * free the scan aswell (and callback that the scan ended). + * + * Parameters: + * scanner: Scanner to check on + * Return: + * None + */ + +static void libopm_check_closed(OPM_T *scanner) +{ + + time_t present; + OPM_NODE_T *node1, *node2, *next1, *next2; + int timeout; + + OPM_SCAN_T *scan; + OPM_CONNECTION_T *conn; + + if(LIST_SIZE(scanner->scans) == 0) + return; + + time(&present); + + timeout = *(int *) libopm_config(scanner->config, OPM_CONFIG_TIMEOUT); + + LIST_FOREACH_SAFE(node1, next1, scanner->scans->head) + { + scan = (OPM_SCAN_T *) node1->data; + LIST_FOREACH_SAFE(node2, next2, scan->connections->head) + { + + conn = (OPM_CONNECTION_T *) node2->data; + + if(conn->state == OPM_STATE_CLOSED) + { + if(conn->fd > 0) + close(conn->fd); + + scanner->fd_use--; + + libopm_list_remove(scan->connections, node2); + libopm_connection_free(conn); + libopm_node_free(node2); + continue; + } + + if(((present - conn->creation) >= timeout) && + conn->state != OPM_STATE_UNESTABLISHED) + { + + close(conn->fd); + scanner->fd_use--; + + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_TIMEOUT, 0); + + libopm_list_remove(scan->connections, node2); + libopm_connection_free(conn); + libopm_node_free(node2); + + continue; + } + } + + /* No more connections left in this scan, let the + client know the scan has ended, then remove the + scan from the scanner, and free it up */ + if(LIST_SIZE(scan->connections) == 0) + { + libopm_do_callback(scanner, scan->remote, OPM_CALLBACK_END, 0); + + libopm_list_remove(scanner->scans, node1); + libopm_scan_free(scan); + libopm_node_free(node1); + } + } +} + + + + +/* do_connect + * + * Call socket() and connect() to start a scan. + * + * Parametsr: + * scan: Scan struct containing the connection + * conn: Connection to establish + * Return: + * None + */ + +static void libopm_do_connect(OPM_T * scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + opm_sockaddr *bind_ip; + + struct sockaddr_in *addr; /* Outgoing host */ + struct sockaddr_in local_addr; /* For binding */ + + addr = (struct sockaddr_in *) &(scan->addr.sa4); /* Already have the IP in byte format from opm_scan */ + + addr->sin_family = AF_INET; + addr->sin_port = htons(conn->port); + + + bind_ip = (opm_sockaddr *) libopm_config(scanner->config, OPM_CONFIG_BIND_IP); + + conn->fd = socket(PF_INET, SOCK_STREAM, 0); + scanner->fd_use++; /* Increase file descriptor use */ + + if(conn->fd == -1) + { + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_NOFD); + conn->state = OPM_STATE_CLOSED; + return; + } + + if(bind_ip != NULL) + { + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.sin_addr.s_addr = bind_ip->sa4.sin_addr.s_addr; + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons(0); + + if(bind(conn->fd, (struct sockaddr *) &(local_addr), sizeof(local_addr)) == -1) + { + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_BIND); + conn->state = OPM_STATE_CLOSED; + return; + } + } + + /* Set socket non blocking */ + fcntl(conn->fd, F_SETFL, O_NONBLOCK); + + connect(conn->fd, (struct sockaddr *) addr, sizeof(*addr)); + + conn->state = OPM_STATE_ESTABLISHED; + time(&(conn->creation)); /* Stamp creation time, for timeout */ +} + + +/* check_poll + * + * Check sockets for ready read/write + * + * Parameters: + * scanner: Scanner to isolate check on + * Return: + * None + */ + +static void libopm_check_poll(OPM_T *scanner) +{ + OPM_NODE_T *node1, *node2; + OPM_SCAN_T *scan; + OPM_CONNECTION_T *conn; + + static unsigned int ufds_size; + static struct pollfd *ufds = NULL; + + unsigned int size, i; + size = 0; + + /* Grow pollfd array (ufds) as needed */ + if(ufds_size < (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT))) + { + MyFree(ufds); + ufds = MyMalloc((sizeof *ufds) * (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT))); + ufds_size = (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT)); + } + + if(LIST_SIZE(scanner->scans) == 0) + return; + + LIST_FOREACH(node1, scanner->scans->head) + { + scan = (OPM_SCAN_T *) node1->data; + LIST_FOREACH(node2, scan->connections->head) + { + if(size >= ufds_size) + break; + + conn = (OPM_CONNECTION_T *) node2->data; + + if(conn->state < OPM_STATE_ESTABLISHED || + conn->state == OPM_STATE_CLOSED) + continue; + + ufds[size].events = 0; + ufds[size].revents = 0; + ufds[size].fd = conn->fd; + + /* Check for HUNG UP. */ + ufds[size].events |= POLLHUP; + /* Check for INVALID FD */ + ufds[size].events |= POLLNVAL; + + switch(conn->state) + { + case OPM_STATE_ESTABLISHED: + ufds[size].events |= POLLOUT; + break; + case OPM_STATE_NEGSENT: + ufds[size].events |= POLLIN; + break; + } + size++; + } + + } + + switch (poll(ufds, size, 0)) + { + case -1: + /* error in select/poll */ + return; + case 0: + /* Nothing to do */ + return; + /* Pass pointer to connection to handler. */ + } + + LIST_FOREACH(node1, scanner->scans->head) + { + scan = (OPM_SCAN_T *) node1->data; + + LIST_FOREACH(node2, scan->connections->head) + { + conn = (OPM_CONNECTION_T *) node2->data; + + for(i = 0; i < size; i++) + { + if((ufds[i].fd == conn->fd) && (conn->state != OPM_STATE_CLOSED)) + { + if(ufds[i].revents & POLLIN) + libopm_do_readready(scanner, scan, conn); + if(ufds[i].revents & POLLOUT) + libopm_do_writeready(scanner, scan, conn); + if(ufds[i].revents & POLLHUP) + libopm_do_hup(scanner, scan, conn); + } + } + } + } +} + + + + +/* do_readready + * + * Remote connection is read ready, read the data into a buffer and check it against + * the target_string if neccessary + * + * Parameters: + * scanner: Scanner doing the scan + * scan: Specific scan + * conn: Specific connection in the scan + * + * Return: + * None + */ + +static void libopm_do_readready(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + + int max_read; + char c; + + /* If protocol has a specific read function, call that instead of + reading data from here. */ + if(conn->protocol->read_function) + { + conn->protocol->read_function(scanner, scan, conn); + return; + } + + max_read = *(int *) libopm_config(scanner->config, OPM_CONFIG_MAX_READ); + + while(1) + { + switch (read(conn->fd, &c, 1)) + { + case 0: + libopm_do_hup(scanner, scan, conn); + return; + + case -1: + if(errno != EAGAIN) + libopm_do_hup(scanner, scan, conn); + return; + + default: + + conn->bytes_read++; + + if(conn->bytes_read >= max_read) + { + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_MAX_READ); + conn->state = OPM_STATE_CLOSED; + return; + } + + if(c == '\0' || c == '\r') + continue; + + if(c == '\n') + { + conn->readbuf[conn->readlen] = '\0'; + conn->readlen = 0; + libopm_do_read(scanner, scan, conn); + + if(conn->state == OPM_STATE_CLOSED) + return; + + continue; + } + + if(conn->readlen < READBUFLEN) + { /* -1 to pad for null term */ + conn->readbuf[++(conn->readlen) - 1] = c; + } + } + } +} + + + + +/* do_read + * + * A line of data has been read from the socket, check it against + * target string. + * + * + * + * Parameters: + * scanner: Scanner doing the scan + * scan: Specific scan + * conn: Specific connection in the scan + * + * Return: + * None + */ + +static void libopm_do_read(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + OPM_LIST_T *list; + OPM_NODE_T *node; + char *target_string; + + /* Check readbuf against target strings */ + list = (OPM_LIST_T *) libopm_config(scanner->config, OPM_CONFIG_TARGET_STRING); + LIST_FOREACH(node, list->head) + { + target_string = (char *) node->data; + if(strstr(conn->readbuf, target_string)) + { + libopm_do_openproxy(scanner, scan, conn); + break; + } + } +} + + +/* do_openproxy + * + * An open proxy was found on connection conn. Cleanup the connection and + * call the appropriate callback to let the client know the proxy was found. + * + * Parameters: + * scanner: Scanner doing the scan + * scan: Specific scan + * conn: Specific connection in the scan + * + * Return: + * None + */ + +static void libopm_do_openproxy(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + OPM_REMOTE_T *remote; + + remote = scan->remote; + + /* Mark the connection for close */ + conn->state = OPM_STATE_CLOSED; + + /* Call client's open proxy callback */ + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_OPENPROXY, 0); +} + + + + +/* do_writeready + * + * Remote connection is write ready, call the specific protocol + * function for writing to this socket. + * + * Parameters: + * scanner: Scanner doing the scan + * scan: Specific scan + * conn: Specific connection in the scan + * + * Return: + * None + */ + +static void libopm_do_writeready(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + OPM_PROTOCOL_T *protocol; + + protocol = conn->protocol; + + /* Call write function for specific protocol */ + if(protocol->write_function) + protocol->write_function(scanner, scan, conn); + + /* Flag as NEGSENT so we don't have to send data again*/ + conn->state = OPM_STATE_NEGSENT; +} + + + + +/* do_hup + * + * Connection ended prematurely + * + * Parameters: + * scanner: Scanner doing the scan + * scan: Specific scan + * conn: Specific connection in the scan + * error: OPM_ERR_T containing the error type + * Return: + * None + */ + +static void libopm_do_hup(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + OPM_REMOTE_T *remote; + + remote = scan->remote; + + /* Mark the connection for close */ + conn->state = OPM_STATE_CLOSED; + + libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_NEGFAIL, 0); +} + + + + +/* do_callback + * + * Call callback + * + * Parameters: + * scanner: scanner remote is on + * remote: remote callback is for + * type: callback type + * var: optional var passed back (error codes, etc ) + * Return: + * None + */ + +static void libopm_do_callback(OPM_T *scanner, OPM_REMOTE_T *remote, int type, int var) +{ + /* Callback is out of range */ + if(type < 0 || type >= (CBLEN + 1)) + return; + + if(scanner->callbacks[type].func) + (scanner->callbacks[type].func) (scanner, remote, var, scanner->callbacks[type].data); +} + + + + +/* setup_remote + * + * Setup an OPM_REMOTE_T with information from an OPM_CONNECTION_T + * for callback + * + * Parameters: + * remote, conn + * + * Return: + * remote + */ + +static OPM_REMOTE_T *libopm_setup_remote(OPM_REMOTE_T *remote, OPM_CONNECTION_T *conn) +{ + remote->port = conn->port; + remote->bytes_read = conn->bytes_read; + remote->protocol = conn->protocol->type; + + return remote; +} diff --git a/libopm/libopm.h b/libopm/libopm.h new file mode 100644 index 0000000..2c0997d --- /dev/null +++ b/libopm/libopm.h @@ -0,0 +1,58 @@ +#ifndef LIBOPM_H +#define LIBOPM_H + +#include "config.h" +#include "inet.h" +#include "opm_common.h" +#include "opm.h" + +#define CBLEN 5 /* Number of callback functions */ +#define READBUFLEN 128 /* Size of conn->readbuf */ +#define SENDBUFLEN 512 /* Size of sendbuffer in proxy.c */ + +typedef struct _OPM_SCAN OPM_SCAN_T; +typedef struct _OPM_CONNECTION OPM_CONNECTION_T; +typedef struct _OPM_PROTOCOL_CONFIG OPM_PROTOCOL_CONFIG_T; +typedef struct _OPM_PROTOCOL OPM_PROTOCOL_T; + +/* Types of hard coded proxy READ/WRITE functions which are + setup in a table in libopm.c */ + +typedef int OPM_PROXYWRITE_T (OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +typedef int OPM_PROXYREAD_T (OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); + +struct _OPM_SCAN { + opm_sockaddr addr; /* Address in byte order of remote client */ + OPM_REMOTE_T *remote; /* Pointed to the OPM_REMOTE_T for this scan, passed by client */ + OPM_LIST_T *connections; /* List of individual connections of this scan (1 for each protocol) */ +}; + +struct _OPM_CONNECTION { + + OPM_PROTOCOL_T *protocol; /* Pointer to specific protocol this connection handles */ + unsigned short int port; /* Some protocols have multiple ports, eg. HTTP */ + + int fd; /* Allocated file descriptor, 0 if not yet allocated */ + unsigned short int bytes_read; /* Bytes read so far in this connection */ + char readbuf[READBUFLEN + 1]; /* 128 byte read buffer, anything over 128 is probably not of use */ + unsigned short int readlen; /* Length of readbuf */ + unsigned short int state; /* State of connection */ + time_t creation; /* When this connection was established */ +}; + +struct _OPM_PROTOCOL_CONFIG +{ + OPM_PROTOCOL_T *type; /* Protocol type */ + unsigned short int port; /* Port to connect on */ + +}; + +struct _OPM_PROTOCOL +{ + int type; /* Protocol type */ + + OPM_PROXYWRITE_T *write_function; /* Write function handler for this protocol */ + OPM_PROXYREAD_T *read_function; /* Read function handler for this protocol */ +}; + +#endif /* LIBOPM_H */ diff --git a/libopm/list.c b/libopm/list.c new file mode 100644 index 0000000..bba241a --- /dev/null +++ b/libopm/list.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2002-2003 Erik Fears + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + * + */ + +#include "setup.h" + +#include "opm_common.h" +#include "list.h" +#include "malloc.h" +#include "opm.h" + +RCSID("$Id: list.c,v 1.11 2003/06/27 18:51:02 strtok Exp $"); + +OPM_NODE_T *libopm_node_create(void *data) +{ + OPM_NODE_T *node = MyMalloc(sizeof *node); + node->next = NULL; + node->prev = NULL; + node->data = (void *) data; + + return node; +} + +OPM_LIST_T *libopm_list_create() +{ + OPM_LIST_T *list = MyMalloc(sizeof *list); + + list->head = NULL; + list->tail = NULL; + + list->elements = 0; + + return list; +} + +OPM_NODE_T *libopm_list_add(OPM_LIST_T *list, OPM_NODE_T *node) +{ + + if(list == NULL || node == NULL) + return NULL; + + if(list->tail == NULL) + { + list->head = node; + list->tail = node; + + node->next = NULL; + node->prev = NULL; + } + else + { + node->prev = list->tail; + list->tail->next = node; + list->tail = node; + node->next = NULL; + } + + list->elements++; + return node; +} + +OPM_NODE_T *libopm_list_remove(OPM_LIST_T *list, OPM_NODE_T *node) +{ + + if(list == NULL || node == NULL) + return NULL; + + if(node == list->head) + { + list->head = node->next; + + if(node->next) + node->next->prev = NULL; + else + list->tail = NULL; + } + else if(node == list->tail) + { + list->tail = list->tail->prev; + list->tail->next = NULL; + } + else + { + node->prev->next = node->next; + node->next->prev = node->prev; + } + + list->elements--; + return node; +} + +void libopm_list_free(OPM_LIST_T *list) +{ + MyFree(list); +} + +void libopm_node_free(OPM_NODE_T *node) +{ + MyFree(node); +} diff --git a/libopm/list.h b/libopm/list.h new file mode 100644 index 0000000..26bd0f4 --- /dev/null +++ b/libopm/list.h @@ -0,0 +1,42 @@ +#ifndef LIST_H +#define LIST_H + + +/* Copyright (C) 2002 by the past and present ircd coders, and others. + * Adapted from Hybrid7 DLINK macros + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + */ + +#define LIST_FOREACH(pos, head) for (pos = (head); pos != NULL; pos = pos->next) +#define LIST_FOREACH_SAFE(pos, n, head) for (pos = (head), n = pos ? pos->next : NULL; pos != NULL; pos = n, n = pos ? pos->next : NULL) +#define LIST_FOREACH_PREV(pos, head) for (pos = (head); pos != NULL; pos = pos->prev) +#define LIST_SIZE(list) list->elements +/* End Copyright */ + +OPM_NODE_T *libopm_node_create(void *); +OPM_LIST_T *libopm_list_create(void); + +OPM_NODE_T *libopm_list_add(OPM_LIST_T *, OPM_NODE_T *); +OPM_NODE_T *libopm_list_remove(OPM_LIST_T *, OPM_NODE_T *); + +void libopm_list_free(OPM_LIST_T *); +void libopm_node_free(OPM_NODE_T *); + +#endif /* LIST_H */ diff --git a/libopm/make b/libopm/make new file mode 100755 index 0000000..7c63e8e --- /dev/null +++ b/libopm/make @@ -0,0 +1,10 @@ +#!/bin/sh +gcc -g -o libopm.o -c libopm.c +gcc -g -o malloc.o -c malloc.c +gcc -g -o list.o -c list.c +gcc -g -o config.o -c config.c +gcc -g -o inet.o -c inet.c +gcc -g -o proxy.o -c proxy.c +ar rcs libopm.a libopm.o malloc.o list.o config.o inet.o proxy.o +gcc -g test.c -o test -L. -lopm + diff --git a/libopm/malloc.c b/libopm/malloc.c new file mode 100644 index 0000000..25763d7 --- /dev/null +++ b/libopm/malloc.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2002 Erik Fears + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + * + */ + +#include +#include "setup.h" + +#include "malloc.h" +#include "opm.h" + +RCSID("$Id: malloc.c,v 1.10 2002/11/07 20:19:06 strtok Exp $"); + + +/* MyMalloc + * + * A wrapper function for malloc(), for catching memory issues + * and error handling. + * + * Parameters + * bytes: amount in bytes to allocate + * + * Return: + * Pointer to allocated memory + */ + +void *libopm_MyMalloc(size_t bytes) +{ + void *ret = calloc(1, bytes); + + assert(ret); + + return ret; +} + + + +/* MyFree + * + * Free memory allocated with MyMalloc + * + * Parameters: + * var: pointer to memory to free + * + * Return: + * None + */ + +void libopm_MyFree(void **var) +{ + if(*var != NULL) + free(*var); + *var = NULL; +} diff --git a/libopm/malloc.h b/libopm/malloc.h new file mode 100644 index 0000000..54c7709 --- /dev/null +++ b/libopm/malloc.h @@ -0,0 +1,16 @@ +#ifndef MALLOC_H +#define MALLOC_H + +#include "setup.h" + +#ifdef STDC_HEADERS +# include +#endif + +#define MyMalloc(SIZE) libopm_MyMalloc(SIZE) +#define MyFree(X) libopm_MyFree((void **) &X) + +void *libopm_MyMalloc(size_t bytes); +void libopm_MyFree(void **var); + +#endif /* MALLOC_H */ diff --git a/libopm/opm.h b/libopm/opm.h new file mode 100644 index 0000000..f333175 --- /dev/null +++ b/libopm/opm.h @@ -0,0 +1,80 @@ +/* vim: set shiftwidth=3 softtabstop=3 expandtab: */ + +/** \file opm.h + * \brief Main header for libopm. + * \author Erik Fears + * \version $Id: opm.h,v 1.26 2003/06/20 04:18:44 andy Exp $ + */ + +#ifndef OPM_H +#define OPM_H + +#include "opm_common.h" + +/* Stuff to shut up warnings about rcsid being unused. */ +#define USE_VAR(var) static char sizeof##var = sizeof(sizeof##var) + sizeof(var) +/* RCS tag. */ +#define RCSID(x) static char rcsid[] = x; USE_VAR(rcsid); + + +typedef struct _OPM_CONFIG OPM_CONFIG_T; +typedef struct _OPM OPM_T; +typedef struct _OPM_REMOTE OPM_REMOTE_T; +typedef struct _OPM_CALLBACK OPM_CALLBACK_T; + +typedef int OPM_ERR_T; + +typedef void OPM_CALLBACK_FUNC (OPM_T *, OPM_REMOTE_T *, int, void *); + +struct _OPM_CALLBACK { + OPM_CALLBACK_FUNC *func; + void *data; +}; + +struct _OPM_CONFIG { + void **vars; +}; + +struct _OPM { + OPM_CONFIG_T *config; /* Individual scanner configuration */ + OPM_LIST_T *queue; /* List of scans in the queue (not yet established) */ + OPM_LIST_T *scans; /* List of scans (each scan containing a list of connections) */ + OPM_LIST_T *protocols; /* List of protocols this scanner handles */ + unsigned int fd_use; /* Number of file descriptors in use */ + + OPM_CALLBACK_T *callbacks; /* Scanner wide callbacks */ +}; + +struct _OPM_REMOTE { + + char *ip; /* Readable IP address */ + + unsigned short int port; /* Port passed back on certain callbacks */ + unsigned short int protocol; /* Protocol passed back on certain callbacks */ + unsigned short int bytes_read; /* Bytes read passed back on certain callbacks */ + + OPM_LIST_T *protocols; /* Remote specific protocols */ + + void *data; /* Arbitrary data that the client can point to for any purpose*/ +}; + +OPM_T *opm_create(void); +void opm_free(OPM_T *); + +OPM_REMOTE_T *opm_remote_create(const char *); +void opm_remote_free(OPM_REMOTE_T *); + +OPM_ERR_T opm_config(OPM_T *, int, void *); +OPM_ERR_T opm_scan(OPM_T *, OPM_REMOTE_T *); +void opm_end(OPM_T *, OPM_REMOTE_T *); +void opm_endscan(OPM_T *, OPM_REMOTE_T *); + +OPM_ERR_T opm_addtype(OPM_T *, int, unsigned short int); +OPM_ERR_T opm_remote_addtype(OPM_REMOTE_T *, int, unsigned short int); +OPM_ERR_T opm_callback(OPM_T *, int, OPM_CALLBACK_FUNC *, void *); + +void opm_cycle(OPM_T *); + +size_t opm_active(OPM_T *); + +#endif /* OPM_H */ diff --git a/libopm/opm_common.h b/libopm/opm_common.h new file mode 100644 index 0000000..18aeda8 --- /dev/null +++ b/libopm/opm_common.h @@ -0,0 +1,24 @@ +#ifndef OPM_COMMON_H +#define OPM_COMMON_H + +typedef struct _libopm_node OPM_NODE_T; +typedef struct _libopm_list OPM_LIST_T; + + +struct _libopm_list { + + struct _libopm_node *head; + struct _libopm_node *tail; + + int elements; +}; + +struct _libopm_node { + + struct _libopm_node *next; + struct _libopm_node *prev; + + void *data; +}; + +#endif /* OPM_COMMON_H */ diff --git a/libopm/opm_error.h b/libopm/opm_error.h new file mode 100644 index 0000000..9c11388 --- /dev/null +++ b/libopm/opm_error.h @@ -0,0 +1,26 @@ +#ifndef LIBOPM_ERROR_H +#define LIBOPM_ERROR_H + +/* Success */ +#define OPM_SUCCESS 1 + +/* Configuration Errors */ +#define OPM_ERR_BADKEY 2 /* Unknown or bad key value */ +#define OPM_ERR_BADVALUE 3 /* Bad value matching key */ +#define OPM_ERR_BADPROTOCOL 4 /* Unknown protocol in config */ + +/* Read Errors */ +#define OPM_ERR_MAX_READ 5 /* Socket reached MAX_READ */ + +/* Callback Registration Errors */ +#define OPM_ERR_CBNOTFOUND 6 /* Callback is out of range */ + +/* opm_scan errors */ +#define OPM_ERR_BADADDR 7 /* IP in remote struct is bad */ +#define OPM_ERR_NOPROTOCOLS 8 /* No protocols to scan! */ + +/* bind/connect errors */ +#define OPM_ERR_BIND 9 /* Error binding to BIND_IP */ +#define OPM_ERR_NOFD 10/* Unable to allocate file descriptor */ + +#endif /* LIBOPM_ERROR_H */ diff --git a/libopm/opm_types.h b/libopm/opm_types.h new file mode 100644 index 0000000..3c17384 --- /dev/null +++ b/libopm/opm_types.h @@ -0,0 +1,44 @@ +#ifndef OPM_TYPES_H +#define OPM_TYPES_H + +/* Configuration Directives */ +#define OPM_CONFIG_FD_LIMIT 0 +#define OPM_CONFIG_BIND_IP 1 +#define OPM_CONFIG_DNSBL_HOST 2 +#define OPM_CONFIG_TARGET_STRING 3 +#define OPM_CONFIG_SCAN_IP 4 +#define OPM_CONFIG_SCAN_PORT 5 +#define OPM_CONFIG_MAX_READ 6 +#define OPM_CONFIG_TIMEOUT 7 + +/* Configuration Variable Types */ +#define OPM_TYPE_INT 1 +#define OPM_TYPE_STRING 2 +#define OPM_TYPE_ADDRESS 3 +#define OPM_TYPE_STRINGLIST 4 + +/* Protocols */ +#define OPM_TYPE_HTTP 1 +#define OPM_TYPE_SOCKS4 2 +#define OPM_TYPE_SOCKS5 3 +#define OPM_TYPE_WINGATE 4 +#define OPM_TYPE_ROUTER 5 +#define OPM_TYPE_HTTPPOST 6 + +/* States */ +#define OPM_STATE_UNESTABLISHED 1 +#define OPM_STATE_ESTABLISHED 2 +#define OPM_STATE_NEGSENT 3 +#define OPM_STATE_CLOSED 4 + + +/* Callbacks -- If more callback types are added, + CBLEN will need to be changed in libopm.h accordingly */ + +#define OPM_CALLBACK_OPENPROXY 0 /* An open proxy has been found REMOTE/SCANNER */ +#define OPM_CALLBACK_NEGFAIL 1 /* Negotiation to a proxy has failed REMOTE/SCANNER */ +#define OPM_CALLBACK_END 2 /* A scan has ended REMOTE/SCANNER */ +#define OPM_CALLBACK_ERROR 3 /* An unrecoverable error has occured */ +#define OPM_CALLBACK_TIMEOUT 4 /* Specific scan (protocol) on host has timed out */ + +#endif /* OPM_TYPES_H */ diff --git a/libopm/proxy.c b/libopm/proxy.c new file mode 100644 index 0000000..733ccf3 --- /dev/null +++ b/libopm/proxy.c @@ -0,0 +1,243 @@ +/* vim: set shiftwidth=3 softtabstop=3 expandtab: */ + +/* Copyright (C) 2002 Erik Fears + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + * + */ + +#include "setup.h" + +#include + +#ifdef STDC_HEADERS +# include +# include +#endif + +#include "inet.h" +#include "compat.h" +#include "config.h" +#include "proxy.h" +#include "opm_common.h" +#include "opm_types.h" +#include "opm_error.h" +#include "libopm.h" + +RCSID("$Id: proxy.c,v 1.16 2003/06/21 04:27:32 andy Exp $"); + +static char SENDBUF[SENDBUFLEN + 1]; + +int libopm_proxy_http_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + USE_VAR(scan); + + snprintf(SENDBUF, SENDBUFLEN, "CONNECT %s:%d HTTP/1.0\r\n\r\n", + (char *) libopm_config(scanner->config, OPM_CONFIG_SCAN_IP), + *(int *) libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT)); + + if(send(conn->fd, SENDBUF, strlen(SENDBUF), 0) == -1) + return 0; /* Return error code ? */ + + return OPM_SUCCESS; +} + + +/* + * CONNECT request byte order for socks4 + * + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * | VN | CD | DSTPORT | DSTIP | USERID |NULL| + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * # of bytes: 1 1 2 4 variable 1 + * + * VN = Version, CD = Command Code (1 is connect request) + */ + +int libopm_proxy_socks4_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + struct in_addr addr; + unsigned long laddr; + int len, scan_port; + char *scan_ip; + USE_VAR(scan); + + scan_ip = (char *) libopm_config(scanner->config, OPM_CONFIG_SCAN_IP); + scan_port = *(int *) libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT); + + laddr = htonl(addr.s_addr); + + len = snprintf(SENDBUF, SENDBUFLEN, "%c%c%c%c%c%c%c%c%c", 4, 1, + (((unsigned short) scan_port) >> 8) & 0xFF, + (((unsigned short) scan_port) & 0xFF), + (char) (laddr >> 24) & 0xFF, (char) (laddr >> 16) & 0xFF, + (char) (laddr >> 8) & 0xFF, (char) laddr & 0xFF, 0); + + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + return OPM_SUCCESS; +} + + +/* + * Send version authentication selection message to socks5 + * + * +----+----------+----------+ + * |VER | NMETHODS | METHODS | + * +----+----------+----------+ + * | 1 | 1 | 1 to 255 | + * +----+----------+----------+ + * + * VER always contains 5, for socks version 5 + * Method 0 is 'No authentication required' + * + * + * + * The SOCKS request is formed as follows: + * + * +----+-----+-------+------+----------+----------+ + * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + * +----+-----+-------+------+----------+----------+ + * | 1 | 1 | X'00' | 1 | Variable | 2 | + * +----+-----+-------+------+----------+----------+ + * + * + * o VER protocol version: X'05' + * o CMD + * o CONNECT X'01' + * o BIND X'02' + * o UDP ASSOCIATE X'03' + * o RSV RESERVED + * o ATYP address type of following address + * o IP V4 address: X'01' + * o DOMAINNAME: X'03' + * o IP V6 address: X'04' + * o DST.ADDR desired destination address + * o DST.PORT desired destination port in network octet + * order + * + * + */ + +int libopm_proxy_socks5_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + struct in_addr addr; + unsigned long laddr; + int len, scan_port; + char *scan_ip; + USE_VAR(scan); + + scan_ip = (char *) libopm_config(scanner->config, OPM_CONFIG_SCAN_IP); + scan_port = *(int *) libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT); + + laddr = htonl(addr.s_addr); + + /* Form authentication string */ + /* Version 5, 1 number of methods, 0 method (no auth). */ + len = snprintf(SENDBUF, SENDBUFLEN, "%c%c%c", 5, 1, 0); + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + /* Form request string */ + + /* Will need to write ipv6 support here in future + * as socks5 is ipv6 compatible + */ + len = snprintf(SENDBUF, SENDBUFLEN, "%c%c%c%c%c%c%c%c%c%c", 5, 1, 0, 1, + (char) (laddr >> 24) & 0xFF, (char) (laddr >> 16) & 0xFF, + (char) (laddr >> 8) & 0xFF, (char) laddr & 0xFF, + (((unsigned short) scan_port) >> 8) & 0xFF, + (((unsigned short) scan_port) & 0xFF)); + + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + return OPM_SUCCESS; +} + +/* + * Open wingates require no authentication, they will send a prompt when + * connect. + */ + +int libopm_proxy_wingate_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + int scan_port, len; + char *scan_ip; + USE_VAR(scan); + + scan_ip = (char *) libopm_config(scanner->config, OPM_CONFIG_SCAN_IP); + scan_port = *(int *) libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT); + + len = snprintf(SENDBUF, SENDBUFLEN, "%s:%d\r\n", scan_ip, scan_port); + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + return OPM_SUCCESS; +} + + +/* + * Cisco scanning + * + * Some cisco routers have 'cisco' set as password which allow open telnet + * relay. Attempt to connect using cisco as a password, then give command for + * telnet to the scanip/scanport + */ + +int libopm_proxy_router_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + int len, scan_port; + char *scan_ip; + USE_VAR(scan); + + scan_ip = (char *) libopm_config(scanner->config, OPM_CONFIG_SCAN_IP); + scan_port = *(int *) libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT); + + len = snprintf(SENDBUF, SENDBUFLEN, "cisco\r\n"); + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + len = snprintf(SENDBUF, SENDBUFLEN, "telnet %s %d\r\n", scan_ip, scan_port); + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + return OPM_SUCCESS; +} + + +/* + * HTTP POST Scanning + * + */ + +int libopm_proxy_httppost_write(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) +{ + int len, scan_port; + char *scan_ip; + USE_VAR(scan); + + scan_ip = (char *) libopm_config(scanner->config, OPM_CONFIG_SCAN_IP); + scan_port = *(int *) libopm_config(scanner->config, OPM_CONFIG_SCAN_PORT); + + len = snprintf(SENDBUF, SENDBUFLEN, "POST http://%s:%d/ HTTP/1.0\r\n" + "Content-type: text/plain\r\n" + "Content-length: 5\r\n\r\n" + "quit\r\n\r\n", + scan_ip, scan_port); + + send(conn->fd, SENDBUF, (unsigned int)len, 0); + + return OPM_SUCCESS; +} diff --git a/libopm/proxy.h b/libopm/proxy.h new file mode 100644 index 0000000..ef21d5e --- /dev/null +++ b/libopm/proxy.h @@ -0,0 +1,14 @@ +#ifndef PROXY_H +#define PROXY_H + +#include "libopm.h" + +int libopm_proxy_http_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +int libopm_proxy_socks4_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +int libopm_proxy_socks5_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +int libopm_proxy_wingate_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +int libopm_proxy_router_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); +int libopm_proxy_httppost_write(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); + + +#endif /* PROXY_H */ diff --git a/libopm/setup.h.in b/libopm/setup.h.in new file mode 100644 index 0000000..070175f --- /dev/null +++ b/libopm/setup.h.in @@ -0,0 +1,101 @@ +/* src/setup.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `gethostbyname' function. */ +#undef HAVE_GETHOSTBYNAME + +/* Define to 1 if you have the `gethostbyname2' function. */ +#undef HAVE_GETHOSTBYNAME2 + +/* Define to 1 if you have the `inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* "enable replacement (v)snprintf if system (v)snprintf is broken" */ +#undef PREFER_PORTABLE_SNPRINTF + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `unsigned' if does not define. */ +#undef size_t diff --git a/libopm/snprintf.c b/libopm/snprintf.c new file mode 100644 index 0000000..9571787 --- /dev/null +++ b/libopm/snprintf.c @@ -0,0 +1,1031 @@ +/* + * NOTE: The author's site (http://www.ijs.si/software/snprintf/) states that + * this software may be used under the terms of the GNU General Public License + * and so that is what we shall be doing. This is the same license as the rest + * of libopm and BOPM. + * + * snprintf.c - a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf only supports the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is supported for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are supported. + * NOTE: + * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + * length modifier 'll' is recognized but treated the same as 'l', + * which may cause argument value truncation! Defining + * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + * handles length modifier 'll'. long long int is a language extension + * which may not be portable. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (data + * types wint_t and wchar_t are not supported). + * + * The following common synonyms for conversion characters are supported: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are supported + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT supported: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + * REVISION HISTORY + * 1999-04 V0.9 Mark Martinec + * - initial version, some modifications after comparing printf + * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, + * and checking how Perl handles sprintf (differently!); + * 1999-04-09 V1.0 Mark Martinec + * - added main test program, fixed remaining inconsistencies, + * added optional (long long int) support; + * 1999-04-12 V1.1 Mark Martinec + * - support the 'p' conversion (pointer to void); + * - if a string precision is specified + * make sure the string beyond the specified precision + * will not be referenced (e.g. by strlen); + * 1999-04-13 V1.2 Mark Martinec + * - support synonyms %D=%ld, %U=%lu, %O=%lo; + * - speed up the case of long format string with few conversions; + * 1999-06-30 V1.3 Mark Martinec + * - fixed runaway loop (eventually crashing when str_l wraps + * beyond 2^31) while copying format string without + * conversion specifiers to a buffer that is too short + * (thanks to Edwin Young for + * spotting the problem); + * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) + * to snprintf.h + * 2000-02-14 V2.0 (never released) Mark Martinec + * - relaxed license terms: The Artistic License now applies. + * You may still apply the GNU GENERAL PUBLIC LICENSE + * as was distributed with previous versions, if you prefer; + * - changed REVISION HISTORY dates to use ISO 8601 date format; + * - added vsnprintf (patch also independently proposed by + * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + * 2000-06-27 V2.1 Mark Martinec + * - removed POSIX check for str_m<1; value 0 for str_m is + * allowed by ISO C99 (and GNU C library 2.1) - (pointed out + * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). + * Besides relaxed license this change in standards adherence + * is the main reason to bump up the major version number; + * - added nonstandard routines asnprintf, vasnprintf, asprintf, + * vasprintf that dynamically allocate storage for the + * resulting string; these routines are not compiled by default, + * see comments where NEED_V?ASN?PRINTF macros are defined; + * - autoconf contributed by Caolan McNamara + * 2000-10-06 V2.2 Mark Martinec + * - BUG FIX: the %c conversion used a temporary variable + * that was no longer in scope when referenced, + * possibly causing incorrect resulting character; + * - BUG FIX: make precision and minimal field width unsigned + * to handle huge values (2^31 <= n < 2^32) correctly; + * also be more careful in the use of signed/unsigned/size_t + * internal variables - probably more careful than many + * vendor implementations, but there may still be a case + * where huge values of str_m, precision or minimal field + * could cause incorrect behaviour; + * - use separate variables for signed/unsigned arguments, + * and for short/int, long, and long long argument lengths + * to avoid possible incompatibilities on certain + * computer architectures. Also use separate variable + * arg_sign to hold sign of a numeric argument, + * to make code more transparent; + * - some fiddling with zero padding and "0x" to make it + * Linux compatible; + * - systematically use macros fast_memcpy and fast_memset + * instead of case-by-case hand optimization; determine some + * breakeven string lengths for different architectures; + * - terminology change: 'format' -> 'conversion specifier', + * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', + * 'alternative form' -> 'alternate form', + * 'data type modifier' -> 'length modifier'; + * - several comments rephrased and new ones added; + * - make compiler not complain about 'credits' defined but + * not used; + */ + +#include "setup.h" + +/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. + * + * If HAVE_SNPRINTF is defined this module will not produce code for + * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, + * causing this portable version of snprintf to be called portable_snprintf + * (and portable_vsnprintf). + */ +/* #define HAVE_SNPRINTF */ + +/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and + * vsnprintf but you would prefer to use the portable routine(s) instead. + * In this case the portable routine is declared as portable_snprintf + * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') + * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . + * Defining this macro is only useful if HAVE_SNPRINTF is also defined, + * but does does no harm if defined nevertheless. + */ +/* #define PREFER_PORTABLE_SNPRINTF */ + +/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support + * data type (long long int) and length modifier 'll' (e.g. %lld). + * If undefined, 'll' is recognized but treated as a single 'l'. + * + * If the system's sprintf does not handle 'll' + * the SNPRINTF_LONGLONG_SUPPORT must not be defined! + * + * This is off by default as (long long int) is a language extension. + */ +/* #define SNPRINTF_LONGLONG_SUPPORT */ + +/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. + * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, + * otherwise both snprintf and vsnprintf routines will be defined + * and snprintf will be a simple wrapper around vsnprintf, at the expense + * of an extra procedure call. + */ +/* #define NEED_SNPRINTF_ONLY */ + +/* Define NEED_V?ASN?PRINTF macros if you need library extension + * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, + * and your system library does not provide them. They are all small + * wrapper routines around portable_vsnprintf. Defining any of the four + * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY + * and turns on PREFER_PORTABLE_SNPRINTF. + * + * Watch for name conflicts with the system library if these routines + * are already present there. + * + * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as + * specified by C99, to be able to traverse the same list of arguments twice. + * I don't know of any other standard and portable way of achieving the same. + * With some versions of gcc you may use __va_copy(). You might even get away + * with "ap2 = ap", in this case you must not call va_end(ap2) ! + * #define va_copy(ap2,ap) ap2 = ap + */ +/* #define NEED_ASPRINTF */ +/* #define NEED_ASNPRINTF */ +/* #define NEED_VASPRINTF */ +/* #define NEED_VASNPRINTF */ + + +/* Define the following macros if desired: + * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, + * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, + * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, + * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, + * + * - For portable applications it is best not to rely on peculiarities + * of a given implementation so it may be best not to define any + * of the macros that select compatibility and to avoid features + * that vary among the systems. + * + * - Selecting compatibility with more than one operating system + * is not strictly forbidden but is not recommended. + * + * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . + * + * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is + * documented in a sprintf man page on a given operating system + * and actually adhered to by the system's sprintf (but not on + * most other operating systems). It may also refer to and enable + * a behaviour that is declared 'undefined' or 'implementation specific' + * in the man page but a given implementation behaves predictably + * in a certain way. + * + * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf + * that contradicts the sprintf man page on the same operating system. + * + * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE + * conditionals take into account all idiosyncrasies of a particular + * implementation, there may be other incompatibilities. + */ + + + +/* ============================================= */ +/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ +/* ============================================= */ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names libopm_snprintf and libopm_vsnprintf */ +#define portable_snprintf libopm_snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf libopm_vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif diff --git a/libopm/snprintf.h b/libopm/snprintf.h new file mode 100644 index 0000000..69fffe3 --- /dev/null +++ b/libopm/snprintf.h @@ -0,0 +1,34 @@ +#ifndef _PORTABLE_SNPRINTF_H_ +#define _PORTABLE_SNPRINTF_H_ +#include +#include + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#ifdef HAVE_SNPRINTF +#include +#else +extern int libopm_snprintf(char *, size_t, const char *, /*args*/ ...); +extern int libopm_vsnprintf(char *, size_t, const char *, va_list); +#undef snprintf +#undef vsnprintf +#define snprintf libopm_snprintf +#define vsnprintf libopm_vsnprintf +#endif + +#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF) +extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#undef snprintf +#undef vsnprintf +#define snprintf portable_snprintf +#define vsnprintf portable_vsnprintf +#endif + +extern int asprintf (char **ptr, const char *fmt, /*args*/ ...); +extern int vasprintf (char **ptr, const char *fmt, va_list ap); +extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap); + +#endif diff --git a/libopm/test.c b/libopm/test.c new file mode 100644 index 0000000..e5960d4 --- /dev/null +++ b/libopm/test.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2002 Erik Fears + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to + * + * The Free Software Foundation, Inc. + * 59 Temple Place - Suite 330 + * Boston, MA 02111-1307, USA. + * + * + */ + +#include "setup.h" + +#include +#include "opm.h" +#include "opm_error.h" +#include "opm_types.h" +#include "compat.h" +#ifdef HAVE_UNISTD_H +# include +#endif + +RCSID("$Id: test.c,v 1.36 2003/06/20 04:55:14 andy Exp $"); + +#define ARRAY_SIZEOF(x) (sizeof(x) / sizeof((x)[0])) + +void open_proxy(OPM_T *, OPM_REMOTE_T *, int, void *); +void negotiation_failed(OPM_T *, OPM_REMOTE_T *, int, void *); +void timeout(OPM_T *, OPM_REMOTE_T *, int, void *); +void end(OPM_T *, OPM_REMOTE_T *, int, void *); +void handle_error(OPM_T *, OPM_REMOTE_T *, int, void *); + +int complete = 0; + +int main(int argc, char **argv) +{ + OPM_ERR_T err; + int fdlimit = 1024; + int scan_port = 6667; + int max_read = 4096; + int scantimeout = 10; + unsigned int i, s; + + unsigned short http_ports[] = { + 8000, 8080, 3128, 80 + }; + + unsigned short wingate_ports[] = { + 23 + }; + + unsigned short router_ports[] = { + 23 + }; + + unsigned short socks4_ports[] = { + 1080 + }; + + unsigned short socks5_ports[] = { + 1080 + }; + + unsigned short httppost_ports[] = { + 80, 8090, 3128 + }; + + OPM_T *scanner; + OPM_REMOTE_T *remote; + + scanner = opm_create(); + + if(argc >= 2) + remote = opm_remote_create(argv[1]); + else + remote = opm_remote_create("127.0.0.1"); + + /* Setup callbacks */ + opm_callback(scanner, OPM_CALLBACK_OPENPROXY, &open_proxy, NULL); + opm_callback(scanner, OPM_CALLBACK_NEGFAIL, &negotiation_failed, NULL); + opm_callback(scanner, OPM_CALLBACK_TIMEOUT, &timeout, NULL); + opm_callback(scanner, OPM_CALLBACK_END, &end, NULL); + opm_callback(scanner, OPM_CALLBACK_ERROR, &handle_error, NULL); + + + /* Setup the scanner configuration */ + opm_config(scanner, OPM_CONFIG_FD_LIMIT, &fdlimit); + opm_config(scanner, OPM_CONFIG_SCAN_IP, "216.175.104.202"); + opm_config(scanner, OPM_CONFIG_SCAN_PORT, &scan_port); + opm_config(scanner, OPM_CONFIG_TARGET_STRING, "*** Looking up your hostname..."); + opm_config(scanner, OPM_CONFIG_TARGET_STRING, "ERROR :Trying to reconnect too fast."); + opm_config(scanner, OPM_CONFIG_TIMEOUT, &scantimeout); + opm_config(scanner, OPM_CONFIG_MAX_READ, &max_read); + + /* Setup the protocol configuration */ + for (s = ARRAY_SIZEOF(http_ports), i = 0; i < s; i++) { + opm_addtype(scanner, OPM_TYPE_HTTP, http_ports[i]); + } + + for (s = ARRAY_SIZEOF(wingate_ports), i = 0; i < s; i++) { + opm_addtype(scanner, OPM_TYPE_WINGATE, wingate_ports[i]); + } + + for (s = ARRAY_SIZEOF(router_ports), i = 0; i < s; i++) { + opm_addtype(scanner, OPM_TYPE_ROUTER, router_ports[i]); + } + + for (s = ARRAY_SIZEOF(socks4_ports), i = 0; i < s; i++) { + opm_addtype(scanner, OPM_TYPE_SOCKS4, socks4_ports[i]); + } + + for (s = ARRAY_SIZEOF(socks5_ports), i = 0; i < s; i++) { + opm_addtype(scanner, OPM_TYPE_SOCKS5, socks5_ports[i]); + } + + for (s = ARRAY_SIZEOF(httppost_ports), i = 0; i < s; i++) { + opm_addtype(scanner, OPM_TYPE_HTTPPOST, httppost_ports[i]); + } + + /* Remote structs can also have their own extended protocol configurations. For instance + if the target hostname contains strings such as 'proxy' or 'www', extended ports could + be scanned. */ + opm_remote_addtype(remote, OPM_TYPE_HTTP, 8001); + opm_remote_addtype(remote, OPM_TYPE_HTTP, 8002); + + switch(err = opm_scan(scanner, remote)) + { + case OPM_SUCCESS: + break; + case OPM_ERR_BADADDR: + printf("Bad address\n"); + opm_free(scanner); + opm_remote_free(remote); + return 0; + default: + printf("Unknown Error %d\n", err); + return 0; + } + + + while(!complete) + opm_cycle(scanner); + + opm_free(scanner); + + return 0; +} + +void open_proxy(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, + void *data) +{ + USE_VAR(notused); + USE_VAR(data); + + printf("Open proxy on %s:%d [%d bytes read]\n", remote->ip, + remote->port, remote->bytes_read); + opm_end(scanner, remote); +} + +void negotiation_failed(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, + void *data) +{ + USE_VAR(scanner); + USE_VAR(notused); + USE_VAR(data); + + printf("Negotiation on %s:%d failed [%d bytes read]\n", remote->ip, + remote->port, remote->bytes_read); +} + +void timeout(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, void *data) +{ + USE_VAR(scanner); + USE_VAR(notused); + USE_VAR(data); + + printf("Negotiation timed out on %s:%d\n", remote->ip, remote->port); +} + +void end(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, void *data) +{ + USE_VAR(scanner); + USE_VAR(notused); + USE_VAR(data); + + printf("Scan on %s has ended\n", remote->ip); + opm_remote_free(remote); + complete = 1; +} + +void handle_error(OPM_T *scanner, OPM_REMOTE_T *remote, int err, void *data) +{ + USE_VAR(scanner); + USE_VAR(data); + + switch(err) + { + case OPM_ERR_MAX_READ: + printf("Reached MAX READ on %s:%d\n", remote->ip, remote->port); + break; + case OPM_ERR_BIND: + printf("Unable to bind for %s:%d\n", remote->ip, remote->port); + break; + case OPM_ERR_NOFD: + printf("Unable to allocate file descriptor for %s:%d\n", + remote->ip, remote->port); + break; + default: + printf("Unknown error on %s:%d, err = %d\n", remote->ip, + remote->port, err); + } +}