diff options
Diffstat (limited to 'lib/libpcap/libpcap/rpcapd')
24 files changed, 6504 insertions, 0 deletions
diff --git a/lib/libpcap/libpcap/rpcapd/CMakeLists.txt b/lib/libpcap/libpcap/rpcapd/CMakeLists.txt new file mode 100644 index 0000000..1821c85 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/CMakeLists.txt @@ -0,0 +1,163 @@ +if(UNIX) + check_function_exists(crypt HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + if(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + set(HAVE_CRYPT TRUE) + else(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + check_library_exists(crypt crypt "" HAVE_CRYPT_IN_LIBCRYPT) + if(HAVE_CRYPT_IN_LIBCRYPT) + set(RPCAPD_LINK_LIBRARIES ${RPCAPD_LINK_LIBRARIES} crypt) + set(HAVE_CRYPT TRUE) + else(HAVE_CRYPT_IN_LIBCRYPT) + message(WARNING "crypt() not found. Won't be able to build rpcapd.") + endif(HAVE_CRYPT_IN_LIBCRYPT) + endif(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) +endif(UNIX) + +# +# On UN*X, we need pthreads and crypt(). +# +if(WIN32 OR ((CMAKE_USE_PTHREADS_INIT OR PTHREADS_FOUND) AND HAVE_CRYPT)) + if(UNIX) + # + # Do we have getspnam()? + # + check_function_exists(getspnam HAVE_GETSPNAM) + + # + # Find library needed for getaddrinfo. + # NOTE: if you hand check_library_exists as its last argument a variable + # that's been set, it skips the test, so we need different variables. + # + include(CheckLibraryExists) + check_function_exists(getaddrinfo STDLIBS_HAVE_GETADDRINFO) + if(NOT STDLIBS_HAVE_GETADDRINFO) + check_library_exists(xnet getaddrinfo "" LIBXNET_HAS_GETADDRINFO) + if(LIBXNET_HAS_GETADDRINFO) + set(RPCAPD_LINK_LIBRARIES ${RPCAPD_LINK_LIBRARIES} xnet) + else(LIBXNET_HAS_GETADDRINFO) + include(CMakePushCheckState) + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES nsl) + check_library_exists(socket getaddrinfo "" LIBSOCKET_HAS_GETADDRINFO) + cmake_pop_check_state() + if(LIBSOCKET_HAS_GETADDRINFO) + set(RPCAPD_LINK_LIBRARIES ${RPCAPD_LINK_LIBRARIES} socket nsl) + endif(LIBSOCKET_HAS_GETADDRINFO) + endif(LIBXNET_HAS_GETADDRINFO) + endif(NOT STDLIBS_HAVE_GETADDRINFO) + endif(UNIX) + + if(WIN32) + set(RPCAPD_EXTRA_SOURCES + win32-svc.c + ${pcap_SOURCE_DIR}/missing/getopt.c + ${pcap_SOURCE_DIR}/missing/win_snprintf.c + rpcapd.rc) + include_directories(${pcap_SOURCE_DIR}/rpcapd ${pcap_SOURCE_DIR}/missing) + endif(WIN32) + + add_executable(rpcapd + daemon.c + fileconf.c + log.c + rpcapd.c + ${pcap_SOURCE_DIR}/rpcap-protocol.c + ${pcap_SOURCE_DIR}/sockutils.c + ${pcap_SOURCE_DIR}/fmtutils.c + ${RPCAPD_EXTRA_SOURCES} + ) + + if(NOT C_ADDITIONAL_FLAGS STREQUAL "") + set_target_properties(rpcapd PROPERTIES COMPILE_FLAGS ${C_ADDITIONAL_FLAGS}) + endif() + + # + # By default, build rpcapd universal with the appropriate set of + # architectures for the OS on which we're doing the build. + # + if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "") + # + # Get the major version of Darwin. + # + string(REGEX MATCH "^([0-9]+)" SYSTEM_VERSION_MAJOR "${CMAKE_SYSTEM_VERSION}") + + if(SYSTEM_VERSION_MAJOR EQUAL 9) + # + # Leopard. Build for 32-bit x86 and 32-bit PowerPC, with + # 32-bit x86 first. + # + set(OSX_PROGRAM_ARCHITECTURES "i386;ppc") + elseif(SYSTEM_VERSION_MAJOR EQUAL 10) + # + # Snow Leopard. Build for x86-64 and 32-bit x86, with + # x86-64 first. + # + set(OSX_PROGRAM_ARCHITECTURES "x86_64;i386") + else() + # + # Post-Snow Leopard. Build only for x86-64. + # XXX - update if and when Apple adds ARM-based Macs. + # (You're on your own for iOS etc.) + # + set(OSX_PROGRAM_ARCHITECTURES "x86_64") + endif() + + set_target_properties(rpcapd PROPERTIES + OSX_ARCHITECTURES "${OSX_PROGRAM_ARCHITECTURES}") + endif() + + if(WIN32) + target_link_libraries(rpcapd ${LIBRARY_NAME} + ${RPCAPD_LINK_LIBRARIES} ${PCAP_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + else(WIN32) + target_link_libraries(rpcapd ${LIBRARY_NAME}_static + ${RPCAPD_LINK_LIBRARIES} ${PCAP_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + endif(WIN32) + + ###################################### + # Install rpcap daemon and man pages + ###################################### + + # + # "Define GNU standard installation directories", which actually + # are also defined, to some degree, by autotools, and at least + # some of which are general UN*X conventions. + # + include(GNUInstallDirs) + + set(MANADMIN_EXPAND rpcapd.manadmin.in) + + set(MANFILE_EXPAND rpcapd-config.manfile.in) + + if(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) + install(TARGETS rpcapd DESTINATION bin/amd64) + else(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) + install(TARGETS rpcapd DESTINATION bin) + endif(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) + + # On UN*X, and on Windows when not using MSVC, generate process man + # pages and arrange that they be installed. + if(NOT MSVC) + # + # Man pages. + # + # For each section of the manual for which we have man pages + # that require macro expansion, do the expansion. + # + set(MANADMIN "") + foreach(TEMPLATE_MANPAGE ${MANADMIN_EXPAND}) + string(REPLACE ".manadmin.in" ".${MAN_ADMIN_COMMANDS}" MANPAGE ${TEMPLATE_MANPAGE}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) + set(MANADMIN ${MANADMIN} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) + endforeach(TEMPLATE_MANPAGE) + install(FILES ${MANADMIN} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_ADMIN_COMMANDS}) + + set(MANFILE "") + foreach(TEMPLATE_MANPAGE ${MANFILE_EXPAND}) + string(REPLACE ".manfile.in" ".${MAN_FILE_FORMATS}" MANPAGE ${TEMPLATE_MANPAGE}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) + set(MANFILE ${MANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) + endforeach(TEMPLATE_MANPAGE) + install(FILES ${MANFILE} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_FILE_FORMATS}) + endif(NOT MSVC) +endif(WIN32 OR ((CMAKE_USE_PTHREADS_INIT OR PTHREADS_FOUND) AND HAVE_CRYPT)) diff --git a/lib/libpcap/libpcap/rpcapd/Makefile b/lib/libpcap/libpcap/rpcapd/Makefile new file mode 100644 index 0000000..114f8b7 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/Makefile @@ -0,0 +1,141 @@ +# Copyright (c) 1993, 1994, 1995, 1996 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that: (1) source code distributions +# retain the above copyright notice and this paragraph in its entirety, (2) +# distributions including binary code include the above copyright notice and +# this paragraph in its entirety in the documentation or other materials +# provided with the distribution, and (3) all advertising materials mentioning +# features or use of this software display the following acknowledgement: +# ``This product includes software developed by the University of California, +# Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +# the University nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +# +# Various configurable paths (remember to edit Makefile.in, not Makefile) +# + +# Top level hierarchy +prefix = /usr/local +exec_prefix = ${prefix} +datarootdir = ${prefix}/share +# Pathname of directory to install the configure program +bindir = ${exec_prefix}/bin +# Pathname of directory to install the rpcapd daemon +sbindir = ${exec_prefix}/sbin +# Pathname of directory to install the include files +includedir = ${prefix}/include +# Pathname of directory to install the library +libdir = ${exec_prefix}/lib +# Pathname of directory to install the man pages +mandir = ${datarootdir}/man + +# VPATH +srcdir = . + + +# +# You shouldn't need to edit anything below. +# + +LD = /usr/bin/ld +CC = gcc +AR = ar +LN_S = ln -s +MKDEP = +CCOPT = -fvisibility=hidden -fno-common +INCLS = -I. -I.. -I. -I./.. -I/usr/local/include +DEFS = -DHAVE_CONFIG_H +ADDLOBJS = +ADDLARCHIVEOBJS = +LIBS = +PTHREAD_LIBS = +CROSSFLAGS= +CFLAGS = -g -O2 ${CROSSFLAGS} +LDFLAGS = ${CROSSFLAGS} +DYEXT = dylib +V_RPATH_OPT = +DEPENDENCY_CFLAG = +PROG=libpcap +RPCAPD_LIBS = + +# Standard CFLAGS +FULL_CFLAGS = $(CCOPT) $(INCLS) $(DEFS) $(CFLAGS) + +INSTALL = /usr/bin/install -c +INSTALL_PROGRAM = ${INSTALL} +INSTALL_DATA = ${INSTALL} -m 644 + +# Explicitly define compilation rule since SunOS 4's make doesn't like gcc. +# Also, gcc does not remove the .o before forking 'as', which can be a +# problem if you don't own the file but can write to the directory. +.c.o: + @rm -f $@ + $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c + +SRC = daemon.c \ + fileconf.c \ + log.c \ + rpcapd.c + +OBJ = $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o +PUBHDR = + +HDR = $(PUBHDR) log.h + +TAGFILES = \ + $(SRC) $(HDR) + +CLEANFILES = $(OBJ) rpcapd + +MANADMIN = \ + rpcapd.manadmin.in + +MANFILE = \ + rpcapd-config.manfile.in + +rpcapd: $(OBJ) ../libpcap.a + $(CC) $(CCOPT) $(CFLAGS) $(LDFLAGS) \ + -o $@ $(OBJ) ../libpcap.a $(LIBS) $(RPCAPD_LIBS) $(PTHREAD_LIBS) +clean: + rm -f $(CLEANFILES) + +distclean: clean + rm -f Makefile config.cache config.log config.status \ + config.h stamp-h stamp-h.in + rm -f $(MANADMIN:.in=) $(MANFILE:.in=) + rm -rf autom4te.cache + +install: rpcapd + [ -d $(DESTDIR)$(sbindir) ] || \ + (mkdir -p $(DESTDIR)$(sbindir); chmod 755 $(DESTDIR)$(sbindir)) + $(INSTALL_PROGRAM) rpcapd $(DESTDIR)$(sbindir)/rpcapd + [ -d $(DESTDIR)$(mandir)/man8 ] || \ + (mkdir -p $(DESTDIR)$(mandir)/man8; chmod 755 $(DESTDIR)$(mandir)/man8) + [ -d $(DESTDIR)$(mandir)/man5 ] || \ + (mkdir -p $(DESTDIR)$(mandir)/man5; chmod 755 $(DESTDIR)$(mandir)/man5) + for i in $(MANADMIN); do \ + $(INSTALL_DATA) `echo $$i | sed 's/.manadmin.in/.manadmin/'` \ + $(DESTDIR)$(mandir)/man8/`echo $$i | sed 's/.manadmin.in/.8/'`; done + for i in $(MANFILE); do \ + $(INSTALL_DATA) `echo $$i | sed 's/.manfile.in/.manfile/'` \ + $(DESTDIR)$(mandir)/man5/`echo $$i | sed 's/.manfile.in/.5/'`; done + +uninstall: + rm -f $(DESTDIR)$(sbindir)/rpcapd + for i in $(MANADMIN); do \ + rm -f $(DESTDIR)$(mandir)/man8/`echo $$i | sed 's/.manadmin.in/.8/'`; done + for i in $(MANFILE); do \ + rm -f $(DESTDIR)$(mandir)/man5/`echo $$i | sed 's/.manfile.in/.5/'`; done + +tags: $(TAGFILES) + ctags -wtd $(TAGFILES) + +depend: + ../$(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) diff --git a/lib/libpcap/libpcap/rpcapd/Makefile.in b/lib/libpcap/libpcap/rpcapd/Makefile.in new file mode 100644 index 0000000..88e632a --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/Makefile.in @@ -0,0 +1,141 @@ +# Copyright (c) 1993, 1994, 1995, 1996 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that: (1) source code distributions +# retain the above copyright notice and this paragraph in its entirety, (2) +# distributions including binary code include the above copyright notice and +# this paragraph in its entirety in the documentation or other materials +# provided with the distribution, and (3) all advertising materials mentioning +# features or use of this software display the following acknowledgement: +# ``This product includes software developed by the University of California, +# Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +# the University nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +# +# Various configurable paths (remember to edit Makefile.in, not Makefile) +# + +# Top level hierarchy +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +# Pathname of directory to install the configure program +bindir = @bindir@ +# Pathname of directory to install the rpcapd daemon +sbindir = @sbindir@ +# Pathname of directory to install the include files +includedir = @includedir@ +# Pathname of directory to install the library +libdir = @libdir@ +# Pathname of directory to install the man pages +mandir = @mandir@ + +# VPATH +srcdir = @srcdir@ +VPATH = @srcdir@ + +# +# You shouldn't need to edit anything below. +# + +LD = /usr/bin/ld +CC = @CC@ +AR = @AR@ +LN_S = @LN_S@ +MKDEP = @MKDEP@ +CCOPT = @V_CCOPT@ +INCLS = -I. -I.. -I@srcdir@ -I@srcdir@/.. @V_INCLS@ +DEFS = @DEFS@ @V_DEFS@ +ADDLOBJS = @ADDLOBJS@ +ADDLARCHIVEOBJS = @ADDLARCHIVEOBJS@ +LIBS = @LIBS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +CROSSFLAGS= +CFLAGS = @CFLAGS@ ${CROSSFLAGS} +LDFLAGS = @LDFLAGS@ ${CROSSFLAGS} +DYEXT = @DYEXT@ +V_RPATH_OPT = @V_RPATH_OPT@ +DEPENDENCY_CFLAG = @DEPENDENCY_CFLAG@ +PROG=libpcap +RPCAPD_LIBS = @RPCAPD_LIBS@ + +# Standard CFLAGS +FULL_CFLAGS = $(CCOPT) @V_PROG_CCOPT_FAT@ $(INCLS) $(DEFS) $(CFLAGS) + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# Explicitly define compilation rule since SunOS 4's make doesn't like gcc. +# Also, gcc does not remove the .o before forking 'as', which can be a +# problem if you don't own the file but can write to the directory. +.c.o: + @rm -f $@ + $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c + +SRC = daemon.c \ + fileconf.c \ + log.c \ + rpcapd.c + +OBJ = $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o +PUBHDR = + +HDR = $(PUBHDR) log.h + +TAGFILES = \ + $(SRC) $(HDR) + +CLEANFILES = $(OBJ) rpcapd + +MANADMIN = \ + rpcapd.manadmin.in + +MANFILE = \ + rpcapd-config.manfile.in + +rpcapd: $(OBJ) ../libpcap.a + $(CC) $(CCOPT) $(CFLAGS) $(LDFLAGS) @V_PROG_LDFLAGS_FAT@ \ + -o $@ $(OBJ) ../libpcap.a $(LIBS) $(RPCAPD_LIBS) $(PTHREAD_LIBS) +clean: + rm -f $(CLEANFILES) + +distclean: clean + rm -f Makefile config.cache config.log config.status \ + config.h stamp-h stamp-h.in + rm -f $(MANADMIN:.in=) $(MANFILE:.in=) + rm -rf autom4te.cache + +install: rpcapd + [ -d $(DESTDIR)$(sbindir) ] || \ + (mkdir -p $(DESTDIR)$(sbindir); chmod 755 $(DESTDIR)$(sbindir)) + $(INSTALL_PROGRAM) rpcapd $(DESTDIR)$(sbindir)/rpcapd + [ -d $(DESTDIR)$(mandir)/man@MAN_ADMIN_COMMANDS@ ] || \ + (mkdir -p $(DESTDIR)$(mandir)/man@MAN_ADMIN_COMMANDS@; chmod 755 $(DESTDIR)$(mandir)/man@MAN_ADMIN_COMMANDS@) + [ -d $(DESTDIR)$(mandir)/man@MAN_FILE_FORMATS@ ] || \ + (mkdir -p $(DESTDIR)$(mandir)/man@MAN_FILE_FORMATS@; chmod 755 $(DESTDIR)$(mandir)/man@MAN_FILE_FORMATS@) + for i in $(MANADMIN); do \ + $(INSTALL_DATA) `echo $$i | sed 's/.manadmin.in/.manadmin/'` \ + $(DESTDIR)$(mandir)/man@MAN_ADMIN_COMMANDS@/`echo $$i | sed 's/.manadmin.in/.@MAN_ADMIN_COMMANDS@/'`; done + for i in $(MANFILE); do \ + $(INSTALL_DATA) `echo $$i | sed 's/.manfile.in/.manfile/'` \ + $(DESTDIR)$(mandir)/man@MAN_FILE_FORMATS@/`echo $$i | sed 's/.manfile.in/.@MAN_FILE_FORMATS@/'`; done + +uninstall: + rm -f $(DESTDIR)$(sbindir)/rpcapd + for i in $(MANADMIN); do \ + rm -f $(DESTDIR)$(mandir)/man@MAN_ADMIN_COMMANDS@/`echo $$i | sed 's/.manadmin.in/.@MAN_ADMIN_COMMANDS@/'`; done + for i in $(MANFILE); do \ + rm -f $(DESTDIR)$(mandir)/man@MAN_FILE_FORMATS@/`echo $$i | sed 's/.manfile.in/.@MAN_FILE_FORMATS@/'`; done + +tags: $(TAGFILES) + ctags -wtd $(TAGFILES) + +depend: + ../$(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) diff --git a/lib/libpcap/libpcap/rpcapd/config_params.h b/lib/libpcap/libpcap/rpcapd/config_params.h new file mode 100644 index 0000000..c219ce1 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/config_params.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __CONFIG_PARAMS_H__ +#define __CONFIG_PARAMS_H__ + +// +// Parameters set from the configuration file. +// + +#define MAX_LINE 2048 /* Maximum chars allowed for the host list (in passive mode) */ +#define MAX_HOST_LIST 64000 +#define MAX_ACTIVE_LIST 10 + +struct active_pars +{ + char address[MAX_LINE + 1]; // keeps the network address (either numeric or literal) to of the active client + char port[MAX_LINE + 1]; // keeps the network port to bind to + int ai_family; // address faimly to use +}; + +extern char hostlist[MAX_HOST_LIST + 1]; //!< Keeps the list of the hosts that are allowed to connect to this server +extern struct active_pars activelist[MAX_ACTIVE_LIST]; //!< Keeps the list of the hosts (host, port) on which I want to connect to (active mode) +extern int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise +extern char loadfile[MAX_LINE + 1]; //!< Name of the file from which we have to load the configuration + +#endif diff --git a/lib/libpcap/libpcap/rpcapd/daemon.c b/lib/libpcap/libpcap/rpcapd/daemon.c new file mode 100644 index 0000000..209dba2 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/daemon.c @@ -0,0 +1,2747 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ftmacros.h" +#include "varattrs.h" + +#include <errno.h> // for the errno variable +#include <stdlib.h> // for malloc(), free(), ... +#include <string.h> // for strlen(), ... + +#ifdef _WIN32 + #include <process.h> // for threads +#else + #include <unistd.h> + #include <pthread.h> + #include <signal.h> + #include <sys/time.h> + #include <sys/types.h> // for select() and such + #include <pwd.h> // for password management +#endif + +#ifdef HAVE_GETSPNAM +#include <shadow.h> // for password management +#endif + +#include <pcap.h> // for libpcap/WinPcap calls + +#include "fmtutils.h" +#include "sockutils.h" // for socket calls +#include "portability.h" +#include "rpcap-protocol.h" +#include "daemon.h" +#include "log.h" + +// +// Timeout, in seconds, when we're waiting for a client to send us an +// authentication request; if they don't send us a request within that +// interval, we drop the connection, so we don't stay stuck forever. +// +#define RPCAP_TIMEOUT_INIT 90 + +// +// Timeout, in seconds, when we're waiting for an authenticated client +// to send us a request, if a capture isn't in progress; if they don't +// send us a request within that interval, we drop the connection, so +// we don't stay stuck forever. +// +#define RPCAP_TIMEOUT_RUNTIME 180 + +// +// Time, in seconds, that we wait after a failed authentication attempt +// before processing the next request; this prevents a client from +// rapidly trying different accounts or passwords. +// +#define RPCAP_SUSPEND_WRONGAUTH 1 + +// Parameters for the service loop. +struct daemon_slpars +{ + SOCKET sockctrl; //!< SOCKET ID of the control connection + int isactive; //!< Not null if the daemon has to run in active mode + int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise +}; + +// +// Data for a session managed by a thread. +// It includes both a Boolean indicating whether we *have* a thread, +// and a platform-dependent (UN*X vs. Windows) identifier for the +// thread; on Windows, we could use an invalid handle to indicate +// that we don't have a thread, but there *is* no portable "no thread" +// value for a pthread_t on UN*X. +// +struct session { + SOCKET sockctrl; + SOCKET sockdata; + uint8 protocol_version; + pcap_t *fp; + unsigned int TotCapt; + int have_thread; +#ifdef _WIN32 + HANDLE thread; +#else + pthread_t thread; +#endif +}; + +// Locally defined functions +static int daemon_msg_err(SOCKET sockctrl, uint32 plen); +static int daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen); +static int daemon_AuthUserPwd(char *username, char *password, char *errbuf); + +static int daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, + uint32 plen); + +static int daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, + uint32 plen, char *source, size_t sourcelen); +static int daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, + uint32 plen, char *source, struct session **sessionp, + struct rpcap_sampling *samp_param); +static int daemon_msg_endcap_req(uint8 ver, struct daemon_slpars *pars, + struct session *session); + +static int daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, + struct session *session, uint32 plen); +static int daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp, char *errbuf); + +static int daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, + struct session *session, uint32 plen, struct pcap_stat *stats, + unsigned int svrcapt); + +static int daemon_msg_setsampling_req(uint8 ver, struct daemon_slpars *pars, + uint32 plen, struct rpcap_sampling *samp_param); + +static void daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *sockaddrout); +#ifdef _WIN32 +static unsigned __stdcall daemon_thrdatamain(void *ptr); +#else +static void *daemon_thrdatamain(void *ptr); +static void noop_handler(int sign); +#endif + +static int rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp); +static int rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf); +static int rpcapd_discard(SOCKET sock, uint32 len); +static void session_close(struct session *); + +static int is_url(const char *source); + +int +daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, + int nullAuthAllowed) +{ + struct daemon_slpars pars; // service loop parameters + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + char errmsgbuf[PCAP_ERRBUF_SIZE + 1]; // buffer for errors to send to the client + int host_port_check_status; + int nrecv; + struct rpcap_header header; // RPCAP message general header + uint32 plen; // payload length from header + int authenticated = 0; // 1 if the client has successfully authenticated + char source[PCAP_BUF_SIZE+1]; // keeps the string that contains the interface to open + int got_source = 0; // 1 if we've gotten the source from an open request +#ifndef _WIN32 + struct sigaction action; +#endif + struct session *session = NULL; // struct session main variable + const char *msg_type_string; // string for message type + int client_told_us_to_close = 0; // 1 if the client told us to close the capture + + // needed to save the values of the statistics + struct pcap_stat stats; + unsigned int svrcapt; + + struct rpcap_sampling samp_param; // in case sampling has been requested + + // Structures needed for the select() call + fd_set rfds; // set of socket descriptors we have to check + struct timeval tv; // maximum time the select() can block waiting for data + int retval; // select() return value + + *errbuf = 0; // Initialize errbuf + + // Set parameters structure + pars.sockctrl = sockctrl; + pars.isactive = isactive; // active mode + pars.nullAuthAllowed = nullAuthAllowed; + + // + // We have a connection. + // + // If it's a passive mode connection, check whether the connecting + // host is among the ones allowed. + // + // In either case, we were handed a copy of the host list; free it + // as soon as we're done with it. + // + if (pars.isactive) + { + // Nothing to do. + free(passiveClients); + passiveClients = NULL; + } + else + { + struct sockaddr_storage from; + socklen_t fromlen; + + // + // Get the address of the other end of the connection. + // + fromlen = sizeof(struct sockaddr_storage); + if (getpeername(pars.sockctrl, (struct sockaddr *)&from, + &fromlen) == -1) + { + sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE); + if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + // + // Are they in the list of host/port combinations we allow? + // + host_port_check_status = sock_check_hostlist(passiveClients, RPCAP_HOSTLIST_SEP, &from, errmsgbuf, PCAP_ERRBUF_SIZE); + free(passiveClients); + passiveClients = NULL; + if (host_port_check_status < 0) + { + if (host_port_check_status == -2) { + // + // We got an error; log it. + // + rpcapd_log(LOGPRIO_ERROR, "%s", errmsgbuf); + } + + // + // Sorry, we can't let you in. + // + if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errmsgbuf, errbuf) == -1) + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + } + +#ifndef _WIN32 + // + // Catch SIGUSR1, but do nothing. We use it to interrupt the + // capture thread to break it out of a loop in which it's + // blocked waiting for packets to arrive. + // + // We don't want interrupted system calls to restart, so that + // the read routine for the pcap_t gets EINTR rather than + // restarting if it's blocked in a system call. + // + memset(&action, 0, sizeof (action)); + action.sa_handler = noop_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGUSR1, &action, NULL); +#endif + + // + // The client must first authenticate; loop until they send us a + // message with a version we support and credentials we accept, + // they send us a close message indicating that they're giving up, + // or we get a network error or other fatal error. + // + while (!authenticated) + { + // + // If we're not in active mode, we use select(), with a + // timeout, to wait for an authentication request; if + // the timeout expires, we drop the connection, so that + // a client can't just connect to us and leave us + // waiting forever. + // + if (!pars.isactive) + { + FD_ZERO(&rfds); + // We do not have to block here + tv.tv_sec = RPCAP_TIMEOUT_INIT; + tv.tv_usec = 0; + + FD_SET(pars.sockctrl, &rfds); + + retval = select(pars.sockctrl + 1, &rfds, NULL, NULL, &tv); + if (retval == -1) + { + sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE); + if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + // The timeout has expired + // So, this was a fake connection. Drop it down + if (retval == 0) + { + if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + } + + // + // Read the message header from the client. + // + nrecv = rpcapd_recv_msg_header(pars.sockctrl, &header); + if (nrecv == -1) + { + // Fatal error. + goto end; + } + if (nrecv == -2) + { + // Client closed the connection. + goto end; + } + + plen = header.plen; + + // + // While we're in the authentication pharse, all requests + // must use version 0. + // + if (header.ver != 0) + { + // + // Send it back to them with their version. + // + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGVER, + "RPCAP version in requests in the authentication phase must be 0", + errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + // Discard the rest of the message and drop the + // connection. + (void)rpcapd_discard(pars.sockctrl, plen); + goto end; + } + + switch (header.type) + { + case RPCAP_MSG_AUTH_REQ: + retval = daemon_msg_auth_req(&pars, plen); + if (retval == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + if (retval == -2) + { + // Non-fatal error; we sent back + // an error message, so let them + // try again. + continue; + } + + // OK, we're authenticated; we sent back + // a reply, so start serving requests. + authenticated = 1; + break; + + case RPCAP_MSG_CLOSE: + // + // The client is giving up. + // Discard the rest of the message, if + // there is anything more. + // + (void)rpcapd_discard(pars.sockctrl, plen); + // We're done with this client. + goto end; + + case RPCAP_MSG_ERROR: + // Log this and close the connection? + // XXX - is this what happens in active + // mode, where *we* initiate the + // connection, and the client gives us + // an error message rather than a "let + // me log in" message, indicating that + // we're not allowed to connect to them? + (void)daemon_msg_err(pars.sockctrl, plen); + goto end; + + case RPCAP_MSG_FINDALLIF_REQ: + case RPCAP_MSG_OPEN_REQ: + case RPCAP_MSG_STARTCAP_REQ: + case RPCAP_MSG_UPDATEFILTER_REQ: + case RPCAP_MSG_STATS_REQ: + case RPCAP_MSG_ENDCAP_REQ: + case RPCAP_MSG_SETSAMPLING_REQ: + // + // These requests can't be sent until + // the client is authenticated. + // + msg_type_string = rpcap_msg_type_string(header.type); + if (msg_type_string != NULL) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "%s request sent before authentication was completed", msg_type_string); + } + else + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message of type %u sent before authentication was completed", header.type); + } + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // Discard the rest of the message. + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + // Network error. + goto end; + } + break; + + case RPCAP_MSG_PACKET: + case RPCAP_MSG_FINDALLIF_REPLY: + case RPCAP_MSG_OPEN_REPLY: + case RPCAP_MSG_STARTCAP_REPLY: + case RPCAP_MSG_UPDATEFILTER_REPLY: + case RPCAP_MSG_AUTH_REPLY: + case RPCAP_MSG_STATS_REPLY: + case RPCAP_MSG_ENDCAP_REPLY: + case RPCAP_MSG_SETSAMPLING_REPLY: + // + // These are server-to-client messages. + // + msg_type_string = rpcap_msg_type_string(header.type); + if (msg_type_string != NULL) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message %s received from client", msg_type_string); + } + else + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); + } + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // Discard the rest of the message. + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + // Fatal error. + goto end; + } + break; + + default: + // + // Unknown message type. + // + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // Discard the rest of the message. + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + // Fatal error. + goto end; + } + break; + } + } + + // + // OK, the client has authenticated itself, and we can start + // processing regular requests from it. + // + + // + // We don't have any statistics yet. + // + stats.ps_ifdrop = 0; + stats.ps_recv = 0; + stats.ps_drop = 0; + svrcapt = 0; + + // + // Service requests. + // + for (;;) + { + errbuf[0] = 0; // clear errbuf + + // Avoid zombies connections; check if the connection is opens but no commands are performed + // from more than RPCAP_TIMEOUT_RUNTIME + // Conditions: + // - I have to be in normal mode (no active mode) + // - if the device is open, I don't have to be in the middle of a capture (session->sockdata) + // - if the device is closed, I have always to check if a new command arrives + // + // Be carefully: the capture can have been started, but an error occurred (so session != NULL, but + // sockdata is 0 + if ((!pars.isactive) && ((session == NULL) || ((session != NULL) && (session->sockdata == 0)))) + { + // Check for the initial timeout + FD_ZERO(&rfds); + // We do not have to block here + tv.tv_sec = RPCAP_TIMEOUT_RUNTIME; + tv.tv_usec = 0; + + FD_SET(pars.sockctrl, &rfds); + + retval = select(pars.sockctrl + 1, &rfds, NULL, NULL, &tv); + if (retval == -1) + { + sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE); + if (rpcap_senderror(pars.sockctrl, 0, + PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + // The timeout has expired + // So, this was a fake connection. Drop it down + if (retval == 0) + { + if (rpcap_senderror(pars.sockctrl, 0, + PCAP_ERR_INITTIMEOUT, + "The RPCAP initial timeout has expired", + errbuf) == -1) + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + } + + // + // Read the message header from the client. + // + nrecv = rpcapd_recv_msg_header(pars.sockctrl, &header); + if (nrecv == -1) + { + // Fatal error. + goto end; + } + if (nrecv == -2) + { + // Client closed the connection. + goto end; + } + + plen = header.plen; + + // + // Did the client specify a protocol version that we + // support? + // + if (!RPCAP_VERSION_IS_SUPPORTED(header.ver)) + { + // + // Tell them it's not a supported version. + // Send the error message with their version, + // so they don't reject it as having the wrong + // version. + // + if (rpcap_senderror(pars.sockctrl, + header.ver, PCAP_ERR_WRONGVER, + "RPCAP version in message isn't supported by the server", + errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + // Discard the rest of the message. + (void)rpcapd_discard(pars.sockctrl, plen); + // Give up on them. + goto end; + } + + switch (header.type) + { + case RPCAP_MSG_ERROR: // The other endpoint reported an error + { + (void)daemon_msg_err(pars.sockctrl, plen); + // Do nothing; just exit; the error code is already into the errbuf + // XXX - actually exit.... + break; + } + + case RPCAP_MSG_FINDALLIF_REQ: + { + if (daemon_msg_findallif_req(header.ver, &pars, plen) == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + break; + } + + case RPCAP_MSG_OPEN_REQ: + { + // + // Process the open request, and keep + // the source from it, for use later + // when the capture is started. + // + // XXX - we don't care if the client sends + // us multiple open requests, the last + // one wins. + // + retval = daemon_msg_open_req(header.ver, &pars, + plen, source, sizeof(source)); + if (retval == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + got_source = 1; + break; + } + + case RPCAP_MSG_STARTCAP_REQ: + { + if (!got_source) + { + // They never told us what device + // to capture on! + if (rpcap_senderror(pars.sockctrl, + header.ver, + PCAP_ERR_STARTCAPTURE, + "No capture device was specified", + errbuf) == -1) + { + // Fatal error; log an + // error and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + goto end; + } + break; + } + + if (daemon_msg_startcap_req(header.ver, &pars, + plen, source, &session, &samp_param) == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + break; + } + + case RPCAP_MSG_UPDATEFILTER_REQ: + { + if (session) + { + if (daemon_msg_updatefilter_req(header.ver, + &pars, session, plen) == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + } + else + { + if (rpcap_senderror(pars.sockctrl, + header.ver, PCAP_ERR_UPDATEFILTER, + "Device not opened. Cannot update filter", + errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + } + break; + } + + case RPCAP_MSG_CLOSE: // The other endpoint close the pcap session + { + // + // Indicate to our caller that the client + // closed the control connection. + // This is used only in case of active mode. + // + client_told_us_to_close = 1; + rpcapd_log(LOGPRIO_DEBUG, "The other end system asked to close the connection."); + goto end; + } + + case RPCAP_MSG_STATS_REQ: + { + if (daemon_msg_stats_req(header.ver, &pars, + session, plen, &stats, svrcapt) == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + break; + } + + case RPCAP_MSG_ENDCAP_REQ: // The other endpoint close the current capture session + { + if (session) + { + // Save statistics (we can need them in the future) + if (pcap_stats(session->fp, &stats)) + { + svrcapt = session->TotCapt; + } + else + { + stats.ps_ifdrop = 0; + stats.ps_recv = 0; + stats.ps_drop = 0; + svrcapt = 0; + } + + if (daemon_msg_endcap_req(header.ver, + &pars, session) == -1) + { + free(session); + session = NULL; + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + free(session); + session = NULL; + } + else + { + rpcap_senderror(pars.sockctrl, + header.ver, PCAP_ERR_ENDCAPTURE, + "Device not opened. Cannot close the capture", + errbuf); + } + break; + } + + case RPCAP_MSG_SETSAMPLING_REQ: + { + if (daemon_msg_setsampling_req(header.ver, + &pars, plen, &samp_param) == -1) + { + // Fatal error; a message has + // been logged, so just give up. + goto end; + } + break; + } + + case RPCAP_MSG_AUTH_REQ: + { + // + // We're already authenticated; you don't + // get to reauthenticate. + // + rpcapd_log(LOGPRIO_INFO, "The client sent an RPCAP_MSG_AUTH_REQ message after authentication was completed"); + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGMSG, + "RPCAP_MSG_AUTH_REQ request sent after authentication was completed", + errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // Discard the rest of the message. + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + // Fatal error. + goto end; + } + goto end; + + case RPCAP_MSG_PACKET: + case RPCAP_MSG_FINDALLIF_REPLY: + case RPCAP_MSG_OPEN_REPLY: + case RPCAP_MSG_STARTCAP_REPLY: + case RPCAP_MSG_UPDATEFILTER_REPLY: + case RPCAP_MSG_AUTH_REPLY: + case RPCAP_MSG_STATS_REPLY: + case RPCAP_MSG_ENDCAP_REPLY: + case RPCAP_MSG_SETSAMPLING_REPLY: + // + // These are server-to-client messages. + // + msg_type_string = rpcap_msg_type_string(header.type); + if (msg_type_string != NULL) + { + rpcapd_log(LOGPRIO_INFO, "The client sent a %s server-to-client message", msg_type_string); + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message %s received from client", msg_type_string); + } + else + { + rpcapd_log(LOGPRIO_INFO, "The client sent a server-to-client message of type %u", header.type); + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); + } + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // Discard the rest of the message. + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + // Fatal error. + goto end; + } + goto end; + + default: + // + // Unknown message type. + // + rpcapd_log(LOGPRIO_INFO, "The client sent a message of type %u", header.type); + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); + if (rpcap_senderror(pars.sockctrl, header.ver, + PCAP_ERR_WRONGMSG, errbuf, errmsgbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // Discard the rest of the message. + if (rpcapd_discard(pars.sockctrl, plen) == -1) + { + // Fatal error. + goto end; + } + goto end; + } + } + } + +end: + // The service loop is finishing up. + // If we have a capture session running, close it. + if (session) + { + session_close(session); + free(session); + session = NULL; + } + + sock_close(sockctrl, NULL, 0); + + // Print message and return + rpcapd_log(LOGPRIO_DEBUG, "I'm exiting from the child loop"); + + return client_told_us_to_close; +} + +/* + * This handles the RPCAP_MSG_ERR message. + */ +static int +daemon_msg_err(SOCKET sockctrl, uint32 plen) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + char remote_errbuf[PCAP_ERRBUF_SIZE]; + + if (plen >= PCAP_ERRBUF_SIZE) + { + /* + * Message is too long; just read as much of it as we + * can into the buffer provided, and discard the rest. + */ + if (sock_recv(sockctrl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, + PCAP_ERRBUF_SIZE) == -1) + { + // Network error. + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + return -1; + } + if (rpcapd_discard(sockctrl, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) + { + // Network error. + return -1; + } + + /* + * Null-terminate it. + */ + remote_errbuf[PCAP_ERRBUF_SIZE - 1] = '\0'; + } + else if (plen == 0) + { + /* Empty error string. */ + remote_errbuf[0] = '\0'; + } + else + { + if (sock_recv(sockctrl, remote_errbuf, plen, + SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, + PCAP_ERRBUF_SIZE) == -1) + { + // Network error. + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + return -1; + } + + /* + * Null-terminate it. + */ + remote_errbuf[plen] = '\0'; + } + // Log the message + rpcapd_log(LOGPRIO_ERROR, "Error from client: %s", remote_errbuf); + return 0; +} + +/* + * This handles the RPCAP_MSG_AUTH_REQ message. + * It checks if the authentication credentials supplied by the user are valid. + * + * This function is called if the daemon receives a RPCAP_MSG_AUTH_REQ + * message in its authentication loop. It reads the body of the + * authentication message from the network and checks whether the + * credentials are valid. + * + * \param sockctrl: the socket for the control connection. + * + * \param nullAuthAllowed: '1' if the NULL authentication is allowed. + * + * \param errbuf: a user-allocated buffer in which the error message + * (if one) has to be written. It must be at least PCAP_ERRBUF_SIZE + * bytes long. + * + * \return '0' if everything is fine, '-1' if an unrecoverable error occurred, + * or '-2' if the authentication failed. For errors, an error message is + * returned in the 'errbuf' variable; this gives a message for the + * unrecoverable error or for the authentication failure. + */ +static int +daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client + int status; + struct rpcap_auth auth; // RPCAP authentication header + char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered + int sendbufidx = 0; // index which keeps the number of bytes currently buffered + struct rpcap_authreply *authreply; // authentication reply message + + status = rpcapd_recv(pars->sockctrl, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); + if (status == -1) + { + return -1; + } + if (status == -2) + { + goto error; + } + + switch (ntohs(auth.type)) + { + case RPCAP_RMTAUTH_NULL: + { + if (!pars->nullAuthAllowed) + { + // Send the client an error reply. + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, + "Authentication failed; NULL authentication not permitted."); + if (rpcap_senderror(pars->sockctrl, 0, + PCAP_ERR_AUTH_FAILED, errmsgbuf, errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + goto error_noreply; + } + break; + } + + case RPCAP_RMTAUTH_PWD: + { + char *username, *passwd; + uint32 usernamelen, passwdlen; + + usernamelen = ntohs(auth.slen1); + username = (char *) malloc (usernamelen + 1); + if (username == NULL) + { + pcap_fmt_errmsg_for_errno(errmsgbuf, + PCAP_ERRBUF_SIZE, errno, "malloc() failed"); + goto error; + } + status = rpcapd_recv(pars->sockctrl, username, usernamelen, &plen, errmsgbuf); + if (status == -1) + { + free(username); + return -1; + } + if (status == -2) + { + free(username); + goto error; + } + username[usernamelen] = '\0'; + + passwdlen = ntohs(auth.slen2); + passwd = (char *) malloc (passwdlen + 1); + if (passwd == NULL) + { + pcap_fmt_errmsg_for_errno(errmsgbuf, + PCAP_ERRBUF_SIZE, errno, "malloc() failed"); + free(username); + goto error; + } + status = rpcapd_recv(pars->sockctrl, passwd, passwdlen, &plen, errmsgbuf); + if (status == -1) + { + free(username); + free(passwd); + return -1; + } + if (status == -2) + { + free(username); + free(passwd); + goto error; + } + passwd[passwdlen] = '\0'; + + if (daemon_AuthUserPwd(username, passwd, errmsgbuf)) + { + // + // Authentication failed. Let the client + // know. + // + free(username); + free(passwd); + if (rpcap_senderror(pars->sockctrl, 0, + PCAP_ERR_AUTH_FAILED, errmsgbuf, errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + // + // Suspend for 1 second, so that they can't + // hammer us with repeated tries with an + // attack such as a dictionary attack. + // + // WARNING: this delay is inserted only + // at this point; if the client closes the + // connection and reconnects, the suspension + // time does not have any effect. + // + sleep_secs(RPCAP_SUSPEND_WRONGAUTH); + goto error_noreply; + } + + free(username); + free(passwd); + break; + } + + default: + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, + "Authentication type not recognized."); + if (rpcap_senderror(pars->sockctrl, 0, + PCAP_ERR_AUTH_TYPE_NOTSUP, errmsgbuf, errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + goto error_noreply; + } + + // The authentication succeeded; let the client know. + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, 0, + RPCAP_MSG_AUTH_REPLY, 0, sizeof(struct rpcap_authreply)); + + authreply = (struct rpcap_authreply *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_authreply), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + // + // Indicate to our peer what versions we support. + // + memset(authreply, 0, sizeof(struct rpcap_authreply)); + authreply->minvers = RPCAP_MIN_VERSION; + authreply->maxvers = RPCAP_MAX_VERSION; + + // Send the reply. + if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // That failed; log a messsage and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + return -1; + } + + return 0; + +error: + if (rpcap_senderror(pars->sockctrl, 0, PCAP_ERR_AUTH, errmsgbuf, + errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + +error_noreply: + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + return -1; + } + + return -2; +} + +static int +daemon_AuthUserPwd(char *username, char *password, char *errbuf) +{ +#ifdef _WIN32 + /* + * Warning: the user which launches the process must have the + * SE_TCB_NAME right. + * This corresponds to have the "Act as part of the Operating System" + * turned on (administrative tools, local security settings, local + * policies, user right assignment) + * However, it seems to me that if you run it as a service, this + * right should be provided by default. + * + * XXX - hopefully, this returns errors such as ERROR_LOGON_FAILURE, + * which merely indicates that the user name or password is + * incorrect, not whether it's the user name or the password + * that's incorrect, so a client that's trying to brute-force + * accounts doesn't know whether it's the user name or the + * password that's incorrect, so it doesn't know whether to + * stop trying to log in with a given user name and move on + * to another user name. + */ + DWORD error; + HANDLE Token; + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to log + + if (LogonUser(username, ".", password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &Token) == 0) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + error = GetLastError(); + if (error != ERROR_LOGON_FAILURE) + { + // Some error other than an authentication error; + // log it. + pcap_fmt_errmsg_for_win32_err(errmsgbuf, + PCAP_ERRBUF_SIZE, error, "LogonUser() failed"); + rpcapd_log(LOGPRIO_ERROR, "%s", errmsgbuf); + } + return -1; + } + + // This call should change the current thread to the selected user. + // I didn't test it. + if (ImpersonateLoggedOnUser(Token) == 0) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + pcap_fmt_errmsg_for_win32_err(errmsgbuf, PCAP_ERRBUF_SIZE, + GetLastError(), "ImpersonateLoggedOnUser() failed"); + rpcapd_log(LOGPRIO_ERROR, "%s", errmsgbuf); + CloseHandle(Token); + return -1; + } + + CloseHandle(Token); + return 0; + +#else + /* + * See + * + * http://www.unixpapa.com/incnote/passwd.html + * + * We use the Solaris/Linux shadow password authentication if + * we have getspnam(), otherwise we just do traditional + * authentication, which, on some platforms, might work, even + * with shadow passwords, if we're running as root. Traditional + * authenticaion won't work if we're not running as root, as + * I think these days all UN*Xes either won't return the password + * at all with getpwnam() or will only do so if you're root. + * + * XXX - perhaps what we *should* be using is PAM, if we have + * it. That might hide all the details of username/password + * authentication, whether it's done with a visible-to-root- + * only password database or some other authentication mechanism, + * behind its API. + */ + int error; + struct passwd *user; + char *user_password; +#ifdef HAVE_GETSPNAM + struct spwd *usersp; +#endif + char *crypt_password; + + // This call is needed to get the uid + if ((user = getpwnam(username)) == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + return -1; + } + +#ifdef HAVE_GETSPNAM + // This call is needed to get the password; otherwise 'x' is returned + if ((usersp = getspnam(username)) == NULL) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + return -1; + } + user_password = usersp->sp_pwdp; +#else + /* + * XXX - what about other platforms? + * The unixpapa.com page claims this Just Works on *BSD if you're + * running as root - it's from 2000, so it doesn't indicate whether + * macOS (which didn't come out until 2001, under the name Mac OS + * X) behaves like the *BSDs or not, and might also work on AIX. + * HP-UX does something else. + * + * Again, hopefully PAM hides all that. + */ + user_password = user->pw_passwd; +#endif + + // + // The Single UNIX Specification says that if crypt() fails it + // sets errno, but some implementatons that haven't been run + // through the SUS test suite might not do so. + // + errno = 0; + crypt_password = crypt(password, user_password); + if (crypt_password == NULL) + { + error = errno; + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + if (error == 0) + { + // It didn't set errno. + rpcapd_log(LOGPRIO_ERROR, "crypt() failed"); + } + else + { + rpcapd_log(LOGPRIO_ERROR, "crypt() failed: %s", + strerror(error)); + } + return -1; + } + if (strcmp(user_password, crypt_password) != 0) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + return -1; + } + + if (setuid(user->pw_uid)) + { + error = errno; + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + error, "setuid"); + rpcapd_log(LOGPRIO_ERROR, "setuid() failed: %s", + strerror(error)); + return -1; + } + +/* if (setgid(user->pw_gid)) + { + error = errno; + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "setgid"); + rpcapd_log(LOGPRIO_ERROR, "setgid() failed: %s", + strerror(error)); + return -1; + } +*/ + return 0; + +#endif + +} + +static int +daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client + char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered + int sendbufidx = 0; // index which keeps the number of bytes currently buffered + pcap_if_t *alldevs = NULL; // pointer to the header of the interface chain + pcap_if_t *d; // temp pointer needed to scan the interface chain + struct pcap_addr *address; // pcap structure that keeps a network address of an interface + struct rpcap_findalldevs_if *findalldevs_if;// rpcap structure that packet all the data of an interface together + uint32 replylen; // length of reply payload + uint16 nif = 0; // counts the number of interface listed + + // Discard the rest of the message; there shouldn't be any payload. + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + // Network error. + return -1; + } + + // Retrieve the device list + if (pcap_findalldevs(&alldevs, errmsgbuf) == -1) + goto error; + + if (alldevs == NULL) + { + if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_NOREMOTEIF, + "No interfaces found! Make sure libpcap/WinPcap is properly installed" + " and you have the right to access to the remote device.", + errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + return 0; + } + + // This checks the number of interfaces and computes the total + // length of the payload. + replylen = 0; + for (d = alldevs; d != NULL; d = d->next) + { + nif++; + + if (d->description) + replylen += strlen(d->description); + if (d->name) + replylen += strlen(d->name); + + replylen += sizeof(struct rpcap_findalldevs_if); + + for (address = d->addresses; address != NULL; address = address->next) + { + /* + * Send only IPv4 and IPv6 addresses over the wire. + */ + switch (address->addr->sa_family) + { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + replylen += (sizeof(struct rpcap_sockaddr) * 4); + break; + + default: + break; + } + } + } + + // RPCAP findalldevs command + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, + PCAP_ERRBUF_SIZE) == -1) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, ver, + RPCAP_MSG_FINDALLIF_REPLY, nif, replylen); + + // send the interface list + for (d = alldevs; d != NULL; d = d->next) + { + uint16 lname, ldescr; + + findalldevs_if = (struct rpcap_findalldevs_if *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_findalldevs_if), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + memset(findalldevs_if, 0, sizeof(struct rpcap_findalldevs_if)); + + if (d->description) ldescr = (short) strlen(d->description); + else ldescr = 0; + if (d->name) lname = (short) strlen(d->name); + else lname = 0; + + findalldevs_if->desclen = htons(ldescr); + findalldevs_if->namelen = htons(lname); + findalldevs_if->flags = htonl(d->flags); + + for (address = d->addresses; address != NULL; address = address->next) + { + /* + * Send only IPv4 and IPv6 addresses over the wire. + */ + switch (address->addr->sa_family) + { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + findalldevs_if->naddr++; + break; + + default: + break; + } + } + findalldevs_if->naddr = htons(findalldevs_if->naddr); + + if (sock_bufferize(d->name, lname, sendbuf, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errmsgbuf, + PCAP_ERRBUF_SIZE) == -1) + goto error; + + if (sock_bufferize(d->description, ldescr, sendbuf, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errmsgbuf, + PCAP_ERRBUF_SIZE) == -1) + goto error; + + // send all addresses + for (address = d->addresses; address != NULL; address = address->next) + { + struct rpcap_sockaddr *sockaddr; + + /* + * Send only IPv4 and IPv6 addresses over the wire. + */ + switch (address->addr->sa_family) + { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + sockaddr = (struct rpcap_sockaddr *) &sendbuf[sendbufidx]; + if (sock_bufferize(NULL, sizeof(struct rpcap_sockaddr), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + daemon_seraddr((struct sockaddr_storage *) address->addr, sockaddr); + + sockaddr = (struct rpcap_sockaddr *) &sendbuf[sendbufidx]; + if (sock_bufferize(NULL, sizeof(struct rpcap_sockaddr), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + daemon_seraddr((struct sockaddr_storage *) address->netmask, sockaddr); + + sockaddr = (struct rpcap_sockaddr *) &sendbuf[sendbufidx]; + if (sock_bufferize(NULL, sizeof(struct rpcap_sockaddr), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + daemon_seraddr((struct sockaddr_storage *) address->broadaddr, sockaddr); + + sockaddr = (struct rpcap_sockaddr *) &sendbuf[sendbufidx]; + if (sock_bufferize(NULL, sizeof(struct rpcap_sockaddr), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + daemon_seraddr((struct sockaddr_storage *) address->dstaddr, sockaddr); + break; + + default: + break; + } + } + } + + // We no longer need the device list. Free it. + pcap_freealldevs(alldevs); + + // Send a final command that says "now send it!" + if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + return 0; + +error: + if (alldevs) + pcap_freealldevs(alldevs); + + if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_FINDALLIF, + errmsgbuf, errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + return 0; +} + +/* + \param plen: the length of the current message (needed in order to be able + to discard excess data in the message, if present) +*/ +static int +daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, + char *source, size_t sourcelen) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client + pcap_t *fp; // pcap_t main variable + int nread; + char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered + int sendbufidx = 0; // index which keeps the number of bytes currently buffered + struct rpcap_openreply *openreply; // open reply message + + if (plen > sourcelen - 1) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Source string too long"); + goto error; + } + + nread = sock_recv(pars->sockctrl, source, plen, + SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); + if (nread == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + return -1; + } + source[nread] = '\0'; + plen -= nread; + + // Is this a URL rather than a device? + // If so, reject it. + if (is_url(source)) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Source string refers to a remote device"); + goto error; + } + + // Open the selected device + // This is a fake open, since we do that only to get the needed parameters, then we close the device again + if ((fp = pcap_open_live(source, + 1500 /* fake snaplen */, + 0 /* no promis */, + 1000 /* fake timeout */, + errmsgbuf)) == NULL) + goto error; + + // Now, I can send a RPCAP open reply message + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, ver, + RPCAP_MSG_OPEN_REPLY, 0, sizeof(struct rpcap_openreply)); + + openreply = (struct rpcap_openreply *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_openreply), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + memset(openreply, 0, sizeof(struct rpcap_openreply)); + openreply->linktype = htonl(pcap_datalink(fp)); + openreply->tzoff = 0; /* This is always 0 for live captures */ + + // We're done with the pcap_t. + pcap_close(fp); + + // Send the reply. + if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + return 0; + +error: + if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_OPEN, + errmsgbuf, errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + return -1; + } + return 0; +} + +/* + \param plen: the length of the current message (needed in order to be able + to discard excess data in the message, if present) +*/ +static int +daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, + char *source, struct session **sessionp, + struct rpcap_sampling *samp_param _U_) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client + char portdata[PCAP_BUF_SIZE]; // temp variable needed to derive the data port + char peerhost[PCAP_BUF_SIZE]; // temp variable needed to derive the host name of our peer + struct session *session = NULL; // saves state of session + int status; + char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered + int sendbufidx = 0; // index which keeps the number of bytes currently buffered + + // socket-related variables + struct addrinfo hints; // temp, needed to open a socket connection + struct addrinfo *addrinfo; // temp, needed to open a socket connection + struct sockaddr_storage saddr; // temp, needed to retrieve the network data port chosen on the local machine + socklen_t saddrlen; // temp, needed to retrieve the network data port chosen on the local machine + int ret; // return value from functions + + // RPCAP-related variables + struct rpcap_startcapreq startcapreq; // start capture request message + struct rpcap_startcapreply *startcapreply; // start capture reply message + int serveropen_dp; // keeps who is going to open the data connection + + addrinfo = NULL; + + status = rpcapd_recv(pars->sockctrl, (char *) &startcapreq, + sizeof(struct rpcap_startcapreq), &plen, errmsgbuf); + if (status == -1) + { + goto fatal_error; + } + if (status == -2) + { + goto error; + } + + startcapreq.flags = ntohs(startcapreq.flags); + + // Create a session structure + session = malloc(sizeof(struct session)); + if (session == NULL) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Can't allocate session structure"); + goto error; + } + + session->sockdata = INVALID_SOCKET; + // We don't have a thread yet. + session->have_thread = 0; + // + // We *shouldn't* have to initialize the thread indicator + // itself, because the compiler *should* realize that we + // only use this if have_thread isn't 0, but we *do* have + // to do it, because not all compilers *do* realize that. + // + // There is no "invalid thread handle" value for a UN*X + // pthread_t, so we just zero it out. + // +#ifdef _WIN32 + session->thread = INVALID_HANDLE_VALUE; +#else + memset(&session->thread, 0, sizeof(session->thread)); +#endif + + // Open the selected device + if ((session->fp = pcap_open_live(source, + ntohl(startcapreq.snaplen), + (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_PROMISC) ? 1 : 0 /* local device, other flags not needed */, + ntohl(startcapreq.read_timeout), + errmsgbuf)) == NULL) + goto error; + +#if 0 + // Apply sampling parameters + fp->rmt_samp.method = samp_param->method; + fp->rmt_samp.value = samp_param->value; +#endif + + /* + We're in active mode if: + - we're using TCP, and the user wants us to be in active mode + - we're using UDP + */ + serveropen_dp = (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_SERVEROPEN) || (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM) || pars->isactive; + + /* + Gets the sockaddr structure referred to the other peer in the ctrl connection + + We need that because: + - if we're in passive mode, we need to know the address family we want to use + (the same used for the ctrl socket) + - if we're in active mode, we need to know the network address of the other host + we want to connect to + */ + saddrlen = sizeof(struct sockaddr_storage); + if (getpeername(pars->sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE); + goto error; + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_socktype = (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM) ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_family = saddr.ss_family; + + // Now we have to create a new socket to send packets + if (serveropen_dp) // Data connection is opened by the server toward the client + { + pcap_snprintf(portdata, sizeof portdata, "%d", ntohs(startcapreq.portdata)); + + // Get the name of the other peer (needed to connect to that specific network address) + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peerhost, + sizeof(peerhost), NULL, 0, NI_NUMERICHOST)) + { + sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE); + goto error; + } + + if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + goto error; + } + else // Data connection is opened by the client toward the server + { + hints.ai_flags = AI_PASSIVE; + + // Let's the server socket pick up a free network port for us + if (sock_initaddress(NULL, "0", &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + goto error; + + // get the complete sockaddr structure used in the data connection + saddrlen = sizeof(struct sockaddr_storage); + if (getsockname(session->sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) + { + sock_geterror("getsockname()", errmsgbuf, PCAP_ERRBUF_SIZE); + goto error; + } + + // Get the local port the system picked up + if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, + 0, portdata, sizeof(portdata), NI_NUMERICSERV)) + { + sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE); + goto error; + } + } + + // addrinfo is no longer used + freeaddrinfo(addrinfo); + addrinfo = NULL; + + // Needed to send an error on the ctrl connection + session->sockctrl = pars->sockctrl; + session->protocol_version = ver; + + // Now I can set the filter + ret = daemon_unpackapplyfilter(pars->sockctrl, session, &plen, errmsgbuf); + if (ret == -1) + { + // Fatal error. A message has been logged; just give up. + goto fatal_error; + } + if (ret == -2) + { + // Non-fatal error. Send an error message to the client. + goto error; + } + + // Now, I can send a RPCAP start capture reply message + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, + RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, ver, + RPCAP_MSG_STARTCAP_REPLY, 0, sizeof(struct rpcap_startcapreply)); + + startcapreply = (struct rpcap_startcapreply *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_startcapreply), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + memset(startcapreply, 0, sizeof(struct rpcap_startcapreply)); + startcapreply->bufsize = htonl(pcap_bufsize(session->fp)); + + if (!serveropen_dp) + { + unsigned short port = (unsigned short)strtoul(portdata,NULL,10); + startcapreply->portdata = htons(port); + } + + if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto fatal_error; + } + + if (!serveropen_dp) + { + SOCKET socktemp; // We need another socket, since we're going to accept() a connection + + // Connection creation + saddrlen = sizeof(struct sockaddr_storage); + + socktemp = accept(session->sockdata, (struct sockaddr *) &saddr, &saddrlen); + + if (socktemp == INVALID_SOCKET) + { + sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "Accept of data connection failed: %s", + errbuf); + goto error; + } + + // Now that I accepted the connection, the server socket is no longer needed + sock_close(session->sockdata, NULL, 0); + session->sockdata = socktemp; + } + + // Now we have to create a new thread to receive packets +#ifdef _WIN32 + session->thread = (HANDLE)_beginthreadex(NULL, 0, daemon_thrdatamain, + (void *) session, 0, NULL); + if (session->thread == 0) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread"); + goto error; + } +#else + ret = pthread_create(&session->thread, NULL, daemon_thrdatamain, + (void *) session); + if (ret != 0) + { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + ret, "Error creating the data thread"); + goto error; + } +#endif + session->have_thread = 1; + + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + goto fatal_error; + + *sessionp = session; + return 0; + +error: + // + // Not a fatal error, so send the client an error message and + // keep serving client requests. + // + *sessionp = NULL; + + if (addrinfo) + freeaddrinfo(addrinfo); + + if (session) + { + session_close(session); + free(session); + } + + if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_STARTCAPTURE, + errmsgbuf, errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + // Network error. + return -1; + } + + return 0; + +fatal_error: + // + // Fatal network error, so don't try to communicate with + // the client, just give up. + // + *sessionp = NULL; + + if (session) + { + session_close(session); + free(session); + } + + return -1; +} + +static int +daemon_msg_endcap_req(uint8 ver, struct daemon_slpars *pars, + struct session *session) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + struct rpcap_header header; + + session_close(session); + + rpcap_createhdr(&header, ver, RPCAP_MSG_ENDCAP_REPLY, 0, 0); + + if (sock_send(pars->sockctrl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + return 0; +} + +// +// We impose a limit on the filter program size, so that, on Windows, +// where there's only one server process with multiple threads, it's +// harder to eat all the server address space by sending larger filter +// programs. (This isn't an issue on UN*X, where there are multiple +// server processes, one per client connection.) +// +// We pick a value that limits each filter to 64K; that value is twice +// the in-kernel limit for Linux and 16 times the in-kernel limit for +// *BSD and macOS. +// +// It also prevents an overflow on 32-bit platforms when calculating +// the total size of the filter program. (It's not an issue on 64-bit +// platforms with a 64-bit size_t, as the filter size is 32 bits.) +// +#define RPCAP_BPF_MAXINSNS 8192 + +static int +daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp, char *errmsgbuf) +{ + int status; + struct rpcap_filter filter; + struct rpcap_filterbpf_insn insn; + struct bpf_insn *bf_insn; + struct bpf_program bf_prog; + unsigned int i; + + status = rpcapd_recv(sockctrl, (char *) &filter, + sizeof(struct rpcap_filter), plenp, errmsgbuf); + if (status == -1) + { + return -1; + } + if (status == -2) + { + return -2; + } + + bf_prog.bf_len = ntohl(filter.nitems); + + if (ntohs(filter.filtertype) != RPCAP_UPDATEFILTER_BPF) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Only BPF/NPF filters are currently supported"); + return -2; + } + + if (bf_prog.bf_len > RPCAP_BPF_MAXINSNS) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, + "Filter program is larger than the maximum size of %u instructions", + RPCAP_BPF_MAXINSNS); + return -2; + } + bf_insn = (struct bpf_insn *) malloc (sizeof(struct bpf_insn) * bf_prog.bf_len); + if (bf_insn == NULL) + { + pcap_fmt_errmsg_for_errno(errmsgbuf, PCAP_ERRBUF_SIZE, + errno, "malloc() failed"); + return -2; + } + + bf_prog.bf_insns = bf_insn; + + for (i = 0; i < bf_prog.bf_len; i++) + { + status = rpcapd_recv(sockctrl, (char *) &insn, + sizeof(struct rpcap_filterbpf_insn), plenp, errmsgbuf); + if (status == -1) + { + return -1; + } + if (status == -2) + { + return -2; + } + + bf_insn->code = ntohs(insn.code); + bf_insn->jf = insn.jf; + bf_insn->jt = insn.jt; + bf_insn->k = ntohl(insn.k); + + bf_insn++; + } + + if (bpf_validate(bf_prog.bf_insns, bf_prog.bf_len) == 0) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "The filter contains bogus instructions"); + return -2; + } + + if (pcap_setfilter(session->fp, &bf_prog)) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "RPCAP error: %s", pcap_geterr(session->fp)); + return -2; + } + + return 0; +} + +static int +daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, + struct session *session, uint32 plen) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client + int ret; // status of daemon_unpackapplyfilter() + struct rpcap_header header; // keeps the answer to the updatefilter command + + ret = daemon_unpackapplyfilter(pars->sockctrl, session, &plen, errmsgbuf); + if (ret == -1) + { + // Fatal error. A message has been logged; just give up. + return -1; + } + if (ret == -2) + { + // Non-fatal error. Send an error reply to the client. + goto error; + } + + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + // Network error. + return -1; + } + + // A response is needed, otherwise the other host does not know that everything went well + rpcap_createhdr(&header, ver, RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0); + + if (sock_send(pars->sockctrl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) + { + // That failed; log a messsage and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + return 0; + +error: + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + return -1; + } + rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_UPDATEFILTER, + errmsgbuf, NULL); + + return 0; +} + +/*! + \brief Received the sampling parameters from remote host and it stores in the pcap_t structure. +*/ +static int +daemon_msg_setsampling_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, + struct rpcap_sampling *samp_param) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + char errmsgbuf[PCAP_ERRBUF_SIZE]; + struct rpcap_header header; + struct rpcap_sampling rpcap_samp; + int status; + + status = rpcapd_recv(pars->sockctrl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); + if (status == -1) + { + return -1; + } + if (status == -2) + { + goto error; + } + + // Save these settings in the pcap_t + samp_param->method = rpcap_samp.method; + samp_param->value = ntohl(rpcap_samp.value); + + // A response is needed, otherwise the other host does not know that everything went well + rpcap_createhdr(&header, ver, RPCAP_MSG_SETSAMPLING_REPLY, 0, 0); + + if (sock_send(pars->sockctrl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // That failed; log a messsage and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + return -1; + } + + return 0; + +error: + if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_SETSAMPLING, + errmsgbuf, errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + // Check if all the data has been read; if not, discard the data in excess + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + return -1; + } + + return 0; +} + +static int +daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, + struct session *session, uint32 plen, struct pcap_stat *stats, + unsigned int svrcapt) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client + char sendbuf[RPCAP_NETBUF_SIZE]; // temporary buffer in which data to be sent is buffered + int sendbufidx = 0; // index which keeps the number of bytes currently buffered + struct rpcap_stats *netstats; // statistics sent on the network + + // Checks that the header does not contain other data; if so, discard it + if (rpcapd_discard(pars->sockctrl, plen) == -1) + { + // Network error. + return -1; + } + + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + rpcap_createhdr((struct rpcap_header *) sendbuf, ver, + RPCAP_MSG_STATS_REPLY, 0, (uint16) sizeof(struct rpcap_stats)); + + netstats = (struct rpcap_stats *) &sendbuf[sendbufidx]; + + if (sock_bufferize(NULL, sizeof(struct rpcap_stats), NULL, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + goto error; + + if (session && session->fp) + { + if (pcap_stats(session->fp, stats) == -1) + { + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "%s", pcap_geterr(session->fp)); + goto error; + } + + netstats->ifdrop = htonl(stats->ps_ifdrop); + netstats->ifrecv = htonl(stats->ps_recv); + netstats->krnldrop = htonl(stats->ps_drop); + netstats->svrcapt = htonl(session->TotCapt); + } + else + { + // We have to keep compatibility with old applications, + // which ask for statistics also when the capture has + // already stopped. + netstats->ifdrop = htonl(stats->ps_ifdrop); + netstats->ifrecv = htonl(stats->ps_recv); + netstats->krnldrop = htonl(stats->ps_drop); + netstats->svrcapt = htonl(svrcapt); + } + + // Send the packet + if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + return -1; + } + + return 0; + +error: + rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_GETSTATS, + errmsgbuf, NULL); + return 0; +} + +#ifdef _WIN32 +static unsigned __stdcall +#else +static void * +#endif +daemon_thrdatamain(void *ptr) +{ + char errbuf[PCAP_ERRBUF_SIZE + 1]; // error buffer + struct session *session; // pointer to the struct session for this session + int retval; // general variable used to keep the return value of other functions + struct rpcap_pkthdr *net_pkt_header;// header of the packet + struct pcap_pkthdr *pkt_header; // pointer to the buffer that contains the header of the current packet + u_char *pkt_data; // pointer to the buffer that contains the current packet + size_t sendbufsize; // size for the send buffer + char *sendbuf; // temporary buffer in which data to be sent is buffered + int sendbufidx; // index which keeps the number of bytes currently buffered + int status; +#ifndef _WIN32 + sigset_t sigusr1; // signal set with just SIGUSR1 +#endif + + session = (struct session *) ptr; + + session->TotCapt = 0; // counter which is incremented each time a packet is received + + // Initialize errbuf + memset(errbuf, 0, sizeof(errbuf)); + + // + // We need a buffer large enough to hold a buffer large enough + // for a maximum-size packet for this pcap_t. + // + if (pcap_snapshot(session->fp) < 0) + { + // + // The snapshot length is negative. + // This "should not happen". + // + rpcapd_log(LOGPRIO_ERROR, + "Unable to allocate the buffer for this child thread: snapshot length of %d is negative", + pcap_snapshot(session->fp)); + sendbuf = NULL; // we can't allocate a buffer, so nothing to free + goto error; + } + // + // size_t is unsigned, and the result of pcap_snapshot() is signed; + // on no platform that we support is int larger than size_t. + // This means that, unless the extra information we prepend to + // a maximum-sized packet is impossibly large, the sum of the + // snapshot length and the size of that extra information will + // fit in a size_t. + // + // So we don't need to make sure that sendbufsize will overflow. + // + sendbufsize = sizeof(struct rpcap_header) + sizeof(struct rpcap_pkthdr) + pcap_snapshot(session->fp); + sendbuf = (char *) malloc (sendbufsize); + if (sendbuf == NULL) + { + rpcapd_log(LOGPRIO_ERROR, + "Unable to allocate the buffer for this child thread"); + goto error; + } + +#ifndef _WIN32 + // + // Set the signal set to include just SIGUSR1, and block that + // signal; we only want it unblocked when we're reading + // packets - we dn't want any other system calls, such as + // ones being used to send to the client or to log messages, + // to be interrupted. + // + sigemptyset(&sigusr1); + sigaddset(&sigusr1, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &sigusr1, NULL); +#endif + + // Retrieve the packets + for (;;) + { +#ifndef _WIN32 + // + // Unblock SIGUSR1 while we might be waiting for packets. + // + pthread_sigmask(SIG_UNBLOCK, &sigusr1, NULL); +#endif + retval = pcap_next_ex(session->fp, &pkt_header, (const u_char **) &pkt_data); // cast to avoid a compiler warning +#ifndef _WIN32 + // + // Now block it again. + // + pthread_sigmask(SIG_BLOCK, &sigusr1, NULL); +#endif + if (retval < 0) + break; // error + if (retval == 0) // Read timeout elapsed + continue; + + sendbufidx = 0; + + // Bufferize the general header + if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, + &sendbufidx, sendbufsize, SOCKBUF_CHECKONLY, errbuf, + PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, + "sock_bufferize() error sending packet message: %s", + errbuf); + goto error; + } + + rpcap_createhdr((struct rpcap_header *) sendbuf, + session->protocol_version, RPCAP_MSG_PACKET, 0, + (uint16) (sizeof(struct rpcap_pkthdr) + pkt_header->caplen)); + + net_pkt_header = (struct rpcap_pkthdr *) &sendbuf[sendbufidx]; + + // Bufferize the pkt header + if (sock_bufferize(NULL, sizeof(struct rpcap_pkthdr), NULL, + &sendbufidx, sendbufsize, SOCKBUF_CHECKONLY, errbuf, + PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, + "sock_bufferize() error sending packet message: %s", + errbuf); + goto error; + } + + net_pkt_header->caplen = htonl(pkt_header->caplen); + net_pkt_header->len = htonl(pkt_header->len); + net_pkt_header->npkt = htonl(++(session->TotCapt)); + net_pkt_header->timestamp_sec = htonl(pkt_header->ts.tv_sec); + net_pkt_header->timestamp_usec = htonl(pkt_header->ts.tv_usec); + + // Bufferize the pkt data + if (sock_bufferize((char *) pkt_data, pkt_header->caplen, + sendbuf, &sendbufidx, sendbufsize, SOCKBUF_BUFFERIZE, + errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, + "sock_bufferize() error sending packet message: %s", + errbuf); + goto error; + } + + // Send the packet + // If the client dropped the connection, don't report an + // error, just quit. + status = sock_send(session->sockdata, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); + if (status < 0) + { + if (status == -1) + { + // + // Error other than "client closed the + // connection out from under us"; report + // it. + // + rpcapd_log(LOGPRIO_ERROR, + "Send of packet to client failed: %s", + errbuf); + } + + // + // Give up in either case. + // + goto error; + } + } + + if (retval < 0 && retval != PCAP_ERROR_BREAK) + { + // + // Failed with an error other than "we were told to break + // out of the loop". + // + // The latter just means that the client told us to stop + // capturing, so there's no error to report. + // + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp)); + rpcap_senderror(session->sockctrl, session->protocol_version, + PCAP_ERR_READEX, errbuf, NULL); + } + +error: + // + // The main thread will clean up the session structure. + // + free(sendbuf); + + return 0; +} + +#ifndef _WIN32 +// +// Do-nothing handler for SIGUSR1; the sole purpose of SIGUSR1 is to +// interrupt the data thread if it's blocked in a system call waiting +// for packets to arrive. +// +static void noop_handler(int sign _U_) +{ +} +#endif + +/*! + \brief It serializes a network address. + + It accepts a 'sockaddr_storage' structure as input, and it converts it appropriately into a format + that can be used to be sent on the network. Basically, it applies all the hton() + conversion required to the input variable. + + \param sockaddrin a 'sockaddr_storage' pointer to the variable that has to be + serialized. This variable can be both a 'sockaddr_in' and 'sockaddr_in6'. + + \param sockaddrout an 'rpcap_sockaddr' pointer to the variable that will contain + the serialized data. This variable has to be allocated by the user. + + \warning This function supports only AF_INET and AF_INET6 address families. +*/ +static void +daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *sockaddrout) +{ + memset(sockaddrout, 0, sizeof(struct sockaddr_storage)); + + // There can be the case in which the sockaddrin is not available + if (sockaddrin == NULL) return; + + // Warning: we support only AF_INET and AF_INET6 + switch (sockaddrin->ss_family) + { + case AF_INET: + { + struct sockaddr_in *sockaddrin_ipv4; + struct rpcap_sockaddr_in *sockaddrout_ipv4; + + sockaddrin_ipv4 = (struct sockaddr_in *) sockaddrin; + sockaddrout_ipv4 = (struct rpcap_sockaddr_in *) sockaddrout; + sockaddrout_ipv4->family = htons(RPCAP_AF_INET); + sockaddrout_ipv4->port = htons(sockaddrin_ipv4->sin_port); + memcpy(&sockaddrout_ipv4->addr, &sockaddrin_ipv4->sin_addr, sizeof(sockaddrout_ipv4->addr)); + memset(sockaddrout_ipv4->zero, 0, sizeof(sockaddrout_ipv4->zero)); + break; + } + +#ifdef AF_INET6 + case AF_INET6: + { + struct sockaddr_in6 *sockaddrin_ipv6; + struct rpcap_sockaddr_in6 *sockaddrout_ipv6; + + sockaddrin_ipv6 = (struct sockaddr_in6 *) sockaddrin; + sockaddrout_ipv6 = (struct rpcap_sockaddr_in6 *) sockaddrout; + sockaddrout_ipv6->family = htons(RPCAP_AF_INET6); + sockaddrout_ipv6->port = htons(sockaddrin_ipv6->sin6_port); + sockaddrout_ipv6->flowinfo = htonl(sockaddrin_ipv6->sin6_flowinfo); + memcpy(&sockaddrout_ipv6->addr, &sockaddrin_ipv6->sin6_addr, sizeof(sockaddrout_ipv6->addr)); + sockaddrout_ipv6->scope_id = htonl(sockaddrin_ipv6->sin6_scope_id); + break; + } +#endif + } +} + + +/*! + \brief Suspends a thread for secs seconds. +*/ +void sleep_secs(int secs) +{ +#ifdef _WIN32 + Sleep(secs*1000); +#else + unsigned secs_remaining; + + if (secs <= 0) + return; + secs_remaining = secs; + while (secs_remaining != 0) + secs_remaining = sleep(secs_remaining); +#endif +} + +/* + * Read the header of a message. + */ +static int +rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) +{ + int nread; + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + + nread = sock_recv(sock, (char *) headerp, sizeof(struct rpcap_header), + SOCK_RECEIVEALL_YES|SOCK_EOF_ISNT_ERROR, errbuf, PCAP_ERRBUF_SIZE); + if (nread == -1) + { + // Network error. + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + return -1; + } + if (nread == 0) + { + // Immediate EOF; that's treated like a close message. + return -2; + } + headerp->plen = ntohl(headerp->plen); + return 0; +} + +/* + * Read data from a message. + * If we're trying to read more data that remains, puts an error + * message into errmsgbuf and returns -2. Otherwise, tries to read + * the data and, if that succeeds, subtracts the amount read from + * the number of bytes of data that remains. + * Returns 0 on success, logs a message and returns -1 on a network + * error. + */ +static int +rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) +{ + int nread; + char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors + + if (toread > *plen) + { + // Tell the client and continue. + pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); + return -2; + } + nread = sock_recv(sock, buffer, toread, + SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); + if (nread == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + return -1; + } + *plen -= nread; + return 0; +} + +/* + * Discard data from a connection. + * Mostly used to discard wrong-sized messages. + * Returns 0 on success, logs a message and returns -1 on a network + * error. + */ +static int +rpcapd_discard(SOCKET sock, uint32 len) +{ + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + + if (len != 0) + { + if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // Network error. + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + return -1; + } + } + return 0; +} + +// +// Shut down any packet-capture thread associated with the session, +// close the SSL handle for the data socket if we have one, close +// the data socket if we have one, and close the underlying packet +// capture handle if we have one. +// +// We do not, of course, touch the controlling socket that's also +// copied into the session, as the service loop might still use it. +// +static void session_close(struct session *session) +{ + if (session->have_thread) + { + // + // Tell the data connection thread main capture loop to + // break out of that loop. + // + // This may be sufficient to wake up a blocked thread, + // but it's not guaranteed to be sufficient. + // + pcap_breakloop(session->fp); + +#ifdef _WIN32 + // + // Set the event on which a read would block, so that, + // if it's currently blocked waiting for packets to + // arrive, it'll wake up, so it can see the "break + // out of the loop" indication. (pcap_breakloop() + // might do this, but older versions don't. Setting + // it twice should, at worst, cause an extra wakeup, + // which shouldn't be a problem.) + // + // XXX - what about modules other than NPF? + // + SetEvent(pcap_getevent(session->fp)); + + // + // Wait for the thread to exit, so we don't close + // sockets out from under it. + // + // XXX - have a timeout, so we don't wait forever? + // + WaitForSingleObject(session->thread, INFINITE); + + // + // Release the thread handle, as we're done with + // it. + // + CloseHandle(session->thread); + session->have_thread = 0; + session->thread = INVALID_HANDLE_VALUE; +#else + // + // Send a SIGUSR1 signal to the thread, so that, if + // it's currently blocked waiting for packets to arrive, + // it'll wake up (we've turned off SA_RESTART for + // SIGUSR1, so that the system call in which it's blocked + // should return EINTR rather than restarting). + // + pthread_kill(session->thread, SIGUSR1); + + // + // Wait for the thread to exit, so we don't close + // sockets out from under it. + // + // XXX - have a timeout, so we don't wait forever? + // + pthread_join(session->thread, NULL); + session->have_thread = 0; + memset(&session->thread, 0, sizeof(session->thread)); +#endif + } + + if (session->sockdata != INVALID_SOCKET) + { + sock_close(session->sockdata, NULL, 0); + session->sockdata = INVALID_SOCKET; + } + + if (session->fp) + { + pcap_close(session->fp); + session->fp = NULL; + } +} + +// +// Check whether a capture source string is a URL or not. +// This includes URLs that refer to a local device; a scheme, followed +// by ://, followed by *another* scheme and ://, is just silly, and +// anybody who supplies that will get an error. +// +static int +is_url(const char *source) +{ + char *colonp; + + /* + * RFC 3986 says: + * + * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + * + * hier-part = "//" authority path-abempty + * / path-absolute + * / path-rootless + * / path-empty + * + * authority = [ userinfo "@" ] host [ ":" port ] + * + * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + * + * Step 1: look for the ":" at the end of the scheme. + * A colon in the source is *NOT* sufficient to indicate that + * this is a URL, as interface names on some platforms might + * include colons (e.g., I think some Solaris interfaces + * might). + */ + colonp = strchr(source, ':'); + if (colonp == NULL) + { + /* + * The source is the device to open. It's not a URL. + */ + return (0); + } + + /* + * All schemes must have "//" after them, i.e. we only support + * hier-part = "//" authority path-abempty, not + * hier-part = path-absolute + * hier-part = path-rootless + * hier-part = path-empty + * + * We need that in order to distinguish between a local device + * name that happens to contain a colon and a URI. + */ + if (strncmp(colonp + 1, "//", 2) != 0) + { + /* + * The source is the device to open. It's not a URL. + */ + return (0); + } + + /* + * It's a URL. + */ + return (1); +} diff --git a/lib/libpcap/libpcap/rpcapd/daemon.h b/lib/libpcap/libpcap/rpcapd/daemon.h new file mode 100644 index 0000000..74e17da --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/daemon.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __DAEMON_H__ +#define __DAEMON_H__ + +// +// Returns 1 if the client closed the control connection explicitly, 0 +// otherwise; the return value is used only by callers that call us +// for active mode. +// +int daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, + int nullAuthAllowed); + +void sleep_secs(int secs); + +#endif diff --git a/lib/libpcap/libpcap/rpcapd/fileconf.c b/lib/libpcap/libpcap/rpcapd/fileconf.c new file mode 100644 index 0000000..2f15c01 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/fileconf.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ftmacros.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <signal.h> +#include <pcap.h> // for PCAP_ERRBUF_SIZE + +#include "portability.h" +#include "rpcapd.h" +#include "config_params.h" // configuration file parameters +#include "fileconf.h" +#include "rpcap-protocol.h" +#include "log.h" + +// +// Parameter names. +// +#define PARAM_ACTIVECLIENT "ActiveClient" +#define PARAM_PASSIVECLIENT "PassiveClient" +#define PARAM_NULLAUTHPERMIT "NullAuthPermit" + +static char *skipws(char *ptr); + +void fileconf_read(void) +{ + FILE *fp; + unsigned int num_active_clients; + + if ((fp = fopen(loadfile, "r")) != NULL) + { + char line[MAX_LINE + 1]; + unsigned int lineno; + + hostlist[0] = 0; + num_active_clients = 0; + lineno = 0; + + while (fgets(line, MAX_LINE, fp) != NULL) + { + size_t linelen; + char *ptr; + char *param; + size_t result; + size_t toklen; + + lineno++; + + linelen = strlen(line); + if (line[linelen - 1] != '\n') + { + int c; + + // + // Either the line doesn't fit in + // the buffer, or we got an EOF + // before the EOL. Assume it's the + // former. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u is longer than %u characters", + loadfile, lineno, MAX_LINE); + + // + // Eat characters until we get an NL. + // + while ((c = getc(fp)) != '\n') + { + if (c == EOF) + goto done; + } + + // + // Try the next line. + // + continue; + } + ptr = line; + + // + // Skip leading white space, if any. + // + ptr = skipws(ptr); + if (ptr == NULL) + { + // Blank line. + continue; + } + + // + // Is the next character a "#"? If so, this + // line is a comment; skip to the next line. + // + if (*ptr == '#') + continue; + + // + // Is the next character alphabetic? If not, + // this isn't a valid parameter name. + // + if (!isascii((unsigned char)*ptr) || + !isalpha((unsigned char)*ptr)) + { + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u doesn't have a valid parameter name", + loadfile, lineno); + continue; + } + + // + // Grab the first token, which is made of + // alphanumerics, underscores, and hyphens. + // That's the name of the parameter being set. + // + param = ptr; + while (isascii((unsigned char)*ptr) && + (isalnum((unsigned char)*ptr) || *ptr == '-' || *ptr == '_')) + ptr++; + + // + // Skip over white space, if any. + // + ptr = skipws(ptr); + if (ptr == NULL || *ptr != '=') + { + // + // We hit the end of the line before + // finding a non-white space character, + // or we found one but it's not an "=". + // That means there's no "=", so this + // line is invalid. Complain and skip + // this line. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a parameter but no =", + loadfile, lineno); + continue; + } + + // + // We found the '='; set it to '\0', and skip + // past it. + // + *ptr++ = '\0'; + + // + // Skip past any white space after the "=". + // + ptr = skipws(ptr); + if (ptr == NULL) + { + // + // The value is empty. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a parameter but no value", + loadfile, lineno); + continue; + } + + // + // OK, what parameter is this? + // + if (strcmp(param, PARAM_ACTIVECLIENT) == 0) { + // + // Add this to the list of active clients. + // + char *address, *port; + + // + // We can't have more than MAX_ACTIVE_LIST + // active clients. + // + if (num_active_clients >= MAX_ACTIVE_LIST) + { + // + // Too many entries for the active + // client list. Complain and + // ignore it. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an %s parameter, but we already have %u active clients", + loadfile, lineno, PARAM_ACTIVECLIENT, + MAX_ACTIVE_LIST); + continue; + } + + // + // Get the address. + // It's terminated by a host list separator + // *or* a #; there *shouldn't* be a #, as + // that starts a comment, and that would + // mean that we have no port. + // + address = ptr; + toklen = strcspn(ptr, RPCAP_HOSTLIST_SEP "#"); + ptr += toklen; // skip to the terminator + if (toklen == 0) + { + if (isascii((unsigned char)*ptr) && + (isspace((unsigned char)*ptr) || *ptr == '#' || *ptr == '\0')) + { + // + // The first character it saw + // was a whitespace character + // or a comment character. + // This means that there's + // no value. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a parameter but no value", + loadfile, lineno); + } + else + { + // + // This means that the first + // character it saw was a + // separator. This means that + // there's no address in the + // value, just a port. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an %s parameter with a value containing no address", + loadfile, lineno, PARAM_ACTIVECLIENT); + } + continue; + } + + // + // Null-terminate the address, and skip past + // it. + // + *ptr++ = '\0'; + + // + // Skip any white space following the + // separating character. + // + ptr = skipws(ptr); + if (ptr == NULL) + { + // + // The value is empty, so there's + // no port in the value. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an %s parameter with a value containing no port", + loadfile, lineno, PARAM_ACTIVECLIENT); + continue; + } + + // + // Get the port. + // We look for a white space character + // or a # as a terminator; the # introduces + // a comment that runs to the end of the + // line. + // + port = ptr; + toklen = strcspn(ptr, " \t#\r\n"); + ptr += toklen; + if (toklen == 0) + { + // + // The value is empty, so there's + // no port in the value. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an %s parameter with a value containing no port", + loadfile, lineno, PARAM_ACTIVECLIENT); + continue; + } + + // + // Null-terminate the port, and skip past + // it. + // + *ptr++ = '\0'; + result = pcap_strlcpy(activelist[num_active_clients].address, address, sizeof(activelist[num_active_clients].address)); + if (result >= sizeof(activelist[num_active_clients].address)) + { + // + // It didn't fit. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an %s parameter with an address with more than %u characters", + loadfile, lineno, PARAM_ACTIVECLIENT, + (unsigned int)(sizeof(activelist[num_active_clients].address) - 1)); + continue; + } + if (strcmp(port, "DEFAULT") == 0) // the user choose a custom port + result = pcap_strlcpy(activelist[num_active_clients].port, RPCAP_DEFAULT_NETPORT_ACTIVE, sizeof(activelist[num_active_clients].port)); + else + result = pcap_strlcpy(activelist[num_active_clients].port, port, sizeof(activelist[num_active_clients].port)); + if (result >= sizeof(activelist[num_active_clients].address)) + { + // + // It didn't fit. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an %s parameter with an port with more than %u characters", + loadfile, lineno, PARAM_ACTIVECLIENT, + (unsigned int)(sizeof(activelist[num_active_clients].port) - 1)); + continue; + } + + num_active_clients++; + } + else if (strcmp(param, PARAM_PASSIVECLIENT) == 0) + { + char *eos; + char *host; + + // + // Get the host. + // We look for a white space character + // or a # as a terminator; the # introduces + // a comment that runs to the end of the + // line. + // + host = ptr; + toklen = strcspn(ptr, " \t#\r\n"); + if (toklen == 0) + { + // + // The first character it saw + // was a whitespace character + // or a comment character. + // This means that there's + // no value. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a parameter but no value", + loadfile, lineno); + continue; + } + ptr += toklen; + *ptr++ = '\0'; + + // + // Append this to the host list. + // Save the curren end-of-string for the + // host list, in case the new host doesn't + // fit, so that we can discard the partially- + // copied host name. + // + eos = hostlist + strlen(hostlist); + if (eos != hostlist) + { + // + // The list is not empty, so prepend + // a comma before adding this host. + // + result = pcap_strlcat(hostlist, ",", sizeof(hostlist)); + if (result >= sizeof(hostlist)) + { + // + // It didn't fit. Discard + // the comma (which wasn't + // added, but...), complain, + // and ignore this line. + // + *eos = '\0'; + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a %s parameter with a host name that doesn't fit", + loadfile, lineno, PARAM_PASSIVECLIENT); + continue; + } + } + result = pcap_strlcat(hostlist, host, sizeof(hostlist)); + if (result >= sizeof(hostlist)) + { + // + // It didn't fit. Discard the comma, + // complain, and ignore this line. + // + *eos = '\0'; + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a %s parameter with a host name that doesn't fit", + loadfile, lineno, PARAM_PASSIVECLIENT); + continue; + } + } + else if (strcmp(param, PARAM_NULLAUTHPERMIT) == 0) + { + char *setting; + + // + // Get the setting. + // We look for a white space character + // or a # as a terminator; the # introduces + // a comment that runs to the end of the + // line. + // + setting = ptr; + toklen = strcspn(ptr, " \t#\r\n"); + ptr += toklen; + if (toklen == 0) + { + // + // The first character it saw + // was a whitespace character + // or a comment character. + // This means that there's + // no value. + // + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has a parameter but no value", + loadfile, lineno); + continue; + } + *ptr++ = '\0'; + + // + // XXX - should we complain if it's + // neither "yes" nor "no"? + // + if (strcmp(setting, "YES") == 0) + nullAuthAllowed = 1; + else + nullAuthAllowed = 0; + } + else + { + rpcapd_log(LOGPRIO_ERROR, + "%s, line %u has an unknown parameter %s", + loadfile, lineno, param); + continue; + } + } + +done: + // clear the remaining fields of the active list + for (int i = num_active_clients; i < MAX_ACTIVE_LIST; i++) + { + activelist[i].address[0] = 0; + activelist[i].port[0] = 0; + num_active_clients++; + } + + rpcapd_log(LOGPRIO_DEBUG, "New passive host list: %s", hostlist); + fclose(fp); + } +} + +int fileconf_save(const char *savefile) +{ + FILE *fp; + + if ((fp = fopen(savefile, "w")) != NULL) + { + char *token; /*, *port;*/ // temp, needed to separate items into the hostlist + char temphostlist[MAX_HOST_LIST + 1]; + int i = 0; + char *lasts; + + fprintf(fp, "# Configuration file help.\n\n"); + + // Save list of clients which are allowed to connect to us in passive mode + fprintf(fp, "# Hosts which are allowed to connect to this server (passive mode)\n"); + fprintf(fp, "# Format: PassiveClient = <name or address>\n\n"); + + strncpy(temphostlist, hostlist, MAX_HOST_LIST); + temphostlist[MAX_HOST_LIST] = 0; + + token = pcap_strtok_r(temphostlist, RPCAP_HOSTLIST_SEP, &lasts); + while(token != NULL) + { + fprintf(fp, "%s = %s\n", PARAM_PASSIVECLIENT, token); + token = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); + } + + + // Save list of clients which are allowed to connect to us in active mode + fprintf(fp, "\n\n"); + fprintf(fp, "# Hosts to which this server is trying to connect to (active mode)\n"); + fprintf(fp, "# Format: ActiveClient = <name or address>, <port | DEFAULT>\n\n"); + + + while ((i < MAX_ACTIVE_LIST) && (activelist[i].address[0] != 0)) + { + fprintf(fp, "%s = %s, %s\n", PARAM_ACTIVECLIENT, + activelist[i].address, activelist[i].port); + i++; + } + + // Save if we want to permit NULL authentication + fprintf(fp, "\n\n"); + fprintf(fp, "# Permit NULL authentication: YES or NO\n\n"); + + fprintf(fp, "%s = %s\n", PARAM_NULLAUTHPERMIT, + nullAuthAllowed ? "YES" : "NO"); + + fclose(fp); + return 0; + } + else + { + return -1; + } + +} + +// +// Skip over white space. +// If we hit a CR or LF, return NULL, otherwise return a pointer to +// the first non-white space character. Replace white space characters +// other than CR or LF with '\0', so that, if we're skipping white space +// after a token, the token is null-terminated. +// +static char *skipws(char *ptr) +{ + while (isascii((unsigned char)*ptr) && isspace((unsigned char)*ptr)) { + if (*ptr == '\r' || *ptr == '\n') + return NULL; + *ptr++ = '\0'; + } + return ptr; +} diff --git a/lib/libpcap/libpcap/rpcapd/fileconf.h b/lib/libpcap/libpcap/rpcapd/fileconf.h new file mode 100644 index 0000000..912dd32 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/fileconf.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __FILECONF_H__ +#define __FILECONF_H__ + +void fileconf_read(void); +int fileconf_save(const char *savefile); + +#endif diff --git a/lib/libpcap/libpcap/rpcapd/log.c b/lib/libpcap/libpcap/rpcapd/log.c new file mode 100644 index 0000000..7b5fee5 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/log.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1998 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <syslog.h> +#endif + +#include "portability.h" + +#include "log.h" + +static int log_to_systemlog; +static int log_debug_messages; + +static void rpcapd_vlog_stderr(log_priority, + PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(2, 0); + +static void rpcapd_vlog_stderr(log_priority priority, const char *message, va_list ap) +{ + const char *tag; + + /* + * Squelch warnings from compilers that *don't* assume that + * priority always has a valid enum value and therefore don't + * assume that we'll always go through one of the case arms. + * + * If we have a default case, compilers that *do* assume that + * will then complain about the default case code being + * unreachable. + * + * Damned if you do, damned if you don't. + */ + tag = ""; + + switch (priority) { + + case LOGPRIO_DEBUG: + tag = "DEBUG: "; + break; + + case LOGPRIO_INFO: + tag = ""; + break; + + case LOGPRIO_WARNING: + tag = "warning: "; + break; + + case LOGPRIO_ERROR: + tag = "error: "; + break; + } + + fprintf(stderr, "rpcapd: %s", tag); + vfprintf(stderr, message, ap); + putc('\n', stderr); +} + +static void rpcapd_vlog_systemlog(log_priority, + PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(2, 0); + +#ifdef _WIN32 +#define MESSAGE_SUBKEY \ + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\rpcapd" + +static void rpcapd_vlog_systemlog(log_priority priority, const char *message, + va_list ap) +{ +#if 0 + static int initialized = 0; + HKEY hey_handle; + static HANDLE log_handle; + WORD eventlog_type; + DWORD event_id; + char msgbuf[1024]; + char *strings[1]; + + if (!initialized) { + /* + * Register our message stuff in the Registry. + * + * First, create the registry key for us. If the key + * already exists, this succeeds and returns a handle + * for it. + */ + if (RegCreateKey(HKEY_LOCAL_MACHINE, MESSAGE_SUBKEY, + &key_handle) != ERROR_SUCCESS) { + /* + * Failed - give up and just log this message, + * and all subsequent messages, to the + * standard error. + */ + log_to_systemlog = 0; + initialized = 1; + rpcapd_vlog_stderr(priority, message, ap); + return; + } + log_handle = RegisterEventSource(NULL, "rpcapd"); + initialized = 1; + } + + switch (priority) { + + case LOGPRIO_DEBUG: + // + // XXX - what *should* we do about debug messages? + // + eventlog_type = EVENTLOG_INFORMATION_TYPE; + event_id = RPCAPD_INFO_ID; + break; + + case LOGPRIO_INFO: + eventlog_type = EVENTLOG_INFORMATION_TYPE; + event_id = RPCAPD_INFO_ID; + break; + + case LOGPRIO_WARNING: + eventlog_type = EVENTLOG_WARNING_TYPE; + event_id = RPCAPD_WARNING_ID; + break; + + case LOGPRIO_ERROR: + eventlog_type = EVENTLOG_ERROR_TYPE; + event_id = RPCAPD_ERROR_ID; + break; + + default: + /* Don't do this. */ + return; + } + + vsprintf(msgbuf, message, ap); + + strings[0] = msgbuf; + /* + * If this fails, how are we going to report it? + */ + (void) ReportEvent(log_handle, eventlog_type, 0, event_id, NULL, 1, 0, + strings, NULL); +#else + rpcapd_vlog_stderr(priority, message, ap); +#endif +} +#else +static void rpcapd_vlog_systemlog(log_priority priority, const char *message, + va_list ap) +{ + static int initialized = 0; + int syslog_priority; + + if (!initialized) { + // + // Open the log. + // + openlog("rpcapd", LOG_PID, LOG_DAEMON); + initialized = 1; + } + + switch (priority) { + + case LOGPRIO_DEBUG: + syslog_priority = LOG_DEBUG; + break; + + case LOGPRIO_INFO: + syslog_priority = LOG_INFO; + break; + + case LOGPRIO_WARNING: + syslog_priority = LOG_WARNING; + break; + + case LOGPRIO_ERROR: + syslog_priority = LOG_ERR; + break; + + default: + /* Don't do this. */ + return; + } + +#ifdef HAVE_VSYSLOG + vsyslog(syslog_priority, message, ap); +#else + /* + * Thanks, IBM, for not providing vsyslog() in AIX! + * + * They also warn that the syslog functions shouldn't + * be used in multithreaded programs, but the only thing + * obvious that seems to make the syslog_r functions + * better is that they have an additional argument + * that points to the information that's static to + * the syslog code in non-thread-safe versions. Most + * of that data is set by openlog(); since we already + * do an openlog before doing logging, and don't + * change that data afterwards, I suspect that, in + * practice, the regular syslog routines are OK for + * us (especially given that we'd end up having one + * static struct syslog_data anyway, which means we'd + * just be like the non-thread-safe version). + */ + char logbuf[1024+1]; + + pcap_vsnprintf(logbuf, sizeof logbuf, message, ap); + syslog(syslog_priority, "%s", logbuf); +#endif +} +#endif + +void rpcapd_log_set(int log_to_systemlog_arg, int log_debug_messages_arg) +{ + log_debug_messages = log_debug_messages_arg; + log_to_systemlog = log_to_systemlog_arg; +} + +void rpcapd_log(log_priority priority, const char *message, ...) +{ + va_list ap; + + if (priority != LOGPRIO_DEBUG || log_debug_messages) { + va_start(ap, message); + if (log_to_systemlog) + { + rpcapd_vlog_systemlog(priority, message, ap); + } + else + { + rpcapd_vlog_stderr(priority, message, ap); + } + va_end(ap); + } +} diff --git a/lib/libpcap/libpcap/rpcapd/log.h b/lib/libpcap/libpcap/rpcapd/log.h new file mode 100644 index 0000000..28a6cee --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/log.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1998 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "pcap/funcattrs.h" + +extern void rpcapd_log_set(int, int); + +typedef enum { + LOGPRIO_DEBUG, + LOGPRIO_INFO, + LOGPRIO_WARNING, + LOGPRIO_ERROR +} log_priority; + +extern void rpcapd_log(log_priority priority, + PCAP_FORMAT_STRING(const char *message), ...) PCAP_PRINTFLIKE(2, 3); diff --git a/lib/libpcap/libpcap/rpcapd/org.tcpdump.rpcapd.plist b/lib/libpcap/libpcap/rpcapd/org.tcpdump.rpcapd.plist new file mode 100644 index 0000000..db3223a --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/org.tcpdump.rpcapd.plist @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Disabled</key> + <false/> + <key>Label</key> + <string>com.tcpdump.rpcapd</string> + <key>Program</key> + <string>/usr/local/libexec/rpcapd</string> + <key>ProgramArguments</key> + <array> + <string>/usr/local/libexec/rpcapd</string> + <string>-i</string> + </array> + <key>Sockets</key> + <dict> + <key>Listeners</key> + <dict> + <key>SockServiceName</key> + <string>2002</string> + </dict> + </dict> + <key>inetdCompatibility</key> + <dict> + <key>Wait</key> + <false/> + </dict> +</dict> +</plist> diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd-config.manfile b/lib/libpcap/libpcap/rpcapd/rpcapd-config.manfile new file mode 100644 index 0000000..c451420 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd-config.manfile @@ -0,0 +1,78 @@ +.\" Copyright (c) 1994, 1996, 1997 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that: (1) source code distributions +.\" retain the above copyright notice and this paragraph in its entirety, (2) +.\" distributions including binary code include the above copyright notice and +.\" this paragraph in its entirety in the documentation or other materials +.\" provided with the distribution, and (3) all advertising materials mentioning +.\" features or use of this software display the following acknowledgement: +.\" ``This product includes software developed by the University of California, +.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +.\" the University nor the names of its contributors may be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.TH RPCAPD-CONFIG 5 "6 January 2019" +.SH NAME +rpcapd-config \- rpcapd configuration file format +.SH DESCRIPTION +An +.B rpcapd +configuration file allows parameters to be set for +.BR rpcapd (8). +.LP +A # introduces a comment that runs to the end of the line. Blank lines, +and lines with only a comment, are ignored. Leading and trailing white +space on a line are also ignored. +.LP +Lines that set a parameter are of the form +.IP +\fIparameter\fB=\fIvalue\fR +.LP +Whitespace preceding or following the = is ignored. +.LP +The +.IR parameter s +are: +.TP +.B ActiveClient +.I value +is a host name or IP addresse, followed by a comma, +semicolon, or space, followed by a port name and address or +.BR DEFAULT . +.B DEFAULT +specifies the default active mode port for rpcapd, port 2003. +Each +.B ActiveClient +line adds the host and port to the list of clients to which the server +should connect in active mode. +.TP +.B PassiveClient +.I value +is a host name or IP addresse, followed by a comma, +semicolon, or space, followed by a port name and address or +.BR DEFAULT . +.B DEFAULT +specifies the default passive mode port for rpcapd, port 2002. +Each +.B PassiveClient +line adds the host and port to the list of clients addresses and ports +that are allowed to connect to the server in passive mode. +.TP +.B NullAuthPermit +.I value +is either +.B YES +or +.BR NO . +.B YES +means that null authentication is permitted; +.B No +means that it is not permitted. +.SH SEE ALSO +rpcapd(8) diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd-config.manfile.in b/lib/libpcap/libpcap/rpcapd/rpcapd-config.manfile.in new file mode 100644 index 0000000..1a87529 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd-config.manfile.in @@ -0,0 +1,78 @@ +.\" Copyright (c) 1994, 1996, 1997 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that: (1) source code distributions +.\" retain the above copyright notice and this paragraph in its entirety, (2) +.\" distributions including binary code include the above copyright notice and +.\" this paragraph in its entirety in the documentation or other materials +.\" provided with the distribution, and (3) all advertising materials mentioning +.\" features or use of this software display the following acknowledgement: +.\" ``This product includes software developed by the University of California, +.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +.\" the University nor the names of its contributors may be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.TH RPCAPD-CONFIG @MAN_FILE_FORMATS@ "6 January 2019" +.SH NAME +rpcapd-config \- rpcapd configuration file format +.SH DESCRIPTION +An +.B rpcapd +configuration file allows parameters to be set for +.BR rpcapd (@MAN_ADMIN_COMMANDS@). +.LP +A # introduces a comment that runs to the end of the line. Blank lines, +and lines with only a comment, are ignored. Leading and trailing white +space on a line are also ignored. +.LP +Lines that set a parameter are of the form +.IP +\fIparameter\fB=\fIvalue\fR +.LP +Whitespace preceding or following the = is ignored. +.LP +The +.IR parameter s +are: +.TP +.B ActiveClient +.I value +is a host name or IP addresse, followed by a comma, +semicolon, or space, followed by a port name and address or +.BR DEFAULT . +.B DEFAULT +specifies the default active mode port for rpcapd, port 2003. +Each +.B ActiveClient +line adds the host and port to the list of clients to which the server +should connect in active mode. +.TP +.B PassiveClient +.I value +is a host name or IP addresse, followed by a comma, +semicolon, or space, followed by a port name and address or +.BR DEFAULT . +.B DEFAULT +specifies the default passive mode port for rpcapd, port 2002. +Each +.B PassiveClient +line adds the host and port to the list of clients addresses and ports +that are allowed to connect to the server in passive mode. +.TP +.B NullAuthPermit +.I value +is either +.B YES +or +.BR NO . +.B YES +means that null authentication is permitted; +.B No +means that it is not permitted. +.SH SEE ALSO +rpcapd(@MAN_ADMIN_COMMANDS@) diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.c b/lib/libpcap/libpcap/rpcapd/rpcapd.c new file mode 100644 index 0000000..430acdc --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.c @@ -0,0 +1,1359 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ftmacros.h" + +#include <errno.h> // for the errno variable +#include <string.h> // for strtok, etc +#include <stdlib.h> // for malloc(), free(), ... +#include <pcap.h> // for PCAP_ERRBUF_SIZE +#include <signal.h> // for signal() + +#include "fmtutils.h" +#include "sockutils.h" // for socket calls +#include "varattrs.h" // for _U_ +#include "portability.h" +#include "rpcapd.h" +#include "config_params.h" // configuration file parameters +#include "fileconf.h" // for the configuration file management +#include "rpcap-protocol.h" +#include "daemon.h" // the true main() method of this daemon +#include "log.h" + +#ifdef _WIN32 + #include <process.h> // for thread stuff + #include "win32-svc.h" // for Win32 service stuff + #include "getopt.h" // for getopt()-for-Windows +#else + #include <fcntl.h> // for open() + #include <unistd.h> // for exit() + #include <sys/wait.h> // waitpid() +#endif + +// +// Element in list of sockets on which we're listening for connections. +// +struct listen_sock { + struct listen_sock *next; + SOCKET sock; +}; + +// Global variables +char hostlist[MAX_HOST_LIST + 1]; //!< Keeps the list of the hosts that are allowed to connect to this server +struct active_pars activelist[MAX_ACTIVE_LIST]; //!< Keeps the list of the hosts (host, port) on which I want to connect to (active mode) +int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise +static struct listen_sock *listen_socks; //!< sockets on which we listen +char loadfile[MAX_LINE + 1]; //!< Name of the file from which we have to load the configuration +static int passivemode = 1; //!< '1' if we want to run in passive mode as well +static struct addrinfo mainhints; //!< temporary struct to keep settings needed to open the new socket +static char address[MAX_LINE + 1]; //!< keeps the network address (either numeric or literal) to bind to +static char port[MAX_LINE + 1]; //!< keeps the network port to bind to +#ifdef _WIN32 +static HANDLE state_change_event; //!< event to signal that a state change should take place +#endif +static volatile sig_atomic_t shutdown_server; //!< '1' if the server is to shut down +static volatile sig_atomic_t reread_config; //!< '1' if the server is to re-read its configuration + +extern char *optarg; // for getopt() + +// Function definition +#ifdef _WIN32 +static unsigned __stdcall main_active(void *ptr); +static BOOL WINAPI main_ctrl_event(DWORD); +#else +static void *main_active(void *ptr); +static void main_terminate(int sign); +static void main_reread_config(int sign); +#endif +static void accept_connections(void); +static void accept_connection(SOCKET listen_sock); +#ifndef _WIN32 +static void main_reap_children(int sign); +#endif +#ifdef _WIN32 +static unsigned __stdcall main_passive_serviceloop_thread(void *ptr); +#endif + +#define RPCAP_ACTIVE_WAIT 30 /* Waiting time between two attempts to open a connection, in active mode (default: 30 sec) */ + +/*! + \brief Prints the usage screen if it is launched in console mode. +*/ +static void printusage(void) +{ + const char *usagetext = + "USAGE:" + " " PROGRAM_NAME " [-b <address>] [-p <port>] [-4] [-l <host_list>] [-a <host,port>]\n" + " [-n] [-v] [-d] " +#ifndef _WIN32 + "[-i] " +#endif + "[-D] [-s <config_file>] [-f <config_file>]\n\n" + " -b <address> the address to bind to (either numeric or literal).\n" + " Default: binds to all local IPv4 and IPv6 addresses\n\n" + " -p <port> the port to bind to.\n" + " Default: binds to port " RPCAP_DEFAULT_NETPORT "\n\n" + " -4 use only IPv4.\n" + " Default: use both IPv4 and IPv6 waiting sockets\n\n" + " -l <host_list> a file that contains a list of hosts that are allowed\n" + " to connect to this server (if more than one, list them one\n" + " per line).\n" + " We suggest to use literal names (instead of numeric ones)\n" + " in order to avoid problems with different address families.\n\n" + " -n permit NULL authentication (usually used with '-l')\n\n" + " -a <host,port> run in active mode when connecting to 'host' on port 'port'\n" + " In case 'port' is omitted, the default port (" RPCAP_DEFAULT_NETPORT_ACTIVE ") is used\n\n" + " -v run in active mode only (default: if '-a' is specified, it\n" + " accepts passive connections as well)\n\n" + " -d run in daemon mode (UNIX only) or as a service (Win32 only)\n" + " Warning (Win32): this switch is provided automatically when\n" + " the service is started from the control panel\n\n" +#ifndef _WIN32 + " -i run in inetd mode (UNIX only)\n\n" +#endif + " -D log debugging messages\n\n" + " -s <config_file> save the current configuration to file\n\n" + " -f <config_file> load the current configuration from file; all switches\n" + " specified from the command line are ignored\n\n" + " -h print this help screen\n\n"; + + (void)fprintf(stderr, "RPCAPD, a remote packet capture daemon.\n" + "Compiled with %s\n\n", pcap_lib_version()); + printf("%s", usagetext); +} + + + +//! Program main +int main(int argc, char *argv[]) +{ + char savefile[MAX_LINE + 1]; // name of the file on which we have to save the configuration + int log_to_systemlog = 0; // Non-zero if we should log to the "system log" rather than the standard error + int isdaemon = 0; // Non-zero if the user wants to run this program as a daemon +#ifndef _WIN32 + int isrunbyinetd = 0; // Non-zero if this is being run by inetd or something inetd-like +#endif + int log_debug_messages = 0; // Non-zero if the user wants debug messages logged + int retval; // keeps the returning value from several functions + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed +#ifndef _WIN32 + struct sigaction action; +#endif + + savefile[0] = 0; + loadfile[0] = 0; + hostlist[0] = 0; + + // Initialize errbuf + memset(errbuf, 0, sizeof(errbuf)); + + strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE); + strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE); + + // Prepare to open a new server socket + memset(&mainhints, 0, sizeof(struct addrinfo)); + + mainhints.ai_family = PF_UNSPEC; + mainhints.ai_flags = AI_PASSIVE; // Ready to a bind() socket + mainhints.ai_socktype = SOCK_STREAM; + + // Getting the proper command line options + while ((retval = getopt(argc, argv, "b:dDhip:4l:na:s:f:v")) != -1) + { + switch (retval) + { + case 'D': + log_debug_messages = 1; + rpcapd_log_set(log_to_systemlog, log_debug_messages); + break; + case 'b': + strncpy(address, optarg, MAX_LINE); + break; + case 'p': + strncpy(port, optarg, MAX_LINE); + break; + case '4': + mainhints.ai_family = PF_INET; // IPv4 server only + break; + case 'd': + isdaemon = 1; + log_to_systemlog = 1; + rpcapd_log_set(log_to_systemlog, log_debug_messages); + break; + case 'i': +#ifdef _WIN32 + printusage(); + exit(1); +#else + isrunbyinetd = 1; + log_to_systemlog = 1; + rpcapd_log_set(log_to_systemlog, log_debug_messages); +#endif + break; + case 'n': + nullAuthAllowed = 1; + break; + case 'v': + passivemode = 0; + break; + case 'l': + { + strncpy(hostlist, optarg, sizeof(hostlist)); + break; + } + case 'a': + { + char *tmpaddress, *tmpport; + char *lasts; + int i = 0; + + tmpaddress = pcap_strtok_r(optarg, RPCAP_HOSTLIST_SEP, &lasts); + + while ((tmpaddress != NULL) && (i < MAX_ACTIVE_LIST)) + { + tmpport = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); + + pcap_strlcpy(activelist[i].address, tmpaddress, MAX_LINE); + + if ((tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0)) // the user choose a custom port + pcap_strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, MAX_LINE); + else + pcap_strlcpy(activelist[i].port, tmpport, MAX_LINE); + + tmpaddress = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); + + i++; + } + + if (i > MAX_ACTIVE_LIST) + rpcapd_log(LOGPRIO_ERROR, "Only MAX_ACTIVE_LIST active connections are currently supported."); + + // I don't initialize the remaining part of the structure, since + // it is already zeroed (it is a global var) + break; + } + case 'f': + pcap_strlcpy(loadfile, optarg, MAX_LINE); + break; + case 's': + pcap_strlcpy(savefile, optarg, MAX_LINE); + break; + case 'h': + printusage(); + exit(0); + /*NOTREACHED*/ + default: + exit(1); + /*NOTREACHED*/ + } + } + +#ifndef _WIN32 + if (isdaemon && isrunbyinetd) + { + rpcapd_log(LOGPRIO_ERROR, "rpcapd: -d and -i can't be used together"); + exit(1); + } +#endif + + if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(-1); + } + + if (savefile[0] && fileconf_save(savefile)) + rpcapd_log(LOGPRIO_DEBUG, "Error when saving the configuration to file"); + + // If the file does not exist, it keeps the settings provided by the command line + if (loadfile[0]) + fileconf_read(); + +#ifdef WIN32 + // + // Create a handle to signal the main loop to tell it to do + // something. + // + state_change_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (state_change_event == NULL) + { + sock_geterror("Can't create state change event", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } + + // + // Catch control signals. + // + if (!SetConsoleCtrlHandler(main_ctrl_event, TRUE)) + { + sock_geterror("Can't set control handler", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } +#else + memset(&action, 0, sizeof (action)); + action.sa_handler = main_terminate; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGTERM, &action, NULL); + memset(&action, 0, sizeof (action)); + action.sa_handler = main_reap_children; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGCHLD, &action, NULL); + // Ignore SIGPIPE - we'll get EPIPE when trying to write to a closed + // connection, we don't want to get killed by a signal in that case + signal(SIGPIPE, SIG_IGN); +#endif + +#ifndef _WIN32 + if (isrunbyinetd) + { + // + // -i was specified, indicating that this is being run + // by inetd or something that can run network daemons + // as if it were inetd (xinetd, launchd, systemd, etc.). + // + // We assume that the program that launched us just + // duplicated a single socket for the connection + // to our standard input, output, and error, so we + // can just use the standard input as our control + // socket. + // + int sockctrl; + int devnull_fd; + + // + // Duplicate the standard input as the control socket. + // + sockctrl = dup(0); + if (sockctrl == -1) + { + sock_geterror("Can't dup standard input", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } + + // + // Try to set the standard input, output, and error + // to /dev/null. + // + devnull_fd = open("/dev/null", O_RDWR); + if (devnull_fd != -1) + { + // + // If this fails, just drive on. + // + (void)dup2(devnull_fd, 0); + (void)dup2(devnull_fd, 1); + (void)dup2(devnull_fd, 2); + close(devnull_fd); + } + + // + // Handle this client. + // This is passive mode, so we don't care whether we were + // told by the client to close. + // + char *hostlist_copy = strdup(hostlist); + if (hostlist_copy == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list"); + exit(0); + } + (void)daemon_serviceloop(sockctrl, 0, hostlist_copy, + nullAuthAllowed); + + // + // Nothing more to do. + // + exit(0); + } +#endif + + if (isdaemon) + { + // + // This is being run as a daemon. + // On UN*X, it might be manually run, or run from an + // rc file. + // +#ifndef _WIN32 + int pid; + + // + // Daemonize ourselves. + // + // Unix Network Programming, pg 336 + // + if ((pid = fork()) != 0) + exit(0); // Parent terminates + + // First child continues + // Set daemon mode + setsid(); + + // generated under unix with 'kill -HUP', needed to reload the configuration + memset(&action, 0, sizeof (action)); + action.sa_handler = main_reread_config; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGHUP, &action, NULL); + + if ((pid = fork()) != 0) + exit(0); // First child terminates + + // LINUX WARNING: the current linux implementation of pthreads requires a management thread + // to handle some hidden stuff. So, as soon as you create the first thread, two threads are + // created. Fom this point on, the number of threads active are always one more compared + // to the number you're expecting + + // Second child continues +// umask(0); +// chdir("/"); +#else + // + // This is being run as a service on Windows. + // + // If this call succeeds, it is blocking on Win32 + // + if (svc_start() != 1) + rpcapd_log(LOGPRIO_DEBUG, "Unable to start the service"); + + // When the previous call returns, the entire application has to be stopped. + exit(0); +#endif + } + else // Console mode + { +#ifndef _WIN32 + // Enable the catching of Ctrl+C + memset(&action, 0, sizeof (action)); + action.sa_handler = main_terminate; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGINT, &action, NULL); + + // generated under unix with 'kill -HUP', needed to reload the configuration + // We do not have this kind of signal in Win32 + memset(&action, 0, sizeof (action)); + action.sa_handler = main_reread_config; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGHUP, &action, NULL); +#endif + + printf("Press CTRL + C to stop the server...\n"); + } + + // If we're a Win32 service, we have already called this function in the service_main + main_startup(); + + // The code should never arrive here (since the main_startup is blocking) + // however this avoids a compiler warning + exit(0); +} + +void main_startup(void) +{ + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket + int i; +#ifdef _WIN32 + HANDLE threadId; // handle for the subthread +#else + pid_t pid; +#endif + + i = 0; + addrinfo = NULL; + memset(errbuf, 0, sizeof(errbuf)); + + // Starts all the active threads + while ((i < MAX_ACTIVE_LIST) && (activelist[i].address[0] != 0)) + { + activelist[i].ai_family = mainhints.ai_family; + +#ifdef _WIN32 + threadId = (HANDLE)_beginthreadex(NULL, 0, main_active, + (void *)&activelist[i], 0, NULL); + if (threadId == 0) + { + rpcapd_log(LOGPRIO_DEBUG, "Error creating the active child threads"); + continue; + } + CloseHandle(threadId); +#else + if ((pid = fork()) == 0) // I am the child + { + main_active((void *) &activelist[i]); + exit(0); + } +#endif + i++; + } + + /* + * The code that manages the active connections is not blocking; + * the code that manages the passive connection is blocking. + * So, if the user does not want to run in passive mode, we have + * to block the main thread here, otherwise the program ends and + * all threads are stopped. + * + * WARNING: this means that in case we have only active mode, + * the program does not terminate even if all the child thread + * terminates. The user has always to press Ctrl+C (or send a + * SIGTERM) to terminate the program. + */ + if (passivemode) + { + struct addrinfo *tempaddrinfo; + + // + // Get a list of sockets on which to listen. + // + if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf); + return; + } + + for (tempaddrinfo = addrinfo; tempaddrinfo; + tempaddrinfo = tempaddrinfo->ai_next) + { + SOCKET sock; + struct listen_sock *sock_info; + + if ((sock = sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + { + switch (tempaddrinfo->ai_family) + { + case AF_INET: + { + struct sockaddr_in *in; + char addrbuf[INET_ADDRSTRLEN]; + + in = (struct sockaddr_in *)tempaddrinfo->ai_addr; + rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for %s:%u: %s", + inet_ntop(AF_INET, &in->sin_addr, + addrbuf, sizeof (addrbuf)), + ntohs(in->sin_port), + errbuf); + break; + } + + case AF_INET6: + { + struct sockaddr_in6 *in6; + char addrbuf[INET6_ADDRSTRLEN]; + + in6 = (struct sockaddr_in6 *)tempaddrinfo->ai_addr; + rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for %s:%u: %s", + inet_ntop(AF_INET6, &in6->sin6_addr, + addrbuf, sizeof (addrbuf)), + ntohs(in6->sin6_port), + errbuf); + break; + } + + default: + rpcapd_log(LOGPRIO_WARNING, "Can't listen on socket for address family %u: %s", + tempaddrinfo->ai_family, + errbuf); + break; + } + continue; + } + + sock_info = (struct listen_sock *) malloc(sizeof (struct listen_sock)); + if (sock_info == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Can't allocate structure for listen socket"); + exit(2); + } + sock_info->sock = sock; + sock_info->next = listen_socks; + listen_socks = sock_info; + } + + freeaddrinfo(addrinfo); + + if (listen_socks == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Can't listen on any address"); + exit(2); + } + + // + // Now listen on all of them, waiting for connections. + // + accept_connections(); + } + + // + // We're done; exit. + // + rpcapd_log(LOGPRIO_DEBUG, PROGRAM_NAME " is closing.\n"); + +#ifndef _WIN32 + // + // Sends a KILL signal to all the processes in this process's + // process group; i.e., it kills all the child processes + // we've created. + // + // XXX - that also includes us, so we will be killed as well; + // that may cause a message to be printed or logged. + // + kill(0, SIGKILL); +#endif + + // + // Just leave. We shouldn't need to clean up sockets or + // anything else, and if we try to do so, we'll could end + // up closing sockets, or shutting Winsock down, out from + // under service loops, causing all sorts of noisy error + // messages. + // + // We shouldn't need to worry about cleaning up any resources + // such as handles, sockets, threads, etc. - exit() should + // terminate the process, causing all those resources to be + // cleaned up (including the threads; Microsoft claims in the + // ExitProcess() documentation that, if ExitProcess() is called, + // "If a thread is waiting on a kernel object, it will not be + // terminated until the wait has completed.", but claims in the + // _beginthread()/_beginthreadex() documentation that "All threads + // are terminated if any thread calls abort, exit, _exit, or + // ExitProcess." - the latter appears to be the case, even for + // threads waiting on the event for a pcap_t). + // + exit(0); +} + +#ifdef _WIN32 +static void +send_state_change_event(void) +{ + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + + if (!SetEvent(state_change_event)) + { + sock_geterror("SetEvent on shutdown event failed", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + } +} + +void +send_shutdown_notification(void) +{ + // + // Indicate that the server should shut down. + // + shutdown_server = 1; + + // + // Send a state change event, to wake up WSAWaitForMultipleEvents(). + // + send_state_change_event(); +} + +void +send_reread_configuration_notification(void) +{ + // + // Indicate that the server should re-read its configuration file. + // + reread_config = 1; + + // + // Send a state change event, to wake up WSAWaitForMultipleEvents(). + // + send_state_change_event(); +} + +static BOOL WINAPI main_ctrl_event(DWORD ctrltype) +{ + // + // ctrltype is one of: + // + // CTRL_C_EVENT - we got a ^C; this is like SIGINT + // CTRL_BREAK_EVENT - we got Ctrl+Break + // CTRL_CLOSE_EVENT - the console was closed; this is like SIGHUP + // CTRL_LOGOFF_EVENT - a user is logging off; this is received + // only by services + // CTRL_SHUTDOWN_EVENT - the systemis shutting down; this is + // received only by services + // + // For now, we treat all but CTRL_LOGOFF_EVENT as indications + // that we should shut down. + // + switch (ctrltype) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_SHUTDOWN_EVENT: + // + // Set a shutdown notification. + // + send_shutdown_notification(); + break; + + default: + break; + } + + // + // We handled this. + // + return TRUE; +} +#else +static void main_terminate(int sign _U_) +{ + // + // Note that the server should shut down. + // select() should get an EINTR error when we return, + // so it will wake up and know it needs to check the flag. + // + shutdown_server = 1; +} + +static void main_reread_config(int sign _U_) +{ + // + // Note that the server should re-read its configuration file. + // select() should get an EINTR error when we return, + // so it will wake up and know it needs to check the flag. + // + reread_config = 1; +} + +static void main_reap_children(int sign _U_) +{ + pid_t pid; + int exitstat; + + // Reap all child processes that have exited. + // For reference, Stevens, pg 128 + + while ((pid = waitpid(-1, &exitstat, WNOHANG)) > 0) + rpcapd_log(LOGPRIO_DEBUG, "Child terminated"); + + return; +} +#endif + +// +// Loop waiting for incoming connections and accepting them. +// +static void +accept_connections(void) +{ +#ifdef _WIN32 + struct listen_sock *sock_info; + DWORD num_events; + WSAEVENT *events; + int i; + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + + // + // How big does the set of events need to be? + // One for the shutdown event, plus one for every socket on which + // we'll be listening. + // + num_events = 1; // shutdown event + for (sock_info = listen_socks; sock_info; + sock_info = sock_info->next) + { + if (num_events == WSA_MAXIMUM_WAIT_EVENTS) + { + // + // WSAWaitForMultipleEvents() doesn't support + // more than WSA_MAXIMUM_WAIT_EVENTS events + // on which to wait. + // + rpcapd_log(LOGPRIO_ERROR, "Too many sockets on which to listen"); + exit(2); + } + num_events++; + } + + // + // Allocate the array of events. + // + events = (WSAEVENT *) malloc(num_events * sizeof (WSAEVENT)); + if (events == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Can't allocate array of events which to listen"); + exit(2); + } + + // + // Fill it in. + // + events[0] = state_change_event; // state change event first + for (sock_info = listen_socks, i = 1; sock_info; + sock_info = sock_info->next, i++) + { + WSAEVENT event; + + // + // Create an event that is signaled if there's a connection + // to accept on the socket in question. + // + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) + { + sock_geterror("Can't create socket event", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } + if (WSAEventSelect(sock_info->sock, event, FD_ACCEPT) == SOCKET_ERROR) + { + sock_geterror("Can't setup socket event", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } + events[i] = event; + } + + for (;;) + { + // + // Wait for incoming connections. + // + DWORD ret; + + ret = WSAWaitForMultipleEvents(num_events, events, FALSE, + WSA_INFINITE, FALSE); + if (ret == WSA_WAIT_FAILED) + { + sock_geterror("WSAWaitForMultipleEvents failed", errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } + + if (ret == WSA_WAIT_EVENT_0) + { + // + // The state change event was set. + // + if (shutdown_server) + { + // + // Time to quit. Exit the loop. + // + break; + } + if (reread_config) + { + // + // We should re-read the configuration + // file. + // + reread_config = 0; // clear the indicator + fileconf_read(); + } + } + + // + // Check each socket. + // + for (sock_info = listen_socks, i = 1; sock_info; + sock_info = sock_info->next, i++) + { + WSANETWORKEVENTS network_events; + + if (WSAEnumNetworkEvents(sock_info->sock, + events[i], &network_events) == SOCKET_ERROR) + { + sock_geterror("WSAEnumNetworkEvents failed", + errbuf, PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(2); + } + if (network_events.lNetworkEvents & FD_ACCEPT) + { + // + // Did an error occur? + // + if (network_events.iErrorCode[FD_ACCEPT_BIT] != 0) + { + // + // Yes - report it and keep going. + // + sock_fmterror("Socket error", + network_events.iErrorCode[FD_ACCEPT_BIT], + errbuf, + PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + continue; + } + + // + // Accept the connection. + // + accept_connection(sock_info->sock); + } + } + } +#else + struct listen_sock *sock_info; + int num_sock_fds; + + // + // How big does the bitset of sockets on which to select() have + // to be? + // + num_sock_fds = 0; + for (sock_info = listen_socks; sock_info; sock_info = sock_info->next) + { + if (sock_info->sock + 1 > num_sock_fds) + { + if ((unsigned int)(sock_info->sock + 1) > + (unsigned int)FD_SETSIZE) + { + rpcapd_log(LOGPRIO_ERROR, "Socket FD is too bit for an fd_set"); + exit(2); + } + num_sock_fds = sock_info->sock + 1; + } + } + + for (;;) + { + fd_set sock_fds; + int ret; + + // + // Set up an fd_set for all the sockets on which we're + // listening. + // + // This set is modified by select(), so we have to + // construct it anew each time. + // + FD_ZERO(&sock_fds); + for (sock_info = listen_socks; sock_info; + sock_info = sock_info->next) + { + FD_SET(sock_info->sock, &sock_fds); + } + + // + // Wait for incoming connections. + // + ret = select(num_sock_fds, &sock_fds, NULL, NULL, NULL); + if (ret == -1) + { + if (errno == EINTR) + { + // + // If this is a "terminate the + // server" signal, exit the loop, + // otherwise just keep trying. + // + if (shutdown_server) + { + // + // Time to quit. Exit the loop. + // + break; + } + if (reread_config) + { + // + // We should re-read the configuration + // file. + // + reread_config = 0; // clear the indicator + fileconf_read(); + } + + // + // Go back and wait again. + // + continue; + } + else + { + rpcapd_log(LOGPRIO_ERROR, "select failed: %s", + strerror(errno)); + exit(2); + } + } + + // + // Check each socket. + // + for (sock_info = listen_socks; sock_info; + sock_info = sock_info->next) + { + if (FD_ISSET(sock_info->sock, &sock_fds)) + { + // + // Accept the connection. + // + accept_connection(sock_info->sock); + } + } + } +#endif + + // + // Close all the listen sockets. + // + for (sock_info = listen_socks; sock_info; sock_info = sock_info->next) + { + closesocket(sock_info->sock); + } + sock_cleanup(); +} + +#ifdef _WIN32 +// +// A structure to hold the parameters to the daemon service loop +// thread on Windows. +// +// (On UN*X, there is no need for this explicit copy since the +// fork "inherits" the parent stack.) +// +struct params_copy { + SOCKET sockctrl; + char *hostlist; +}; +#endif + +// +// Accept a connection and start a worker thread, on Windows, or a +// worker process, on UN*X, to handle the connection. +// +static void +accept_connection(SOCKET listen_sock) +{ + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + SOCKET sockctrl; // keeps the socket ID for this control connection + struct sockaddr_storage from; // generic sockaddr_storage variable + socklen_t fromlen; // keeps the length of the sockaddr_storage variable + +#ifdef _WIN32 + HANDLE threadId; // handle for the subthread + u_long off = 0; + struct params_copy *params_copy = NULL; +#else + pid_t pid; +#endif + + // Initialize errbuf + memset(errbuf, 0, sizeof(errbuf)); + + for (;;) + { + // Accept the connection + fromlen = sizeof(struct sockaddr_storage); + + sockctrl = accept(listen_sock, (struct sockaddr *) &from, &fromlen); + + if (sockctrl != INVALID_SOCKET) + { + // Success. + break; + } + + // The accept() call can return this error when a signal is catched + // In this case, we have simply to ignore this error code + // Stevens, pg 124 +#ifdef _WIN32 + if (WSAGetLastError() == WSAEINTR) +#else + if (errno == EINTR) +#endif + continue; + + // Don't check for errors here, since the error can be due to the fact that the thread + // has been killed + sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "Accept of control connection from client failed: %s", + errbuf); + return; + } + +#ifdef _WIN32 + // + // Put the socket back into blocking mode; doing WSAEventSelect() + // on the listen socket makes that socket non-blocking, and it + // appears that sockets returned from an accept() on that socket + // are also non-blocking. + // + // First, we have to un-WSAEventSelect() this socket, and then + // we can turn non-blocking mode off. + // + // If this fails, we aren't guaranteed that, for example, any + // of the error message will be sent - if it can't be put in + // the socket queue, the send will just fail. + // + // So we just log the message and close the connection. + // + if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR) + { + sock_geterror("WSAEventSelect()", errbuf, PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + sock_close(sockctrl, NULL, 0); + return; + } + if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR) + { + sock_geterror("ioctlsocket(FIONBIO)", errbuf, PCAP_ERRBUF_SIZE); + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + sock_close(sockctrl, NULL, 0); + return; + } + + // + // Make a copy of the host list to pass to the new thread, so that + // if we update it in the main thread, it won't catch us in the + // middle of updating it. + // + // daemon_serviceloop() will free it once it's done with it. + // + char *hostlist_copy = strdup(hostlist); + if (hostlist_copy == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list"); + sock_close(sockctrl, NULL, 0); + return; + } + + // + // Allocate a location to hold the values of sockctrl. + // It will be freed in the newly-created thread once it's + // finished with it. + // + params_copy = malloc(sizeof(*params_copy)); + if (params_copy == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Out of memory allocating the parameter copy structure"); + free(hostlist_copy); + sock_close(sockctrl, NULL, 0); + return; + } + params_copy->sockctrl = sockctrl; + params_copy->hostlist = hostlist_copy; + + threadId = (HANDLE)_beginthreadex(NULL, 0, + main_passive_serviceloop_thread, (void *) params_copy, 0, NULL); + if (threadId == 0) + { + rpcapd_log(LOGPRIO_ERROR, "Error creating the child thread"); + free(params_copy); + free(hostlist_copy); + sock_close(sockctrl, NULL, 0); + return; + } + CloseHandle(threadId); +#else /* _WIN32 */ + pid = fork(); + if (pid == -1) + { + rpcapd_log(LOGPRIO_ERROR, "Error creating the child process: %s", + strerror(errno)); + sock_close(sockctrl, NULL, 0); + return; + } + if (pid == 0) + { + // + // Child process. + // + // Close the socket on which we're listening (must + // be open only in the parent). + // + closesocket(listen_sock); + +#if 0 + // + // Modify thread params so that it can be killed at any time + // XXX - is this necessary? This is the main and, currently, + // only thread in the child process, and nobody tries to + // cancel us, although *we* may cancel the thread that's + // handling the capture loop. + // + if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) + goto end; + if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) + goto end; +#endif + + // + // Run the service loop. + // This is passive mode, so we don't care whether we were + // told by the client to close. + // + char *hostlist_copy = strdup(hostlist); + if (hostlist_copy == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list"); + exit(0); + } + (void)daemon_serviceloop(sockctrl, 0, hostlist_copy, + nullAuthAllowed); + + exit(0); + } + + // I am the parent + // Close the socket for this session (must be open only in the child) + closesocket(sockctrl); +#endif /* _WIN32 */ +} + +/*! + \brief 'true' main of the program in case the active mode is turned on. + + This function loops forever trying to connect to the remote host, until the + daemon is turned down. + + \param ptr: it keeps the 'activepars' parameters. It is a 'void *' + just because the thread APIs want this format. +*/ +#ifdef _WIN32 +static unsigned __stdcall +#else +static void * +#endif +main_active(void *ptr) +{ + char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed + SOCKET sockctrl; // keeps the socket ID for this control connection + struct addrinfo hints; // temporary struct to keep settings needed to open the new socket + struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket + struct active_pars *activepars; + + activepars = (struct active_pars *) ptr; + + // Prepare to open a new server socket + memset(&hints, 0, sizeof(struct addrinfo)); + // WARNING Currently it supports only ONE socket family among IPv4 and IPv6 + hints.ai_family = AF_INET; // PF_UNSPEC to have both IPv4 and IPv6 server + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = activepars->ai_family; + + rpcapd_log(LOGPRIO_DEBUG, "Connecting to host %s, port %s, using protocol %s", + activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": + (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified"); + + // Initialize errbuf + memset(errbuf, 0, sizeof(errbuf)); + + // Do the work + if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf); + return 0; + } + + for (;;) + { + int activeclose; + + if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + { + rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf); + + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s", + activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": + (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified"); + + rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf); + + sleep_secs(RPCAP_ACTIVE_WAIT); + + continue; + } + + char *hostlist_copy = strdup(hostlist); + if (hostlist_copy == NULL) + { + rpcapd_log(LOGPRIO_ERROR, "Out of memory copying the host/port list"); + activeclose = 0; + sock_close(sockctrl, NULL, 0); + } + else + { + // + // daemon_serviceloop() will free the copy. + // + activeclose = daemon_serviceloop(sockctrl, 1, + hostlist_copy, nullAuthAllowed); + } + + // If the connection is closed by the user explicitely, don't try to connect to it again + // just exit the program + if (activeclose == 1) + break; + } + + freeaddrinfo(addrinfo); + return 0; +} + +#ifdef _WIN32 +// +// Main routine of a passive-mode service thread. +// +unsigned __stdcall main_passive_serviceloop_thread(void *ptr) +{ + struct params_copy params = *(struct params_copy *)ptr; + free(ptr); + + // + // Handle this client. + // This is passive mode, so we don't care whether we were + // told by the client to close. + // + (void)daemon_serviceloop(params.sockctrl, 0, params.hostlist, + nullAuthAllowed); + + return 0; +} +#endif diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.h b/lib/libpcap/libpcap/rpcapd/rpcapd.h new file mode 100644 index 0000000..90ba7ff --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __RPCAPD_H__ +#define __RPCAPD_H__ + +#define PROGRAM_NAME "rpcapd" +#define SOCKET_MAXCONN 10 /* Maximum number of connections queued into the accept() */ +#ifdef _WIN32 +void send_shutdown_notification(void); // Send notification to shut down the daemon +void send_reread_configuration_notification(void); // Send notification to re-read the configuration file +#endif +void main_startup(void); + +#endif diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.inetd.conf b/lib/libpcap/libpcap/rpcapd/rpcapd.inetd.conf new file mode 100644 index 0000000..86823f0 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.inetd.conf @@ -0,0 +1 @@ +2002 stream tcp nowait root /usr/local/sbin/rpcapd rpcapd -i diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.manadmin b/lib/libpcap/libpcap/rpcapd/rpcapd.manadmin new file mode 100644 index 0000000..e4bbc8f --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.manadmin @@ -0,0 +1,233 @@ +.\" rpcapd.8 +.\" +.\" Copyright (c) 2002-2005 NetGroup, Politecnico di Torino (Italy) +.\" Copyright (c) 2005-2009 CACE Technologies +.\" Copyright (c) 2018- The TCPdump Group +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the Politecnico di Torino nor the names of its +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +.\" OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.TH RPCAPD 8 "April 20, 2018" +.SH NAME +rpcapd \- capture daemon to be controlled by a remote libpcap application +.SH SYNOPSIS +.na +rpcapd +[ +.B \-b +.I address +] [ +.B \-p +.I port +] [ +.B \-4 +] [ +.B \-l +.I host_list +] +.br +.ti +8 +[ +.B \-a +.IR host , port +] [ +.B \-n +] [ +.B \-v +] [ +.B \-d +] [ +.B \-i +] +.br +.ti +8 +[ +.B \-D +] [ +.B \-s +.I config_file +] [ +.B \-f +.I config_file +] +.br +.ad +.SH DESCRIPTION +.LP +\fIRpcapd\fP is a daemon (Unix) or service (Win32) that allows the capture +and filter part of libpcap to be run on a remote system. +.LP +Rpcapd can run in two modes: passive mode (default) and active mode. +.LP +In passive mode, the client (e.g., a network sniffer) connects to +.BR rpcapd . +It then sends hem the appropriate commands to start the capture. +.LP +In active mode, +.B rpcapd +tries to establish a connection toward the client +(e.g., a network sniffer). The client then sends the appropriate commands +to rpcapd to start the capture. +.LP +Active mode is useful in case +.B rpcapd +is run behind a firewall and +cannot receive connections from the external world. In this case, +.B rpcapd +can be configured to establish the connection to a given host, +which has to be configured in order to wait for that connection. After +establishing the connection, the protocol continues its job in almost +the same way in both active and passive mode. +.SH Configuration file +.LP +The user can create a configuration file in the same folder of the +executable, and put the configuration commands in there. In order for +rpcapd to execute the commands, you have to restart it on Win32, i.e. +the initialization file is parsed only at the beginning). The UNIX +version of rpcapd will reread the configuration file when receiving a +HUP signel. In that case, all the existing connections remain in place, +while the new connections will be created according to the new parameters. +.LP +In case a user does not want to create the configuration file manually, +they can launch rpcapd with the requested parameters plus "-s filename". +Rpcapd will parse all the parameters and save them into the specified +configuration file. +.SH Installing rpcapd on Win32 +.LP +The remote daemon is installed automatically when installing WinPcap. +The installation process places the rpcapd file into the WinPcap folder. +This file can be executed either from the command line, or as a service. +For instance, the installation process updates the list of available +services list and it creates a new item (Remote Packet Capture Protocol +v.0 (experimental) ). To avoid security problems, the service is +inactive and it has to be started manually (control panel - +administrative tools - services - start). +.LP +The service has a set of "standard" parameters, i.e. it is launched +with the +.B \-d +flag (in order to make it run as a service) and the +.B "-f rpcapd.ini" +flag. +.SH Starting rpcapd on Win32 +.LP +The rpcapd executable can be launched directly, i.e. it can run in the +foreground as well (not as a daemon/service). The procedure is quite +simple: you have to invoke the executable from the command line with all +the requested parameters except for the +.B \-d +flag. The capture server will +start in the foreground. +.SH Installing rpcapd on Unix-like systems +TBD +.SH Starting rpcapd on Unix-like systems +.B rpcapd +needs sufficient privileges to perform packet capture, e.g. +run as root or be owned by root and have suid set. Most operating +systems provide more elegant solutions when run as user than the +above solutions, all of them different. +.SH OPTIONS +.TP +.BI \-b " address" +Bind to the IP address specified by +.I address +(either numeric or literal). +By default, +.B rpcapd +binds to all local IPv4 and IPv6 addresses. +.TP +.BI \-p " port" +Bind to the port specified by +.IR port . +By default, +.B rpcapd +binds to port 2002. +.TP +.B \-4 +Listen only on IPv4 addresses. +By default, +.B rpcapd +listens on both IPv4 and IPv6 addresses. +.TP +.BI -l " host_list" +Only allow hosts specified in the +.I host_list +argument to connect to this server. +.I host_list +is a list of host names or IP addresses, separated by commas. +We suggest that you use use host names rather than literal IP addresses +in order to avoid problems with different address families. +.TP +.B \-n +Permit NULL authentication (usually used with +.BR \-l ). +.TP +.BI \-a " host" , "port" +Run in active mode, connecting to host +.I host +on port +.IR port . +In case +.I port +is omitted, the default port (2003) is used. +.TP +.B -v +Run in active mode only; by default, if +.B \-a +is specified, +.B rpcapd +it accepts passive connections as well. +.TP +.B \-d +Run in daemon mode (UNIX only) or as a service (Win32 only) +Warning (Win32): this switch is provided automatically when +the service is started from the control panel. +.TP +.B \-i +Run in inetd mode (UNIX only). +.TP +.B \-D +Log debugging messages. +.TP +.BI \-s " config_file" +Save the current configuration to +.I config_file +in the format specified by +.BR rpcapd-config (5). +.TP +.BI \-f " config_file" +Load the current configuration from +.I config_file +in the format specified by +.BR rpcapd-config (5); +all switches specified from the command line are ignored. +.TP +.B \-h +Print this help screen. +.br +.ad +.SH "SEE ALSO" +pcap(3PCAP), rpcapd-config(5) diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.manadmin.in b/lib/libpcap/libpcap/rpcapd/rpcapd.manadmin.in new file mode 100644 index 0000000..0a9d4e0 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.manadmin.in @@ -0,0 +1,233 @@ +.\" rpcapd.8 +.\" +.\" Copyright (c) 2002-2005 NetGroup, Politecnico di Torino (Italy) +.\" Copyright (c) 2005-2009 CACE Technologies +.\" Copyright (c) 2018- The TCPdump Group +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the Politecnico di Torino nor the names of its +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +.\" OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.TH RPCAPD @MAN_ADMIN_COMMANDS@ "April 20, 2018" +.SH NAME +rpcapd \- capture daemon to be controlled by a remote libpcap application +.SH SYNOPSIS +.na +rpcapd +[ +.B \-b +.I address +] [ +.B \-p +.I port +] [ +.B \-4 +] [ +.B \-l +.I host_list +] +.br +.ti +8 +[ +.B \-a +.IR host , port +] [ +.B \-n +] [ +.B \-v +] [ +.B \-d +] [ +.B \-i +] +.br +.ti +8 +[ +.B \-D +] [ +.B \-s +.I config_file +] [ +.B \-f +.I config_file +] +.br +.ad +.SH DESCRIPTION +.LP +\fIRpcapd\fP is a daemon (Unix) or service (Win32) that allows the capture +and filter part of libpcap to be run on a remote system. +.LP +Rpcapd can run in two modes: passive mode (default) and active mode. +.LP +In passive mode, the client (e.g., a network sniffer) connects to +.BR rpcapd . +It then sends hem the appropriate commands to start the capture. +.LP +In active mode, +.B rpcapd +tries to establish a connection toward the client +(e.g., a network sniffer). The client then sends the appropriate commands +to rpcapd to start the capture. +.LP +Active mode is useful in case +.B rpcapd +is run behind a firewall and +cannot receive connections from the external world. In this case, +.B rpcapd +can be configured to establish the connection to a given host, +which has to be configured in order to wait for that connection. After +establishing the connection, the protocol continues its job in almost +the same way in both active and passive mode. +.SH Configuration file +.LP +The user can create a configuration file in the same folder of the +executable, and put the configuration commands in there. In order for +rpcapd to execute the commands, you have to restart it on Win32, i.e. +the initialization file is parsed only at the beginning). The UNIX +version of rpcapd will reread the configuration file when receiving a +HUP signel. In that case, all the existing connections remain in place, +while the new connections will be created according to the new parameters. +.LP +In case a user does not want to create the configuration file manually, +they can launch rpcapd with the requested parameters plus "-s filename". +Rpcapd will parse all the parameters and save them into the specified +configuration file. +.SH Installing rpcapd on Win32 +.LP +The remote daemon is installed automatically when installing WinPcap. +The installation process places the rpcapd file into the WinPcap folder. +This file can be executed either from the command line, or as a service. +For instance, the installation process updates the list of available +services list and it creates a new item (Remote Packet Capture Protocol +v.0 (experimental) ). To avoid security problems, the service is +inactive and it has to be started manually (control panel - +administrative tools - services - start). +.LP +The service has a set of "standard" parameters, i.e. it is launched +with the +.B \-d +flag (in order to make it run as a service) and the +.B "-f rpcapd.ini" +flag. +.SH Starting rpcapd on Win32 +.LP +The rpcapd executable can be launched directly, i.e. it can run in the +foreground as well (not as a daemon/service). The procedure is quite +simple: you have to invoke the executable from the command line with all +the requested parameters except for the +.B \-d +flag. The capture server will +start in the foreground. +.SH Installing rpcapd on Unix-like systems +TBD +.SH Starting rpcapd on Unix-like systems +.B rpcapd +needs sufficient privileges to perform packet capture, e.g. +run as root or be owned by root and have suid set. Most operating +systems provide more elegant solutions when run as user than the +above solutions, all of them different. +.SH OPTIONS +.TP +.BI \-b " address" +Bind to the IP address specified by +.I address +(either numeric or literal). +By default, +.B rpcapd +binds to all local IPv4 and IPv6 addresses. +.TP +.BI \-p " port" +Bind to the port specified by +.IR port . +By default, +.B rpcapd +binds to port 2002. +.TP +.B \-4 +Listen only on IPv4 addresses. +By default, +.B rpcapd +listens on both IPv4 and IPv6 addresses. +.TP +.BI -l " host_list" +Only allow hosts specified in the +.I host_list +argument to connect to this server. +.I host_list +is a list of host names or IP addresses, separated by commas. +We suggest that you use use host names rather than literal IP addresses +in order to avoid problems with different address families. +.TP +.B \-n +Permit NULL authentication (usually used with +.BR \-l ). +.TP +.BI \-a " host" , "port" +Run in active mode, connecting to host +.I host +on port +.IR port . +In case +.I port +is omitted, the default port (2003) is used. +.TP +.B -v +Run in active mode only; by default, if +.B \-a +is specified, +.B rpcapd +it accepts passive connections as well. +.TP +.B \-d +Run in daemon mode (UNIX only) or as a service (Win32 only) +Warning (Win32): this switch is provided automatically when +the service is started from the control panel. +.TP +.B \-i +Run in inetd mode (UNIX only). +.TP +.B \-D +Log debugging messages. +.TP +.BI \-s " config_file" +Save the current configuration to +.I config_file +in the format specified by +.BR rpcapd-config (@MAN_FILE_FORMATS@). +.TP +.BI \-f " config_file" +Load the current configuration from +.I config_file +in the format specified by +.BR rpcapd-config (@MAN_FILE_FORMATS@); +all switches specified from the command line are ignored. +.TP +.B \-h +Print this help screen. +.br +.ad +.SH "SEE ALSO" +pcap(3PCAP), rpcapd-config(@MAN_FILE_FORMATS@) diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.rc b/lib/libpcap/libpcap/rpcapd/rpcapd.rc new file mode 100644 index 0000000..695c00b --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.rc @@ -0,0 +1,39 @@ +#include "config.h" +#undef PACKAGE_NAME +#include <winver.h> +#include <rpcapd.h> +#define PACKAGE_NAME PROGRAM_NAME + + VS_VERSION_INFO VERSIONINFO + FILEVERSION PACKAGE_VERSION_DLL + PRODUCTVERSION PACKAGE_VERSION_DLL + FILEFLAGSMASK 0x3fL + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "https://github.com/the-tcpdump-group/libpcap/" + VALUE "CompanyName", "The TCPdump Group" + VALUE "FileDescription", "Remote Packet Capture Daemon" + VALUE "FileVersion", "PACKAGE_VERSION_DLL" + VALUE "InternalName", PACKAGE_NAME + VALUE "LegalCopyright", "Copyright (c) The TCPdump Group" + VALUE "LegalTrademarks", "" + VALUE "OriginalFilename", "rpcapd.exe" + VALUE "ProductName", PACKAGE_NAME + VALUE "ProductVersion", PACKAGE_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END + END diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.socket b/lib/libpcap/libpcap/rpcapd/rpcapd.socket new file mode 100644 index 0000000..9d5a0bd --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.socket @@ -0,0 +1,9 @@ +[Unit] +Description=Rpcap Socket for Per-Connection Servers + +[Socket] +ListenStream=2002 +Accept=yes + +[Install] +WantedBy=sockets.target diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd.xinetd.conf b/lib/libpcap/libpcap/rpcapd/rpcapd.xinetd.conf new file mode 100644 index 0000000..2c79348 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd.xinetd.conf @@ -0,0 +1,8 @@ +service rpcap { + socket_type = stream + protocol = tcp + wait = no + user = root + server = /usr/local/sbin/rpcapd + server_args = -i +} diff --git a/lib/libpcap/libpcap/rpcapd/rpcapd@.service b/lib/libpcap/libpcap/rpcapd/rpcapd@.service new file mode 100644 index 0000000..92d1171 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/rpcapd@.service @@ -0,0 +1,6 @@ +[Unit] +Description=Rpcap Per-Connection Server + +[Service] +ExecStart=-/usr/local/sbin/rpcapd -i +StandardInput=socket diff --git a/lib/libpcap/libpcap/rpcapd/win32-svc.c b/lib/libpcap/libpcap/rpcapd/win32-svc.c new file mode 100644 index 0000000..3a19910 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/win32-svc.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "rpcapd.h" +#include <pcap.h> // for PCAP_ERRBUF_SIZE +#include "fmtutils.h" +#include "portability.h" +#include "fileconf.h" +#include "log.h" + +static SERVICE_STATUS_HANDLE service_status_handle; +static SERVICE_STATUS service_status; + +static void WINAPI svc_main(DWORD argc, char **argv); +static void update_svc_status(DWORD state, DWORD progress_indicator); + +int svc_start(void) +{ + int rc; + SERVICE_TABLE_ENTRY ste[] = + { + { PROGRAM_NAME, svc_main }, + { NULL, NULL } + }; + char string[PCAP_ERRBUF_SIZE]; + + // This call is blocking. A new thread is created which will launch + // the svc_main() function + if ((rc = StartServiceCtrlDispatcher(ste)) == 0) { + pcap_fmt_errmsg_for_win32_err(string, sizeof (string), + GetLastError(), "StartServiceCtrlDispatcher() failed"); + rpcapd_log(LOGPRIO_ERROR, "%s", string); + } + + return rc; // FALSE if this is not started as a service +} + +void WINAPI svc_control_handler(DWORD Opcode) +{ + switch(Opcode) + { + case SERVICE_CONTROL_STOP: + // + // XXX - is this sufficient to clean up the service? + // To be really honest, only the main socket and + // such these stuffs are cleared; however the threads + // that are running are not stopped. + // This can be seen by placing a breakpoint at the + // end of svc_main(), in which you will see that is + // never reached. However, as soon as you set the + // service status to "stopped", the + // StartServiceCtrlDispatcher() returns and the main + // thread ends. Then, Win32 has a good automatic + // cleanup, so that all the threads which are still + // running are stopped when the main thread ends. + // + send_shutdown_notification(); + + update_svc_status(SERVICE_STOP_PENDING, 0); + break; + + /* + Pause and Continue have an usual meaning and they are used just to be able + to change the running parameters at run-time. In other words, they act + like the SIGHUP signal on UNIX. All the running threads continue to run and + they are not paused at all. + Particularly, + - PAUSE does nothing + - CONTINUE re-reads the configuration file and creates the new threads that + can be needed according to the new configuration. + */ + case SERVICE_CONTROL_PAUSE: + update_svc_status(SERVICE_PAUSED, 0); + break; + + case SERVICE_CONTROL_CONTINUE: + update_svc_status(SERVICE_RUNNING, 0); + // + // Tell the main loop to re-read the configuration. + // + send_reread_configuration_notification(); + break; + + case SERVICE_CONTROL_INTERROGATE: + // Fall through to send current status. + // WARNING: not implemented + update_svc_status(SERVICE_RUNNING, 0); + MessageBox(NULL, "Not implemented", "warning", MB_OK); + break; + + case SERVICE_CONTROL_PARAMCHANGE: + // + // Tell the main loop to re-read the configuration. + // + send_reread_configuration_notification(); + break; + } + + // Send current status. + return; +} + +void WINAPI svc_main(DWORD argc, char **argv) +{ + service_status_handle = RegisterServiceCtrlHandler(PROGRAM_NAME, svc_control_handler); + + if (!service_status_handle) + return; + + service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; + service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_PARAMCHANGE; + // | SERVICE_ACCEPT_SHUTDOWN ; + update_svc_status(SERVICE_RUNNING, 0); + + // + // Service requests until we're told to stop. + // + main_startup(); + + // + // It returned, so we were told to stop. + // + update_svc_status(SERVICE_STOPPED, 0); +} + +static void +update_svc_status(DWORD state, DWORD progress_indicator) +{ + service_status.dwWin32ExitCode = NO_ERROR; + service_status.dwCurrentState = state; + service_status.dwCheckPoint = progress_indicator; + service_status.dwWaitHint = 0; + SetServiceStatus(service_status_handle, &service_status); +} + +/* +sc create rpcapd DisplayName= "Remote Packet Capture Protocol v.0 (experimental)" binpath= "C:\cvsroot\winpcap\wpcap\PRJ\Debug\rpcapd -d -f rpcapd.ini" +sc description rpcapd "Allows to capture traffic on this host from a remote machine." +*/ diff --git a/lib/libpcap/libpcap/rpcapd/win32-svc.h b/lib/libpcap/libpcap/rpcapd/win32-svc.h new file mode 100644 index 0000000..3f511d2 --- /dev/null +++ b/lib/libpcap/libpcap/rpcapd/win32-svc.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +int svc_start(void); |