pax_global_header00006660000000000000000000000064133305521700014511gustar00rootroot0000000000000052 comment=6a4af7786630ce48747d9687e2f18f45ea6684c4 xinetd-2.3.15.4/000077500000000000000000000000001333055217000132365ustar00rootroot00000000000000xinetd-2.3.15.4/.gitignore000066400000000000000000000011231333055217000152230ustar00rootroot00000000000000build/ *.orig *.rej *.o *.lo *.la *~ *.swp # Makefiles Makefile Makefile.in .deps .dirstamp # http://www.gnu.org/software/autoconf /autom4te.cache /autoscan.log /autoscan-*.log /aclocal.m4 /compile /config.guess /config.h.in /config.h /config.status /config.sub /config.log /configure /configure.scan /depcomp /install-sh /missing /stamp-h1 # https://www.gnu.org/software/libtool/ .libs /ltmain.sh /libtool m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 # http://www.gnu.org/software/texinfo /texinfo.tex # output binaries xinetd itox # tarballs *.tar.* xinetd-2.3.15.4/.travis.yml000066400000000000000000000006201333055217000153450ustar00rootroot00000000000000sudo: required language: c os: - linux - osx compiler: - clang - gcc before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y libtirpc-dev libwrap0-dev libselinux-dev ; fi script: - sh autogen.sh - ./configure --without-libwrap --without-labeled-networking --without-loadavg - make - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make distcheck ; fi xinetd-2.3.15.4/CHANGELOG000066400000000000000000001262711333055217000144610ustar00rootroot00000000000000 This file describes the changes to xinetd. The base version is 2.0.0. 2.0.0: (not released) 2.0.1: (not released) 2.0.2: Changes to the Makefile. Trivial mods to the usage() function 2.0.3: Changes to the Makefile. Fixed a bug in the usage() function 2.0.4: Distribution versions of the Makefile no longer contain any references to options.opt 2.0.5: 1) Clarified what an "unlisted RPC service" is in the man page 2) Fixed a bug in remote_address_check which caused access to be denied for all hosts if no_access was set and only_from was not set. 3) Fixed a bug which caused arbitrary syslog levels if the log_type was specified as SYSLOG in the "defaults" entry 2.0.6: bug fix in child.c: replaced strx_sprint with strx_print when naming interceptor processes ------------------------------------------------------------------------------- 2.1.0: (not released) This was mostly a clean-up of 2.0 List of changes: A. The man page has been split into 3 parts: xinetd.man : man page describing the program xinetd.conf.man : man page describing the configuration file This file now includes figures about the overhead of interception. xinetd.log.man : man page describing the log file B. New service attributes rpc_number : to support unlisted RPC services nice : to set the nice value of forked servers C. The IDONLY service flag was added D. Now uses the timer library unless NO_TIMERS is defined. If NO_TIMERS is not defined, the following are also available: a) a new option, -cc, to do periodic consistency checks b) timeout for reconfiguration E. Configuration file man page now mentions that access control is based on IP-address instead of domain address. F. The interception code now sets the TCP_NODELAY option. G. The timeout when contacting a remote identification server is configurable both when the service request has been accepted and when the request is rejected. In the former case it defaults to infinity while in the latter it defaults to 30 sec. H. The log line ids are now constants in an include file. I. The fsma library is no longer used. J. Most structure fields were renamed; certain functions were renamed too. Access to structure fields is now via macros. K. A bug in not restoring the number of descriptors to the soft limit was fixed. L. The -pid option works M. New internal services: "servers" : lists active servers "services" : lists active services 2.1.1: xinetd produces more meaningful messages if it can't start logging 2.1.2: Fixed 2 bugs: 1) xinetd would request a port number for unlisted RPC services One could get around this by simply specifying the port attribute for the service; the specified value would be ignored anyway. 2) xinetd initialization might fail sometimes in function msg_init() syslog logging was specified (i.e. the option -syslog was used) The cause of this bug was that the xlog_control( xlog, XLOG_GETFD, &fd ) operation will not fail for xlogs connected to syslog and the value of 'fd' would be used in the subsequent fcntl(). That value is arbitrary since 'fd' is a local variable. If 'fd' did not happen to refer to an open descriptor, the program would terminate since the fcntl() would fail (btw, the success of the fcntl() call would be harmless if 'fd' happened to refer to an open descriptor). 2.1.3: Bug fix: only_from/no_access addresses would get inverted on little-endian machines when such addresses were specified using the numeric notation (for example, 128.138.45.3). This bug was in the numeric_addr function which did not convert the result of inet_addr to host-byte-order. A work around for the bug would be to use the factorized address notation (for example, 128.138.45.{3}) 2.1.4: Bug fixes: 1) in dgram_echo(), sin_len was not being set before the invocation of recvfrom 2) in finger_shutdown(), it was possible for Srdline() to return NULL (if the remote end would close the socket without sending anything). If the RECORD option was set in the log_on_failure flags, this would cause the forked xinetd process which did the recording to die since it would try to dereference a NULL pointer. 2.1.5: Bug fixes: 1) in exec_server() service descriptor might be closed when execing server. This made it impossible to start servers for 'nowait' services. The bug occurred only Ultrix version 4.3a or (probably) later (bug discovered and fix provided by doug@seas.smu.edu) 2) for systems that supported supplementary group id's, the set_credentials() function did not set those group id's (they were being inherited from xinetd). Now initgroups(3) is called to set the supplementary group id's properly. (bug discovered and fix provided by maf+@osu.edu) 2.1.6: Bug fixes: 1) xinetd will crash after reconfiguration if there is a running server for a service that was removed from the configuration and which logs on exit. 2) xinetd forked process falls in infinite loop if identd server sends a reply that is missing the ending CR-LF. (bug discovered and fix provided by Laurent.Wacrenier@gin.obspm.fr). We also change the LOGUSER_SUCCESS_TIMEOUT constant in config.h from 0 seconds (i.e. infinite timeout) to 30 seconds. This avoids infinite waits in case the remote host does not send a RST reply when we attempt to connect to the IDENTD port, and there is no server listening at that port. 2.1.7: Bug fix: the HOST flag in the 'log_on_success' attribute was ignored; the code was incorrectly checking if the HOST flag was set in the log_on_failure attribute (bug discovered by frolich@corrine.cpc.cs.ucf.edu) 2.1.8.1: Added support for TCP redirect to a remote host. --bbraun 2.1.8.2: Added support for binding to specific interface. --bbraun 2.1.8.3: Changed redirect so that a remote port can be specified as well. --bbraun 2.1.8.4 10/98: Changed use of varargs to be more modern (and actually work on IRIX 6.5) Added use of 1.2.3.4/24 style address ranges for access control --bbraun 2.1.8.4p2 11/98: Fixed stupid error with logging of ip addresses. Pointed out by Todd R. Eigenschink 2.1.8.4p3 11/98: Fixed to compile correctly under HPUX Includes fixes from Pavel Roskin 2.1.8.4p4 11/98: Fixed to compile correctly under BSDi 2.1.8.4p5 11/98: Fixed Linux libc5 to work with RPC. Pointed out by Frodo Looijaard 2.1.8.4p8 11/98: Minor jump because I went through many revisions of testing things - Fixed littleendian bug with 1.2.3.4/26 style access control - Added support for inet_addr instead of inet_aton, as Solaris 2.5.1 does not support inet_aton. Both the previous bugs were found with the help of Thomas E. (tht@inlink.com) - Went entirely to stdarg.h version of variable arguement handling, since some newer platforms doen't handle varargs.h style very gracefully. If this breaks too many older architectures I'll ifdef all the old stuff back in... 2.1.8.5 11/98: Fixed /tmp/xinetd.dump symlink problem pointed out on BugTraq by Balazs Nagy 2.1.8.5p1 1/99: Fixed compilation problems on BSD systems. 2.1.8.5p2 1/99: Fixed compilation problems on AIX 4.1.x/powerpc 2.1.8.5p3 3/99: Fixed compilation problems on FreeBSD Thanks to Sascha Schumann Fixed a bug in sio/sprint.c Thanks to Steven Stanfield 2.1.8.6b1 4/99: Rudimentary administrative interface added (use the service name xadmin). Supports the "banner" directive, which is the name of the file that will be splatted when a connection is denied. Has a fixed open() on the dump file that is much better. Thanks to Steven Stanfield $REMOTE_HOST environment variable is set to the name of the remote host. Currently, if no name is available, it is set to NULL. 2.1.8.6b2 4/99: Fixed a problem with shells that don't define $PWD 2.1.8.6b3 4/99: Fixed a problem I introduced with automagic port selection If both a port and a protocol are specified, no checks are done. xinetd will believe the config, even if /etc/services says otherwise. xadmin service is an internal service now, just like tcp echo, etc. Use the INTERNAL flag. xadmin service now just calls server_dump when a "show run" command is executed. Added a NAMEINARGS flag, so that you can use tcpd with xinetd. Normally, xinetd takes argv[0] from the "server" directive. This prevents tcpd from working properly. Now, you can use tcp wrappers like this: service telnet { scoket_type = stream protocol = tcp flags = NAMEINARGS wait = no user = root server = /usr/libexec/tcpd server_args = /usr/libexec/telnetd } 2.1.86b4 4/99: Added support for libwrap. Compile with --with-libwrap. Access checking is done with libwrap (if compiled in) first, then with xinetd's internal access control. 2.1.8.6b5 4/99: Some versions of libwrap needed allow_severity. Added it. Changed severity to INFO from WARNING. 2.1.8.6b6 7/99: Added ability to use names for the bind and redirect functions. They lookup the name, and use the first address it returns. They only do the lookup once. Added "interface" as an alias to "bind" Fixed potential bug with redirection losing data. Pointed out by Solar Designer Fixed potential bug in the parsing of xadmin commands. Pointed out by Solar Designer Changed default location of the dump file to /var/run/xinetd.dump configurable in config.h Added Solar Designer's per_source feature. Allows you to limit the number of services spawned per source address. Added Solar Designer's supplementary groups fix. 2.1.8.6b7 7/99: Updated the make files to not require absolute paths. This will help with people having problems with the LOCATION Makefile variable. Updated configure to compile properly with Debian's version of libwrap and glibc (look for yp_get_default_domain in nsl) If banner directive is used, banner is printed regardless of access control. It is actually printed before access control check take place. banner_success and banner_fail are added to explicitly give a message depending on access control. Added banner_success option. This is a banner that is printed when access to the service is granted. This is before any attempt to execute the server has been made. The service may still fail but it will not be for access control reasons. Added banner_fail option. This is a banner that is printed when access to the service has been denied. This allows you to inform your users that they are doing something bad and they shouldn't be doing it anymore. Added max_load option. This option allows the operator to specify the max load at which to run the service. If the machine reaches the specified max load level, connections are denied to that service. Each service can have a different max_load, and it can be listed in the default service. Linux is working, Solaris is kind of wierd. Solaris people should try it, but don't use it as a real security mechanism yet. 2.6 and 2.7 should be fine. I'm not sure about 2.5.1 and earlier. 2.1.8.7 10/99: Fixed per_source to actually work properly. Added the NODELAY flag. This will set the TCP_NODELAY sockopt on the socket. If the service is not a TCP service, then this flag has no effect. Updated the man page with more explicit definition of the "groups" attribute. Includes xconv.pl to replace itox. xconv.pl handles most of the inetd.conf files I've seen, including the use of tcpd, even though it is recommended that you compile with libwrap instead of using tcpd. Includes a man page to itox donated by Norbert Veber of Debian. Updates to configure and Makefile to better handle the configuration of install directories. Again, changes donated by Debian group. Updates to itox to handle user.group syntax and checking on "wait". 2.1.8.8pre1 11/99 Fixed a problem with the banner_fail parser. Added IPv6 support to xinetd. 2.1.8.8pre2 12/99 Added the option "cps". This allows you to disable a service if the rate of incoming connections is too great. This number may be set higher than the instances or per_source number. This is used as a last ditch measure, if someone is bombarding a service, and either 1) logs are piling up because of failed attempts, or 2) way too much is happening, ditch the service. This actually does a close() on the socket, so nothing is listening to that port anymore. All connections will fail. 2.1.8.8pre3 12/99 Made the options mask_t an unsigned long long (64bits on x86) instead of an unsigned. xinetd was running out of bits to store options in. Eventually, a real solution will need to be implemented, but this works for the short term. This may break on compilers that don't understand long long's. Be aware. Most modern compilers are ok. Added the option "enabled", similar to the "disabled" function. If "enabled" is used, only the services listed in the enabled line are available, regardless of what other services are configured. Changed the behavior of only_from and no_access. First, if you specify a host by _name_ in only_from or no_access, a lookup happens when a client connects. The _canonical_ name that is returned is compared to the name specified in the access control option. If the _names_ match, access is granted or denied. See the readme for more information. Added the ability to specify .domain.com to the access control options. This is very similar to tcp wrapper's method of specifying domain access. If the connecting client's reverse lookup returns a name that ends in .domain.com then access is granted or denied. See the readme for more information. 2.1.8.8pre4 12/99 The enable function didn't work. It worked for one entry, but more than 1 entry would not be recognized and xinetd would exit with no services available. I believe this has been fixed. limits.h is included in parsers.c now, because of LONG_MIN and LONG_MAX. Some setups would automagically include limits.h through the other include files, and some wouldn't. This should fix compile problems on those that don't. Added a check for sys_siglist in the configuration script. This is better than statically defining #ifdef's in signals.c. 2.1.8.8pre5 12/99 Fixed numeric addresses being entered in the only_from field. host_addr parser was identifying them and marking them as HOST_ADDRs instead of NUMERIC_ADDRS. 2.1.8.8pre6 1/100 Fixed year formatting in log to print 00 instead of 100. 2.1.8.8pre7 1/00 Fixed the TIMEOFFSET macro in builtins.c so the 'time' service printed the correct output. 2.1.8.8pre8 1/00 Fixed a problem where banners would not work saying "could not find banner: bannername". 2.1.8.8pre9 1/00 The dump file was logging the ip address incorrectly for only_from addresses. For some reason xntoa() was reporting the wrong address. Changed to inet_ntoa, and works fine. BSDI 4.1 was not compiling correctly, "inet_ntoa" not found. This is because BSDI4.x switched to using bind 8 resolver libraries, so you have to include to #define all these functions to __func_name. Some BSD's don't let you set the group permissions of a process to NULL, so you get the setgroups(0,NULL) error whenever a connection is made. To avoid this, set 'groups = yes' and be aware of the extra group permissions the server may be running with. A message to this effect has been added to the syslog error, so confusion is minimized. 2.1.8.8pre10 2/00 Fixed a syntax error when compiling IPv6 support. 2.1.8.8pre11 2/00 Always call no_control_tty(). This calls setsid() and fixes a problem under FreeBSD. 2.1.8.8 2/00 Bumped version number. 2.1.8.9pre1 Added the "include" directive. You can now include other files into your .conf file. "include filename" Added preliminary an inetd compatibility mode. Start xinetd with -inetd-compat and specify /etc/inetd.conf as your configuration file. 2.1.8.9pre2 Fixed up RPC support 2.1.8.9pre3 Incorporated patches for Mac OS X and Tru64 support. Also incorporated a patch for the includedir directive. 2.1.8.9pre4 Incorporated a patch to allow logging to the AUTHPRIV level. patch from Trond Eivind Glomsr. Numerous cleanups. Mostly superficial, but gets rid of *lots* of compile warnings when using -Wall. These cleanups may have affected portability issues... Support for Darwin! Now compiles and runs fine. Note that xinetd doing mmap didn't work right (always lost the first byte of the file). Workaround is to disable mmapped io for Darwin. Added a DISABLE flag for services, that will prevent a service from starting. Added a "disable" boolean for services that does the same thing as the DISABLE flag. "groups" can now be specified in the defaults section. 2.1.8.9pre5 Implemented better error checking in redirect.c, so hopefully it will detect error conditions more reliably and prevent lots of child xinetd's running unecisarily. Ramon Krikken sent a redirector implementation that replaced the two process redirection with a single process using select. Modifications to his patch were integrated. includedir parses only regular files, or symlinks to regular files, that do not begin with '.'. Added includedir to the xinetd.conf man page. 2.1.8.9pre6 Fixed a bug in the access lists. If you specified a host by name in only_from, any connection from a host without a reverse dns entry would be accepted. 2.1.8.9pre8 Now allows you to specify multiple instances of the same service as long as all but one is disabled. Fixed a documentation issue in the xinetd.conf man page. The user attribute can be specified for an unlisted service, just not an internal service. When including files with "includedir", it parses the files in alphabetical order, as determined by strcmp(). Under Solaris, I've removed the no_control_tty() call in child.c. This was causing some confusion. xinetd its self still calls no_control_tty(). Fixed a compile error with --with-inet6 Changed the exiting behavior: xinetd now kills only the RPC services and internal services (like redir) when it exits (or does a hard reconfigure). This keeps things like telnet sessions open across restarts of xinetd (assuming you are using REUSE). 2.1.8.9pre9 Fixed a potential bug in parsing of filenames from includedir. Possibly fixed tcp wait=yes handling. Fixed man pages so they say they're installed in the sections they are really installed into. Added .cvsignore to empty directories 2.1.8.9pre10 Hopefully fixed a few compile errors on architectures such as DUNIX and Darwin. When compiled with libwrap support, xinetd passes the server name to be checked in hosts.{allow,deny} instead of the service name. Behavior should now match tcpd. Incorporated Trond's pidfile patch. You can now specify -pidfile on the command line, and xinetd will make a /var/run/xinetd.pid file. Note that this _replaces_ the -pid option. 2.1.8.9pre11 Removed '\n's from syslog messages. Also moved some of the syslog()'s to pasemsg()'s. Added a patch from mob@de.uu.net to make the bind attribute specifiable in the default section. Added the KEEPALIVE flag, which sets the SO_KEEPALIVE socketopt on tcp sockets. Added a patch from Trond at RedHat that will hopefully fix some of the remaining tcp wait=yes problems. More paranoid handling of access control in addr.c Always allow access to the internal pseudo services. For internal services and libwrap, access control is performed by the service name (instead of the server, since there is no server). The last two entries together fix problems with segfaults when doing access control. If a hostname is specified in only_from, xinetd will try to match the connecting address to any of the IP addresses associated with the hostname in only_from. For redirection services with libwrap support, the service name is used for access control rather than the server name, since the server name makes no sense. 2.1.8.9pre12 Remove the pidfile when exiting. Added a -stayalive option to keep xinetd running even when there are no services available. 2.1.8.9pre13 Added paranoid access control for the udp internal servers. Do not reply to udp packets on dangerous ports (avoid looping echo services). For libwrap, if the server isn't specified use the service _id_ instead of the service name (this usually is the service name). This makes more sense for things like the internal servers (echo-stream instead of using echo, which will get echo-stream and echo-dgram) Included an rlimit patch from Nick Burrett, which should help keep some nasty users at bay. He's even updated the man page. This patch brings a tear to my eye. 2.1.8.9pre14 Moved the libwrap and address checking to the child process. The purpose of this is to move the expensive checking into the child process (such as name resolution, exec()'ing, etc), but leave some of the lighter weigh checking in the parent in an attempt to prevent DoS'. Incorporated Charles Levert's NOLIBWRAP patch. This adds a flag, NOLIBWRAP, which will turn off libwrap access control for a single service. This is a good thing when you're wanting to use tcpd with a service. It's also useful when wanting libwrap for some lesser used services, but the high volume ones need higher performance. Included Motonobu Ichimura's patches for v6. The first one sets v6 services with incoming v4mapped or compatible addresses to AF_INET instead of AF_INET6. This should make incoming v4 connections to v4 servers work correctly. Motonobu Ichimura's second patch adds the service attribute v6config, which allows a v6 service to only accept v6 connections. Moved most of the configured defines into a config.h. This involved renaming xinetd/config.h to xinetd/xconfig.h The configure system actually works with --srcdir now. You can build xinetd from a directory other than the source directory now. Minor cleanups to remove warnings with -Wall 2.1.8.9pre15 5/20/2001 Added Steve Grubb's SENSOR patch which provides a SENSOR flag and deny_time attribute. These will help to stop script kiddies doing port scans by turning off all access to all services on all IP addresses until the timeout expires. Cleaned up pset stuff in the lib section. Continued fixing warnings produced by -Wall Added "child" security check for internal services. Fixed some possible memory leaks. Changed snprintf to the internal strx_nprint() function, standardizing on that. Removed the need for the snprintf() implementation for systems lacking snprintf(). Another attempt to fix the tcp wait condition. TCP wait services were being accept()'d when they shouldn't This caused services such as linuxconf, which accept() their own connections, to fail. Fixed a problem with displaying bound and redirected addresses in the xadmin service. Don't print banner always and banner success twice. Converted to ANSI C style prototypes instead of K&R. Use spaces instead of 3 character tabs. Use POSIX types for network, time, and other length specific purposes. Make the initial log message of compile time options atomic. When grow()'ing the environment variables, initialize things to prevent bad pointers. Mitigated consequences of possible SIGCHLD race with intercepted services. Removed all remnants of varargs syntax, and have completely moved to the stdarg style syntax. Added BSD/OS loadavg support from Robert Brewer at LavaNet. Fixed a potential buffer overflow when using USERID. Found by zen-parse. Added permissions to the pidfile's open call. Suggested by zen-parse. Changed xinetd's umask to 022 from 0. 2.1.8.9pre16 6/13/2001 Attempted to fix IPv6 support broken in the pre15 release. -Steve Grubb Moved remote_address_check ahead of the libwrap checks. This is needed so that SENSORS work without modifying the hosts.allow file if tcp_wrappers is compiled in. -Steve Grubb Added missing includes to several library files Fixed bug where DISABLE flag was being set/cleared in xflags rather than types. -Matthias Andree Fixed memory leak in attr_check. -Steve Grubb Continued converting to ANSI C prototypes. -Steve Grubb Fixed reads in service.c to continue through interupts. -Steve Grubb Corrected Includedir directive. -Solar Designer Changed umask to OR 022 with the current umask. -Solar Designer Cleaned up the address list dump to format the different addresses types correctly. -Steve Grubb Cleaned up parsing of address lists. -Steve Grubb Integrated support for building on OS X. Added wait/nowait support to "small services". Fixed IPv6 support for "small services". Added "umask" keyword to specify service's umask in octal. Umask situation is described in the umask section of xinetd.conf.man Inspect all configuration files. Will now emit warnings if any are world writeable, symlinks, or not owned by root.-Steve Grubb 2.3.0 Fixed a bounds checking case in strx* functions. Pointed out by Sebastian Krahmer. 2.3.1 Reworked all headers in xinetd directory. -Steve Grubb Fixed redirect & shutdown to read & write through signal interrupts. -Steve Grubb Inspect all servers. Prints warnings for things that look funny. Applied _many_ security and reliability fixes, see AUDIT. -Solar Designer Removed the Soft Reconfigure capability. Soft Reconfigure is now the same as Hard Reconfigure (SIGUSR1 = SIGUSR2). -Steve Grubb Attempt to fix segfaulting seen since 2.3.0. -Steve Grubb Moved the re-enable service after cps violation to run off of the flags system to avoid re-entrancy issues. -Steve Grubb Only call drain if service is active. Deactivated services close the descriptor. -Steve Grubb Reorganized flags to process terminating children first, -Steve Grubb 2.3.2 more K&R -> ANSI prototype -Steve Grubb Stop using C++ keywords (even though this is really C...) -Steve Grubb Fix a heap overrun in grow(). -teg@redhat.com Fix a parse error with multiple explict masks. If protocol is not specified, but socket type is, infer protocol from socket type. -Ahmon Dancy Added a check to the return value of env_addstr() in child.c to make sure we aren't exec()ing with an invalid environment. Wrap the remaining includes with autoconf macros. Removed all unused functions. -Steve Grubb Don't use SIGALRM for the cps directive. Instead, do a generic timer routine that evolves around the main event loop. 2.3.3 Fixed the filelog problem of printing garbage. Fixed the RPC parser to correctly handle RPC version ranges again. 2.3.4 Removed the old flags construct and replaced it with the timers added in 2.3.2. This will handle multiple of the same signals occuring before the event loop cycles. Removed all the old timer code that uses SIGALRM, and replace it with the timer code from 2.3.2. The conf timer bit the dust along the way, since the event loop hasn't started by the time the conf parser started. It was never built anyway, so not a big loss. Made xinetd unlink its pidfile properly. - Solar Designer Enabled loadavg support for Darwin. Remove the exit when user or group is specified and xinetd is not root. Enables running xinetd as non-root again. Fixed various portability issues: - strerror for SunOS 4 - difftime for SunOS 4 - inet_aton for Solaris 2.5.1 - uint{16,32,64}_t for systems without them - rlim_t for systems without them. Moved compat.c into the portable library, where it belonged. Added finer testing of the {e,f,g}cvt functions in autoconf. Begin transition to combined IPv4/IPv6 support. Add compatibility code from OpenSSH into the portable library. Made socket_type or protocol optional (only need to specify one) Removed syslog()'s, replaced with internal msg(). Fixed a parsing bug when specified server is invalid. Change the signal handling. Signal handlers write their signal to a pipe, which wakes up the main select(), and the signal is then dealt with. Removed the looping option. The cps directive duplicated much of the functionality. Fixed a bug re-enabling internal wait services. Continue syncing IPv4 and IPv6 code. Made a flag to specify IPv4 or IPv6 based service. Redirect currently only redirects between IPv4<->IPv4 or IPv6<->IPv6. It should handle IPv4<->IPv6 and vice versa Fixed the reconfig case where a bind directive changes. SIGHUP now reconfigures xinetd. SIGUSR1 now dumps the internal state. SIGUSR2 still reconfigures xinetd, but will be deprecated in future releases. Fixed a warning of redefined SA macro with some tcp wrapper header files. Make the configuration keyword comparisons case insensitive. This allows things like Service foo { blah } instead of just service foo { blah }. Merged IPv4 and IPv6 support. The only difference between compiling with IPv6 support is that services default to being IPv6. Manually set the service to IPv6 (or IPv4) with the IPv{4,6} service flags. The only_from numeric address specification works for IPv6 addresses, and mixing IPv4 style address specifications will now match for IPv4 mapped addresses on IPv6 services. Fixed an environment variable setting problem. Fixed a misuse of the sio routines in the banner routines. This will probably fix a variety of bugs related to banners. More code cleanups. -Steve Grubb. Pass the expected size of the address structure used to bind() calls rather than the total memory allocated. Fix some compile errors and warnings on BSD/OS. Prevent some possible unnecissary DNS lookups. Makefiles now accept make -j. This is only mildly useful at the moment, since the main chunk of xinetd blocks on all the libraries being built first, and the libraries are small but somewhat serialized. Fixed a problem with the access control on builtin services. (Introduced in the development cycle) Removed libpq. It is no longer needed. -Steve Grubb Removed the %n processing from __sio_converter(). It is not used and would cause a core dump if it was. -Steve Grubb Make the man page match the new signal change. - Steve Grubb Fix some potential problems with only_from access. Start using "const". Go const happy. All services now essentially default to having the REUSE flag. The -reuse option and the REUSE flag are now silently ignored. When dumping service configs, print out the redir config information, also fixes a hostname lookup problem when using systems that have a too strict getnameinfo(). Also print the CPS directive information. Print out symbolic system names that don't have strsignal(), but do have sys_siglist[]. Fix a potential crasher in xtimer_add(). Add %q and %ll modifiers to the sio library for displaying quad_t's or long long's. Restructure the argument parsing a little to reduce global memory usage. Fixed bug in service parameter verification. Prior versions did not verify that all require attributes were specified. Disable the service when a parse error is detected for its configuration. (Steve Grubb) 2.3.5 Included patch from Trond at RedHat to check signal pipe if there are no services running. Update the access_times parser to disable service if there is a parse error. Also corrected ti_add too. -Steve Grubb Updated all parsers to propagate errors so service will be disabled. -Steve Grubb Updated internal services to check the signal pipe periodically. -Steve Grubb Updated sendsig to wait for termed children so zombies aren't created and ports are clear to rebind to on hard_reconfig. -Steve Grubb Included a patch from Hendrik Visage for a problem with ident. 2.3.6 Updated only_from to 'and' both the remote address and the specified portion if a network mask type address is used. -Steve Grubb Updated explicit_mask to chose NUMERIC_ADDR if mask is 32. -Steve Grubb Many parser updates. -Steve Grubb Propogate default attributes even if not specified. log_on_success, log_on_failure, passenv were the main items affected by this problem. only_from and no_access now conform to this new propogation technique. -Steve Grubb Xremove now uses domain names in addition to ip addresses. -Steve Grubb Moved sensor code into its own file and changed it to use timer facility. -Steve Grubb Reworked remote_addr check & addrlist_match to not cache IP addresses and corrected several algorithmic problems. -Steve Grubb Off-by-one error corrected in pset_delete. -Steve Grubb Updated attrfill to use IPv4 addresses if bind specified. -Steve Grubb Fixed bug in libwrap code to check server better for NULL -Steve Grubb Added better testing of configuration if NAMEINARGS flag is set. -Steve Grubb Updated config.guess & config.sub. -Thomas Seyrat Added an rpm spec file. -Steve Grubb Removed sio/suite directory. It appears that key files have been deleted in the past so it no longer works. -Steve Grubb Make the file descriptor buffer allocation dynamic. This substantially reduces xinetd's normal memory footprint (~760KB). -Rob Braun Work around bugs in Mac OS X's getrlimit/setrlimit which causes them to be almost unusable. -Rob Braun 2.3.7 Added fixes or workarounds for issues introduced after 2.3.3 including the signal pipe leak into child processes (a security hole). -Solar Designer Made xinetd unlink its pidfile when there turns out to be no services configured on reload. -Solar Designer 2.3.8 Reworked redirect to better detect problems in its configuration. Also, redirect now allows service names for port numbers. -Steve Grubb Reworked attribute checking in confparse & updated attr.h. -Steve Grubb Ensure that children have the default signal handlers installed. Added support for DNS service registration ala Rendezvous. Fixed some compile errors on Mac OS X, FreeBSD, and OpenBSD. Added preliminary support for tcpmux -Philip Armstrong Update the xinetd man page to document the -version option. Now ignores the --with-inet6 compile option. Services will default to IPv4 unless configured otherwise. Bring back the inetd.conf parser in a different form. Parse all the xinetd config files first, then parse /etc/inetd.conf, and add services from there (if it exists). Use the -inetd_compat option to read inetd.conf. IPv6 updates for bind_parser, only_from, and no_access. -Steve Grubb 2.3.9 Fixed bug uncovered by IPv6 updates for bind parser. In the id_parser, a test was being performed for uniqueness. It relied on the address already being given. However, the address may not be known if more than one record came back from the getaddrinfo function call. -Steve Grubb Added code to service_fill to resolve port if unspecified. -Steve Grubb Consolidated duplicate services tests into check_entry. -Steve Grubb Fixed a bug with access control & internal services. Make sure we byteswap the value returned by time services. Fix an omission with the tcpmux integration. Fixed a race when there's 1 service configured and it is a wait service. 2.3.10 Close the service descriptors on fork. This only matters for internal forking services, since anything that calls exec() will get those closed automagically. This will help reduce the file discriptors used by the daemon when using some internal services Fix a numbering bug with xinetd's internal flag representation that was manifesting its self as all services being disabled upon recieving a SIGUSR2 (hard reconfig) Don't pass a hostname to dns registration calls, it'll pick a hostname automagically. Remove CLEANUP and other dead code. Make sure tcp internal non-forking services close their filedescriptors. Added syslog facilities for the xinetd log configuration to match SUS. - Steve Grubb Start reporting the Per Source value when dumping debugging output - Steve Grubb Correct the fake-getnameinfo.h to include appropriate macros. - Steve Grubb Up the default CPS value to 50 from 10. Document the default CPS values in the xinetd.conf man page. Fix a closing of the connection when there's an error handling the service. - Steve Grubb Fixed a bug in the includedir path where a reference was kept to free()'d memory. Fixed a bug in the consistency check mechanism where it would report the signal pipe as a problem. Clear memory before freeing it. This isn't strictly necessary, but aids in debugging. Add the pid to debugging messages. Remove access control calls for special services. The access control functions must always allow these services anyway. Fixed a bug with reconfiguring services using the interface attribute. -- Adam Lukosek Fixed a bug with deallocating dns registrations. Fixed a bug where tcp_wrappers would not identify an internal service since the internal service has no executable. -Steve Grubb Sclose was being used on the config file descriptor. Since sio never touches the descriptor, it failed to close it. - Steve Grubb Fix a potential memory leak with bind_addr and service reconfig. - Andrey Alekseyev Fix a reconfig timing issue. - Andrey Alekseyev 2.3.11 Fix some compile time errors on Solaris Fixed a bounds check in Sdone(). Patch from Dmitry V. Levin Added FreeBSD loadavg support. Patch from Vanilla I. Shu TCPMUX parser updates. -Steve Grubb TCPMUX was causing core dumps due to changes made in 2.3.10's child_process(), reverted changes. -Philip Armstrong Remove RECORD logging option. -Steve Grubb Change Sclose to make sure it always closes the fd. -Steve Grubb Added better error handling to filelog.c. -Steve Grubb Error messages now go to syslog instead of stderr in strparse.c. -Steve Grubb Fixed memory leaks found with valgrind. -Steve Grubb Correct problems with bind specified in the default section. -Steve Grubb Use Sclose to close banner commands. -Jay Fenlason Correct banner to match man pages. -Jay Fenlason 2.3.12 Cleanup some signal handling if not defined. -MARUYAMA Shinichi Make ident protocol work properly for multi-homed hosts. -Alan Sundell Code cleanup for const warnings. -Steve Grubb Make redirect protocol independent. -Cougar Make reconfig iterate over all services. Some may be in "not started" state and were being missed. -Steve Grubb Make redirected, forking builtins, & tcpmux close all listening descriptors so reconfig works. -Jay Fenlason Add support for the IPV6_V6ONLY socket option. Don't assume a default setting, as the default seems to be in flux. Address compare in readjust was wrong. Its now corrected to handle each address family separately. -Steve Grubb Add command line option to not fork. This will allow xinetd to be started by init or daemontools. -Matthias Andree Fixed a leak in reconfig. If log_type = file is used for a service and SIGHUP is sent to xinetd, it leaked a file descriptor and 44 bytes of memory per service using FILE. -Steve Grubb Change all close() calls to Sclose() to prevent future problems. -Steve Grubb Fixed bug if service name is unparsable and in an included directory that caused xinetd to core dump. -Steve Grubb Fixed bug where address lists had "version" uninitialized when the list used hostnames with no dots in the name. -Steve Grubb Numerous memory leaks when parsing errors occurred. -Steve Grubb Remove the servers and xadmin internal services. Update addrlist_match to use the address part of the IPv6 address structure. -Christof Meerwald Correct looping problems for udp connections rejected by the child access controls. -Steve Grubb Added TRAFFIC logging option to report total bytes in and out for a redirected service. -Christof Meerwald Correct a double-free condition if a retry was scheduled. -Steve Grubb Add filename to parse messages. -Steve Grubb Improve port error messages after parsing. -Steve Grubb Sclose dump_fd if SIO error causes dump abort. -Steve Grubb Smorefds wasn't allocating the proper amount of sio descriptor space if the fd was > 5 over its last known fd. -Steve Grubb 2.3.13 Add NULL entry to success_log_options to properly end the nvlist. -Steve Grubb Portability updates to libportable.h. -Matthias Andree Occasionally Smorefds didn't allocate more fds as expected. -Jay Fenlason Address list parsing considered the comma in factorized addresses to be an error. Updated test to allow factorized address. -Steve Grubb When parsing inet.conf, the filename wasn't being set for subsequent messages. -Steve Grubb Fix addrlist_match to correctly handle IPv4 mapped IPv6 addresses. -Christof Meerwald Fixed a bug where reloading configuration would core dump if file logging was enabled in defaults and a connection existed when SIGHUP was received. -Steve Grubb If too many connections hit and exhausts the file descriptors such that accept fails, deactivate the service like the cps access control does. This problem was reported by David Cook. -Steve Grubb Updated rpm.spec file and added default config files to contrib directory. -Steve Grubb Allow group & user to be specified by numeric value. -Steve Grubb 2.3.14 Applied patch from Art Haas for gcc 3.5 compat. Flush the descriptor after writing a banner. -Jay Fenlason Don't assume char is signed in the udp drain() function -Don Provan If log remote user is on, a descriptor between 0-2 is likely to be opened. Call msg_suspend before dup'ing socket to avoid this bug. -Glen Johnson Added confparse() RPC patch from RedHat's RHEL4 srpm. Fixed some service release bugs with accesses to dangling pointers. Updated BACKLOG to 64 from 7 Updated xconv.pl to understand ":" in inetd.conf files from FreeBSD ports patch. Added howl support for mdns advertising. Added a libwrap service attribute to specify the service name to check access via libwrap. Make some type cleanups to fix some warnings. Parse things as unsigned instead of signed where it makes sense. Based on a patch from Tony Ernst. Remove the <1024 port check for UDP builtin services. This check has been rather antiquated for years. 2.3.15 If the address we're binding to is a multicast address, do the multicast join. Merge the Fedora patch to turn off libwrap processing on tcp rpc services. Patch xinetd-2.3.12-tcp_rpc.patch. Merge the Fedora patch to add labeled networking. Patch xinetd-2.3.14-label.patch r1.4. Merge the Fedora patch to fix getpeercon() for labeled networking in MLS environments. Patch xinetd-2.3.14-contextconf.patch r1.1 Merge the Fedora patch for int->ssize_t. Patch xinetd-2.3.14-ssize_t.patch r1.1 Some modifications to this patch were necessary. Change compiler flags, -Wconversion generates excessive and unnecessary warnings with gcc, particularly all cases of ntohs(uint16_t). http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6614 Additionally add -Wno-unused to prevent unnecessary warnings regarding unused function parameters when the function is a callback conforming to a standard interface. Change version number to 2.3.15devel, indicating an interim developmental source snapshot. Merge patch from Thomas Swan regarding CVE-2012-0862 2.3.15.1 Remove unused variables a bit Restructure the repository for saner file locations Redo buildsystem for proper autotools Include patches from SUSE, Debian, and Fedora from their packages Remove HOWL support completely Remove deprecated compatibility detections that are mostly true Enable travis build to see if we build on osx (bsd) and linux Add possibility to use POLL instead of FD code to alocate xinetd-2.3.15.4/COPYRIGHT000066400000000000000000000041701333055217000145330ustar00rootroot00000000000000ORIGINAL LICENSE: This software is (c) Copyright 1992 by Panagiotis Tsirigotis The author (Panagiotis Tsirigotis) grants permission to use, copy, and distribute this software and its documentation for any purpose and without fee, provided that the above copyright notice extant in files in this distribution is not removed from files included in any redistribution and that this copyright notice is also included in any redistribution. Modifications to this software may be distributed, either by distributing the modified software or by distributing patches to the original software, under the following additional terms: 1. The version number will be modified as follows: a. The first 3 components of the version number (i.e ..) will remain unchanged. b. A new component will be appended to the version number to indicate the modification level. The form of this component is up to the author of the modifications. 2. The author of the modifications will include his/her name by appending it along with the new version number to this file and will be responsible for any wrong behavior of the modified software. The author makes no representations about the suitability of this software for any purpose. It is provided "as is" without any express or implied warranty. Modifications: Version 2.3.15.x Copyright 2017 SUSE LINUX GmbH and other parties Modifications: Version: 2.1.8.7-current Copyright 1998-2001 by Rob Braun Sensor Addition Version: 2.1.8.9pre14a Copyright 2001 by Steve Grubb This is an exerpt from an email I recieved from the original author, allowing xinetd as maintained by me, to use the higher version numbers: I appreciate your maintaining the version string guidelines as specified in the copyright. But I did not mean them to last as long as they did. So, if you want, you may use any 2.N.* (N >= 3) version string for future xinetd versions that you release. Note that I am excluding the 2.2.* line; using that would only create confusion. Naming the next release 2.3.0 would put to rest the confusion about 2.2.1 and 2.1.8.*. xinetd-2.3.15.4/Makefile.am000066400000000000000000000072161333055217000153000ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = \ contrib/empty.conf \ contrib/xinetd \ contrib/xinetd.service \ README.md \ CHANGELOG \ COPYRIGHT \ $(bin_SCRIPTS) \ $(man_MANS) \ man/m_env.3 \ man/pset.3 \ man/psi.3 \ man/sio.3 \ man/xlog.3 \ man/Sprint.3 \ man/strparse.3 \ man/strprint.3 \ man/strutil.3 \ $(sysconf_DATA) \ $(xinetconf_DATA) sysconf_DATA = \ contrib/xinetd.conf xinetconfdir=$(sysconfdir)/xinetd.d xinetconf_DATA = \ contrib/xinetd.d/daytime \ contrib/xinetd.d/daytime-udp \ contrib/xinetd.d/discard \ contrib/xinetd.d/discard-udp \ contrib/xinetd.d/echo \ contrib/xinetd.d/echo-udp \ contrib/xinetd.d/chargen \ contrib/xinetd.d/chargen-udp \ contrib/xinetd.d/servers \ contrib/xinetd.d/services \ contrib/xinetd.d/time \ contrib/xinetd.d/time-udp man_MANS = \ man/itox.8 \ man/xconv.pl.8 \ man/xinetd.conf.5 \ man/xinetd.log.5 \ man/xinetd.8 AM_LDFLAGS = $(LDFLAGS) $(HARDEN_LDFLAGS) AM_CFLAGS = $(CFLAGS) $(HARDEN_CFLAGS) AM_CPPFLAGS = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/misc \ -I$(top_srcdir)/src/portable \ -I$(top_srcdir)/src/pset \ -I$(top_srcdir)/src/sio \ -I$(top_srcdir)/src/str \ -I$(top_srcdir)/src/xlog noinst_LTLIBRARIES = \ libmisc.la \ libportable.la \ libpset.la \ libsio.la \ libstr.la \ libxlog.la libmisc_la_SOURCES = \ src/misc/m_env.c \ src/misc/m_env.h libportable_la_SOURCES = \ src/portable/cvt.c \ src/portable/libportable.h libpset_la_SOURCES = \ src/pset/ops.c \ src/pset/pset.c \ src/pset/pset.h libsio_la_SOURCES = \ src/sio/impl.h \ src/sio/sio.c \ src/sio/sioconf.h \ src/sio/sio.h \ src/sio/siosup.c \ src/sio/sprint.c libstr_la_SOURCES = \ src/str/str.h \ src/str/strparse.c \ src/str/strparse.h \ src/str/strprint.c \ src/str/strutil.c libxlog_la_SOURCES = \ src/xgetloadavg.c \ src/xgetloadavg.h \ src/xlog/filelog.c \ src/xlog/filelog.h \ src/xlog/impl.h \ src/xlog/slog.c \ src/xlog/slog.h \ src/xlog/util.c \ src/xlog/xlog.c \ src/xlog/xlog.h bin_PROGRAMS = itox itox_SOURCES = src/itox.c itox_LDADD = \ libsio.la \ libstr.la bin_SCRIPTS = src/xconv.pl sbin_PROGRAMS = xinetd xinetd_CFLAGS = \ $(HARDEN_CFLAGS) \ $(SELINUX_CFLAGS) \ $(TIRPC_CFLAGS) xinetd_SOURCES = \ src/access.c \ src/access.h \ src/addr.c \ src/addr.h \ src/attr.h \ src/builtins.c \ src/builtins.h \ src/conf.c \ src/conf.h \ src/confparse.c \ src/confparse.h \ src/connection.c \ src/connection.h \ src/defs.h \ src/env.c \ src/env.h \ src/child.c \ src/child.h \ src/ident.c \ src/ident.h \ src/includedir.c \ src/includedir.h \ src/inet.c \ src/inet.h \ src/init.c \ src/init.h \ src/int.c \ src/intcommon.c \ src/intcommon.h \ src/internals.c \ src/internals.h \ src/int.h \ src/log.c \ src/logctl.c \ src/logctl.h \ src/log.h \ src/main.c \ src/main.h \ src/mask.h \ src/msg.c \ src/msg.h \ src/nvlists.c \ src/nvlists.h \ src/options.c \ src/options.h \ src/parse.c \ src/parse.h \ src/parsers.c \ src/parsers.h \ src/parsesup.c \ src/parsesup.h \ src/reconfig.c \ src/reconfig.h \ src/redirect.c \ src/redirect.h \ src/retry.c \ src/retry.h \ src/sconf.c \ src/sconf.h \ src/sconst.h \ src/sensor.c \ src/sensor.h \ src/server.c \ src/server.h \ src/service.c \ src/service.h \ src/signals.c \ src/signals.h \ src/special.c \ src/special.h \ src/state.h \ src/tcpint.c \ src/tcpint.h \ src/time.c \ src/timex.h \ src/udpint.c \ src/udpint.h \ src/util.c \ src/util.h \ src/xconfig.h \ src/xpoll.h \ src/xtimer.c \ src/xtimer.h xinetd_LDADD = \ libmisc.la \ libportable.la \ libpset.la \ libsio.la \ libstr.la \ libxlog.la \ $(WRAP_LIBS) \ $(SELINUX_LIBS) \ $(TIRPC_LIBS) distclean-local: rm -rf *.cache *~ xinetd-2.3.15.4/README.md000066400000000000000000000057101333055217000145200ustar00rootroot00000000000000# xinetd # [![Build Status](https://travis-ci.org/openSUSE/xinetd.svg?branch=master)](https://travis-ci.org/openSUSE/xinetd) xinetd is a powerful replacement for inetd. If you are planning to use xinetd on recent linux distribution also consider using [systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html) instead. Original site (DEAD): http://www.xinetd.org xinetd has access control mechanisms, extensive logging capabilities, the ability to make services available based on time, can place limits on the number of servers that can be started, and has deployable defence mechanisms to protect against port scanners, among other things. There are a number of differences between xinetd and inetd. The largest difference to the end user is the config file. xinetd's config file format is more C like, and somewhat similar to bind 8's. # Access Control xinetd keeps all the names you specify on the access control directives. When a client attempts to connect to a service, a reverse lookup is performed on the client's IP address. The canonical name returned is compared with the specified names. If the first character of the name being specified in the config file is a '.', then all hosts within that domain are matched. For example, if I put .synack.net, all hosts with a reverse mapping that are in .synack.net domain, are matched. # libwrap support For libwrap access control, the access control is done by the server name for the service. So, if you have an entry like this: service telnet ~~~ { ... server = /usr/sbin/in.telnetd ... } ~~~ Your corresponding `hosts.{allow|deny}` entry would look something like this: ~~~ in.telnetd: ALL ~~~ However, many services don't have a "server". Internal services and redirection services don't have a "server" line in the configuration file. Fma these services, the service name is used. For example: ~~~ server telnet { ... redirect = 10.0.0.1 23 ... } ~~~ Your `hosts.{allow|deny}` entry would look something like this: telnet: ALL So, in general, if a service has a "server" attribute to it, access control is performed based on that entry. If a service does not have a "server" attribute, (internal and redirection services) then access control is based on the service name. This is only for libwrap access control. # History xinetd was originally written by panos@cs.colorado.edu. At least one other version of xinetd has been seen floating around the net. Another version is maintained by Rob Braun (bbraun@synack.net) and bug reports for that version should be directed to https://github.com/xinetd-org/xinetd/. This version is simple collection of patches contained over Rob Brauns version that were present in all major distributions. Plans are to include fixes as required for keeping it workable in openSUSE and also to merge commits from the above github branch. # Issues Bug reports/comments/suggestions/flames for this version should be sent to https://github.com/openSUSE/xinetd/issues/ xinetd-2.3.15.4/autogen.sh000077500000000000000000000001541333055217000152370ustar00rootroot00000000000000#!/bin/bash set -e CURDIR=$(pwd) SCRIPT_DIR=$(cd $(dirname $0); pwd) cd $SCRIPT_DIR autoreconf -v -i -f xinetd-2.3.15.4/configure.ac000066400000000000000000000150141333055217000155250ustar00rootroot00000000000000# Process this file with autoconf to create configure. AC_PREREQ([2.65]) # ==================== # Version informations # ==================== m4_define([xinetd_version_major],[2]) m4_define([xinetd_version_minor],[3]) m4_define([xinetd_version_micro],[15]) m4_define([xinetd_version_nano],[4]) m4_define([xinetd_version],[xinetd_version_major.xinetd_version_minor.xinetd_version_micro.xinetd_version_nano]) # ============= # Automake init # ============= AC_INIT([xinetd],[xinetd_version]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 subdir-objects foreign dist-xz dist-bzip2]) AM_SILENT_RULES([yes]) AC_LANG([C]) # =========================== # Find required base packages # =========================== AC_PROG_CPP AC_PROG_CC AC_PROG_CC_STDC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_LIBTOOL PKG_PROG_PKG_CONFIG([0.20]) AC_CHECK_LIB([m], [log10], [ LIBM_LIBS="-lm" ], [ AC_MSG_ERROR([Unable to find working libm.so]) ]) AC_SUBST([LIBM_LIBS]) # ====================================== # Check for various headers and settings # ====================================== AC_FUNC_MMAP AC_CHECK_HEADER(sys/filio.h, [AC_DEFINE([HAVE_SYS_FILIO_H], [1], [Have FreeBSD filio.h])]) AC_CHECK_FUNC(poll, [AC_DEFINE([HAVE_POLL], [1], [Have available poll function])]) # ====== # Switch # ====== AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [Build with debug symbols.]), [enable_debug="$enableval"], [enable_debug=no] ) AS_IF([test "x$enable_debug" != "xno"], [ CFLAGS="$CFLAGS -ggdb" ]) AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat all warnings as errors, useful for development])], [enable_werror="$enableval"], [enable_werror=no] ) AS_IF([test x"$enable_werror" != "xno"], [ CFLAGS="$CFLAGS -Werror" ]) CFLAGS="$CFLAGS -Wall -Wextra" AC_ARG_WITH([loadavg], [AS_HELP_STRING([--without-loadavg], [Do not use loadavg for balancing])], [with_loadavg="$withval"], [with_loadavg=yes] ) AS_IF([test x"$with_loadavg" != "xno"], [ AC_DEFINE([HAVE_LOADAVG], [1], [Have loadavg for balancing]) ]) AC_ARG_WITH([libwrap], [AS_HELP_STRING([--without-libwrap], [Do not use libwrap tcp_wrappers support])], [with_libwrap="$withval"], [with_libwrap=auto] ) AS_IF([test x"$with_libwrap" != "xno"], [ AC_CHECK_LIB(wrap, request_init, [ AC_DEFINE([LIBWRAP], [1], [Have libwrap TCP wrappers present]) WRAP_LIBS="-lwrap" AC_CHECK_LIB(nsl, yp_get_default_domain, [WRAP_LIBS="$WRAP_LIBS -lnsl" ]) AC_SUBST([WRAP_LIBS]) ],[ AS_IF([test x"$with_labeled_networking" = "xyes"], [ AC_MSG_ERROR([Missing libwrap from tcp_wrappers]) ], [ with_libwrap="no" AC_MSG_WARN([Not using libwrap from tcp_wrappers]) ]) ]) ]) AC_ARG_WITH([labeled-networking], [AS_HELP_STRING([--without-labeled-networking], [Do not use selinux for labeled networking])], [with_labeled_networking="$withval"], [with_labeled_networking="auto"] ) AS_IF([test x"$with_labeled_networking" != "xno"], [ PKG_CHECK_MODULES([SELINUX],[libselinux], [ AC_DEFINE([LABELED_NET], [1], [Use selinux for network labeling]) ],[ AS_IF([test x"$with_labeled_networking" = "xyes"], [ AC_MSG_ERROR([selinux support requested but not found]) ], [ with_labeled_networking="no" AC_MSG_WARN([selinux support will not be built in]) ]) ]) ]) AC_ARG_WITH([rpc], [AS_HELP_STRING([--without-rpc], [Do not include RPC support])], [with_rpc="$withval"], [with_rpc="auto"] ) AS_IF([test x"$with_rpc" != "xno"], [ PKG_CHECK_MODULES([TIRPC],[libtirpc], [ AC_DEFINE([HAVE_RPCENT_H], [1], [Have .]) ], [ AS_IF([test x"$with_rpc" = "xyes"], [ AC_MSG_ERROR([RPC support requested but not found]) ]) with_rpc="no" AC_MSG_WARN([Libtirpc not found, will not use ]) ]) ]) AS_IF([test x"$with_rpc" = "xno"], [ AC_DEFINE([NO_RPC], [1], [Omit RPC support.]) ]) # ========= # Hardening # ========= # We use the same hardening flags for C and C++. We must check that each flag # is supported by both compilers. AC_DEFUN([check_cc_cxx_flag], [AC_LANG_PUSH(C) AX_CHECK_COMPILE_FLAG([$1], [$2], [$3], [-Werror $4]) AC_LANG_POP(C)]) AC_DEFUN([check_link_flag], [AX_CHECK_LINK_FLAG([$1], [$2], [$3], [-Werror $4])]) HARDEN_CFLAGS="" HARDEN_LDFLAGS="" check_cc_cxx_flag([-fno-strict-overflow], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fno-strict-overflow"]) # This one will likely succeed, even on platforms where it does nothing. check_cc_cxx_flag([-D_FORTIFY_SOURCE=2], [HARDEN_CFLAGS="$HARDEN_CFLAGS -D_FORTIFY_SOURCE=2"]) check_cc_cxx_flag([-fstack-protector-all], [check_link_flag([-fstack-protector-all], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fstack-protector-all" check_cc_cxx_flag([-Wstack-protector], [HARDEN_CFLAGS="$HARDEN_CFLAGS -Wstack-protector"], [], [-fstack-protector-all]) check_cc_cxx_flag([--param ssp-buffer-size=1], [HARDEN_CFLAGS="$HARDEN_CFLAGS --param ssp-buffer-size=1"], [], [-fstack-protector-all])])]) # At the link step, we might want -pie (GCC) or -Wl,-pie (Clang on OS X) # # The linker checks also compile code, so we need to include -fPIE as well. check_cc_cxx_flag([-fPIE], [check_link_flag([-fPIE -pie], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fPIE" HARDEN_LDFLAGS="$HARDEN_LDFLAGS -pie"], [check_link_flag([-fPIE -Wl,-pie], [HARDEN_CFLAGS="$HARDEN_CFLAGS -fPIE" HARDEN_LDFLAGS="$HARDEN_LDFLAGS -Wl,-pie"])])]) check_link_flag([-Wl,-z,relro], [HARDEN_LDFLAGS="$HARDEN_LDFLAGS -Wl,-z,relro" check_link_flag([-Wl,-z,now], [HARDEN_LDFLAGS="$HARDEN_LDFLAGS -Wl,-z,now"])]) AC_SUBST([HARDEN_CFLAGS]) AC_SUBST([HARDEN_LDFLAGS]) AC_SUBST([CFLAGS]) # ============== # Setup config.h # ============== AC_CONFIG_HEADERS([config.h]) # ===================== # Prepare all .in files # ===================== AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT # ============================================== # Display final informations about configuration # ============================================== AC_MSG_NOTICE([ ============================================================================== Build configuration: debug: ${enable_debug} werror: ${enable_werror} labeled-networking: ${with_labeled_networking} libwrap: ${with_libwrap} loadavg: ${with_loadavg} rpc: ${with_rpc} HARDEN_CFLAGS: ${HARDEN_CFLAGS} HARDEN_LDFLAGS: ${HARDEN_LDFLAGS} ============================================================================== ]) xinetd-2.3.15.4/contrib/000077500000000000000000000000001333055217000146765ustar00rootroot00000000000000xinetd-2.3.15.4/contrib/empty.conf000066400000000000000000000024041333055217000167030ustar00rootroot00000000000000# This is an empty xinetd service configuration. To add a service to # xinetd copy this file to /etc/xinetd.d giving it a name similar to the # service you are adding. Next change the service name before the curly # brace to match the name found in /etc/services. Then modify the following # attributes service empty { # This is for quick on or off of the service disable = no # The next attributes are mandatory for all services id = type = wait = socket_type = # protocol = socket type is usually enough # External services must fill out the following user = group = server = server_args = # External services not listed in /etc/services must fill out the next one port = # RPC based services must fill out these rpc_version = rpc_number = # Logging options log_type = log_on_success = log_on_failure = # Networking options flags = bind = redirect = v6only = # Access restrictions only_from = no_access = access_times = cps = instances = per_source = max_load = deny_time = mdns = # Environmental options env = passenv = nice = umask = groups = rlimit_as = rlimit_cpu = rlimit_data = rlimit_rss = rlimit_stack = # Banner options. (Banners aren't normally used) # banner = # banner_success = # banner_fail = } xinetd-2.3.15.4/contrib/xinetd000077500000000000000000000051071333055217000161220ustar00rootroot00000000000000#!/bin/bash # # xinetd This starts and stops xinetd. # # chkconfig: 345 65 50 # description: xinetd is a powerful replacement for inetd. \ # xinetd has access control machanisms, extensive \ # logging capabilities, the ability to make services \ # available based on time, and can place \ # limits on the number of servers that can be started, \ # among other things. # # processname: /usr/sbin/xinetd # config: /etc/sysconfig/network # config: /etc/xinetd.conf # pidfile: /var/run/xinetd.pid PATH=/sbin:/bin:/usr/bin:/usr/sbin # Source function library. . /etc/init.d/functions # Get config. test -f /etc/sysconfig/network && . /etc/sysconfig/network # More config test -f /etc/sysconfig/xinetd && . /etc/sysconfig/xinetd # Check that networking is up. [ ${NETWORKING} = "yes" ] || exit 0 [ -f /usr/sbin/xinetd ] || exit 1 [ -f /etc/xinetd.conf ] || exit 1 RETVAL=0 if [ "$NETWORKING_IPV6" = "yes" ] && [ -x /usr/sbin/xinetd6 ]; then prog="xinetd6" else prog="xinetd" fi start(){ echo -n $"Starting $prog: " # Need to get rid of localization for external services - # it doesn't make much sense to have i18n on the server side here LANG=en_US LC_TIME=en_US LC_ALL=en_US LC_MESSAGES=en_US LC_NUMERIC=en_US LC_MONETARY=en_US LC_COLLATE=en_US export LANG LC_TIME LC_ALL LC_MESSAGES LC_NUMERIC LC_MONETARY LC_COLLATE unset HOME MAIL USER USERNAME daemon $prog -stayalive -reuse -pidfile /var/run/xinetd.pid "$EXTRAOPTIONS" RETVAL=$? echo touch /var/lock/subsys/xinetd return $RETVAL } stop(){ echo -n $"Stopping $prog: " killproc $prog RETVAL=$? echo rm -f /var/lock/subsys/xinetd return $RETVAL } reload(){ echo -n $"Reloading configuration: " killproc $prog -HUP RETVAL=$? echo return $RETVAL } restart(){ stop start } condrestart(){ [ -e /var/lock/subsys/xinetd ] && restart return 0 } dump(){ echo -n $"Dumping configuration: " killproc $prog -USR1 RETVAL=$? echo return $RETVAL } check(){ echo -n $"Performing Consistency Check: " /bin/kill -s IOT $prog RETVAL=$? echo return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $prog ;; restart) restart ;; reload) reload ;; condrestart) condrestart ;; dump) dump ;; check) check ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|reload|dump|check}" RETVAL=1 esac exit $RETVAL xinetd-2.3.15.4/contrib/xinetd.conf000066400000000000000000000026601333055217000170440ustar00rootroot00000000000000# # This is the master xinetd configuration file. Settings in the # default section will be inherited by all service configurations # unless explicitly overridden in the service configuration. See # xinetd.conf in the man pages for a more detailed explanation of # these attributes. defaults { # The next two items are intended to be a quick access place to # temporarily enable or disable services. # # enabled = # disabled = # Previous default in SUSE - please don't forget to use the logrotate. The # sample configuration is in /usr/share/packages/doc/xinetd/logrotate # log_type = FILE /var/log/xinetd.log # Define general logging characteristics. log_type = SYSLOG daemon info log_on_failure = HOST ATTEMPT log_on_success = HOST EXIT DURATION # Define access restriction defaults # # no_access = # only_from = localhost # max_load = 0 cps = 50 10 instances = 30 per_source = 10 # # The specification of an interface is interesting, if we are on a firewall. # For example, if you only want to provide services from an internal # network interface, you may specify your internal interfaces IP-Address. # # bind = 127.0.0.1 # Address and networking defaults # # bind = # mdns = yes v6only = no # setup environmental attributes # # passenv = groups = yes umask = 002 # Generally, banners are not used. This sets up their global defaults # # banner = # banner_fail = # banner_success = } includedir /etc/xinetd.d xinetd-2.3.15.4/contrib/xinetd.d/000077500000000000000000000000001333055217000164135ustar00rootroot00000000000000xinetd-2.3.15.4/contrib/xinetd.d/chargen000066400000000000000000000004451333055217000177500ustar00rootroot00000000000000# default: off # description: A chargen server. This is the tcp version. service chargen { type = INTERNAL id = chargen-stream socket_type = stream protocol = tcp user = root wait = no disable = yes } xinetd-2.3.15.4/contrib/xinetd.d/chargen-udp000066400000000000000000000004711333055217000205350ustar00rootroot00000000000000# default: off # description: A chargen server. This is the udp version. service chargen { type = INTERNAL UNLISTED id = chargen-dgram socket_type = dgram protocol = udp user = root wait = yes disable = yes port = 19 } xinetd-2.3.15.4/contrib/xinetd.d/daytime000066400000000000000000000004451333055217000177750ustar00rootroot00000000000000# default: off # description: A daytime server. This is the tcp version. service daytime { type = INTERNAL id = daytime-stream socket_type = stream protocol = tcp user = root wait = no disable = yes } xinetd-2.3.15.4/contrib/xinetd.d/daytime-udp000066400000000000000000000004711333055217000205620ustar00rootroot00000000000000# default: off # description: A daytime server. This is the udp version. service daytime { type = INTERNAL UNLISTED id = daytime-dgram socket_type = dgram protocol = udp user = root wait = yes disable = yes port = 13 } xinetd-2.3.15.4/contrib/xinetd.d/discard000066400000000000000000000004451333055217000177520ustar00rootroot00000000000000# default: off # description: A discard server. This is the tcp version. service discard { type = INTERNAL id = discard-stream socket_type = stream protocol = tcp user = root wait = no disable = yes } xinetd-2.3.15.4/contrib/xinetd.d/discard-udp000066400000000000000000000004701333055217000205360ustar00rootroot00000000000000# default: off # description: A discard server. This is the udp version. service discard { type = INTERNAL UNLISTED id = discard-dgram socket_type = dgram protocol = udp user = root wait = yes disable = yes port = 9 } xinetd-2.3.15.4/contrib/xinetd.d/echo000066400000000000000000000004351333055217000172560ustar00rootroot00000000000000# default: off # description: An echo server. This is the tcp version. service echo { type = INTERNAL id = echo-stream socket_type = stream protocol = tcp user = root wait = no disable = yes } xinetd-2.3.15.4/contrib/xinetd.d/echo-udp000066400000000000000000000004601333055217000200420ustar00rootroot00000000000000# default: off # description: An echo server. This is the udp version. service echo { type = INTERNAL UNLISTED id = echo-dgram socket_type = dgram protocol = udp user = root wait = yes disable = yes port = 7 } xinetd-2.3.15.4/contrib/xinetd.d/servers000066400000000000000000000004701333055217000200300ustar00rootroot00000000000000# default: off # description: An internal xinetd service, listing active servers. service servers { type = INTERNAL UNLISTED port = 9099 socket_type = stream protocol = tcp wait = no disable = yes only_from = 127.0.0.1 } xinetd-2.3.15.4/contrib/xinetd.d/services000066400000000000000000000004721333055217000201640ustar00rootroot00000000000000# default: off # description: An internal xinetd service, listing active services. service services { type = INTERNAL UNLISTED port = 9098 socket_type = stream protocol = tcp wait = no disable = yes only_from = 127.0.0.1 } xinetd-2.3.15.4/contrib/xinetd.d/time000066400000000000000000000004771333055217000173040ustar00rootroot00000000000000# default: off # description: An RFC 868 time server. This is the tcp version, # which is used by rdate. service time { type = INTERNAL id = time-stream socket_type = stream protocol = tcp user = root wait = no disable = yes } xinetd-2.3.15.4/contrib/xinetd.d/time-udp000066400000000000000000000004711333055217000200640ustar00rootroot00000000000000# default: off # description: An RFC 868 time server. This is the udp version. service time { type = INTERNAL UNLISTED id = time-dgram socket_type = dgram protocol = udp user = root wait = yes disable = yes port = 37 } xinetd-2.3.15.4/contrib/xinetd.service000066400000000000000000000004701333055217000175540ustar00rootroot00000000000000[Unit] Description=Xinetd A Powerful Replacement For Inetd After=network.target Documentation=man:xinetd Documentation=man:xinetd.conf Documentation=man:xinetd.log [Service] Type=simple ExecStart=/usr/sbin/xinetd -stayalive -dontfork ExecReload=/usr/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target xinetd-2.3.15.4/m4/000077500000000000000000000000001333055217000135565ustar00rootroot00000000000000xinetd-2.3.15.4/m4/ax_check_compile_flag.m4000066400000000000000000000062511333055217000202720ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS xinetd-2.3.15.4/m4/ax_check_link_flag.m4000066400000000000000000000057601333055217000176030ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the linker or gives an error. # (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the linker's default flags # when the check is done. The check is thus made with the flags: "LDFLAGS # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_LINK_FLAG], [AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" AC_LINK_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_LINK_FLAGS xinetd-2.3.15.4/man/000077500000000000000000000000001333055217000140115ustar00rootroot00000000000000xinetd-2.3.15.4/man/Sprint.3000066400000000000000000000121521333055217000153550ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH Sprint 3X "29 May 1992" .SH NAME Sprint -- formatted stream output .SH SYNOPSIS .LP .nf .ft B int Sprint( fd, format [ , ... ] ) int fd ; char *format ; .SH DESCRIPTION \fBSprint()\fR provides formatted output conversion. The formatting is controlled by the \fIformat\fR argument. All characters in \fIformat\fR that do not specify a conversion are printed. A conversion is specified by a '%' followed by a string that ends with a conversion type character. The string may contain flags, a field width, a precision, and a modifier. .LP Possible flags (more that one can be specified and they can be in any order) include: .TP 10 .B \'-' specifies left adjustment of the converted argument. The default is right adjustment. This flag is meaningful only if a field width is specified. .TP .B \'+' specifies that a number will always have a sign as a prefix (this forces a '+' sign to appear if the number is positive). .TP .B \' ' prefixes a \fIspace\fR to the number if the number has not a sign (therefore the '+' flag overrides this flag). .TP .B \'#' The meaning of '#' depends on the conversion type character: for \fBo\fR conversions the first digit will be 0; for \fBx\fR or \fBX\fR conversions \fB0x\fR or \fB0X\fR respectively will be prefixed to the number (if it is not zero); for \fBe\fR, \fBE\fR, \fBf\fR, \fBg\fR, and \fBG\fR conversions the number will always have a decimal point. .TP .B \'0' specifies padding with zeros instead of spaces. .LP The field width is specified by a number. This number indicates the \fIminimum\fR width for the field. A '*' may be used instead of the number. In that case the width is the value of the next argument which should be an \fBint\fR. A negative width value specifies left adjustment with a width equal to the absolute width value. .LP A precision is specified by a '.' followed by a number. The meaning of the precision depends on the type conversion character. For a string conversion, precision determines how many characters are printed from the string. For integer conversions, precision determines the number of digits used to print the number (zero padding is done if the precision exceeds the length of the number). For floating point conversions, precision determines the number of digits after the decimal point (\fBe\fR, \fBE\fR, \fBf\fR) or the number of significant digits (\fBg\fR, \fBG\fR). A '*' may be used instead of a number to specify the precision. In that case the precision is the value of the next argument which should be an \fBint\fR. The behavior of \fBSprint()\fR is undefined if the precision is negative. .LP The length modifier is \fBl\fR and indicates that the argument is a \fBlong\fR integer. .LP The type conversion characters are: \fBd, i, o, x, X, u, c, s, f, e, E, g, G, p, n, %\fR. For floating point conversions the argument should be of type \fIdouble\fR. .TP 10 .B d,i specify signed decimal conversion. .TP .B u specifies unsigned decimal conversion. .TP .B o specifies octal conversion. .TP .B x,X specify hexadecimal conversion. For .B x the hex digits used are 0123456789abcdef. For .B X the hex digits used are 0123456789ABCDEF. There is no leading .B 0x or .B 0X (use the '#' flag for that). .TP .B c specifies character conversion; the argument should be of type \fIchar\fR. .TP .B s specifies string conversion; the argument should be of type \fIchar *\fR. .TP .B f specifies conversion to the form [-]ddd.dddd. The number of digits after the decimal point depends on precision; the default is 6. If the precision is 0, the decimal point is not printed (this can be overridden with the '#' flag). .B e,E specify conversion to the form [-]ddd.dddd[eE][+-]ddd. The number of digits after the decimal point depends on precision; the default is 6. If the precision is 0, the decimal point is not printed (this can be overridden with the '#' flag). The exponent is at least 2 digit wide. .TP .B g,G specify a conversion using the .B e,E format respectively if the exponent is less than -4 or greater than or equal to the precision; otherwise the .B f format is used. .TP .B p is used to print pointers (type \fIvoid *\fR, or \fIchar *\fR if the compiler does not support the former). .TP .B n expects a \fIint *\fR argument and puts in that integer the number of characters already printed by this call. .TP .B % is used to print a \fI%\fR. .LP If an unknown conversion character is specified, the percent sign followed by that character will be printed. .SH RETURN VALUE .LP If no error occurred, \fBSprint()\fR returns the number of characters printed. In case of error, it returns \fBSIO_ERR\fR. .SH BUGS .LP This is a list of differences between \fBSprint()\fR and the ANSI C Standard \fBprintf()\fR: .LP \fBSprint()\fR does not support non-removal of trailing zeroes for \fBg\fR and \fBG\fR conversions when the '#' flag is used. .LP \fBSprint()\fR does not support the h and L modifiers. .LP The current implementation assumes that \fIsizeof(int)==sizeof(long)\fR. .LP \fBSprint()\fR supports "%p" only if \fIsizeof(pointer)<=sizeof(int)\fR. xinetd-2.3.15.4/man/itox.8000066400000000000000000000013671333055217000150740ustar00rootroot00000000000000.TH ITOX 8 "March 2005" "xinetd" .SH NAME itox \- converts inetd.conf style configuration files to xinetd.conf .SH SYNOPSIS itox [\-daemon_dir ] .SH DESCRIPTION .B itox takes on its standard input inetd.conf style entries and dumps to standard output the corresponding xinetd.conf style entries. .SH OPTIONS .TP .I \-daemon_dir If you use tcpd, this option specifies the directory where all the daemons are. You must specify this option if you use tcpd and the daemon file names are not absolute. .SH EXAMPLES itox \-daemon_dir /usr/sbin < inetd.conf > xinetd.conf .SH AUTHOR xinetd and itox were written by Panagiotis Tsirigotis. .sp This man page was written by Norbert Veber and Thomas Seyrat xinetd-2.3.15.4/man/m_env.3000066400000000000000000000055151333055217000152070ustar00rootroot00000000000000.\"(c) Copyright 1992 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH ENV 3L "20 October 1992" .SH NAME env_create, env_destroy, env_make, env_addvar, env_addstr, env_remvar, env_lookup, env_getvars -- environment manipulation functions .SH SYNOPSIS .LP .nf .ft B #include "m_env.h" .LP .ft B env_h env_create( env ) env_h env ; .LP .ft B void env_destroy( env ) env_h env ; .LP .ft B env_h env_make( env_strings ) char **env_strings ; .LP .ft B int env_addvar( env, from_env, var ) env_h env ; env_h from_env ; char *var ; .LP .ft B int env_addstr( env, str ) env_h env ; char *str ; .LP .ft B int env_remvar( env, var ) env_h env ; char *var ; .LP .ft B char **env_getvars( env ) env_h env ; .SH DESCRIPTION This library handles environments. An environment is a set of strings of the form .I "name=value". In the following, we will use the term string as a synonym of NUL-terminated array of .I char. .LP .B env_create() creates a new environment. The new environment will be empty unless the argument .I env is not .SB ENV_NULL. In that case, the new environment will be a duplicate of .I env (i.e. they will contain the same strings). .LP .B env_destroy() destroys the specified environment. .LP .B env_make() creates a new environment which includes the .I env_strings. .I env_strings should be a NULL-terminated array of strings. .LP .B env_addvar() adds the specified variable .I var to .I env. The variable value is obtained from the environment .I from_env. If the variable exists already in .I env the old value is replaced with the new value. .LP .B env_addstr() adds a string of the form .I "name=value" to .I env. .LP .B env_remvar() removes the specified variable .I var from the environment .I env. .LP .B env_lookup() searches .I env for variable .I var. It returns a string of the form .I "name=value" where .I name is the name of the variable (i.e. it is equal to .I var). .LP .B env_getvars returns a NULL-terminated array of strings of the form .I "name=value". .SH "RETURN VALUES" In case of error, all calls will place an error code in the global variable .I env_errno. Possible error codes: .TP 15 .SB ENV_ENOMEM out of memory .TP .SB ENV_EBADVAR variable is not in environment .TP .SB ENV_EBADSTRING string is not well-formed (i.e. is not of the form \fIname=value\fR). .LP .B env_create() returns a handle or .SM ENV_NULL if it fails. .LP .B env_make() returns a handle or .SM ENV_NULL if it fails. .LP .B env_addvar() returns .SM ENV_OK on success or .SM ENV_ERR on failure. .LP .B env_addstr() returns .SM ENV_OK on success or .SM ENV_ERR on failure. .LP .B env_remvar() returns .SM ENV_OK on success or .SM ENV_ERR if the variable is not part of the environment. .LP .B env_loopkup() returns a string on success or .SM NULL on failure. .SH "SEE ALSO" environ(5) xinetd-2.3.15.4/man/pset.3000066400000000000000000000124371333055217000150570ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH PSET 3X "23 April 1993" .SH NAME pset_create, pset_destroy, pset_add, pset_insert, pset_remove, pset_delete, pset_remove_index, pset_clear, pset_count, pset_pointer, pset_compact, pset_sort, pset_apply - routines that handle pointer sets .SH SYNOPSIS .LP .nf .ft B #include "pset.h" .LP .ft B pset_h pset_create( alloc_start, alloc_step ) unsigned alloc_start, alloc_step ; .LP .ft B void pset_destroy( pset ) pset_h pset ; .LP .ft B ANY_TYPE *pset_add( pset, ptr ) pset_h pset ; ANY_TYPE *ptr ; .LP .ft B void *pset_insert( pset, ptr ) pset_h pset ; void *ptr ; .LP .ft B void pset_remove( pset, ptr ) pset_h pset ; ANY_TYPE *ptr ; .LP .ft B void pset_delete( pset, ptr ) pset_h pset ; void *ptr ; .LP .ft B void pset_remove_index( pset, index ) pset_h pset ; unsigned index ; .LP .ft B void pset_clear( pset ) pset_h pset ; .LP .ft B unsigned pset_count( pset ) pset_h pset ; .LP .ft B void *pset_pointer( pset, index ) pset_h pset ; unsigned index ; .LP .ft B void pset_compact( pset ) pset_h pset ; .LP .ft B void pset_sort( pset, compfunc ) pset_h pset ; int (*compfunc)() ; .LP .ft B void pset_apply( pset, func, arg ) pset_h pset ; void (*func)() ; void *arg ; .SH DESCRIPTION This library provides functions that handle sets of pointers. Pointers can be inserted and deleted from sets and the sets can be enumerated. Pointers are inserted in sets in no particular order. However it is guaranteed that a sequence of insertions will result in a set which if enumerated will provide the pointers in the same order in which they were inserted (assuming no intervening deletions). .LP .B pset_create() creates a pointer set. .I alloc_start determines the initial table size, and .I alloc_step determines the amount by which the set size is increased in case of overflow. If any of these parameters is 0, a default value is used. .LP .B pset_destroy() destroys the specified pointer set. .LP .B pset_add() is a macro that adds a pointer to the specified set. The pointer can be of any type. .LP .B pset_insert() inserts a pointer to the specified set. This is the same operation as .B pset_add(). .LP .B pset_remove() removes a pointer from the specified set. .LP .B pset_delete() deletes a pointer from the specified set. This is the same operation as .B pset_remove(). .LP .B pset_remove_index() removes the pointer that is at position .I index in the set. .I index should be in the range [0, \fBpset_count(pset)\fP) (but there is no check to enforce this). After this operation, the .I index position will be occupied by another pointer. .LP .B pset_clear() removes all pointers from the specified set. .LP .B pset_count() returns the number of pointers in the specified set. .LP .B pset_pointer() returns the pointer at position .I index in the specified set. .I index must be between 0 and .B "pset_count(pset)." .B pset_pointer() is a macro and it can also be used in the left-hand side of assignments. .LP .B pset_compact() removes all NULL pointers from .I pset. .LP .B pset_sort() sorts the pointers in .I pset using the specified function. .I compfunc is invoked with 2 arguments that are pointers pointing to pointers stored in .I pset. For example, if the pset holds pointers to objects of type T, then the function F whose address is in .I compfunc should be defined as: F( T **p1, T **p2 ). .br .I compfunc should return a negative, zero or positive value if its first argument is less than, equal to, or greater than its second argument. .LP .B pset_apply() applies .I func to all pointers in .I pset. If .I arg is not .SM NULL the function is invoked as: .RS (*func)( arg, p ) .RE where .I p is a pset pointer. If .I arg is .SM NULL the function is invoked as: .RS (*func)( p ) .RE .SH EXAMPLE The following code fragment reads lines from standard input and places them in a pset. Then it sorts the pset, prints the sorted contents to standard output and then it eliminates duplicate lines (which it also prints to standard output). .RS .sp 1 .ft B .nf pset_h ph ; char buf[ 80 ] ; unsigned u ; int compstr() ; void printstr() ; .sp 1 ph = pset_create( 0, 0 ) ; while ( gets( buf ) ) .RS pset_add( strcpy( malloc( strlen( buf ) + 1 ), buf ) ) ; .RE pset_sort( ph, compstr ) ; for ( u = 0 ; u < pset_count( ph ) ; u++ ) .RS printf( "%s\\n", (char *) pset_pointer( ph, u ) ) ; .RE .RE .fi .ft R .LP The function .I compstr() follows: .sp 1 .RS .ft B .nf int compstr( p1, p2 ) .RS char **p1, **p2 ; .RE { .RS return( strcmp( *p1, *p2 ) ) ; .RE } .RE .SH "RETURN VALUES" .LP .I pset_h is a pointer type. Functions that return .I pset_h will return .SM NULL to indicate an error. .LP .B pset_create() returns a pointer set handle or .SM NULL if it fails. .LP .B pset_add() returns its second argument if successful or .SM NULL if it fails. .LP .B pset_insert() returns its second argument if successful or .SM NULL if it fails. .LP .B pset_count() always returns the number of pointers in the set. .LP .B pset_pointer() always returns a pointer. There is no check if the specified index is within range. .SH BUGS .LP .B pset_add(), .B pset_remove(), .B pset_remove_index(), .B pset_count(), .B pset_clear(), .B pset_pointer() and .B pset_sort() are macros, therefore the \fI&\fR operator cannot be applied to them. xinetd-2.3.15.4/man/psi.3000066400000000000000000000033471333055217000146770ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH PSET 3X "25 September 1992" .SH NAME psi_create, psi_destroy, psi_reset, psi_start, psi_next, psi_remove - pointer set iterator functions .SH SYNOPSIS .LP .nf .ft B #include "pset.h" .LP .ft B psi_h psi_create( pset ) pset_h pset ; .LP .ft B void psi_destroy( iter ) psi_h iter ; .LP .ft B void psi_reset( iter, pset ) psi_h iter ; pset_h pset ; .LP .ft B void *psi_start( iter ) psi_h iter ; .LP .ft B void *psi_next( iter ) psi_h iter ; .LP .ft B void psi_remove( iter ) psi_h iter ; .SH DESCRIPTION These functions provide a means to iterate over psets (pointer sets). .LP .B psi_create() creates an iterator. The only operation that should be applied to an iterator after it is created is .B psi_start(). .LP .B psi_destroy() destroys the iterator. .LP .B psi_reset() changes the pset that is being iterated to .I pset. .LP .B psi_start() starts an iteration and returns the first pointer in the pointer set. .LP .B psi_next() returns the next pointer in the set. .LP .B psi_remove() removes the current pointer from the set. The current pointer is the one returned most recently from either .B psi_start() or .B psi_next(). .SH "RETURN VALUES" .LP .B psi_create() returns an iterator handle on success or .SM NULL on failure. .LP .B psi_start() returns the first pointer from the set or .SM NULL if the set is empty. .LP .B psi_next() returns a pointer or .SM NULL if the end of the set is reached. .SH WARNINGS .B psi_create() is the only function in this library. The rest of the interface is macros. .LP This interface may be eliminated in a future release of the .I pset library. xinetd-2.3.15.4/man/sio.3000066400000000000000000000301021333055217000146630ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH SIO 3X "29 May 1992" .SH NAME Sread, Sgetc, Srdline, Sfetch, Swrite, Sputc, Sprint, Sprintv, Sdone, Sundo, Stie, Suntie, Sflush, Sclose, Sbuftype, Smorefds, Sgetchar, Sputchar, SIOLINELEN - fast stream I/O .SH SYNOPSIS .LP .nf .ft B #include "sio.h" #include .LP .ft B int Sread( fd, buf, nbytes ) int fd ; char *buf ; int nbytes ; .LP .ft B int Sgetc( fd ) int fd ; .LP .ft B char *Srdline( fd ) int fd ; .LP .ft B char *Sfetch( fd, length ) int fd ; long *length ; .LP .ft B int Swrite( fd, buf, nbytes ) int fd ; char *buf ; int nbytes ; .LP .ft B int Sputc( fd, c ) int fd ; char c ; .LP .ft B int Sprint( fd, format [ , ... ] ) int fd ; char *format ; .LP .ft B int Sprintv( fd, format, ap ) int fd ; char *format ; va_list ap ; .LP .ft B int Sdone( fd ) int fd ; .LP .ft B int Sundo( fd, type ) int fd ; int type ; .LP .ft B int Stie( ifd, ofd ) int ifd, ofd ; .LP .ft B int Suntie( fd ) int fd ; .LP .ft B int Sbuftype( fd, type ) int fd, type ; .LP .ft B int Smorefds() .LP .ft B int Sflush( fd ) int fd ; .LP .ft B int Sclose( fd ) int fd ; .LP .ft B int Sgetchar( fd ) int fd ; .LP .ft B int Sputchar( fd, c ) int fd; char c ; .LP .ft B int SIOLINELEN( fd ) int fd ; .SH DESCRIPTION The \fISIO\fR library provides support for \fIstream\fR I/O on file descriptors. The first argument of every function or macro is a file descriptor. The file descriptor may be used either for input or for output but not both. Attempting to use a descriptor for both input and output will cause the call to fail. When you are done with using a file descriptor, you should inform \fISIO\fR by invoking \fBSdone()\fR (unless the program is about to call \fIexit(3)\fR). You can also use \fBSdone()\fR if you want to perform a different type of operation on the same file descriptor (e.g. first you were reading data from the file descriptor and then you want to write some data). Another possibility is to do stream I/O at different file offsets by using \fBSdone()\fR before doing the \fBlseek(2)\fR to the new file offset. .LP I/O operations on different file descriptors do not interfere (unless the file descriptors refer to the same file, in which case the results are undefined). .LP For disk files I/O always starts at the current file offset. If that offset is not a multiple of the preferred block size for file system I/O (the \fIst_blksize\fR field in \fIstruct stat\fR), performance will not be optimal. For optimal performance, it is recommended that no I/O operations (like \fIread(2)\fR or \fIwrite(2)\fR) are applied to the file descriptor if it is to be used by \fISIO\fR. .LP Read I/O is either buffered or is done using memory mapping whenever that is possible and appropriate. .LP The library functions that do stream I/O resemble system calls (for example \fBSread()\fR resembles \fIread(2)\fR) so that modifying a program that uses the system calls to use the \fISIO\fR functions is easy (e.g. just replace \fIread(2)\fR with \fBSread()\fR; the function signatures as well as the return values are exactly the same). .LP .B Sread() reads \fInbytes\fR bytes from the stream associated with file descriptor \fIfd\fR into the buffer pointed to by \fIbuf\fR. .LP .B Sgetc() reads a character from the stream associated with file descriptor \fIfd\fR. It returns \fBSIO_EOF\fR if the end of file has been reached. .LP .B Sgetchar() (a macro) performs exactly the same function as \fBSgetc()\fR but it is much faster. .LP .B Srdline() reads a line from the stream associated with file descriptor \fIfd\fR. The newline at the end of the line is replaced by a 0 byte. Lines longer than the maximum line length supported by \fISIO\fR will have characters deleted. .LP .B SIOLINELEN() (a macro) returns the length of the line returned by the last call to \fBSrdline()\fR (the value returned by \fBSIOLINELEN()\fR is valid only after \fBSrdline()\fR and as long as no other \fISIO\fR calls are performed on that file descriptor). .LP .B Sfetch() returns a pointer to data coming from the stream associated with file descriptor \fIfd\fR. The amount of data available is indicated by the \fIlength\fR argument. One possible use for this function is copying of files. .LP .B Swrite() writes \fInbytes\fR bytes to the stream associated with file descriptor \fIfd\fR from the buffer pointed to by \fIbuf\fR. .LP .B Sputc() writes a single character to the stream associated with file descriptor \fIfd\fR. .LP .B Sputchar() (a macro) performs exactly the same function as \fBSputc()\fR but it is much faster. .LP .B Sprint() imitates the behavior of printf(3) as defined in the ANSI C Standard. There are some limitations. Check the \fBSprint()\fR man page for more information. .LP .B Sprintv() is the same as \fBSprint()\fR except that it takes a \fIstdarg\fR argument list. .LP .B Sundo() returns the characters returned by the last call to \fBSrdline()\fR, \fBSgetc()\fR or \fBSgetchar()\fR to the stream so that they can be reread. The \fItype\fR argument to \fBSundo()\fR can be \fBSIO_UNDO_LINE\fR or \fBSIO_UNDO_CHAR\fR depending on whether the call whose effect needs to be undone was \fBSrdline()\fR or \fBSgetc()\fR/\fBSgetchar()\fR respectively. There is no check on whether the last function invoked on \fIfd\fR was one of the above and the results are undefined if there is no correspondence between the \fItype\fR and the last operation on \fIfd\fR. (i.e. the result is undefined if you try \fBSIO_UNDO_CHAR\fR and the last operation was not \fBSgetchar()\fR or \fBSgetc()\fR). .LP .B Stie() ties the file descriptor \fIifd\fR to the file descriptor \fIofd\fR. This means that whenever a \fIread(2)\fR is done on \fIifd\fR, it is preceded by a \fIwrite(2)\fR on \fIofd\fR. For filters it is useful to do \fIStie( 0, 1 )\fR to maximize concurrency. It is also useful to do the same thing when you issue prompts to the user and you want the user reply to appear on the same line with the prompt. \fIifd\fR, \fIofd\fR will be initialized for input, output respectively (if any of them is initialized, it must be for the appropriate stream type (input or output)). If \fIifd\fR was tied to another file descriptor, the old tie is broken. .LP .B Suntie() undoes the effect of \fBStie()\fR for the specified input file descriptor. .LP .B Sbuftype() determines the buffering type for the output stream associated with file descriptor \fIfd\fR. By default output directed to terminals is line buffered, output directed to file descriptor 2 (standard error) is unbuffered and everything else is fully buffered. Possible values for the \fItype\fR argument are .RS .TP 15 .B SIO_FULLBUF for full buffering .TP .B SIO_LINEBUF for line buffering .TP .B SIO_NOBUF for no buffering .RE .LP .B Smorefds() should be used to inform \fBSIO\fR that the number of available file descriptors has been increased. \fBSIO\fR uses an array of internal stream descriptors which are indexed by the file descriptor number. Some operating systems (ex. SunOS 4.1[.x]) allow the number of available file descriptors to vary. If that number is increased beyond its initial value \fBSIO\fR needs to know in order to allocate more stream descriptors. .LP .B Sdone() flushes any buffered output for \fIfd\fR and releases the \fISIO\fR resources used. \fBSdone()\fR is useful in case the program needs to reprocess the data of a file descriptor (assuming the file descriptor corresponds to a file). The program can call \fBSdone()\fR, \fIlseek(2)\fR to the beginning of the file and then proceed to reread the file. .LP .B Sflush() causes any buffered stream output to be written to the file descriptor. If its argument is the special value \fBSIO_FLUSH_ALL\fR then all output streams will be flushed. .LP .B Sclose() closes a file descriptor used for stream I/O, flushes any buffered output and releases the \fISIO\fR resources used. .SH EXAMPLES .LP The following code implements a (poor) substitute for the tee command (it copies standard input to a file as well as to standard output). .ne 10 .RS .nf .ft B #include "sio.h" .sp .5 main( argc, argv ) int argc ; char *argv[] ; { char *file = (argc > 1) ? argv[ 1 ] : "tee.file" ; int fd = creat( file, 0644 ) ; long length ; char *s ; .sp .5 while ( s = Sfetch( 0, &length ) ) { Swrite( 1, s, length ) ; Swrite( fd, s, length ) ; } exit( 0 ) ; } .fi .ft R .RE .SH RETURN VALUES .LP .B Sread() returns the number of bytes read on success (0 means end-of-file) or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sgetc() returns the character read on success, SIO_EOF when the end-of-file is reached, or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Srdline() returns a pointer to the next line on success. On failure or when the end-of-file is reached it returns .SM NULL. If the end-of-file is reached \fIerrno\fR is set to 0, otherwise it indicates the error. .LP .B Sfetch() returns a pointer to file data on success. (the \fIlength\fR argument indicates how many bytes are available). On failure or when the end-of-file is reached it returns .SM NULL. If the end-of-file is reached \fIerrno\fR is set to 0, otherwise it indicates the error. .LP .B Swrite() returns the number of bytes written on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sputc() returns the character it was given as an argument on success .B Sprint() returns the number of characters printed on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sdone() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sundo() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Stie() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Suntie() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to \fBEBADF\fR if there was no tied file descriptor). .LP .B Sbuftype() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to \fBEBADF\fR if this is not an output stream or to \fBEINVAL\fR if an unknown \fItype\fR is specified). .LP .B Smorefds() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (because of lack of memory). .LP .B Sflush() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sclose() returns \fB0\fR on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sgetchar() returns the character read on success, SIO_EOF when the end-of-file is reached, or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B Sputchar() returns the character it was given as an argument on success or \fBSIO_ERR\fR on failure (\fIerrno\fR is set to indicate the error). .LP .B SIOLINELEN() returns the length of the last line read by \fBSrdline()\fR. .LP Attempting a read operation on a descriptor opened for writing or vice versa will cause the operation to fail with \fIerrno\fR set to \fBEBADF\fR. .LP The first \fISIO\fR operation on a descriptor must be a read or write operation. It cannot be a control operation (like \fBSflush()\fR). Such an operation will fail with \fIerrno\fR set to \fBEBADF\fR. .LP .IP "\fBNOTE 1:\fR" 15 \fBStie()\fR is an input/output operation for the respective file descriptors, not a control operation. \fBSuntie()\fR is a control operation. .IP "\fBNOTE 2:\fR" \fBSIO_ERR\fR is defined to be \fB-1\fR. .SH "SEE ALSO" .LP Sprint(3) .SH BUGS .LP If the operating system does not provide for invocation of a finalization function upon exit, the program will have to explicitly flush all output streams. The following operating systems provide such a facility: SunOS 4.x, Ultrix 4.x. .LP Socket file descriptors can be used for input as well as output but \fBSIO\fR does not support this. .LP The current implementation will not try to use memory mapping to read a file if the file offset is not 0 (it will use buffered I/O instead). .LP Pointers returned by \fBSfetch()\fR point to read-only memory. Attempting to modify this memory will result in a segmentation violation. xinetd-2.3.15.4/man/strparse.3000066400000000000000000000077561333055217000157570ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH STRPARSE 3X "30 September 1992" .SH NAME str_parse, str_endparse, str_component, str_separator, str_nextpos .SH SYNOPSIS .LP .nf .ft B #include "str.h" .LP .ft B str_h str_parse( str, separ, flags, errnop ) char *str ; char *separ ; int flags ; int *errnop ; .LP .ft B void str_endparse( handle ) str_h handle ; .LP .ft B char *str_component( handle ) str_h handle ; .LP .ft B int str_setstr( handle, newstr ) str_h handle ; char *newstr ; .LP .ft B int str_separator( handle, separ ) str_h handle ; char *separ ; .LP .ft B char *str_nextpos( handle ) str_h handle ; .LP extern int str_errno ; .SH DESCRIPTION .LP These functions are useful for parsing strings. In this context parsing means breaking the string into substrings. The substrings are separated by a list of possible separator characters. .LP .B str_component() returns successive substrings of the string. .B str_parse() creates and initializes a string parser with the string that will be processed, \fIstr\fR, the list of possible separator characters, \fIsepar\fR, and flags that control how the parser works. The \fIflags\fR argument is formed by ORing one or more of the following constants: .TP 20 .SB STR_RETURN_ERROR If something goes wrong return a value that indicates that an error occurred (e.g. out of memory). The default is for the program to be terminated with an appropriate error message. .TP .SB STR_NULL_START If \fIstr\fR starts with a separator then a zero-length string will be returned the first time \fBstr_component()\fR is called. .TP .SB STR_NULL_END If \fIstr\fR ends with a separator then a zero-length string will be returned by \fBstr_component()\fR when the substrings of \fIstr\fR are exhausted. .TP .SB STR_MALLOC The strings returned by \fBstr_component()\fR will be in malloc'ed memory. By default the substrings are part of \fIstr\fR. If this option is not used \fIstr\fR will be modified by \fBstr_component()\fR. .LP Finally, \fBSTR_NOFLAGS\fR may be used to specify no flags. The \fIerrnop\fR argument points to an integer where the string processing functions will deposit an error code if an error occurs. If \fIerrnop\fR is .SM NULL the error codes will be placed in \fIstr_errno\fR. This is useful only if \fBSTR_RETURN_ERROR\fR is used in \fIflags\fR. It is possible that \fIstr\fP is .SM NULL. In this case, a subsequent .B str_setstr() should be used to specify the string to be processed. .LP .B str_component() returns successive substrings from the string associated with the parser specified by \fIhandle\fR. .LP .B str_endparse() destroys the parser specified by \fIhandle\fR. .LP .B str_setstr() changes the processed string to \fInewstr\fP. .LP .B str_separator() replaces the list of separator characters with \fIsepar\fR. Processing continues from the current position. .LP .B str_nextpos() returns a pointer to the rest of the string. The previous character is a separator character (if \fBSTR_MALLOC\fR is not set, then the previous character is .SM NUL ). .SH "RETURN VALUES" .LP .B str_parse() returns a parser handle or .SM NULL if something goes wrong and \fIflags\fR & \fBSTR_RETURN_ERROR\fR is true. Possible \fIstr_errno\fR values: .RS .TP 20 .SB STR_ENULLSEPAR \fIsepar\fR is .SM NULL .TP .SB STR_ENOMEM the program ran out of memory .RE .LP .B str_component() returns a pointer to the next substring or .SM NULL if something goes wrong and \fIflags\fR & \fBSTR_RETURN_ERROR\fR is true. .LP .B str_setstr() returns .SB STR_OK on success or .SB STR_ERR on failure. .LP .B str_separator() returns .SB STR_OK on success or .SB STR_ERR on failure. .LP .B str_nextpos() returns a pointer or .SM NULL if the end of string has been reached. .SH BUGS .B str_component() modifies the string unless \fBSTR_MALLOC\fR is set in the parser. .LP There should be only one parser active on a specific string. If there is more than one, they all must use the \fBSTR_MALLOC\fR option. xinetd-2.3.15.4/man/strprint.3000066400000000000000000000062611333055217000157670ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH STRPRINT 3X "30 September 1992" .SH NAME str_sprint, tr_sprintv, str_nprint, str_nprintv, str_print, str_printv, strx_sprint, strx_sprintv, strx_nprint, strx_nprintv, strx_print, strx_printv -- formatted conversion to string .SH SYNOPSIS .LP .nf .ft B #include "str.h" .LP .ft B char *str_sprint( buf, format, ... ) char *buf ; char *format ; .LP .ft B char *str_sprintv( buf, format, ap ) char *buf ; char *format ; va_list ap ; .LP .ft B int str_nprint( buf, format, ... ) char *buf ; char *format ; .LP .ft B int str_nprintv( buf, format, ap ) char *buf ; char *format ; va_list ap ; .LP .ft B void str_print( countp, buf, format, ... ) int *countp ; char *buf ; char *format ; .LP .ft B void str_printv( countp, buf, format, ap ) int *countp ; char *buf ; char *format ; va_list ap ; .LP .ft B char *strx_sprint( buf, len, format, ... ) char *buf ; int len ; char *format ; .LP .ft B char *strx_sprintv( buf, len, format, ap ) char *buf ; int len ; char *format ; va_list ap ; .LP .ft B int strx_nprint( buf, len, format, ... ) char *buf ; int len ; char *format ; .LP .ft B int strx_nprintv( buf, len, format, ap ) char *buf ; int len ; char *format ; va_list ap ; .LP .ft B void strx_print( countp, buf, len, format, ... ) int *countp ; char *buf ; int len ; char *format ; .LP .ft B void strx_printv( countp, buf, len, format, ap ) int *countp ; char *buf ; int len ; char *format ; va_list ap ; .SH DESCRIPTION .LP All functions are similar in functionality to \fIsprintf()\fR. Their only difference is in their return values. For information about their conversion capabilities, check \fISprint(3)\fR. .LP The difference between the \fIstr_*\fR and the \fIstrx_*\fR functions is that the latter take an extra argument, the size of the buffer, so that they will never write beyond the end of the buffer. Writing beyond the end of the buffer is possible with the \fIstr_*\fR functions. Previously, invoking any of the \fIstrx_*\fR functions with the .I len argument set to 0 used to be the same as calling the equivalent \fIstr_*\fR function. This dangerous behavior has since been changed and now the \fIstrx_*\fR functions don't touch the buffer when .I len is 0 or negative. .LP All functions will append a .SM NUL at the end of .I buf (the \fIstrx_*\fR functions will not do this if it would cause a buffer overrun). .LP .B str_print(), .B str_printv(), .B strx_print(), and .B strx_printv() will put in .I "*countp" the number of characters placed in .I buf excluding the ending .SM NUL (this happens only if .I "*countp" is not .SM NULL ). .LP The functions that have a name ending in 'v' are similar to those without the 'v' at the end of their name except that instead of accepting a variable number of arguments, they expect a \fIstdarg(3)\fR argument list. .SH "RETURN VALUES" .LP .B str_sprint(), .B str_sprintv(), .B strx_sprint(), and .B strx_sprintv() return .I buf. .LP .B str_nprint(), .B str_nprintv(), .B strx_nprint(), and .B strx_nprintv() return the number of characters placed in .I buf excluding the ending .SM NUL. .SH "SEE ALSO" Sprint(3) xinetd-2.3.15.4/man/strutil.3000066400000000000000000000024451333055217000156100ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH STRUTIL 3X "30 September 1992" .SH NAME str_find, str_casefind, str_fill, str_lower, str_upper -- string utility functions .SH SYNOPSIS .LP .nf .ft B #include "str.h" .LP .ft B char *str_find( s1, s2 ) char *s1, *s2 ; .LP .ft B char *str_casefind( s1, s2 ) char *s1, *s2 ; .LP .ft B void str_fill( s, c ) char *s ; char c ; .LP .ft B char *str_lower( s ) char *s ; .LP .ft B char *str_upper( s ) char *s ; .SH DESCRIPTION .B str_find() returns a pointer to the first instance of string \fIs2\fR in string \fIs1\fR. If \fIs2\fR is the empty string a pointer to \fIs1\fR is returned. .LP .B str_casefind() performs the same function as .B str_find() except that it performs case insensitive character comparisons. .LP .B str_fill() fills the string \fIs\fR with the character \fIc\fR. .LP .B str_lower() and .B str_upper() convert their argument in place to a lower or upper case string respectively. .SH "RETURN VALUES" .LP \fBstr_find()\fR and .B str_casefind() return a pointer to the first occurrence of \fIs2\fR in \fIs1\fR or .SM NULL if \fIs2\fR does not exist in \fIs1\fR. .LP \fBstr_lower()\fR and \fBstr_upper()\fR return \fIs\fR. xinetd-2.3.15.4/man/xconv.pl.8000066400000000000000000000107451333055217000156600ustar00rootroot00000000000000.\" Automatically generated by Pod::Man v1.3, Pod::Parser v1.13 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "XCONV 8" .TH XCONV 8 "July 31 2002" " " "xinetd" .UC .SH "NAME" \&\fBxconv.pl\fR \- inetd.conf to xinetd.conf converter .SH "SYNOPSIS" .IX Header "SYNOPSIS" xconv.pl can convert existing inetd.conf files (configuration for the \fBinetd\fR program) into xinetd.conf files (configuration for the \fBxinetd\fR program) .SH "USAGE" .IX Header "USAGE" \&\fBxconv.pl\fR < /etc/inetd.conf > /etc/xinetd.conf .SH "DESCRIPTION / PURPOSE" .IX Header "DESCRIPTION / PURPOSE" \&\fBxconv.pl\fR is provided by the xinetd package to assist in the migration from \fBinetd\fR based systems to ones based on \fBxinetd\fR. .PP Functionality is similar to the \fBitox\fR program, so reading \fIitox\fR\|(8) is recommended. An important note here is that \fBxconv.pl\fR does not support the \-daemon_dir argument. .SH "AUTHOR" .IX Header "AUTHOR" xconv was written by Rob Braun and xinetd was written by Panagiotis Tsirigotis. xinetd-2.3.15.4/man/xinetd.8000066400000000000000000000161421333055217000154010ustar00rootroot00000000000000.\"(c) Copyright 1992 by Panagiotis Tsirigotis .\"(c) Sections Copyright 1998-2001 by Rob Braun .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH XINETD 8 "14 June 2001" .\" *************************** NAME ********************************* .SH NAME xinetd \- the extended Internet services daemon .\" *************************** SYNOPSIS ********************************* .SH SYNOPSIS .B xinetd [\fIoptions\fP] .\" *************************** DESCRIPTION ********************************* .SH DESCRIPTION \fBxinetd\fP performs the same function as \fBinetd\fP: it starts programs that provide Internet services. Instead of having such servers started at system initialization time, and be dormant until a connection request arrives, \fBxinetd\fP is the only daemon process started and it listens on all service ports for the services listed in its configuration file. When a request comes in, \fBxinetd\fP starts the appropriate server. Because of the way it operates, \fBxinetd\fP (as well as \fBinetd\fP) is also referred to as a super-server. .LP The services listed in \fBxinetd\fP's configuration file can be separated into two groups. Services in the first group are called .I "multi-threaded" and they require the forking of a new server process for each new connection request. The new server then handles that connection. For such services, \fBxinetd\fP keeps listening for new requests so that it can spawn new servers. On the other hand, the second group includes services for which the service daemon is responsible for handling all new connection requests. Such services are called .I "single-threaded" and \fBxinetd\fP will stop handling new requests for them until the server dies. Services in this group are usually datagram-based. .LP So far, the only reason for the existence of a super-server was to conserve system resources by avoiding to fork a lot of processes which might be dormant for most of their lifetime. While fulfilling this function, \fBxinetd\fP takes advantage of the idea of a super-server to provide features such as access control and logging. Furthermore, \fBxinetd\fP is not limited to services listed in .I /etc/services. Therefore, anybody can use \fBxinetd\fP to start special-purpose servers. .\" *************************** OPTIONS ********************************* .SH OPTIONS .TP .BR \-d Enables debug mode. This produces a lot of debugging output, and it makes it possible to use a debugger on \fBxinetd\fP. .TP .BI \-syslog " syslog_facility" This option enables syslog logging of \fBxinetd\fP-produced messages using the specified syslog facility. The following facility names are supported: .I daemon, .I auth, .I user, .I "local[0-7]" (check \fIsyslog.conf(5)\fP for their meanings). This option is ineffective in debug mode since all relevant messages are sent to the terminal. .TP .BI \-filelog " logfile" \fBxinetd\fP-produced messages will be placed in the specified file. Messages are always appended to the file. If the file does not exist, it will be created. This option is ineffective in debug mode since all relevant messages are sent to the terminal. .TP .BI \-f " config_file" Determines the file that \fBxinetd\fP uses for configuration. The default is \fI/etc/xinetd.conf\fP. .TP .BR \-pidfile " pid_file" .br The process ID is written to the file. This option is ineffective in debug mode. .TP .BI \-dontfork Tells xinetd to stay in the foreground rather than detaching itself, to support being run from init or daemontools. This option automatically sets .B \-stayalive (see below). .TP .BI \-stayalive Tells xinetd to stay running even if no services are specified. .TP .BI \-limit " proc_limit" This option places a limit on the number of concurrently running processes that can be started by .B xinetd. Its purpose is to prevent process table overflows. .TP .BI \-logprocs " limit" This option places a limit on the number of concurrently running servers for remote userid acquisition. .TP .BI \-version This option causes xinetd to print out its version information. .TP .BI \-inetd_compat This option causes xinetd to read /etc/inetd.conf in addition to the standard xinetd config files. /etc/inetd.conf is read after the standard xinetd config files. .TP .BI \-cc " interval" This option instructs .B xinetd to perform periodic consistency checks on its internal state every .I interval seconds. .LP The \fIsyslog\fP and \fIfilelog\fP options are mutually exclusive. If none is specified, the default is syslog using the .I daemon facility. You should not confuse \fBxinetd\fP messages with messages related to service logging. The latter are logged only if this is specified via the configuration file. .\" *********************** CONTROLLING XINETD **************************** .SH "CONTROLLING XINETD" .LP \fBxinetd\fP performs certain actions when it receives certain signals. The actions associated with the specific signals can be redefined by editing \fIconfig.h\fP and recompiling. .TP 15 .B SIGHUP causes a hard reconfiguration, which means that \fBxinetd\fP re-reads the configuration file and terminates the servers for services that are no longer available. Access control is performed again on running servers by checking the remote location, access times and server instances. If the number of server instances is lowered, some arbitrarily picked servers will be killed to satisfy the limit; this will happen \fIafter\fP any servers are terminated because of failing the remote location or access time checks. Also, if the .B INTERCEPT flag was clear and is set, any running servers for that service will be terminated; \fIthe purpose of this is to ensure that after a hard reconfiguration there will be no running servers that can accept packets from addresses that do not meet the access control criteria\fP. .TP .B SIGQUIT causes program termination. .TP .B SIGTERM terminates all running servers before terminating \fBxinetd\fP. .TP .B SIGUSR1 causes an internal state dump (the default dump file is \fI/var/run/xinetd.dump\fP; to change the filename, edit \fIconfig.h\fP and recompile). .TP .B SIGABRT causes an internal consistency check to verify that the data structures used by the program have not been corrupted. When the check is completed .B xinetd will generate a message that says if the check was successful or not. .LP On reconfiguration the log files are closed and reopened. This allows removal of old log files. .\" *********************** FILES **************************** .SH FILES .LP .PD .1v .TP 20 .B /etc/xinetd.conf default configuration file .TP .B /var/run/xinetd.dump default dump file .\" *********************** ENVIRONMENT **************************** .SH "ENVIRONMENT" .B REMOTE_HOST Contains the IP address of the client. .PD .\" *********************** SEE ALSO **************************** .SH "SEE ALSO" .I "inetd(8)," .LP .I "xinetd.conf(5)," .LP .I "xinetd.log(5)" .LP .I "http://cr.yp.to/daemontools.html" .\" *********************** AUTHOR **************************** .SH AUTHOR Panos Tsirigotis, CS Dept, University of Colorado, Boulder Rob Braun .\" *********************** PRONUNCIATION **************************** .SH PRONUNCIATION zy-net-d xinetd-2.3.15.4/man/xinetd.conf.5000066400000000000000000001051231333055217000163200ustar00rootroot00000000000000.\"(c) Copyright 1992, by Panagiotis Tsirigotis .\"(c) Sections Copyright 1998-2001 by Rob Braun .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH XINETD.CONF 5 "14 June 2001" .\" *************************** NAME ********************************* .SH NAME xinetd.conf \- Extended Internet Services Daemon configuration file .\" *********************** DESCRIPTION **************************** .SH DESCRIPTION .B "xinetd.conf" is the configuration file that determines the services provided by \fBxinetd\fP. Any line whose first non\-white\-space character is a '#' is considered a comment line. Empty lines are ignored. .LP The file contains entries of the form: .RS .nf .ft B .sp service { .RS .ft B ... .I "..." .RE } .ft R .fi .RE .LP The assignment operator, .I assign_op, can be one of .B '=', .B '+=', .B '-='. The majority of attributes support only the simple assignment operator, .B '='. Attributes whose value is a set of values support all assignment operators. For such attributes, .B '+=' means adding a value to the set and .B '-=' means removing a value from the set. A list of these attributes will be given after all the attributes are described. .LP Each entry defines a service identified by the \fIservice_name\fP. The following is a list of available attributes: .TP 17 .B id This attribute is used to uniquely identify a service. This is useful because there exist services that can use different protocols and need to be described with different entries in the configuration file. By default, the service id is the same as the service name. .TP .B type Any combination of the following values may be used: .RS .TP 12 .B RPC if this is an RPC service .TP .B INTERNAL if this is a service provided by \fBxinetd\fP. .TP .B TCPMUX/TCPMUXPLUS if this is a service that will be started according to the RFC 1078 protocol on the TCPMUX well\-known port. See the section describing TCPMUX services below. .TP .B UNLISTED if this is a service not listed in a standard system file (like .I /etc/rpc for RPC services, or .I /etc/services for non\-RPC services). .RE .TP .B flags Any combination of the following flags may be used: .RS .TP 12 .B INTERCEPT Intercept packets or accepted connections in order to verify that they are coming from acceptable locations (internal or multi\-threaded services cannot be intercepted). .TP .B NORETRY Avoid retry attempts in case of fork failure. .TP .B IDONLY Accept connections only when the remote end identifies the remote user (i.e. the remote host must run an identification server). This flag applies only to connection\-based services. This flag is ineffective if the .B USERID log option is not used. .TP .B NAMEINARGS This will cause the first argument in "server_args" to be argv[0] when executing the server, as specified in "server". This allows you to use tcpd by putting tcpd in "server" and the name of the server in "server_args" like in normal inetd. .TP .B NODELAY If the service is a tcp service and the NODELAY flag is set, then the TCP_NODELAY flag will be set on the socket. If the service is not a tcp service, this option has no effect. .TP .B KEEPALIVE If the service is a tcp service and the KEEPALIVE flag is set, then the SO_KEEPALIVE socket flag will be set on the socket. If the service is not a tcp service, this option has no effect. .TP .B NOLIBWRAP This disables internal calling of the tcpwrap library to determine access to the service. This may be needed in order to use libwrap functionality not available to long\-running processes such as xinetd; in this case, the tcpd program can be called explicitly (see also the NAMEINARGS flag). For RPC services using TCP transport, this flag is automatically turned on, because xinetd cannot get remote host address information for the rpc port. .TP .B SENSOR This replaces the service with a sensor that detects accesses to the specified port. NOTE: It will NOT detect stealth scans. This flag should be used only on services that you know you don't need. When an access is made to this service's port, the IP Address is added to a global no_access list. This causes all subsequent accesses from the originating IP address to be denied access until the deny_time setting expires. The amount of time spent on this list is configurable as the deny_time attribute. The SENSOR flag will also cause xinetd to consider the server attribute to be INTERNAL no matter what is typed on the same line. Another important thing to remember is that if the socket_type is set to stream, then the wait attribute should be set to no. .TP .B IPv4 Sets the service to be an IPv4 service (AF_INET). .TP .B IPv6 Sets the service to be an IPv6 service (AF_INET6), if IPv6 is available on the system. .TP .B LABELED The LABELED flag will tell xinetd to change the child processes SE Linux context to match that of the incoming connection as it starts the service. This only works for external tcp non-waiting servers and is an error if applied to an internal, udp, or tcp-wait server. .TP .B REUSE The REUSE flag is deprecated. All services now implicitly use the REUSE flag. .RE .TP .B v6only This is boolean "yes" or "no". This will result in a service accepting only IPv6 connections, instead of both IPv6 and IPv4 connections. The default is determined by the "bindv6only" kernel variable. .TP .B disable This is boolean "yes" or "no". This will result in the service being disabled and not starting. .RE .TP .B socket_type Possible values for this attribute include: .RS .TP 12 .I stream stream\-based service .TP .I dgram datagram\-based service .TP .I raw service that requires direct access to IP .TP .I seqpacket service that requires reliable sequential datagram transmission .RE .TP .B protocol determines the protocol that is employed by the service. The protocol must exist in .I /etc/protocols. If this attribute is not defined, the default protocol employed by the service will be used. .TP .B wait This attribute determines if the service is single\-threaded or multi\-threaded and whether or not xinetd accepts the connection or the server program accepts the connection. If its value is \fIyes\fP, the service is single\-threaded; this means that \fBxinetd\fP will start the server and then it will stop handling requests for the service until the server dies and that the server software will accept the connection. If the attribute value is \fIno\fP, the service is multi\-threaded and \fBxinetd\fP will keep handling new service requests and xinetd will accept the connection. It should be noted that udp/dgram services normally expect the value to be yes since udp is not connection oriented, while tcp/stream servers normally expect the value to be no. .TP .B user determines the uid for the server process. The user attribute can either be numeric or a name. If a name is given (recommended), the user name must exist in .I /etc/passwd. This attribute is ineffective if the effective user ID of \fBxinetd\fP is not super\-user. .TP .B group determines the gid for the server process. The group attribute can either be numeric or a name. If a name is given (recommended), the group name must exist in .I /etc/group. If a group is not specified, the group of \fIuser\fP will be used (from .I /etc/passwd). This attribute is ineffective if the effective user ID of \fBxinetd\fP is not super\-user and if the \fBgroups\fP attribute is not set to 'yes'. .TP .B instances determines the number of servers that can be simultaneously active for a service (the default is no limit). The value of this attribute can be either a number or .B UNLIMITED which means that there is no limit. .TP .B nice determines the server priority. Its value is a (possibly negative) number; check nice(3) for more information. .TP .B server determines the program to execute for this service. .TP .B server_args determines the arguments passed to the server. In contrast to \fBinetd\fP, the server name should \fInot\fP be included in \fIserver_args\fP. .TP .B libwrap overrides the service name passed to libwrap (which defaults to the server name, the first server_args component with NAMEINARGS, the id for internal services and the service name for redirected services). This attribute is only valid if xinetd has been configured with the libwrap option. .TP .B only_from determines the remote hosts to which the particular service is available. Its value is a list of IP addresses which can be specified in any combination of the following ways: .RS .TP 5 .B a) a numeric address in the form of %d.%d.%d.%d. If the rightmost components are 0, they are treated as wildcards (for example, 128.138.12.0 matches all hosts on the 128.138.12 subnet). 0.0.0.0 matches all Internet addresses. IPv6 hosts may be specified in the form of abcd:ef01::2345:6789. The rightmost rule for IPv4 addresses does not apply to IPv6 addresses. .TP .B b) a factorized address in the form of %d.%d.%d.{%d,%d,...}. There is no need for all 4 components (i.e. %d.%d.{%d,%d,...%d} is also ok). However, the factorized part must be at the end of the address. This form does not work for IPv6 hosts. .TP .B c) a network name (from .I /etc/networks). This form does not work for IPv6 hosts. .TP .B d) a host name. When a connection is made to xinetd, a reverse lookup is performed, and the canonical name returned is compared to the specified host name. You may also use domain names in the form of .domain.com. If the reverse lookup of the client's IP is within .domain.com, a match occurs. .TP .B e) an ip address/netmask range in the form of 1.2.3.4/32. IPv6 address/netmask ranges in the form of 1234::/46 are also valid. .RE .TP .B "" Specifying this attribute without a value makes the service available to nobody. .TP .B no_access determines the remote hosts to which the particular service is unavailable. Its value can be specified in the same way as the value of the \fBonly_from\fP attribute. These two attributes determine the location access control enforced by \fBxinetd\fP. If none of the two is specified for a service, the service is available to anyone. If both are specified for a service, the one that is the better match for the address of the remote host determines if the service is available to that host (for example, if the \fBonly_from\fP list contains 128.138.209.0 and the \fBno_access\fP list contains 128.138.209.10 then the host with the address 128.138.209.10 can not access the service). .TP .B access_times determines the time intervals when the service is available. An interval has the form \fIhour:min\-hour:min\fP (connections .I will be accepted at the bounds of an interval). Hours can range from 0 to 23 and minutes from 0 to 59. .TP .B log_type determines where the service log output is sent. Select just one of the two formats: .RS .TP .B SYSLOG " \fIsyslog_facility [syslog_level]\fP" The log output is sent to syslog at the specified facility. Possible facility names include: .I daemon, .I auth, .I authpriv, .I user, .I mail, .I lpr, .I news, .I uucp, .I ftp .I "local0-7." Possible level names include: .I emerg, .I alert, .I crit, .I err, .I warning, .I notice, .I info, .I debug. If a level is not present, the messages will be recorded at the .I info level. .TP .B FILE " \fIfile [soft_limit [hard_limit]]\fP" The log output is appended to \fIfile\fP which will be created if it does not exist. Two limits on the size of the log file can be optionally specified. The first limit is a soft one; .B xinetd will log a message the first time this limit is exceeded (if .B xinetd logs to syslog, the message will be sent at the .I alert priority level). The second limit is a hard limit; .B xinetd will stop logging for the affected service (if the log file is a common log file, then more than one service may be affected) and will log a message about this (if .B xinetd logs to syslog, the message will be sent at the .I alert priority level). If a hard limit is not specified, it defaults to the soft limit increased by 1% but the extra size must be within the parameters .SM LOG_EXTRA_MIN and .SM LOG_EXTRA_MAX which default to 5K and 20K respectively (these constants are defined in \fIxconfig.h\fP). .RE .TP .B log_on_success determines what information is logged when a server is started and when that server exits (the service id is always included in the log entry). Any combination of the following values may be specified: .RS .TP 12 .B PID logs the server process id (if the service is implemented by \fBxinetd\fP without forking another process the logged process id will be 0) .TP .B HOST logs the remote host address .TP .B USERID logs the user id of the remote user using the RFC 1413 identification protocol. This option is available only for multi\-threaded stream services. .TP .B EXIT logs the fact that a server exited along with the exit status or the termination signal (the process id is also logged if the .B PID option is used) .TP .B DURATION logs the duration of a service session .TP .B TRAFFIC logs the total bytes in and out for a redirected service. .RE .TP .B log_on_failure determines what information is logged when a server cannot be started (either because of a lack of resources or because of access control restrictions). The service id is always included in the log entry along with the reason for failure. Any combination of the following values may be specified: .RS .TP 12 .B HOST logs the remote host address. .TP .B USERID logs the user id of the remote user using the RFC 1413 identification protocol. This option is available only for multi\-threaded stream services. .TP .B ATTEMPT logs the fact that a failed attempt was made (this option is implied by all others). .RE .TP .B rpc_version determines the RPC version for a RPC service. The version can be a single number or a range in the form \fInumber\fP-\fInumber\fP. .TP .B rpc_number determines the number for an .I UNLISTED RPC service (this attribute is ignored if the service is not unlisted). .TP .B env The value of this attribute is a list of strings of the form 'name=value'. These strings will be added to the environment before starting a server (therefore the server's environment will include \fBxinetd\fP's environment plus the specified strings). .TP .B passenv The value of this attribute is a list of environment variables from \fBxinetd\fP's environment that will be passed to the server. An empty list implies passing no variables to the server except for those explicitly defined using the .I env attribute. (notice that you can use this attribute in conjunction with the .I env attribute to specify exactly what environment will be passed to the server). .TP .B port determines the service port. If this attribute is specified for a service listed in .I /etc/services, it must be equal to the port number listed in that file. .TP .B redirect Allows a tcp service to be redirected to another host. When xinetd receives a tcp connection on this port it spawns a process that establishes a connection to the host and port number specified, and forwards all data between the two hosts. This option is useful when your internal machines are not visible to the outside world. Syntax is: redirect = (ip address) (port). You can also use a hostname instead of the IP address in this field. The hostname lookup is performed only once, when xinetd is started, and the first IP address returned is the one that is used until xinetd is restarted. The "server" attribute is not required when this option is specified. If the "server" attribute is specified, this attribute takes priority. .TP .B bind Allows a service to be bound to a specific interface on the machine. This means you can have a telnet server listening on a local, secured interface, and not on the external interface. Or one port on one interface can do something, while the same port on a different interface can do something completely different. Syntax: bind = (ip address of interface). .TP .B interface Synonym for bind. .TP .B banner Takes the name of a file to be splatted at the remote host when a connection to that service is established. This banner is printed regardless of access control. It should *always* be printed when a connection has been made. \fBxinetd\fP outputs the file as\-is, so you must ensure the file is correctly formatted for the service's protocol. In particular, if the protocol requires CR\-LF pairs for line termination, you must supply them. .TP .B banner_success Takes the name of a file to be splatted at the remote host when a connection to that service is granted. This banner is printed as soon as access is granted for the service. \fBxinetd\fP outputs the file as\-is, so you must ensure the file is correctly formatted for the service's protocol. In particular, if the protocol requires CR\-LF pairs for line termination, you must supply them. .TP .B banner_fail Takes the name of a file to be splatted at the remote host when a connection to that service is denied. This banner is printed immediately upon denial of access. This is useful for informing your users that they are doing something bad and they shouldn't be doing it anymore. \fBxinetd\fP outputs the file as\-is, so you must ensure the file is correctly formatted for the service's protocol. In particular, if the protocol requires CR\-LF pairs for line termination, you must supply them. .TP .B per_source Takes an integer or "UNLIMITED" as an argument. This specifies the maximum instances of this service per source IP address. This can also be specified in the defaults section. .TP .B cps Limits the rate of incoming connections. Takes two arguments. The first argument is the number of connections per second to handle. If the rate of incoming connections is higher than this, the service will be temporarily disabled. The second argument is the number of seconds to wait before re\-enabling the service after it has been disabled. The default for this setting is 50 incoming connections and the interval is 10 seconds. .TP .B max_load Takes a floating point value as the load at which the service will stop accepting connections. For example: 2 or 2.5. The service will stop accepting connections at this load. This is the one minute load average. This is an OS dependent feature, and currently only Linux, Solaris, and FreeBSD are supported for this. This feature is only available if xinetd was configured with the \-with\-loadavg option. .TP .B groups Takes either "yes" or "no". If the groups attribute is set to "yes", then the server is executed with access to the groups that the server's effective UID has access to. Alternatively, if the \fBgroup\fP attribute is set, the server is executed with access to the groups specified. If the groups attribute is set to "no", then the server runs with no supplementary groups. This attribute must be set to "yes" for many BSD systems. This attribute can be set in the defaults section as well. .TP .B mdns Takes either "yes" or "no". On systems that support mdns registration of services (currently only Mac OS X), this will enable or disable registration of the service. This defaults to "yes". .TP .B umask Sets the inherited umask for the service. Expects an octal value. This option may be set in the "defaults" section to set a umask for all services. xinetd sets its own umask to the previous umask OR'd with 022. This is the umask that will be inherited by all child processes if the umask option is not used. .TP .B enabled Takes a list of service ID's to enable. This will enable only the services listed as arguments to this attribute; the rest will be disabled. If you have 2 ftp services, you will need to list both of their ID's and not just ftp. (ftp is the service name, not the ID. It might accidentally be the ID, but you better check.) Note that the service "disable" attribute can prevent a service from being enabled despite being listed in this attribute. .TP .B include Takes a filename in the form of "include /etc/xinetd/service". The file is then parsed as a new configuration file. It is not the same thing as pasting the file into xinetd.conf where the include directive is given. The included file must be in the same form as xinetd.conf. This may not be specified from within a service. It must be specified outside a service declaration. .TP .B includedir Takes a directory name in the form of "includedir /etc/xinetd.d". Every file inside that directory, excluding files with names containing a dot ('.') or ending with a tilde ('~'), will be parsed as xinetd configuration files. The files will be parsed in alphabetical order according to the C locale. This allows you to specify services one per file within a directory. The .B includedir directive may not be specified from within a service declaration. .TP .B rlimit_as Sets the Address Space resource limit for the service. One parameter is required, which is either a positive integer representing the number of bytes to set the limit to (K or M may be used to specify kilobytes/megabytes) or "UNLIMITED". Due to the way Linux's libc malloc is implemented, it is more useful to set this limit than rlimit_data, rlimit_rss and rlimit_stack. This resource limit is only implemented on Linux systems. .TP .B rlimit_files Sets the maximum number of open files that the service may use. One parameter is required, which is a positive integer representing the number of open file descriptors. Practical limit of this number is around 1024000. .TP .B rlimit_cpu Sets the maximum number of CPU seconds that the service may use. One parameter is required, which is either a positive integer representing the number of CPU seconds limit to, or "UNLIMITED". .TP .B rlimit_data Sets the maximum data size resource limit for the service. One parameter is required, which is either a positive integer representing the number of bytes or "UNLIMITED". .TP .B rlimit_rss Sets the maximum resident set size limit for the service. Setting this value low will make the process a likely candidate for swapping out to disk when memory is low. One parameter is required, which is either a positive integer representing the number of bytes or "UNLIMITED". .TP .B rlimit_stack Set the maximum stack size limit for the service. One parameter is required, which is either a positive integer representing the number of bytes or "UNLIMITED". .TP .B deny_time Sets the time span that access to all services on all IP addresses are denied to someone that sets off the SENSOR. The unit of time is in minutes. Valid options are: FOREVER, NEVER, and a numeric value. FOREVER causes the IP address not to be purged until xinetd is restarted. NEVER has the effect of just logging the offending IP address. A typical time value would be 60 minutes. This should stop most DOS attacks while allowing IP addresses that come from a pool to be recycled for legitimate purposes. This option must be used in conjunction with the SENSOR flag. .LP You don't need to specify all of the above attributes for each service. The necessary attributes for a service are: .sp 1 .PD .1v .RS .TP 18 .B socket_type .TP .B user (non-\fIinternal\fP services only) .TP .B server (non-\fIinternal\fP services only) .TP .B wait .TP .B protocol (\fIRPC\fP and \fIunlisted\fP services only) .TP .B rpc_version (\fIRPC\fP services only) .TP .B rpc_number (\fIunlisted\fP RPC services only) .TP .B port (\fIunlisted\fP non\-RPC services only) .RE .PD .LP The following attributes support all assignment operators: .sp 1 .PD .1v .RS .TP 18 .B only_from .TP .B no_access .TP .B log_on_success .TP .B log_on_failure .TP .B passenv .TP .B env (does not support the .B '-=' operator) .RE .PD .LP These attributes can also appear more than once in a service entry. The remaining attributes support only the .B '=' operator and can appear at most once in a service entry. .LP The configuration file may also contain a single defaults entry that has the form .LP .RS .nf .ft B defaults { .RS .ft B = ... .I "..." .RE .ft B } .ft R .fi .RE .LP This entry provides default attribute values for service entries that don't specify those attributes. Possible default attributes: .sp 1 .PD .1v .RS .TP 18 .B log_type (cumulative effect) .TP .B bind .TP .B per_source .TP .B umask .TP .B log_on_success (cumulative effect) .TP .B log_on_failure (cumulative effect) .TP .B only_from (cumulative effect) .TP .B no_access (cumulative effect) .TP .B passenv (cumulative effect) .TP .B instances .TP .B disabled (cumulative effect) .TP .B enabled (cumulative effect) .TP .B banner .TP .B banner_success .TP .B banner_fail .TP .B per_source .TP .B groups .TP .B cps .TP .B max_load .TP .RE .PD .LP Attributes with a cumulative effect can be specified multiple times with the values specified each time accumulating (i.e. '=' does the same thing as '+='). With the exception of .I disabled they all have the same meaning as if they were specified in a service entry. .I disabled determines services that are disabled even if they have entries in the configuration file. This allows for quick reconfiguration by specifying disabled services with the .I disabled attribute instead of commenting them out. The value of this attribute is a list of space separated service ids. .I enabled has the same properties as disabled. The difference being that .I enabled is a list of which services are to be enabled. If .I enabled is specified, only the services specified are available. If .I enabled is not specified, all services are assumed to be enabled, except those listed in .I disabled. .\" *********************** INTERNAL SERVICES **************************** .SH "INTERNAL SERVICES" .LP \fBxinetd\fP provides the following services internally (both stream and datagram based): .I echo, .I time, .I daytime, .I chargen, and .I discard. These services are under the same access restrictions as all other services except for the ones that don't require \fBxinetd\fP to fork another process for them. Those ones (\fItime\fP, \fIdaytime\fP, and the datagram\-based \fIecho\fP, \fIchargen\fP, and \fIdiscard\fP) have no limitation in the number of .B instances. .LP .\" *********************** TCPMUX Services **************************** .SH "TCPMUX Services" .LP \fBxinetd\fP supports TCPMUX services that conform to RFC 1078. These services may not have a well\-known port associated with them, and can be accessed via the TCPMUX well\-known port. .LP For each service that is to be accessed via TCPMUX, a service entry in \fB/etc/xinetd.conf\fP or in a configuration file in an \fBincludedir\fP directory must exist. .LP The \fIservice_name\fP field (as defined above for each service in any \fBxinetd\fP configuration file) must be identical to the string that is passed (according to RFC 1078 protocol) to \fBxinetd\fP when the remote service requestor first makes the connection on the TCPMUX well\-known port. Private protocols should use a service name that has a high probability of being unique. One way is to prepend the service name with some form of organization ID. .LP The \fItype\fP field can be either \fBTCPMUX\fP or \fBTCPMUXPLUS\fP. If the type is \fBTCPMUXPLUS\fP, \fBxinetd\fP will handle the initial protocol handshake (as defined in RFC 1078) with the calling process before initiating the service. If the type is \fBTCPMUX\fP, the server that is started is responsible for performing the handshake. .LP The \fItype\fP field should also include \fBUNLISTED\fP if the service is not listed in a standard system file (like .I /etc/rpc for RPC services, or .I /etc/services for non\-RPC services). .LP The \fIsocket_type\fP for these services must be \fBstream\fP, and the \fIprotocol\fP must be \fBtcp\fP. .LP Following is a sample TCPMUX service configuration: .PD .1v .RS .nf service myorg_server { .RS .IP disable 20 = no .IP type = TCPMUX .IP socket_type = stream .IP protocol = tcp .IP wait = no .IP user = root .IP server = /usr/bin/my_server_exec .RE } .fi .RE .PD .LP Besides a service entry for each service that can be accessed via the TCPMUX well\-known port, a service entry for TCPMUX itself must also be included in the \fBxinetd\fP configuration. Consider the following sample: .PD .1v .RS .nf service tcpmux { .RS .IP type 20 = INTERNAL .IP id = tcpmux .IP socket_type = stream .IP protocol = tcp .IP user = root .IP wait = no .RE } .fi .RE .PD .\" *********************** NOTES **************************** .SH NOTES .IP 1. 6 The following service attributes \fIcannot\fP be changed on reconfiguration: .B socket_type, .B wait, .B protocol, .B type. .IP 2. When the attributes .I only_from and .I no_access are not specified for a service (either directly or via \fIdefaults\fP) the address check is considered successful (i.e. access will not be denied). .IP 3. The maximum line length of the configuration file is limited to 16 KiB (it might be less on systems without mmap, the length limit is two times the optimal I/O blocksize then). .IP 4. The address check is based on the IP address of the remote host and not on its domain address. We do this so that we can avoid remote name lookups which may take a long time (since .B xinetd is single\-threaded, a name lookup will prevent the daemon from accepting any other requests until the lookup is resolved). The down side of this scheme is that if the IP address of a remote host changes, then access to that host may be denied until .B xinetd is reconfigured. Whether access is actually denied or not will depend on whether the new host IP address is among those allowed access. For example, if the IP address of a host changes from 1.2.3.4 to 1.2.3.5 and only_from is specified as 1.2.3.0 then access will not be denied. .IP 5. If the .B USERID log option is specified and the remote host either does not run an identification server or the server sends back a bad reply, access will not be denied unless the .I IDONLY service flag is used. .IP 6. Interception works by forking a process which acts as a filter between the remote host(s) and the local server. This obviously has a performance impact so it is up to you to make the compromise between security and performance for each service. The following tables show the overhead of interception. The first table shows the time overhead\-per\-datagram for a UDP\-based service using various datagram sizes. For TCP\-based services we measured the bandwidth reduction because of interception while sending a certain amount of data from client to server (the time overhead should the same as for UDP\-based services but it is "paid" only by the first packet of a continuous data transmission). The amount of data is given in the table as \fIsystem_calls\fPx\fIdata_sent_per_call\fP, i.e. each .I "send(2)" system call transferred so many bytes of data. The bandwidth reduction is given in terms of bytes per second and as a percentage of the bandwidth when interception is not performed. All measurements were done on a SparcStation IPC running SunOS 4.1. .sp 1 .RS .RS .PD .1v .TP 25 Datagram size (bytes) Latency (msec) .TP --------------------- -------------- .TP 64 1.19 .TP 256 1.51 .TP 1024 1.51 .TP 4096 3.58 .sp 2 .TP Bytes sent Bandwidth reduction .TP ---------- ------------------- .TP 10000x64 941 (1.2%) .TP 10000x256 4,231 (1.8%) .TP 10000x1024 319,300 (39.5%) .TP 10000x4096 824,461 (62.1%) .RE .RE .sp 1 .\" *********************** EXAMPLE **************************** .SH EXAMPLE .LP .PD .1v .RS .nf # # Sample configuration file for xinetd # defaults { .RS .IP log_type 20 = FILE /var/log/servicelog .IP log_on_success = PID .IP log_on_failure = HOST .IP only_from = 128.138.193.0 128.138.204.0 .IP only_from = 128.138.252.1 .IP instances = 10 .IP disabled = rstatd .RE } # # Note 1: the protocol attribute is not required # Note 2: the instances attribute overrides the default # service login { .RS .IP socket_type 20 = stream .IP protocol = tcp .IP wait = no .IP user = root .IP server = /usr/sbin/in.rlogind .IP instances = UNLIMITED .RE } # # Note 1: the instances attribute overrides the default # Note 2: the log_on_success flags are augmented # service shell { .RS .IP socket_type 20 = stream .IP wait = no .IP user = root .IP instances = UNLIMITED .IP server = /usr/sbin/in.rshd .IP log_on_success += HOST .RE } service ftp { .RS .IP socket_type 20 = stream .IP wait = no .IP nice = 10 .IP user = root .IP server = /usr/sbin/in.ftpd .IP server_args = \-l .IP instances = 4 .IP log_on_success += DURATION HOST USERID .IP access_times = 2:00-9:00 12:00-24:00 .RE } # Limit telnet sessions to 8 Mbytes of memory and a total # 20 CPU seconds for child processes. service telnet { .RS .IP socket_type 20 = stream .IP wait = no .IP nice = 10 .IP user = root .IP server = /usr/sbin/in.telnetd .IP rlimit_as = 8M .IP rlimit_cpu = 20 .RE } # # This entry and the next one specify internal services. Since # this is the same service using a different socket type, the # id attribute is used to uniquely identify each entry # service echo { .RS .IP id 20 = echo\-stream .IP type = INTERNAL .IP socket_type = stream .IP user = root .IP wait = no .RE } service echo { .RS .IP id 20 = echo\-dgram .IP type = INTERNAL .IP socket_type = dgram .IP user = root .IP wait = no .RE } # # Sample RPC service # service rstatd { .RS .IP type 20 = RPC .IP socket_type = dgram .IP protocol = udp .IP server = /usr/sbin/rpc.rstatd .IP wait = yes .IP user = root .IP rpc_version = 2-4 .IP env = LD_LIBRARY_PATH=/etc/securelib .RE } # # Sample unlisted service # service unlisted { .RS .IP type 20 = UNLISTED .IP socket_type = stream .IP protocol = tcp .IP wait = no .IP server = /home/user/some_server .IP port = 20020 .RE } .RE .PD .\" *********************** SEE ALSO **************************** .SH "SEE ALSO" .I "xinetd(1L)," .LP .I "xinetd.log(5)" .LP Postel J., .IR "Echo Protocol" , RFC 862, May 1983 .LP Postel J., .IR "Discard Protocol" , RFC 863, May 1983 .LP Postel J., .IR "Character Generator Protocol" , RFC 864, May 1983 .LP Postel J., .IR "Daytime Protocol" , RFC 867, May 1983 .LP Postel J., Harrenstien K., .IR "Time Protocol" , RFC 868, May 1983 .LP M. Lottor, .IR "TCP Port Service Multiplexer (TCPMUX)" , RFC 1078 Nov 1988 .LP StJohns M., .IR " Identification Protocol" , RFC 1413, February 1993 .\" *********************** BUGS **************************** .SH BUGS .LP If the .B INTERCEPT flag is not used, access control on the address of the remote host is not performed when \fIwait\fP is \fIyes\fP and \fIsocket_type\fP is \fIstream\fP. .LP The NOLIBWRAP flag is automatically turned on for RPC services whose \fIsocket_type\fP is \fIstream\fP because xinetd cannot determine the address of the remote host. .LP If the .B INTERCEPT flag is not used, access control on the address of the remote host for services where \fIwait\fP is \fIyes\fP and \fIsocket_type\fP is \fIdgram\fP is performed only on the first packet. The server may then accept packets from hosts not in the access control list. This can happen with .B RPC services. .LP There is no way to put a .SM SPACE in an environment variable. .LP When \fIwait\fP is \fIyes\fP and \fIsocket_type\fP is \fIstream\fP, the socket passed to the server can only accept connections. .LP The .B INTERCEPT flag is not supported for internal services or multi\-threaded services. xinetd-2.3.15.4/man/xinetd.log.5000066400000000000000000000061371333055217000161610ustar00rootroot00000000000000.\"(c) Copyright 1992 by Panagiotis Tsirigotis .\"(c) Sections Copyright 1998-2001 by Rob Braun .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH XINETD.LOG 5 "28 April 1993" .SH NAME xinetd.log \- xinetd service log format .\" *********************** DESCRIPTION **************************** .SH "DESCRIPTION" A service configuration may specify various degrees of logging when attempts are made to access the service. When logging for a service is enabled, .B xinetd will generate one-line log entries which have the following format (all entries have a timestamp as a prefix): .sp 1 .RS \fIentry\fP: \fIservice-id\fP \fIdata\fP .RE .LP The \fIdata\fP depends on the \fIentry\fP. Possible \fIentry\fP types include: .RS .TP 12 .B START generated when a server is started .TP .B EXIT generated when a server exits .TP .B FAIL generated when it is not possible to start a server .TP .B USERID generated if the \fIUSERID\fP log option is used. .TP .B NOID generated if the .I USERID log option is used, and the .I IDONLY service flag is used, and the remote end does not identify who is trying to access the service. .RE .LP In the following, the information enclosed in brackets appears if the appropriate log option is used. .LP A \fISTART\fP entry has the format: .sp 1 .RS START: \fIservice-id\fP [pid=%d] [from=%d.%d.%d.%d] .RE .LP An \fIEXIT\fP entry has the format: .sp 1 .RS EXIT: \fIservice-id\fP [\fItype\fP=%d] [pid=%d] [duration=%d(sec)] .RE .sp 1 .I type can be either .B status or .B signal. The number is either the exit status or the signal that caused process termination. .LP A \fIFAIL\fP entry has the format: .sp 1 .RS FAIL: \fIservice-id\fP \fIreason\fP [from=%d.%d.%d.%d] .RE .sp 1 Possible \fIreasons\fP are: .RS .TP 15 .B fork a certain number of consecutive fork attempts failed (this number is a configurable parameter) .TP .B time the time check failed .TP .B address the address check failed .TP .B service_limit the allowed number of server instances for this service would be exceeded .TP .B process_limit a limit on the number of forked processes was specified and it would be exceeded .RE .LP A \fIDATA\fP entry has the format: .sp 1 .RS DATA: \fIservice-id\fP \fIdata\fP .RE .sp 1 The \fIdata\fP logged depends on the service. .RS .TP 12 .B login remote_user=%s local_user=%s tty=%s .TP .B exec remote_user=%s verify=\fIstatus\fP command=%s .br Possible .I status values: .RS .TP 10 .I ok the password was correct .TP .I failed the password was incorrect .TP .I baduser no such user .RE .TP .B shell remote_user=%s local_user=%s command=%s .TP .B finger \fIreceived string\fP or .I EMPTY-LINE .RE .LP A \fIUSERID\fP entry has the format: .sp 1 .RS USERID: \fIservice-id\fP \fItext\fP .RE .sp 1 The \fItext\fP is the response of the identification daemon at the remote end excluding the port numbers (which are included in the response). .LP A \fINOID\fP entry has the format: .sp 1 .RS NOID: \fIservice-id\fP \fIIP-address\fP \fIreason\fP .RE .\" *********************** SEE ALSO **************************** .SH "SEE ALSO" .BR xinetd "(1L), " xinetd.conf (5) xinetd-2.3.15.4/man/xlog.3000066400000000000000000000156341333055217000150570ustar00rootroot00000000000000.\"(c) Copyright 1992, 1993 by Panagiotis Tsirigotis .\"All rights reserved. The file named COPYRIGHT specifies the terms .\"and conditions for redistribution. .\" .\" $Id$ .TH XLOG 3X "15 June 1993" xlog_parms, xlog_create, xlog_destroy, xlog_write, xlog_control -- general purpose logging facility .SH SYNOPSIS .LP .nf .ft B #include "xlog.h" .LP .ft B xlog_h xlog_create( type, id, flags, ... ) xlog_e type ; char *id ; int flags ; .LP .ft B int xlog_parms( type, ... ) xlog_e type ; .LP .ft B void xlog_destroy( xlog ) xlog_h xlog ; .LP .ft B void xlog_write( xlog, buf, len, flags, ... ) xlog_h xlog ; char buf[] ; int len ; int flags ; .LP .ft B int xlog_control( xlog, cmd, ... ) xlog_h xlog ; xlog_cmd_e cmd ; .SH DESCRIPTION The purpose of this library is to provide a general purpose logging facility by providing .I xlogs, logging objects that may be connected to various existent logging facilities. Currently, the only logging facilities supported are .I "syslog(3)" and logging to files. Log entries are timestamped lines which may contain arbitrary information. .\" ********************* xlog_create *********************** .LP .B xlog_create() creates a new xlog of the specified .I type. Possible type values are: .RS .TP 20 .SB XLOG_SYSLOG Varargs: \fIint facility, int priority\fP. The xlog will be connected to .I "syslog(3)." .I facility determines the syslog facility to use for logged messages and .I priority is the default message priority. .TP .SB XLOG_FILELOG Varargs: \fIchar *pathname, int flags [, int flags]\fP. The xlog will be connected to the file identified by .I pathname. The variable part of the argument list has the same semantics as the argument list of the .I "open(2)" system call. .RE .LP All xlogs have an id, specified by the .I id argument. The .I flags argument is formed by ORing one or more of the following constants: .RS .TP 20 .SB XLOG_NO_ERRNO do not replace .I "%m" with an explanation of the current value of errno. .TP .SB XLOG_NO_SIZECHECK .I "(XLOG_FILELOG only)" do not perform size checks on the file. .TP .SB XLOG_PRINT_ID precede each log entry with the xlog id .TP .SB XLOG_PRINT_PID precede each log entry with the process id (the process id will follow the xlog id) .RE .LP Flags that do not apply to the xlog are ignored. The contant .SM XLOG_NOFLAGS can be used if you don't want to specify any flags. .\" ********************* xlog_parms *********************** .LP .B xlog_parms() sets default parameters for the specified xlog .I type: .RS .TP 20 .SB XLOG_SYSLOG 3 arguments are expected which correspond one-to-one to the arguments of .I "openlog(3)." The defaults, in case this function is not used, are: "XLOG", LOG_PID + LOG_NOWAIT, LOG_USER .TP .SB XLOG_FILELOG No action. .RE .LP .B xlog_parms() should be invoked before any xlogs of the specified type are created. .\" ********************* xlog_destroy *********************** .LP .B xlog_destroy() destroys an xlog. The action taken depends on the type of the xlog: .RS .TP 20 .SB XLOG_SYSLOG if this is the last xlog using syslog, then .I "closelog(3)" is invoked. .TP .SB XLOG_FILELOG The file is closed. .RE .\" ********************* xlog_control *********************** .LP .B xlog_control() applies control operations to an xlog. Certain operations are common to all xlogs while others are type-specific. The common ones are: .RS .TP 15 .SB XLOG_LINK Argument list: \fIxlog_h link_to\fP. Link the specified xlog to the one provided as argument. (if the argument is .SM NULL any existent link is severed). Linking xlogs has the effect that if one finds an error it uses the other to report it. .TP .SB XLOG_CALLBACK Argument list: \fIvoid (*callback)(), void *arg\fP. This function will be invoked in case of error while writing a log entry. It will be given 3 arguments: the xlog handle, an integer that indicates the type of error and the .I arg specified in this call. Possible errors include: .RS .TP 15 .SB XLOG_ENOMEM lack of memory .TP .SB XLOG_EOPEN .I "open(2)" failed .TP .SB XLOG_EFSTAT .I "fstat(2)" failed .TP .SB XLOG_ESIZE hard limit exceeded .RE .TP .SB XLOG_SETFLAG Argument list: \fIint flag, int *value\fP. The value of .I flag (one of those listed before) is set according to .I "*value" which should be either 0 or 1. The old flag value is placed in .I "*value." .TP .SB XLOG_GETFLAG Argument list: \fIint flag, int *value\fP. The value of .I flag (one of those listed before) is placed in .I "*value." .RE .LP Xlogs of type .B XLOG_SYSLOG also support the following operations: .RS .TP 15 .SB XLOG_FACILITY Argument list: \fIint facility\fP. Sets the syslog facility to the specified value. .TP .SB XLOG_LEVEL Argument list: \fIint level\fP. Sets the default syslog level for this xlog to the specified value. .TP .SB XLOG_PREEXEC Argument list: \fIvoid\fP. Prepares the xlog for an impending exec operation .TP .SB XLOG_POSTEXEC Argument list: \fIvoid\fP. Informs the xlog that the exec failed .RE .LP Xlogs of type .B XLOG_FILELOG also support the following operations: .RS .TP 15 .SB XLOG_LIMITS Argument list: \fIunsigned soft_limit, unsigned hard_limit\fP. Sets soft and hard limits on the size of the file. When any of the limits is exceeded a message is sent to the linked xlog. (if there is no linked xlog, no message is sent) When the soft limit is exceeded a warning message is sent to the linked xlog (if the linked xlog is of the .SB XLOG_SYSLOG type, the message will be sent at the .I LOG_ALERT level). If the exceeded limit is the hard limit, logging is terminated. The actual file size is checked every time this operation is applied to the file. If logging was terminated because the hard limit was exceeded and this operation increases the hard limit beyond the actual file size, logging will be resumed. .TP .SB XLOG_SIZECHECK Argument list: \fIvoid\fP. Checks the actual file size. .TP .SB XLOG_GETFD Argument list: \fIint *value\fP. Places in .I "*value" the file descriptor of the log file. .RE .\" ********************* xlog_write *********************** .LP .B xlog_write() writes a message to the specified xlog. A .SM NEWLINE is always appended to the message. The first occurrence of "%m" in .I buf is replaced by a string explaining the current value of .I errno. The .I flags argument is formed in the same way as in .B xlog_create(). One additional constant is available: .RS .TP 20 .SB XLOG_SET_LEVEL .I "(XLOG_SYSLOG only)" the next argument is an integer that should be used as the syslog level for this message instead of the default level of the xlog. .RE .SH "RETURN VALUES" .B xlog_create() returns an xlog handle or .SM NULL if it fails. .LP .B xlog_control() returns an error code (it returns .SM XLOG_ENOERROR if it is successful). .LP .B xlog_parms() returns an error code (it returns .SM XLOG_ENOERROR if it is successful). .SH "SEE ALSO" openlog(3), syslog(3), closelog(3) .SH BUGS .LP Only the first occurrence of .I "%m" is replaced by an errno explanation. .LP There is no check for cycles when linking xlogs. In particular it is possible to link a xlog to itself. xinetd-2.3.15.4/src/000077500000000000000000000000001333055217000140255ustar00rootroot00000000000000xinetd-2.3.15.4/src/access.c000066400000000000000000000251031333055217000154330ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #endif #include "str.h" #include "access.h" #ifdef LIBWRAP #include #include int deny_severity = LOG_INFO; int allow_severity = LOG_INFO; #endif #ifdef HAVE_LOADAVG #include "xgetloadavg.h" #endif #include "msg.h" #include "addr.h" #include "sconf.h" #include "log.h" #include "main.h" /* for ps */ #include "sconst.h" #include "sensor.h" #include "state.h" #include "timex.h" #include "xconfig.h" #include "xtimer.h" #ifdef NAME_MAX #define NAME_MAX 256 #endif const struct name_value access_code_names[] = { { "address", (int) AC_ADDRESS }, { "time", (int) AC_TIME }, { "fork", (int) AC_FORK }, { "service_limit", (int) AC_SERVICE_LIMIT }, { "per_source_limit", (int) AC_PER_SOURCE_LIMIT}, { "process_limit", (int) AC_PROCESS_LIMIT }, { "libwrap", (int) AC_LIBWRAP }, { "load", (int) AC_LOAD }, { "connections per second", (int) AC_CPS }, { CHAR_NULL, 1 }, { "UNKNOWN", 0 } } ; /* This is called by the flags processor */ static void cps_service_restart(void) { unsigned int i; time_t nowtime; const char *func = "cps_service_restart"; nowtime = time(NULL); for( i=0; i < pset_count( SERVICES(ps) ); i++ ) { struct service *sp; struct service_config *scp; sp = pset_pointer( SERVICES(ps), i); if( SVC_STATE(sp) == SVC_DISABLED ) { scp = SVC_CONF( sp ); if ( SC_TIME_REENABLE(scp) <= nowtime ) { /* re-enable the service */ if( svc_activate(sp) == OK ) { msg(LOG_ERR, func, "Activating service %s", SC_NAME(scp)); } else { msg(LOG_ERR, func, "Error activating service %s", SC_NAME(scp)) ; } /* else */ } } } /* for */ } void cps_service_stop(struct service *sp, const char *reason) { struct service_config *scp = SVC_CONF( sp ) ; time_t nowtime; svc_deactivate( sp ); msg(LOG_ERR, "service_stop", "Deactivating service %s due to %s. Restarting in %d seconds.", SC_NAME(scp), reason, (int)SC_TIME_WAIT(scp)); nowtime = time(NULL); SC_TIME_REENABLE(scp) = nowtime + SC_TIME_WAIT(scp); xtimer_add(cps_service_restart, SC_TIME_WAIT(scp)); } /* * Returns OK if the IP address in sinp is acceptable to the access control * lists of the specified service. */ static status_e remote_address_check(const struct service *sp, const union xsockaddr *sinp) { /* * of means only_from, na means no_access */ const char *func = "remote_addr_chk"; bool_int of_matched = FALSE; bool_int na_matched = FALSE; /* The IP address is unknown. Nothing to reject, so accept it. */ if (sinp == NULL ) return OK; if ( SC_SENSOR( SVC_CONF(sp) )) { /* They hit a sensor...return FAILED since this isn't a real service */ process_sensor( sp, sinp ) ; return FAILED ; } /* They hit a real server...note, this is likely to be a child process. */ else if ( check_sensor( sinp ) == FAILED ) return FAILED ; /* * The addrlist_match function returns an offset+1 to a matching * entry in the supplied list. It is not a true/false answer. */ if ( SC_NO_ACCESS( SVC_CONF(sp) ) != NULL ) na_matched = addrlist_match( SC_NO_ACCESS( SVC_CONF(sp) ), CSA(sinp)); if ( SC_ONLY_FROM( SVC_CONF(sp) ) != NULL ) of_matched = addrlist_match( SC_ONLY_FROM( SVC_CONF(sp) ), CSA(sinp)); /* * Check if the specified address is in both lists */ if ( na_matched && of_matched ) { /* * If there is a match in both lists, this is an error in the * service entry and we cannot allow a server to start. * We do not disable the service entry (not our job). */ msg( LOG_ERR, func, "Service=%s: only_from list and no_access list match equally the address %s", SVC_ID( sp ), xaddrname( sinp ) ) ; return FAILED ; } /* A no_access list was specified and the socket is on it, fail */ if ( SC_NO_ACCESS( SVC_CONF(sp) ) != NULL && (na_matched != 0) ) return FAILED ; /* A only_from list was specified and the socket wasn't on the list, fail */ if ( SC_ONLY_FROM( SVC_CONF(sp) ) != NULL && (of_matched == 0) ) return FAILED ; /* If no lists were specified, the default is to allow starting a server */ return OK ; } /* * mp is the mask pointer, t is the check type */ #define CHECK( mp, t ) ( ( (mp) == NULL ) || M_IS_SET( *(mp), t ) ) /* * Perform the access controls specified by check_mask. * If check_mask is NULL, perform all access controls */ access_e access_control( struct service *sp, const connection_s *cp, const mask_t *check_mask ) { struct service_config *scp = SVC_CONF( sp ) ; #ifdef LIBWRAP const char *func = "access_control"; #endif /* make sure it's not one of the special pseudo services */ if( (strncmp(SC_NAME( scp ), INTERCEPT_SERVICE_NAME, sizeof(INTERCEPT_SERVICE_NAME)) == 0) || (strncmp(SC_NAME( scp ), LOG_SERVICE_NAME, sizeof(LOG_SERVICE_NAME)) == 0) ) { return (AC_OK); } /* This has to be before the TCP_WRAPPERS stuff to make sure that the sensor gets a chance to see the address */ if ( CHECK( check_mask, CF_ADDRESS ) && remote_address_check( sp, CONN_XADDRESS( cp ) ) == FAILED ) return( AC_ADDRESS ) ; if( ! SC_NOLIBWRAP( scp ) ) { /* LIBWRAP code block */ #ifdef LIBWRAP struct request_info req; char *server = NULL; /* get the server name to pass to libwrap */ if( SC_NAMEINARGS( scp ) ) { if ( SC_SERVER_ARGV(scp) ) server = strrchr( SC_SERVER_ARGV(scp)[0], '/' ); } else { if( SC_SERVER(scp) == NULL ) { /* probably an internal server, use the service id instead */ server = SC_ID(scp); server--; /* nasty. we increment it later... */ } else { server = strrchr( SC_SERVER(scp), '/' ); } } /* If this is a redirection or internal , go by the service name, * since the server name will be bogus. */ if( (SC_REDIR_ADDR(scp) != NULL) || (SC_IS_INTERNAL(scp) )) { server = SC_NAME(scp); server--; /* nasty but ok. */ } if( server == NULL ) { if ( SC_SERVER_ARGV(scp)) server = SC_SERVER_ARGV(scp)[0]; } else server++; if ( scp->sc_libwrap != NULL ) { server = SC_LIBWRAP(scp); } if ( server == NULL ) { msg(deny_severity, func, "server param not provided to libwrap refusing connection to %s from %s", SC_ID(scp), conn_addrstr(cp)); return(AC_LIBWRAP); } request_init(&req, RQ_DAEMON, server, RQ_FILE, cp->co_descriptor, 0); fromhost(&req); if (!hosts_access(&req)) { msg(deny_severity, func, "libwrap refused connection to %s (libwrap=%s) from %s", SC_ID(scp), server, conn_addrstr(cp)); return(AC_LIBWRAP); } #endif } /* LIBWRAP code block */ return( AC_OK ) ; } /* Do the "light weight" access control here */ access_e parent_access_control( struct service *sp, const connection_s *cp ) { struct service_config *scp = SVC_CONF( sp ) ; int n; time_t nowtime; /* make sure it's not one of the special pseudo services */ if( (strncmp(SC_NAME( scp ), INTERCEPT_SERVICE_NAME, sizeof(INTERCEPT_SERVICE_NAME)) == 0) || (strncmp(SC_NAME( scp ), LOG_SERVICE_NAME, sizeof(LOG_SERVICE_NAME)) == 0) ) return (AC_OK); /* CPS handler */ if( SC_TIME_CONN_MAX(scp) != 0 ) { int time_diff; nowtime = time(NULL); time_diff = nowtime - SC_TIME_LIMIT(scp) ; if( SC_TIME_CONN(scp) == 0 ) { SC_TIME_CONN(scp)++; SC_TIME_LIMIT(scp) = nowtime; } else if( time_diff < SC_TIME_CONN_MAX(scp) ) { SC_TIME_CONN(scp)++; if( time_diff == 0 ) time_diff = 1; if( SC_TIME_CONN(scp)/time_diff > SC_TIME_CONN_MAX(scp) ) { cps_service_stop(sp, "excessive incoming connections"); return(AC_CPS); } } else { SC_TIME_LIMIT(scp) = nowtime; SC_TIME_CONN(scp) = 1; } } #ifdef HAVE_LOADAVG if ( SC_MAX_LOAD(scp) != 0 ) { if ( xgetloadavg() >= SC_MAX_LOAD(scp) ) { msg(LOG_ERR, "xinetd", "refused connect from %s due to excessive load", conn_addrstr(cp)); return( AC_LOAD ); } } #endif if ( SC_ACCESS_TIMES( scp ) != NULL && ! ti_current_time_check( SC_ACCESS_TIMES( scp ) ) ) return( AC_TIME ) ; if ( SC_INSTANCES( scp ) != UNLIMITED && SVC_RUNNING_SERVERS( sp ) >= (unsigned)SC_INSTANCES( scp ) ) return( AC_SERVICE_LIMIT ) ; if( SC_PER_SOURCE(scp) != UNLIMITED ) { if ( CONN_XADDRESS(cp) != NULL ) { unsigned int u ; n = 0 ; for ( u = 0 ; u < pset_count( SERVERS( ps ) ) ; u++ ) { struct server *serp = NULL; connection_s *cop = NULL; serp = SERP( pset_pointer( SERVERS( ps ), u ) ) ; if ( (SERVER_SERVICE( serp ) == sp) && ( cop = SERVER_CONNECTION( serp ) ) ) { if ( SC_IPV6( scp ) && IN6_ARE_ADDR_EQUAL( &(cop->co_remote_address.sa_in6.sin6_addr), &((CONN_XADDRESS(cp))->sa_in6.sin6_addr)) ) n++; if ( SC_IPV4( scp ) && (cop->co_remote_address.sa_in.sin_addr.s_addr == (CONN_XADDRESS(cp))->sa_in.sin_addr.s_addr) ) n++; } } if ( n >= SC_PER_SOURCE(scp) ) return( AC_PER_SOURCE_LIMIT ) ; } } if ( ps.ros.process_limit ) { unsigned processes_to_create = SC_IS_INTERCEPTED( scp ) ? 2 : 1 ; if ( pset_count( SERVERS( ps ) ) + processes_to_create > ps.ros.process_limit ) { return( AC_PROCESS_LIMIT ) ; } } return (AC_OK); } xinetd-2.3.15.4/src/access.h000066400000000000000000000033431333055217000154420ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef ACCESS_H #define ACCESS_H /* * $Id$ */ #include "util.h" /* for nv_get_name() */ #include "defs.h" /* bool_int */ #include "connection.h" /* * These flags are used to form a mask for access_control. * The mask determines which checks will be performed. */ #define CF_ADDRESS 1 #define CF_TIME 2 #define CF_SERVICE_LIMIT 3 typedef enum { AC_OK, /* ok to start a server */ AC_FORK, /* tried to start a server but fork failed */ AC_ADDRESS, /* we do not accept requests from that address */ AC_TIME, /* we do not accept requests at this time */ AC_SERVICE_LIMIT, /* server limit would be exceeded for this */ /* service */ AC_PER_SOURCE_LIMIT, /* server limit would be exceeded for this */ /* service and source address */ AC_PROCESS_LIMIT, /* total process limit would be exceeded */ AC_LIBWRAP, AC_LOAD, AC_CPS } access_e ; #define ACCESS_EXPLAIN( code ) nv_get_name( access_code_names, (int) (code) ) extern const struct name_value access_code_names[]; void cps_service_stop(struct service *sp, const char *reason); access_e access_control(struct service *sp, const connection_s *cp,const mask_t *check_mask); access_e parent_access_control(struct service *sp,const connection_s *cp); #endif /* ACCESS_H */ xinetd-2.3.15.4/src/addr.c000066400000000000000000000521371333055217000151130ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sio.h" #include "str.h" #include "addr.h" #include "msg.h" #include "util.h" #include "xtimer.h" #include "libportable.h" #define OPEN_CURLY_BRACKET '{' #define CLOSED_CURLY_BRACKET '}' #define COMMA ',' #define DOT '.' #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 255 #endif typedef enum { CANT_PARSE, PARSED, ERROR } result_e ; typedef enum { NUMERIC_ADDR, NET_ADDR, HOST_ADDR } address_e ; /* * address types denote how the actual numeric address was obtained. * Currently they are only useful for debugging. * Note that NUMERIC_ADDR includes both simple (e.g. 128.138.91.1) and * factorized symbolic addresses (e.g. 128.138.91.{1,2,3}). */ struct comp_addr { address_e addr_type ; char name[MAXHOSTNAMELEN+1] ; char version; /* v4 vs. v6 addresses/masks */ union { struct in6_addr addr6 ; uint32_t addr ; /* host byte order */ } a; union { struct in6_addr mask6 ; uint32_t mask ; } m; } ; #define CAP( p ) ( (struct comp_addr *) (p) ) #define NEW_CAP() NEW( struct comp_addr ) #define FREE_CAP( cap ) FREE( cap ) /* The addrlist_match function sets the mask for IPv6 addresses. * mask is a pointer to the in6_addr structure, bits is the * number of bits to set in the mask, and len is the length of mask. */ static void xsetmask(char *mask, unsigned int bits, unsigned int len) { int i; int bytes = bits/8; int remain = bits - (bytes * 8); memset(mask, 0, len); /* This may be wrong for bigendian... */ for(i=0; i < bytes; i++ ) { mask[i] = 0xFF; } if( remain > 0 ) mask[i] = ( 0xFF << (8-remain) ); return; } /* This is a helper function to make address matching with mask * work ok w/ipv6. The len parameter is in bytes, not bits. * Returns TRUE if addr1&mask1 == addr2 */ static bool_int xmatch(const char *addr1, const char *mask1, const char *addr2, int len) { int i; for(i=0; i < len; i++ ) { if( (addr1[i] & mask1[i]) != ( addr2[i] & mask1[i] ) ) return( FALSE ); } return TRUE; } /* * This function returns 0 if no match and the offset+1 * to list which element in the list matched. The elements * in the addr_list are expected to be comp_addr structs. */ int addrlist_match( const pset_h addr_list, const struct sockaddr *addr ) { unsigned u, addr_count, length; char hname[NI_MAXHOST] ; if ( addr == NULL ) return 0; length = (addr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); addr_count = pset_count( addr_list ); if (addr_count == 0) return 0; hname[0] = 0; for ( u = 0 ; u < addr_count ; u++ ) { struct comp_addr *cap = CAP( pset_pointer( addr_list, u ) ) ; if ( cap == NULL ) continue ; if( cap->addr_type == HOST_ADDR ) { char *tmpname = NULL; if ( hname[0] == 0 ) { memset(hname, 0, NI_MAXHOST); if ( getnameinfo(addr, length, hname, NI_MAXHOST, NULL, 0, NI_NAMEREQD) ) { /* * Name cannot be looked up if here. We should continue * searching the list in case a IP address or net mask agrees */ hname[0] = 0; continue ; } } /* Parse the address as a domain portion */ if( cap->name[0] == '.' ) { tmpname = str_casefind( hname, cap->name ); if( tmpname != NULL ) { if( strlen(cap->name) == strlen(tmpname) ) return( u+1 ); } } else { if( (strlen(hname) == strlen(cap->name)) && (str_casefind( hname, cap->name ) == (char *)hname) ) return( u+1 ); } } /* End HOST_ADDR */ else { /* NUMERIC or NET addresses */ if( (addr->sa_family == AF_INET) && (cap->version == 4) ) { const struct sockaddr_in *inp = CSAIN(addr); if( ( ntohl(inp->sin_addr.s_addr) & cap->m.mask ) == ( cap->a.addr & cap->m.mask ) ) return (u+1) ; } else if( (addr->sa_family == AF_INET6) && (cap->version == 6)) { if (cap->addr_type == NUMERIC_ADDR) { if (IN6_ARE_ADDR_EQUAL(&CSAIN6(addr)->sin6_addr, &cap->a.addr6)) return( u+1 ); } else { /* NET_ADDR */ if ( xmatch( (const char *)CSAIN6(addr)->sin6_addr.s6_addr, (const char *)&(cap->m.mask6), (const char *)&(cap->a.addr6), 16) == TRUE ) return( u+1 ); } } else if (((addr->sa_family) == AF_INET6) && (cap->version == 4)) { /* * If it's a mapped address, and a v4 address is specified, see * if the mapped address matches the v4 equivalent. */ if( IN6_IS_ADDR_V4MAPPED( &CSAIN6(addr)->sin6_addr ) ) { uint32_t tmp_addr; memcpy(&tmp_addr, &CSAIN6(addr)->sin6_addr.s6_addr[12], sizeof(tmp_addr)); if( (ntohl(tmp_addr) & cap->m.mask) == ( cap->a.addr & cap->m.mask ) ) return (u+1); } } } /* End NUMERIC or NET address check */ } /* End for loop */ return ( 0 ); } void addrlist_dump( const pset_h addr_list, int fd ) { unsigned u, num ; char addrstring[1025]; char maskstring[1025]; num = pset_count( addr_list ); for ( u = 0 ; u < num ; u++ ) { struct comp_addr *cap = CAP( pset_pointer( addr_list, u ) ) ; const char *type ; if ( cap->addr_type == NUMERIC_ADDR ) type = "NUMERIC" ; else if ( cap->addr_type == NET_ADDR ) type = "NET" ; else if ( cap->addr_type == HOST_ADDR ) type = "HOST" ; else type = "BAD" ; memset(addrstring, 0, sizeof(addrstring)); memset(maskstring, 0, sizeof(maskstring)); if( cap->version == 4 ) { uint32_t addr = htonl(cap->a.addr); uint32_t mask = htonl(cap->m.mask); inet_ntop(AF_INET, &addr, addrstring, sizeof(addrstring)); inet_ntop(AF_INET, &mask, maskstring, sizeof(maskstring)); } else if( cap->version == 6 ) { inet_ntop(AF_INET6, &cap->a.addr6, addrstring, sizeof(addrstring)); inet_ntop(AF_INET6, &cap->m.mask6, maskstring, sizeof(maskstring)); } if ( cap->addr_type == NET_ADDR ) Sprint(fd, " %s/%s(%s)", addrstring, maskstring, type); else if ( cap->addr_type == HOST_ADDR ) Sprint( fd, " %s(%s)", cap->name, type ) ; else Sprint( fd, " %s(%s)", addrstring, type ) ; } } void addrlist_free( pset_h addr_list ) { pset_apply( addr_list, free, NULL ) ; } /* * Verify's an address has more than numbers & dots. * Returns 0 if numbers & dots, 1 otherwise. */ int check_hostname( const char *addr ) { int i; for (i = 0; addr[i]; ++i) { if ( !isdigit(addr[i]) && (addr[i] != '.') ) return 1; } return 0; } /* * Add an address to the address list */ static status_e add( pset_h addr_list, const struct comp_addr *cap ) { struct comp_addr *new_cap = NULL; const char *func = "add" ; new_cap = NEW_CAP() ; if ( new_cap == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } *new_cap = *cap ; if ( pset_add( addr_list, new_cap ) == NULL ) { out_of_memory( func ) ; FREE_CAP( new_cap ) ; return( FAILED ) ; } return( OK ) ; } /* * Find the address and remove it from the list * Since there is no check when we add entries that an * address is not entered twice, in this function we remove all * addresses that match. * * XXX: we need to work on the way two cap's are considered equal */ static status_e xremove( pset_h addr_list, const struct comp_addr *cap ) { unsigned u = 0 ; struct comp_addr *old_cap ; for ( u = 0 ; u < pset_count( addr_list ) ; u++ ) { old_cap = CAP( pset_pointer( addr_list, u ) ) ; if ( (cap->addr_type == HOST_ADDR) && ( old_cap->addr_type == HOST_ADDR )) { if ( EQ(cap->name, old_cap->name) ) { pset_pointer( addr_list, u ) = NULL ; FREE_CAP( old_cap ) ; } } /* If the versions are the same, and the v6 addresses are the same, * or the v4 addresses are the same, then one's a dup. */ else if ( (old_cap->version == cap->version) && (((old_cap->version == 6) && (IN6_ARE_ADDR_EQUAL( &(old_cap->a.addr6), &(cap->a.addr6))) && (IN6_ARE_ADDR_EQUAL( &(old_cap->m.mask6), &(cap->m.mask6))) ) || ((old_cap->version == cap->version) && (old_cap->version == 4) && old_cap->a.addr == cap->a.addr && old_cap->m.mask == cap->m.mask)) ) { pset_pointer( addr_list, u ) = NULL ; FREE_CAP( old_cap ) ; } } pset_compact( addr_list ) ; return( OK ) ; } /* * Function allows the use of 0.0.0.0/24 style address ranges for access cntl. * --bbraun 10/26/98 * * Updated to handle ::/46 style address ranges for access cntl. */ static result_e explicit_mask( const char *str_addr, statfunc op, pset_h addr_list) { struct comp_addr ca ; int val; unsigned mask; struct addrinfo hints, *res; char saddr[INET6_ADDRSTRLEN]; memset(saddr, 0, INET6_ADDRSTRLEN); if (strchr(str_addr, OPEN_CURLY_BRACKET)) /* Don't even try factorized */ return CANT_PARSE ; if (sizeof(saddr) < 46) return CANT_PARSE ; val = sscanf(str_addr, "%45[^/]/%u", saddr, &mask); if( val < 2 ) return CANT_PARSE ; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_NUMERICHOST; if( getaddrinfo(saddr, NULL, &hints, &res) ) { return CANT_PARSE; } if( res->ai_family == AF_INET ) { ca.version = 4; ca.a.addr = ntohl( SAIN(res->ai_addr)->sin_addr.s_addr ); if(mask == 32) { ca.addr_type = NUMERIC_ADDR; ca.m.mask = 0xFFFFFFFF; } else { uint32_t i; unsigned n; ca.addr_type = NET_ADDR ; i = 0x80000000U; ca.m.mask = 0; /* Go through and set each bit to 1 */ for( n=mask; n != 0 ; n--) { ca.m.mask |= i; i /= 2; } } } /* PF_INET */ else if( res->ai_family == AF_INET6 ) { ca.version = 6; if( mask >= 128 ) { ca.addr_type = HOST_ADDR; } else { ca.addr_type = NET_ADDR; } memcpy( &ca.a.addr6, &(SAIN6(res->ai_addr)->sin6_addr), sizeof(struct in6_addr) ); xsetmask((char *)&ca.m.mask6, mask, sizeof(ca.m.mask6)); } /* PF_INET6 */ freeaddrinfo(res); return( ( (*op)( addr_list, &ca ) == OK ) ? PARSED : ERROR ) ; } /* * Try to parse 'str_addr' as a symbolic net name * * NOTE: This doesn't work with IPv6 addresses. */ static result_e net_addr( const char *str_addr, statfunc op, pset_h addr_list ) { /* * * The following table demonstrates how the mask is determined * given a net number N and following the relation: * net #1 <= N <= #2 * * net #1 net #2 mask * 0 0 FFFFFFFF (this should be rejected) * 1 FF FF000000 * 100 FFFF FFFF0000 * 10000 FFFFFF FFFFFF00 * 1000000 FFFFFFFF FFFFFFFF */ static struct { uint32_t lim, mask, shift ; } net_to_mask[] = { { 0, 0xFF000000, 24 }, { 0xFF, 0xFFFF0000, 16 }, { 0xFFFF, 0xFFFFFF00, 8 }, { 0xFFFFFF, 0xFFFFFFFF, 0 }, { 0xFFFFFFFF, 0, 0 } } ; struct comp_addr ca ; struct netent *nep ; uint32_t net_num ; int i ; const char *func = "net_addr" ; nep = getnetbyname( str_addr ) ; if ( nep == NULL || nep->n_addrtype != AF_INET || nep->n_net == 0 ) return( CANT_PARSE ) ; for ( i = 0, net_num = (uint32_t) nep->n_net ;; i++ ) { if ( net_to_mask[ i ].mask == 0 ) { msg( LOG_CRIT, func, "INTERNAL ERROR: Cannot process net number %u", net_num ) ; return( ERROR ) ; } if ( net_to_mask[i].lim < net_num && net_num <= net_to_mask[i+1].lim ) { ca.addr_type = NET_ADDR ; ca.a.addr = net_num << net_to_mask[ i ].shift ; ca.m.mask = net_to_mask[ i ].mask ; ca.version = 4; return( ( (*op)( addr_list, &ca ) == OK ) ? PARSED : ERROR ) ; } } } /* * Try to parse 'str_addr' as a numeric address */ static result_e numeric_addr( const char *str_addr, status_e (*op)(), pset_h addr_list ) { struct comp_addr ca ; struct addrinfo hints, *res = NULL; struct in6_addr zero; uint32_t mask, addr; if (strchr(str_addr, '/')) /* Don't even try explicit masks */ return CANT_PARSE; if(strchr(str_addr, OPEN_CURLY_BRACKET)) /* Don't even try factorized */ return CANT_PARSE; memset(&zero, 0, sizeof(zero)); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; if (strchr(str_addr, ':')) hints.ai_family = PF_INET6; else hints.ai_family = PF_INET; if( getaddrinfo(str_addr, NULL, &hints, &res) != 0 ) return CANT_PARSE; if ( res == NULL ) return CANT_PARSE; if ( res->ai_addr == NULL ) { freeaddrinfo(res); return CANT_PARSE; } if( res->ai_family == AF_INET6 ) { ca.version = 6; ca.addr_type = NUMERIC_ADDR; if( memcmp( &(SAIN6(res->ai_addr)->sin6_addr), &zero, sizeof(struct in6_addr) ) == 0 ) { memset( &ca.a.addr6, 0, sizeof(struct in6_addr) ); memset( &ca.m.mask6, 0, sizeof(struct in6_addr) ); } else { memcpy( &ca.a.addr6, &(SAIN6(res->ai_addr)->sin6_addr), sizeof(struct in6_addr) ); memset(&ca.m.mask6, 0xFF, sizeof(struct in6_addr)); } } else if( res->ai_family == AF_INET ) { ca.version = 4; ca.addr_type = NUMERIC_ADDR; if( SAIN(res->ai_addr)->sin_addr.s_addr == 0 ) { ca.a.addr = 0; ca.m.mask = 0; } else { addr = (uint32_t) ntohl( SAIN(res->ai_addr)->sin_addr.s_addr ); for ( mask = 0xFF ;; ) { if ( addr & mask ) break ; mask <<= 8 ; mask |= 0xFF ; } mask = ~( mask >> 8 ) ; ca.a.addr = addr; ca.m.mask = mask; } } freeaddrinfo(res); return( ( (*op)( addr_list, &ca ) == OK ) ? PARSED : ERROR ) ; } /* * Try to parse 'str_addr' as a symbolic host name * Apply 'op' to the 'addrlist' for *all* IP addresses of the host */ static result_e host_addr( const char *str_addr, status_e (*op)(), pset_h addr_list ) { struct comp_addr ca; struct addrinfo hints, *res = NULL; char addr[46]; if (strchr(str_addr, '/')) /* Don't even try explicit masks */ return CANT_PARSE; if(strchr(str_addr, OPEN_CURLY_BRACKET)) /* Don't even try factorized */ return CANT_PARSE; if( str_addr[0] == '.' ) { if ( check_hostname(str_addr) ) { ca.version = 0xFF; ca.addr_type = HOST_ADDR; /* XXX: does this really need to be NUL-padded? */ strncpy(ca.name, str_addr, sizeof(ca.name)-1) ; ca.name[sizeof(ca.name)-1] = '\0'; if ( (*op)( addr_list, &ca ) == FAILED ) return ERROR ; return PARSED ; } else return CANT_PARSE; } memset(&hints, 0, sizeof(hints)); memset(addr, 0, sizeof(addr)); hints.ai_flags = AI_CANONNAME; hints.ai_family = PF_UNSPEC; if ( getaddrinfo(str_addr, NULL, &hints, &res) != 0 ) return CANT_PARSE; if ( res == NULL ) return CANT_PARSE; strncpy(ca.name, str_addr, sizeof(ca.name)-1) ; ca.name[sizeof(ca.name)-1] = '\0'; ca.addr_type = HOST_ADDR ; ca.version = 0xFF; freeaddrinfo(res); if ( (*op)( addr_list, &ca ) == FAILED ) return ERROR ; else return PARSED ; } /* * Try to parse 'str_addr' as a factorized address * (for example, 128.138.{200,201}) * * XXX: It is unclear whether this function should exist. It is really doing * the job of a preprocessor. * * This does not work for IPv6 Addresses. */ static result_e factorized_addr( const char *str_addr, status_e (*op)(), pset_h addr_list ) { int pass ; char last = DOT ; unsigned num = 0 ; int shift = 24 ; /* because we assume a 32-bit IP address */ uint32_t addr = 0 ; struct comp_addr ca ; const char *func = "factorized_addr" ; int i, j; for ( i = 0 ; str_addr[i] != OPEN_CURLY_BRACKET ; last = str_addr[i++] ) { if ( isdigit( str_addr[i] ) ) { num = num * 10 + str_addr[i] - '0' ; continue ; } switch ( str_addr[i] ) { case DOT: if ( last == DOT ) { parsemsg( LOG_ERR, func, "Bad address: %s. Consecutive dots", str_addr ) ; return( ERROR ) ; } addr = addr * 256 + num ; num = 0 ; shift -= 8 ; break ; default: return( CANT_PARSE ) ; } } ca.addr_type = NUMERIC_ADDR ; ca.version = 4; if ( addr != 0 ) addr <<= ( shift+8 ) ; /* * First pass is for syntax checking */ j = i; for ( pass = 0 ; pass < 2 ; pass++ ) { i = j; num = 0 ; for ( i = i+1, last = COMMA ;; last = str_addr[i++] ) { if ( isdigit( str_addr[i] ) ) { num = num * 10 + str_addr[i] - '0' ; continue ; } switch ( str_addr[i] ) { case COMMA: case CLOSED_CURLY_BRACKET: if ( last == COMMA ) { parsemsg( LOG_ERR, func, "Bad address: %s. Consecutive commas", str_addr ) ; return( ERROR ) ; } if ( pass == 1 ) { ca.a.addr = addr + ( num << shift ) ; ca.m.mask = ~( ( 1 << shift ) - 1 ) ; if ( (*op)( addr_list, &ca ) == FAILED ) return( ERROR ) ; num = 0 ; } break ; default: parsemsg( LOG_ERR, func, "Bad address: %s", str_addr ) ; return( ERROR ) ; } if ( str_addr[i] == CLOSED_CURLY_BRACKET ) { if ( str_addr[i+1] != NUL ) { parsemsg( LOG_ERR, func, "Bad address: %s", str_addr ) ; return( ERROR ) ; } if ( pass == 0 ) break ; else return( PARSED ) ; } } } /* NOTREACHED */ return( ERROR ); } /* * Try to parse 'str_addr' using all known methods. * Try until one of the methods succeeds. * A successful method will apply 'op' with the parsed address to the * 'addr_list'. The 'op' can be either 'add' or 'remove' * This means that the parsed address will be either added or removed * from the addr_list. */ static status_e addrlist_op( pset_h addr_list, status_e (*op)(), const char *str_addr ) { int i ; static result_e (*addr_parser[])() = { numeric_addr, host_addr, explicit_mask, factorized_addr, net_addr, NULL } ; const char *func = "addrlist_op" ; if (str_addr == NULL) return FAILED; if (str_addr[0] == NUL ) return OK; for ( i = 0 ; addr_parser[ i ] != NULL ; i++ ) switch ( (*addr_parser[ i ])( str_addr, op, addr_list ) ) { case PARSED: return OK; case ERROR: return FAILED; case CANT_PARSE: break; } parsemsg( LOG_ERR, func, "failed to parse %s", str_addr ) ; return OK; } status_e addrlist_add( pset_h addr_list, const char *str_addr ) { return( addrlist_op( addr_list, add, str_addr ) ) ; } status_e addrlist_remove( pset_h addr_list, const char *str_addr ) { return( addrlist_op( addr_list, xremove, str_addr ) ) ; } status_e addrlist_copy( const pset_h from, pset_h *to ) { return( copy_pset( from, to, sizeof( struct comp_addr ) ) ) ; } xinetd-2.3.15.4/src/addr.h000066400000000000000000000013521333055217000151110ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef ADDR_H #define ADDR_H /* * $Id$ */ #include "pset.h" #include "defs.h" #ifdef HAVE_STDINT_H #include #endif int addrlist_match(const pset_h addr_list, const struct sockaddr *addr); void addrlist_dump(const pset_h addr_list, int fd); void addrlist_free(pset_h addr_list); status_e addrlist_add(pset_h addr_list, const char *str_addr); status_e addrlist_remove(pset_h addr_list, const char *str_addr); status_e addrlist_copy(const pset_h from, pset_h *to); int check_hostname(const char *addr); #endif /* ADDR_H */ xinetd-2.3.15.4/src/attr.h000066400000000000000000000045761333055217000151640ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef ATTR_H #define ATTR_H /* * $Id$ */ /* * Attribute IDs */ #define A_NONE 0 #define A_WAIT 1 #define A_SOCKET_TYPE 2 #define A_PROTOCOL 3 #define A_USER 4 #define A_GROUP 5 #define A_SERVER 6 #define A_SERVER_ARGS 7 #define A_INSTANCES 8 #define A_ID 9 #define A_ONLY_FROM 10 #define A_ACCESS_TIMES 11 #define A_RPC_VERSION 12 #define A_LOG_TYPE 13 #define A_NO_ACCESS 14 #define A_TYPE 15 #define A_LOG_ON_FAILURE 16 #define A_LOG_ON_SUCCESS 17 #define A_ENV 18 #define A_PORT 19 #define A_PASSENV 20 #define A_FLAGS 21 #define A_RPC_NUMBER 22 #define A_NICE 23 #define A_REDIR 24 #define A_BIND 25 #define A_BANNER 26 #define A_PER_SOURCE 27 #define A_GROUPS 28 #define A_BANNER_SUCCESS 29 #define A_BANNER_FAIL 30 #define A_MAX_LOAD 31 #define A_CPS 32 #define A_SVCDISABLE 33 #define A_RLIMIT_AS 34 #define A_RLIMIT_CPU 35 #define A_RLIMIT_DATA 36 #define A_RLIMIT_RSS 37 #define A_RLIMIT_STACK 38 #define A_V6ONLY 39 #define A_DENY_TIME 40 #define A_UMASK 41 #define A_ENABLED 42 #define A_DISABLED 43 #define A_MDNS 44 #define A_LIBWRAP 45 #define A_RLIMIT_FILES 46 /* * SERVICE_ATTRIBUTES is the number of service attributes and also * the number from which defaults-only attributes start. */ #define SERVICE_ATTRIBUTES ( A_MDNS + 2 ) /* * Mask of attributes that must be specified. */ #define NECESSARY_ATTRS ( XMASK( A_SOCKET_TYPE ) + XMASK( A_WAIT ) ) #define NECESSARY_ATTRS_EXTERNAL ( XMASK( A_SERVER ) + XMASK( A_USER ) ) #define NECESSARY_ATTRS_UNLISTED ( XMASK( A_PROTOCOL ) + XMASK( A_PORT ) ) #define NECESSARY_ATTRS_UNLISTED_MUX ( XMASK( A_PROTOCOL ) ) #define NECESSARY_ATTRS_RPC ( XMASK( A_PROTOCOL ) + \ XMASK( A_RPC_VERSION ) ) #define NECESSARY_ATTRS_RPC_UNLISTED XMASK( A_RPC_NUMBER ) #endif /* ATTR_H */ xinetd-2.3.15.4/src/builtins.c000066400000000000000000000415671333055217000160370ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "str.h" #include "pset.h" #include "sio.h" #include "builtins.h" #include "msg.h" #include "connection.h" #include "server.h" #include "service.h" #include "sconf.h" #include "main.h" #include "util.h" #include "xconfig.h" #include "state.h" #include "libportable.h" #include "signals.h" #include "nvlists.h" #include "child.h" #include "access.h" #define BUFFER_SIZE 1024 static void stream_echo(const struct server *) ; static void dgram_echo(const struct server *) ; static void stream_discard(const struct server *) ; static void dgram_discard(const struct server *) ; static void stream_time(const struct server *) ; static void dgram_time(const struct server *) ; static void stream_daytime(const struct server *) ; static void dgram_daytime(const struct server *) ; static void stream_chargen(const struct server *) ; static void dgram_chargen(const struct server *) ; static void tcpmux_handler(const struct server *) ; /* * SG - This is the call sequence to get to a built-in service * * main_loop main.c * svc_request service.c * svc_handle -- aka svc_handler_func -- aka svc_generic_handler service.c * server_run server.c * server_internal server.c * SC_INTERNAL service.h * BUILTIN_INVOKE sc_conf.h * sc_builtin -- index into builtin_services builtins.c */ static const struct builtin_service builtin_services[] = { { "echo", SOCK_STREAM, { stream_echo, FORK } }, { "echo", SOCK_DGRAM, { dgram_echo, NO_FORK } }, { "discard", SOCK_STREAM, { stream_discard, FORK } }, { "discard", SOCK_DGRAM, { dgram_discard, NO_FORK } }, { "time", SOCK_STREAM, { stream_time, NO_FORK } }, { "time", SOCK_DGRAM, { dgram_time, NO_FORK } }, { "daytime", SOCK_STREAM, { stream_daytime, NO_FORK } }, { "daytime", SOCK_DGRAM, { dgram_daytime, NO_FORK } }, { "chargen", SOCK_STREAM, { stream_chargen, FORK } }, { "chargen", SOCK_DGRAM, { dgram_chargen, NO_FORK } }, { "sensor", SOCK_STREAM, { stream_discard, NO_FORK } }, { "sensor", SOCK_DGRAM, { dgram_discard, NO_FORK } }, { "tcpmux", SOCK_STREAM, { tcpmux_handler, FORK } }, { NULL, 0, { NULL, 0 } } } ; const builtin_s *builtin_find( const char *service_name, int type ) { const builtin_s *bsp ; const char *func = "builtin_find" ; if ( (bsp = builtin_lookup( builtin_services, service_name, type )) ) return( bsp ) ; else { const char *type_name; const struct name_value *sock_type = nv_find_name( socket_types, type ); if (sock_type == NULL) type_name = "Unknown socket type"; else type_name = sock_type->name; msg( LOG_ERR, func, "No such internal service: %s/%s - DISABLING", service_name, type_name ) ; return( NULL ) ; } } const builtin_s *builtin_lookup( const struct builtin_service services[], const char *service_name, int type ) { const struct builtin_service *bsp ; for ( bsp = services ; bsp->bs_name != NULL ; bsp++ ) if ( EQ( bsp->bs_name, service_name ) && bsp->bs_socket_type == type ) return( &bsp->bs_handle ) ; return( NULL ) ; } /* * The rest of this file contains the functions that implement the * builtin services */ static void stream_echo( const struct server *serp ) { char buf[ BUFFER_SIZE ] ; ssize_t cc ; int descriptor = SERVER_FD( serp ) ; struct service *svc = SERVER_SERVICE( serp ) ;; if( SVC_WAITS( svc ) ) { descriptor = accept(descriptor, NULL, NULL); if ( descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(svc, "no available descriptors"); return; } } close_all_svc_descriptors(); for ( ;; ) { cc = read( descriptor, buf, sizeof( buf ) ) ; if ( cc == 0 ) break ; if ( cc == (ssize_t)-1 ) { if ( errno == EINTR ) continue ; else break ; } if ( write_buf( descriptor, buf, cc ) == FAILED ) break ; } if( SVC_WAITS( svc ) ) /* Service forks, so close it */ Sclose(descriptor); } static void dgram_echo( const struct server *serp ) { char buf[ DATAGRAM_SIZE ] ; union xsockaddr lsin; ssize_t cc ; socklen_t sin_len = 0; int descriptor = SERVER_FD( serp ) ; if( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in ); else if( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in6 ); cc = recvfrom( descriptor, buf, sizeof( buf ), 0, (struct sockaddr *)( &lsin ), &sin_len ) ; if ( cc != (ssize_t)-1 ) { (void) sendto( descriptor, buf, (size_t)cc, 0, SA( &lsin ), sizeof( lsin ) ) ; } } static void stream_discard( const struct server *serp ) { char buf[ BUFFER_SIZE ] ; ssize_t cc ; int descriptor = SERVER_FD( serp ) ; struct service *svc = SERVER_SERVICE( serp ) ;; if( SVC_WAITS( svc ) ) { descriptor = accept(descriptor, NULL, NULL); if ( descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(svc, "no available descriptors"); return; } } close_all_svc_descriptors(); for ( ;; ) { cc = read( descriptor, buf, sizeof( buf ) ) ; if ( (cc == 0) || ((cc == (ssize_t)-1) && (errno != EINTR)) ) break ; } if( SVC_WAITS( svc ) ) /* Service forks, so close it */ Sclose(descriptor); } static void dgram_discard( const struct server *serp ) { char buf[ 1 ] ; (void) recv( SERVER_FD( serp ), buf, sizeof( buf ), 0 ) ; } /* * Generate the current time using the SMTP format: * 02 FEB 1991 12:31:42 MST * * The result is placed in buf. * buflen is a value-result parameter. It indicates the size of * buf and on exit it has the length of the string placed in buf. */ static void daytime_protocol( char *buf, unsigned int *buflen ) { static const char *month_name[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" } ; time_t now ; struct tm *tmp ; int size = *buflen ; int cc ; (void) time( &now ) ; tmp = localtime( &now ) ; cc = strx_nprint( buf, size, "%02d %s %d %02d:%02d:%02d", tmp->tm_mday, month_name[ tmp->tm_mon ], 1900 + tmp->tm_year, tmp->tm_hour, tmp->tm_min, tmp->tm_sec ) ; if ( cc >= 0 ) { *buflen = cc ; size -= cc ; cc = strftime( &buf[ *buflen ], (size_t)size, " %Z\r\n", tmp ) ; *buflen += cc ; } } static void stream_daytime( const struct server *serp ) { char time_buf[ BUFFER_SIZE ] ; unsigned int buflen = sizeof( time_buf ) ; int descriptor = SERVER_FD( serp ) ; struct service *svc = SERVER_SERVICE( serp ) ;; if( SVC_WAITS( svc ) ) { descriptor = accept(descriptor, NULL, NULL); if ( descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(svc, "no available descriptors"); return; } } daytime_protocol( time_buf, &buflen ) ; (void) write_buf( descriptor, time_buf, (int)buflen ) ; Sclose(descriptor); } static void dgram_daytime( const struct server *serp ) { char time_buf[ BUFFER_SIZE ] ; union xsockaddr lsin ; socklen_t sin_len = 0 ; unsigned int buflen = sizeof( time_buf ) ; int descriptor = SERVER_FD( serp ) ; ssize_t val; if ( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in ); else if ( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in6 ); val = recvfrom( descriptor, time_buf, sizeof( time_buf ), 0, (struct sockaddr *)( &lsin ), &sin_len ); if ( val == (ssize_t)-1 ) return ; daytime_protocol( time_buf, &buflen ) ; (void) sendto( descriptor, time_buf, buflen, 0, SA(&lsin), sizeof( lsin ) ) ; } #define TIME_OFFSET 2208988800UL /* * We always report the time as 32 bits in network-byte-order */ static void time_protocol( unsigned char *timep ) { time_t now ; unsigned long base1900; (void) time( &now ) ; base1900 = (unsigned long)now + TIME_OFFSET ; timep[0] = base1900 >> 24; timep[1] = base1900 >> 16; timep[2] = base1900 >> 8; timep[3] = base1900; } static void stream_time( const struct server *serp ) { unsigned char time_buf[4]; int descriptor = SERVER_FD( serp ); struct service *svc = SERVER_SERVICE( serp ); if( SVC_WAITS( svc ) ) { descriptor = accept(descriptor, NULL, NULL); if ( descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(svc, "no available descriptors"); return; } } time_protocol( time_buf ) ; (void) write_buf( descriptor, (char *) time_buf, 4 ) ; Sclose(descriptor); } static void dgram_time( const struct server *serp ) { char buf[ 1 ] ; unsigned char time_buf[4]; union xsockaddr lsin ; socklen_t sin_len = 0 ; int fd = SERVER_FD( serp ) ; ssize_t val; if ( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in ); else if ( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in6 ); val = recvfrom( fd, buf, sizeof( buf ), 0, (struct sockaddr *)( &lsin ), &sin_len ); if ( val == (ssize_t)-1 ) return ; time_protocol( time_buf ) ; (void) sendto( fd, (char *) time_buf, 4, 0, SA( &lsin ), sin_len ) ; } #define ASCII_PRINTABLE_CHARS 94 #define LINE_LENGTH 72 #define RING_BUF_SIZE ASCII_PRINTABLE_CHARS + LINE_LENGTH static char *ring_buf = NULL ; static char *ring ; #define ASCII_START ( ' ' + 1 ) #define ASCII_END 126 #define min( a, b ) ((a)<(b) ? (a) : (b)) static char *generate_line( char *buf, unsigned int len ) { unsigned int line_len = min( LINE_LENGTH, len-2 ) ; if ( len < 2 ) /* If len < 2, min will be wrong */ return( NULL ) ; /* This never gets freed. That's ok, because the reference to it is * always kept for future reference. */ if ( (ring_buf == NULL) && ((ring_buf = malloc(RING_BUF_SIZE)) == NULL) ) return(NULL); if ( ring == NULL ) { char ch ; char *p ; for ( p = ring_buf, ch = ASCII_START ; p <= &ring_buf[ RING_BUF_SIZE - 1 ] ; p++ ) { *p = ch++ ; if ( ch == ASCII_END ) ch = ASCII_START ; } ring = ring_buf ; } (void) memcpy( buf, ring, line_len ) ; buf[ line_len ] = '\r' ; buf[ line_len+1 ] = '\n' ; ring++ ; if ( &ring_buf[ RING_BUF_SIZE - 1 ] - ring + 1 < LINE_LENGTH ) ring = ring_buf ; return( buf ) ; } static void stream_chargen( const struct server *serp ) { char line_buf[ LINE_LENGTH+2 ] ; int descriptor = SERVER_FD( serp ) ; struct service *svc = SERVER_SERVICE( serp ); if( SVC_WAITS( svc ) ) { descriptor = accept(descriptor, NULL, NULL); if ( descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(svc, "no available descriptors"); return; } } (void) shutdown( descriptor, 0 ) ; close_all_svc_descriptors(); for ( ;; ) { if ( generate_line( line_buf, sizeof( line_buf ) ) == NULL ) break ; if ( write_buf( descriptor, line_buf, sizeof( line_buf ) ) == FAILED ) break ; } if( SVC_WAITS( svc ) ) /* Service forks, so close it */ Sclose(descriptor); } static void dgram_chargen( const struct server *serp ) { char buf[ BUFFER_SIZE ] ; char *p ; unsigned int len ; union xsockaddr lsin ; socklen_t sin_len = 0 ; int fd = SERVER_FD( serp ) ; unsigned int left = sizeof( buf ) ; ssize_t val; if ( SC_IPV4( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in ); else if ( SC_IPV6( SVC_CONF( SERVER_SERVICE( serp ) ) ) ) sin_len = sizeof( struct sockaddr_in6 ); val = recvfrom( fd, buf, (size_t)sizeof( buf ), 0, (struct sockaddr *)( &lsin ), &sin_len ); if ( val == (ssize_t)-1 ) return ; #if BUFFER_SIZE < LINE_LENGTH+2 bad_variable = 1 ; /* this will cause a compilation error */ #endif for ( p = buf ; left > 2 ; left -= len, p += len ) { len = min( LINE_LENGTH+2, left ) ; if ( generate_line( p, len ) == NULL ) break ; } (void) sendto( fd, buf, (size_t)(p-buf), 0, SA( &lsin ), sin_len ) ; } /* Handle a request for a tcpmux service. * It's helpful to remember here that we are now a child of the original * xinetd process. We were forked to keep the parent from blocking * when we try to read the service name off'n the socket connection. * Serp still points to an actual tcpmux 'server', or at least the * service pointer of serp is valid. */ static void tcpmux_handler( const struct server *serp ) { char svc_name[ BUFFER_SIZE ] ; int cc ; int descriptor = SERVER_FD( serp ) ; const struct service *svc = SERVER_SERVICE( serp ) ; unsigned u; struct service *sp = NULL; struct server server, *nserp; struct service_config *scp = NULL; close_all_svc_descriptors(); /* Read in the name of the service in the format "svc_name\r\n". * * XXX: should loop on partial reads (could probably use Sread() if * it wasn't thrown out of xinetd source code a few revisions back). */ do { cc = read( descriptor, svc_name, sizeof( svc_name ) ) ; } while (cc == -1 && errno == EINTR); if ( cc <= 0 ) { msg(LOG_ERR, "tcpmux_handler", "read failed"); exit(0); } if ( ( cc <= 2 ) || ( ( svc_name[cc - 1] != '\n' ) || ( svc_name[cc - 2] != '\r' ) ) ) { if ( debug.on ) msg(LOG_DEBUG, "tcpmux_handler", "Invalid service name format."); exit(0); } svc_name[cc - 2] = '\0'; /* Remove \r\n for compare */ if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Input (%d bytes) %s as service name.", cc, svc_name); } /* Search the services for the a match on name. */ for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( strcasecmp( svc_name, SC_NAME( SVC_CONF( sp ) ) ) == 0 ) { /* Found the pointer. Validate its type. */ scp = SVC_CONF( sp ); if ( ! SVC_IS_MUXCLIENT( sp ) && ! SVC_IS_MUXPLUSCLIENT( sp ) ) { if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Non-tcpmux service name: %s.", svc_name); } continue; } /* Send the accept string if we're a PLUS (+) client. */ if ( SVC_IS_MUXPLUSCLIENT( sp ) ) { if ( Swrite( descriptor, TCPMUX_ACK, sizeof( TCPMUX_ACK ) ) != sizeof( TCPMUX_ACK ) ) { msg(LOG_ERR, "tcpmux_handler", "Ack write failed for %s.", svc_name); exit(0); } } break; /* Time to get on with the service */ } continue; /* Keep looking */ } if ( u >= pset_count( SERVICES( ps ) ) ) { if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Service name %s not found.", svc_name); } /* If a service was not found, we should say so. */ if ( Swrite( descriptor, TCPMUX_NOT_FOUND, sizeof( TCPMUX_NOT_FOUND ) ) != sizeof ( TCPMUX_NOT_FOUND ) ) { msg(LOG_ERR, "tcpmux_handler", "Not found write failed for %s.", svc_name); exit(0); } /* Flush and exit, nothing to do */ Sflush( descriptor ); Sclose( descriptor ); exit(0); } if( SVC_WAITS( svc ) ) /* Service forks, so close it */ Sclose(descriptor); server.svr_sp = sp; server.svr_conn = SERVER_CONNECTION(serp); nserp = server_alloc(&server); if( SC_IS_INTERNAL( scp ) ) { SC_INTERNAL(scp, nserp); } else { child_process(nserp); } } xinetd-2.3.15.4/src/builtins.h000066400000000000000000000023261333055217000160320ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef BUILTINS_H #define BUILTINS_H /* * $Id$ */ #include "defs.h" #define FORK YES #define NO_FORK NO struct builtin { voidfunc b_handler ; /* builtin service handler */ boolean_e b_fork_server ; /* whether a server must be forked */ } ; typedef struct builtin builtin_s ; /* * All builtins are invoked with a struct server argument */ #define BUILTIN_HANDLER( bp ) ( (bp)->b_handler ) #define BUILTIN_INVOKE( bp, serp ) (*(bp)->b_handler)( serp ) #define BUILTIN_FORKS( bp ) ( (bp)->b_fork_server == YES ) struct builtin_service { const char *bs_name ; /* for identification purposes */ int bs_socket_type ; /* for identification purposes */ builtin_s bs_handle ; } ; const builtin_s *builtin_find(const char *service_name,int type); const builtin_s *builtin_lookup(const struct builtin_service services[],const char *service_name,int type); #endif /* BUILTIN_H */ xinetd-2.3.15.4/src/child.c000066400000000000000000000341211333055217000152550ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LABELED_NET #include #include #endif #include "str.h" #include "child.h" #include "sconf.h" #include "msg.h" #include "main.h" #include "xconfig.h" #include "ident.h" #include "sconst.h" #include "signals.h" #include "options.h" #include "redirect.h" /* Local declarations */ #ifdef LABELED_NET static int set_context_from_socket( const struct service_config *scp, int fd ); #endif /* * This function is running in the new process */ void exec_server( const struct server *serp ) { const struct service_config *scp = SVC_CONF( SERVER_SERVICE( serp ) ) ; struct rlimit rl ; int fd ; int descriptor = SERVER_FD( serp ) ; const char *server = SC_SERVER( scp ) ; const char *func = "exec_server" ; /* * The following code solves a problem with post-version-4.3 * Ultrix systems (the bug was reported, and a fix was provided by * doug@seas.smu.edu; a slightly modified version of this * fix is included here). * * If this is a 'nowait' service, we pass the service descriptor * to the server. Note that we have set the close-on-exec flag * on all service descriptors. It is unclear whether the dup2() * will create a descriptor with the close-on-exec flag set, * so we explicitly clear the flag (since we are doing this * after the fork, it does not affect the descriptor of the * parent process). */ if ( fcntl( descriptor, F_SETFD, 0 ) == -1 ) msg( LOG_WARNING, func, "fcntl( %d, clear close-on-exec ) failed: %m", descriptor ) ; if ( debug.on ) msg( LOG_DEBUG, func, "duping %d", descriptor ) ; /* * If server_loguser flag is on, then syslog may have opened fd 0, 1, or * 2. We call msg_suspend() now so that the logging system doesn't use * the dup'ed descriptor. */ msg_suspend() ; for ( fd = 0 ; fd <= MAX_PASS_FD ; fd++ ) { if ( dup2( descriptor, fd ) == -1 ) { msg_resume(); msg( LOG_ERR, func, "dup2( %d, %d ) failed: %m", descriptor, fd ) ; _exit( 1 ) ; } } #ifdef RLIMIT_NOFILE if ( SC_RLIM_FILES( scp )) { ps.ros.max_descriptors = SC_RLIM_FILES( scp ); } rl.rlim_max = rl.rlim_cur = ps.ros.max_descriptors ; (void) setrlimit( RLIMIT_NOFILE, &rl ) ; #endif #ifdef RLIMIT_AS if (SC_RLIM_AS (scp)) { rl.rlim_cur = SC_RLIM_AS( scp ); rl.rlim_max = SC_RLIM_AS( scp ); (void) setrlimit( RLIMIT_AS, &rl ); } #endif #ifdef RLIMIT_CPU if (SC_RLIM_CPU (scp)) { rl.rlim_cur = SC_RLIM_CPU( scp ); rl.rlim_max = SC_RLIM_CPU( scp ); (void) setrlimit( RLIMIT_CPU, &rl ); } #endif #ifdef RLIMIT_DATA if (SC_RLIM_DATA (scp)) { rl.rlim_cur = SC_RLIM_DATA( scp ); rl.rlim_max = SC_RLIM_DATA( scp ); (void) setrlimit( RLIMIT_DATA, &rl ); } #endif #ifdef RLIMIT_RSS if (SC_RLIM_RSS (scp)) { rl.rlim_cur = SC_RLIM_RSS( scp ); rl.rlim_max = SC_RLIM_RSS( scp ); (void) setrlimit( RLIMIT_RSS, &rl ); } #endif #ifdef RLIMIT_STACK if (SC_RLIM_STACK (scp)) { rl.rlim_cur = SC_RLIM_STACK( scp ); rl.rlim_max = SC_RLIM_STACK( scp ); (void) setrlimit( RLIMIT_STACK, &rl ); } #endif /* Set the context if the option was given */ #ifdef LABELED_NET if (SC_LABELED_NET(scp)) { if (set_context_from_socket( scp, descriptor ) < 0) { msg( LOG_ERR, func, "Changing process context failed for %s", SC_ID( scp )) ; _exit( 1 ) ; } } #endif (void) Sclose( descriptor ) ; #ifndef solaris no_control_tty() ; #endif (void) execve( server, SC_SERVER_ARGV( scp ), env_getvars( SC_ENV( scp )->env_handle ) ) ; /* * The exec failed. Log the error and exit. */ msg_resume() ; msg( LOG_ERR, func, "execv( %s ) failed: %m", server ) ; _exit( 0 ) ; } /* * Rename this process by changing the ps.ros.Argv vector * Try to put the name of the service in ps.ros.Argv[0], Argv[1] * until either the service name is exhausted or we run out * of ps.ros.Argv's. * The rest of ps.ros.Argv is cleared to spaces */ static void rename_process( const char *name ) { const char *from = name ; char *to = ps.ros.Argv[ 0 ] ; int tmp_index = 1 ; while ( *from != NUL ) { if ( *to != NUL ) *to++ = *from++ ; else if ( tmp_index < ps.ros.Argc ) to = ps.ros.Argv[ tmp_index++ ] ; else break ; } str_fill( to, ' ' ) ; while ( tmp_index < ps.ros.Argc ) str_fill( ps.ros.Argv[ tmp_index++ ], ' ' ) ; } static void set_credentials( const struct service_config *scp ) { const char *func = "set_credentials" ; if ( SC_SPECIFIED( scp, A_GROUP ) || SC_SPECIFIED( scp, A_USER ) ) { if ( ps.ros.is_superuser ) { gid_t gid = SC_GETGID( scp ) ; if ( setgid( gid ) == -1 ) { msg( LOG_ERR, func, "setgid failed: %m" ) ; _exit( 1 ) ; } #ifndef NO_INITGROUPS /* * Bug discovered by maf+@osu.edu (a bug fix was also provided; * a slightly modified version is included here): * initgroups was not being invoked to set the remaining * groups appropriately */ /* Solar Designer's groups fix */ if ( SC_SPECIFIED( scp, A_USER ) && SC_SPECIFIED( scp, A_GROUPS ) && SC_GROUPS(scp) == YES ) { struct passwd *pwd ; /* * Invoke getpwuid() to get the user's name. * * XXX: we should not need to invoke getpwuid(); we should * remember the user name in the configuration file. */ if ( ( pwd = getpwuid( SC_UID( scp ) ) ) == NULL ) { msg( LOG_ERR, func, "getpwuid( %d ) (service=%s) failed: %m", SC_UID( scp ), SC_ID( scp ) ) ; _exit( 1 ) ; } str_fill( pwd->pw_passwd, ' ' ); if ( initgroups( pwd->pw_name, pwd->pw_gid ) == -1 ) { msg( LOG_ERR, func, "initgroups( %s, %d ) failed: %m", pwd->pw_name, pwd->pw_gid ) ; _exit( 1 ) ; } } else { if ( setgroups( 0, NULL ) ) { msg( LOG_ERR, func, "setgroups( 0, NULL ) failed: %m" ) ; msg( LOG_ERR, func, "Your system may require that 'groups = yes' be defined for this service: %s", SC_NAME(scp)); _exit( 1 ) ; } } #endif /* ! NO_INITGROUPS */ } } if ( SC_SPECIFIED( scp, A_USER ) ) { if ( setuid( SC_UID( scp ) ) == -1 ) { msg( LOG_ERR, func, "setuid failed: %m" ) ; _exit( 1 ) ; } } if ( SC_SPECIFIED( scp, A_UMASK ) ) umask(SC_UMASK(scp)); } /* * This function is invoked in a forked process to run a server. * If the service is internal the appropriate function is invoked * otherwise the server program is exec'ed. * This function also logs the remote user id if appropriate */ void child_process( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; connection_s *cp = SERVER_CONNECTION( serp ) ; struct service_config *scp = SVC_CONF( sp ) ; const char *func = "child_process" ; signal_default_state(); if ((signals_pending[0] >= 0 && Sclose(signals_pending[0])) || (signals_pending[1] >= 0 && Sclose(signals_pending[1]))) { msg(LOG_ERR, func, "Failed to close the signal pipe: %m"); _exit(1); } signals_pending[0] = -1; signals_pending[1] = -1; #ifdef DEBUG_SERVER if ( debug.on ) { msg( LOG_DEBUG, func, "Process %d is sleeping", getpid() ) ; sleep( 10 ) ; } #endif if ( ! SC_IS_INTERCEPTED( scp ) ) { set_credentials( scp ) ; if ( SC_SPECIFIED( scp, A_NICE ) ) (void) nice( SC_NICE( scp ) ) ; } if ( svc_child_access_control(sp, cp) != OK ) exit(0); if ( SERVER_LOGUSER( serp ) ) { unsigned timeout ; idresult_e result ; /* * We use LOGUSER_SUCCESS_TIMEOUT unless the service requires * identification, in which case we use an infinite timeout */ timeout = SC_MUST_IDENTIFY( scp ) ? 0 : LOGUSER_SUCCESS_TIMEOUT ; result = log_remote_user( serp, timeout ) ; if ( result != IDR_OK && SC_MUST_IDENTIFY( scp ) ) { svc_logprint( sp, NOID_ENTRY, "%s %s", conn_addrstr( SERVER_CONNECTION( serp ) ), idresult_explain( result ) ) ; _exit( 0 ) ; } } /* this is where the server gets executed -bbraun */ if ( ! SC_IS_INTERNAL( scp ) ) { if( SC_REDIR_ADDR(scp) != NULL ) { redir_handler( serp ); } else { char buff[1024]; strx_sprint(buff, sizeof(buff)-1, "REMOTE_HOST=%s", conn_addrstr(cp)); if( env_addstr(SC_ENV(scp)->env_handle, buff) != ENV_OK ) { msg( LOG_ERR, func, "Error adding REMOTE_HOST variable for %s: %m", SC_NAME(scp) ); _exit( 1 ) ; } exec_server( serp ) ; } } else { char name[ 180 ] ; /* * We don't bother to disassociate from the controlling terminal * (we have a controlling terminal only if debug.on is TRUE) * * Also, for interceptor processes, we give them the name: * interceptor */ if ( SC_IS_INTERCEPTED( scp ) ) strx_print( INT_NULL, name, sizeof( name ) - 1, "%s %s interceptor", program_name, SC_ID( scp ) ) ; else { int namelen = sizeof( name ) - 1 ; /* leave space for the NUL */ char host[NI_MAXHOST]; size_t hostlen = NI_MAXHOST; socklen_t addrlen = 0; union xsockaddr *sinp = CONN_XADDRESS(SERVER_CONNECTION(serp)); int len; if( sinp == NULL ) exit(0); if( SC_IPV6(scp) ) addrlen = sizeof(struct sockaddr_in6); else if( SC_IPV4(scp) ) addrlen = sizeof(struct sockaddr_in); len = strx_nprint(name, namelen, "(%s service) %s", program_name, SC_ID( scp ) ) ; if( getnameinfo( SA(sinp), addrlen, host, hostlen, NULL, 0, 0) != 0 ) strcpy(host, "unknown"); if ( SC_IPV6(scp) && SC_ACCEPTS_CONNECTIONS( scp ) && !IN6_IS_ADDR_UNSPECIFIED(&sinp->sa_in6.sin6_addr) ) strx_print( INT_NULL, &name[ len ], namelen - len, " %s" , host ) ; if ( SC_IPV4(scp) && SC_ACCEPTS_CONNECTIONS( scp ) ) strx_print( INT_NULL, &name[ len ], namelen - len, " %s", host ) ; } rename_process( name ) ; SVC_INTERNAL( sp, serp ) ; } _exit( 0 ) ; /* NOTREACHED */ } /* * This function is invoked when a SIGCLD is received */ void child_exit(void) { const char *func = "child_exit" ; for ( ;; ) /* Find all children that exited */ { int status ; pid_t pid ; struct server *serp ; pid = waitpid( -1, &status, WNOHANG ) ; if ( debug.on ) msg( LOG_DEBUG, func, "waitpid returned = %d", pid ) ; if ( pid == -1 ) { if ( errno == EINTR ) continue ; else break ; } if ( pid == 0 ) break ; if ( ( serp = server_lookup( pid ) ) != NULL ) { SERVER_EXITSTATUS(serp) = status ; server_end( serp ) ; } else msg( LOG_NOTICE, func, "unknown child process %d %s", pid, PROC_STOPPED( status ) ? "stopped" : "died" ) ; } } #ifdef LABELED_NET static int set_context( security_context_t cntx ) { const char *func = "set_context" ; int retval = setexeccon(cntx); if (debug.on) { security_context_t current_exec_context; if ( getexeccon( ¤t_exec_context ) == 0 ) { msg( LOG_DEBUG, func, "current security exec context now: %s", current_exec_context ? current_exec_context : "unknown" ); freecon( current_exec_context ); } else msg( LOG_DEBUG, func, "Error calling getexeccon: %m" ); } return retval; } static int set_context_from_socket( const struct service_config *scp, int fd ) { security_context_t curr_context = NULL; security_context_t peer_context = NULL; security_context_t exec_context = NULL; context_t bcon = NULL; context_t pcon = NULL; security_context_t new_context = NULL; security_context_t new_exec_context = NULL; int retval = -1; const char *exepath = NULL; if (getcon(&curr_context) < 0) goto fail; if (getpeercon(fd, &peer_context) < 0) goto fail; exepath = SC_SERVER_ARGV( scp )[0]; if (getfilecon(exepath, &exec_context) < 0) goto fail; if (!(bcon = context_new(curr_context))) goto fail; if (!(pcon = context_new(peer_context))) goto fail; if (!context_range_get(pcon)) goto fail; if (context_range_set(bcon, context_range_get(pcon))) goto fail; if (!(new_context = context_str(bcon))) goto fail; if (security_compute_create(new_context, exec_context, string_to_security_class ("process"), &new_exec_context) < 0) goto fail; retval = set_context(new_exec_context); freecon(new_exec_context); fail: context_free(pcon); context_free(bcon); freecon(exec_context); freecon(peer_context); freecon(curr_context); return retval; } #endif xinetd-2.3.15.4/src/child.h000066400000000000000000000006541333055217000152660ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef _X_CHILD #define _X_CHILD #include "defs.h" #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void child_process(struct server *serp); void child_exit(void); #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void exec_server( const struct server *serp ); #endif xinetd-2.3.15.4/src/conf.c000066400000000000000000000121271333055217000151210ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include "sio.h" #include "conf.h" #include "msg.h" #include "main.h" void cnf_free( struct configuration *confp ) { unsigned u ; pset_h sconfs = confp->cnf_service_confs ; for ( u = 0 ; u < pset_count( sconfs ) ; u++ ) sc_free( SCP( pset_pointer( sconfs, u ) ) ) ; pset_destroy( sconfs ) ; if ( confp->cnf_defaults ) sc_free( confp->cnf_defaults ) ; CLEAR(*confp); } /* * Extract from 'confp' the service that matches 'scp' */ struct service_config *cnf_extract( struct configuration *confp, struct service_config *scp ) { pset_h stab = confp->cnf_service_confs ; unsigned u ; for ( u = 0 ; u < pset_count( stab ) ; u++ ) { struct service_config *iscp = SCP( pset_pointer( stab, u ) ) ; if ( ! EQ( SC_ID(scp), SC_ID(iscp) ) || sc_different_confs( scp, iscp ) ) continue ; pset_remove_index( stab, u ) ; return( iscp ) ; } return( NULL ) ; } void cnf_dump( struct configuration *confp, int fd ) { pset_h stab; unsigned u ; stab = confp->cnf_service_confs; sc_dump( confp->cnf_defaults, fd, 0, TRUE ) ; tabprint( fd, 0, "\n" ) ; for ( u = 0 ; u < pset_count( stab ) ; u++ ) { sc_dump( SCP( pset_pointer( stab, u ) ), fd, 0, FALSE ) ; Sputchar( fd, '\n' ) ; } } status_e cnf_init( struct configuration *confp, int *fdp, psi_h *iterp ) { int fd ; pset_h pset ; psi_h pset_iter ; struct service_config *scp ; const char *func = "cnf_init" ; /* * Open configuration file */ fd = open( ps.ros.config_file, O_RDONLY ) ; if ( fd == -1 ) { msg( LOG_ERR, func, "open( %s ) failed: %m", ps.ros.config_file ) ; return( FAILED ) ; } if ( ( pset = pset_create( 0, 0 ) ) == NULL ) { msg( LOG_CRIT, func, "can't create service table" ) ; (void) Sclose( fd ) ; return( FAILED ) ; } if ( ( scp = sc_alloc( CHAR_NULL ) ) == NULL ) { msg( LOG_ERR, func, "can't allocate defaults service" ) ; pset_destroy( pset ) ; (void) Sclose( fd ) ; return( FAILED ) ; } if ( ( pset_iter = psi_create( pset ) ) == NULL ) { msg( LOG_ERR, func, "can't create service table iterator" ) ; sc_free( scp ) ; pset_destroy( pset ) ; (void) Sclose( fd ) ; return( FAILED ) ; } *fdp = fd ; confp->cnf_service_confs = pset ; confp->cnf_defaults = scp ; *iterp = pset_iter ; return( OK ) ; } static void destroy_service( struct service *sp ) { svc_deactivate( sp ) ; svc_free( sp ) ; sp = NULL; } /* * Try to start all services. * Return the # of services started. */ unsigned cnf_start_services( struct configuration *confp ) { pset_h sconfs = confp->cnf_service_confs ; unsigned services_started = 0 ; unsigned u ; const char *func = "cnf_start_services" ; for ( u = 0 ; u < pset_count( sconfs ) ; u++ ) { struct service_config *scp = SCP( pset_pointer( sconfs, u ) ) ; struct service *sp ; if ( ( sp = svc_new( scp ) ) == NULL ) { sc_free( scp ) ; scp = NULL; continue ; } if ( svc_activate( sp ) == FAILED ) { msg( LOG_ERR, func, "Service %s failed to start and is deactivated.", SVC_ID( sp ) ) ; svc_free( sp ) ; scp = NULL; continue ; } /* * descriptors_free can be negative without a descriptor-allocating * system call failing because some of the descriptors we reserve * are transient */ if ( ps.rws.descriptors_free < 0 ) { msg( LOG_ERR, func, "Service %s disabled because of lack of file descriptors", SVC_ID( sp ) ) ; destroy_service( sp ) ; continue ; } /* * Activation successful; add service to service table */ if ( pset_add( SERVICES( ps ), sp ) == NULL ) { out_of_memory( func ) ; destroy_service( sp ) ; break ; } SVC_HOLD( sp ) ; services_started++ ; if ( debug.on ) msg( LOG_DEBUG, func, "Started service: %s", SVC_ID( sp ) ) ; } /* * All the configurations have been linked to their services * so we don't need to hold references to them in the pset. * We need to clear the pset so that the cnf_free will not free the memory. */ pset_clear( sconfs ) ; if ( debug.on ) #ifdef HAVE_POLL msg( LOG_DEBUG, func, "pfds_last = %d, services_started = %d", ps.rws.pfds_last, services_started ) ; #else msg( LOG_DEBUG, func, "mask_max = %d, services_started = %d", ps.rws.mask_max, services_started ) ; #endif return( services_started ) ; } xinetd-2.3.15.4/src/conf.h000066400000000000000000000015401333055217000151230ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef CONF_H #define CONF_H /* * $Id$ */ #include "pset.h" #include "sconf.h" struct configuration { pset_h cnf_service_confs ; struct service_config *cnf_defaults ; } ; #define CNF_DEFAULTS( confp ) (confp)->cnf_defaults #define CNF_SERVICE_CONFS( confp ) (confp)->cnf_service_confs void cnf_free(struct configuration *confp); struct service_config *cnf_extract(struct configuration *confp,struct service_config *scp); void cnf_dump(struct configuration *confp,int fd); status_e cnf_init(struct configuration *confp,int *fdp,psi_h *iterp); unsigned cnf_start_services(struct configuration *confp); #endif /* CONF_H */ xinetd-2.3.15.4/src/confparse.c000066400000000000000000000722531333055217000161620ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #ifndef NO_RPC #include #ifdef HAVE_RPCENT_H #include #endif #endif #include #include "str.h" #include "sio.h" #include "confparse.h" #include "msg.h" #include "xconfig.h" #include "parse.h" #include "special.h" #include "sconst.h" #include "env.h" #include "sconf.h" #include "sensor.h" #include "inet.h" #include "main.h" extern int inetd_compat; /* * Pset iterator used by functions in this file. * It lives only when get_configuration is called (i.e. it is created and * destroyed each time). This is because the pset it is iterating on * changes. */ static psi_h iter ; static status_e fix_server_argv( struct service_config *scp ) { char *server_name ; const char *func = "fix_server_argv" ; if( SC_SERVER(scp) == NULL ) { msg( LOG_ERR, func, "Must specify a server in %s", SC_NAME(scp)); return( FAILED ); } if( SC_NAMEINARGS( scp ) ) { if( !SC_SPECIFIED(scp, A_SERVER_ARGS ) ){ msg( LOG_ERR, func, "Must specify server args if using NAMEINARGS flag"); return( FAILED ); } return ( OK ); } /* * Check if the user specified any server arguments. * If not, then the server_argv has not been allocated yet, * so malloc it (size 2) * Put in argv[ 0 ] the last component of the server pathname */ if ( ! SC_SPECIFIED( scp, A_SERVER_ARGS ) ) { SC_SERVER_ARGV(scp) = (char **) malloc( 2 * sizeof( char * ) ) ; if ( SC_SERVER_ARGV(scp) == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } SC_SERVER_ARGV(scp)[ 0 ] = NULL ; SC_SERVER_ARGV(scp)[ 1 ] = NULL ; SC_PRESENT( scp, A_SERVER_ARGS ) ; } /* * Determine server name */ server_name = strrchr( SC_SERVER(scp), '/' ) ; if ( server_name == NULL ) server_name = SC_SERVER(scp) ; else server_name++ ; /* skip the '/' */ /* * Place it in argv[ 0 ] */ SC_SERVER_ARGV(scp)[ 0 ] = new_string( server_name ) ; if ( SC_SERVER_ARGV(scp)[ 0 ] == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } return( OK ) ; } #define USE_DEFAULT( scp, def, attr_id ) \ ( ! SC_SPECIFIED( scp, attr_id ) && SC_SPECIFIED( def, attr_id ) ) /* * Fill the service configuration with attributes that were not * explicitly specified. These can be: * 1) implied attributes (like the server name in argv[0]) * 2) attributes from 'defaults' so that we won't need to check * 'defaults' anymore. * 3) default values (like the service instance limit) */ static status_e service_fill( struct service_config *scp, struct service_config *def ) { const char *func = "service_fill" ; /* Note: if the service was specified, it won't be honored. */ if( (SC_REDIR_ADDR(scp) != NULL) ) { if ( SC_SPECIFIED( scp, A_SERVER ) && SC_SERVER(scp)) free(SC_SERVER(scp)); SC_SERVER(scp) = new_string( "/bin/true" ); SC_SPECIFY(scp, A_SERVER); } if ( ! SC_IS_INTERNAL( scp ) && fix_server_argv( scp ) == FAILED ) return( FAILED ) ; /* * FIXME: Should all these set SPECIFY or PRESENT ? * PRESENT means that either a default or specified value. * SPECIFIED means that the user specified a value. * PRESENT makes more sense for default values. -SG */ if ( ! SC_SPECIFIED( scp, A_INSTANCES ) ) { SC_INSTANCES(scp) = SC_SPECIFIED( def, A_INSTANCES ) ? SC_INSTANCES(def) : DEFAULT_INSTANCE_LIMIT ; SC_PRESENT( scp, A_INSTANCES ) ; } if ( (! SC_SPECIFIED( scp, A_UMASK )) && SC_SPECIFIED( def, A_UMASK ) ) { SC_UMASK(scp) = SC_UMASK(def); SC_SPECIFY( scp, A_UMASK ); } if ( ! SC_SPECIFIED( scp, A_PER_SOURCE ) ) { SC_PER_SOURCE(scp) = SC_SPECIFIED( def, A_PER_SOURCE ) ? SC_PER_SOURCE(def) : DEFAULT_INSTANCE_LIMIT ; SC_SPECIFY( scp, A_PER_SOURCE ) ; } if ( ! SC_SPECIFIED( scp, A_GROUPS ) ) { SC_GROUPS(scp) = SC_SPECIFIED( def, A_GROUPS ) ? SC_GROUPS(def) : NO; SC_SPECIFY( scp, A_GROUPS ); } if ( ! SC_SPECIFIED( scp, A_CPS ) ) { SC_TIME_CONN_MAX(scp) = SC_SPECIFIED( def, A_CPS ) ? SC_TIME_CONN_MAX(def) : DEFAULT_LOOP_RATE; SC_TIME_WAIT(scp) = SC_SPECIFIED( def, A_CPS ) ? SC_TIME_WAIT(def) : DEFAULT_LOOP_TIME; SC_TIME_REENABLE(scp) = 0; } #ifdef HAVE_LOADAVG if ( ! SC_SPECIFIED( scp, A_MAX_LOAD ) ) { SC_MAX_LOAD(scp) = SC_SPECIFIED( def, A_MAX_LOAD ) ? SC_MAX_LOAD(def) : 0; SC_SPECIFY( scp, A_MAX_LOAD ) ; } #endif /* * we need to check a few things. A_BIND can be specified & sc_bind_addr * is NULL. This means the address couldn't be determined in bind_parser * and it was stored into sc_orig_bind_addr. We unset the attribute * so that its processed correctly. */ if (SC_SPECIFIED( scp, A_BIND ) && SC_BIND_ADDR(scp) == NULL) M_CLEAR( scp->sc_specified_attributes, A_BIND ) ; if ( (! SC_SPECIFIED( scp, A_BIND )) && (SC_ORIG_BIND_ADDR(scp) == 0) ) { if ( SC_SPECIFIED( def, A_BIND ) ) { SC_BIND_ADDR(scp) = (union xsockaddr *)malloc(sizeof(union xsockaddr)); if( SC_BIND_ADDR(scp) == NULL ) { msg(LOG_ERR, func, "can't allocate space for bind addr"); return( FAILED ); } memcpy(SC_BIND_ADDR(scp), SC_BIND_ADDR(def), sizeof(union xsockaddr)); SC_SPECIFY( scp, A_BIND ) ; } else if ( SC_ORIG_BIND_ADDR(def) ) SC_ORIG_BIND_ADDR(scp) = new_string( SC_ORIG_BIND_ADDR(def) ); } if ( ! SC_SPECIFIED( scp, A_V6ONLY ) ) { SC_V6ONLY(scp) = SC_SPECIFIED( def, A_V6ONLY ) ? SC_V6ONLY(def) : NO; } if ( ! SC_SPECIFIED( scp, A_DENY_TIME ) ) { SC_DENY_TIME(scp) = SC_SPECIFIED( def, A_DENY_TIME ) ? SC_DENY_TIME(def) : 0 ; SC_SPECIFY( scp, A_DENY_TIME ) ; } if ( (!SC_IPV4( scp )) && (!SC_IPV6( scp )) ) { /* * If bind is specified, check the address and see what family is * available. If not, then use default. */ if ( SC_SPECIFIED( scp, A_BIND ) && !SC_ORIG_BIND_ADDR(scp) ) { if ( SAIN(SC_BIND_ADDR(scp))->sin_family == AF_INET ) M_SET(SC_XFLAGS(scp), SF_IPV4); else M_SET(SC_XFLAGS(scp), SF_IPV6); } else M_SET(SC_XFLAGS(scp), SF_IPV6); /*try bind IPv6 by default*/ } if (SC_ORIG_BIND_ADDR(scp)) { /* * If we are here, we have a dual stack machine with multiple * entries for a domain name. We can finally use the flags for * a hint to see which one to use. */ struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; if (SC_IPV6(scp)) hints.ai_family = AF_INET6; else hints.ai_family = AF_INET; if( getaddrinfo(SC_ORIG_BIND_ADDR(scp), NULL, &hints, &res) < 0 ) { msg(LOG_ERR, func, "bad address given for: %s", SC_NAME(scp)); return( FAILED ); } if( (res == NULL) || (res->ai_addr == NULL) ) { msg(LOG_ERR, func, "no addresses returned for: %s", SC_NAME(scp)); return( FAILED ); } if( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6) ) { SC_BIND_ADDR(scp) = (union xsockaddr *) malloc(sizeof(union xsockaddr)); if( SC_BIND_ADDR(scp) == NULL ) { msg(LOG_ERR, func, "can't allocate space for bind addr of: %s", SC_NAME(scp)); return( FAILED ); } memset(SC_BIND_ADDR(scp), 0, sizeof(union xsockaddr)); memcpy(SC_BIND_ADDR(scp), res->ai_addr, res->ai_addrlen); free(SC_ORIG_BIND_ADDR(scp)); SC_ORIG_BIND_ADDR(scp) = 0; SC_SPECIFY( scp, A_BIND ); } freeaddrinfo(res); } /* This should be removed if sock_stream is ever something other than TCP, * or sock_dgram is ever something other than UDP. */ if ( (! SC_SPECIFIED( scp, A_PROTOCOL )) && ( SC_SPECIFIED( scp, A_SOCKET_TYPE ) ) ) { struct protoent *pep ; if( SC_SOCKET_TYPE(scp) == SOCK_STREAM ) { if( (pep = getprotobyname( "tcp" )) != NULL ) { SC_PROTONAME(scp) = new_string ( "tcp" ); if( SC_PROTONAME(scp) == NULL ) return( FAILED ); SC_PROTOVAL(scp) = pep->p_proto ; SC_SPECIFY(scp, A_PROTOCOL); } } if( SC_SOCKET_TYPE(scp) == SOCK_DGRAM ) { if( (pep = getprotobyname( "udp" )) != NULL ) { SC_PROTONAME(scp) = new_string ( "udp" ); if( SC_PROTONAME(scp) == NULL ) return( FAILED ); SC_PROTOVAL(scp) = pep->p_proto ; SC_SPECIFY(scp, A_PROTOCOL); } } } if ( ( SC_SPECIFIED( scp, A_PROTOCOL )) && (! SC_SPECIFIED( scp, A_SOCKET_TYPE ) ) ) { if( (SC_PROTONAME(scp) != NULL) && EQ("tcp", SC_PROTONAME(scp)) ) { SC_SOCKET_TYPE(scp) = SOCK_STREAM; SC_SPECIFY(scp, A_SOCKET_TYPE); } if( (SC_PROTONAME(scp) != NULL) && EQ("udp", SC_PROTONAME(scp)) ) { SC_SOCKET_TYPE(scp) = SOCK_DGRAM; SC_SPECIFY(scp, A_SOCKET_TYPE); } } /* * Next assign a port based on service name if not specified. Based * on the code immediately before this, if either a socket_type or a * protocol is specied, the other gets set appropriately. We will only * use protocol for this code. */ if (! SC_SPECIFIED( scp, A_PORT ) && ! SC_IS_MUXCLIENT( scp ) && ! SC_IS_RPC( scp )) { if ( SC_IS_UNLISTED( scp ) ) { msg(LOG_ERR, func, "Unlisted service: %s must have a port entry", SC_NAME(scp)); return(FAILED); } if ( SC_SPECIFIED( scp, A_PROTOCOL ) ) { /* * Look up the service based on the protocol and service name. * If not found, don't worry. Message will be emitted in * check_entry(). */ struct servent *sep = getservbyname( SC_NAME(scp), SC_PROTONAME(scp) ) ; if ( sep != NULL ) { /* s_port is in network-byte-order */ SC_PORT(scp) = ntohs(sep->s_port); SC_SPECIFY(scp, A_PORT); } else { msg(LOG_ERR, func, "Port not specified and can't find service: %s with getservbyname", SC_NAME(scp)); return(FAILED); } } else { msg(LOG_ERR, func, "Port not specified for service: %s and no protocol given", SC_NAME(scp)); return(FAILED); } } if ( USE_DEFAULT( scp, def, A_LOG_ON_SUCCESS ) ) { SC_LOG_ON_SUCCESS(scp) = SC_LOG_ON_SUCCESS(def) ; SC_SPECIFY( scp, A_LOG_ON_SUCCESS ) ; } if ( USE_DEFAULT( scp, def, A_LOG_ON_FAILURE ) ) { SC_LOG_ON_FAILURE(scp) = SC_LOG_ON_FAILURE(def) ; SC_SPECIFY( scp, A_LOG_ON_FAILURE ) ; } if ( USE_DEFAULT( scp, def, A_LOG_TYPE ) ) { struct log *dlp = SC_LOG( def ) ; struct log *slp = SC_LOG( scp ) ; switch ( LOG_GET_TYPE( dlp ) ) { case L_NONE: LOG_SET_TYPE( slp, L_NONE ) ; break ; case L_SYSLOG: *slp = *dlp ; break ; case L_FILE: LOG_SET_TYPE( slp, L_COMMON_FILE ) ; break ; default: msg( LOG_ERR, func, "bad log type: %d", (int) LOG_GET_TYPE( dlp ) ) ; return( FAILED ) ; } SC_SPECIFY( scp, A_LOG_TYPE ) ; } if ( setup_environ( scp, def ) == FAILED ) return( FAILED ) ; return( OK ) ; } static void remove_disabled_services( struct configuration *confp ) { pset_h disabled_services ; pset_h enabled_services ; struct service_config *scp ; struct service_config *defaults = confp->cnf_defaults ; if( SC_SPECIFIED( defaults, A_ENABLED ) ) { enabled_services = SC_ENABLED(defaults) ; /* Mark all the services disabled */ for ( scp = SCP( psi_start( iter ) ) ; scp ; scp = SCP( psi_next(iter) ) ) SC_DISABLE( scp ); /* Enable the selected services */ for ( scp = SCP( psi_start( iter ) ) ; scp ; scp = SCP( psi_next(iter) ) ) { register char *sid = SC_ID( scp ) ; register unsigned u ; for ( u = 0 ; u < pset_count( enabled_services ) ; u++ ) { if ( EQ( sid, (char *) pset_pointer( enabled_services, u ) ) ) { SC_ENABLE( scp ); break; } } } } /* Remove any services that are left marked disabled */ for ( scp = SCP( psi_start( iter ) ) ; scp ; scp = SCP( psi_next(iter)) ){ if( SC_IS_DISABLED( scp ) ) { msg(LOG_DEBUG, "remove_disabled_services", "removing %s", SC_NAME(scp)); SC_DISABLE( scp ); sc_free(scp); psi_remove(iter); } } if ( ! SC_SPECIFIED( defaults, A_DISABLED ) ) return ; disabled_services = SC_DISABLED(defaults) ; for ( scp = SCP( psi_start( iter ) ) ; scp ; scp = SCP( psi_next( iter ) ) ) { register char *sid = SC_ID( scp ) ; register unsigned u ; for ( u = 0 ; u < pset_count( disabled_services ) ; u++ ) if ( EQ( sid, (char *) pset_pointer( disabled_services, u ) ) ) { sc_free( scp ) ; psi_remove( iter ) ; break ; } } } /* * Check if all required attributes have been specified */ static status_e service_attr_check( struct service_config *scp ) { mask_t necessary_and_specified ; mask_t necessary_and_missing ; mask_t must_specify = NECESSARY_ATTRS ; /* socket_type & wait */ unsigned int attr_id ; const char *attr_name ; const char *func = "service_attr_check" ; /* * Determine what attributes must be specified */ if ( ! SC_IS_INTERNAL( scp ) ) { /* user & server */ M_OR( must_specify, must_specify, NECESSARY_ATTRS_EXTERNAL ) ; if ( SC_IS_UNLISTED( scp ) ) { if ( ! SC_IS_MUXCLIENT( scp ) ) /* protocol, & port */ { M_OR( must_specify, must_specify, NECESSARY_ATTRS_UNLISTED ) ; } else /* Don't need port for TCPMUX CLIENT */ { M_OR( must_specify, must_specify, NECESSARY_ATTRS_UNLISTED_MUX ) ; } } } if ( SC_IS_RPC( scp ) ) { M_CLEAR( must_specify, A_PORT ); /* port is already known for RPC */ /* protocol & rpc_version */ M_OR( must_specify, must_specify, NECESSARY_ATTRS_RPC ) ; if ( SC_IS_UNLISTED( scp ) ) /* rpc_number */ M_OR( must_specify, must_specify, NECESSARY_ATTRS_RPC_UNLISTED ) ; } else { if ( SC_SPECIFIED( scp, A_REDIR ) ) M_CLEAR( must_specify, A_SERVER ); /* server isn't used */ } if( SC_IPV4( scp ) && SC_IPV6( scp ) ) { msg( LOG_INFO, func, "Service %s will use IPv6 or fallback to IPv4", SC_NAME(scp)); // we only need ipv6 defined and fallback to ipv4 if needed // so clean it up for now M_CLEAR(SC_XFLAGS(scp), SF_IPV4); } /* * Check if all necessary attributes have been specified * * NOTE: None of the necessary attributes can belong to "defaults" * This is why we use the sc_attributes_specified mask instead * of the sc_attributes_present mask. */ M_AND( necessary_and_specified, scp->sc_specified_attributes, must_specify ) ; M_XOR( necessary_and_missing, necessary_and_specified, must_specify ) ; if ( M_ARE_ALL_CLEAR( necessary_and_missing) ) return OK ; /* * Print names of missing attributes */ for ( attr_id = 0 ; attr_id < SERVICE_ATTRIBUTES ; attr_id++ ) if ( M_IS_SET( necessary_and_missing, attr_id ) && ( attr_name = attr_name_lookup( attr_id ) ) != NULL ) { msg( LOG_ERR, func, "Service %s missing attribute %s - DISABLING", SC_ID(scp), attr_name ) ; } return FAILED ; } /* * Perform validity checks on the whole entry. At this point, all * attributes have been read and we can do an integrated check that * all parameters make sense. * * Also does the following: * 1. If this is an internal service, it finds the function that * implements it * 2. For RPC services, it finds the program number * 3. For non-RPC services, it finds the port number. */ static status_e check_entry( struct service_config *scp, const struct configuration *confp ) { const char *func = "check_entry" ; unsigned int u; const pset_h sconfs = CNF_SERVICE_CONFS( confp ) ; /* * Make sure the service id is unique */ for ( u = 0 ; u < pset_count( sconfs ) ; u++ ) { int diff = 1; const struct service_config *tmp_scp = SCP( pset_pointer( sconfs, u ) ); if (tmp_scp == scp) break; /* Don't check ourselves, or anything after us */ if ( EQ( SC_ID(tmp_scp), SC_ID(scp) ) ) { diff = 0; } if( SC_BIND_ADDR(tmp_scp) == NULL) continue; /* problem entry, skip it */ if ( (SC_PORT(scp) != SC_PORT(tmp_scp)) || (SC_PROTOVAL(scp) != SC_PROTOVAL(tmp_scp)) ) continue; /* if port or protocol are different, its OK */ if (SC_BIND_ADDR(scp) != NULL) { if (SC_BIND_ADDR(scp)->sa.sa_family != SC_BIND_ADDR(tmp_scp)->sa.sa_family) continue; if (SC_BIND_ADDR(scp)->sa.sa_family == AF_INET) { if (memcmp(&SC_BIND_ADDR(scp)->sa_in.sin_addr, &SC_BIND_ADDR(tmp_scp)->sa_in.sin_addr, sizeof(struct in_addr) ) ) continue; } else /* We assume that all bad address families are weeded out */ { if (memcmp(&SC_BIND_ADDR(scp)->sa_in6.sin6_addr, &SC_BIND_ADDR(tmp_scp)->sa_in6.sin6_addr, sizeof(struct in6_addr) ) ) continue; } } if( SC_IS_DISABLED( tmp_scp ) || SC_IS_DISABLED(scp) ) { /* * Allow multiple configs, as long as all but one are * disabled. */ continue; } if ( SC_IS_RPC( scp ) && SC_IS_RPC ( tmp_scp ) ) { const struct rpc_data *rdp1 = SC_RPCDATA( scp ) ; const struct rpc_data *rdp2 = SC_RPCDATA( tmp_scp ) ; if ( rdp1->rd_program_number != rdp2->rd_program_number ) continue; if ( rdp1->rd_min_version > rdp2->rd_max_version || rdp1->rd_max_version < rdp2->rd_min_version ) continue; } if (diff) msg( LOG_ERR, func, "service: %s id: %s is unique but its identical to " "service: %s id: %s - DISABLING", SC_NAME(scp), SC_ID(scp), SC_NAME(tmp_scp), SC_ID(tmp_scp) ) ; else msg( LOG_ERR, func, "service: %s id: %s not unique or is a duplicate - DISABLING", SC_NAME(scp), SC_ID(scp) ) ; return FAILED ; } /* for */ /* * Currently, we cannot intercept: * 1) internal services * 2) multi-threaded services * We clear the INTERCEPT flag without disabling the service. */ if ( SC_IS_INTERCEPTED( scp ) ) { if ( SC_IS_INTERNAL( scp ) ) { msg( LOG_ERR, func, "Internal services cannot be intercepted: %s ", SC_ID(scp) ) ; M_CLEAR( SC_XFLAGS(scp), SF_INTERCEPT ) ; } if ( SC_WAIT(scp) == NO ) { msg( LOG_ERR, func, "Multi-threaded services cannot be intercepted: %s", SC_ID(scp) ) ; M_CLEAR( SC_XFLAGS(scp), SF_INTERCEPT ) ; } } /* Steer the lost sheep home */ if ( SC_SENSOR( scp ) ) M_SET( SC_TYPE(scp), ST_INTERNAL ); if ( SC_IS_INTERNAL( scp ) ) { /* If SENSOR flagged redirect to internal builtin function. */ if ( SC_SENSOR( scp ) ) { init_sensor(); SC_BUILTIN(scp) = builtin_find( "sensor", SC_SOCKET_TYPE(scp) ); } else SC_BUILTIN(scp) = builtin_find( SC_NAME(scp), SC_SOCKET_TYPE(scp) ); if (SC_BUILTIN(scp) == NULL ) return( FAILED ) ; } #ifdef LABELED_NET if (SC_LABELED_NET(scp)) { if ( SC_IS_INTERNAL( scp ) ) { msg( LOG_ERR, func, "Internal services cannot support labeled networking: %s", SC_ID(scp) ) ; return( FAILED ) ; } if ( SC_SOCKET_TYPE(scp) != SOCK_STREAM ) { msg( LOG_ERR, func, "Non-stream socket types cannot support labeled networking: %s", SC_ID(scp) ) ; return( FAILED ) ; } if ( SC_WAITS( scp ) ) { msg( LOG_ERR, func, "Tcp wait services cannot support labeled networking: %s", SC_ID(scp) ) ; return( FAILED ) ; } if ( SC_REDIR_ADDR( scp ) != NULL) { msg( LOG_ERR, func, "Redirected services cannot support labeled networking: %s", SC_ID(scp) ) ; return( FAILED ) ; } } #endif if ( SC_IS_MUXCLIENT( scp ) ) { if ( !SC_IS_UNLISTED( scp ) ) { msg(LOG_ERR, func, "Service: %s (tcpmux) should have UNLISTED in type.", SC_NAME(scp)); return( FAILED ); } if (!EQ("tcp", SC_PROTONAME(scp))) { msg(LOG_ERR, func, "Service: %s (tcpmux) should have tcp in protocol.", SC_NAME(scp)); return( FAILED ); } } #ifndef NO_RPC if ( SC_IS_RPC( scp ) && !SC_IS_UNLISTED( scp ) ) { struct rpcent *rep = (struct rpcent *)getrpcbyname( SC_NAME(scp) ) ; if ( rep == NULL ) { msg( LOG_ERR, func, "unknown RPC service: %s", SC_NAME(scp) ) ; return( FAILED ) ; } SC_RPCDATA( scp )->rd_program_number = rep->r_number ; } else #endif { if ( !SC_IS_UNLISTED( scp ) ) { uint16_t service_port ; struct servent *sep ; /* * Check if a protocol was specified. Based on the code in * service_fill, if either socket_type or protocol is specified, * the other one is filled in. Protocol should therefore always * be filled in unless they made a mistake. Then verify it is the * proper protocol for the given service. * We don't need to check MUXCLIENTs - they aren't in /etc/services. */ if ( SC_SPECIFIED( scp, A_PROTOCOL ) ) { sep = getservbyname( SC_NAME(scp), SC_PROTONAME(scp) ) ; if ( sep == NULL ) { msg( LOG_ERR, func, "service/protocol combination not in /etc/services: %s/%s", SC_NAME(scp), SC_PROTONAME(scp) ) ; return( FAILED ) ; } } else { msg( LOG_ERR, func, "A protocol or a socket_type must be specified for service: %s.", SC_NAME(scp) ) ; return( FAILED ) ; } /* s_port is in network-byte-order */ service_port = ntohs(sep->s_port); /* * If a port was specified, it must be the right one */ if ( SC_SPECIFIED( scp, A_PORT ) && SC_PORT(scp) != service_port ) { msg( LOG_ERR, func, "Service %s expects port %d, not %d", SC_NAME(scp), service_port, SC_PORT(scp) ) ; return( FAILED ) ; } } /* if not unlisted */ } if ( SC_SPECIFIED( scp, A_REDIR )) { if ( SC_SOCKET_TYPE( scp ) != SOCK_STREAM ) { msg( LOG_ERR, func, "Only tcp sockets are supported for redirected service %s", SC_NAME(scp)); return FAILED; } if ( SC_WAITS( scp ) ) { msg( LOG_ERR, func, "Redirected service %s must not wait", SC_NAME(scp)); return FAILED; } if ( SC_NAMEINARGS( scp ) ) { msg( LOG_ERR, func, "Redirected service %s should not have NAMEINARGS flag set", SC_NAME(scp)); return FAILED; } } else /* Not a redirected service */ { if( M_IS_SET( SC_LOG_ON_SUCCESS(scp), LO_TRAFFIC ) ) { msg( LOG_ERR, func, "Service %s should not have TRAFFIC flag set since its" " not redirected", SC_NAME(scp)); return FAILED; } } if ( SC_NAMEINARGS(scp) ) { if (SC_IS_INTERNAL( scp ) ) { msg( LOG_ERR, func, "Service %s is INTERNAL and has NAMEINARGS flag set", SC_NAME(scp) ); return FAILED; } else if (!SC_SPECIFIED( scp, A_SERVER_ARGS) ) { msg( LOG_ERR, func, "Service %s has NAMEINARGS flag set and no server_args", SC_NAME(scp) ); return FAILED; } } if ( SC_SOCKET_TYPE(scp) == SOCK_DGRAM && !SC_WAITS(scp) ) { msg( LOG_ERR, func, "Service %s has socket_type dgram, but does not wait", SC_NAME(scp) ); return FAILED; } if ( service_attr_check( scp ) == FAILED ) return( FAILED ) ; return( OK ) ; } /* * Get a configuration from the specified file. */ static status_e get_conf( int fd, struct configuration *confp ) { parse_conf_file( fd, confp, ps.ros.config_file ) ; parse_end() ; return( OK ) ; } #define CHECK_AND_CLEAR( scp, mask, mask_name ) \ if ( M_IS_SET( mask, LO_USERID ) ) \ { \ msg( LOG_WARNING, func, \ "%s service: clearing USERID option from %s", SC_ID(scp), mask_name ) ; \ M_CLEAR( mask, LO_USERID ) ; \ } /* * Get a configuration by reading the configuration file. */ status_e cnf_get( struct configuration *confp ) { int config_fd ; struct service_config *scp ; const char *func = "cnf_get" ; if ( cnf_init( confp, &config_fd, &iter ) == FAILED ) return( FAILED ) ; else if ( get_conf( config_fd, confp ) == FAILED ) { Sclose( config_fd ) ; cnf_free( confp ) ; psi_destroy( iter ) ; return( FAILED ) ; } /* get_conf eventually calls Srdline, try Sclosing to unmmap memory. */ Sclose( config_fd ); if( inetd_compat ) { current_file = "/etc/inetd.conf"; config_fd = open(current_file, O_RDONLY); if( config_fd >= 0 ) { parse_inet_conf_file( config_fd, confp ); parse_end() ; /* * parse_inet_conf eventually calls Srdline, try Sclosing to * unmmap memory. */ Sclose(config_fd); } } remove_disabled_services( confp ) ; for ( scp = SCP( psi_start( iter ) ) ; scp ; scp = SCP( psi_next( iter ) ) ) { /* * Fill the service configuration from the defaults. * We do this so that we don't have to look at the defaults any more. */ if ( service_fill( scp, confp->cnf_defaults ) == FAILED ) { sc_free( scp ) ; psi_remove( iter ) ; continue ; } if ( check_entry( scp, confp ) == FAILED ) { sc_free( scp ) ; psi_remove( iter ) ; continue ; } /* * If the INTERCEPT flag is set, change this service to an internal * service using the special INTERCEPT builtin. */ if ( SC_IS_INTERCEPTED( scp ) ) { const builtin_s *bp ; bp = spec_find( INTERCEPT_SERVICE_NAME, SC_SOCKET_TYPE(scp) ) ; if ( bp == NULL ) { msg( LOG_ERR, func, "removing service %s", SC_ID( scp ) ) ; sc_free( scp ) ; psi_remove( iter ) ; continue ; } SC_BUILTIN(scp) = bp ; M_SET( SC_TYPE(scp), ST_INTERNAL ) ; } /* * Clear the USERID flag for the identity service because * it may lead to loops (for example, remote xinetd issues request, * local xinetd issues request to remote xinetd etc.) * We identify the identity service by its (protocol,port) combination. */ if ( SC_PORT(scp) == IDENTITY_SERVICE_PORT && SC_PROTOVAL(scp) == IPPROTO_TCP ) { CHECK_AND_CLEAR( scp, SC_LOG_ON_SUCCESS(scp), "log_on_success" ) ; CHECK_AND_CLEAR( scp, SC_LOG_ON_FAILURE(scp), "log_on_failure" ) ; } } psi_destroy( iter ) ; if ( debug.on && debug.fd != -1 ) cnf_dump( confp, debug.fd ) ; endservent() ; endprotoent() ; #ifndef NO_RPC endrpcent() ; #endif return( OK ) ; } xinetd-2.3.15.4/src/confparse.h000066400000000000000000000002331333055217000161540ustar00rootroot00000000000000#ifndef CONFPARSE_H #define CONFPARSE_H #include "defs.h" #include "conf.h" #include "xconfig.h" status_e cnf_get(struct configuration *confp); #endif xinetd-2.3.15.4/src/connection.c000066400000000000000000000144221333055217000163330ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include "sio.h" #include "connection.h" #include "sconf.h" #include "msg.h" #include "main.h" #include "state.h" #include "special.h" #include "access.h" #define NEW_CONN() NEW( connection_s ) #define FREE_CONN( cop ) FREE( cop ) /* * Get a new connection request and initialize 'cp' appropriately */ static status_e get_connection( struct service *sp, connection_s *cp ) { struct service_config *scp = SVC_CONF( sp ); socklen_t sin_len; const char *func = "get_connection" ; int on = 1; if( SC_IPV4(scp) ) sin_len = sizeof(struct sockaddr_in); if( SC_IPV6(scp) ) sin_len = sizeof(struct sockaddr_in6); if ( SVC_SOCKET_TYPE( sp ) == SOCK_STREAM ) { /* If it's a TCP socket, and we're set to wait, the accept is * done by the child process. Don't set NEW_DESCRIPTOR, since * there isn't one. The descriptor will be/was removed from * the descriptor set in svc_suspend and re-enabled in svc_resume. */ if( SC_WAITS( scp ) ) { cp->co_descriptor = SVC_FD( sp ); } else { cp->co_descriptor = accept( SVC_FD( sp ), &(cp->co_remote_address.sa), &sin_len ) ; if ( cp->co_descriptor == -1 ) { if ((errno == EMFILE) || (errno == ENFILE)) cps_service_stop(sp, "no available descriptors"); else msg( LOG_ERR, func, "service %s, accept: %m", SVC_ID( sp ) ) ; return( FAILED ) ; } M_SET( cp->co_flags, COF_NEW_DESCRIPTOR ) ; M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ; } if( SC_NODELAY( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) ) if( setsockopt(SVC_FD(sp), IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof( on ) ) < 0 ) msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp)); if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) ) { if( setsockopt(SVC_FD(sp), SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof( on ) ) < 0 ) msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp)); } if( SC_IPV6(scp) && !(SC_V6ONLY( scp )) && (IN6_IS_ADDR_V4MAPPED(&cp->co_remote_address.sa_in6.sin6_addr) || IN6_IS_ADDR_V4COMPAT(&cp->co_remote_address.sa_in6.sin6_addr)) ) { int af = AF_INET; if( setsockopt(cp->co_descriptor, IPPROTO_IPV6, IPV6_ADDRFORM, &af, sizeof( af ) ) ) { if( debug.on ) msg( LOG_WARNING, func, "service %s, IPV6_ADDRFORM setsockopt() failed: %m", SVC_ID( sp) ); } } } else { if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) { char t_ch ; ssize_t val; /* * This trick is done to get the remote address. * select(2) guaranteed that we won't block on the recvfrom */ val = recvfrom( SVC_FD( sp ), &t_ch, 1, MSG_PEEK, &cp->co_remote_address.sa, &sin_len ); if ( val == (ssize_t)-1 ) { msg( LOG_ERR, func, "service %s, recvfrom: %m", SVC_ID( sp ) ) ; return( FAILED ) ; } M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ; } cp->co_descriptor = SVC_FD( sp ) ; } return( OK ) ; } /* * Get a connection for the specified service and return a pointer * to a new connection_s */ connection_s *conn_new( struct service *sp ) { connection_s new_conn ; connection_s *cp ; const char *func = "conn_new" ; CLEAR( new_conn ) ; /* * The reason we first get the connection and then allocate a * 'connection_s' is because we want to always consume some input. */ if ( get_connection( sp, &new_conn ) == FAILED ) return( NULL ) ; new_conn.co_sp = sp ; SVC_HOLD( sp ) ; if ( SVC_WAITS( sp ) ) svc_suspend( sp ) ; cp = NEW_CONN() ; if ( cp == CONN_NULL ) { out_of_memory( func ) ; conn_free( &new_conn, 0 ) ; CLEAR( new_conn ) ; return( CONN_NULL ) ; } memcpy(cp, &new_conn, sizeof(connection_s)); return( cp ) ; } /* * Release the specified connection. * Certain actions may be performed before doing this: * - drain of a single UDP packet if the socket type is SOCK_DGRAM */ void conn_free( connection_s *cp, int release_mem ) { struct service *sp ; if( cp == NULL ) return; if( debug.on ) msg( LOG_INFO, "conn_free", "freeing connection") ; sp = cp->co_sp ; if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) ) drain( cp->co_descriptor ) ; if ( SVC_RELE( sp ) == 0 ) { pset_remove( SERVICES( ps ), sp ) ; svc_release( sp ); } cp->co_sp = NULL; if ( CONN_DESCRIPTOR( cp ) > 0 ) CONN_CLOSE( cp ) ; CLEAR( *cp ) ; if (release_mem) { FREE_CONN( cp ) ; } } /* This returns a pointer to a local static stack variable. * The behavior is a remnant of inet_ntoa() behavior. */ const char *conn_addrstr( const connection_s *cp ) { static char name[NI_MAXHOST]; unsigned int len = 0; if( !M_IS_SET( (cp)->co_flags, COF_HAVE_ADDRESS ) ) return ""; if( cp->co_remote_address.sa.sa_family == AF_INET ) len = sizeof(struct sockaddr_in); else if( cp->co_remote_address.sa.sa_family == AF_INET6 ) len = sizeof(struct sockaddr_in6); if( getnameinfo( &cp->co_remote_address.sa, len, name, NI_MAXHOST, NULL, 0, NI_NUMERICHOST ) ) { return ""; } return name; } void conn_dump( const connection_s *cp, int fd ) { const char *name = conn_addrstr( cp ); tabprint( fd, 1, "service = %s\n", SVC_ID( cp->co_sp ) ) ; tabprint( fd, 1, "descriptor = %d\n", cp->co_descriptor ) ; tabprint( fd, 1, "flags = %#lx\n", cp->co_flags ) ; tabprint( fd, 1, "remote_address = %s,%d\n", name, ntohs( cp->co_remote_address.sa_in.sin_port ) ) ; } xinetd-2.3.15.4/src/connection.h000066400000000000000000000047131333055217000163420ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef CONNECTION_H #define CONNECTION_H /* * $Id$ */ #include "config.h" #include #include #include #include #include "mask.h" #include "service.h" #include "defs.h" #include "msg.h" #include "sio.h" #ifndef IN6_IS_ADDR_V4MAPPED #define IN6_IS_ADDR_V4MAPPED(a) \ ((((uint32_t *) (a))[0] == 0) && (((uint32_t *) (a))[1] == 0) && \ (((uint32_t *) (a))[2] == htonl (0xffff))) #endif #ifndef IN6_IS_ADDR_V4COMPAT #define IN6_IS_ADDR_V4COMPAT(a) \ ((((uint32_t *) (a))[0] == 0) && (((uint32_t *) (a))[1] == 0) && \ (((uint32_t *) (a))[2] == 0) && (ntohl (((uint32_t *) (a))[3]) > 1)) #endif #define MAX_ALTERNATIVES 3 /* Connection flags */ #define COF_HAVE_ADDRESS 1 #define COF_NEW_DESCRIPTOR 2 struct connection { struct service *co_sp ; int co_descriptor ; mask_t co_flags ; union xsockaddr co_remote_address ; } ; #define CONN_CLOSE( cp ) { Sclose( (cp)->co_descriptor ); (cp)->co_descriptor = -1; } #define COP( p ) ((connection_s *)(p)) #define CONN_NULL COP( NULL ) /* * Field access macros */ #define CONN_DESCRIPTOR( cp ) (cp)->co_descriptor #define CONN_SERVICE( cp ) (cp)->co_sp #define CONN_SET_FLAG( cp, flag ) M_SET( (cp)->co_flags, flag ) #define CONN_SET_DESCRIPTOR( cp, fd ) (cp)->co_descriptor = (fd) #define CONN_SETADDR( cp, sinp ) \ { \ CONN_SET_FLAG( cp, COF_HAVE_ADDRESS ) ; \ memcpy(((cp)->co_remote_address.pad), sinp, sizeof(*sinp) ); \ } #define CONN_ADDRESS( cp ) \ ( \ M_IS_SET( (cp)->co_flags, COF_HAVE_ADDRESS ) \ ? &((cp)->co_remote_address.sa) \ : SA(NULL) \ ) #define CONN_XADDRESS( cp ) \ ( \ M_IS_SET( (cp)->co_flags, COF_HAVE_ADDRESS ) \ ? &((cp)->co_remote_address) \ : NULL \ ) connection_s *conn_new(struct service *sp); void conn_free(connection_s *cp, int); void conn_dump(const connection_s *cp,int fd); const char *conn_addrstr( const connection_s *cp ); #endif /* CONNECTION_H */ xinetd-2.3.15.4/src/defs.h000066400000000000000000000117521333055217000151250ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef DEFS_H #define DEFS_H /* * $Id$ */ #include "config.h" #include #include #include #include #include union xsockaddr { struct sockaddr sa; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; char pad[128]; }; #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif #define NUL '\0' #define ES_NOMEM "out of memory" #define INT_NULL ((int *)0) #define CHAR_NULL ((char *)0) #define VOID_NULL ((void *)0) #define FD_SET_NULL ((fd_set *)0) #define RUSAGE_NULL ((struct rusage *)0) #define TIMEVAL_NULL ((struct timeval *)0) #define EQ( s1, s2 ) ( strcasecmp( s1, s2 ) == 0 ) #define CLEAR( x ) (void) memset( (char *)&(x), 0, sizeof( x ) ) /* Apparently, some tcp wrapper header files export an SA definition. * make sure we use ours instead of some other one. */ #undef SA #define SA( p ) ( (struct sockaddr *) (p) ) #define SAIN( p ) ( (struct sockaddr_in *) (p) ) #define SAIN6( p ) ( (struct sockaddr_in6 *) (p) ) #define CSA( p ) ( (const struct sockaddr *) (p) ) #define CSAIN( p ) ( (const struct sockaddr_in *) (p) ) #define CSAIN6( p ) ( (const struct sockaddr_in6 *) (p) ) #define NEW( type ) (type *) malloc( sizeof( type ) ) #define FREE( p ) (void) free( (char *)(p) ) /* * Value for unlimited server instances */ #define UNLIMITED (-1) /* * We pass to the child the descriptors 0..MAX_PASS_FD */ #define MAX_PASS_FD 2 /* * Service port for the identification service */ #define IDENTITY_SERVICE_PORT 113 /* * This is the signal sent to interceptor processes to tell them * to stop intercepting */ #define INTERCEPT_SIG SIGUSR1 /* * This is how many descriptors we reserve for ourselves: * * 3 for stdin, stdout, stderr * 1 for syslog/debug * * For the rest we just need to reserve the maximum of each category. * * 1 for doing accepts * 1 for registering rpc services (initialization phase) * 4 for reading the configuration file during reconfiguration * 1 for the configuration file * 1 for /etc/passwd * 1 for /etc/group * 1 for /etc/services, /etc/protocols, /etc/rpc * NOTE: We need only 1 descriptor for the last 3 files because * the functions get{serv,proto,rpc}byname close the * respective files after accessing them. * 1 for dumping the internal state * 1 for talking to the portmapper (reconfiguration phase) * 1 for doing identification * * NOTE: we assume that the socket used for pmap_{set,unset} is closed * after the operation is completed. If it stays open, then we * need to increase DESCRIPTORS_RESERVED. */ #define DESCRIPTORS_RESERVED 8 /* * Used for listen(2) */ #define LISTEN_BACKLOG 64 /* * constants for limiting ps.rws.fd_list */ #define MAX_FDS 4096 /* * When explicit values are given for enum's, that is because the structures * that the enum's are in may be initialized by a memory clear operation. */ typedef enum { FAILED = 0, OK } status_e ; typedef enum { NO = 0, YES } boolean_e ; /* * Possible outcomes of an identification attempt */ typedef enum { IDR_OK, IDR_NOSERVER, IDR_TIMEDOUT, IDR_RESPERR, IDR_BADRESP, IDR_ERROR } idresult_e ; typedef int bool_int ; typedef void (*voidfunc)() ; typedef status_e (*statfunc)() ; /* * A name-value list is exactly what its name says. * The functions nv_get_name() and nv_get_value() return a pointer to * the entry with the specified value or name respectively. * The list ends when an antry with a NULL name is encountered. * The value field of that entry is treated in a special manner: if it * is non-zero, it is assumed that there exists one more entry whose * name field will be returned by the nv_get_name function if it can't * find an entry whose value field is equal to its 2nd parameter. * If the value field of the NULL entry is 0, then nv_get_name() will * return NULL. */ struct name_value { const char *name ; int value ; } ; struct protocol_name_value { char *name ; int value ; } ; struct debug { bool_int on ; int fd ; } ; /* This is some forward prototypes to work out a couple * circular dependencies in the data structures */ struct service; struct server; struct connection; typedef struct connection connection_s ; extern struct debug debug ; #endif /* DEFS_H */ xinetd-2.3.15.4/src/env.c000066400000000000000000000112071333055217000147620ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include "pset.h" #include "env.h" #include "msg.h" extern char **environ ; env_h std_env ; /* created from environ */ status_e initenv(void) { std_env = env_make( environ ) ; return( ( std_env == NULL ) ? FAILED : OK ) ; } static status_e make_env_with_strings(struct environment *, env_h, pset_h); static status_e make_env_from_vars(struct environment *, env_h, pset_h) ; static status_e update_env_with_strings(env_h, pset_h) ; status_e setup_environ( struct service_config *scp, struct service_config *def ) { struct environment *ep = SC_ENV( scp ) ; if ( ! SC_SPECIFIED( scp, A_PASSENV ) ) { if ( ! SC_SPECIFIED( def, A_PASSENV ) ) { if ( ! SC_SPECIFIED( scp, A_ENV ) ) { ep->env_type = STD_ENV ; ep->env_handle = std_env ; return( OK ) ; } else return( make_env_with_strings( ep, std_env, SC_ENV_VAR_DEFS(scp) ) ) ; } else /* SC_SPECIFIED( def, A_PASSENV ) */ { struct environment *dep = SC_ENV( def ) ; if ( dep->env_type == NO_ENV && make_env_from_vars( dep, std_env, SC_PASS_ENV_VARS(def) ) == FAILED ) return( FAILED ) ; if ( ! SC_SPECIFIED( scp, A_ENV ) ) { ep->env_type = DEF_ENV ; ep->env_handle = dep->env_handle ; return( OK ) ; } else return( make_env_with_strings( ep, dep->env_handle, SC_ENV_VAR_DEFS(scp) ) ) ; } } else /* SC_SPECIFIED( scp, A_PASSENV ) */ { if ( make_env_from_vars( ep, std_env, SC_PASS_ENV_VARS(scp) ) == FAILED ) return( FAILED ) ; if ( ! SC_SPECIFIED( scp, A_ENV ) ) return( OK ) ; else { if ( update_env_with_strings( ep->env_handle, SC_ENV_VAR_DEFS(scp) ) == FAILED ) { env_destroy( ep->env_handle ) ; return( FAILED ) ; } return( OK ) ; } } } /* * Create a new environment from environ and env_strings * env_strings contains strings of the form "var=value" */ static status_e make_env_with_strings( struct environment *ep, env_h env, pset_h env_strings ) { env_h new_env ; const char *func = "make_env_with_strings" ; if ( ( new_env = env_create( env ) ) == ENV_NULL ) { out_of_memory( func ) ; return( FAILED ) ; } if ( update_env_with_strings( new_env, env_strings ) == FAILED ) { env_destroy( new_env ) ; return( FAILED ) ; } ep->env_type = CUSTOM_ENV ; ep->env_handle = new_env ; return( OK ) ; } static status_e make_env_from_vars( struct environment *ep, env_h env, pset_h vars ) { env_h new_env ; char *varname ; unsigned u ; const char *func = "make_env_from_vars" ; if ( ( new_env = env_create( ENV_NULL ) ) == ENV_NULL ) { out_of_memory( func ) ; return( FAILED ) ; } for ( u = 0 ; u < pset_count( vars ) ; u++ ) { varname = (char *) pset_pointer( vars, u ) ; if ( env_addvar( new_env, env, varname ) == ENV_ERR ) switch ( env_errno ) { case ENV_EBADVAR: msg( LOG_ERR, func, "Unknown variable %s", varname ) ; break ; case ENV_ENOMEM: out_of_memory( func ) ; env_destroy( new_env ) ; return( FAILED ) ; } } ep->env_type = CUSTOM_ENV ; ep->env_handle = new_env ; return( OK ) ; } static status_e update_env_with_strings( env_h env, pset_h strings ) { unsigned u ; const char *func = "update_env_with_strings" ; for ( u = 0 ; u < pset_count( strings ) ; u++ ) { char *p = (char *) pset_pointer( strings, u ) ; if ( env_addstr( env, p ) == ENV_ERR ) switch ( env_errno ) { case ENV_ENOMEM: out_of_memory( func ) ; return( FAILED ) ; case ENV_EBADSTRING: msg( LOG_ERR, func, "Bad environment string: %s", p ) ; break ; } } return( OK ) ; } xinetd-2.3.15.4/src/env.h000066400000000000000000000003371333055217000147710ustar00rootroot00000000000000#ifndef X_ENV_H #define X_ENV_H #include "m_env.h" #include "defs.h" #include "sconf.h" extern env_h std_env; status_e initenv(void); status_e setup_environ(struct service_config *scp,struct service_config *def); #endif xinetd-2.3.15.4/src/ident.c000066400000000000000000000205321333055217000152760ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "str.h" #include "ident.h" #include "msg.h" #include "server.h" #include "connection.h" #include "util.h" #include "log.h" #include "sconst.h" static char *get_line( int sd, register char *buf, unsigned bufsize ); static char *verify_line( char *line, unsigned local_port, unsigned remote_port ); #define IBUFSIZE 1024 /* RFC-1413 suggests 1000 */ #define START_TIMER( t ) (void) alarm( t ) #define STOP_TIMER() (void) alarm( 0 ) static sigjmp_buf env ; #ifdef __GNUC__ __attribute__ ((noreturn)) #endif static void sigalrm_handler(int signum) { siglongjmp( env, 1 ) ; } /* * This function always runs in a forked process. */ idresult_e log_remote_user( const struct server *serp, unsigned timeout ) { static char buf[ IBUFSIZE ] ; int cc ; union xsockaddr sin_local, sin_remote, sin_contact, sin_bind; volatile unsigned local_port; volatile unsigned remote_port; int sd ; socklen_t sin_len ; char *p ; const char *func = "log_remote_user" ; if ( timeout && signal( SIGALRM, sigalrm_handler ) == SIG_ERR ) { msg( LOG_ERR, func, "signal: %m" ) ; return( IDR_ERROR ) ; } /* * Determine local and remote addresses */ sin_len = sizeof( sin_local ) ; if ( getsockname( SERVER_FD( serp ), &sin_local.sa, &sin_len ) == -1 ) { msg( LOG_ERR, func, "(%d) getsockname: %m", getpid() ) ; return( IDR_ERROR ) ; } if ( CONN_XADDRESS( SERVER_CONNECTION( serp ) ) == NULL ) { /* * This shouldn't happen since identification only works for * connection-based services. */ msg( LOG_ERR, func, "connection has no address" ) ; return( IDR_ERROR ) ; } CLEAR( sin_contact ); sin_len = sizeof( sin_remote ); if ( getpeername( SERVER_FD( serp ), &sin_remote.sa, &sin_len ) == -1 ) { msg( LOG_ERR, func, "(%d) getpeername: %m", getpid() ) ; return( IDR_ERROR ) ; } sin_contact = sin_remote; memcpy( &sin_bind, &sin_local, sizeof(sin_bind) ) ; local_port = 0; remote_port = 0; if( sin_remote.sa.sa_family == AF_INET6 ) { local_port = ntohs( sin_local.sa_in6.sin6_port ) ; remote_port = ntohs( sin_remote.sa_in6.sin6_port ) ; sin_contact.sa_in6.sin6_port = htons( IDENTITY_SERVICE_PORT ) ; sin_bind.sa_in.sin_port = 0 ; } else if( sin_remote.sa.sa_family == AF_INET ) { local_port = ntohs( sin_local.sa_in.sin_port ) ; remote_port = ntohs( sin_remote.sa_in.sin_port ) ; sin_contact.sa_in.sin_port = htons( IDENTITY_SERVICE_PORT ) ; sin_bind.sa_in6.sin6_port = 0 ; } /* * Create a socket, bind it, and set the close-on-exec flag on the * descriptor. We set the flag in case we are called as part of a * successful attempt to start a server (i.e. execve will follow). * The socket must be bound to the receiving address or ident might * fail for multi-homed hosts. */ sd = socket( sin_remote.sa.sa_family, SOCK_STREAM, 0 ) ; if ( sd == -1 ) { msg( LOG_ERR, func, "socket creation: %m" ) ; return( IDR_ERROR ) ; } if ( sin_bind.sa.sa_family == AF_INET ) sin_len = sizeof( sin_bind.sa_in ) ; else sin_len = sizeof( sin_bind.sa_in6 ) ; if ( bind(sd, &sin_bind.sa, sin_len) == -1 ) { msg( LOG_ERR, func, "socket bind: %m" ) ; (void) Sclose( sd ) ; return( IDR_ERROR ) ; } if ( fcntl( sd, F_SETFD, FD_CLOEXEC ) == -1 ) { msg( LOG_ERR, func, "fcntl F_SETFD: %m" ) ; (void) Sclose( sd ) ; return( IDR_ERROR ) ; } if ( timeout ) { if ( sigsetjmp( env, 1 ) == 0 ) START_TIMER( timeout ) ; else { Sclose( sd ) ; return( IDR_TIMEDOUT ) ; } } if ( connect( sd, &sin_contact.sa, sizeof( sin_contact ) ) == -1 ) { if ( timeout ) { STOP_TIMER() ; signal ( SIGALRM, SIG_DFL ) ; } Sclose( sd ); return( IDR_NOSERVER ) ; } cc = strx_nprint( buf, sizeof( buf ), "%d,%d\r\n", remote_port, local_port ) ; if ( write_buf( sd, buf, cc ) == FAILED ) { if ( timeout ) { STOP_TIMER() ; signal ( SIGALRM, SIG_DFL ) ; } Sclose( sd ); return( IDR_ERROR ) ; } p = get_line( sd, buf, sizeof( buf ) ) ; if ( timeout ) { STOP_TIMER() ; signal ( SIGALRM, SIG_DFL ) ; } if ( p == NULL ) { Sclose( sd ); return( IDR_RESPERR ) ; } /* * Verify that the received line is OK */ if ( ( p = verify_line( buf, local_port, remote_port ) ) == NULL ) { msg(LOG_ERR, func, "Bad line received from identity server at %s: %s", xaddrname( &sin_remote ), buf ) ; Sclose( sd ); return( IDR_BADRESP ) ; } svc_logprint( SERVER_CONNSERVICE( serp ), USERID_ENTRY, "%s", p ) ; return( IDR_OK ) ; } static char *verify_line( char *line, unsigned local_port, unsigned remote_port ) { char *p ; char *start = line ; int port; /* * Verify port numbers */ p = strchr( start, ',' ) ; if ( p == NULL ) return( NULL ) ; *p = NUL ; if ( parse_base10( start, &port ) || port < 0 || (unsigned)port != remote_port ) { *p = ','; return( NULL ) ; } *p = ',' ; start = p+1 ; p = strchr( start, ':' ) ; if ( p == NULL ) return( NULL ) ; *p = NUL ; if ( parse_base10( start, &port ) || port < 0 || (unsigned)port != local_port ) { *p = ':'; return( NULL ) ; } *p = ':'; /* * Look for the 'USERID' string */ { const char *line_id = "USERID" ; unsigned int line_id_len = strlen( line_id ) ; start = p+1 ; for ( p = start ; isspace( *p ) ; p++ ) ; if ( *p == NUL ) return( NULL ) ; start = p ; if ( strncmp( start, line_id, line_id_len ) != 0 ) return( NULL ) ; start += line_id_len ; /* skip it */ } for ( p = start ; isspace( *p ) ; p++ ) ; /* skip any white-space */ if ( *p != ':' ) return( NULL ) ; for ( p++ ; isspace( *p ) ; p++ ) ; if ( *p == NUL ) return( NULL ) ; return( p ) ; } /* * Get a line terminated by CR-LF. * Replace the CR-LF with NUL. */ static char *get_line( int sd, char *buf, unsigned bufsize ) { int size ; ssize_t cc ; char *p ; char *s ; const char *func = "get_line" ; for ( p = buf, size = bufsize ; size > 0 ; p += cc, size -= cc ) { cc = read( sd, p, size ) ; if ( cc == (ssize_t)-1 ) { if ( errno == EINTR ) { cc = 0 ; continue ; } else { msg( LOG_ERR, func, "read: %m" ) ; return( CHAR_NULL ) ; } } if ( cc == 0 ) { msg( LOG_ERR, func, "identd server reply missing ending CR-LF" ) ; return( CHAR_NULL ) ; } for ( s = p ; s < p + cc ; s++ ) { if ( (*s == '\n') && (s != buf) && (*(s-1) == '\r') ) { *(s-1) = NUL ; return( buf ) ; } } } msg( LOG_ERR, func, "Too much input from identity server" ) ; return( CHAR_NULL ) ; } const char *idresult_explain( idresult_e result ) { const char *reason = "UNKNOWN" ; switch ( result ) { case IDR_OK: reason = "no error" ; break ; case IDR_NOSERVER: reason = "no server" ; break ; case IDR_TIMEDOUT: reason = "timeout" ; break ; case IDR_ERROR: reason = "system error" ; break ; case IDR_RESPERR: reason = "error while receiving response" ; break ; case IDR_BADRESP: reason = "bad response" ; break ; } return( reason ) ; } xinetd-2.3.15.4/src/ident.h000066400000000000000000000002661333055217000153050ustar00rootroot00000000000000#ifndef IDENT_H #define IDENT_H #include "defs.h" idresult_e log_remote_user(const struct server *serp,unsigned timeout); const char *idresult_explain(idresult_e result); #endif xinetd-2.3.15.4/src/includedir.c000066400000000000000000000101031333055217000163060ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "pset.h" #include "str.h" #include "includedir.h" #include "msg.h" #include "parse.h" #include "sio.h" #ifndef NAME_MAX #define NAME_MAX 256 #endif static int compfunc( const void *_a, const void *_b ) { const char * const *a = (const char * const *)_a; const char * const *b = (const char * const *)_b; if( a == NULL || a[0] == NULL ) return -1; if( b == NULL || b[0] == NULL ) return 1; return strcmp(a[0], b[0]); } void handle_includedir(const char *service_name, struct configuration *confp) { char *filename; pset_h dir_list; DIR *dirfp; struct dirent *direntry; char *storename; struct stat sb; int u, incfd, len_sn; const char *func = "handle_includedir"; if( service_name == NULL ) return; dir_list = pset_create(0, 0); if( dir_list == NULL ) return; len_sn = strlen(service_name); filename = (char *)malloc(len_sn + NAME_MAX + 2); if (! filename) { parsemsg( LOG_ERR, func, ES_NOMEM ); return; } errno = 0; dirfp = opendir(service_name); if (! dirfp) { parsemsg( LOG_ERR, func, "Unable to read included directory: %s", service_name); free(filename); return; } /* Get the list of files in the directory */ while ((direntry = readdir(dirfp)) != 0) { storename = new_string(direntry->d_name); if( storename == NULL ) { parsemsg( LOG_ERR, func, ES_NOMEM ); free( filename ); return; } pset_add(dir_list, storename); } closedir(dirfp); /* Sort the list using "compfunc" */ pset_sort(dir_list, compfunc); /* Now, traverse the list in alphabetic order * (as determined by strcmp). */ for( u = 0; (unsigned)u < pset_count(dir_list); u++ ) { storename = pset_pointer(dir_list, u); /* Don't try to parse any files containing a dot ('.') * or ending with a tilde ('~'). This catches the case of * '.' and '..', as well as preventing the parsing of * many editor files, temporary files and those saved by RPM * package upgrades. */ if ( !storename[0] /* Shouldn't happen */ || strchr(storename, '.') || storename[strlen(storename)-1] == '~') { pset_remove(dir_list, storename); free(storename); u--; continue; } strx_sprint(filename, len_sn+NAME_MAX+1, "%s/%s", service_name, storename); if( stat(filename, &sb) < 0 ) { parsemsg( LOG_ERR, func, "Unable to stat includedir file %s", filename); pset_remove(dir_list, storename); free(storename); u--; continue; } /* Only open it if it's a regular file. */ if( !S_ISREG(sb.st_mode) ) { msg( LOG_ERR, func, "%s is not a regular file. It is being skipped.", filename ); pset_remove(dir_list, storename); free(storename); u--; continue; } incfd = open(filename, O_RDONLY); if( incfd < 0 ) { parsemsg( LOG_ERR, func, "Unable to open included configuration file: %s", filename); pset_remove(dir_list, storename); free(storename); u--; continue; } parsemsg( LOG_DEBUG,func,"Reading included configuration file: %s",filename); parse_conf_file(incfd, confp, filename); /* * parse_conf_file eventually calls Srdline, try Sclosing to * unmmap memory. */ Sclose(incfd); pset_remove(dir_list, storename); free(storename); u--; } if ( errno != 0) { parsemsg( LOG_ERR, func, "Error reading included directory: %s", service_name); } pset_destroy(dir_list); free(filename); } xinetd-2.3.15.4/src/includedir.h000066400000000000000000000002241333055217000163160ustar00rootroot00000000000000#ifndef INCLUDEDIR_H #define INCLUDEDIR_H #include "conf.h" void handle_includedir(const char *service_name,struct configuration *confp); #endif xinetd-2.3.15.4/src/inet.c000066400000000000000000000247301333055217000151360ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "str.h" #include "inet.h" #include "msg.h" #include "parse.h" #include "parsesup.h" #include "nvlists.h" static int get_next_inet_entry( int fd, pset_h sconfs); void parse_inet_conf_file( int fd, struct configuration *confp ) { pset_h sconfs = CNF_SERVICE_CONFS( confp ); line_count = 0; for( ;; ) { if (get_next_inet_entry(fd, sconfs) == -2) break; } } static int get_next_inet_entry( int fd, pset_h sconfs) { char *p; str_h strp; char *line = next_line(fd); struct service_config *scp; unsigned u, i; const char *func = "get_next_inet_entry"; char *name = NULL, *rpcvers = NULL, *rpcproto = NULL; char *group, *proto, *stype; const struct name_value *nvp; struct protoent *pep ; struct passwd *pw ; struct group *grp ; const char *dot = "."; const char *slash = "/"; pset_h args; if( line == CHAR_NULL ) return -2; strp = str_parse( line, " \t", STR_RETURN_ERROR, INT_NULL ) ; if( strp == NULL ) { parsemsg( LOG_CRIT, func, "inetd.conf - str_parse failed" ) ; return( -1 ) ; } if( (args = pset_create(10,10)) == NULL ) { out_of_memory(func); return -1; } /* Break the line into components, based on spaces */ while( (p = str_component( strp )) ) { if( pset_add(args, p) == NULL ) { parsemsg( LOG_CRIT, func, ES_NOMEM ); pset_destroy(args); return -1; } } str_endparse( strp ); /* get the service name */ name = new_string((char *)pset_pointer( args, 0 )); if( name == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - Invalid service name" ); pset_destroy(args); return -1; } /* Check to find the '/' for specifying RPC version numbers */ if( (rpcvers = strstr(name, slash)) != NULL ) { *rpcvers = '\0'; rpcvers++; } scp = sc_alloc( name ); if( scp == NULL ) { pset_destroy(args); free( name ); return -1; } /* * sc_alloc makes its own copy of name. At this point, sc_alloc worked * so we will free our copy to avoid leaks. */ free( name ); /* Replicate inetd behavior in this regard. Also makes sure the * service actually works on system where setgroups(0,NULL) doesn't * work. */ SC_GROUPS(scp) = YES; SC_SPECIFY( scp, A_GROUPS ); /* Get the socket type (stream dgram) */ stype = (char *)pset_pointer(args, 1); if( stype == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - Invalid socket type" ); pset_destroy(args); sc_free(scp); return -1; } nvp = nv_find_value( socket_types, stype ); if( nvp == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - Bad socket type: %s", p); pset_destroy(args); sc_free(scp); return -1; } SC_SOCKET_TYPE(scp) = nvp->value; /* Get the protocol type */ proto = (char *)pset_pointer(args,2); if( strstr(proto, "rpc") != NULL ) { int rpcmin, rpcmax; struct rpc_data *rdp = SC_RPCDATA( scp ) ; if( rpcvers == NULL ) { pset_destroy(args); sc_free(scp); return -1; /* uh oh */ } p = strchr(rpcvers, '-'); if( p && parse_int(rpcvers, 10, '-', &rpcmin) == 0 ) { if( parse_base10(p + 1, &rpcmax) || rpcmin > rpcmax ) { pset_destroy(args); sc_free(scp); return -1; } } else { if( parse_base10(rpcvers, &rpcmin) ) { pset_destroy(args); sc_free(scp); return -1; } rpcmax = rpcmin; } /* now have min and max rpc versions */ rdp->rd_min_version = rpcmin; rdp->rd_max_version = rpcmax; rpcproto = strstr(proto, slash); if( rpcproto == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - bad rpc version numbers" ); pset_destroy(args); sc_free(scp); return -1; } *rpcproto = '\0'; rpcproto++; proto = rpcproto; /* Set the RPC type field */ nvp = nv_find_value( service_types, "RPC" ); if ( nvp == NULL ) { parsemsg( LOG_WARNING, func, "inetd.conf - Bad foo %s", SC_NAME(scp) ) ; pset_destroy(args); sc_free(scp); return -1; } M_SET(SC_TYPE(scp), nvp->value); } if ( ( pep = getprotobyname( proto ) ) == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - Protocol %s not in /etc/protocols", proto ) ; pset_destroy(args); sc_free(scp); return -1; } SC_PROTONAME(scp) = new_string( proto ) ; if ( SC_PROTONAME(scp) == NULL ) { out_of_memory( func ) ; pset_destroy(args); sc_free(scp); return -1; } SC_PROTOVAL(scp) = pep->p_proto; SC_SPECIFY(scp, A_PROTOCOL); /* Get the wait attribute */ p = (char *)pset_pointer(args, 3); if ( p == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - No value specified for wait" ); sc_free(scp); return -1; } if ( EQ( p, "wait" ) ) SC_WAIT(scp) = YES ; else if ( EQ( p, "nowait" ) ) SC_WAIT(scp) = NO ; else parsemsg( LOG_ERR, func, "inetd.conf - Bad value for wait: %s", p ) ; /* Get the user to run as */ p = (char *)pset_pointer(args, 4); if ( p == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - No value specified for user" ); sc_free(scp); return -1; } if( (group = strstr(p, dot)) ) { *group = '\0'; group++; grp = (struct group *)getgrnam( (char *)group ) ; if ( grp == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - Unknown group: %s", group ) ; pset_destroy(args); sc_free(scp); return -1; } SC_GID(scp) = ((struct group *)grp)->gr_gid; SC_SPECIFY( scp, A_GROUP ); } pw = getpwnam( p ); if ( pw == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - Unknown user: %s", p ) ; pset_destroy(args); sc_free(scp); return -1; } str_fill( pw->pw_passwd, ' ' ); SC_UID(scp) = pw->pw_uid; SC_USER_GID(scp) = pw->pw_gid; /* Get server name, or flag as internal */ p = (char *)pset_pointer(args, 5); if ( p == NULL ) { parsemsg( LOG_ERR, func, "inetd.conf - No value specified for user" ); sc_free(scp); return -1; } if( EQ( p, "internal" ) ) { nvp = nv_find_value( service_types, "INTERNAL" ); if ( nvp == NULL ) { parsemsg( LOG_WARNING, func, "inetd.conf - Bad foo %s", SC_NAME(scp) ) ; pset_destroy(args); sc_free(scp); return -1; } M_SET(SC_TYPE(scp), nvp->value); if( EQ( SC_NAME(scp), "time" ) ) { if( EQ( proto, "stream" ) ) SC_ID(scp) = new_string("time-stream"); else SC_ID(scp) = new_string("time-dgram"); } if( EQ( SC_NAME(scp), "daytime" ) ) { if( EQ( proto, "stream" ) ) SC_ID(scp) = new_string("daytime-stream"); else SC_ID(scp) = new_string("daytime-dgram"); } if( EQ( SC_NAME(scp), "chargen" ) ) { if( EQ( proto, "stream" ) ) SC_ID(scp) = new_string("chargen-stream"); else SC_ID(scp) = new_string("chargen-dgram"); } if( EQ( SC_NAME(scp), "echo" ) ) { if( EQ( proto, "stream" ) ) SC_ID(scp) = new_string("echo-stream"); else SC_ID(scp) = new_string("echo-dgram"); } if( EQ( SC_NAME(scp), "discard" ) ) { parsemsg(LOG_WARNING, func, "inetd.conf - service discard not supported"); pset_destroy(args); sc_free(scp); return -1; } } else { SC_SERVER(scp) = new_string( p ); if ( SC_SERVER(scp) == NULL ) { out_of_memory( func ) ; pset_destroy(args); sc_free(scp); return -1; } SC_SPECIFY( scp, A_SERVER); /* Get argv */ SC_SERVER_ARGV(scp) = (char **)argv_alloc(pset_count(args)+1); for( u = 0; u < pset_count(args)-6 ; u++ ) { p = new_string((char *)pset_pointer(args, u+6)); if( p == NULL ) { for ( i = 1 ; i < u ; i++ ) free( SC_SERVER_ARGV(scp)[i] ); free( SC_SERVER_ARGV(scp) ); pset_destroy(args); sc_free(scp); return -1; } SC_SERVER_ARGV(scp)[u] = p; } /* Set the reuse flag, as this is the default for inetd */ nvp = nv_find_value( service_flags, "REUSE" ); if ( nvp == NULL ) { parsemsg( LOG_WARNING, func, "inetd.conf - Bad foo %s", SC_NAME(scp) ) ; pset_destroy(args); sc_free(scp); return -1; } M_SET(SC_XFLAGS(scp), nvp->value); /* Set the NOLIBWRAP flag, since inetd doesn't have libwrap built in */ nvp = nv_find_value( service_flags, "NOLIBWRAP" ); if ( nvp == NULL ) { parsemsg( LOG_WARNING, func, "inetd.conf - Bad foo %s", SC_NAME(scp) ) ; pset_destroy(args); sc_free(scp); return -1; } M_SET(SC_XFLAGS(scp), nvp->value); /* Set the NAMEINARGS flag, as that's the default for inetd */ nvp = nv_find_value( service_flags, "NAMEINARGS" ); if ( nvp == NULL ) { parsemsg( LOG_WARNING, func, "inetd.conf - Bad foo %s", SC_NAME(scp) ) ; pset_destroy(args); sc_free(scp); return (-1); } M_SET(SC_XFLAGS(scp), nvp->value); SC_SPECIFY( scp, A_SERVER_ARGS ); if ( (SC_ID(scp) = new_string( SC_NAME(scp) )) ) SC_PRESENT( scp, A_ID ) ; else { out_of_memory( func ) ; pset_destroy(args); sc_free(scp); return -1; } } SC_SPECIFY( scp, A_PROTOCOL ); SC_SPECIFY( scp, A_USER ); SC_SPECIFY( scp, A_SOCKET_TYPE ); SC_SPECIFY( scp, A_WAIT ); if( ! pset_add(sconfs, scp) ) { out_of_memory( func ); pset_destroy(args); sc_free(scp); return -1; } pset_destroy(args); parsemsg( LOG_DEBUG, func, "added service %s", SC_NAME(scp)); return 0; } xinetd-2.3.15.4/src/inet.h000066400000000000000000000002421333055217000151330ustar00rootroot00000000000000#ifndef X_INET_H #define X_INET_H #include "pset.h" #include "sconf.h" #include "conf.h" void parse_inet_conf_file(int fd,struct configuration *confp); #endif xinetd-2.3.15.4/src/init.c000066400000000000000000000210471333055217000151400ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "sio.h" #include "init.h" #include "defs.h" #include "msg.h" #include "signals.h" #include "env.h" #include "confparse.h" #include "options.h" #include "main.h" #include "xconfig.h" #include "special.h" #include "retry.h" #include "internals.h" #include "libportable.h" struct module { const char *name ; status_e (*initializer)() ; } ; static const struct module program_modules[] = { { "signal", signal_init }, { "environment", initenv }, { CHAR_NULL, NULL } } ; static bool_int have_stderr ; #define STDERR_FD 2 static void set_fd_limit(void); /* * This function is invoked when a system call fails during initialization. * A message is printed to stderr, and the program is terminated */ #ifdef __GNUC__ __attribute__ ((noreturn)) #endif static void syscall_failed( const char *call ) { char *err ; if ( have_stderr ) { err = strerror(errno); Sprint( STDERR_FD, "%s: %s failed: %s\n", program_name, call, err ) ; } exit( 1 ) ; } /* * Close all descriptors except STDERR_FD. We need this to report * errors and the process pid of the daemon. * Open all descriptors in the range 0..MAX_PASS_FD (except STDERR_FD) * to /dev/null. * STDERR_FD should not be 0. * * msg() cannot be used from this function, as it has not been initialized yet. */ static void setup_file_descriptors(void) { int fd ; int new_fd ; int null_fd ; if ( Smorefds(3) == SIO_ERR ) { syscall_failed("Smorefds"); exit( 1 ) ; } set_fd_limit() ; /* * Close all unneeded descriptors */ for ( fd = STDERR_FD + 1 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ ) if ( Sclose( fd ) && errno != EBADF ) { syscall_failed("Sclose"); exit( 1 ) ; } /* * Check if the STDERR_FD descriptor is open. */ new_fd = dup( STDERR_FD ) ; if ( new_fd != -1 ) { have_stderr = TRUE ; (void) Sclose( new_fd ) ; } if ( ( null_fd = open( "/dev/null", O_RDONLY ) ) == -1 ) syscall_failed( "open of '/dev/null'" ) ; for ( fd = 0 ; fd <= MAX_PASS_FD ; fd++ ) { if ( have_stderr && fd == STDERR_FD ) continue ; if ( fd != null_fd && dup2( null_fd, fd ) == -1 ) syscall_failed( "dup2" ) ; } if ( null_fd > MAX_PASS_FD ) (void) Sclose( null_fd ) ; } /* msg() cannot be used in this function, as it has not been initialized yet. */ static void set_fd_limit(void) { #ifdef RLIMIT_NOFILE struct rlimit rl ; /* * Set the soft file descriptor limit to the hard limit. */ if ( getrlimit( RLIMIT_NOFILE, &rl ) == -1 ) { syscall_failed("getrlimit(RLIMIT_NOFILE)"); exit( 1 ) ; } if ( rl.rlim_max == RLIM_INFINITY ) rl.rlim_max = MAX_FDS; ps.ros.max_descriptors = rl.rlim_max ; #else /* ! RLIMIT_NOFILE */ ps.ros.max_descriptors = getdtablesize() ; #endif /* RLIMIT_NOFILE */ } static void init_common( int argc, char *argv[] ) { const struct module *mp = NULL; const char *func = "init_common" ; /* * Initialize the program state */ ps.ros.Argv = argv ; ps.ros.Argc = argc ; ps.ros.is_superuser = ( geteuid() == 0 ) ; /* * Initialize the program modules */ for ( mp = program_modules ; mp->name ; mp++ ) if ( (*mp->initializer)() == FAILED ) { msg( LOG_CRIT, func, "Initialization of %s facility failed. Exiting...", mp->name ) ; exit( 1 ) ; } (void) umask( umask( 077 ) | 022 ) ; } /* Create the pidfile. * This is called after msg_init(), and potentially after * we've become_daemon() (depending on if we're in debug or not-forking) */ static void create_pidfile(void) { int pidfd; FILE *pidfile; if ( ps.ros.pid_file ) { unlink(ps.ros.pid_file); pidfd = open(ps.ros.pid_file, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (pidfd >= 0) { /* successfully created file */ pidfile = fdopen(pidfd, "w"); if (pidfile) { fchmod(pidfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } else { msg(LOG_DEBUG, "create_pidfile", "fdopen failed: %m"); Sclose(pidfd); } } else msg(LOG_DEBUG, "create_pidfile", "open failed: %m"); } } /* * Become a daemon by forking a new process. The parent process exits. */ static void become_daemon(void) { int tries ; int pid ; const char *func = "become_daemon" ; /* * First fork so that the parent will think we have exited */ for ( tries = 0 ;; tries++ ) { if ( tries == 5 ) { msg( LOG_CRIT, func, "fork: %m. Exiting..." ) ; exit( 0 ) ; } pid = fork() ; if ( pid == -1 ) { sleep( 1 ) ; /* wait for a second */ continue ; /* and then retry */ } else if ( pid == 0 ) break ; else exit( 0 ) ; } (void) dup2( 0, STDERR_FD ) ; no_control_tty() ; #ifdef DEBUG_DAEMON sleep( 20 ) ; /* XXX: timers will probably not work after this */ #endif } static pset_h new_table( unsigned size ) { const char *func = "new_table" ; pset_h tab = pset_create( size, 0 ) ; if ( tab == NULL ) { msg( LOG_CRIT, func, "Failed to create table" ) ; exit( 1 ) ; } return( tab ) ; } /* * Create tables */ static void init_rw_state( void ) { const char *func = "init_rw_state" ; SERVERS( ps ) = new_table( 0 ) ; RETRIES( ps ) = new_table( 0 ) ; SERVICES( ps ) = new_table( 0 ) ; ps.rws.descriptors_free = ps.ros.max_descriptors - DESCRIPTORS_RESERVED ; #ifdef HAVE_POLL ps.rws.pfds_allocated = ps.ros.max_descriptors ; ps.rws.pfd_array = (struct pollfd *) malloc( sizeof( struct pollfd ) * ps.rws.pfds_allocated ) ; if ( ps.rws.pfd_array == NULL ) { out_of_memory(func) ; exit( 1 ) ; } ps.rws.pfds_last = 0 ; #else FD_ZERO( &ps.rws.socket_mask ) ; ps.rws.mask_max = 0 ; #endif /* HAVE_POLL */ } /* * Perform all necessary initializations */ void init_daemon( int argc, char *argv[] ) { const char *fail = NULL; debug.on = 0; memset(&ps, 0, sizeof(ps)); setup_file_descriptors() ; ps.ros.config_file = DEFAULT_CONFIG_FILE ; (void) opt_recognize( argc, argv ) ; /* * XXX: we only use xlog_parms on XLOG_SYSLOG-type logs but in general * we should do it for all types of xlog's we may use. We can get * away with this now, because xlog_parms for XLOG_FILELOG is a noop. */ (void) xlog_parms( XLOG_SYSLOG, program_name, LOG_PID + LOG_NOWAIT, LOG_DAEMON ) ; /* * Initialize the message facility; after this everything can use the * msg() interface */ if ( (fail = msg_init()) ) { if ( have_stderr ) Sprint( STDERR_FD, "%s: msg_init failed: %s\n", program_name, fail ) ; exit( 1 ) ; } init_common( argc, argv ) ; if ( ! debug.on && !dont_fork ) become_daemon() ; create_pidfile(); init_rw_state() ; } /* * Initialize all services * * This function is either successful in starting some services * or it terminates the program. */ void init_services( void ) { struct configuration conf ; const char *func = "init_services" ; if ( cnf_get( &conf ) == FAILED ) { msg( LOG_CRIT, func, "couldn't get configuration. Exiting..." ) ; exit( 1 ) ; } DEFAULTS( ps ) = CNF_DEFAULTS( &conf ) ; (void) cnf_start_services( &conf ) ; CNF_DEFAULTS( &conf ) = NULL ; /* to avoid the free by cnf_free */ cnf_free( &conf ) ; /* * The number of available/active services is kept by the service functions */ if ( stayalive_option == 0 ) { if ( ps.rws.available_services == 0 ) { msg( LOG_CRIT, func, "no services. Exiting..." ) ; if ( ps.ros.pid_file ) { unlink(ps.ros.pid_file); } exit( 1 ) ; } } spec_include() ; /* include special services */ } xinetd-2.3.15.4/src/init.h000066400000000000000000000001541333055217000151410ustar00rootroot00000000000000#ifndef INIT_H #define INIT_H void init_daemon(int argc,char *argv[]); void init_services(void); #endif xinetd-2.3.15.4/src/int.c000066400000000000000000000165271333055217000147760ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "int.h" #include "msg.h" #include "log.h" #include "tcpint.h" #include "udpint.h" #include "sconf.h" #include "intcommon.h" #include "child.h" #include "state.h" #include "main.h" #include "signals.h" #include "xconfig.h" #include static void start_server( struct intercept_s *ip ); static void terminate_server( struct intercept_s *ip ); typedef struct intercept_s *(*initfunc)() ; struct lookup_table { initfunc initializer ; int socket_type ; } ; static struct lookup_table intercept_lookup_table[] = { { di_init, SOCK_DGRAM }, { si_init, SOCK_STREAM }, { NULL, 0 } } ; /* * This variable has file scope for the benefit of the signal handler */ static struct intercept_s *intp = NULL; static initfunc find_initializer( int type ) { struct lookup_table *ltp ; for ( ltp = intercept_lookup_table ; ltp->initializer ; ltp++ ) if ( ltp->socket_type == type ) return( ltp->initializer ) ; msg( LOG_ERR, "find_initializer", "No initializer for type %d", type ) ; _exit( 0 ) ; /* NOTREACHED */ return (initfunc)0; } /* * This function is the interface of the intercept code with the rest of * the program. */ void intercept( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; initfunc initializer ; #ifdef DEBUG_INTERCEPTOR if ( debug.on ) { msg( LOG_DEBUG, "intercept", "%d is sleeping", getpid() ) ; sleep( 10 ) ; } #endif initializer = find_initializer( SVC_SOCKET_TYPE( sp ) ) ; intp = (*initializer)( serp ) ; start_server( intp ) ; (*intp->int_ops->mux)() ; terminate_server( intp ) ; /* * the terminate_server function should not return but even if it * does, child_process will do the _exit. */ } /* * Create a socket and bind it to (INADDR_LOOPBACK,0) */ static int get_server_socket( struct intercept_s *ip ) { struct service *sp = SERVER_SERVICE( INT_SERVER( ip ) ) ; union xsockaddr *sinp = INT_LOCALADDR( ip ) ; int sd ; socklen_t size = AF_UNIX ; const char *func = "get_server_socket" ; if( SC_IPV6(SVC_CONF(sp)) ) { struct addrinfo hint, *res = NULL; memset(&hint, 0, sizeof(struct addrinfo)); hint.ai_family = AF_INET6; hint.ai_flags = AI_NUMERICHOST; sinp->sa_in6.sin6_family = AF_INET6; sinp->sa_in6.sin6_port = 0; if( getaddrinfo("::1", NULL, &hint, &res) != 0 ) int_fail( ip, "can't find ::1" ); if( res == NULL ) int_fail( ip, "no results for ::1" ); if( res->ai_family != AF_INET6 ) int_fail( ip, "non IPv6 result for ::1" ); memcpy(sinp, res->ai_addr, sizeof( struct sockaddr_in6 )); freeaddrinfo(res); size = sizeof(struct sockaddr_in6); } else if( SC_IPV4(SVC_CONF(sp)) ) { sinp->sa_in.sin_family = AF_INET; sinp->sa_in.sin_port = 0; sinp->sa_in.sin_addr.s_addr = inet_addr( "127.0.0.1" ); size = sizeof(struct sockaddr_in); } else int_fail( ip, "unknown socket family" ); if ( ( sd = socket( sinp->sa.sa_family, SVC_SOCKET_TYPE( sp ), SC_PROTOVAL(SVC_CONF(sp)) ) ) == -1 ) int_fail( ip, "socket creation" ) ; if ( bind( sd, SA( sinp ), size ) == -1 ) int_fail( ip, "bind" ) ; size = sizeof( *sinp ) ; if ( getsockname( sd, (struct sockaddr *)( sinp ), &size ) == -1 ) int_fail( ip, "getsockname" ) ; if ( debug.on ) msg( LOG_DEBUG, func, "address = %s, port = %d", xaddrname( sinp ), ntohs( xaddrport( sinp ) ) ) ; if ( ip->int_socket_type == SOCK_STREAM ) (void) listen( sd, LISTEN_BACKLOG ) ; return( sd ) ; } static void start_server( struct intercept_s *ip ) { struct server *serp = INT_SERVER( ip ) ; struct service *sp = SERVER_SERVICE( serp ) ; int server_socket ; pid_t pid ; server_socket = get_server_socket( ip ) ; pid = fork() ; switch ( pid ) { case -1: int_fail( ip, "fork" ) ; /* NOTREACHED */ case 0: CONN_SET_DESCRIPTOR( SERVER_CONNECTION( serp ), server_socket ) ; SVC_MAKE_EXTERNAL( sp ) ; /* avoid looping */ child_process( serp ) ; /* NOTREACHED */ default: SERVER_SET_PID( serp, pid ) ; (void) Sclose( server_socket ) ; } } /* * Return value: * OK if the server died * FAILED otherwise */ static status_e wait_child( struct intercept_s *ip ) { const char *func = "wait_child" ; int status ; status_e ret = FAILED; pid_t pid ; while( (pid = waitpid( -1, &status, WNOHANG )) != 0 ) { if ( pid == -1 ) { if ( errno != EINTR ) { msg( LOG_ERR, func, "wait: %m" ) ; return( ret ) ; } } else if ( pid == SERVER_PID( INT_SERVER( ip ) ) ) { if ( PROC_STOPPED( status ) ) { ret = FAILED; return( ret ) ; } SERVER_SET_EXIT_STATUS( INT_SERVER( ip ), status ) ; ret = OK; } else { unsigned u; /* Ideally, this will never be executed */ msg( LOG_ERR, func, "wait returned pid of unknown process: %d", pid ) ; /* Since we don't have the intercept pointer to this service, * do our best to shut it down safely... */ for( u = 0; u < pset_count( SERVERS(ps) ); u++ ) { struct server *p = SERP( pset_pointer( SERVERS(ps), u) ); if( (p != NULL) && (SERVER_PID(p) == pid) ) { struct service *sp = SERVER_SERVICE(p); struct service_config *scp = SVC_CONF(sp); if( SC_PROTOVAL(scp) == IPPROTO_TCP ) { SERVER_SET_EXIT_STATUS( p, status ); si_exit(); } else if( SC_PROTOVAL(scp) == IPPROTO_UDP ) { SERVER_SET_EXIT_STATUS( p, status ); di_exit(); } else { msg( LOG_ERR, func, "Don't know how to exit %d", pid); } break; } } } } return ret; } static void terminate_server( struct intercept_s *ip ) { pid_t pid = SERVER_PID( INT_SERVER( intp ) ) ; if ( pid > 0 ) (void) kill( pid, SIGKILL ) ; /* * Normally, wait_child should never return since a SIGCHLD will * invoke the signal handler which will then call the exit function. */ if ( wait_child( ip ) == OK ) (*intp->int_ops->exit)() ; } void int_sighandler( int sig ) { const char *func = "int_sighandler" ; if ( debug.on ) msg( LOG_DEBUG, func, "Received signal %s", sig_name( sig ) ) ; if ( sig == SERVER_EXIT_SIG ) { if ( wait_child( intp ) == OK ) (*intp->int_ops->exit)() ; } else if ( sig == INTERCEPT_SIG ) INTERCEPT( intp ) = FALSE ; else if ( sig == SIGTERM ) terminate_server( intp ) ; } xinetd-2.3.15.4/src/int.h000066400000000000000000000033541333055217000147750ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef INT_H #define INT_H /* * $Id$ */ #include #include #include "pset.h" #include "defs.h" #include "server.h" typedef enum { GOOD_CHANNEL, BAD_CHANNEL } channel_state_e ; struct channel { channel_state_e ch_state ; union xsockaddr ch_from ; int ch_local_socket ; int ch_remote_socket ; } ; typedef struct channel channel_s ; #define CHP( p ) ((struct channel *)(p)) #define CHANNEL_NULL CHP( NULL ) #define NEW_CHANNEL() NEW( channel_s ) #define FREE_CHANNEL( chp ) FREE( chp ) struct intercept_common { bool_int ic_intercept ; int ic_remote_socket ; union xsockaddr ic_local_addr ; pset_h ic_connections ; struct server ic_server ; } ; struct intercept_ops { void (*mux)() ; void (*exit)() ; } ; struct intercept_s { int int_socket_type ; struct intercept_common int_common ; void *int_priv ; const struct intercept_ops *int_ops ; } ; #define INT_SERVER( p ) (&(p)->int_common.ic_server) #define INT_LOCALADDR( p ) (&(p)->int_common.ic_local_addr) #define INT_REMOTE( p ) ((p)->int_common.ic_remote_socket) #define INT_CONNECTIONS( p ) ((p)->int_common.ic_connections) #define INTERCEPT( p ) ((p)->int_common.ic_intercept) void intercept(struct server *serp); void int_sighandler(int sig); #endif /* INT_H */ xinetd-2.3.15.4/src/intcommon.c000066400000000000000000000136311333055217000162000ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include "intcommon.h" #include "msg.h" #include "signals.h" #include "connection.h" #include "sconf.h" #include "state.h" #include "main.h" #include "xconfig.h" void int_fail( const struct intercept_s *ip, const char *lsyscall ) { msg( LOG_ERR, "fail", "%s failed: %m", lsyscall ) ; (*ip->int_ops->exit)() ; /* NOTREACHED */ } /* * Returns either a positive number or -1 */ #ifdef HAVE_POLL int int_poll( int pfds_last, struct pollfd *pfd_array ) { const char *func = "int_poll" ; #else int int_select( int max, fd_set *read_mask ) { const char *func = "int_select" ; #endif for ( ;; ) { int n_ready ; do { #ifdef HAVE_POLL n_ready = poll( pfd_array, pfds_last, -1 ); #else n_ready = select( max+1, read_mask, FD_SET_NULL, FD_SET_NULL, TIMEVAL_NULL ) ; #endif } while (n_ready == -1 && errno == EINTR); if ( n_ready > 0 ) return( n_ready ) ; #ifdef HAVE_POLL msg( LOG_ERR, func, "poll: %m" ) ; #else msg( LOG_ERR, func, "select: %m" ) ; #endif return( -1 ) ; } } void int_exit( struct intercept_s *ip ) { int status = SERVER_EXITSTATUS( INT_SERVER( ip ) ) ; const char *func = "int_exit" ; if ( debug.on ) { if ( PROC_EXITED( status ) ) msg( LOG_DEBUG, func, "intercepted server died" ) ; else if ( PROC_SIGNALED( status ) ) msg( LOG_DEBUG, func, "intercepted server received signal %s", sig_name( (int) PROC_TERMSIG( status ) ) ) ; } _exit( (int) PROC_EXITSTATUS( status ) ) ; } /* * The ops vector must be installed before invoking this function */ void int_init( struct intercept_s *ip, struct server *serp ) { unsigned u ; const char *func = "int_init" ; /* * Sanity test */ if ( SERVER_SERVICE( serp ) != SERVER_CONNSERVICE( serp ) ) { msg( LOG_ERR, func, "server service (%s) != connection service (%s)", SVC_ID( SERVER_SERVICE( serp ) ), SVC_ID( SERVER_CONNSERVICE( serp ) ) ) ; exit( 1 ) ; } /* * Close all unneeded descriptors */ for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { struct service *sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( sp == SERVER_SERVICE( serp ) ) continue ; if ( LOG_GET_TYPE( SC_LOG( SVC_CONF( sp ) ) ) == L_FILE ) xlog_destroy( SVC_LOG( sp ) ) ; (void) Sclose( SVC_FD( sp ) ) ; } /* * Setup signal handling */ if ( signal( SERVER_EXIT_SIG, int_sighandler ) == SIG_ERR ) int_fail( ip, "signal" ) ; if ( signal( INTERCEPT_SIG, int_sighandler ) == SIG_ERR ) int_fail( ip, "signal" ) ; if ( signal( SIGTERM, int_sighandler ) == SIG_ERR ) int_fail( ip, "signal" ) ; /* * Initialize state */ INTERCEPT( ip ) = TRUE ; *INT_SERVER( ip ) = *serp ; INT_REMOTE( ip ) = SERVER_FD( serp ) ; INT_CONNECTIONS( ip ) = pset_create( 0, 0 ) ; if ( INT_CONNECTIONS( ip ) == NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; (*ip->int_ops->exit)() ; } } /* * Make a new connection to the local server */ channel_s *int_newconn( struct intercept_s *ip, union xsockaddr *sinp, int remote_socket ) { struct service *sp = SERVER_SERVICE( INT_SERVER( ip ) ) ; int socket_type = SVC_SOCKET_TYPE( sp ) ; union xsockaddr *local = INT_LOCALADDR( ip ) ; char *sid = SVC_ID( sp ) ; channel_s *chp ; int sd ; const char *func = "int_newconn" ; /* * Get a socket and connect it to the local address * */ if ( ( sd = socket( local->sa.sa_family, socket_type, SC_PROTOVAL(SVC_CONF(sp)) ) ) == -1 ) { msg( LOG_ERR, func,"(intercepting %s) socket creation failed: %m", sid ) ; return( CHANNEL_NULL ) ; } if ( connect( sd, SA( local ), sizeof( *local ) ) == -1 ) { msg( LOG_ERR, func, "(intercepting %s) connect failed: %m", sid ) ; (void) Sclose( sd ) ; return( CHANNEL_NULL ) ; } chp = NEW_CHANNEL() ; if ( chp == CHANNEL_NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; (void) Sclose( sd ) ; return( CHANNEL_NULL ) ; } if ( pset_add( INT_CONNECTIONS( ip ), chp ) == NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; FREE_CHANNEL( chp ) ; (void) Sclose( sd ) ; return( CHANNEL_NULL ) ; } chp->ch_state = GOOD_CHANNEL ; chp->ch_from = *sinp ; chp->ch_local_socket = sd ; chp->ch_remote_socket = remote_socket ; return( chp ) ; } /* * Check if the (address,port) in sinp is already in the connection table. * Return value: * a connection pointer if the address is found * NULL if the address if not found * * *addr_checked is set to TRUE of FALSE depending on whether there * is already a connection from the same IP address in the table. */ channel_s *int_lookupconn( struct intercept_s *ip, union xsockaddr *sinp, bool_int *addr_checked ) { unsigned u ; pset_h conntab = INT_CONNECTIONS( ip ) ; *addr_checked = FALSE ; for ( u = 0 ; u < pset_count( conntab ) ; u++ ) { register channel_s *chp = CHP( pset_pointer( conntab, u ) ) ; if ( memcmp( &chp->ch_from, sinp, sizeof( *sinp ) ) == 0 ) { *addr_checked = TRUE ; if ( xaddrport(&chp->ch_from) == xaddrport(sinp) ) return( chp ) ; } } return( CHANNEL_NULL ) ; } xinetd-2.3.15.4/src/intcommon.h000066400000000000000000000013441333055217000162030ustar00rootroot00000000000000#ifndef INTCOMMON_H #define INTCOMMON_H #include "config.h" #include #ifdef HAVE_POLL #include #endif #include "int.h" void int_fail(const struct intercept_s *ip,const char *lsyscall); #ifdef HAVE_POLL int int_poll(int pfds_last, struct pollfd *pfd_array); #else int int_select(int max,fd_set *read_mask); #endif #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void int_exit(struct intercept_s *ip); void int_init(struct intercept_s *ip,struct server *serp); channel_s *int_newconn( struct intercept_s *ip, union xsockaddr *sinp, int remote_socket ); channel_s *int_lookupconn( struct intercept_s *ip, union xsockaddr *sinp, bool_int *addr_checked ); #endif xinetd-2.3.15.4/src/internals.c000066400000000000000000000377711333055217000162070ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "sio.h" #include "internals.h" #include "msg.h" #include "sconf.h" #include "state.h" #include "main.h" #include "xconfig.h" #include "xtimer.h" #include "options.h" static unsigned thread_check( register struct service *sp,unsigned running_servers, unsigned retry_servers ); static unsigned refcount_check( struct service *sp, unsigned *running_servers, unsigned *retry_servers ); static unsigned service_count_check( register struct service *sp, unsigned running_servers, unsigned retry_servers ); static void periodic_check(void); static void dump_services( int fd ) { unsigned u ; /* * Dump the current configuration (services + defaults) */ Sprint( fd, "Services + defaults:\n" ) ; sc_dump( DEFAULTS( ps ), fd, 0, TRUE ) ; for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) svc_dump( SP( pset_pointer( SERVICES( ps ), u ) ), fd ) ; } void dump_internal_state(void) { int dump_fd ; const char *dump_file = DUMP_FILE ; time_t current_time ; int fd ; #ifdef HAVE_POLL int *listed_fds; #endif unsigned u ; const char *func = "dump_internal_state" ; if ( debug.on ) msg( LOG_DEBUG, func, "Dumping State" ) ; dump_fd = open( dump_file, O_WRONLY | O_CREAT | O_APPEND, DUMP_FILE_MODE); if ( dump_fd == -1 ) { msg( LOG_ERR, func, "failed to open %s: %m", dump_file ) ; return ; } if (Sbuftype( dump_fd, SIO_LINEBUF ) == SIO_ERR ) { /* * If the above function failed, Sprint will most likely * fail, too. Output a message for troubleshooting and quit. */ msg( LOG_ERR, func, "failed setting up sio buffering: %m fd:%d", dump_fd ) ; Sclose(dump_fd); return; } /* * Print the program name, version, and timestamp. * Note that the program_version variable contains the program name. */ (void) time( ¤t_time ) ; Sprint( dump_fd, "INTERNAL STATE DUMP: %s\n", program_version ) ; Sprint( dump_fd, "Current time: %s\n", ctime( ¤t_time ) ) ; dump_services( dump_fd ) ; /* * Dump the server table */ Sprint( dump_fd, "Server table dump:\n" ) ; for ( u = 0 ; u < pset_count( SERVERS( ps ) ) ; u++ ) server_dump( SERP( pset_pointer( SERVERS( ps ), u ) ), dump_fd ) ; Sputchar( dump_fd, '\n' ) ; /* * Dump the retry_table */ Sprint( dump_fd, "Retry table dump:\n" ) ; for ( u = 0 ; u < pset_count( RETRIES( ps ) ) ; u++ ) server_dump( SERP( pset_pointer( RETRIES( ps ), u ) ), dump_fd ) ; Sputchar( dump_fd, '\n' ) ; #ifdef HAVE_POLL /* * Dump the socket mask */ listed_fds = (int *)calloc(sizeof(int),ps.ros.max_descriptors); if (listed_fds != NULL) { Sprint( dump_fd, "Socket mask:" ) ; for ( fd = 0 ; fd < ps.rws.pfds_last ; fd++ ) { listed_fds[ps.rws.pfd_array[fd].fd] = 1; Sprint( dump_fd, " %d", ps.rws.pfd_array[fd].fd ) ; } Sputchar( dump_fd, '\n' ) ; Sprint( dump_fd, "pfds_last = %d\n", ps.rws.pfds_last ) ; /* * Dump the descriptors that are open and are *not* in the socket list */ Sprint( dump_fd, "Open descriptors (not in socket mask):" ) ; for ( fd = 0 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ ) { struct stat st ; if ( !listed_fds[fd] && fstat( fd, &st ) != -1 ) Sprint( dump_fd, " %d", fd ) ; } Sputchar( dump_fd, '\n' ) ; Sputchar( dump_fd, '\n' ) ; free(listed_fds); } else Sprint( dump_fd, "Could not dump open descriptors, not enough memory!\n" ); #else /* !HAVE_POLL */ /* * Dump the socket mask */ Sprint( dump_fd, "Socket mask:" ) ; for ( fd = 0 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ ) if ( FD_ISSET( fd, &ps.rws.socket_mask ) ) Sprint( dump_fd, " %d", fd ) ; Sputchar( dump_fd, '\n' ) ; Sprint( dump_fd, "mask_max = %d\n", ps.rws.mask_max ) ; /* * Dump the descriptors that are open and are *not* in the socket mask */ Sprint( dump_fd, "Open descriptors (not in socket mask):" ) ; for ( fd = 0 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ ) { struct stat st ; if ( FD_ISSET( fd, &ps.rws.socket_mask ) ) continue ; if ( fstat( fd, &st ) == -1 ) continue ; Sprint( dump_fd, " %d", fd ) ; } Sputchar( dump_fd, '\n' ) ; Sputchar( dump_fd, '\n' ) ; #endif /* !HAVE_POLL */ Sprint( dump_fd, "active_services = %d\n", ps.rws.active_services ) ; Sprint( dump_fd, "available_services = %d\n", ps.rws.available_services ) ; Sprint( dump_fd, "descriptors_free = %d\n", ps.rws.descriptors_free ) ; Sprint( dump_fd, "running_servers = %d\n", pset_count( SERVERS( ps ) ) ) ; Sprint( dump_fd, "Logging service = %s\n", LOG_SERVICE( ps ) != NULL ? "enabled" : "not enabled" ) ; Sputchar( dump_fd, '\n' ) ; Sprint( dump_fd, "max_descriptors = %d\n", (int)ps.ros.max_descriptors ) ; Sprint( dump_fd, "process_limit = %d\n", (int)ps.ros.process_limit ) ; Sprint( dump_fd, "config_file = %s\n", ps.ros.config_file ) ; if ( debug.on ) Sprint( dump_fd, "debug_fd = %d\n", debug.fd ) ; Sputchar( dump_fd, '\n' ) ; Sprint( dump_fd, "END OF DUMP\n\n" ) ; Sclose( dump_fd ); msg( LOG_INFO, func, "generated state dump in file %s", dump_file ) ; } /* * Types of consistency checks */ enum check_type { PERIODIC, USER_REQUESTED } ; static void consistency_check( enum check_type type ) { int fd ; unsigned u ; int errors ; unsigned total_running_servers = 0 ; unsigned total_retry_servers = 0 ; unsigned error_count = 0 ; bool_int service_count_check_failed = FALSE ; const char *func = "consistency_check" ; #ifdef HAVE_POLL struct pollfd *pfd_array_copy = calloc(sizeof(struct pollfd), ps.rws.pfds_last); if (pfd_array_copy == NULL) { msg( LOG_ERR, func, "Could not run consistency check! Not enough memory!\n" ) ; return; } memcpy(pfd_array_copy, ps.rws.pfd_array, ps.rws.pfds_last*sizeof(struct pollfd)); #else /* !HAVE_POLL */ fd_set socket_mask_copy ; socket_mask_copy = ps.rws.socket_mask ; #endif /* HAVE_POLL */ for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { register struct service *sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; char *sid = SVC_ID( sp ) ; unsigned running_servers ; unsigned retry_servers ; error_count += refcount_check( sp, &running_servers, &retry_servers ) ; if ( SVC_IS_AVAILABLE( sp ) || SVC_IS_DISABLED ( sp ) ) { /* * In this case, there may be some servers running */ #ifdef HAVE_POLL if ( pfd_array_copy[ SVC_POLLFD_OFF( sp ) ].events ) { if ( SVC_IS_DISABLED( sp ) ) { msg( LOG_ERR, func, "fd of disabled service %s still in socket mask", sid ) ; error_count++ ; } pfd_array_copy[ SVC_POLLFD_OFF( sp ) ].events = 0; } #else /* !HAVE_POLL */ if ( FD_ISSET( SVC_FD( sp ), &socket_mask_copy ) ) { if ( SVC_IS_DISABLED( sp ) ) { msg( LOG_ERR, func, "fd of disabled service %s still in socket mask", sid ) ; error_count++ ; } FD_CLR( SVC_FD( sp ), &socket_mask_copy ) ; } #endif /* HAVE_POLL */ error_count += thread_check( sp, running_servers, retry_servers ) ; errors = service_count_check( sp, running_servers, retry_servers ) ; if ( ! errors && ! service_count_check_failed ) { total_retry_servers += retry_servers ; total_running_servers += running_servers ; } if ( errors ) { service_count_check_failed = TRUE ; error_count += errors ; } if ( SVC_IS_DISABLED( sp ) && SVC_RUNNING_SERVERS( sp ) == 0 ) { msg( LOG_ERR, func, "disabled service %s has 0 running servers\n", sid ) ; error_count++ ; continue ; } } /* TCPMUX client programs are always stopped until they run. */ else if ( ! SVC_IS_MUXCLIENT( sp ) ) { msg( LOG_ERR, func, "service %s not started", SVC_ID( sp ) ) ; error_count++ ; } } if ( ! service_count_check_failed ) { if ( total_running_servers != pset_count( SERVERS( ps ) ) ) { msg( LOG_ERR, func, "total running servers (%d) != number of running servers (%d)", total_running_servers, pset_count( SERVERS( ps ) ) ) ; error_count++ ; } if ( total_retry_servers != pset_count( RETRIES( ps ) ) ) { msg( LOG_ERR, func, "total retry servers (%d) != number of retry servers (%d)", total_retry_servers, pset_count( RETRIES( ps ) ) ) ; error_count++ ; } } /* * Check if there are any descriptors set in socket_mask_copy */ #ifdef HAVE_POLL for ( fd = 0 ; fd < ps.rws.pfds_last ; fd++) if ( pfd_array_copy[fd].events && pfd_array_copy[fd].fd != signals_pending[0] && pfd_array_copy[fd].fd != signals_pending[1] ) { msg( LOG_ERR, func, "descriptor %d set in socket mask but there is no service for it", fd ) ; error_count++ ; } free(pfd_array_copy); #else /* !HAVE_POLL */ for ( fd = 0 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ ) if ( FD_ISSET( fd, &socket_mask_copy ) && ((fd != signals_pending[0]) && fd != signals_pending[1])) { msg( LOG_ERR, func, "descriptor %d set in socket mask but there is no service for it", fd ) ; error_count++ ; } #endif /* !HAVE_POLL */ if ( error_count != 0 ) msg( LOG_WARNING, func, "Consistency check detected %d errors", error_count ) ; else if ( type == USER_REQUESTED || debug.on ) msg( LOG_INFO, func, "Consistency check passed" ) ; if( type == PERIODIC ) if ( xtimer_add( periodic_check, ps.ros.cc_interval ) == -1 ) msg( LOG_ERR, func, "Failed to start consistency timer" ) ; } /* * Check that the counts of running and retry servers stored in struct service * are accurate */ static unsigned service_count_check( struct service *sp, unsigned running_servers, unsigned retry_servers ) { char *sid = SVC_ID( sp ) ; int error_count = 0 ; const char *func = "service_count_check" ; if ( SVC_RUNNING_SERVERS( sp ) != running_servers ) { msg( LOG_ERR, func, "service %s: actual running servers = %d, known running servers = %d", sid, running_servers, SVC_RUNNING_SERVERS( sp ) ) ; error_count++ ; } if ( SVC_RETRIES( sp ) != retry_servers ) { msg( LOG_ERR, func, "service %s: actual retry servers = %d, known retry servers = %d", sid, retry_servers, SVC_RETRIES( sp ) ) ; error_count++ ; } if ( error_count && debug.on ) msg( LOG_DEBUG, func, "%s: %d errors detected", sid, error_count ) ; return( error_count ) ; } /* * If the service is single-threaded: * if the descriptor is set in the socket mask, there must * be a server running (or to be retried) * If the service is multi-threaded: * the descriptor must be always set */ static unsigned thread_check( struct service *sp, unsigned running_servers, unsigned retry_servers ) { unsigned error_count = 0 ; #ifdef HAVE_POLL struct pollfd *pfd= SVC_POLLFD( sp ) ; #else int sd = SVC_FD( sp ) ; #endif char *sid = SVC_ID( sp ) ; const char *func = "thread_check" ; if ( SVC_WAITS( sp ) ) { bool_int has_servers = ( running_servers + retry_servers != 0 ) ; #ifdef HAVE_POLL if ( has_servers && pfd->events ) #else if ( has_servers && FD_ISSET( sd, &ps.rws.socket_mask ) ) #endif { msg( LOG_ERR, func, "Active single-threaded service %s: server running, descriptor set", sid ) ; error_count++ ; } #ifdef HAVE_POLL if ( !has_servers && !pfd->events ) #else if ( !has_servers && !FD_ISSET( sd, &ps.rws.socket_mask ) ) #endif { msg( LOG_ERR, func, "Active single-threaded service %s: no server running, descriptor not set", sid ) ; error_count++ ; } } else #ifdef HAVE_POLL if ( ! pfd->events ) #else if ( ! FD_ISSET( sd, &ps.rws.socket_mask ) ) #endif { msg( LOG_ERR, func, "Active multi-threaded service %s: descriptor not set", sid ) ; error_count++ ; } if ( error_count && debug.on ) msg( LOG_DEBUG, func, "%s: %d errors detected", sid, error_count ) ; return( error_count ) ; } /* * Count the number of references to the specified service contained * in the specified table of servers; put the number of servers * in *countp */ static int count_refs( struct service *sp, pset_h servers, unsigned *countp ) { unsigned u ; struct server *serp ; int refs = 0 ; unsigned count = 0 ; for ( u = 0 ; u < pset_count( servers ) ; u++ ) { serp = SERP( pset_pointer( SERVERS( ps ), u ) ) ; if ( SERVER_SERVICE( serp ) == sp ) { refs++ ; count++ ; } if ( SERVER_CONNSERVICE( serp ) == sp ) refs++ ; /* * XXX: in the future we may want to check if the given service * is any of the alternative services (currently only SPECIAL * services can be alternative services and SPECIAL services * are not included in the service table) */ } *countp = count ; return( refs ) ; } /* * Check for reference counting errors. * Returns number of errors found. * Always set the number of running and retry servers. */ static unsigned refcount_check( struct service *sp, unsigned *running_servers, unsigned *retry_servers ) { char *sid = SVC_ID( sp ) ; unsigned errors = 0 ; int refs ; int refcount = SVC_REFCOUNT( sp ) ; const char *func = "refcount_check" ; if ( refcount <= 0 ) { msg( LOG_ERR, func, "%s service has bad refcount: %d", sid, refcount ) ; errors++ ; } /* * The service table holds a reference to the service. The remaining * references must be from servers and connections. */ refcount-- ; refs = count_refs( sp, SERVERS( ps ), running_servers ) ; if ( ! errors && refs > refcount ) { msg( LOG_ERR, func, "running servers: too many references for %s (%d with max=%d)", sid, refs, refcount ) ; errors++ ; } refs = count_refs( sp, RETRIES( ps ), retry_servers ) ; if ( ! errors && refs > refcount ) { msg( LOG_ERR, func, "retry servers: too many references for %s (%d with max=%d)", sid, refs, refcount ) ; errors++ ; } if ( errors && debug.on ) msg( LOG_DEBUG, func, "%s: %d errors detected", sid, errors ) ; return( errors ) ; } void user_requested_check(void) { consistency_check( USER_REQUESTED ) ; } static void periodic_check(void) { consistency_check( PERIODIC ) ; } /* This actually gets called during initialization, so be careful what * gets put in here. */ void enable_periodic_check( unsigned interval ) { const char *func = "enable_periodic_check" ; if ( xtimer_add( periodic_check, interval ) == -1 ) { msg( LOG_ERR, func, "Failed to start consistency timer" ) ; return ; } } xinetd-2.3.15.4/src/internals.h000066400000000000000000000003001333055217000161660ustar00rootroot00000000000000#ifndef INTERNALS_H #define INTERNALS_H #include "config.h" #include "defs.h" void dump_internal_state(void); void user_requested_check(void); void enable_periodic_check( unsigned ); #endif xinetd-2.3.15.4/src/itox.c000066400000000000000000000146531333055217000151650ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #define EQ( s1, s2 ) ( strcmp( s1, s2 ) == 0 ) #define NUL '\0' #define static static #define FIELD_WIDTH 15 #define DAEMON_DIR_OPTION "-daemon_dir" #define TCPD_NAME "tcpd" #include #include #include "sio.h" #include "str.h" #include str_h strp ; int line_count ; static void print_line( const char *name, const char *value ); static char *next_word( const char *description ); static char *make_string_cat( register unsigned count, ... ); static char *make_pathname( register unsigned count, ... ); /* * This program works only as a filter. * Options: * -daemon_dir : if you use tcpd, this option specifies the * directory where all the daemons are. * You must specify this option if you use tcpd * * Note that we don't bother to free the memory we malloc. */ int main(int argc, char *argv[] ) { char *s ; int uses_tcpd ; char *daemon_dirpath = NULL ; if ( argc != 1 && argc != 3 ) { Sprint( 2, "Usage: %s [%s dir_path]\n", basename( argv[ 0 ] ), DAEMON_DIR_OPTION ) ; exit( 1 ) ; } uses_tcpd = ( argc == 3 ) ; if ( uses_tcpd ) { int len ; daemon_dirpath = argv[ 2 ] ; len = strlen( daemon_dirpath ) ; if ( daemon_dirpath[ len-1 ] == '/' ) daemon_dirpath[ --len ] = NUL ; } strp = str_parse( (char *)0, " \t", STR_NOFLAGS, (int *)0 ) ; while ( (s = Srdline( 0 )) ) { char *word ; char *p ; char *socket_type, *protocol ; char *service ; int is_rpc ; line_count++ ; if ( SIOLINELEN( 0 ) == 0 || s[ 0 ] == '#' ) continue ; str_setstr( strp, s ) ; service = word = next_word( "service name" ) ; /* * Check if it is an RPC service */ p = strchr( word, '/' ) ; if ( p != NULL ) *p = 0 ; Sprint( 1, "service %s\n{\n", word ) ; if ( (is_rpc = ( p != NULL )) ) { print_line( "type", "RPC" ) ; print_line( "rpc_version", p+1 ) ; } socket_type = word = next_word( "socket type" ) ; print_line( "socket_type", socket_type ) ; word = next_word( "protocol" ) ; p = strchr( word, '/' ) ; protocol = ( p == NULL ) ? word : p+1 ; print_line( "protocol", protocol ) ; word = next_word( "wait/nowait" ) ; p = strchr(word, '.'); if (p != NULL) { Sprint( 2, "The entry for service %s/%s may be wrong, because\n", service, protocol); Sprint( 2, "we can't convert .max option for wait/nowait field\n"); *p = '\0'; print_line( "wait", EQ( word, "wait" ) ? "yes" : "no" ); } else print_line( "wait", EQ( word, "wait" ) ? "yes" : "no" ); word = next_word( "user[.group]" ) ; p = strchr(word, '.'); if (p != NULL) { *p = '\0'; print_line( "user", word ) ; word = ++p; print_line( "group", word ); } else print_line( "user", word ) ; word = next_word( "server" ) ; if ( EQ( word, "internal" ) ) { /* * We are in trouble if this is an RPC service */ if ( is_rpc ) { Sprint( 2, "The entry for service %s will be wrong because\n", service ) ; Sprint( 2, "we can't handle internal RPC services\n" ) ; } else { print_line( "type", "INTERNAL" ) ; print_line( "id", make_string_cat( 3, service, "-", socket_type ) ) ; } } else { char *server_path = word ; /* from inetd.conf */ char *server_of_server_path = basename( server_path ) ; char *server_name = next_word( "server name" ) ; char *server ; /* for xinetd config file */ if ( EQ( server_of_server_path, TCPD_NAME ) ) { if ( ! uses_tcpd ) { Sprint( 2, "You must use option %s if you use %s\n", DAEMON_DIR_OPTION, TCPD_NAME ) ; exit( 1 ) ; } if ( server_name[ 0 ] == '/' ) server = server_name ; else server = make_pathname( 2, daemon_dirpath, server_name ) ; } else server = server_path ; print_line( "server", server ) ; word = str_component( strp ) ; /* 1st arg */ if ( word != NULL ) { Sprint( 1, "\t%-*s = %s", FIELD_WIDTH, "server_args", word ) ; while ( (word = str_component( strp )) ) Sprint( 1, " %s", word ) ; Sputchar( 1, '\n' ) ; } } Sprint( 1, "}\n\n" ) ; } Sflush( 1 ) ; exit( 0 ) ; } static void print_line( const char *name, const char *value ) { Sprint( 1, "\t%-*s = %s\n", FIELD_WIDTH, name, value ) ; } static char *next_word( const char *description ) { char *word = str_component( strp ) ; if ( word == NULL ) { Sprint( 2, "Line %d: %s missing \n", line_count, description ) ; exit( 1 ) ; } return( word ) ; } static char *make_string_cat( const unsigned count, ... ) { va_list ap ; register unsigned i ; register unsigned len = 0 ; register char *s, *p ; char *newstring ; if ( count == 0 ) return( NULL ) ; va_start( ap, count ) ; if (count == 1) { /* 9 out of 10 have just 1, so this optimizes it */ s = va_arg( ap, char * ) ; va_end( ap ); if ( s == NULL ) return strdup(""); else return strdup(s); } for ( i = 0 ; i < count ; i++ ) { s = va_arg( ap, char * ) ; if ( s == NULL ) continue ; len += strlen( s ) ; } va_end( ap ) ; newstring = (char *)malloc( len + 1 ) ; if ( newstring == NULL ) return( NULL ) ; p = newstring ; va_start( ap, count ) ; for ( i = 0 ; i < count ; i++ ) { s = va_arg( ap, char * ) ; if ( s == NULL ) continue ; while ( (*p++ = *s++) ) ; p-- ; } va_end( ap ) ; newstring[len] = 0; /* if len == 0, must terminate or boom! */ return newstring ; } static char *make_pathname( const unsigned count, ... ) { va_list ap ; register unsigned i ; register unsigned len = 0 ; register char *s, *p ; char *pathname ; if ( count == 0 ) return( NULL ) ; va_start( ap, count ) ; for ( i = 0 ; i < count ; i++ ) { s = va_arg( ap, char * ) ; len += strlen( s ) ; } va_end( ap ) ; pathname = (char *)malloc( len + count ) ; if ( pathname == NULL ) return( NULL ) ; p = pathname ; va_start( ap, count ) ; for ( i = 0 ; i < count ; i++ ) { s = va_arg( ap, char * ) ; while ( (*p++ = *s++) ) ; *(p-1) = '/' ; /* change '\0' to '/' */ } *(p-1) = '\0' ; va_end( ap ) ; return( pathname ) ; } xinetd-2.3.15.4/src/log.c000066400000000000000000000137341333055217000147620ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "str.h" #include "log.h" #include "sconf.h" #include "sconst.h" #include "msg.h" #define LOGBUF_SIZE 1024 static char ipv6_ret[NI_MAXHOST]; const char *xaddrname(const union xsockaddr *inaddr) { unsigned int len = 0; if( inaddr->sa.sa_family == AF_INET ) len = sizeof(struct sockaddr_in); if( inaddr->sa.sa_family == AF_INET6 ) len = sizeof(struct sockaddr_in6); memset(ipv6_ret, 0, sizeof(ipv6_ret)); if( getnameinfo(&inaddr->sa, len, ipv6_ret, sizeof(ipv6_ret), NULL, 0, NI_NUMERICHOST) ) strncpy(ipv6_ret, "", NI_MAXHOST); return ipv6_ret; } uint16_t xaddrport(const union xsockaddr *inaddr) { if( inaddr->sa.sa_family == AF_INET ) return inaddr->sa_in.sin_port; if( inaddr->sa.sa_family == AF_INET6 ) return inaddr->sa_in6.sin6_port; return 0; } static int log_common(mask_t *, char *, int, const connection_s *) ; /* * This function writes log records of the form: * * START: service [pid] [from_address] */ void svc_log_success( struct service *sp, const connection_s *cp, pid_t pid ) { char buf[ LOGBUF_SIZE ] ; int bufsize ; struct service_config *scp = SVC_CONF( sp ) ; int len ; int cc ; if ( ! SVC_LOGS_ON_SUCCESS( sp ) ) return ; bufsize = sizeof( buf ) ; len = 0 ; cc = strx_nprint( buf, bufsize, "%s: %s", START_ENTRY, SC_ID( scp ) ) ; len += cc ; bufsize -= cc ; if ( SC_LOGS_PID( scp ) ) { cc = strx_nprint( &buf[ len ], bufsize, " pid=%d", pid ) ; len += cc ; bufsize -= cc ; } cc = log_common( &SC_LOG_ON_SUCCESS( scp ), &buf[len], bufsize, cp ) ; len += cc ; bufsize -= cc ; xlog_write( SVC_LOG(sp), buf, len, XLOG_NO_ERRNO ) ; } /* * This function writes log records of the form: * * FAIL: service failure-type [from_address] * */ void svc_log_failure( struct service *sp, const connection_s *cp, access_e access_failure ) { char buf[ LOGBUF_SIZE ] ; int bufsize ; struct service_config *scp = SVC_CONF( sp ) ; int len = 0 ; int cc ; if ( ! SVC_LOGS_ON_FAILURE( sp ) ) return ; bufsize = sizeof( buf ) ; cc = strx_nprint( buf, bufsize, "%s: %s", FAIL_ENTRY, SC_ID( scp ) ) ; len += cc ; bufsize -= cc ; cc = strx_nprint( &buf[ len ], bufsize, " %s", ACCESS_EXPLAIN( access_failure ) ) ; len += cc ; bufsize -= cc ; cc = log_common( &SC_LOG_ON_FAILURE( scp ), &buf[ len ], bufsize, cp ) ; len += cc ; bufsize -= cc ; xlog_write( SVC_LOG(sp), buf, len, XLOG_NO_ERRNO ) ; } static int log_common( mask_t *logmask, char *buf, int bufsize, const connection_s *cp ) { int len = 0 ; if ( M_IS_SET( *logmask, LO_HOST ) ) len = strx_nprint( buf, bufsize, " from=%s", conn_addrstr( cp ) ) ; return( len ) ; } void svc_log_exit( struct service *sp, const struct server *serp ) { char buf[ LOGBUF_SIZE ] ; int bufsize ; int cc ; int len ; int exit_status = SERVER_EXITSTATUS( serp ) ; struct service_config *scp = SVC_CONF( sp ) ; const char *func = "log_exit" ; if ( ! SVC_LOGS_ON_EXIT( sp ) ) return ; bufsize = sizeof( buf ) ; len = 0 ; cc = strx_nprint( buf, bufsize, "%s: %s", EXIT_ENTRY, SC_ID( scp ) ) ; bufsize -= cc ; len += cc ; /* * If the EXIT flag was used, log the exit status or the signal that * killed the process. We assume that these are the only reasons * for process termination. */ if ( SC_LOGS_EXITS( scp ) ) { int num = 0; const char *s ; if ( PROC_EXITED( exit_status ) ) { s = "status" ; num = PROC_EXITSTATUS( exit_status ) ; } else if ( PROC_SIGNALED( exit_status ) ) { s = "signal" ; num = PROC_TERMSIG( exit_status ) ; } else { msg( LOG_ERR, func, "Bad exit status" ) ; s = NULL ; } if ( s ) { cc = strx_nprint( &buf[ len ], bufsize, " %s=%d", s, num ) ; len += cc ; bufsize -= cc ; } } if ( SC_LOGS_PID( scp ) ) { cc = strx_nprint( &buf[ len ], bufsize, " pid=%d", SERVER_PID( serp ) ) ; len += cc ; bufsize -= cc ; } if ( SC_LOGS_DURATION( scp ) ) { time_t current_time ; (void) time( ¤t_time ) ; cc = strx_nprint( &buf[ len ], bufsize, " duration=%ld(sec)", (long)(current_time - SERVER_STARTTIME( serp )) ) ; len += cc ; bufsize -= cc ; } xlog_write( SVC_LOG(sp), buf, len, XLOG_NO_ERRNO ) ; } /* * Used by other parts of xinetd that want to log something without * going through the proper channels (i.e. log_{success,failure} and log_exit) */ /* VARARGS3 */ void svc_logprint( struct service *sp, const char *line_id, const char *fmt, ...) { char buf[ LOGBUF_SIZE ] ; int bufsize = sizeof( buf ) ; int len ; int cc ; va_list ap ; if ( ! SVC_IS_LOGGING( sp ) ) return ; len = strx_nprint( buf, bufsize, "%s: %s ", line_id, SVC_ID( sp ) ) ; va_start( ap, fmt ) ; cc = strx_nprintv( &buf[ len ], bufsize-len, fmt, ap ) ; va_end( ap ) ; xlog_write( SVC_LOG(sp), buf, len+cc, XLOG_NO_ERRNO | XLOG_NO_SIZECHECK ) ; } xinetd-2.3.15.4/src/log.h000066400000000000000000000033251333055217000147620ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef LOG_H #define LOG_H #include #include "defs.h" #include "access.h" /* * $Id$ */ /* * Meaning of logtype flags: * * L_NONE: no logging * L_FILE: log output goes to a file * L_SYSLOG: log output goes to syslog(3) * L_COMMON_FILE: log output goes to the file specified in defaults */ typedef enum { L_NONE = 0, L_FILE, L_SYSLOG, L_COMMON_FILE } logtype_e ; struct filelog { char *fl_filename ; /* always malloc'ed */ unsigned fl_soft_limit ; unsigned fl_hard_limit ; } ; #define FILELOG_SIZE_CONTROL( flp ) ( flp->fl_soft_limit != 0 ) struct syslog { int sl_facility ; int sl_level ; } ; struct log { logtype_e l_type ; struct filelog l_fl ; struct syslog l_sl ; } ; #define LOG_GET_TYPE( lp ) (lp)->l_type #define LOG_SET_TYPE( lp, type ) (lp)->l_type = (type) #define LOG_GET_FILELOG( lp ) (&(lp)->l_fl) #define LOG_GET_SYSLOG( lp ) (&(lp)->l_sl) const char *xaddrname(const union xsockaddr *inaddr); uint16_t xaddrport(const union xsockaddr *inaddr); void svc_log_success(struct service *sp, const connection_s *cp,pid_t pid); void svc_log_failure(struct service *sp, const connection_s *cp,access_e access_failure); void svc_log_exit(struct service *sp,const struct server *serp); void svc_logprint(struct service *sp,const char *line_id,const char *fmt,...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))); #else ; #endif #endif /* LOG_H */ xinetd-2.3.15.4/src/logctl.c000066400000000000000000000111571333055217000154620ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include "logctl.h" #include "msg.h" #include "xconfig.h" #include "main.h" #include "sconf.h" static xlog_h start_filelog( const char *id, struct filelog *flp ) { xlog_h xh ; int fd ; int log_file_mode = ( debug.on ) ? 0644 : LOG_FILE_MODE ; const char *func = "start_filelog" ; xh = xlog_create( XLOG_FILELOG, id, XLOG_NOFLAGS, flp->fl_filename, LOG_OPEN_FLAGS, log_file_mode ) ; if ( xh == NULL ) { msg( LOG_ERR, func, "creation of %s log failed", id ) ; return( NULL ) ; } if ( xlog_control( xh, XLOG_GETFD, &fd ) != XLOG_ENOERROR || fcntl( fd, F_SETFD, FD_CLOEXEC ) == -1 ) { msg( LOG_ERR, func, "Failed to set close-on-exec flag for log file" ) ; xlog_destroy( xh ) ; return( NULL ) ; } ps.rws.descriptors_free-- ; if ( FILELOG_SIZE_CONTROL( flp ) ) (void) xlog_control( xh, XLOG_LIMITS, flp->fl_soft_limit, flp->fl_hard_limit ) ; return( xh ) ; } /* * This function is invoked when a xlog detects an error (for example, * exceeding the file size limit). * The function just enters a log message. * * NOTE: We could destroy the xlog at this point but we choose not to. */ static void log_in_error( xlog_h xh, int error_code, void *arg ) { struct service *sp = SP( arg ) ; const char *log_id = ( sp == NULL ) ? "common" : SVC_ID( sp ) ; const char *func = "log_in_error" ; #ifdef lint xh = xh ; #endif if ( error_code == XLOG_ESIZE ) msg( LOG_ERR, func, "Size of %s log exceeded hard limit", log_id ) ; else msg( LOG_ERR, func, "Error in %s log: %d", log_id, error_code ) ; } /* * Start logging for the specified service. * The current configuration is used to determine the common log file. */ status_e log_start( struct service *sp, xlog_h *xhp ) { xlog_h xh ; const char *sid = SVC_ID( sp ) ; struct log *lp = SC_LOG( SVC_CONF( sp ) ) ; const char *func = "log_start" ; switch ( lp->l_type ) { case L_NONE: xh = NULL ; break ; case L_SYSLOG: xh = xlog_create( XLOG_SYSLOG, sid, XLOG_NOFLAGS, LOG_GET_SYSLOG( lp )->sl_facility, LOG_GET_SYSLOG( lp )->sl_level ) ; if ( xh == NULL ) { msg( LOG_ERR, func, "failed to create a log for service %s", sid ) ; return( FAILED ) ; } xlog_control( xh, XLOG_CALLBACK, log_in_error, (void *)sp ) ; break ; case L_FILE: /* * NOTE: if the same file is specified for more than one service, * it will be opened as many times. * Furthermore, size control will not be accurate. */ xh = start_filelog( sid, LOG_GET_FILELOG( lp ) ) ; if ( xh == NULL ) return( FAILED ) ; (void) xlog_control( xh, XLOG_CALLBACK, log_in_error, (void *)sp ) ; break ; case L_COMMON_FILE: if ( DEFAULT_LOG( ps ) == NULL ) if ( DEFAULT_LOG_ERROR( ps ) ) return( FAILED ) ; else { xh = start_filelog( "default", LOG_GET_FILELOG( SC_LOG( DEFAULTS( ps ) ) ) ) ; if ( xh == NULL ) { DEFAULT_LOG_ERROR( ps ) = TRUE ; return( FAILED ) ; } DEFAULT_LOG( ps ) = xh ; (void) xlog_control( xh, XLOG_CALLBACK, log_in_error, VOID_NULL ) ; } else xh = DEFAULT_LOG( ps ) ; break ; default: /* SHOULDN'T HAPPEN */ msg( LOG_ERR, func, "bad log type (%d) for service %s", (int) LOG_GET_TYPE( lp ), sid ) ; return( FAILED ) ; } *xhp = xh ; return( OK ) ; } void log_end( struct log *lp, xlog_h xh ) { const char *func = "log_end" ; if ( xh == NULL ) /* shouldn't be NULL but just in case */ { msg( LOG_NOTICE, func, "called with NULL handle" ) ; return ; } switch ( LOG_GET_TYPE( lp ) ) { case L_FILE: ps.rws.descriptors_free++ ; /* FALL THROUGH */ case L_SYSLOG: xlog_destroy( xh ) ; case L_NONE: case L_COMMON_FILE: ; } } xinetd-2.3.15.4/src/logctl.h000066400000000000000000000002761333055217000154670ustar00rootroot00000000000000#ifndef LOGCTL_H #define LOGCTL_H #include "defs.h" #include "xlog.h" #include "log.h" status_e log_start(struct service *sp,xlog_h *xhp); void log_end(struct log *lp,xlog_h xh); #endif xinetd-2.3.15.4/src/main.c000066400000000000000000000214451333055217000151230ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include "main.h" #include "init.h" #include "msg.h" #include "internals.h" #include "signals.h" #include "service.h" #include "sconf.h" #include "xtimer.h" #include "sensor.h" #ifdef HAVE_POLL #include "xpoll.h" #endif #ifdef __GNUC__ __attribute__ ((noreturn)) #endif static void main_loop(void); static void find_bad_fd(void) ; /* * The following are the only global variables of this program */ struct program_state ps ; struct debug debug ; char program_version[] = VERSION ; int signals_pending[2] = {-1, -1} ; /* * This is where the story starts... */ int main( int argc, char *argv[] ) { const char *func = "main" ; init_daemon( argc, argv ) ; init_services() ; /* Do the chdir after reading the config file. Relative path names * will work better. */ if (chdir("/") < 0) { msg(LOG_ERR, func, "Can't chdir to /: %m"); } /* Print out all the options we're compiled with. Makes support * a tad easier. * Also, try to get them all into one syslog message for atomicity */ msg( LOG_NOTICE, func, "%s started with " #ifdef LIBWRAP "libwrap " #endif #ifdef HAVE_LOADAVG "loadavg " #endif #ifdef LABELED_NET "labeled-networking " #endif #if !defined(LIBWRAP) && !defined(HAVE_LOADAVG) && !defined(LABELED_NET) "no " #endif "options compiled in." , VERSION ); msg( LOG_NOTICE, func, "Started working: %d available service%s", ps.rws.available_services, ( ps.rws.available_services != 1 ) ? "s" : "" ) ; /* * The reason for doing the setjmp here instead of in main_loop is * that setjmp is not guaranteed to restore register values which * can cause a problem for register variables */ if ( sigsetjmp( ps.rws.env, 1 ) == 0 ) ps.rws.env_is_valid = TRUE ; main_loop() ; /* NOTREACHED */ exit(1); } /* * What main_loop does: * * select on all active services * for each socket where a request is pending * try to start a server */ static void main_loop(void) { const char *func = "main_loop" ; struct timeval tv, *tvptr = NULL; #ifdef HAVE_POLL struct pollfd *signal_pfd; ps.rws.pfd_array[ps.rws.pfds_last].fd = signals_pending[0] ; ps.rws.pfd_array[ps.rws.pfds_last].events = POLLIN ; signal_pfd = &ps.rws.pfd_array[ps.rws.pfds_last] ; ps.rws.pfds_last++; #else FD_SET(signals_pending[0], &ps.rws.socket_mask) ; if ( signals_pending[0] > ps.rws.mask_max ) ps.rws.mask_max = signals_pending[0] ; #endif /* HAVE_POLL */ for ( ;; ) { #ifndef HAVE_POLL fd_set read_mask ; #endif int n_active ; unsigned u ; if ( debug.on ) msg( LOG_DEBUG, func, "active_services = %d", ps.rws.active_services ) ; /* get the next timer value, if there is one, and select for that time */ if( (tv.tv_sec = xtimer_nexttime()) >= 0 ) { tv.tv_usec = 0; tvptr = &tv; } else { tvptr = NULL; } #ifdef HAVE_POLL n_active = poll( ps.rws.pfd_array, ps.rws.pfds_last, tvptr == NULL ? -1 : tvptr->tv_sec*1000 ) ; #else read_mask = ps.rws.socket_mask ; n_active = select( ps.rws.mask_max+1, &read_mask, FD_SET_NULL, FD_SET_NULL, tvptr ) ; #endif if ( n_active == -1 ) { if ( errno == EINTR ) { continue ; } else if ( errno == EBADF ) find_bad_fd() ; continue ; } else if ( n_active == 0 ) { xtimer_poll(); continue ; } if ( debug.on ) msg( LOG_DEBUG, func, "select returned %d", n_active ) ; xtimer_poll(); #ifdef HAVE_POLL if ( POLLFD_REVENTS( signal_pfd ) ) { if ( POLLFD_REVENTS( signal_pfd ) & (POLLERR | POLLHUP | POLLNVAL) ) find_bad_fd(); else { check_pipe(); if ( --n_active == 0 ) continue ; } } #else if( FD_ISSET(signals_pending[0], &read_mask) ) { check_pipe(); if ( --n_active == 0 ) continue ; } #endif for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { struct service *sp ; sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( ! SVC_IS_ACTIVE( sp ) ) continue ; #ifdef HAVE_POLL if ( SVC_REVENTS( sp ) ) { if ( SVC_REVENTS( sp ) & (POLLERR | POLLHUP | POLLNVAL) ) find_bad_fd(); else { svc_request( sp ) ; if ( --n_active == 0 ) break ; } } #else if ( FD_ISSET( SVC_FD( sp ), &read_mask ) ) { svc_request( sp ) ; if ( --n_active == 0 ) break ; } #endif } if ( n_active > 0 ) msg( LOG_ERR, func, "%d descriptors still set", n_active ) ; } } /* * This function identifies if any of the fd's in the socket mask * is bad. We use it in case select(2) returns EBADF * When we identify such a bad fd, we remove it from the mask * and deactivate the service. */ static void find_bad_fd(void) { int fd ; #ifndef HAVE_POLL struct stat st ; #endif unsigned bad_fd_count = 0 ; const char *func = "find_bad_fd" ; #ifdef HAVE_POLL for ( fd = 0 ; fd < ps.rws.pfds_last ; fd++ ) if ( ps.rws.pfd_array[fd].revents & ( POLLHUP|POLLNVAL|POLLERR ) ) { #else for ( fd = 0 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ ) if ( FD_ISSET( fd, &ps.rws.socket_mask ) && fstat( fd, &st ) == -1 ) { #endif int found = FALSE ; unsigned u ; for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { register struct service *sp ; sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( ! SVC_IS_AVAILABLE( sp ) ) continue ; if ( SVC_FD( sp ) == fd ) { msg( LOG_ERR, func, "file descriptor of service %s has been closed", SVC_ID( sp ) ) ; svc_deactivate( sp ) ; found = TRUE ; bad_fd_count++ ; break ; } } if ( ! found ) { #ifdef HAVE_POLL ps.rws.pfd_array[fd].events = 0; ps.rws.pfd_array[fd].fd = -1; #else FD_CLR( fd, &ps.rws.socket_mask ) ; #endif msg( LOG_ERR, func, "No active service for file descriptor %d\n", fd ) ; bad_fd_count++ ; } } if ( bad_fd_count == 0 ) msg( LOG_NOTICE, func, "select reported EBADF but no bad file descriptors were found" ) ; } /* * Deactivates all active processes. * The real reason for doing this instead of just exiting is * to deregister the RPC services */ void quit_program(void) { unsigned u ; struct service_config *scp = NULL; const char *func = "quit_program" ; destroy_global_access_list() ; for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { scp = SVC_CONF( SP(pset_pointer(SERVICES(ps), u)) ); /* This is essentially the same as the following function, * Except we forcibly deactivate them, rather than just * send signals. */ if( SC_IS_INTERNAL( scp ) ) svc_deactivate( SP( pset_pointer( SERVICES( ps ), u ) ) ) ; if( SC_REDIR_ADDR(scp) != NULL ) svc_deactivate( SP( pset_pointer( SERVICES( ps ), u ) ) ) ; if( SC_IS_RPC( scp ) ) svc_deactivate( SP( pset_pointer( SERVICES( ps ), u ) ) ) ; } if ( ps.ros.pid_file ) { unlink(ps.ros.pid_file); } msg( LOG_WARNING, func, "Exiting..." ) ; exit( 0 ) ; } void terminate_program(void) { unsigned u ; struct service_config *scp = NULL; void terminate_servers(struct service *); for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { scp = SVC_CONF( SP(pset_pointer(SERVICES(ps), u)) ); /* Terminate the service if it is: * 1) internal (if we don't, it'll zombie) * 2) a redirector (again, if we don't it'll zombie) * 3) It's RPC (we must deregister it. */ if( SC_IS_INTERNAL( scp ) ) terminate_servers( SP( pset_pointer( SERVICES( ps ), u ) ) ) ; if( SC_REDIR_ADDR( scp ) != NULL ) terminate_servers( SP( pset_pointer( SERVICES( ps ), u ) ) ) ; if( SC_IS_RPC( scp ) ) terminate_servers( SP( pset_pointer( SERVICES( ps ), u ) ) ) ; } quit_program() ; } xinetd-2.3.15.4/src/main.h000066400000000000000000000005061333055217000151230ustar00rootroot00000000000000#ifndef MAIN_H #define MAIN_H #include "state.h" #include "defs.h" extern char program_version[]; extern struct program_state ps; extern int signals_pending[2]; #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void quit_program(void); #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void terminate_program(void); #endif xinetd-2.3.15.4/src/mask.h000066400000000000000000000021571333055217000151360ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef MASK_H #define MASK_H /* * $Id$ */ #include "config.h" #ifdef HAVE_STDINT_H #include #endif /* * Macros about masks - Note: 64 bits is needed because of attr.h */ typedef uint64_t mask_t; #define MASK_NULL ((mask_t *)0) #define XMASK( v ) ( (mask_t)1 << ( (v)-1 ) ) #define M_CLEAR_ALL( mask ) (mask) = 0 #define M_ASSIGN( mask1, mask2 ) (mask1) = (mask2) #define M_ARE_ALL_CLEAR( mask ) ( (mask) == 0 ) #define M_SET( mask, v ) (mask) |= XMASK(v) #define M_CLEAR( mask, v ) (mask) &= ~XMASK(v) #define M_IS_SET( mask, v ) ( (mask) & XMASK(v) ) #define M_IS_CLEAR( mask, v ) ( ! M_IS_SET( mask, v ) ) #define M_AND( mres, m1, m2 ) ( (mres) = (m1) & (m2) ) #define M_OR( mres, m1, m2 ) ( (mres) = (m1) | (m2) ) #define M_XOR( mres, m1, m2 ) ( (mres) = (m1) ^ (m2) ) #endif /* MASK_H */ xinetd-2.3.15.4/src/misc/000077500000000000000000000000001333055217000147605ustar00rootroot00000000000000xinetd-2.3.15.4/src/misc/m_env.c000066400000000000000000000110351333055217000162300ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include "m_env.h" #include "str.h" typedef struct __env env_s ; #define INITIAL_VARS 20 #define INCREASE 10 int env_errno ; static char **lookup( const env_h env, const char *var, register unsigned int len ); static env_s *alloc_env( unsigned max_vars ) { env_s *ep ; unsigned size ; char **pointers ; ep = (env_s *) malloc( sizeof( env_s ) ) ; if ( ep == ENV_NULL ) { env_errno = ENV_ENOMEM ; return( ENV_NULL ) ; } memset( ep, 0, sizeof( env_s ) ); size = ( max_vars + 1 ) * sizeof( char * ) ; pointers = (char **) malloc( size ) ; if ( pointers == NULL ) { free( (char *)ep ) ; env_errno = ENV_ENOMEM ; return( ENV_NULL ) ; } (void) memset( (char *)pointers, 0, size ) ; ep->vars = pointers ; ep->max_vars = max_vars ; ep->n_vars = 0 ; return( ep ) ; } env_h env_create( const env_h init_env ) { unsigned u ; env_s *ep ; unsigned max_vars ; if ( init_env == ENV_NULL ) max_vars = INITIAL_VARS ; else max_vars = init_env->n_vars + 5 ; ep = alloc_env( max_vars ) ; if ( ep == NULL ) { env_errno = ENV_ENOMEM ; return( ENV_NULL ) ; } if ( init_env == ENV_NULL ) return( ep ) ; for ( u = 0, ep->n_vars = 0 ; u < init_env->n_vars ; u++, ep->n_vars++ ) { ep->vars[ ep->n_vars ] = new_string( init_env->vars[ u ] ) ; if ( ep->vars[ ep->n_vars ] == NULL ) { env_destroy( ep ) ; env_errno = ENV_ENOMEM ; return( ENV_NULL ) ; } } return( ep ) ; } void env_destroy( env_h env ) { unsigned u ; for ( u = 0 ; u < env->n_vars ; u++ ) free( env->vars[ u ] ) ; free( (char *)env->vars ) ; free( (char *)env ) ; } env_h env_make( char **env_strings ) { env_s *ep ; char **pp ; for ( pp = env_strings ; *pp ; pp++ ) ; ep = alloc_env( (unsigned) (pp-env_strings) ) ; if ( ep == NULL ) { env_errno = ENV_ENOMEM ; return( ENV_NULL ) ; } for ( pp = env_strings ; *pp ; pp++ ) { char *p = new_string( *pp ) ; if ( p == NULL ) { env_destroy( ep ) ; env_errno = ENV_ENOMEM ; return( ENV_NULL ) ; } ep->vars[ ep->n_vars++ ] = p ; } return( ep ) ; } char *env_lookup( env_h env, const char *var ) { char **pp = lookup( env, var, strlen( var ) ) ; return( ( pp == NULL ) ? NULL : *pp ) ; } static char **lookup( const env_h env, const char * var, register unsigned int len ) { char **pp ; for ( pp = env->vars ; *pp ; pp++ ) if ( strncmp( *pp, var, len ) == 0 && (*pp)[ len ] == '=' ) return( pp ) ; return( NULL ) ; } static int grow( env_s *ep ) { char **new_vars ; unsigned new_max_vars ; unsigned new_size ; new_max_vars = ep->max_vars + INCREASE ; new_size = ( new_max_vars+1 ) * sizeof( char * ) ; new_vars = (char **) realloc( (char *)ep->vars, new_size ) ; if ( new_vars == NULL ) return( ENV_ERR ) ; ep->vars = new_vars ; ep->max_vars = new_max_vars ; memset(&ep->vars[ep->n_vars], 0, new_size - (ep->n_vars * sizeof(char *))); return( ENV_OK ) ; } /* * Add the variable string to the given environment. */ static int addstring( env_s *ep, const char *var_string, unsigned int len ) { char **pp ; char *p ; p = new_string( var_string ) ; if ( p == NULL ) return( ENV_ERR ) ; pp = lookup( ep, var_string, len ) ; if ( pp == NULL ) { if ( ep->n_vars >= ep->max_vars && grow( ep ) == ENV_ERR ) { free( p ) ; env_errno = ENV_ENOMEM ; return( ENV_ERR ) ; } ep->vars[ ep->n_vars++ ] = p ; ep->vars[ ep->n_vars ] = NULL; } else { free( *pp ) ; *pp = p ; } return( ENV_OK ) ; } int env_addvar( env_h env, env_h from_env, char *var_name ) { char *var_string = env_lookup( from_env, var_name ) ; if ( var_string == NULL ) { env_errno = ENV_EBADVAR ; return( ENV_ERR ) ; } return( addstring( env, var_string, strlen( var_name ) ) ) ; } int env_addstr( env_h env, char *var_string ) { char *p = strchr( var_string, '=' ) ; if ( p == NULL ) { env_errno = ENV_EBADSTRING ; return( ENV_ERR ) ; } return( addstring( env, var_string, (unsigned int)(p-var_string) ) ) ; } xinetd-2.3.15.4/src/misc/m_env.h000066400000000000000000000016671333055217000162470ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef __M_ENV_H #define __M_ENV_H /* * $Id$ */ struct __env { unsigned max_vars ; unsigned n_vars ; char **vars ; } ; typedef struct __env *env_h ; #define ENV_NULL ((env_h)0) /* * Return values */ #define ENV_ERR (-1) #define ENV_OK 0 /* * Error codes */ #define ENV_ENOMEM 1 #define ENV_EBADVAR 2 #define ENV_EBADSTRING 3 env_h env_create ( const env_h ) ; void env_destroy ( env_h ) ; env_h env_make ( char **env_strings ) ; int env_addvar ( env_h, env_h from_env, char *var ) ; int env_addstr ( env_h, char *str ) ; char *env_lookup ( env_h, const char *var ) ; #define env_getvars( env ) (env)->vars extern int env_errno ; #endif /* __M_ENV_H */ xinetd-2.3.15.4/src/msg.c000066400000000000000000000127111333055217000147610ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include "xlog.h" #include "str.h" #include "msg.h" #include "defs.h" #include "options.h" #include "xconfig.h" #include "util.h" #include "nvlists.h" #include "main.h" #include "parse.h" static const struct name_value priorities[] = { { "WARNING", LOG_WARNING }, { "ERROR", LOG_ERR }, { "CRITICAL", LOG_CRIT }, { "NOTICE", LOG_NOTICE }, { "DEBUG", LOG_DEBUG }, { "INFO", LOG_INFO }, { NULL, 1 }, { "UNKNOWN", 0 } } ; #define BUFSIZE 2048 #define DEFAULT_SYSLOG_LEVEL LOG_INFO const char *msg_init(void) { xlog_h xh ; int fd ; xlog_e type_of_xlog ; bool_int facility_error = FALSE ; const char *func = "msg_init" ; if ( debug.on ) { type_of_xlog = XLOG_FILELOG ; xh = xlog_create( type_of_xlog, program_name, XLOG_NOFLAGS, "/dev/tty", O_APPEND + O_WRONLY, 0 ) ; debug.fd = -1 ; } else { if ( filelog_option ) { type_of_xlog = XLOG_FILELOG ; xh = xlog_create( type_of_xlog, program_name, XLOG_PRINT_ID + XLOG_PRINT_PID, filelog_option_arg, LOG_OPEN_FLAGS, LOG_FILE_MODE ) ; } else { int facility = DEFAULT_SYSLOG_FACILITY ; if ( syslog_option ) { const struct name_value *nvp ; nvp = nv_find_value( syslog_facilities, syslog_option_arg ) ; if ( nvp != NULL ) facility = nvp->value ; else facility_error = TRUE ; } type_of_xlog = XLOG_SYSLOG ; xh = xlog_create( type_of_xlog, program_name, XLOG_NOFLAGS, facility, DEFAULT_SYSLOG_LEVEL ) ; } } if ( xh == NULL ) { /* * This simply returns the most likely reason for failure. * We can't do any better since xlog_create does not return an * error code. */ if ( type_of_xlog == XLOG_SYSLOG ) return( "can't connect to syslog" ) ; else if ( type_of_xlog == XLOG_FILELOG ) return( "can't open log file" ) ; else return( "unknown reason" ) ; } /* * XXX: we shouldn't have to check the xlog type. * xlog_control should not succeed if the undelying logging * object does not support the XLOG_GETFD operation. */ if ( type_of_xlog == XLOG_FILELOG && xlog_control( xh, XLOG_GETFD, &fd ) == XLOG_ENOERROR ) { if ( fcntl( fd, F_SETFD, FD_CLOEXEC ) == -1 ) { xlog_destroy( xh ) ; return( "can't set close-on-exec flag of log file" ) ; } if ( debug.on ) debug.fd = fd ; } ps.rws.program_log = xh ; if ( facility_error ) msg( LOG_ERR, func, "Bad syslog facility: %s", syslog_option_arg ) ; return( CHAR_NULL ) ; } void msg_suspend(void) { (void) xlog_control( ps.rws.program_log, XLOG_PREEXEC ) ; } void msg_resume(void) { (void) xlog_control( ps.rws.program_log, XLOG_POSTEXEC ) ; } /* * The size argument is a value-result argument */ static int #ifdef __GNUC__ __attribute__ ((format (printf, 5, 0))) #endif prepare_buffer( int level, const char *func, char *buf, unsigned size, const char *fmt, va_list ap ) { int cc ; char *bufstart = buf ; unsigned bytes_left = size ; /* * Check if we need to print the level name */ if ( debug.on || filelog_option ) { cc = strx_nprint( bufstart, bytes_left, "%s: ", nv_get_name( priorities, level ) ) ; bufstart += cc ; bytes_left -= cc ; } /* * Check if we need to print the function name */ if ( debug.on || level == LOG_CRIT ) { cc = strx_nprint( bufstart, bytes_left, "%d {%s} ", getpid(), func ) ; bufstart += cc ; bytes_left -= cc ; } cc = strx_nprintv( bufstart, bytes_left, fmt, ap ) ; bytes_left -= cc ; return( size - bytes_left ) ; } /* VARARGS3 */ void msg( int level, const char *func, const char *fmt, ...) { va_list ap ; char buf[ BUFSIZE ] ; int len ; va_start( ap, fmt ) ; len = prepare_buffer( level, func, buf, sizeof( buf ), fmt, ap ) ; va_end( ap ) ; xlog_write( ps.rws.program_log, buf, len, XLOG_SET_LEVEL, level ) ; } /* * Parser message. * There are 2 differences from msg(): * 1) parsemsg() prints the line # * 2) parsemsg() does not interpret %m */ /* VARARGS3 */ void parsemsg( int msg_level, const char *func, const char *fmt, ...) { va_list ap ; char buf[ BUFSIZE ] ; int cc ; int len ; va_start( ap, fmt ) ; len = prepare_buffer( msg_level, func, buf, sizeof( buf ), fmt, ap ) ; va_end( ap ) ; cc = strx_nprint( &buf[ len ], sizeof(buf)-len, " [file=%s] [line=%d]", current_file, line_count ) ; len += cc ; xlog_write( ps.rws.program_log, buf, len, XLOG_NO_ERRNO + XLOG_SET_LEVEL, msg_level ) ; } xinetd-2.3.15.4/src/msg.h000066400000000000000000000006611333055217000147670ustar00rootroot00000000000000#ifndef MSG_H #define MSG_H #include const char *msg_init(void); void msg_suspend(void); void msg_resume(void); void msg(int level,const char *func,const char *fmt,...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))); #else ; #endif void parsemsg(int msg_level,const char *func,const char *fmt,...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))); #else ; #endif #endif xinetd-2.3.15.4/src/nvlists.c000066400000000000000000000105041333055217000156730ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include "nvlists.h" #include "sconf.h" /* * A NULL value for the name field marks the end of the table */ const struct name_value service_types[] = { #ifndef NO_RPC { "RPC", ST_RPC }, #endif { "INTERNAL", ST_INTERNAL }, { "UNLISTED", ST_UNLISTED }, { "SPECIAL", ST_SPECIAL }, { "TCPMUX", ST_TCPMUX }, { "TCPMUXPLUS", ST_TCPMUXPLUS }, { CHAR_NULL, 0 } } ; /* REUSE is only used for backward compatibility. All services are now reuse */ const struct name_value service_flags[] = { { "REUSE", SF_REUSE }, { "INTERCEPT", SF_INTERCEPT }, { "NORETRY", SF_NORETRY }, { "IDONLY", SF_IDONLY }, { "NAMEINARGS", SF_NAMEINARGS }, { "NODELAY", SF_NODELAY }, { "KEEPALIVE", SF_KEEPALIVE }, { "NOLIBWRAP", SF_NOLIBWRAP }, { "SENSOR", SF_SENSOR }, { "IPv4", SF_IPV4 }, { "IPv6", SF_IPV6 }, { "LABELED", SF_LABELED }, { CHAR_NULL, 0 } } ; const struct name_value socket_types[] = { { "stream", SOCK_STREAM }, { "dgram", SOCK_DGRAM }, { "raw", SOCK_RAW }, { "seqpacket", SOCK_SEQPACKET }, { CHAR_NULL, 1 }, { "BAD SOCKET TYPE", 0 } } ; const struct name_value success_log_options[] = { { "HOST", LO_HOST }, { "DURATION", LO_DURATION }, { "EXIT", LO_EXIT }, { "PID", LO_PID }, { "USERID", LO_USERID }, { "TRAFFIC", LO_TRAFFIC }, { CHAR_NULL, 0 } } ; const struct name_value failure_log_options[] = { { "HOST", LO_HOST }, { "ATTEMPT", LO_ATTEMPT }, { "USERID", LO_USERID }, { CHAR_NULL, 0 } } ; const struct name_value syslog_facilities[] = { { "daemon", LOG_DAEMON }, { "auth", LOG_AUTH }, #ifdef linux { "authpriv", LOG_AUTHPRIV }, #endif { "user", LOG_USER }, #ifdef LOG_MAIL { "mail", LOG_MAIL }, #endif #ifdef LOG_LPR { "lpr", LOG_LPR }, #endif #ifdef LOG_NEWS { "news", LOG_NEWS }, #endif #ifdef LOG_UUCP { "uucp", LOG_UUCP }, #endif #ifdef LOG_FTP { "ftp", LOG_FTP }, #endif { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { CHAR_NULL, 1 }, { "BAD FACILITY", 0 } } ; const struct name_value syslog_levels[] = { { "emerg", LOG_EMERG }, { "alert", LOG_ALERT }, { "crit", LOG_CRIT }, { "err", LOG_ERR }, { "warning", LOG_WARNING }, { "notice", LOG_NOTICE }, { "info", LOG_INFO }, { "debug", LOG_DEBUG }, { CHAR_NULL, 1 }, { "BAD LEVEL", 0 } } ; xinetd-2.3.15.4/src/nvlists.h000066400000000000000000000006371333055217000157060ustar00rootroot00000000000000#ifndef NVLISTS_H #define NVLISTS_H #include "defs.h" extern const struct name_value service_types[]; extern const struct name_value service_flags[]; extern const struct name_value socket_types[]; extern const struct name_value success_log_options[]; extern const struct name_value failure_log_options[]; extern const struct name_value syslog_facilities[]; extern const struct name_value syslog_levels[]; #endif xinetd-2.3.15.4/src/options.c000066400000000000000000000106241333055217000156670ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include "str.h" #include "sio.h" #include "options.h" #include "main.h" #include "util.h" #include "internals.h" /* for enable_periodic_check() */ /* * $Id$ */ int filelog_option ; char * filelog_option_arg ; int syslog_option ; char * syslog_option_arg ; int logprocs_option ; unsigned logprocs_option_arg ; int stayalive_option=0; char *program_name ; int inetd_compat = 0 ; int dont_fork = 0; #ifdef __GNUC__ __attribute__ ((noreturn)) #endif static void usage(void); int opt_recognize( int argc, char *argv[] ) { int arg, arg_1 ; unsigned int uarg_1; unsigned long long ullarg_1; program_name = strrchr( argv[ 0 ], '/' ) ; program_name = ( program_name == NULL ) ? argv[ 0 ] : program_name + 1 ; for ( arg = 1 ; arg < argc ; arg++ ) if ( argv[ arg ][ 0 ] == '-' && argv[ arg ][ 1 ] != 0 ) { if ( strcmp( &argv[ arg ][ 1 ], "d" ) == 0 ) debug.on = 1 ; else if ( strcmp( &argv[ arg ][ 1 ], "f" ) == 0 ) { if ( ++arg == argc ) usage() ; ps.ros.config_file = argv[ arg ]; } else if ( strcmp( &argv[ arg ][ 1 ], "filelog" ) == 0 ) { if ( ++arg == argc ) usage() ; filelog_option_arg = ( argv[ arg ] ) ; filelog_option = 1 ; } else if ( strcmp( &argv[ arg ][ 1 ], "syslog" ) == 0 ) { if ( ++arg == argc ) usage() ; syslog_option_arg = ( argv[ arg ] ) ; syslog_option = 1 ; } else if ( strcmp( &argv[ arg ][ 1 ], "reuse" ) == 0 ) ; /* This is now a null option, kept for compatibility */ else if ( strcmp( &argv[ arg ][ 1 ], "limit" ) == 0 ) { if ( ++arg == argc ) usage() ; if ( parse_ull( argv[ arg ], 10, NUL, &ullarg_1 ) < 0 ) usage() ; ps.ros.process_limit = (rlim_t)ullarg_1 ; if( ps.ros.process_limit != ullarg_1 ) usage() ; } else if ( strcmp( &argv[ arg ][ 1 ], "pidfile" ) == 0 ) { if( ++arg ==argc ) usage () ; ps.ros.pid_file = (char *)new_string( argv[arg] ); } else if ( strcmp( &argv[ arg ][ 1 ], "stayalive" )==0) stayalive_option = 1; else if ( strcmp( &argv[ arg ][ 1 ], "dontfork" )==0) { dont_fork = 1; stayalive_option = 1; } else if ( strcmp( &argv[ arg ][ 1 ], "logprocs" ) == 0 ) { if ( ++arg == argc ) usage() ; if ( parse_uint( argv[ arg ], 10, NUL, &uarg_1 ) < 0 ) usage() ; logprocs_option_arg = uarg_1 ; logprocs_option = 1 ; } else if ( strcmp( &argv[ arg ][ 1 ], "shutdownprocs" ) == 0 ) { if ( ++arg == argc ) usage() ; Sprint(2, "The shutdownprocs option has been deprecated.\n"); } else if ( strcmp( &argv[ arg ][ 1 ], "cc" ) == 0 ) { if ( ++arg == argc ) usage() ; if ( parse_int( argv[ arg ], 10, NUL, &arg_1 ) || arg_1 < 0 ) usage() ; ps.ros.cc_interval = arg_1; enable_periodic_check( arg_1 ) ; } else if ( strcmp( &argv[ arg ][ 1 ], "version" ) == 0 ) { fprintf(stderr, "%s", program_version); #ifdef LIBWRAP fprintf(stderr, " libwrap"); #endif #ifdef HAVE_LOADAVG fprintf(stderr, " loadavg"); #endif fprintf(stderr, "\n"); exit(0); } else if ( strcmp ( &argv[ arg ][ 1 ], "inetd_compat" ) == 0 ) inetd_compat = 1; } else break ; if ( filelog_option + syslog_option > 1 ) usage() ; if ( argc - arg != 0 ) usage() ; return( arg ) ; } static void usage(void) { Sprint( 2, "Usage: %s [-d] [-f config_file] [-filelog filename] [-syslog facility] [-reuse] [-limit proc_limit] [-pidfile filename] [-logprocs limit] [-shutdownprocs limit] [-cc interval]\n", program_name ) ; exit( 1 ) ; } xinetd-2.3.15.4/src/options.h000066400000000000000000000010641333055217000156720ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef OPTIONS_H #define OPTIONS_H extern int filelog_option; extern char *filelog_option_arg; extern int syslog_option; extern char *syslog_option_arg; extern int logprocs_option; extern unsigned logprocs_option_arg; extern int stayalive_option; extern char *program_name; extern int dont_fork; int opt_recognize(int argc,char *argv[]); #endif xinetd-2.3.15.4/src/parse.c000066400000000000000000000543031333055217000153100ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "str.h" #include "parse.h" #include "parsers.h" #include "msg.h" #include "parsesup.h" #include "addr.h" #include "includedir.h" #include "main.h" #include "sio.h" #ifndef NAME_MAX #define NAME_MAX 255 #endif /* * A NULL value for the name field marks the end of the table * * The 3rd value is the number of attribute values. * If the number is positive, exactly that many values must be specified. * If the number is -1, 0 or more values may be specified. * If the number is -2, 0 or more values may be specified and the operators * '+=' and '-=' may be used. */ static const struct attribute service_attributes[] = { { "socket_type", A_SOCKET_TYPE, 1, socket_type_parser }, { "protocol", A_PROTOCOL, 1, protocol_parser }, { "wait", A_WAIT, 1, wait_parser }, { "user", A_USER, 1, user_parser }, { "group", A_GROUP, 1, group_parser }, { "server", A_SERVER, 1, server_parser }, { "server_args", A_SERVER_ARGS, -1, server_args_parser }, { "instances", A_INSTANCES, 1, instances_parser }, { "log_on_success", A_LOG_ON_SUCCESS,-2, log_on_success_parser }, { "log_on_failure", A_LOG_ON_FAILURE,-2, log_on_failure_parser }, { "log_type", A_LOG_TYPE, -1, log_type_parser }, { "only_from", A_ONLY_FROM, -2, only_from_parser }, { "no_access", A_NO_ACCESS, -2, no_access_parser }, { "access_times", A_ACCESS_TIMES, -1, access_times_parser }, { "type", A_TYPE, -1, type_parser }, #ifndef NO_RPC { "rpc_version", A_RPC_VERSION, 1, rpc_version_parser }, { "rpc_number", A_RPC_NUMBER, 1, rpc_number_parser }, #endif { "id", A_ID, 1, id_parser }, { "env", A_ENV, -2, env_parser }, { "port", A_PORT, 1, port_parser }, { "passenv", A_PASSENV, -2, passenv_parser }, { "flags", A_FLAGS, -1, flags_parser }, { "nice", A_NICE, 1, nice_parser }, { "redirect", A_REDIR, 2, redir_parser }, { "banner", A_BANNER, 1, banner_parser }, { "bind", A_BIND, 1, bind_parser }, { "interface", A_BIND, 1, bind_parser }, { "per_source", A_PER_SOURCE, 1, per_source_parser }, { "groups", A_GROUPS, 1, groups_parser }, { "banner_success", A_BANNER_SUCCESS, 1, banner_success_parser }, { "banner_fail", A_BANNER_FAIL, 1, banner_fail_parser }, { "cps", A_CPS, 2, cps_parser }, { "disable", A_SVCDISABLE, 1, svcdisable_parser }, #ifdef HAVE_LOADAVG { "max_load", A_MAX_LOAD, 1, max_load_parser }, #endif #ifdef RLIMIT_AS { "rlimit_as", A_RLIMIT_AS, 1, rlim_as_parser }, #endif #ifdef RLIMIT_CPU { "rlimit_cpu", A_RLIMIT_CPU, 1, rlim_cpu_parser }, #endif #ifdef RLIMIT_DATA { "rlimit_data", A_RLIMIT_DATA, 1, rlim_data_parser }, #endif #ifdef RLIMIT_NOFILE { "rlimit_files", A_RLIMIT_FILES, 1, rlim_files_parser }, #endif #ifdef RLIMIT_RSS { "rlimit_rss", A_RLIMIT_RSS, 1, rlim_rss_parser }, #endif #ifdef RLIMIT_STACK { "rlimit_stack", A_RLIMIT_STACK, 1, rlim_stack_parser }, #endif { "v6only", A_V6ONLY, 1, v6only_parser }, { "deny_time", A_DENY_TIME, 1, deny_time_parser }, { "umask", A_UMASK, 1, umask_parser }, #ifdef LIBWRAP { "libwrap", A_LIBWRAP, 1, libwrap_parser }, #endif { NULL, A_NONE, -1, NULL } } ; static const struct attribute default_attributes[] = { { "log_type", A_LOG_TYPE, -2, log_type_parser }, { "log_on_success", A_LOG_ON_SUCCESS, -2, log_on_success_parser }, { "log_on_failure", A_LOG_ON_FAILURE, -2, log_on_failure_parser }, { "disabled", A_DISABLED, -2, disabled_parser }, { "no_access", A_NO_ACCESS, -2, no_access_parser }, { "only_from", A_ONLY_FROM, -2, only_from_parser }, { "instances", A_INSTANCES, 1, instances_parser }, { "passenv", A_PASSENV, -2, passenv_parser }, { "banner", A_BANNER, 1, banner_parser }, { "bind", A_BIND, 1, bind_parser }, { "interface", A_BIND, 1, bind_parser }, { "per_source", A_PER_SOURCE, 1, per_source_parser }, { "groups", A_GROUPS, 1, groups_parser }, { "banner_success", A_BANNER_SUCCESS, 1, banner_success_parser }, { "banner_fail", A_BANNER_FAIL, 1, banner_fail_parser }, { "cps", A_CPS, 2, cps_parser }, { "enabled", A_ENABLED, -2, enabled_parser }, #ifdef HAVE_LOADAVG { "max_load", A_MAX_LOAD, 1, max_load_parser }, #endif { "v6only", A_V6ONLY, 1, v6only_parser }, { "umask", A_UMASK, 1, umask_parser }, { NULL, A_NONE, 0, NULL } } ; #define MODIFIABLE( ap ) ( (ap)->a_nvalues == -2 ) #define FIXED_VALUES( ap ) ( (ap)->a_nvalues > 0 ) int line_count ; const char *current_file = NULL; static void get_service_entry( int fd, pset_h, const char *, struct service_config * ); static void fill_attribute( unsigned attr_id, struct service_config *scp, struct service_config *def ); static entry_e find_next_entry(int , char **) ; static status_e parse_entry(entry_e, int, struct service_config *) ; /* * Given the id, return the name (only the service attributes are searched) */ const char *attr_name_lookup( unsigned int id ) { const struct attribute *ap ; for ( ap = &service_attributes[ 0 ] ; ap->a_name ; ap++ ) if ( id == ap->a_id ) return( ap->a_name ) ; return( CHAR_NULL ) ; } void parse_end(void) { endprotoent() ; endpwent() ; endgrent() ; endnetent() ; endhostent() ; } /* * Parsing rules and rationale * * The parse_conf_file function parses a configuration file identified * by a file descriptor and fills the service table and defaults of * the configuration argument. * * The configuration information for a service comes from 2 sources: the * service entry and, possibly, the defaults entry. * Attributes specified in the defaults entry can be overriden or * modified by the service entry. Modifiable attributes can be identified * by the value -2 for the 'a_nvalues' field of the struct attribute. Those * attributes with a different value for 'a_nvalues' are overridable ones. * The modifiable attributes are filled in only if the entry tries to modify * them. */ /* * Read the configuration file (descriptor fd) and place all * services found there in the configuration. */ void parse_conf_file( int fd, struct configuration *confp, const char *filename) { pset_h sconfs = CNF_SERVICE_CONFS( confp ) ; struct service_config *default_config = CNF_DEFAULTS( confp ) ; boolean_e found_defaults = NO ; struct service_config default_default_config ; const char *func = "parse_conf_file" ; int incfd; line_count = 0 ; current_file = filename; CLEAR( default_default_config ) ; for ( ;; ) { entry_e entry_type ; char *service_name = NULL; /* * if find_next_entry is successful, service_name * will point to malloc'ed memory */ entry_type = find_next_entry( fd, &service_name ) ; switch ( entry_type ) { case INCLUDE_ENTRY: { int saved_line_count = line_count; incfd = open(service_name, O_RDONLY); if( incfd < 0 ) { parsemsg( LOG_ERR, func, "Unable to open included configuration file: %s", service_name); break; } parsemsg( LOG_DEBUG,func, "Reading included configuration file: %s",service_name); parse_conf_file(incfd, confp, service_name); /* * parse_conf_file eventually calls Srdline, try Sclosing it * to unmmap memory. */ Sclose(incfd); /* Restore since we've returned from included file */ current_file = filename; line_count = saved_line_count; } break; case INCLUDEDIR_ENTRY: { int saved_line_count = line_count; handle_includedir(service_name, confp); current_file = filename; line_count = saved_line_count; } break; case SERVICE_ENTRY: get_service_entry( fd, sconfs, service_name, default_config ) ; break ; case DEFAULTS_ENTRY: if ( found_defaults == YES ) { parsemsg( LOG_ERR, func, "only 1 defaults entry is allowed. This entry will be ignored" ) ; skip_entry( fd ) ; } else if ( parse_entry( DEFAULTS_ENTRY, fd, default_config ) == OK ) { found_defaults = YES ; /* * We must check bind_address to see if it was deferred. */ if (SC_SPECIFIED( default_config, A_BIND) && SC_BIND_ADDR(default_config) == NULL) M_CLEAR( default_config->sc_specified_attributes, A_BIND ) ; } break ; case BAD_ENTRY: skip_entry( fd ) ; break ; case NO_ENTRY: return ; } if (service_name) free(service_name); } } /* * Find the next service entry. * Look for a line of the form: * * service * * followed by a line containing only the ENTRY_BEGIN character */ static entry_e find_next_entry( int fd, char **snamep ) { char *p ; str_h strp ; char *sname = NULL; entry_e entry_type=0; char *line = next_line( fd ) ; const char *func = "find_next_entry" ; if ( line == CHAR_NULL ) return( NO_ENTRY ) ; strp = str_parse( line, " \t", STR_RETURN_ERROR, INT_NULL ) ; if ( strp == NULL ) { parsemsg( LOG_CRIT, func, "str_parse failed" ) ; return( BAD_ENTRY ) ; } if ( ( p = str_component( strp ) ) == CHAR_NULL ) { /* * This shouldn't happen since it implies that there is a bug * in next_line */ parsemsg( LOG_WARNING, func, "empty line" ) ; str_endparse( strp ) ; return( BAD_ENTRY ) ; } /* * Look for a keyword */ if ( EQ( p, KW_SERVICE ) || EQ( p, KW_INCLUDE ) || EQ(p, KW_INCLUDEDIR)) { if ( EQ( p, KW_INCLUDE )) entry_type = INCLUDE_ENTRY; else if ( EQ( p, KW_INCLUDEDIR )) entry_type = INCLUDEDIR_ENTRY; /* * Now get the service name */ if ( ( p = str_component( strp ) ) == CHAR_NULL ) { parsemsg( LOG_ERR, func, "service name missing" ) ; str_endparse( strp ) ; return( BAD_ENTRY ) ; } sname = new_string( p ) ; if ( sname == CHAR_NULL ) { out_of_memory( func ) ; str_endparse( strp ) ; return( BAD_ENTRY ) ; } str_endparse( strp ) ; if( (entry_type == INCLUDE_ENTRY) || (entry_type == INCLUDEDIR_ENTRY)) { *snamep = sname ; return( entry_type ) ; } else entry_type = SERVICE_ENTRY ; } else if ( EQ( p, KW_DEFAULTS ) ) { str_endparse( strp ) ; entry_type = DEFAULTS_ENTRY ; } else { parsemsg( LOG_ERR, func, "missing service keyword" ) ; str_endparse( strp ) ; return( BAD_ENTRY ) ; } /* * Now look for ENTRY_BEGIN */ line = next_line( fd ) ; if ( line == NULL || ! line_has_only_1_char( line, ENTRY_BEGIN ) ) { parsemsg( LOG_ERR, func, "Service %s: missing '%c'", sname, ENTRY_BEGIN ) ; if ( entry_type == SERVICE_ENTRY ) free( sname ) ; return( BAD_ENTRY ) ; } *snamep = sname ; return( entry_type ) ; } /* * Get a service entry. Steps: * * 1. Parse entry attributes * 2. Determine service id * 3. Insert entry in table */ static void get_service_entry( int fd, pset_h sconfs, const char *name, struct service_config *defaults ) { struct service_config *scp ; const char *func = "get_service_entry" ; scp = sc_alloc( name ) ; if ( scp == NULL ) { skip_entry( fd ) ; return ; } /* Now fill in default attributes if given. */ if ( SC_SPECIFIED( defaults, A_LOG_ON_SUCCESS ) && ! SC_IS_PRESENT( scp, A_LOG_ON_SUCCESS) ) fill_attribute( A_LOG_ON_SUCCESS, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_LOG_ON_FAILURE ) && ! SC_IS_PRESENT( scp, A_LOG_ON_FAILURE ) ) fill_attribute( A_LOG_ON_FAILURE, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_ONLY_FROM ) && ! SC_IS_PRESENT( scp, A_ONLY_FROM ) ) fill_attribute( A_ONLY_FROM, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_NO_ACCESS ) && ! SC_IS_PRESENT( scp, A_NO_ACCESS ) ) fill_attribute( A_NO_ACCESS, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_PASSENV ) && ! SC_IS_PRESENT( scp, A_PASSENV ) ) fill_attribute( A_PASSENV, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_ACCESS_TIMES ) && ! SC_IS_PRESENT( scp, A_ACCESS_TIMES ) ) fill_attribute( A_ACCESS_TIMES, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_BANNER ) && ! SC_IS_PRESENT( scp, A_BANNER ) ) fill_attribute( A_BANNER, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_BANNER_SUCCESS ) && ! SC_IS_PRESENT( scp, A_BANNER_SUCCESS ) ) fill_attribute( A_BANNER_SUCCESS, scp, defaults ) ; if ( SC_SPECIFIED( defaults, A_BANNER_FAIL ) && ! SC_IS_PRESENT( scp, A_BANNER_FAIL ) ) fill_attribute( A_BANNER_FAIL, scp, defaults ) ; if ( parse_entry( SERVICE_ENTRY, fd, scp ) == FAILED ) { sc_free( scp ) ; skip_entry( fd ) ; return ; } /* * If no service id was specified, set it equal to the service name */ if ( ! SC_SPECIFIED( scp, A_ID ) ) { if ( (SC_ID(scp) = new_string( SC_NAME(scp) )) ) SC_PRESENT( scp, A_ID ) ; else { out_of_memory( func ) ; sc_free( scp ) ; return ; } } if ( ! (pset_add( sconfs, scp )) ) { out_of_memory( func ) ; sc_free( scp ) ; return ; } } /* * Fill in scp the value of the modifiable attribute attr from def. * These modifiable attributes are: * log_on_{success,failure} * only_from * no_access * passenv */ static void fill_attribute( unsigned attr_id, struct service_config *scp, struct service_config *def ) { switch ( attr_id ) { case A_LOG_ON_SUCCESS: M_ASSIGN( SC_LOG_ON_SUCCESS(scp), SC_LOG_ON_SUCCESS(def) ) ; SC_PRESENT( scp, A_LOG_ON_SUCCESS ) ; break ; case A_LOG_ON_FAILURE: M_ASSIGN( SC_LOG_ON_FAILURE(scp), SC_LOG_ON_FAILURE(def) ) ; SC_PRESENT( scp, A_LOG_ON_FAILURE ) ; break ; case A_ONLY_FROM: if ( addrlist_copy( SC_ONLY_FROM(def), &SC_ONLY_FROM(scp) ) == OK ) SC_PRESENT( scp, A_ONLY_FROM ) ; break ; case A_NO_ACCESS: if ( addrlist_copy( SC_NO_ACCESS(def), &SC_NO_ACCESS(scp) ) == OK ) SC_PRESENT( scp, A_NO_ACCESS ) ; break ; case A_PASSENV: if ( copy_pset( SC_PASS_ENV_VARS(def), &SC_PASS_ENV_VARS(scp), 0 ) == OK ) SC_PRESENT( scp, A_PASSENV ) ; break ; case A_ACCESS_TIMES: if ( copy_pset( SC_ACCESS_TIMES(def), &SC_ACCESS_TIMES(scp), 0 ) == OK ) SC_PRESENT( scp, A_ACCESS_TIMES ) ; break ; case A_BANNER: if ((SC_BANNER(scp) = new_string(SC_BANNER(def))) != NULL) SC_PRESENT( scp, A_BANNER ); break ; case A_BANNER_SUCCESS: if ((SC_BANNER_SUCCESS(scp) = new_string(SC_BANNER_SUCCESS(def))) != NULL) SC_PRESENT( scp, A_BANNER_SUCCESS ); break ; case A_BANNER_FAIL: if ((SC_BANNER_FAIL(scp) = new_string(SC_BANNER_FAIL(def))) != NULL) SC_PRESENT( scp, A_BANNER_FAIL ); break ; } } /* * Find the attribute with the specified name */ static const struct attribute *attr_lookup( const struct attribute attr_array[], const char *attr_name ) { const struct attribute *ap ; const char *func = "attr_lookup" ; for ( ap = &attr_array[ 0 ] ; ap->a_name ; ap++ ) if ( EQ( attr_name, ap->a_name ) ) return ap; if ( attr_array == service_attributes ) parsemsg( LOG_WARNING, func, "bad service attribute: %s", attr_name ) ; else parsemsg( LOG_WARNING, func, "attribute: %s should not be in default section", attr_name ) ; return NULL; } /* * Identify the attribute in . * * Check if * 1) the attribute has been defined already * 2) the value count is correct * 3) the assign op is appropriate * * Invoke appropriate parser. * * This function will return FAILED only if its in the default section * and an attribute cannot be ID'd. Otherwise, it returns OK. */ static status_e identify_attribute( entry_e entry_type, struct service_config *scp, const char *attr_name, enum assign_op op, pset_h attr_values ) { const struct attribute *ap ; const char *func = "identify_attribute" ; if ( entry_type == SERVICE_ENTRY ) ap = attr_lookup( service_attributes, attr_name ) ; else ap = attr_lookup( default_attributes, attr_name ) ; if ( ap == NULL ) return OK; /* We simply ignore keywords not on the list */ if ( ! MODIFIABLE( ap ) ) { if ( SC_SPECIFIED( scp, ap->a_id ) ) { parsemsg( LOG_WARNING, func, "Service %s: attribute already set: %s", SC_NAME(scp), attr_name ) ; return OK; } if ( op != SET_EQ ) { parsemsg( LOG_WARNING, func, "Service %s: operator '%s' cannot be used for attribute '%s'", SC_NAME(scp), ( op == PLUS_EQ ) ? "+=" : "-=", attr_name ) ; return OK; } } else /* modifiable attribute */ { /* * For the defaults entry, '=' and '+=' have the same meaning */ if ( entry_type == DEFAULTS_ENTRY && op == SET_EQ ) op = PLUS_EQ ; } if ( FIXED_VALUES( ap ) && (unsigned)ap->a_nvalues != pset_count( attr_values ) ) { parsemsg( LOG_WARNING, func, "attribute %s expects %d values and %d values were specified", attr_name, ap->a_nvalues, pset_count( attr_values ) ) ; return OK; } if ( (*ap->a_parser)( attr_values, scp, op ) == OK ) { /* This is the normal path. */ SC_SPECIFY( scp, ap->a_id ) ; } else if ( entry_type == SERVICE_ENTRY ) { parsemsg( LOG_ERR, func, "Error parsing attribute %s - DISABLING SERVICE", attr_name ) ; SC_DISABLE( scp ); } /* * We are in the default section and an error was detected. At * this point, we should terminate since whatever attribute * was trying to be specified cannot be propagated. */ else if ( !debug.on ) return FAILED; return OK; } /* * Read the entry line-by-line and add the information in scp * Use defaults to initialize modifiable entry fields. */ static status_e parse_entry( entry_e entry_type, int fd, struct service_config *scp ) { static pset_h attr_values = NULL; char *line ; char *attr_name ; enum assign_op op ; const char *func = "parse_entry" ; if ( ! attr_values && ( attr_values = pset_create( 10, 10 ) ) == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } for ( ;; ) { line = next_line( fd ) ; if ( line == CHAR_NULL ) { parsemsg( LOG_ERR, func, "incomplete entry" ) ; return( FAILED ) ; } if ( line_has_only_1_char( line, ENTRY_END ) ) return( OK ) ; if ( parse_line( line, &attr_name, &op, attr_values ) == FAILED ) { pset_clear( attr_values ) ; return( FAILED ) ; } if (identify_attribute( entry_type, scp, attr_name, op, attr_values ) == FAILED ) { /* * An error was detected in the default section. We will terminate * since whatever attribute being specified cannot be propagated. */ msg(LOG_ERR, func, "A fatal error was encountered while parsing the default section." " xinetd will exit."); Sclose( fd ); terminate_program(); } pset_clear( attr_values ) ; /* * As soon as we realize that the service is disabled * we don't continue parsing its config */ if (EQ(attr_name, "disable") && SC_IS_DISABLED(scp)) return( FAILED ); } } xinetd-2.3.15.4/src/parse.h000066400000000000000000000023621333055217000153130ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef PARSE_H #define PARSE_H #include "defs.h" #include "conf.h" /* * $Id$ */ typedef enum { NO_ENTRY, BAD_ENTRY, SERVICE_ENTRY, DEFAULTS_ENTRY, INCLUDE_ENTRY, INCLUDEDIR_ENTRY } entry_e ; enum assign_op { SET_EQ, PLUS_EQ, MINUS_EQ } ; struct attribute { const char *a_name ; /* name of attribute */ unsigned a_id ; /* attribute id */ int a_nvalues ; /* number of values */ status_e (*a_parser)() ; /* function that parses the attribute */ } ; #define ENTRY_BEGIN '{' #define ENTRY_END '}' #define COMMENT_BEGIN '#' #define KW_SERVICE "service" #define KW_DEFAULTS "defaults" #define KW_INCLUDE "include" #define KW_INCLUDEDIR "includedir" extern int line_count; extern const char *current_file; const char *attr_name_lookup(unsigned int id); void parse_end(void); void parse_conf_file(int fd,struct configuration *confp, const char *filename); #endif /* PARSE_H */ xinetd-2.3.15.4/src/parsers.c000066400000000000000000001142411333055217000156530ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* For LONG_MIN and LONG_MAX */ #include #ifdef HAVE_LOADAVG #include #endif #if defined(hpux) && !defined(X_OK) #define X_OK 1 #endif #include "str.h" #include "parsers.h" #include "msg.h" #include "nvlists.h" #include "env.h" #include "xconfig.h" #include "addr.h" #include "libportable.h" #include "timex.h" #include "addr.h" /* check_hostname() */ #define NEW_SET( set, v1, v2 ) \ if ( (set) == NULL && \ ( (set) = pset_create( (v1), (v2) ) ) == NULL ) \ { \ out_of_memory( func ) ; \ return( FAILED ) ; \ } static void missing_attr_msg(const char *par, const char *item) { parsemsg( LOG_WARNING, par, "attribute %s expects at least 1 value and none was given.", item ); } /* * Find the flags corresponding to strings in "values" and apply * them to "*maskp" (apply means add or remove depending on "op") * "description" describes the type of flags. */ static status_e parse_value_list( pset_h values, mask_t *maskp, const struct name_value list[], enum assign_op op, const char *description ) { unsigned u ; const struct name_value *nvp ; const char *func = "parse_value_list" ; for ( u=0; uvalue ) ; else M_CLEAR( *maskp, nvp->value ) ; } else { parsemsg( LOG_WARNING, func, "Bad %s: %s", description, name ) ; return( FAILED ); } } return( OK ) ; } status_e type_parser( pset_h values, struct service_config *scp, enum assign_op op ) { if ( pset_count( values ) >= 1 ) { return( parse_value_list( values, &SC_TYPE(scp), service_types, PLUS_EQ, "service type" ) ) ; } else { missing_attr_msg("type_parser", "type"); return FAILED ; } } status_e flags_parser( pset_h values, struct service_config *scp, enum assign_op op ) { if ( pset_count( values ) >= 1 ) { return( parse_value_list( values, &SC_XFLAGS(scp), service_flags, PLUS_EQ, "service flag" ) ) ; } else { missing_attr_msg("flags_parser", "flags"); return FAILED ; } } status_e socket_type_parser( const pset_h values, struct service_config *scp, enum assign_op op ) { const struct name_value *nvp ; const char *type = (char *) pset_pointer( values, 0 ) ; const char *func = "socket_type_parser" ; nvp = nv_find_value( socket_types, type ) ; if ( nvp != NULL ) { SC_SOCKET_TYPE(scp) = nvp->value ; return( OK ) ; } else { parsemsg( LOG_ERR, func, "Bad socket type: %s", type ) ; return( FAILED ) ; } } status_e rpc_version_parser( pset_h values, struct service_config *scp, enum assign_op op ) { struct rpc_data *rdp = SC_RPCDATA( scp ) ; char *version = (char *) pset_pointer( values, 0 ) ; int min_version=0, max_version=0; char *p = strchr( version, '-' ) ; const char *func = "rpc_version_parser" ; if ( p == NULL ) { if ( parse_base10(version, &min_version) ) max_version = min_version - 1; else max_version = min_version; } else { *p = NUL ; if ( parse_base10(version, &min_version) || parse_base10(p+1, &max_version) ) max_version = min_version - 1; } if ( min_version > max_version ) { parsemsg( LOG_ERR, func, "bad version range: %s", version ) ; return( FAILED ) ; } rdp->rd_min_version = min_version; rdp->rd_max_version = max_version; return( OK ) ; } status_e rpc_number_parser( pset_h values, struct service_config *scp, enum assign_op op ) { int num; if ( parse_base10((char *) pset_pointer( values, 0 ), &num) ) { parsemsg(LOG_ERR, "rpc_number_parser", "Error parsing: %s", (char *)pset_pointer( values, 0 )); return( FAILED ); } SC_RPCDATA( scp )->rd_program_number = num; return( OK ) ; } status_e protocol_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *proto_name = (char *) pset_pointer( values, 0 ) ; struct protoent *pep ; const char *func = "protocol_parser" ; if( proto_name == NULL ) { parsemsg( LOG_ERR, func, "Protocol name is null in %s", SC_NAME(scp) ); return( FAILED ); } if ( ( pep = getprotobyname( proto_name ) ) == NULL ) { parsemsg( LOG_ERR, func, "Protocol %s not in /etc/protocols", proto_name ) ; return( FAILED ) ; } SC_PROTONAME(scp) = new_string( proto_name ) ; if ( SC_PROTONAME(scp) == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } SC_PROTOVAL(scp) = pep->p_proto ; return( OK ) ; } status_e wait_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *val = (char *) pset_pointer( values, 0 ) ; const char *func = "wait_parser" ; if ( EQ( val, "yes" ) ) SC_WAIT(scp) = YES ; else if ( EQ( val, "no" ) ) SC_WAIT(scp) = NO ; else { parsemsg( LOG_ERR, func, "Bad value for wait: %s", val ) ; return( FAILED ); } return( OK ) ; } status_e user_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *user = (char *) pset_pointer( values, 0 ) ; const char *func = "user_parser" ; if (parse_all_digits(user) == TRUE) { /* We will assume the number is a valid user. This is a workaround for some Solaris systems that have problems doing getgr*. */ if (parse_ubase10(user, (unsigned int *)&SC_UID(scp))) { parsemsg( LOG_ERR, func, "Error parsing user as a number: %s", user ) ; return( FAILED ) ; } SC_USER_GID(scp) = SC_UID(scp) ; } else { struct passwd *pw ; pw = getpwnam( user ) ; if ( pw == NULL ) { parsemsg( LOG_ERR, func, "Unknown user: %s", user ) ; return( FAILED ) ; } str_fill( pw->pw_passwd, ' ' ); SC_UID(scp) = pw->pw_uid ; SC_USER_GID(scp) = pw->pw_gid ; } return( OK ) ; } status_e group_parser( pset_h values, struct service_config *scp, enum assign_op op ) { const char *func = "group_parser" ; char *group_ptr = (char *) pset_pointer( values, 0 ) ; if (parse_all_digits(group_ptr) == TRUE) { /* We will assume the number is a valid group. This is a workaround for some Solaris systems that have problems doing getgr*. */ if (parse_ubase10(group_ptr, (unsigned int *)&SC_GID(scp))) { parsemsg( LOG_ERR, func, "Error parsing group as a number: %s", group_ptr ) ; return( FAILED ) ; } } else { struct group *grp = getgrnam( group_ptr ) ; if ( grp == NULL ) { parsemsg( LOG_ERR, func, "Unknown group: %s", group_ptr ) ; return( FAILED ) ; } SC_GID(scp) = grp->gr_gid ; } return( OK ) ; } status_e svcdisable_parser( pset_h values, struct service_config *scp, enum assign_op op ) { const char *func = "svcdisable_parser" ; char *val = (char *) pset_pointer( values, 0 ) ; if( EQ( val, "yes" ) ) SC_DISABLE( scp ); else if( EQ( val, "no" ) ) SC_ENABLE( scp ); else { parsemsg( LOG_ERR, func, "Bad value: %s", val ) ; return( FAILED ); } return( OK ); } status_e groups_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *val = (char *) pset_pointer( values, 0 ) ; const char *func = "groups_parser" ; if ( EQ( val, "yes" ) ) SC_GROUPS(scp) = YES ; else if ( EQ( val, "no" ) ) SC_GROUPS(scp) = NO ; else { parsemsg( LOG_ERR, func, "Bad value for groups: %s", val ) ; return( FAILED ); } return( OK ); } status_e v6only_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *val = (char *) pset_pointer( values, 0 ); const char *func = "v6only_parser" ; if ( EQ( val, "yes" ) ) SC_V6ONLY(scp) = YES; else if ( EQ( val, "no" ) ) SC_V6ONLY(scp) = NO; else { parsemsg( LOG_ERR, func, "Bad value for v6only: %s", val ); return( FAILED ); } return( OK ); } status_e server_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *server = (char *) pset_pointer( values, 0 ) ; const char *func = "server_parser" ; struct stat sb; /* * Access is used so that the real user ID permissions * are checked. */ if ( access( server, X_OK ) == -1 ) { parsemsg( LOG_ERR, func, "Server %s is not executable", server ) ; return( FAILED ) ; } if (stat(server, &sb) == -1) { parsemsg( LOG_ERR, func, "Unable to stat: %s.", server ) ; return( FAILED ) ; } SC_SERVER(scp) = new_string( server ) ; if ( SC_SERVER(scp) == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } return( OK ) ; } status_e server_args_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char **argv ; unsigned u ; unsigned i ; unsigned count; unsigned argv_index ; unsigned n_args = pset_count( values ) ; const char *func = "server_args_parser" ; /* * Create the argv for a future exec call * Reserve space for the server. We cannot use scp->sc_server * since it may not have a value yet. */ argv = argv_alloc( n_args+1 ) ; count = pset_count( values ); if ( count == 0 ) { missing_attr_msg("server_args_parser", "server_args"); free( (char *) argv ) ; return FAILED; } if( SC_NAMEINARGS( scp ) ) { for (u = 0; u < count; u++) { register char *s = new_string( (char *) pset_pointer( values, u )) ; if ( s == NULL ) { for ( i = 1 ; i < u ; i++ ) free( argv[ i ] ) ; free( (char *) argv ) ; out_of_memory( func ) ; return( FAILED ) ; } argv[ u ] = s ; } } else { for (u = 0, argv_index = 1 ; u < count; u++, argv_index++) { register char *s = new_string((char *) pset_pointer( values, u )) ; if ( s == NULL ) { for ( i = 1 ; i < argv_index ; i++ ) free( argv[ i ] ) ; free( (char *) argv ) ; out_of_memory( func ) ; return( FAILED ) ; } argv[ argv_index ] = s ; } argv[ argv_index ] = argv[ 0 ] = NULL ; } SC_SERVER_ARGV(scp) = argv ; return( OK ) ; } status_e instances_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *instances = (char *) pset_pointer( values, 0 ) ; const char *func = "instances_parser" ; if ( EQ( instances, "UNLIMITED" ) ) SC_INSTANCES(scp) = UNLIMITED ; else { if ( parse_base10(instances, &SC_INSTANCES(scp)) || SC_INSTANCES(scp) < 0 ) { parsemsg( LOG_ERR, func, "Number of instances is invalid: %s", instances ) ; return( FAILED ) ; } } return( OK ) ; } status_e per_source_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *per_source = (char *) pset_pointer( values, 0 ) ; const char *func = "per_source_parser" ; if ( EQ( per_source, "UNLIMITED" ) ) SC_PER_SOURCE(scp) = UNLIMITED; else { if ( parse_base10(per_source, &SC_PER_SOURCE(scp)) || SC_PER_SOURCE(scp) < 0 ) { parsemsg( LOG_ERR, func, "Number of per source instances is invalid: %s", per_source ) ; return( FAILED ); } } return(OK); } status_e cps_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *cps = (char *) pset_pointer(values, 0); char *waittime = (char *) pset_pointer(values, 1); unsigned int waittime_int, conn_max; if( cps == NULL || waittime == NULL ) { parsemsg(LOG_ERR, "cps_parser", "NULL options specified in cps"); return( FAILED ); } if( parse_ubase10(cps, &conn_max) ) { parsemsg(LOG_ERR, "cps_parser", "cps argument not a number"); SC_TIME_CONN_MAX(scp) = 0; SC_TIME_WAIT(scp) = 0; return( FAILED ); } if( parse_ubase10(waittime, &waittime_int) ) { parsemsg(LOG_ERR, "cps_parser", "cps time argument not a number"); SC_TIME_CONN_MAX(scp) = 0; SC_TIME_WAIT(scp) = 0; return( FAILED ); } SC_TIME_WAIT(scp) = waittime_int; SC_TIME_CONN_MAX(scp) = conn_max; if( SC_TIME_CONN_MAX(scp) < 0 || SC_TIME_WAIT(scp) < 0 ) { parsemsg(LOG_ERR, "cps_parser", "cps arguments invalid"); SC_TIME_CONN_MAX(scp) = 0; SC_TIME_WAIT(scp) = 0; return( FAILED ); } return(OK); } status_e id_parser( pset_h values, struct service_config *scp, enum assign_op op ) { const char *func = "id_parser" ; SC_ID(scp) = new_string( (char *) pset_pointer( values, 0 ) ) ; if ( SC_ID(scp) != NULL ) return( OK ) ; out_of_memory( func ) ; return( FAILED ) ; } #define PORT_BITS 16 #define PORT_MAX ( 1 << PORT_BITS ) status_e port_parser( pset_h values, struct service_config *scp, enum assign_op op ) { int port; const char *func = "port_parser" ; if ( parse_base10((char *) pset_pointer( values, 0 ), &port) || port < 0 || port >= PORT_MAX ) { parsemsg( LOG_ERR, func, "port number is invalid" ) ; return( FAILED ) ; } SC_PORT(scp) = (uint16_t)port ; return( OK ) ; } static status_e add_new_string( pset_h set, char *str ) { char *p = new_string( str ) ; const char *func = "add_new_string" ; if ( p == NULL ) { parsemsg( LOG_CRIT, func, ES_NOMEM ) ; return( FAILED ) ; } if ( pset_add( set, p ) == NULL ) { free( p ) ; parsemsg( LOG_CRIT, func, ES_NOMEM ) ; return( FAILED ) ; } return( OK ) ; } status_e env_parser( pset_h values, struct service_config *scp, enum assign_op op ) { unsigned u ; const char *func = "env_parser" ; if ( op == MINUS_EQ ) { parsemsg( LOG_WARNING, func, "operator '-=' not supported for env attribute" ) ; return( FAILED ) ; } NEW_SET( SC_ENV_VAR_DEFS(scp), 5, 5 ) ; if ( op == SET_EQ && pset_count( SC_ENV_VAR_DEFS(scp) ) != 0 ) { pset_apply( SC_ENV_VAR_DEFS(scp), free, NULL ) ; pset_clear( SC_ENV_VAR_DEFS(scp) ) ; } for ( u = 0 ; u < pset_count( values ) ; u++ ) { char *str = (char *) pset_pointer( values, u ) ; /* * Check if the string contains an '=' */ if ( strchr( str, '=' ) == NULL ) { parsemsg( LOG_ERR, func, "%s has no '='", str ) ; return( FAILED ) ; } if ( add_new_string( SC_ENV_VAR_DEFS(scp), str ) == FAILED ) return( FAILED ) ; } return( OK ) ; } status_e passenv_parser( pset_h values, struct service_config *scp, enum assign_op op ) { pset_h var_set ; unsigned u ; const char *func = "passenv_parser" ; NEW_SET( SC_PASS_ENV_VARS(scp), 0, 0 ) ; var_set = SC_PASS_ENV_VARS(scp) ; if ( op == SET_EQ ) { pset_apply( var_set, free, NULL ) ; pset_clear( var_set ) ; op = PLUS_EQ ; } for ( u = 0 ; u < pset_count( values ) ; u++ ) { char *env_var = (char *) pset_pointer( values, u ) ; unsigned v ; boolean_e found ; /* * Check if it is already there */ for ( found = NO, v = 0 ; v < pset_count( var_set ) ; v++ ) if ( EQ( env_var, (char *) pset_pointer( var_set, v ) ) ) { found = YES ; break ; } if ( ((op == MINUS_EQ) && (found == NO)) || ((op != MINUS_EQ) && (found == YES)) ) continue ; if ( op == MINUS_EQ ) { free( (char *) pset_pointer( var_set, v ) ) ; pset_remove_index( var_set, v ) ; } else { if ( env_lookup( std_env, env_var ) == CHAR_NULL ) { parsemsg( LOG_WARNING, func, "undefined environment variable: %s", env_var ) ; continue ; } if ( add_new_string( var_set, env_var ) == FAILED ) return( FAILED ) ; } } return( OK ) ; } status_e disabled_parser( pset_h values, struct service_config *scp, enum assign_op op ) { unsigned u ; const char *func = "disabled_parser" ; NEW_SET( SC_DISABLED(scp), pset_count( values ), 0 ) ; for ( u = 0 ; u < pset_count( values ) ; u++ ) { char *name = (char *) pset_pointer( values, u ) ; if ( add_new_string( SC_DISABLED(scp), name ) == FAILED ) return( FAILED ) ; } return( OK ) ; } status_e enabled_parser( pset_h values, struct service_config *scp, enum assign_op op ) { unsigned u ; const char *func = "enabled_parser" ; NEW_SET( SC_ENABLED(scp), pset_count( values ), 0 ) ; for ( u = 0 ; u < pset_count( values ) ; u++ ) { char *name = (char *) pset_pointer( values, u ) ; if ( add_new_string( SC_ENABLED(scp), name ) == FAILED ) return( FAILED ) ; } return( OK ) ; } /* * Interpret a number of the form: [m|M|k|K] * m and M mean megabytes, k and K mean kilobytes, nothing means bytes */ static int get_limit( char *limit_str, rlim_t *res ) { unsigned long long limit_int; int multiplier; char *p; if (*limit_str == NUL) { *res = 0; return -1; } p = limit_str + strlen( limit_str ) - 1; while ( p > limit_str && isspace( *p ) ) p--; if (*p == 'k' || *p == 'K') { *p = NUL; multiplier = 1024; } else if (*p == 'm' || *p == 'M') { *p = NUL; multiplier = 1024 * 1024; } else multiplier = 1; if (parse_ull(limit_str, 10, -1, &limit_int)) { *res = 0; return -1; } *res = (rlim_t)limit_int; if (*res != limit_int) { *res = 0; return -1; } *res = (rlim_t)limit_int * multiplier; if (*res / multiplier != (rlim_t)limit_int) { *res = 0; return -1; } return 0; } static status_e parse_filelog( struct filelog *flp, pset_h values ) { rlim_t soft_limit ; rlim_t hard_limit ; char *file ; unsigned count = pset_count( values ) ; const char *func = "parse_filelog" ; if ( count < 2 || count > 4 ) { parsemsg( LOG_ERR, func, "wrong number of arguments" ) ; return( FAILED ) ; } file = new_string( (char *) pset_pointer( values, 1 ) ) ; if ( file == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } /* * Get the limits, if any */ if ( count > 2 ) { if ( get_limit( (char *) pset_pointer( values, 2 ), &soft_limit ) ) { parsemsg( LOG_ERR, func, "soft limit is invalid" ) ; free( file ) ; return( FAILED ) ; } /* * If a hard limit was specified check that it is at least equal * to the soft limit. If no hard limit was specified, determine * it from the formula: * hard = soft + x * where * min( 1%soft,LOG_EXTRA_MIN ) <= x <= max( 1%soft,LOG_EXTRA_MAX ) */ if ( count == 4 ) { if ( get_limit( (char *) pset_pointer( values, 3 ), &hard_limit) ) { parsemsg( LOG_ERR, func, "hard limit is invalid" ) ; free( file ) ; return( FAILED ) ; } if ( hard_limit < soft_limit ) { parsemsg( LOG_ERR, func, "hard limit (%lu) is less than soft limit (%lu)", (unsigned long)hard_limit, (unsigned long)soft_limit ) ; free( file ) ; return( FAILED ) ; } } else { unsigned extra = soft_limit / 100 ; /* 1% of soft limit */ if ( extra < LOG_EXTRA_MIN ) extra = LOG_EXTRA_MIN ; else if ( extra > LOG_EXTRA_MAX ) extra = LOG_EXTRA_MAX ; hard_limit = soft_limit + extra ; } flp->fl_soft_limit = soft_limit ; flp->fl_hard_limit = hard_limit ; } flp->fl_filename = file ; return( OK ) ; } static status_e parse_syslog( struct syslog *slp, pset_h values ) { const char *facility ; const char *level ; const struct name_value *nvp ; unsigned count = pset_count( values ) ; const char *func = "parse_syslog" ; if ( count < 2 || count > 3 ) { parsemsg( LOG_ERR, func, "wrong number of arguments" ) ; return( FAILED ) ; } facility = (char *) pset_pointer( values, 1 ) ; if ( ( nvp = nv_find_value( syslog_facilities, facility ) ) == NULL ) { parsemsg( LOG_ERR, func, "Unknown syslog facility: %s", facility ) ; return( FAILED ) ; } slp->sl_facility = nvp->value ; if ( count == 3 ) { level = (char *) pset_pointer( values, 2 ) ; if ( ( nvp = nv_find_value( syslog_levels, level ) ) == NULL ) { parsemsg( LOG_ERR, func, "Unknown syslog level: %s", level ) ; return( FAILED ) ; } slp->sl_level = nvp->value ; } else slp->sl_level = DEFAULT_SERVICE_SYSLOG_LEVEL ; return( OK ) ; } status_e log_type_parser( pset_h values, struct service_config *scp, enum assign_op op ) { struct log *lp = SC_LOG( scp ) ; char *type ; const char *func = "parse_log_type" ; int count = pset_count( values); if ( count == 0 ) { missing_attr_msg(func, "log_type"); return( FAILED ); } if ( LOG_GET_TYPE( lp ) != L_NONE) { parsemsg( LOG_ERR, func, "Cannot set more than one log_type attribute"); return( FAILED ); } type = (char *) pset_pointer( values, 0 ) ; if ( EQ( type, "FILE" ) ) { if ( parse_filelog( LOG_GET_FILELOG( lp ), values ) == FAILED ) return( FAILED ) ; lp->l_type = L_FILE ; } else if ( EQ( type, "SYSLOG" ) ) { if ( parse_syslog( LOG_GET_SYSLOG( lp ), values ) == FAILED ) return( FAILED ) ; lp->l_type = L_SYSLOG ; } else { parsemsg( LOG_ERR, func, "Unknown log type: %s", type ) ; return( FAILED ) ; } return( OK ) ; } static status_e parse_log_flags( pset_h values, enum assign_op op, mask_t *maskp, const struct name_value options[], const char *name ) { if ( op == SET_EQ ) { M_CLEAR_ALL( *maskp ) ; op = PLUS_EQ ; } return( parse_value_list( values, maskp, options, op, name ) ) ; } status_e log_on_success_parser( pset_h values, struct service_config *scp, enum assign_op op ) { return( parse_log_flags( values, op, &SC_LOG_ON_SUCCESS(scp), success_log_options, "log_on_success flag" ) ) ; } status_e log_on_failure_parser( pset_h values, struct service_config *scp, enum assign_op op ) { return( parse_log_flags( values, op, &SC_LOG_ON_FAILURE(scp), failure_log_options, "log_on_failure flag" ) ) ; } static status_e parse_inet_addresses( pset_h values, enum assign_op op, pset_h *addr_list ) { unsigned u ; pset_h addr_set ; statfunc addrlist_func ; const char *func = "parse_inet_addresses" ; NEW_SET( *addr_list, 0, 0 ); addr_set = *addr_list; /* * If the op was '=' clear the existing list of addresses */ if ( op == SET_EQ ) { op = PLUS_EQ ; addrlist_free( addr_set ) ; pset_clear( addr_set ) ; } addrlist_func = ( op == PLUS_EQ ) ? addrlist_add : addrlist_remove ; for ( u = 0 ; u < pset_count( values ) ; u++ ) { register char *str_addr = (char *) pset_pointer( values, u ) ; /* If it is factorized, allow a comma. Otherwise complain */ if (strchr(str_addr, ',') && !strchr(str_addr, '{')) { parsemsg( LOG_ERR, func, "Address: %s has a comma in it - remove the comma", str_addr ) ; return( FAILED ); } if ( (*addrlist_func)( addr_set, str_addr ) == FAILED ) { parsemsg( LOG_ERR, func, "Failed adding: %s", str_addr ) ; return( FAILED ); } } return( OK ) ; } status_e only_from_parser( pset_h values, struct service_config *scp, enum assign_op op ) { return( parse_inet_addresses( values, op, &SC_ONLY_FROM(scp) ) ) ; } status_e no_access_parser( pset_h values, struct service_config *scp, enum assign_op op ) { return( parse_inet_addresses( values, op, &SC_NO_ACCESS(scp) ) ) ; } status_e banner_parser(pset_h values, struct service_config *scp, enum assign_op op) { const char *func = "banner_parser"; if( pset_pointer(values, 0) == NULL ) { msg(LOG_ERR, func, "pset_pointer returned NULL"); return( FAILED ); } SC_BANNER(scp) = new_string( pset_pointer(values,0) ); if( SC_BANNER(scp) == NULL ) { msg(LOG_ERR, func, ES_NOMEM); return( FAILED ); } return OK; } status_e banner_success_parser(pset_h values, struct service_config *scp, enum assign_op op) { const char *func = "banner_success_parser"; if( pset_pointer(values, 0) == NULL ) { msg(LOG_ERR, func, "pset_pointer returned NULL" ); return( FAILED ); } SC_BANNER_SUCCESS(scp) = new_string(pset_pointer(values,0) ); if( SC_BANNER_SUCCESS(scp) == NULL ) { msg(LOG_ERR, func, ES_NOMEM); return( FAILED ); } return OK; } status_e banner_fail_parser(pset_h values, struct service_config *scp, enum assign_op op) { const char *func = "banner_fail_parser"; if( pset_pointer(values, 0) == NULL ) { msg(LOG_ERR, func, "pset_pointer returned NULL"); return( FAILED ); } SC_BANNER_FAIL(scp) = new_string(pset_pointer(values,0) ); if( SC_BANNER_FAIL(scp) == NULL ) { msg(LOG_ERR, func, ES_NOMEM); return( FAILED ); } return OK; } #ifdef HAVE_LOADAVG status_e max_load_parser(pset_h values, struct service_config *scp, enum assign_op op) { const char *func = "max_load_parser" ; char *adr = (char *)pset_pointer(values, 0); if( sscanf(adr, "%lf", &SC_MAX_LOAD(scp)) < 1 ) { parsemsg(LOG_ERR, func, "error reading max_load argument"); return( FAILED ); } if( SC_MAX_LOAD(scp) == 0 ) { parsemsg(LOG_ERR, func, "error parsing max_load argument"); return( FAILED ); } return OK; } #endif status_e redir_parser(pset_h values, struct service_config *scp, enum assign_op op) { char *adr = (char *)pset_pointer(values, 0); const char *func = "redir_parser"; char *port_char; int port_int; struct addrinfo hints, *res; port_char = pset_pointer(values, 1); if (parse_base10(port_char, &port_int) || port_int <= 0) { /* OK, maybe its a service name... */ struct servent *entry; entry = getservbyname(port_char, "tcp"); if (entry == 0) { parsemsg(LOG_ERR, func, "port number invalid"); return FAILED; } port_int = ntohs(entry->s_port); } if (port_int >= PORT_MAX) { parsemsg(LOG_ERR, func, "port number too large"); return FAILED; } SC_REDIR_ADDR(scp) = (union xsockaddr *)malloc(sizeof(union xsockaddr)); if( SC_REDIR_ADDR(scp) == NULL ) { parsemsg(LOG_ERR, func, "can't allocate space for redir addr"); return FAILED; } memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; if (strchr(adr, ':')) hints.ai_family = AF_INET6; else hints.ai_family = AF_INET; if( getaddrinfo(adr, NULL, &hints, &res) < 0 ) { parsemsg(LOG_ERR, func, "bad address"); free( SC_REDIR_ADDR(scp) ); SC_REDIR_ADDR(scp) = NULL; return FAILED; } if( (res == NULL) || (res->ai_addr == NULL) ) { parsemsg(LOG_ERR, func, "no addresses returned"); free( SC_REDIR_ADDR(scp) ); SC_REDIR_ADDR(scp) = NULL; return FAILED; } if( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6) ) memcpy(SC_REDIR_ADDR(scp), res->ai_addr, res->ai_addrlen); if( SC_REDIR_ADDR(scp)->sa.sa_family == AF_INET ) SC_REDIR_ADDR(scp)->sa_in.sin_port = port_int; if( SC_REDIR_ADDR(scp)->sa.sa_family == AF_INET6 ) SC_REDIR_ADDR(scp)->sa_in6.sin6_port = port_int; freeaddrinfo(res); return OK; } status_e bind_parser( pset_h values, struct service_config *scp, enum assign_op op) { char *adr = (char *)pset_pointer(values, 0); const char *func = "bind_parser"; struct addrinfo hints, *res, *ressave; int addr_cnt = 0; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; /* * Use tcp to cut down returned address records. Get addrinfo normally * returns 2 address records, one for each socket type. */ hints.ai_socktype = SOCK_STREAM; if (check_hostname(adr) == 0) { hints.ai_family = AF_INET; hints.ai_flags |= AI_NUMERICHOST; } else if (strchr(adr, ':')) { hints.ai_family = AF_INET6; hints.ai_flags |= AI_NUMERICHOST; } else { hints.ai_family = AF_UNSPEC; } if( getaddrinfo(adr, NULL, &hints, &res) < 0 ) { parsemsg(LOG_ERR, func, "bad address"); return( FAILED ); } if( (res == NULL) || (res->ai_addr == NULL) ) { parsemsg(LOG_ERR, func, "no addresses returned"); return( FAILED ); } /* * If more than 1 record comes back, we need to defer selection * until we are finished reading all attributes of the service. * Hopefully, they will have specified IPv4 or IPv6. */ ressave = res; while (res) { addr_cnt++; res = res->ai_next; } res = ressave; if (addr_cnt == 1) { SC_BIND_ADDR(scp) = (union xsockaddr *)malloc(sizeof(union xsockaddr)); if( SC_BIND_ADDR(scp) == NULL ) { parsemsg(LOG_ERR, func, "can't allocate space for bind addr"); return( FAILED ); } memcpy(SC_BIND_ADDR(scp), res->ai_addr, res->ai_addrlen); } else SC_ORIG_BIND_ADDR(scp) = new_string(adr); freeaddrinfo(res); return( OK ); } status_e access_times_parser( pset_h values, struct service_config *scp, enum assign_op op ) { unsigned u, count ; const char *func = "access_times_parser" ; NEW_SET( SC_ACCESS_TIMES(scp), 0, 0 ) ; count = pset_count( values) ; if ( count == 0 ) { missing_attr_msg("access_times_parser", "access_times"); return FAILED; } for ( u = 0 ; u < count ; u++ ) { register char *interval = (char *) pset_pointer( values, u ) ; if ( ti_add( SC_ACCESS_TIMES(scp), interval ) == FAILED ) return FAILED ; } return OK ; } status_e nice_parser( pset_h values, struct service_config *scp, enum assign_op op ) { if ( parse_base10((char *) pset_pointer( values, 0 ), &SC_NICE(scp)) ) { parsemsg(LOG_ERR, "nice_parser", "Error parsing: %s", (char *)pset_pointer( values, 0 )); return( FAILED ); } return( OK ) ; } #ifdef RLIMIT_AS status_e rlim_as_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *mem = (char *) pset_pointer( values, 0 ) ; const char *func = "rlim_as_parser" ; if ( EQ( mem, "UNLIMITED" ) ) SC_RLIM_AS(scp) = (rlim_t)RLIM_INFINITY ; else { if ( get_limit ( mem, &SC_RLIM_AS(scp)) ) { parsemsg( LOG_ERR, func, "Address space limit is invalid: %s", mem ) ; return( FAILED ) ; } } return( OK ) ; } #endif #ifdef RLIMIT_CPU status_e rlim_cpu_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *cpu_str = (char *) pset_pointer( values, 0 ) ; unsigned long long cpu_int; const char *func = "rlim_cpu_parser" ; if ( EQ( cpu_str, "UNLIMITED" ) ) SC_RLIM_CPU(scp) = (rlim_t)RLIM_INFINITY ; else { if ( parse_ull(cpu_str, 10, -1, &cpu_int) < 0 ) { parsemsg( LOG_ERR, func, "CPU limit is invalid: %s", cpu_str ) ; return( FAILED ) ; } SC_RLIM_CPU(scp) = (rlim_t) cpu_int ; if ( SC_RLIM_CPU(scp) != cpu_int ) { parsemsg( LOG_ERR, func, "CPU limit is invalid: %s", cpu_str ); return( FAILED ); } } return( OK ) ; } #endif #ifdef RLIMIT_DATA status_e rlim_data_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *mem = (char *) pset_pointer( values, 0 ) ; const char *func = "rlim_data_parser" ; if ( EQ( mem, "UNLIMITED" ) ) SC_RLIM_DATA(scp) = (rlim_t)RLIM_INFINITY ; else { if ( get_limit ( mem, &SC_RLIM_DATA(scp) ) ) { parsemsg( LOG_ERR, func, "Data limit is invalid: %s", mem ) ; return( FAILED ) ; } } return( OK ) ; } #endif #ifdef RLIMIT_NOFILE status_e rlim_files_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *mem = (char *) pset_pointer( values, 0 ) ; const char *func = "rlim_files_parser" ; if ( EQ( mem, "UNLIMITED" ) ) SC_RLIM_FILES(scp) = (rlim_t)RLIM_INFINITY ; else { if ( get_limit ( mem, &SC_RLIM_FILES(scp)) ) { parsemsg( LOG_ERR, func, "Max files limit is invalid: %s", mem ) ; return( FAILED ) ; } } return( OK ) ; } #endif #ifdef RLIMIT_RSS status_e rlim_rss_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *mem = (char *) pset_pointer( values, 0 ) ; const char *func = "rlim_rss_parser" ; if ( EQ( mem, "UNLIMITED" ) ) SC_RLIM_RSS(scp) = (rlim_t)RLIM_INFINITY ; else { if ( get_limit ( mem, &SC_RLIM_RSS(scp) ) ) { parsemsg( LOG_ERR, func, "RSS limit is invalid: %s", mem ) ; return( FAILED ) ; } } return( OK ) ; } #endif #ifdef RLIMIT_STACK status_e rlim_stack_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *mem = (char *) pset_pointer( values, 0 ) ; const char *func = "rlim_stack_parser" ; if ( EQ( mem, "UNLIMITED" ) ) SC_RLIM_STACK(scp) = (rlim_t)RLIM_INFINITY ; else { if ( get_limit ( mem, &SC_RLIM_STACK(scp) ) ) { parsemsg( LOG_ERR, func, "Stack limit is invalid: %s", mem ) ; return( FAILED ) ; } } return( OK ) ; } #endif status_e deny_time_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *deny_time = (char *) pset_pointer( values, 0 ) ; if ( EQ( deny_time, "FOREVER" ) ) SC_DENY_TIME(scp) = -1 ; else if ( EQ( deny_time, "NEVER" ) ) SC_DENY_TIME(scp) = 0 ; else if ( parse_base10( deny_time, &SC_DENY_TIME(scp) ) ) { parsemsg(LOG_ERR, "deny_time_parser", "Error parsing: %s", deny_time); return( FAILED ); } return( OK ) ; } status_e umask_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *umask_str = (char *)pset_pointer(values, 0); int umask_int; if( parse_int(umask_str, 8, -1, &umask_int) || umask_int < 0 || umask_int > 0777) { parsemsg(LOG_ERR, "umask_parser", "umask argument is invalid.\n"); return( FAILED ); } SC_UMASK(scp) = umask_int; return( OK ); } #ifdef LIBWRAP status_e libwrap_parser( pset_h values, struct service_config *scp, enum assign_op op ) { char *libwrap = (char *) pset_pointer( values, 0 ) ; const char *func = "libwrap_parser" ; SC_LIBWRAP(scp) = new_string( libwrap ) ; if ( SC_LIBWRAP(scp) == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } return( OK ) ; } #endif xinetd-2.3.15.4/src/parsers.h000066400000000000000000000075651333055217000156720ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef _X_PARSERS #define _X_PARSERS #include "config.h" #include "pset.h" #include "defs.h" #include "parse.h" status_e socket_type_parser(pset_h, struct service_config *, enum assign_op) ; status_e protocol_parser(pset_h, struct service_config *, enum assign_op) ; status_e wait_parser(pset_h, struct service_config *, enum assign_op) ; status_e user_parser(pset_h, struct service_config *, enum assign_op) ; status_e group_parser(pset_h, struct service_config *, enum assign_op) ; status_e server_parser(pset_h, struct service_config *, enum assign_op) ; status_e server_args_parser(pset_h, struct service_config *, enum assign_op) ; status_e instances_parser(pset_h, struct service_config *, enum assign_op) ; status_e log_on_success_parser(pset_h, struct service_config *, enum assign_op) ; status_e log_on_failure_parser(pset_h, struct service_config *, enum assign_op) ; status_e log_type_parser(pset_h, struct service_config *, enum assign_op) ; status_e only_from_parser(pset_h, struct service_config *, enum assign_op) ; status_e no_access_parser(pset_h, struct service_config *, enum assign_op) ; status_e access_times_parser(pset_h, struct service_config *, enum assign_op) ; status_e type_parser(pset_h, struct service_config *, enum assign_op) ; status_e id_parser(pset_h, struct service_config *, enum assign_op) ; status_e env_parser(pset_h, struct service_config *, enum assign_op) ; status_e port_parser(pset_h, struct service_config *, enum assign_op) ; status_e rpc_version_parser(pset_h, struct service_config *, enum assign_op) ; status_e passenv_parser(pset_h, struct service_config *, enum assign_op) ; status_e flags_parser(pset_h, struct service_config *, enum assign_op) ; status_e disabled_parser(pset_h, struct service_config *, enum assign_op) ; status_e rpc_number_parser(pset_h, struct service_config *, enum assign_op) ; status_e nice_parser(pset_h, struct service_config *, enum assign_op) ; status_e redir_parser(pset_h, struct service_config *, enum assign_op) ; status_e bind_parser(pset_h, struct service_config *, enum assign_op) ; status_e banner_parser(pset_h, struct service_config *, enum assign_op) ; status_e per_source_parser(pset_h, struct service_config *, enum assign_op) ; status_e groups_parser(pset_h, struct service_config *, enum assign_op) ; status_e banner_success_parser(pset_h, struct service_config *, enum assign_op) ; status_e banner_fail_parser(pset_h, struct service_config *, enum assign_op) ; status_e cps_parser(pset_h, struct service_config *, enum assign_op) ; status_e enabled_parser(pset_h, struct service_config *, enum assign_op) ; status_e svcdisable_parser(pset_h, struct service_config *, enum assign_op); #ifdef HAVE_LOADAVG status_e max_load_parser(pset_h, struct service_config *, enum assign_op) ; #endif #ifdef RLIMIT_AS status_e rlim_as_parser(pset_h, struct service_config *, enum assign_op) ; #endif #ifdef RLIMIT_CPU status_e rlim_cpu_parser(pset_h, struct service_config *, enum assign_op) ; #endif #ifdef RLIMIT_DATA status_e rlim_data_parser(pset_h, struct service_config *, enum assign_op) ; #endif #ifdef RLIMIT_NOFILE status_e rlim_files_parser(pset_h, struct service_config *, enum assign_op) ; #endif #ifdef RLIMIT_RSS status_e rlim_rss_parser(pset_h, struct service_config *, enum assign_op) ; #endif #ifdef RLIMIT_STACK status_e rlim_stack_parser(pset_h, struct service_config *, enum assign_op) ; #endif status_e v6only_parser(pset_h, struct service_config *, enum assign_op); status_e deny_time_parser(pset_h, struct service_config *, enum assign_op) ; status_e umask_parser(pset_h, struct service_config *, enum assign_op) ; status_e mdns_parser(pset_h, struct service_config *, enum assign_op) ; #ifdef LIBWRAP status_e libwrap_parser(pset_h, struct service_config *, enum assign_op) ; #endif #endif xinetd-2.3.15.4/src/parsesup.c000066400000000000000000000114501333055217000160340ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #ifdef HAVE_STDINT_H #include #endif #include #include #include "sio.h" #include "str.h" #include "parsesup.h" #include "msg.h" /* * next_line returns the next line of the file or NULL if the end of file * is reached. * Comment lines and empty lines are skipped. */ char *next_line( int fd ) { for ( ;; ) { char *p ; char *line = Srdline( fd ) ; if ( line == NULL ) return( NULL ) ; line_count++ ; for ( p = line ;; p++ ) if ( *p == NUL || *p == COMMENT_BEGIN ) break ; /* skip this line */ else if ( isspace( *p ) ) continue ; /* skip white space */ else return( line ) ; } } /* * Input: * a line of the form * name [SPACE] OP [SPACE] value [SPACE] value ... * * Recognize the attribute name and operator and place them in *attrp, *opp * * Currently, we allow any non-space character to be used in the * attribute name. * * Return value: a pointer to the character after OP. */ static char *get_attr_op( char *line, char **attrp, enum assign_op *opp ) { char *p ; char *attr ; enum assign_op op ; const char *func = "get_attr_op" ; /* * First get the attribute name */ for ( p = line ; isspace( *p ) ; p++ ) ; /* skip spaces */ if ( *p == NUL ) { parsemsg( LOG_ERR, func, "Empty line" ) ; return( NULL ) ; } attr = p ; for ( ; ! isspace( *p ) && (*p != '='); p++ ) ; /* skip attribute name */ if ( *p == NUL ) { parsemsg( LOG_ERR, func, "Nothing after attribute: %s", attr ) ; return( NULL ) ; } if( *p == '=' ) { *p = NUL ; /* now attribute name is NUL terminated */ parsemsg( LOG_ERR, func, "Attribute %s needs a space before operator", attr); return( NULL ) ; } *p++ = NUL ; /* now attribute name is NUL terminated */ while ( isspace( *p ) ) p++ ; /* skip spaces */ switch ( *p ) { case NUL: parsemsg( LOG_ERR, func, "Nothing after attribute: %s", attr ) ; return( NULL ) ; case '=': op = SET_EQ ; break ; case '+': case '-': op = ( *p++ == '+' ) ? PLUS_EQ : MINUS_EQ ; if ( *p == '=' ) break ; /* FALL THROUGH if there is no '=' after the '+' or '-' */ default: parsemsg( LOG_ERR, func, "Bad operator for attribute: %s", attr ) ; return( NULL ) ; } *attrp = attr ; *opp = op ; return( ++p ) ; /* skip the '=' */ } /* * Parse a line of the form: * name OP value value value ... * where each value is a string and OP can be '=', '+=', '-=' * * NOTE: We do not allocate space for the name and values. Instead we keep * pointers to the line. */ status_e parse_line( char *line, char **namep, enum assign_op *opp, pset_h values ) { char *value ; char *values_string ; char *attribute ; str_h strp ; const char *func = "parse_line" ; if ( ( values_string = get_attr_op( line, &attribute, opp ) ) == NULL ) return( FAILED ) ; /* * Now grab the values */ strp = str_parse( values_string, " \t", STR_RETURN_ERROR, (int *)0 ) ; if ( strp == NULL ) { parsemsg( LOG_CRIT, func, ES_NOMEM ) ; return( FAILED ) ; } while ( (value = str_component( strp )) ) { if ( pset_add( values, value ) == NULL ) { parsemsg( LOG_CRIT, func, ES_NOMEM ) ; str_endparse( strp ) ; return( FAILED ) ; } } str_endparse( strp ) ; *namep = attribute ; return( OK ) ; } void skip_entry( int fd ) { for ( ;; ) { char *line = next_line( fd ) ; if ( line == NULL ) /* reached EOF ? */ { parsemsg( LOG_WARNING, "skip_entry", "missing %c in last service entry", ENTRY_END ) ; break ; } if ( line_has_only_1_char( line, ENTRY_END ) ) break ; } } /* * Returns TRUE if the given line contains a single instance of the * specified character and no other non-space characters */ int line_has_only_1_char( const char *line, char ch ) { const char *p ; char target_char = ch ; for ( p = line ; *p ; p++ ) if ( *p == target_char ) target_char = NUL ; else if ( ! isspace( *p ) ) return( FALSE ) ; return( target_char != ch ) ; } xinetd-2.3.15.4/src/parsesup.h000066400000000000000000000003771333055217000160470ustar00rootroot00000000000000#ifndef PARSESUP_H #define PARSESUP_H #include "parse.h" char *next_line(int fd); status_e parse_line( char *line, char **namep, enum assign_op *opp, pset_h values); void skip_entry(int fd); int line_has_only_1_char(const char *line, char ch); #endif xinetd-2.3.15.4/src/portable/000077500000000000000000000000001333055217000156355ustar00rootroot00000000000000xinetd-2.3.15.4/src/portable/cvt.c000066400000000000000000000026721333055217000166040ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #ifndef FLOAT_TYPE #define FLOAT_TYPE double #define FUNC_PREFIX #define FLOAT_FMT_FLAG #define FLOAT_NAME_EXT #endif #ifndef MAXDIG #define MAXDIG 120 #endif #ifndef APPEND #define APPEND(a, b) APPEND2 (a, b) #endif #ifndef APPEND2 #define APPEND2(a, b) a##b #endif #ifndef FLOOR #define FLOOR APPEND(floor, FLOAT_NAME_EXT) #endif #ifndef FABS #define FABS APPEND(fabs, FLOAT_NAME_EXT) #endif #ifndef LOG10 #define LOG10 APPEND(log10, FLOAT_NAME_EXT) #endif #ifndef EXP #define EXP APPEND(exp, FLOAT_NAME_EXT) #endif #ifndef ISINF #define ISINF APPEND(isinf, FLOAT_NAME_EXT) #endif #ifndef ISNAN #define ISNAN APPEND(isnan, FLOAT_NAME_EXT) #endif #ifndef EINVAL #define EINVAL 22 #endif #ifndef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) #endif #ifndef __set_errno #define __set_errno(x) errno = (x) #endif #define weak_extern2(name) weak_extern2 (FLOOR) weak_extern2 (LOG10) weak_extern2 (FABS) weak_extern2 (EXP) #ifndef HAVE_GCVT char * APPEND (FUNC_PREFIX, gcvt) (FLOAT_TYPE value, int ndigit, char *buf) { sprintf (buf, "%.*" FLOAT_FMT_FLAG "g", ndigit, value); return buf; } #endif /* HAVE_GCVT */ xinetd-2.3.15.4/src/portable/libportable.h000066400000000000000000000016071333055217000203110ustar00rootroot00000000000000#ifndef _XINETD_LIBPORTABLE #define _XINETD_LIBPORTABLE 1 #include "config.h" #include #include #include #include #ifndef IPV6_ADDRFORM #define IPV6_ADDRFORM 1 #endif #ifndef NI_MAXHOST #define NI_MAXHOST 1025 #endif #ifndef APPEND #define APPEND(a, b) APPEND2 (a, b) #endif #ifndef APPEND2 #define APPEND2(a, b) a##b #endif #ifndef FLOAT_TYPE #define FLOAT_TYPE double #endif #ifndef FUNC_PREFIX #define FUNC_PREFIX #endif /* from OpenSSH's fake-socket.h */ #ifndef IN6_IS_ADDR_LOOPBACK # define IN6_IS_ADDR_LOOPBACK(a) \ (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == htonl (1)) #endif /* !IN6_IS_ADDR_LOOPBACK */ #ifndef AF_INET6 /* Define it to something that should never appear */ #define AF_INET6 AF_MAX #endif #endif /* _XINETD_LIBPORTABLE */ xinetd-2.3.15.4/src/pset/000077500000000000000000000000001333055217000150005ustar00rootroot00000000000000xinetd-2.3.15.4/src/pset/ops.c000066400000000000000000000025111333055217000157440ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include "pset.h" #include #define POINTER __pset_pointer /* * Remove all NULL pointers from a pset */ void pset_compact( register pset_h pset ) { register unsigned u ; for ( u = 0 ; u < pset_count( pset ) ; ) { POINTER ptr = pset_pointer( pset, u ); if ( ptr != NULL ) u++ ; else pset_delete( pset, ptr ) ; } /* See if we can reclaim some memory, make sure we are 2 below for some hysteresis */ if ((int)( pset->max - pset->alloc_step - 2) > (int)pset_count( pset )) { /* This rounds up to the next unit of steps */ POINTER *new_ptrs ; unsigned new_max = ((pset_count( pset ) / pset->alloc_step) + 1)*pset->alloc_step; new_ptrs = (POINTER *) realloc( (char *)pset->ptrs, new_max * sizeof( POINTER ) ) ; if ( new_ptrs == NULL ) return; pset->max = new_max ; pset->ptrs = new_ptrs ; } } /* * Apply a function to all pointers of a pset */ void pset_apply( register pset_h pset, void (*func)(), register void *arg ) { register unsigned u ; for ( u = 0 ; u < pset_count( pset ) ; u++ ) if ( arg ) (*func)( arg, pset_pointer( pset, u ) ) ; else (*func)( pset_pointer( pset, u ) ) ; } xinetd-2.3.15.4/src/pset/pset.c000066400000000000000000000052471333055217000161270ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include "pset.h" #define ALLOC_START 20 #define ALLOC_STEP 10 static __pset_pointer pset_insert( pset_h pset, const __pset_pointer p ); /* * Create a pointer set and return a handle to it. * Some space is initially allocated for the set. */ pset_h pset_create( unsigned alloc_start, unsigned alloc_step ) { pset_h pset ; unsigned start ; pset = (pset_h) malloc( sizeof( struct __pset ) ) ; if ( pset == NULL ) return( NULL ) ; start = ( alloc_start == 0 ) ? ALLOC_START : alloc_start ; pset->ptrs = (__pset_pointer *) malloc( start * sizeof( __pset_pointer ) ) ; if ( pset->ptrs == NULL ) { free( (char *) pset ) ; return( NULL ) ; } pset->max = start ; pset->count = 0 ; pset->alloc_step = ( alloc_step == 0 ) ? ALLOC_STEP : alloc_step ; return( pset ) ; } /* * Destroy a pset */ void pset_destroy( pset_h pset ) { if ( pset == NULL ) return; free( (char *) pset->ptrs ) ; free( (char *) pset ) ; } __pset_pointer pset_add( pset_h pset, const __pset_pointer ptr ) { return (pset->count < pset->max ) ? (pset->ptrs[ pset->count++ ] = ptr) : pset_insert( pset, ptr) ; } /* * Append a pointer to a pset */ static __pset_pointer pset_insert( pset_h pset, const __pset_pointer p ) { if ( pset->count >= pset->max ) { unsigned new_max = pset->max + pset->alloc_step ; __pset_pointer *new_ptrs ; new_ptrs = (__pset_pointer *) realloc( (char *)pset->ptrs, new_max * sizeof( __pset_pointer ) ) ; if ( new_ptrs == NULL ) return( NULL ) ; pset->max = new_max ; pset->ptrs = new_ptrs ; } return( pset->ptrs[ pset->count++ ] = p ) ; } /* * Remove a pointer from a pset by moving every thing above it down 1 spot. */ void pset_delete( register pset_h pset, register const __pset_pointer ptr ) { register unsigned u = 0; register int found_it = 0; if ( pset->count == 0 ) return ; while ( u < pset->count ) { if ( pset->ptrs[ u ] == ptr ) found_it = 1; if ( found_it ) { /* If not the last one, copy it */ if ( (u+1) < pset->count ) pset->ptrs[ u ] = pset->ptrs[ u+1 ]; } u++; } pset->count--; } /* * Create a pset iterator */ psi_h psi_create( pset_h pset ) { psi_h iter = (psi_h) malloc( sizeof( struct __pset_iterator ) ) ; if ( iter == NULL ) return( NULL ) ; iter->pset = pset ; return( iter ) ; } /* * Remove an element from a pset */ void psi_remove( psi_h iter ) { if ( iter->current < pset_count( iter->pset ) ) { pset_remove_index( iter->pset, iter->current ) ; iter->step = 0; } } xinetd-2.3.15.4/src/pset/pset.h000066400000000000000000000046151333055217000161320ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef __PSET_H #define __PSET_H /* * $Id$ */ #include typedef void *__pset_pointer ; struct __pset { unsigned alloc_step ; __pset_pointer *ptrs ; /* void Pointer to a Pointer */ unsigned max ; unsigned count ; } ; /* * INTERFACE */ typedef struct __pset *pset_h ; pset_h pset_create( unsigned alloc_start, unsigned alloc_step ); void pset_destroy( pset_h pset ); void pset_delete( pset_h pset, const __pset_pointer ptr ); __pset_pointer pset_add( pset_h pset, const __pset_pointer ptr ); /* These 2 are in ops.c */ void pset_compact( pset_h pset ); void pset_apply( pset_h pset, void (*func)(), void *arg ); /* * Macros */ #define pset_remove( pset, ptr ) pset_delete( pset, (__pset_pointer)(ptr) ) #define pset_remove_index( pset, i ) \ { \ if ( ((unsigned)i) < (pset)->count ) \ pset_delete(pset, (pset)->ptrs[ (unsigned)(i) ]); \ } #define pset_clear( pset ) (pset)->count = 0 #define pset_count( pset ) (pset)->count #define pset_pointer( pset, i ) (pset)->ptrs[ (unsigned)(i) ] #define pset_sort( pset, compfunc ) \ (void) qsort( (char *) &pset_pointer( pset, 0 ), \ pset_count( pset ), sizeof( __pset_pointer ), compfunc ) /* * PSET iterators * * Note that the iterators do NOT use any knowledge about the internals * of pset's. */ struct __pset_iterator { pset_h pset ; unsigned current ; int step ; } ; typedef struct __pset_iterator *psi_h ; void psi_remove( psi_h iter ); #define __psi_current( iter ) \ ( (iter)->current < pset_count( (iter)->pset ) \ ? pset_pointer( (iter)->pset, (iter)->current ) \ : NULL ) #define psi_start( iter ) \ ( (iter)->current = 0, (iter)->step = 1, \ __psi_current( iter ) ) #define psi_next( iter ) \ ( (iter)->current += (iter)->step, (iter)->step = 1, \ __psi_current( iter ) ) #define psi_destroy( iter ) free( (char *) iter ) psi_h psi_create( pset_h pset ); #endif /* __PSET_H */ xinetd-2.3.15.4/src/reconfig.c000066400000000000000000000354371333055217000160010ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #ifndef NO_RPC #ifdef __sun #include #include #endif #include #include #include #include #include #include #endif #include "reconfig.h" #include "msg.h" #include "sconf.h" #include "conf.h" #include "confparse.h" #include "state.h" #include "main.h" #include "retry.h" #include "logctl.h" #include "options.h" static status_e readjust(struct service *sp, struct service_config **new_conf_ptr) ; static void swap_defaults(struct configuration *new_conf) ; static void close_default_log(struct configuration *confp, xlog_h def_log); #define SWAP( x, y, temp ) (temp) = (x), (x) = (y), (y) = (temp) /* * Reconfigure the server by rereading the configuration file. * Services may be added, deleted or have their attributes changed. * All syslog output uses the LOG_NOTICE priority level (except for * errors). */ void hard_reconfig( void ) { struct service *osp ; struct service_config *nscp ; struct configuration new_conf ; psi_h iter ; unsigned new_services ; unsigned old_services = 0 ; unsigned dropped_services = 0 ; xlog_h def_log = DEFAULT_LOG( ps ); const char *func = "hard_reconfig" ; msg( LOG_NOTICE, func, "Starting reconfiguration" ) ; if ( cnf_get( &new_conf ) == FAILED ) { msg( LOG_WARNING, func, "reconfiguration failed" ) ; return ; } iter = psi_create( SERVICES( ps ) ) ; if ( iter == NULL ) { out_of_memory( func ) ; cnf_free( &new_conf ) ; return ; } /* After this call, new_conf's defaults point to the old one's defaults */ msg( LOG_NOTICE, func, "Swapping defaults" ) ; swap_defaults( &new_conf ) ; /* * Glossary: * Sconf: service configuration * Lconf: list of service configurations * * Iterate over all existing services. If the service is included in the * new Lconf, readjust its attributes (as a side-effect, the new service * Sconf is removed from the new Lconf). * Services not in the new Lconf are deactivated. */ for ( osp = SP( psi_start( iter ) ) ; osp ; osp = SP( psi_next( iter ) ) ) { char *sid = SVC_ID( osp ) ; boolean_e drop_service ; /* * Check if this service is in the new Lconf * Notice that the service Sconf is removed from the new Lconf * if it is found there. */ if ( (nscp = cnf_extract( &new_conf, SVC_CONF( osp ) )) ) { /* * The first action of readjust is to swap the service configuration * with nscp. This is the reason for passing the address of nscp * (so that on return nscp will *always* point to the old service * configuration). */ if ( readjust( osp, &nscp ) == OK ) { old_services++ ; drop_service = NO ; } else /* the readjustment failed */ drop_service = YES ; sc_free( nscp ) ; } else drop_service = YES ; if ( drop_service == YES ) { /* * Procedure for disabling a service: * * a. Deactivate the service to prevent new connections * b. Terminate running servers and cancel retry attempts, in case * of reconfiguration */ terminate_servers( osp ) ; svc_deactivate( osp ) ; cancel_service_retries( osp ) ; /* * Deactivate the service; the service will be deleted only * if its reference count drops to 0. */ /* Always remove the service, even if not all the children * have been killed, or there are other references. */ psi_remove( iter ) ; msg( LOG_NOTICE, func, "service %s deactivated", sid ) ; /* * If the refcount is > 0 release also all the members */ if ( SVC_RELE( osp ) > 0 ) { svc_release( osp ); } dropped_services++ ; } } psi_destroy( iter ) ; /* * All services have terminated by now, so close the old common logfile. * remember that swap_defaults put the old defaults section in new_conf. */ close_default_log( &new_conf, def_log ) ; /* * At this point the new Lconf only contains services that were not * in the old Lconf. */ new_services = cnf_start_services( &new_conf ) ; msg( LOG_NOTICE, func, "Reconfigured: new=%d old=%d dropped=%d (services)", new_services, old_services, dropped_services ) ; if ( stayalive_option == 0 ) { if ( ps.rws.available_services == 0 ) { msg( LOG_CRIT, func, "No available services. Exiting" ); if ( ps.ros.pid_file ) { unlink(ps.ros.pid_file); } exit( 1 ) ; } } cnf_free( &new_conf ) ; } static void swap_defaults( struct configuration *new_conf ) { struct service_config *temp ; DEFAULT_LOG_ERROR( ps ) = FALSE ; DEFAULT_LOG( ps ) = NULL ; SWAP( DEFAULTS( ps ), CNF_DEFAULTS( new_conf ), temp ) ; } static void close_default_log(struct configuration *confp, xlog_h def_log) { /* Close the common log file, if one was specified */ if ( def_log != NULL ) log_end( SC_LOG( CNF_DEFAULTS( confp ) ), def_log) ; } static void sendsig( struct server *serp, int sig ) { char *sid = SVC_ID( SERVER_SERVICE( serp ) ) ; pid_t pid = SERVER_PID( serp ) ; const char *func = "sendsig" ; /* * Always use a positive pid, because of the semantics of kill(2) */ if ( pid > 0 ) { msg( LOG_WARNING, func, "Sending signal %d to %s server %d", sig, sid, pid ) ; kill( pid, sig ) ; if ((sig == SIGTERM) || (sig == SIGKILL)) { int i, killed = 0; struct timeval tv; /* * We will try 4 seconds to TERM or KILL it. If it hasn't * responded by 2.5 seconds, we will send a KILL to hasten * its demise. */ tv.tv_sec = 0; tv.tv_usec = 500000; /* half a second */ for (i=0; i<8; i++) { if( server_lookup(pid) == NULL ) { killed = 1; break; } else { int wret = waitpid(pid, NULL, WNOHANG); if (wret == pid) { killed = 1; break; } server_end(serp); } /* May not have responded to TERM, send a KILL */ if ( i == 5) kill( pid, SIGKILL ) ; /* Not dead yet, give some time. */ select(0, NULL, NULL, NULL, &tv); } /* * If it didn't die, expect problems rebinding to this port if * a hard_reconfig is in process. */ if (!killed) msg( LOG_ERR, func, "Server %d did not exit after SIGKILL", pid ) ; /* no need to server_end() here. The killed process will generate * a sigchld, which will invoke the signal handler, and clean things * up there. */ } } else if ( pid != 0 ) msg( LOG_ERR, func, "Negative server pid = %d. Service %s", pid, sid ) ; } /* * Send signal sig to all running servers of service sp */ static void deliver_signal( struct service *sp, int sig ) { unsigned u ; for ( u = 0 ; u < pset_count( SERVERS( ps ) ) ; u++ ) { struct server *serp ; serp = SERP( pset_pointer( SERVERS( ps ), u ) ) ; if ( SERVER_SERVICE( serp ) == sp ) { sendsig( serp, sig ) ; if ( (sig == SIGTERM) || (sig == SIGKILL) ) u--; } } } /* * Terminate all servers of the specified service */ void terminate_servers( struct service *sp ) { int sig = SC_IS_INTERNAL( SVC_CONF( sp ) ) ? SIGTERM : SIGKILL ; deliver_signal( sp, sig ) ; } static void stop_interception( struct service *sp ) { deliver_signal( sp, INTERCEPT_SIG ) ; } /* * Stop logging. svc_activate starts logging and will leak a file * descriptor and memory if this is not called prior. */ static void stop_log( struct service *sp, struct service_config *old_conf ) { struct log *lp = SC_LOG( old_conf ) ; if ( LOG_GET_TYPE( lp ) != L_NONE && SVC_IS_LOGGING( sp ) ) log_end( lp, SVC_LOG( sp ) ) ; SVC_LOG( sp ) = NULL ; } /* * Stop any logging and restart if necessary. * Note that this has the side-effect of using the new common log * handle as it should. */ static status_e restart_log( struct service *sp, struct service_config *old_conf ) { stop_log( sp, old_conf ); return( log_start( sp, &SVC_LOG( sp ) ) ) ; } /* * Unregister past versions, register new ones * We do it the dumb way: first unregister; then register * We try to be a little smart by checking if there has * been any change in version numbers (if not, we do nothing). * Also, we save the port number */ static status_e readjust_rpc_service( struct service_config *old_scp, struct service_config *new_scp ) { unsigned long vers ; uint16_t port = SC_PORT( old_scp ) ; struct rpc_data *new_rdp = SC_RPCDATA( new_scp ) ; struct rpc_data *old_rdp = SC_RPCDATA( old_scp ) ; unsigned registered_versions = 0 ; const char *func = "readjust_rpc_service" ; #ifndef NO_RPC SC_PORT( new_scp ) = SC_PORT( old_scp ) ; if ( RD_MINVERS( old_rdp ) == RD_MINVERS( new_rdp ) && RD_MAXVERS( old_rdp ) == RD_MAXVERS( new_rdp ) ) return( OK ) ; for ( vers = RD_MINVERS( old_rdp ) ; vers <= RD_MAXVERS( old_rdp ) ; vers++ ) (void) pmap_unset( RD_PROGNUM( old_rdp ), vers ) ; for ( vers = RD_MINVERS( new_rdp ) ; vers <= RD_MAXVERS( new_rdp ) ; vers++ ) if ( pmap_set( RD_PROGNUM( new_rdp ), vers, SC_PROTOVAL( new_scp ), port ) ) registered_versions++ ; else msg( LOG_ERR, func, "pmap_set failed. service=%s, program=%ld, version = %ld", SC_ID( new_scp ), RD_PROGNUM( new_rdp ), vers ) ; if ( registered_versions == 0 ) { msg( LOG_ERR, func, "No versions registered for RPC service %s", SC_ID( new_scp ) ) ; /* * Avoid the pmap_unset */ RD_MINVERS( new_rdp ) = RD_MAXVERS( new_rdp ) + 1 ; return( FAILED ) ; } #endif /* ! NO_RPC */ return( OK ) ; } /* * Readjust service attributes. * * We assume that the following attributes are the same: * wait * socket_type * type * protocol * * Readjustment happens in 3 steps: * 1) We swap the svc_conf fields * This has the side-effect of free'ing the memory associated * with the old service configuration when the new configuration * is destroyed. * 2) We readjust the fields that require some action to be taken: * RPC mapping * log file open * 3) We update the address control fields. */ static status_e readjust( struct service *sp, struct service_config **new_conf_ptr ) { struct service_config *temp_conf ; struct service_config *old_conf = SVC_CONF( sp ) ; struct service_config *new_conf = *new_conf_ptr ; char *sid = SVC_ID( sp ) ; const char *func = "readjust" ; msg( LOG_NOTICE, func, "readjusting service %s", sid ) ; SWAP( SVC_CONF( sp ), *new_conf_ptr, temp_conf ) ; if ( SC_IS_RPC( old_conf ) && readjust_rpc_service( old_conf, new_conf ) == FAILED ) return( FAILED ) ; /* * This is what happens if the INTERCEPT flag is toggled and an * interceptor is running: * * Case 1: clear->set * Wait until the server dies (soft reconfig) or * terminate the server (hard reconfig) * * Case 2: set->clear * Send a signal to the interceptor to tell it to stop intercepting */ if ( SC_IS_INTERCEPTED( old_conf ) != SC_IS_INTERCEPTED( new_conf ) ) { if ( SC_IS_INTERCEPTED( new_conf ) ) /* case 1 */ terminate_servers( sp ) ; else /* case 2 */ { stop_interception( sp ) ; msg( LOG_NOTICE, func, "Stopping interception for %s", sid ) ; } } /* * See if the bind address was specified in both the old and new config, * then if it changed, readjust the service. The algorithm is check to * see if they are in the same address family, if so start a simple * comparison based on the address family. If IPv4, the addresses can be * compared directly, otherwise use the IPv6 macro. If they are not the * same, terminate & restart the service. */ if( (SC_BIND_ADDR(old_conf) != NULL) && (SC_BIND_ADDR(new_conf) != NULL) ) { int same = 0; if ( SA(SC_BIND_ADDR(old_conf))->sa_family == SA(SC_BIND_ADDR(new_conf))->sa_family ) { if ( SA(SC_BIND_ADDR(old_conf))->sa_family == AF_INET ) { if ( SAIN(SC_BIND_ADDR(old_conf))->sin_addr.s_addr == SAIN(SC_BIND_ADDR(new_conf))->sin_addr.s_addr) same = 1; } else if ( IN6_ARE_ADDR_EQUAL( &SAIN6(SC_BIND_ADDR(old_conf))->sin6_addr, &SAIN6(SC_BIND_ADDR(new_conf))->sin6_addr) ) same = 1; } if ( !same ) { terminate_servers( sp ); svc_deactivate( sp ); stop_log( sp, old_conf ); /* svc_activate re-starts logging */ svc_activate( sp ); return OK; } } /* If the service didn't have a bind address before, but does now, * make sure the new bind directive takes effect. */ if( (SC_BIND_ADDR(old_conf) == NULL) && (SC_BIND_ADDR(new_conf) != NULL) ) { terminate_servers( sp ); svc_deactivate(sp); stop_log( sp, old_conf ); /* svc_activate re-starts logging */ svc_activate(sp); return OK; } if( (SC_IPV4(old_conf) && SC_IPV6(new_conf)) || (SC_IPV6(old_conf) && SC_IPV4(new_conf)) ) { terminate_servers( sp ); svc_deactivate(sp); stop_log( sp, old_conf ); /* svc_activate re-starts logging */ svc_activate(sp); return OK; } return( restart_log( sp, old_conf ) ) ; } xinetd-2.3.15.4/src/reconfig.h000066400000000000000000000002101333055217000157630ustar00rootroot00000000000000#ifndef RECONFIG_H #define RECONFIG_H #include "defs.h" void hard_reconfig(void); void terminate_servers(struct service *sp); #endif xinetd-2.3.15.4/src/redirect.c000066400000000000000000000163321333055217000157770ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "redirect.h" #include "service.h" #include "log.h" #include "sconf.h" #include "msg.h" #define NET_BUFFER 1500 static int RedirServerFd = -1; /* Theoretically, this gets invoked when the remote side is no * longer available for reading or writing. * So, we send a HUP to the child process, wait(), then exit. */ #ifdef __GNUC__ __attribute__ ((noreturn)) #endif static void redir_sigpipe( int signum ) { Sclose(RedirServerFd); _exit(0); } /* Do the redirection of a service */ /* This function gets called from child.c after we have been forked */ void redir_handler( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ); struct service_config *scp = SVC_CONF( sp ); int RedirDescrip = SERVER_FD( serp ); ssize_t num_read, num_wrote=0, ret=0; unsigned int sin_len = 0; unsigned long bytes_in = 0, bytes_out = 0; int no_to_nagle = 1; int on = 1, v6on; char buff[NET_BUFFER]; #ifdef HAVE_POLL struct pollfd *pfd_array; int pfds_last = 0; #else fd_set rdfd, msfd; int maxfd; struct timeval *timep = NULL; #endif const char *func = "redir_handler"; union xsockaddr serveraddr ; if( signal(SIGPIPE, redir_sigpipe) == SIG_ERR ) msg(LOG_ERR, func, "unable to setup signal handler"); close_all_svc_descriptors(); /* If it's a tcp service we are redirecting */ if( SC_PROTOVAL(scp) == IPPROTO_TCP ) { memcpy(&serveraddr, SC_REDIR_ADDR(scp), sizeof(serveraddr)); if( serveraddr.sa_in.sin_family == AF_INET ) { sin_len = sizeof( struct sockaddr_in ); RedirServerFd = socket(AF_INET, SOCK_STREAM, 0); } else if( serveraddr.sa_in.sin_family == AF_INET6 ) { sin_len = sizeof( struct sockaddr_in6 ); RedirServerFd = socket(AF_INET6, SOCK_STREAM, 0); } else { msg(LOG_ERR, func, "not a valid protocol. Use IPv4 or IPv6."); exit(0); } if( RedirServerFd < 0 ) { msg(LOG_ERR, func, "cannot create socket: %m"); exit(0); } if( SC_IPV6( scp ) ) { if( SC_V6ONLY( scp ) ) { v6on = 1; } else { v6on = 0; } #ifdef IPV6_V6ONLY if( setsockopt(RedirServerFd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6on, sizeof(v6on)) < 0 ) { msg( LOG_ERR, func, "Setting IPV6_V6ONLY option failed (%m)" ); } #endif } if( SC_KEEPALIVE( scp ) ) if (setsockopt(RedirServerFd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof( on ) ) < 0 ) msg(LOG_ERR, func, "setsockopt SO_KEEPALIVE RedirServerFd failed: %m"); if( serveraddr.sa_in.sin_family == AF_INET ) serveraddr.sa_in.sin_port = htons(serveraddr.sa_in.sin_port); if( serveraddr.sa_in.sin_family == AF_INET6 ) serveraddr.sa_in6.sin6_port = htons(serveraddr.sa_in6.sin6_port); if( connect(RedirServerFd, &serveraddr.sa, sin_len) < 0 ) { msg(LOG_ERR, func, "can't connect to remote host %s: %m", xaddrname( &serveraddr ) ); exit(0); } /* connection now established */ if (setsockopt(RedirServerFd, IPPROTO_TCP, TCP_NODELAY, (char *) &no_to_nagle, sizeof( on ) ) < 0) { msg(LOG_ERR, func, "setsockopt RedirServerFd failed: %m"); } if (setsockopt(RedirDescrip, IPPROTO_TCP, TCP_NODELAY, (char *) &no_to_nagle, sizeof( on ) ) < 0) { msg(LOG_ERR, func, "setsockopt RedirDescrip failed: %m"); } #ifdef HAVE_POLL #define REDIR_DESCRIP_INDEX 0 #define REDIR_SERVER_INDEX 1 pfd_array = (struct pollfd *)calloc(sizeof(struct pollfd),MAX_FDS); if (pfd_array == NULL) { msg( LOG_ERR, func, "Cannot allocate memory for file descriptors!\n"); exit( 1 ); } pfd_array[ REDIR_DESCRIP_INDEX ].fd = RedirDescrip; pfd_array[ REDIR_DESCRIP_INDEX ].events = POLLIN; pfd_array[ REDIR_SERVER_INDEX ].fd = RedirServerFd; pfd_array[ REDIR_SERVER_INDEX ].events = POLLIN; pfds_last += 2; #else maxfd = (RedirServerFd > RedirDescrip)?RedirServerFd:RedirDescrip; FD_ZERO(&msfd); FD_SET(RedirDescrip, &msfd); FD_SET(RedirServerFd, &msfd); #endif while(1) { #ifdef HAVE_POLL if ( poll( pfd_array, pfds_last, -1 ) <= 0 ) { #else memcpy(&rdfd, &msfd, sizeof(rdfd)); if (select(maxfd + 1, &rdfd, (fd_set *)0, (fd_set *)0, timep) <= 0) { #endif /* place for timeout code, currently does not time out */ break; } #ifdef HAVE_POLL if ( pfd_array[REDIR_DESCRIP_INDEX].revents ) { #else if (FD_ISSET(RedirDescrip, &rdfd)) { #endif do { num_read = read(RedirDescrip, buff, sizeof(buff)); if (num_read == (ssize_t)-1 && errno == EINTR) continue; if (num_read <= 0) goto REDIROUT; bytes_in += num_read; } while (num_read < 0); /* Loop until we have written everything * that was read */ num_wrote = 0; while( num_wrote < num_read ) { ret = write(RedirServerFd, buff + num_wrote, num_read - num_wrote); if (ret == -1 && errno == EINTR) continue; if (ret <= 0) goto REDIROUT; num_wrote += ret; } } #ifdef HAVE_POLL if ( pfd_array[REDIR_SERVER_INDEX].revents ) { #else if (FD_ISSET(RedirServerFd, &rdfd)) { #endif do { num_read = read(RedirServerFd, buff, sizeof(buff)); if (num_read == -1 && errno == EINTR) continue; if (num_read <= 0) goto REDIROUT; bytes_out += num_read; } while (num_read < 0); /* Loop until we have written everything * that was read */ num_wrote = 0; while( num_wrote < num_read ) { ret = write(RedirDescrip, buff + num_wrote, num_read - num_wrote); if (ret == -1 && errno == EINTR) continue; if (ret <= 0) goto REDIROUT; num_wrote += ret; } } } REDIROUT: if( M_IS_SET( SC_LOG_ON_SUCCESS(scp), LO_TRAFFIC ) ) { svc_logprint( SERVER_CONNSERVICE( serp ), "TRAFFIC", "in=%lu(bytes) out=%lu(bytes)", bytes_in, bytes_out ); } exit(0); } msg(LOG_ERR, func, "redirect with any protocol other than tcp is not supported at this time."); exit(0); } xinetd-2.3.15.4/src/redirect.h000066400000000000000000000002371333055217000160010ustar00rootroot00000000000000#ifndef REDIRECT_H #define REDIRECT_H #include "server.h" #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void redir_handler(struct server *serp); #endif xinetd-2.3.15.4/src/retry.c000066400000000000000000000120431333055217000153360ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include "pset.h" #include "retry.h" #include "state.h" #include "main.h" #include "server.h" #include "service.h" #include "connection.h" #include "xconfig.h" #include "msg.h" #include "sconf.h" #include "xtimer.h" static int retry_timer_running ; static void cancel_retry(struct server * serp ); static void stop_retry_timer(void) ; static void start_retry_timer(void) ; /* * Attempt to start all servers in the retry table */ static void server_retry(void) { unsigned servers_started = 0 ; unsigned u ; const char *func = "server_retry" ; for ( u = 0 ; u < pset_count( RETRIES( ps ) ) ; u++ ) { struct server *retry = SERP( pset_pointer( RETRIES( ps ), u ) ) ; struct service *sp = SERVER_SERVICE( retry ) ; connection_s *cp = SERVER_CONNECTION( retry ) ; /* * Drop the retry if access control fails or we have * a memory allocation problem */ if ( svc_parent_access_control( sp, cp ) == FAILED || svc_child_access_control (sp, cp) == FAILED || pset_add( SERVERS( ps ), retry ) == NULL ) { cancel_retry( retry ) ; pset_pointer( RETRIES( ps ), u ) = NULL ; continue ; } if ( server_start( retry ) == OK ) { servers_started++ ; SVC_DEC_RETRIES( sp ) ; if ( !SVC_WAITS( sp ) ) CONN_CLOSE( cp ) ; pset_pointer( RETRIES( ps ), u ) = NULL ; continue ; } else { pset_remove( SERVERS( ps ), retry ) ; if ( SERVER_FORKLIMIT( retry ) ) { /* * give up retrying */ msg( LOG_ERR, func, "service %s: too many consecutive fork failures", SVC_ID(sp) ) ; svc_log_failure( sp, cp, AC_FORK ) ; cancel_retry( retry ) ; pset_pointer( RETRIES( ps ), u ) = NULL ; continue ; } else { if ( debug.on ) msg( LOG_DEBUG, func, "fork failed for service %s. Retrying...", SVC_ID( sp ) ) ; } } } pset_compact( RETRIES( ps ) ) ; if ( debug.on ) msg( LOG_DEBUG, func, "%d servers started, %d left to retry", servers_started, pset_count( RETRIES( ps ) ) ) ; /* If there's more, start another callback */ if ( pset_count( RETRIES( ps ) ) > 0 ) { if ((retry_timer_running=xtimer_add(server_retry, RETRY_INTERVAL)) == -1) { msg( LOG_ERR, func, "xtimer_add: %m" ) ; retry_timer_running = 0; } } else retry_timer_running = 0; } /* * Schedule a retry by inserting the struct server in the retry table * and starting the timer if necessary */ status_e schedule_retry( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; const char *func = "schedule_retry" ; if ( pset_add( RETRIES( ps ), serp ) == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } SVC_INC_RETRIES( sp ) ; start_retry_timer() ; if ( debug.on ) msg( LOG_DEBUG, func, "Scheduled retry attempt for %s", SVC_ID( sp ) ) ; return( OK ) ; } /* * This function should not be called for servers that correspond to * services not in the service table because server_release will result * in releasing all memory associated with the service (since the ref * count will drop to 0). */ static void cancel_retry( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; conn_free( SERVER_CONNECTION( serp ), 1 ) ; SVC_DEC_RETRIES( sp ) ; server_release( serp ) ; } /* * Cancel all retry attempts for the specified service */ void cancel_service_retries( struct service *sp ) { unsigned u ; const char *func = "cancel_service_retries" ; if ( SVC_RETRIES( sp ) == 0 ) return ; u = 0 ; while ( u < pset_count( RETRIES( ps ) ) ) { struct server *serp ; serp = SERP( pset_pointer( RETRIES( ps ), u ) ) ; if ( SERVER_SERVICE( serp ) == sp ) { msg( LOG_NOTICE, func, "dropping retry attempt for service %s", SVC_ID( sp ) ) ; cancel_retry( serp ) ; pset_remove_index( RETRIES( ps ), u ) ; continue ; } u++ ; } if ( pset_count( RETRIES( ps ) ) == 0 ) stop_retry_timer() ; } static void start_retry_timer(void) { const char *func = "start_retry_timer" ; /* * Enable timer if necessary. */ if ( retry_timer_running == 0 ) if((retry_timer_running=xtimer_add(server_retry, RETRY_INTERVAL)) == -1 ){ msg( LOG_ERR, func, "xtimer_add: %m" ) ; retry_timer_running = 0; } } static void stop_retry_timer(void) { if ( retry_timer_running != 0) { xtimer_remove(retry_timer_running); retry_timer_running = 0 ; } } xinetd-2.3.15.4/src/retry.h000066400000000000000000000002571333055217000153470ustar00rootroot00000000000000#ifndef RETRY_H #define RETRY_H #include "config.h" #include "defs.h" status_e schedule_retry(struct server *serp); void cancel_service_retries(struct service *sp); #endif xinetd-2.3.15.4/src/sample.conf000066400000000000000000000115441333055217000161620ustar00rootroot00000000000000# # Sample configuration file for xinetd # defaults { instances = 25 log_type = FILE /var/log/servicelog log_on_success = HOST PID log_on_failure = HOST only_from = 128.138.193.0 128.138.204.0 128.138.209.0 128.138.243.0 only_from = localhost 192.231.139.0/24 disabled = tftp } # # Group 1: BSD services # # Shell, login, exec, comsat, talk, ntalk # service login { socket_type = stream protocol = tcp wait = no user = root server = /usr/etc/in.rlogind log_type = SYSLOG local4 info } service shell { socket_type = stream wait = no user = root instances = UNLIMITED flags = IDONLY log_on_success += USERID server = /usr/etc/in.rshd } service exec { socket_type = stream wait = no user = root server = /usr/etc/in.rexecd } service comsat { socket_type = dgram wait = yes user = nobody group = tty server = /usr/etc/in.comsat } service talk { socket_type = dgram wait = yes user = root server = /usr/etc/in.talkd } service ntalk { socket_type = dgram wait = yes user = root server = /usr/etc/in.ntalkd } # # Group 2: standard Internet services # # Telnet, ftp # service telnet { socket_type = stream wait = no user = root server = /usr/etc/in.telnetd bind = 127.0.0.1 log_on_failure += USERID } service telnet { socket_type = stream wait = no user = root # server = /usr/etc/in.telnetd bind = 192.231.139.175 redirect = 128.138.202.20 23 log_on_failure += USERID } service ftp { socket_type = stream wait = no user = root server = /usr/etc/in.ftpd server_args = -l instances = 4 log_on_success += DURATION USERID log_on_failure += USERID access_times = 2:00-8:59 12:00-23:59 nice = 10 } # # Group 3: other services # # # Tnamed serves the obsolete IEN-116 name server protocol. # service name { socket_type = dgram wait = yes user = root server = /usr/etc/in.tnamed } #service uucp #{ # socket_type = stream # wait = no # user = root # server = /usr/etc/in.uucpd #} service tftp { socket_type = dgram wait = yes user = root server = /usr/etc/in.tftpd server_args = -s /tftpboot } # # Group 4: information services # service finger { socket_type = stream wait = no user = nobody server = /usr/etc/in.fingerd } service systat { socket_type = stream wait = no user = nobody server = /usr/bin/ps server_args = -auwwx only_from = 128.138.209.0 log_on_success = HOST } service netstat { socket_type = stream wait = no user = nobody server = /usr/ucb/netstat server_args = -f inet only_from = 128.138.209.0 log_on_success = HOST } # # Group 5: internal services # # echo, time, daytime, chargen, servers, services # service echo { type = INTERNAL id = echo-stream socket_type = stream protocol = tcp user = root wait = no } service echo { type = INTERNAL id = echo-dgram socket_type = dgram protocol = udp user = root wait = yes } service chargen { type = INTERNAL id = chargen-stream socket_type = stream protocol = tcp user = root wait = no } service chargen { type = INTERNAL id = chargen-dgram socket_type = dgram protocol = udp user = root wait = yes } service daytime { type = INTERNAL id = daytime-stream socket_type = stream protocol = tcp user = root wait = no } service daytime { type = INTERNAL id = daytime-dgram socket_type = dgram protocol = udp user = root wait = yes } service time { type = INTERNAL id = time-stream socket_type = stream protocol = tcp user = root wait = no } service time { type = INTERNAL id = time-dgram socket_type = dgram protocol = udp user = root wait = yes } # # Group 6: RPC services # service rstatd { type = RPC flags = INTERCEPT rpc_version = 2-4 socket_type = dgram protocol = udp server = /usr/etc/rpc.rstatd wait = yes user = root } service rquotad { type = RPC rpc_version = 1 socket_type = dgram protocol = udp wait = yes user = root server = /usr/etc/rpc.rstatd } service rusersd { type = RPC rpc_version = 1-2 socket_type = dgram protocol = udp wait = yes user = root server = /usr/etc/rpc.rusersd } service sprayd { type = RPC rpc_version = 1 socket_type = dgram protocol = udp wait = yes user = root server = /usr/etc/rpc.sprayd } service walld { type = RPC rpc_version = 1 socket_type = dgram protocol = udp wait = yes user = nobody group = tty server = /usr/etc/rpc.rwalld } # # Group 7: Security Sensors # service irc { socket_type = stream wait = no user = root flags = SENSOR type = INTERNAL bind = 192.168.1.30 deny_time = 60 } xinetd-2.3.15.4/src/sconf.c000066400000000000000000000365321333055217000153120ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include "str.h" #include "sio.h" #include "sconf.h" #include "timex.h" #include "addr.h" #include "nvlists.h" #define NEW_SCONF() NEW( struct service_config ) #define FREE_SCONF( scp ) FREE( scp ) /* * Conditional free; checks if the pointer is NULL */ #define COND_FREE( x ) if ( x ) \ { \ *x = NUL ; \ free( (char *) x ) ; \ } /* * Allocate a new service_config and initialize the service name field * with 'name'; the rest of the fields are set to 0 which gives them * their default values. */ struct service_config *sc_alloc( const char *name ) { struct service_config *scp ; const char *func = "sc_alloc" ; scp = NEW_SCONF() ; if ( scp == NULL ) { out_of_memory( func ) ; return( NULL ) ; } CLEAR( *scp ) ; SC_NAME(scp) = new_string( name ) ; return( scp ) ; } static void release_string_pset( pset_h pset ) { pset_apply( pset, free, NULL ) ; pset_destroy( pset ) ; } /* * Free all malloc'ed memory for the specified service */ void sc_free( struct service_config *scp ) { #ifdef LIBWRAP COND_FREE( SC_LIBWRAP(scp) ); #endif COND_FREE( SC_NAME(scp) ) ; COND_FREE( SC_ID(scp) ) ; COND_FREE( SC_PROTONAME(scp) ) ; COND_FREE( SC_SERVER(scp) ) ; COND_FREE( (char *)SC_REDIR_ADDR(scp) ) ; COND_FREE( (char *)SC_BIND_ADDR(scp) ) ; COND_FREE( (char *)SC_ORIG_BIND_ADDR(scp) ) ; COND_FREE( (char *)SC_BANNER(scp) ) ; COND_FREE( (char *)SC_BANNER_SUCCESS(scp) ) ; COND_FREE( (char *)SC_BANNER_FAIL(scp) ) ; if ( SC_SERVER_ARGV(scp) ) { char **pp ; /* * argv[ 0 ] is a special case because it may not have been allocated yet */ if ( SC_SERVER_ARGV(scp)[ 0 ] != NULL) free( SC_SERVER_ARGV(scp)[ 0 ] ) ; for ( pp = &SC_SERVER_ARGV(scp)[ 1 ] ; *pp != NULL ; pp++ ) free( *pp ) ; free( (char *) SC_SERVER_ARGV(scp) ) ; } COND_FREE( LOG_GET_FILELOG( SC_LOG( scp ) )->fl_filename ) ; if ( SC_ACCESS_TIMES(scp) != NULL ) { ti_free( SC_ACCESS_TIMES(scp) ) ; pset_destroy( SC_ACCESS_TIMES(scp) ) ; } if ( SC_ONLY_FROM(scp) != NULL ) { addrlist_free( SC_ONLY_FROM(scp) ) ; pset_destroy( SC_ONLY_FROM(scp) ) ; } if ( SC_NO_ACCESS(scp) != NULL ) { addrlist_free( SC_NO_ACCESS(scp) ) ; pset_destroy( SC_NO_ACCESS(scp) ) ; } if ( SC_ENV_VAR_DEFS(scp) != NULL ) release_string_pset( SC_ENV_VAR_DEFS(scp) ) ; if ( SC_PASS_ENV_VARS(scp) != NULL ) release_string_pset( SC_PASS_ENV_VARS(scp) ) ; if ( SC_ENV( scp )->env_type == CUSTOM_ENV && SC_ENV( scp )->env_handle != ENV_NULL ) env_destroy( SC_ENV( scp )->env_handle ) ; if (SC_DISABLED(scp) ) release_string_pset( SC_DISABLED(scp) ) ; if (SC_ENABLED(scp) ) release_string_pset( SC_ENABLED(scp) ) ; CLEAR( *scp ) ; FREE_SCONF( scp ) ; } /* * Create a configuration for one of the special services */ struct service_config *sc_make_special( const char *service_name, const builtin_s *bp, int instances ) { struct service_config *scp ; const char *func = "sc_make" ; if ( ( scp = sc_alloc( service_name ) ) == NULL ) return( NULL ) ; SC_ID(scp) = new_string( SC_NAME(scp) ) ; if ( SC_ID(scp) == NULL ) { out_of_memory( func ) ; /* * Since we're returning instead of exiting, it's probably a good idea to * free scp */ sc_free( scp ); return( NULL ) ; } SC_SPECIFY( scp, A_ID ) ; /* * All special services are internal */ M_SET( SC_TYPE(scp), ST_SPECIAL ) ; M_SET( SC_TYPE(scp), ST_INTERNAL ) ; SC_BUILTIN(scp) = bp ; SC_SPECIFY( scp, A_TYPE ) ; M_SET( SC_XFLAGS(scp), SF_NORETRY ) ; SC_SPECIFY( scp, A_FLAGS ) ; SC_INSTANCES(scp) = instances ; SC_SPECIFY( scp, A_INSTANCES ) ; SC_WAIT(scp) = NO ; SC_SPECIFY( scp, A_WAIT ) ; return( scp ) ; } static void dump_log_data( int fd, struct service_config *scp, int tab_level ) { struct log *lp = SC_LOG( scp ) ; struct filelog *flp ; int i ; switch ( LOG_GET_TYPE( lp ) ) { case L_NONE: tabprint( fd, tab_level, "No logging\n" ) ; return ; case L_COMMON_FILE: tabprint( fd, tab_level, "Logging to common log file\n" ) ; break ; case L_FILE: flp = LOG_GET_FILELOG( lp ) ; tabprint( fd, tab_level, "Logging to file: %s", flp->fl_filename ) ; if ( FILELOG_SIZE_CONTROL( flp ) ) Sprint( fd, " (soft=%d hard=%d)\n", flp->fl_soft_limit, flp->fl_hard_limit ) ; else Sprint( fd, " (no limits)\n" ) ; break ; case L_SYSLOG: tabprint( fd, tab_level, "Logging to syslog. Facility = %s, level = %s\n", nv_get_name( syslog_facilities, lp->l_sl.sl_facility ), nv_get_name( syslog_levels, lp->l_sl.sl_level ) ) ; break ; } tabprint( fd, tab_level, "Log_on_success flags =" ) ; for ( i = 0 ; success_log_options[ i ].name != NULL ; i++ ) if ( M_IS_SET( SC_LOG_ON_SUCCESS(scp), success_log_options[ i ].value ) ) Sprint( fd, " %s", success_log_options[ i ].name ) ; Sputchar( fd, '\n' ) ; tabprint( fd, tab_level, "Log_on_failure flags =" ) ; for ( i = 0 ; failure_log_options[ i ].name != NULL ; i++ ) if ( M_IS_SET( SC_LOG_ON_FAILURE(scp), failure_log_options[ i ].value ) ) Sprint( fd, " %s", failure_log_options[ i ].name ) ; Sputchar( fd, '\n' ) ; } /* * Print info about service scp to file descriptor fd */ void sc_dump( struct service_config *scp, int fd, int tab_level, bool_int is_defaults ) { const struct name_value *nvp ; unsigned u ; char **pp ; if ( is_defaults ) tabprint( fd, tab_level, "Service defaults\n" ) ; else tabprint( fd, tab_level, "Service configuration: %s\n", SC_NAME(scp) ) ; if ( ! is_defaults ) { tabprint( fd, tab_level+1, "id = %s\n", SC_ID(scp) ) ; if ( ! M_ARE_ALL_CLEAR( SC_XFLAGS(scp) ) ) { tabprint( fd, tab_level+1, "flags =" ) ; for ( nvp = &service_flags[ 0 ] ; nvp->name != NULL ; nvp++ ) if ( M_IS_SET( SC_XFLAGS(scp), nvp->value ) ) Sprint( fd, " %s", nvp->name ) ; Sputchar( fd, '\n' ) ; } if ( ! M_ARE_ALL_CLEAR( SC_TYPE(scp) ) ) { tabprint( fd, tab_level+1, "type =" ) ; for ( nvp = &service_types[ 0 ] ; nvp->name != NULL ; nvp++ ) if ( M_IS_SET( SC_TYPE(scp), nvp->value ) ) Sprint( fd, " %s", nvp->name ) ; Sputchar( fd, '\n' ) ; } tabprint( fd, tab_level+1, "socket_type = %s\n", nv_get_name( socket_types, SC_SOCKET_TYPE(scp) ) ) ; tabprint( fd, tab_level+1, "Protocol (name,number) = (%s,%d)\n", SC_PROTONAME(scp), SC_PROTOVAL(scp) ) ; if ( SC_SPECIFIED( scp, A_PORT ) ) tabprint( fd, tab_level+1, "port = %d\n", SC_PORT(scp) ) ; } if ( SC_SPECIFIED( scp, A_INSTANCES ) ) { if ( SC_INSTANCES(scp) == UNLIMITED ) tabprint( fd, tab_level+1, "Instances = UNLIMITED\n" ) ; else tabprint( fd, tab_level+1, "Instances = %d\n", SC_INSTANCES(scp) ) ; } if ( SC_SPECIFIED( scp, A_WAIT ) ) { if ( SC_WAIT(scp) ) tabprint( fd, tab_level+1, "wait = yes\n" ) ; else tabprint( fd, tab_level+1, "wait = no\n" ) ; } if ( SC_SPECIFIED( scp, A_USER ) ) tabprint( fd, tab_level+1, "user = %d\n", SC_UID(scp) ) ; if ( SC_SPECIFIED( scp, A_GROUP ) ) tabprint( fd, tab_level+1, "group = %d\n", SC_GID(scp) ) ; if ( SC_SPECIFIED( scp, A_GROUPS ) ) { if (SC_GROUPS(scp) == 1) tabprint( fd, tab_level+1, "Groups = yes\n" ); else tabprint( fd, tab_level+1, "Groups = no\n" ); } if ( SC_SPECIFIED( scp, A_UMASK ) ) tabprint( fd, tab_level+1, "umask = %o\n", SC_UMASK(scp) ) ; if ( SC_SPECIFIED( scp, A_NICE ) ) tabprint( fd, tab_level+1, "Nice = %d\n", SC_NICE(scp) ) ; if ( SC_SPECIFIED( scp, A_CPS ) ) tabprint( fd, tab_level+1, "CPS = max conn:%lu wait:%lu\n", SC_TIME_CONN_MAX(scp), SC_TIME_WAIT(scp) ); if ( SC_SPECIFIED( scp, A_PER_SOURCE ) ) tabprint( fd, tab_level+1, "PER_SOURCE = %d\n", SC_PER_SOURCE(scp) ); if ( SC_SPECIFIED( scp, A_BIND ) ) { if ( SC_BIND_ADDR(scp) ) { char bindname[NI_MAXHOST]; unsigned int len = 0; if( SC_BIND_ADDR(scp)->sa.sa_family == AF_INET ) len = sizeof(struct sockaddr_in); else len = sizeof(struct sockaddr_in6); memset(bindname, 0, sizeof(bindname)); if( getnameinfo(&SC_BIND_ADDR(scp)->sa, len, bindname, NI_MAXHOST, NULL, 0, 0) != 0 ) strcpy(bindname, "unknown"); tabprint( fd, tab_level+1, "Bind = %s\n", bindname ); } else if ( SC_ORIG_BIND_ADDR(scp) ) { tabprint( fd, tab_level+1, "Bind = %s\n", SC_ORIG_BIND_ADDR(scp) ); } else { /* This should NEVER happen */ msg(LOG_ERR, "sc_dump", "bad configuration for %s:", SC_NAME(scp)); } } else tabprint( fd, tab_level+1, "Bind = All addresses.\n" ); if ( ! is_defaults ) { if ( (! SC_IS_INTERNAL( scp )) && (SC_REDIR_ADDR(scp) == NULL) ) { tabprint( fd, tab_level+1, "Server = %s\n", SC_SERVER(scp) ) ; tabprint( fd, tab_level+1, "Server argv =" ) ; if ( SC_SERVER_ARGV(scp) ) { for ( pp = SC_SERVER_ARGV(scp) ; *pp ; pp++ ) Sprint( fd, " %s", *pp ) ; } else Sprint( fd, " (NULL)"); Sputchar( fd, '\n' ) ; } #ifdef LIBWRAP if ( SC_LIBWRAP(scp) != NULL ) { tabprint( fd, tab_level + 1, "Libwrap = %s\n", SC_LIBWRAP(scp) ); } #endif if ( SC_REDIR_ADDR(scp) != NULL ) { char redirname[NI_MAXHOST]; unsigned int len = 0; if( SC_REDIR_ADDR(scp)->sa.sa_family == AF_INET ) len = sizeof(struct sockaddr_in); if( SC_REDIR_ADDR(scp)->sa.sa_family == AF_INET6 ) len = sizeof(struct sockaddr_in6); memset(redirname, 0, sizeof(redirname)); if( getnameinfo(&SC_REDIR_ADDR(scp)->sa, len, redirname, NI_MAXHOST, NULL, 0, 0) != 0 ) strcpy(redirname, "unknown"); tabprint( fd, tab_level+1, "Redirect = %s:%d\n", redirname, SC_REDIR_ADDR(scp)->sa_in.sin_port ); } if ( SC_IS_RPC( scp ) ) { struct rpc_data *rdp = SC_RPCDATA( scp ) ; tabprint( fd, tab_level+1, "RPC data\n" ) ; tabprint( fd, tab_level+2, "program number = %ld\n", rdp->rd_program_number ) ; tabprint( fd, tab_level+2, "rpc_version = " ) ; if ( rdp->rd_min_version == rdp->rd_max_version ) Sprint( fd, "%ld\n", rdp->rd_min_version ) ; else Sprint( fd, "%ld-%ld\n", rdp->rd_min_version, rdp->rd_max_version ) ; } if ( SC_SPECIFIED( scp, A_ACCESS_TIMES ) ) { tabprint( fd, tab_level+1, "Access times =" ) ; ti_dump( SC_ACCESS_TIMES(scp), fd ) ; Sputchar ( fd, '\n' ) ; } } /* This is important enough that each service should list it. */ tabprint( fd, tab_level+1, "Only from: " ) ; if ( SC_ONLY_FROM(scp) ) { /* Next check is done since -= doesn't zero out lists. */ if ( pset_count(SC_ONLY_FROM(scp)) == 0) Sprint( fd, "All sites" ); else addrlist_dump( SC_ONLY_FROM(scp), fd ) ; } else Sprint( fd, "All sites" ); Sputchar( fd, '\n' ) ; /* This is important enough that each service should list it. */ tabprint( fd, tab_level+1, "No access: " ) ; if ( SC_NO_ACCESS(scp) ) { /* Next check is done since -= doesn't zero out lists. */ if ( pset_count(SC_NO_ACCESS(scp)) == 0) Sprint( fd, "No blocked sites" ); else addrlist_dump( SC_NO_ACCESS(scp), fd ) ; } else Sprint( fd, "No blocked sites" ); Sputchar( fd, '\n' ) ; if ( SC_SENSOR(scp) ) { tabprint( fd, tab_level+1, "Deny Time: " ) ; Sprint( fd, "%d\n", SC_DENY_TIME(scp)); } dump_log_data( fd, scp, tab_level+1 ) ; if ( SC_IS_PRESENT( scp, A_PASSENV ) ) { tabprint( fd, tab_level+1, "Passenv =" ) ; for ( u = 0 ; u < pset_count( SC_PASS_ENV_VARS(scp) ) ; u++ ) Sprint( fd, " %s", (char *) pset_pointer( SC_PASS_ENV_VARS(scp), u ) ) ; Sputchar ( fd, '\n' ) ; } if ( ! is_defaults ) if ( SC_SPECIFIED( scp, A_ENV ) ) { tabprint( fd, tab_level+1, "Environment additions:\n" ) ; for ( u = 0 ; u < pset_count( SC_ENV_VAR_DEFS(scp) ) ; u++ ) tabprint( fd, tab_level+2, "%s\n", (char *) pset_pointer( SC_ENV_VAR_DEFS(scp), u ) ) ; } if ( SC_ENV( scp )->env_type == CUSTOM_ENV ) { tabprint( fd, tab_level+1, "Environment strings:\n" ) ; for ( pp = env_getvars( SC_ENV( scp )->env_handle ) ; *pp ; pp++ ) tabprint( fd, tab_level+2, "%s\n", *pp ) ; } Sflush( fd ) ; } #define SC_RPCPROGNUM( s ) RD_PROGNUM( SC_RPCDATA( s ) ) #define SAME_RPC( s1, s2 ) ( SC_RPCPROGNUM( s1 ) == SC_RPCPROGNUM( s2 ) ) #define SAME_NONRPC( s1, s2 ) ( SC_SOCKET_TYPE((s1)) == SC_SOCKET_TYPE((s2)) \ && SC_PORT((s1)) == SC_PORT((s2)) ) /* * Two service configurations are considered different if any of the * following is TRUE: * 1) only one is unlisted * 2) only one is internal * 3) only one is RPC * 4) they have different values for the 'wait' attribute * 5) they use different protocols * 6) they are both RPC services but have different program numbers * 7) neither is an RPC service and they have different socket_types or * use diffent ports * * This function returns TRUE if the specified configurations are different. * * Note that this function is closely related to the 'readjust' function * that is invoked on reconfiguration; that function will not change * attributes that this function checks to determine if two configurations * are different. */ bool_int sc_different_confs( struct service_config *scp1, struct service_config *scp2 ) { if ( SC_IS_UNLISTED( scp1 ) != SC_IS_UNLISTED( scp2 ) || SC_IS_INTERNAL( scp1 ) != SC_IS_INTERNAL( scp2 ) || SC_IS_RPC( scp1 ) != SC_IS_RPC( scp2 ) ) return( TRUE ) ; if ( SC_WAIT(scp1) != SC_WAIT(scp2) ) return( TRUE ) ; if ( SC_PROTOVAL(scp1) != SC_PROTOVAL(scp2) ) return( TRUE ) ; if ( SC_IS_RPC( scp1 ) ) { if ( ! SAME_RPC( scp1, scp2 ) ) return( TRUE ) ; } else { if ( ! SAME_NONRPC( scp1, scp2 ) ) return( TRUE ) ; } return( FALSE ) ; } xinetd-2.3.15.4/src/sconf.h000066400000000000000000000277251333055217000153230ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef SCONF_H #define SCONF_H /* * $Id$ */ #include "config.h" #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include "libportable.h" #include "pset.h" #include "m_env.h" #include "mask.h" #include "defs.h" #include "log.h" #include "builtins.h" #include "attr.h" /* * Service types */ #define ST_RPC 1 #define ST_INTERNAL 2 #define ST_UNLISTED 3 #define ST_SPECIAL 4 #define ST_DISABLED 5 #define ST_TCPMUX 6 #define ST_TCPMUXPLUS 7 /* * Service flags */ #define SF_INTERCEPT 1 #define SF_REUSE 2 #define SF_NORETRY 3 #define SF_IDONLY 4 #define SF_NAMEINARGS 5 #define SF_NODELAY 6 #define SF_KEEPALIVE 7 #define SF_NOLIBWRAP 8 #define SF_SENSOR 9 #define SF_IPV4 10 #define SF_IPV6 11 #define SF_LABELED 12 /* * Values for log options */ #define LO_HOST 1 #define LO_DURATION 3 #define LO_ATTEMPT 4 #define LO_EXIT 5 #define LO_PID 6 #define LO_USERID 7 #define LO_TRAFFIC 8 struct rpc_data { unsigned long rd_min_version ; unsigned long rd_max_version ; unsigned long rd_program_number ; } ; #define RD_MINVERS( rdp ) (rdp)->rd_min_version #define RD_MAXVERS( rdp ) (rdp)->rd_max_version #define RD_PROGNUM( rdp ) (rdp)->rd_program_number typedef enum { NO_ENV = 0, STD_ENV, DEF_ENV, CUSTOM_ENV } environ_e ; struct environment { environ_e env_type ; env_h env_handle ; } ; /* * NOTE: Clearing the structure will give all its fields their default values */ struct service_config { mask_t sc_specified_attributes; /* specified attributes */ mask_t sc_attributes_present; /* includes those from defaults*/ mask_t sc_type; /* RPC, UNLISTED etc */ mask_t sc_xflags ; /* INTERCEPT etc */ char *sc_name; /* e g "echo" */ char *sc_id ; /* e.g. "echo-stream" */ uint16_t sc_port ; /* in host byte order */ int sc_socket_type ; /* e.g. SOCK_DGRAM */ struct protocol_name_value sc_protocol ; /* e.g. "TCP", IPPROTO_TCP */ boolean_e sc_wait ; uid_t sc_uid ; gid_t sc_user_gid ; /* gid corresponding to uid */ gid_t sc_gid ; /* gid corresponding to group */ char *sc_server ; char **sc_server_argv ; int sc_instances ; int sc_nice ; /* argument for nice(3) */ pset_h sc_env_var_defs ; /* list of env strings */ pset_h sc_pass_env_vars ; /* env vars to pass to server */ pset_h sc_access_times ; pset_h sc_only_from ; pset_h sc_no_access ; mask_t sc_log_on_success ; mask_t sc_log_on_failure ; struct log sc_log ; struct rpc_data sc_rd ; pset_h sc_disabled ; /* used only by the default entry */ pset_h sc_enabled ; /* used only by the default entry */ struct environment sc_environment ; const builtin_s *sc_builtin ; union xsockaddr *sc_redir_addr ; char *sc_orig_bind_addr ; /* used only when dual stack */ union xsockaddr *sc_bind_addr ; boolean_e sc_v6only; char *sc_banner ; int sc_per_source ; boolean_e sc_groups ; char *sc_banner_success ; char *sc_banner_fail ; double sc_max_load ; time_t sc_time_limit ; time_t sc_time_conn ; time_t sc_time_conn_max ; time_t sc_time_wait ; time_t sc_time_reenable ; rlim_t sc_rlim_as; rlim_t sc_rlim_cpu; rlim_t sc_rlim_data; rlim_t sc_rlim_files; rlim_t sc_rlim_rss; rlim_t sc_rlim_stack; mode_t sc_umask; int sc_deny_time; /* Sensor deny access time: -1: forever 0: never X: X minutes */ #ifdef LIBWRAP char *sc_libwrap; #endif } ; #define SCP( p ) ((struct service_config *)(p)) /* * Field access macros */ #define SC_LOG( scp ) (&(scp)->sc_log) #define SC_RPCDATA( scp ) (&(scp)->sc_rd) #define SC_ENV( scp ) (&(scp)->sc_environment) #define SC_ENV_VAR_DEFS( scp ) (scp)->sc_env_var_defs #define SC_PORT( scp ) (scp)->sc_port #define SC_NICE( scp ) (scp)->sc_nice #define SC_SOCKET_TYPE( scp ) (scp)->sc_socket_type #define SC_ID( scp ) (scp)->sc_id #define SC_NAME( scp ) (scp)->sc_name #define SC_PROTOVAL( scp ) (scp)->sc_protocol.value #define SC_PROTONAME( scp ) (scp)->sc_protocol.name #define SC_INSTANCES( scp ) (scp)->sc_instances #define SC_UID( scp ) (scp)->sc_uid #define SC_GID( scp ) (scp)->sc_gid #define SC_USER_GID( scp ) (scp)->sc_user_gid #define SC_SERVER( scp ) (scp)->sc_server #define SC_SERVER_ARGV( scp ) (scp)->sc_server_argv #define SC_ONLY_FROM( scp ) (scp)->sc_only_from #define SC_NO_ACCESS( scp ) (scp)->sc_no_access #define SC_ACCESS_TIMES( scp ) (scp)->sc_access_times #define SC_LOG_ON_SUCCESS( scp ) (scp)->sc_log_on_success #define SC_LOG_ON_FAILURE( scp ) (scp)->sc_log_on_failure #define SC_PASS_ENV_VARS( scp ) (scp)->sc_pass_env_vars #define SC_RLIM_AS( scp ) (scp)->sc_rlim_as #define SC_RLIM_CPU( scp ) (scp)->sc_rlim_cpu #define SC_RLIM_DATA( scp ) (scp)->sc_rlim_data #define SC_RLIM_FILES( scp ) (scp)->sc_rlim_files #define SC_RLIM_RSS( scp ) (scp)->sc_rlim_rss #define SC_RLIM_STACK( scp ) (scp)->sc_rlim_stack #define SC_TYPE( scp ) (scp)->sc_type #define SC_WAIT( scp ) (scp)->sc_wait #define SC_XFLAGS( scp ) (scp)->sc_xflags #define SC_V6ONLY( scp ) (scp)->sc_v6only #define SC_ENABLED( scp ) (scp)->sc_enabled #define SC_DISABLED( scp ) (scp)->sc_disabled #define SC_BUILTIN( scp ) (scp)->sc_builtin #define SC_REDIR_ADDR( scp ) (scp)->sc_redir_addr #define SC_ORIG_BIND_ADDR( scp ) (scp)->sc_orig_bind_addr #define SC_BIND_ADDR( scp ) (scp)->sc_bind_addr #define SC_BANNER( scp ) (scp)->sc_banner #define SC_BANNER_SUCCESS( scp ) (scp)->sc_banner_success #define SC_BANNER_FAIL( scp ) (scp)->sc_banner_fail #define SC_GROUPS( scp ) (scp)->sc_groups #define SC_MAX_LOAD( scp ) (scp)->sc_max_load #define SC_TIME_LIMIT( scp ) (scp)->sc_time_limit #define SC_TIME_CONN( scp ) (scp)->sc_time_conn #define SC_TIME_CONN_MAX( scp ) (scp)->sc_time_conn_max #define SC_TIME_WAIT( scp ) (scp)->sc_time_wait #define SC_TIME_REENABLE( scp ) (scp)->sc_time_reenable #define SC_UMASK( scp ) (scp)->sc_umask #define SC_DENY_TIME( scp ) (scp)->sc_deny_time #define SC_MDNS_NAME( scp ) (scp)->sc_mdns_name #define SC_MDNS( scp ) (scp)->sc_mdns #define SC_PER_SOURCE( scp ) (scp)->sc_per_source #define SC_LIBWRAP( scp ) (scp)->sc_libwrap /* * Field set macros */ #define SC_SET_PORT( scp, port ) (scp)->sc_port = (port) /* * Predicate checking macros */ #define SC_FORKS( scp ) ( ! SC_IS_INTERNAL( scp ) || \ BUILTIN_FORKS( (scp)->sc_builtin ) ) #define SC_WAITS( scp ) ( (scp)->sc_wait == YES ) #define SC_RETRY( scp ) ( M_IS_CLEAR( (scp)->sc_xflags, SF_NORETRY ) ) #define SC_MUST_IDENTIFY( scp ) M_IS_SET( (scp)->sc_xflags, SF_IDONLY ) #define SC_NAMEINARGS( scp ) M_IS_SET( (scp)->sc_xflags, SF_NAMEINARGS ) #define SC_NODELAY( scp ) M_IS_SET( (scp)->sc_xflags, SF_NODELAY ) #define SC_KEEPALIVE( scp ) M_IS_SET( (scp)->sc_xflags, SF_KEEPALIVE ) #define SC_NOLIBWRAP( scp ) M_IS_SET( (scp)->sc_xflags, SF_NOLIBWRAP ) #define SC_IS_INTERCEPTED( scp ) ( M_IS_SET( (scp)->sc_xflags, SF_INTERCEPT ) ) #define SC_SENSOR( scp ) M_IS_SET( (scp)->sc_xflags, SF_SENSOR ) #define SC_IPV4( scp ) M_IS_SET( (scp)->sc_xflags, SF_IPV4 ) #define SC_IPV6( scp ) M_IS_SET( (scp)->sc_xflags, SF_IPV6 ) #define SC_LABELED_NET( scp ) M_IS_SET( (scp)->sc_xflags, SF_LABELED ) #define SC_IS_RPC( scp ) ( M_IS_SET( (scp)->sc_type, ST_RPC ) ) #define SC_IS_INTERNAL( scp ) ( M_IS_SET( (scp)->sc_type, ST_INTERNAL ) ) #define SC_IS_SPECIAL( scp ) ( M_IS_SET( (scp)->sc_type, ST_SPECIAL ) ) #define SC_IS_UNLISTED( scp ) ( M_IS_SET( (scp)->sc_type, ST_UNLISTED ) ) #define SC_IS_DISABLED( scp ) ( M_IS_SET( (scp)->sc_type, ST_DISABLED ) ) #define SC_DISABLE(scp) ( M_SET( (scp)->sc_type, ST_DISABLED ) ) #define SC_ENABLE(scp) ( M_CLEAR( (scp)->sc_type, ST_DISABLED ) ) #define SC_IS_MUXCLIENT( scp ) ( M_IS_SET( (scp)->sc_type, ST_TCPMUX ) || \ M_IS_SET( (scp)->sc_type, ST_TCPMUXPLUS ) ) #define SC_IS_MUXPLUSCLIENT(scp) ( M_IS_SET( (scp)->sc_type, ST_TCPMUXPLUS ) ) #define SC_IS_TCPMUX( scp ) ( (scp)->sc_builtin && \ (BUILTIN_HANDLER( (scp)->sc_builtin ) == \ (void *)tcpmux_handler ) ) #define LOGS_USERID( scp, flags ) \ ( M_IS_SET( (scp)->flags, LO_USERID ) && SC_ACCEPTS_CONNECTIONS( scp ) ) #define LOGS_ANY( scp, flags ) ( ! M_ARE_ALL_CLEAR( (scp)->flags ) ) #define SC_LOGS_ON_SUCCESS( scp ) LOGS_ANY( scp, sc_log_on_success ) #define SC_LOGS_ON_FAILURE( scp ) LOGS_ANY( scp, sc_log_on_failure ) #define SC_LOGS_USERID_ON_FAILURE( scp ) LOGS_USERID( scp, sc_log_on_failure ) #define SC_LOGS_USERID_ON_SUCCESS( scp ) LOGS_USERID( scp, sc_log_on_success ) #define SC_LOGS_ON_EXIT( scp ) \ ( M_IS_SET( (scp)->sc_log_on_success, LO_DURATION ) || \ M_IS_SET( (scp)->sc_log_on_success, LO_EXIT ) ) #define SC_LOGS_PID( scp ) M_IS_SET( (scp)->sc_log_on_success, LO_PID ) #define SC_LOGS_EXITS( scp ) M_IS_SET( (scp)->sc_log_on_success, LO_EXIT ) #define SC_LOGS_DURATION( scp ) \ M_IS_SET( (scp)->sc_log_on_success, LO_DURATION ) #define SC_MUST_LISTEN( scp ) ( (scp)->sc_socket_type == SOCK_STREAM ) #define SC_ACCEPTS_CONNECTIONS( scp ) \ ( (scp)->sc_wait == NO && (scp)->sc_socket_type == SOCK_STREAM ) #define SC_SPECIFIED( scp, attr ) \ M_IS_SET( (scp)->sc_specified_attributes, (attr) ) #define SC_SPECIFY( scp, attr ) \ { \ M_SET( (scp)->sc_specified_attributes, (attr) ) ; \ SC_PRESENT( (scp), (attr) ) ; \ } #define SC_IS_PRESENT( scp, attr ) \ M_IS_SET( (scp)->sc_attributes_present, (attr) ) #define SC_PRESENT( scp, attr ) \ M_SET( (scp)->sc_attributes_present, (attr) ) #define SC_GETGID( scp ) ( SC_SPECIFIED( scp, A_GROUP ) \ ? (scp)->sc_gid : (scp)->sc_user_gid ) #define SC_INTERNAL( scp, serp ) BUILTIN_INVOKE( (scp)->sc_builtin, serp ) #define SC_MAKE_EXTERNAL( scp ) M_CLEAR( (scp)->sc_type, ST_INTERNAL ) struct service_config *sc_alloc(const char *name); void sc_free(struct service_config *scp); struct service_config *sc_make_special(const char *service_name,const builtin_s *bp,int instances); void sc_dump(struct service_config *scp,int fd,int tab_level,bool_int is_defaults); bool_int sc_different_confs(struct service_config *scp1,struct service_config *scp2); #endif /* SCONF_H */ xinetd-2.3.15.4/src/sconst.h000066400000000000000000000012731333055217000155120ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef SCONST_H #define SCONST_H /* * $Id$ */ /* * Names of internal non-visible services */ #define INTERCEPT_SERVICE_NAME "intercept" #define LOG_SERVICE_NAME "logging" /* * Log entry ids */ #define START_ENTRY "START" #define FAIL_ENTRY "FAIL" #define EXIT_ENTRY "EXIT" #define USERID_ENTRY "USERID" #define NOID_ENTRY "NOID" #endif /* SCONST_H */ xinetd-2.3.15.4/src/sensor.c000066400000000000000000000144651333055217000155140ustar00rootroot00000000000000/* * (c) Copyright 2001-2002 by Steve Grubb * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include #include #include #include #include "config.h" #include "pset.h" #include "str.h" #include "addr.h" #include "msg.h" #include "sconf.h" #include "sensor.h" #include "xconfig.h" #include "xtimer.h" /* * This is the globals for the Sensor. The Sensor will add the incoming IP * address to the global_no_access table for whatever the configured time is. */ static pset_h global_no_access = NULL; /* global no_access list */ static pset_h global_no_access_time = NULL; /* time of the infraction */ static int timer_id = 0; /* Timer ID */ /* This function is called via a timer callback every 60 seconds */ static void scrub_global_access_list( void ); void init_sensor( void ) { if ( global_no_access == NULL ) global_no_access = pset_create(10, 10); if ( global_no_access_time == NULL ) global_no_access_time = pset_create(10, 10); } /* * This function runs in the parent context and updates the global_no_access * list. */ void process_sensor( const struct service *sp, const union xsockaddr *addr) { const char *func = "process_sensor"; if (SC_DENY_TIME(SVC_CONF(sp)) != 0) /* 0 simply logs it */ { if ( pset_count( global_no_access ) < MAX_GLOBAL_NO_ACCESS) { int item_matched = addrlist_match( global_no_access, CSA(addr) ); if ( item_matched == 0) { /* no match...adding to the list */ char *dup_addr = new_string(xaddrname( addr ) ); if (dup_addr == NULL ) return ; if (addrlist_add(global_no_access, dup_addr) == FAILED) msg(LOG_ERR, func, "Failed adding %s to the global_no_access list", dup_addr); else { time_t nowtime; char time_buf[40], *tmp; nowtime = time(NULL); msg(LOG_CRIT, func, "Adding %s to the global_no_access list for %d minutes", dup_addr, SC_DENY_TIME(SVC_CONF(sp))); if (SC_DENY_TIME(SVC_CONF(sp)) == -1) strcpy(time_buf, "-1"); else strx_nprint(time_buf, 38, "%ld", (time_t)nowtime+(60*SC_DENY_TIME(SVC_CONF(sp)))); tmp = new_string(time_buf); if (tmp != NULL) { if (pset_add(global_no_access_time, tmp) == NULL) { msg(LOG_ERR, func, "Failed adding %s to the global_no_access_time list. " "global_no_access list is broken, xinetd needs " "restarting.", dup_addr); /* ideally, we should rollback the previous addr addition. */ } } if (pset_count(global_no_access) && (timer_id == 0) ) timer_id = xtimer_add( scrub_global_access_list, 60 ); } free(dup_addr); } else { /* Here again, eh?...update time stamp. */ char *exp_time; time_t stored_time; item_matched--; /* Is # plus 1, to even get here must be >= 1 */ exp_time = pset_pointer( global_no_access_time, item_matched ) ; if (exp_time == NULL) return ; if ( parse_base10(exp_time, (int *)&stored_time) ) { /* if never let them off, bypass */ if (stored_time != -1) { time_t nowtime, new_time; nowtime = time(NULL); new_time = (time_t)nowtime+(60*SC_DENY_TIME(SVC_CONF(sp))); if (difftime(new_time, (time_t)stored_time) > 0.0) { /* new_time is longer save it */ char time_buf[40], *new_exp_time; strx_nprint(time_buf, 38, "%ld", (long)new_time); new_exp_time = new_string(time_buf); if ( new_exp_time ) { free(exp_time); global_no_access_time->ptrs[ (unsigned)item_matched ] = new_exp_time; } } } } } } else msg(LOG_ERR, func, "Maximum global_no_access count reached."); } } /* They hit a real server...note, this is likely to be a child process. */ status_e check_sensor( const union xsockaddr *addr) { if ( (global_no_access) && pset_count( global_no_access ) ) { if (addrlist_match( global_no_access, CSA(addr))) return FAILED; } return OK; } static void scrub_global_access_list( void ) { unsigned count; const char *func = "scrub_global_no_access_list"; if ( global_no_access == NULL ) count = 0; else count = pset_count( global_no_access ); if ( count ) { int found_one = 0; unsigned u; time_t nowtime = time(NULL); for (u=0; u < count; u++) { char *exp_time; time_t stored_time; exp_time = pset_pointer( global_no_access_time, u ) ; stored_time = atol(exp_time); if (stored_time == -1) /* never let them off */ continue; if (difftime(nowtime, (time_t)stored_time) >= 0.0) { __pset_pointer ptr; pset_pointer(global_no_access, u) = NULL; ptr = global_no_access_time->ptrs[ u ]; free(ptr); pset_pointer(global_no_access_time, u ) = NULL; found_one = 1; } } if (found_one) { pset_compact( global_no_access ); pset_compact( global_no_access_time ); msg(LOG_INFO, func, "At least 1 DENY_TIME has expired, global_no_access list updated"); } /* If there's still more on the list, start another callback. */ count = pset_count( global_no_access ); if ( count ) timer_id = xtimer_add( scrub_global_access_list, 60 ); else { timer_id = 0; msg(LOG_INFO, func, "global_no_access list is empty."); } } } void destroy_global_access_list( void ) { if ( global_no_access ) { pset_apply( global_no_access, free, NULL ) ; pset_destroy( global_no_access ) ; } if ( global_no_access_time ) { pset_apply( global_no_access_time, free, NULL ) ; pset_destroy( global_no_access_time ) ; } } xinetd-2.3.15.4/src/sensor.h000066400000000000000000000006551333055217000155150ustar00rootroot00000000000000/* * (c) Copyright 2001-2002 by Steve Grubb * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef SENSOR_H #define SENSOR_H #include "defs.h" #include "service.h" void init_sensor( void ); void process_sensor( const struct service *, const union xsockaddr *); status_e check_sensor( const union xsockaddr * ); void destroy_global_access_list( void ); #endif xinetd-2.3.15.4/src/server.c000066400000000000000000000212511333055217000155000ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "pset.h" #include "sio.h" #include "server.h" #include "util.h" #include "msg.h" #include "service.h" #include "sconf.h" #include "state.h" #include "main.h" #include "xconfig.h" #include "retry.h" #include "child.h" #include "signals.h" #define NEW_SERVER() NEW( struct server ) #define FREE_SERVER( serp ) FREE( serp ) #ifndef DEBUG_RETRY #define do_fork() fork() #else #include extern int errno ; /* * 3 out of 4 times the do_fork() will fail */ #define do_fork() ( random() & 0x11 ) ? ( errno = EAGAIN, -1 ) : fork() #endif /* DEBUG_RETRY */ /* * Allocate a server, initialize it from init_serp, and insert it in the server * table. */ struct server *server_alloc( const struct server *init_serp ) { struct server *serp ; const char *func = "server_alloc" ; serp = NEW_SERVER() ; if ( serp == NULL ) { out_of_memory( func ) ; return( NULL ) ; } if ( pset_add( SERVERS(ps), serp ) == NULL ) { msg( LOG_CRIT, func, "couldn't insert server in server table" ) ; CLEAR( *serp ) ; FREE_SERVER( serp ) ; return( NULL ) ; } *serp = *init_serp ; /* initialize it */ SVC_HOLD( SERVER_SERVICE(serp) ) ; return( serp ) ; } void server_release( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; int count = SVC_RELE( sp ) ; pset_remove(SERVERS(ps), serp); if ( count == 0 ) { if( ! SC_IS_SPECIAL( SVC_CONF( sp ) ) ) pset_remove( SERVICES( ps ), sp ) ; svc_release( sp ); } CLEAR( *serp ) ; FREE_SERVER( serp ) ; } /* * If a service is internal and does not require forking a process: * - if it accepts connections, we put the accepted connection * in non-blocking mode to avoid a possible block on * the write(2). * - the log flags that have to do with the server exiting are * ignored (i.e. nothing is logged). * - it can be identified in the log because the server pid is 0. */ static void server_internal( struct server *serp ) { struct service *sp = SERVER_SERVICE(serp) ; const char *func = "server_internal" ; SERVER_PID(serp) = 0 ; if ( SVC_ACCEPTS_CONNECTIONS( sp ) && fcntl( SERVER_FD( serp ), F_SETFL, FNDELAY ) == -1 ) { msg( LOG_ERR, func, "%s: fcntl F_SETFL failed: %m", SVC_ID( sp ) ) ; return ; } svc_log_success( sp, SERVER_CONNECTION(serp), SERVER_PID(serp) ) ; SVC_INTERNAL( sp, serp ) ; } /* * Attempt to start a server for service 'sp' to handle * connection 'cp'. * Return value: * OK: if a server is started or a retry attempt is scheduled * FAILED: otherwise (a log entry is also made) */ status_e server_run( struct service *sp, connection_s *cp ) { struct server server ; struct server *serp = NULL; const char *func = "server_run" ; CLEAR( server ) ; server.svr_sp = sp ; server.svr_conn = cp ; if ( ! SVC_FORKS( sp ) ) { /* * SG - Added this check so that internal services get the * same protection that external services get. This is * mandatory for the sensor patch to work. */ if (svc_child_access_control( sp, cp ) == OK) server_internal( &server ) ; else { if ( SVC_WAITS( sp ) ) svc_resume( sp ); return( FAILED ); } if ( SVC_WAITS( sp ) ) svc_resume( sp ); return( OK ) ; } /* * Insert new struct server in server table first, to avoid the * possibility of running out of memory *after* the fork. */ serp = server_alloc( &server ) ; if ( serp == NULL ) return( FAILED ) ; if ( server_start( serp ) == OK ) { if( !SVC_WAITS(sp) ) CONN_CLOSE( cp ) ; return( OK ) ; } /* server will be removed in server_release() */ /* * Currently, fork failures are the only reason for retrying. * There is no retry if we exceed the max allowed number of fork failures */ if ( ! SERVER_FORKLIMIT( serp ) && SVC_RETRY( sp ) ) { if ( schedule_retry( serp ) == OK ) return( OK ) ; else msg( LOG_ERR, func, "Retry failure for %s service", SVC_ID( sp ) ) ; } else svc_log_failure( sp, cp, AC_FORK ) ; server_release( serp ) ; return( FAILED ) ; } /* * Try to fork a server process. * Actually, we won't fork if tcpmux_child is set, becuase we have * already forked to keep the xinetd parent from blocking on the * read of the service name. */ status_e server_start( struct server *serp ) { struct service *sp = SERVER_SERVICE(serp) ; const char *func = "server_start" ; if( debug.on ) msg( LOG_DEBUG, func, "Starting service %s", SC_NAME( SVC_CONF( sp ) ) ); SERVER_LOGUSER(serp) = SVC_LOGS_USERID_ON_SUCCESS( sp ) ; SERVER_PID(serp) = do_fork() ; switch ( SERVER_PID(serp) ) { case 0: ps.rws.env_is_valid = FALSE ; child_process( serp ) ; msg( LOG_ERR, func, "INTERNAL ERROR: child_process returned" ) ; _exit( 0 ) ; /* NOTREACHED */ case -1: msg( LOG_ERR, func, "%s: fork failed: %m", SVC_ID( sp ) ) ; SERVER_FORK_FAILURES(serp)++ ; return( FAILED ) ; default: (void) time( &SERVER_STARTTIME(serp) ) ; SVC_INC_RUNNING_SERVERS( sp ) ; /* * Log the start of another server (if it is not an interceptor). * Determine if the server writes to the log (because in that case * we will have to check the log size). */ if ( ! SVC_IS_INTERCEPTED( sp ) ) svc_log_success( sp, SERVER_CONNECTION(serp), SERVER_PID(serp) ) ; else SERVER_WRITES_TO_LOG(serp) = SVC_IS_LOGGING( sp ) ; SERVER_WRITES_TO_LOG(serp) |= SERVER_LOGUSER(serp) ; return( OK ) ; } } void server_dump( const struct server *serp, int fd ) { const struct service *sp = SERVER_SERVICE(serp) ; Sprint( fd, "%s server\n", SVC_ID( sp ) ) ; Sprint( fd, "pid = %d\n", SERVER_PID(serp) ) ; Sprint( fd, "start_time = %s", ctime( &SERVER_STARTTIME(serp) ) ) ; Sprint( fd, "Connection info:\n" ) ; conn_dump( SERVER_CONNECTION(serp), fd ) ; if ( SERVER_FORK_FAILURES(serp) ) Sprint( fd, "fork_failures = %d\n", SERVER_FORK_FAILURES(serp) ) ; Sprint( fd, "log_remote_user = %s\n", SERVER_LOGUSER(serp) ? "YES" : "NO" ) ; Sprint( fd, "writes_to_log = %s\n", SERVER_WRITES_TO_LOG(serp) ? "YES" : "NO" ) ; Sputchar( fd, '\n' ) ; Sflush( fd ) ; } /* * Invoked when a server dies, either because of a signal or in case of * a normal exit. */ void server_end( struct server *serp ) { struct service *sp = SERVER_SERVICE(serp) ; const char *func = "server_end" ; if ( PROC_EXITED( SERVER_EXITSTATUS(serp) ) || PROC_SIGNALED( SERVER_EXITSTATUS(serp) ) ) { const char *death_type = PROC_EXITED( SERVER_EXITSTATUS(serp) ) ? "exited" : "died" ; if ( debug.on ) { struct service *conn_sp = CONN_SERVICE( SERVER_CONNECTION(serp) ) ; if ( conn_sp == sp ) msg( LOG_DEBUG, func, "%s server %d %s", SVC_ID( sp ) , SERVER_PID(serp), death_type ) ; else msg( LOG_DEBUG, func, "%s server %d running on behalf of service %s %s", SVC_ID( sp ), SERVER_PID(serp), SVC_ID( conn_sp ), death_type ) ; } /* Added this for when accepting wait=yes services */ if( SVC_WAITS( sp ) ) #ifdef HAVE_POLL SVC_EVENTS( sp ) = POLLIN ; #else FD_SET( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif /* HAVE_POLL */ svc_postmortem( sp, serp ) ; server_release( serp ) ; } else if ( PROC_STOPPED( SERVER_EXITSTATUS(serp) ) ) msg( LOG_WARNING, func, "service %s: server with pid %d stopped", SVC_ID( sp ), SERVER_PID(serp) ) ; } /* * Find the running server with the specified pid */ struct server *server_lookup( pid_t pid ) { unsigned u ; for ( u = 0 ; u < pset_count( SERVERS( ps ) ) ; u++ ) { register struct server *serp ; serp = SERP( pset_pointer( SERVERS( ps ), u ) ) ; if ( SERVER_PID(serp) == pid ) return( serp ) ; } return( NULL ) ; } xinetd-2.3.15.4/src/server.h000066400000000000000000000060631333055217000155110ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef SERVER_H #define SERVER_H /* * $Id$ */ #include "config.h" #include #include #include #include "defs.h" #include "pset.h" #ifdef NO_POSIX_TYPES typedef int pid_t ; #endif /* * This struct describes running servers */ struct server { pid_t svr_pid ; time_t svr_start_time ; connection_s *svr_conn ; struct service *svr_sp ; /* service that owns this server */ int svr_fork_failures ; /* number of fork(2) failures */ int svr_exit_status ; bool_int svr_log_remote_user ; bool_int svr_writes_to_log ; /* needed because a service may be */ /* reconfigured between server */ /* forking and exit */ } ; #define SERP( p ) ((struct server *)(p)) #define SERVER_SERVICE( serp ) (serp)->svr_sp #define SERVER_CONNECTION( serp) (connection_s *)(serp)->svr_conn #define SERVER_CONNSERVICE( serp ) CONN_SERVICE( SERVER_CONNECTION( serp ) ) #define SERVER_FD( serp ) CONN_DESCRIPTOR( (serp)->svr_conn ) #define SERVER_PID( serp ) (serp)->svr_pid #define SERVER_EXITSTATUS( serp ) (serp)->svr_exit_status #define SERVER_STARTTIME( serp ) (serp)->svr_start_time #define SERVER_LOGUSER( serp ) (serp)->svr_log_remote_user #define SERVER_FORK_FAILURES( serp ) (serp)->svr_fork_failures #define SERVER_WRITES_TO_LOG( serp ) (serp)->svr_writes_to_log #define SERVER_FORKLIMIT( serp ) \ ( (serp)->svr_fork_failures >= MAX_FORK_FAILURES ) #define SERVER_SET_PID( serp, pid ) (serp)->svr_pid = (pid) #define SERVER_SET_EXIT_STATUS( serp, status ) \ (serp)->svr_exit_status = (status) /* * Macros for compatibility */ #ifndef OLD_WAIT #define PROC_EXITED( s ) WIFEXITED( s ) #define PROC_SIGNALED( s ) WIFSIGNALED( s ) #define PROC_STOPPED( s ) WIFSTOPPED( s ) #define PROC_EXITSTATUS( s ) WEXITSTATUS( s ) #define PROC_TERMSIG( s ) WTERMSIG( s ) #else #define PROC_EXITED( s ) WIFEXITED( *(union wait *)&(s) ) #define PROC_SIGNALED( s ) WIFSIGNALED( *(union wait *)&(s) ) #define PROC_STOPPED( s ) WIFSTOPPED( *(union wait *)&(s) ) #define PROC_EXITSTATUS( s ) (((union wait *)&(s))->w_T.w_Retcode) #define PROC_TERMSIG( s ) (((union wait *)&(s))->w_T.w_Termsig) #endif /* OLD_WAIT */ void server_release(struct server *serp); status_e server_run(struct service *sp,connection_s *cp); status_e server_start(struct server *serp); void server_dump(const struct server *serp,int fd); void server_end(struct server *serp); struct server *server_lookup(pid_t pid); struct server *server_alloc( const struct server *init_serp ); #endif /* SERVER_H */ xinetd-2.3.15.4/src/service.c000066400000000000000000000642161333055217000156420ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_RPC #ifdef __sun #include #include #endif #include #include #include #include #include #include #endif #include #include "sio.h" #include "service.h" #include "util.h" #include "main.h" #include "sconf.h" #include "msg.h" #include "logctl.h" #include "xconfig.h" #include "special.h" #define NEW_SVC() NEW( struct service ) #define FREE_SVC( sp ) FREE( sp ) #define DISABLE( sp ) SVC_STATE((sp)) = SVC_DISABLED static void deactivate( const struct service *sp ); static int banner_always( const struct service *sp, const connection_s *cp ); static const struct name_value service_states[] = { { "Not started", (int) SVC_NOT_STARTED }, { "Active", (int) SVC_ACTIVE }, { "Disabled", (int) SVC_DISABLED }, { "Suspended", (int) SVC_SUSPENDED }, { NULL, 1 }, { "BAD STATE", 0 } } ; /* * Allocate a new struct service and initialize it from scp */ struct service *svc_new( struct service_config *scp ) { struct service *sp ; const char *func = "svc_new" ; sp = NEW_SVC() ; if ( sp == NULL ) { out_of_memory( func ) ; return( NULL ) ; } CLEAR( *sp ) ; SVC_CONF(sp) = scp ; sp->svc_pfd_index = -1; return( sp ) ; } struct service *svc_make_special( struct service_config *scp ) { struct service *sp ; const char *func = "svc_make_special" ; if ( ( sp = svc_new( scp ) ) == NULL ) { out_of_memory( func ) ; return( NULL ) ; } SVC_NOT_GENERIC(sp) = 1 ; SVC_LOG(sp) = ps.rws.program_log ; SVC_REFCOUNT(sp) = 1 ; SVC_STATE(sp) = SVC_ACTIVE ; return( sp ) ; } void svc_free( struct service *sp ) { sc_free( SVC_CONF(sp) ) ; CLEAR( *sp ) ; FREE_SVC( sp ) ; } static status_e set_fd_modes( struct service *sp ) { int sd = SVC_FD( sp ) ; const char *func = "set_fd_modes" ; /* * There is a possibility of blocking on a send/write if * * the service does not require forking (==> is internal) AND * it does not accept connections * * To avoid this, we put the descriptor in FNDELAY mode. * (if the service accepts connections, we still need to put the * 'accepted' connection in FNDELAY mode but this is done elsewhere) */ if ( ! SVC_FORKS( sp ) && ! SVC_ACCEPTS_CONNECTIONS( sp ) && fcntl( sd, F_SETFL, FNDELAY ) == -1 ) { msg( LOG_ERR, func, "fcntl failed (%m) for FNDELAY. service = %s", SVC_ID( sp ) ) ; return( FAILED ) ; } /* * Always set the close-on-exec flag */ if ( fcntl( sd, F_SETFD, FD_CLOEXEC ) == -1 ) { msg( LOG_ERR, func, "fcntl failed (%m) for close-on-exec. service = %s", SVC_ID( sp ) ) ; return( FAILED ) ; } return( OK ) ; } #ifndef NO_RPC static status_e activate_rpc( struct service *sp ) { union xsockaddr tsin; socklen_t sin_len = sizeof(tsin); unsigned long vers ; struct service_config *scp = SVC_CONF( sp ) ; struct rpc_data *rdp = SC_RPCDATA( scp ) ; char *sid = SC_ID( scp ) ; unsigned registered_versions = 0 ; int sd = SVC_FD( sp ) ; const char *func = "activate_rpc" ; if( SC_BIND_ADDR(scp) != 0 ) memcpy( &tsin, SC_BIND_ADDR(scp), sizeof(tsin) ); else memset( &tsin, 0, sizeof(tsin)); if ( SC_PROTOVAL ( scp ) == IPPROTO_TCP ) { M_SET ( scp->sc_xflags, SF_NOLIBWRAP ); } if( SC_IPV4( scp ) ) { tsin.sa_in.sin_family = AF_INET ; sin_len = sizeof(struct sockaddr_in); } else if( SC_IPV6( scp ) ) { tsin.sa_in6.sin6_family = AF_INET6 ; sin_len = sizeof(struct sockaddr_in6); } if ( bind( sd, &tsin.sa, sin_len ) == -1 ) { msg( LOG_ERR, func, "bind failed (%m). service = %s", sid ) ; return( FAILED ) ; } /* * Find the port number that was assigned to the socket */ if ( getsockname( sd, &tsin.sa, &sin_len ) == -1 ) { msg( LOG_ERR, func, "getsockname failed (%m). service = %s", sid ) ; return( FAILED ) ; } if( tsin.sa.sa_family == AF_INET ) SC_SET_PORT( scp, ntohs( tsin.sa_in.sin_port ) ) ; else if( tsin.sa.sa_family == AF_INET6 ) SC_SET_PORT( scp, ntohs( tsin.sa_in6.sin6_port ) ) ; /* * Try to register as many versions as possible */ for ( vers = RD_MINVERS( rdp ) ; vers <= RD_MAXVERS( rdp ) ; vers++ ) { /* Is this right? For instance, if we have both tcp and udp services, * this will unregister the previously registered protocol. * pmap_unset(RD_PROGNUM(rdp), vers); */ if ( pmap_set( RD_PROGNUM( rdp ), vers, SC_PROTOVAL( scp ), SC_PORT( scp ) ) ) registered_versions++ ; else msg( LOG_ERR, func, "pmap_set failed. service=%s program=%ld version=%ld", sid, RD_PROGNUM( rdp ), vers ) ; sleep(1); } if ( debug.on ) msg( LOG_DEBUG, func, "Registered %d versions of %s", registered_versions, sid ) ; return( ( registered_versions == 0 ) ? FAILED : OK ) ; } #endif /* ! NO_RPC */ #define MAX_BIND_ATTEMPTS 10 static status_e activate_normal( struct service *sp ) { union xsockaddr tsin; int sd = SVC_FD( sp ) ; struct service_config *scp = SVC_CONF( sp ) ; uint16_t service_port = SC_PORT( scp ) ; char *sid = SC_ID( scp ) ; const char *func = "activate_normal" ; unsigned int sin_len = sizeof(tsin); int on = 1; int retries = MAX_BIND_ATTEMPTS; useconds_t bind_retry_delay= 0; char *brd_str = NULL; #ifdef IPV6_V6ONLY int v6on = 0; #endif brd_str = getenv("XINETD_BIND_DELAY"); if (brd_str) { bind_retry_delay = atoi(brd_str); if (bind_retry_delay > 500000) { bind_retry_delay = 0; } } if( SC_BIND_ADDR(scp) != NULL ) memcpy(&tsin, SC_BIND_ADDR(scp), sin_len); else memset(&tsin, 0, sin_len); if( SC_IPV4( scp ) ) { tsin.sa_in.sin_family = AF_INET ; tsin.sa_in.sin_port = htons( service_port ) ; sin_len = sizeof(struct sockaddr_in); } else if( SC_IPV6( scp ) ) { tsin.sa_in6.sin6_family = AF_INET6; tsin.sa_in6.sin6_port = htons( service_port ); sin_len = sizeof(struct sockaddr_in6); } #ifdef IPV6_V6ONLY if( SC_IPV6(scp) ) { if( SC_SPECIFIED(scp, A_V6ONLY) ) { v6on = 1; } else { v6on = 0; } if( setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6on, sizeof(v6on)) < 0 ) { msg( LOG_ERR, func, "Setting IPV6_V6ONLY option failed (%m)" ); } } #endif if ( setsockopt( sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof( on ) ) == -1 ) msg( LOG_WARNING, func, "setsockopt SO_REUSEADDR failed (%m). service = %s", sid ) ; if( SC_NODELAY( scp ) && (SC_PROTOVAL(scp) == IPPROTO_TCP) ) { if ( setsockopt( sd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof( on ) ) == -1 ) msg( LOG_WARNING, func, "setsockopt TCP_NODELAY failed (%m). service = %s", sid ) ; } if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL(scp) == IPPROTO_TCP) ) { if( setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof( on ) ) < 0 ) msg( LOG_WARNING, func, "setsockopt SO_KEEPALIVE failed (%m). service = %s", sid ) ; } while ( bind( sd, &tsin.sa, sin_len ) == -1 ) { msg( LOG_ERR, func, "bind failed (%m). service = %s", sid ) ; if (retries-- > 0) { msg( LOG_NOTICE, func, "bind retry attempt %i", MAX_BIND_ATTEMPTS - retries); if (bind_retry_delay) { usleep(bind_retry_delay); } } else { return( FAILED ) ; } } #ifdef IN_MULTICAST if( SC_IPV4(scp) && IN_MULTICAST(tsin.sa_in.sin_addr.s_addr) ) { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = tsin.sa_in.sin_addr.s_addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if ( debug.on ) msg( LOG_DEBUG, func, "Adding multicast membership." ); } #endif return( OK ) ; } /* * Activate a service. */ status_e svc_activate( struct service *sp ) { struct service_config *scp = SVC_CONF( sp ) ; status_e status ; const char *func = "svc_activate" ; /* No activation for MUXCLIENTS. */ if (SC_IS_MUXCLIENT( scp )) { return( OK ); } #ifdef HAVE_POLL if ( ps.rws.descriptors_free <= 0 ) { msg(LOG_ERR, func, "Maximum number of services reached") ; return( FAILED ) ; } if ( sp->svc_pfd_index >= 0 ) { SVC_POLLFD( sp ) = &ps.rws.pfd_array[sp->svc_pfd_index] ; } else { sp->svc_pfd_index = ps.rws.pfds_last ; SVC_POLLFD( sp ) = &ps.rws.pfd_array[ps.rws.pfds_last++] ; } #endif /* HAVE_POLL */ if( SC_IPV4( scp ) ) { SVC_FD(sp) = socket( AF_INET, SC_SOCKET_TYPE( scp ), SC_PROTOVAL( scp ) ) ; } else if( SC_IPV6( scp ) ) { SVC_FD(sp) = socket( AF_INET6, SC_SOCKET_TYPE( scp ), SC_PROTOVAL( scp ) ) ; } if ( SVC_FD(sp) == -1 ) { if (SC_BIND_ADDR(scp) == NULL && SC_IPV6( scp )) { /* there was no bind address configured and IPv6 fails. Try IPv4 */ msg( LOG_NOTICE, func, "IPv6 socket creation failed for service %s, trying IPv4", SC_ID( scp ) ) ; M_CLEAR(SC_XFLAGS(scp), SF_IPV6); M_SET(SC_XFLAGS(scp), SF_IPV4); return svc_activate(sp); } msg( LOG_ERR, func, "socket creation failed (%m). service = %s", SC_ID( scp ) ) ; #ifdef HAVE_POLL SVC_EVENTS( sp ) = 0; SVC_FD( sp ) = 0; #else FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif /* HAVE_POLL */ return( FAILED ) ; } if ( set_fd_modes( sp ) == FAILED ) { (void) Sclose( SVC_FD(sp) ) ; #ifdef HAVE_POLL SVC_EVENTS( sp ) = 0; SVC_FD( sp ) = 0; #else FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif /* HAVE_POLL */ return( FAILED ) ; } #ifndef NO_RPC if ( SC_IS_RPC( scp ) ) status = activate_rpc( sp ) ; else #endif /* ! NO_RPC */ status = activate_normal( sp ) ; if ( status == FAILED ) { (void) Sclose( SVC_FD(sp) ) ; #ifdef HAVE_POLL SVC_EVENTS( sp ) = 0; SVC_FD( sp ) = 0; #else FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif /* HAVE_POLL */ return( FAILED ) ; } if ( log_start( sp, &SVC_LOG(sp) ) == FAILED ) { deactivate( sp ) ; return( FAILED ) ; } /* * Initialize the service data */ SVC_RUNNING_SERVERS(sp) = SVC_RETRIES(sp) = 0 ; if ( SC_MUST_LISTEN( scp ) ) (void) listen( SVC_FD(sp), LISTEN_BACKLOG ) ; ps.rws.descriptors_free-- ; SVC_STATE(sp) = SVC_ACTIVE ; #ifdef HAVE_POLL SVC_EVENTS( sp ) = POLLIN ; #else FD_SET( SVC_FD(sp), &ps.rws.socket_mask ) ; if ( SVC_FD(sp) > ps.rws.mask_max ) ps.rws.mask_max = SVC_FD(sp) ; #endif /* HAVE_POLL */ ps.rws.active_services++ ; ps.rws.available_services++ ; return( OK ) ; } static void deactivate( const struct service *sp ) { (void) Sclose( SVC_FD( sp ) ) ; #ifdef HAVE_POLL SVC_FD( sp ) = 0; #else FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif if (debug.on) msg(LOG_DEBUG, "deactivate", "%d Service %s deactivated", getpid(), SC_NAME( SVC_CONF(sp) ) ); #ifndef NO_RPC if ( SC_IS_RPC( SVC_CONF( sp ) ) ) { unsigned long vers ; const struct rpc_data *rdp = SC_RPCDATA( SVC_CONF( sp ) ) ; for ( vers = RD_MINVERS( rdp ) ; vers <= RD_MAXVERS( rdp ) ; vers++ ) { (void) pmap_unset( RD_PROGNUM( rdp ), vers ) ; } } #endif /* ! NO_RPC */ } /* * Close the service descriptor. * If this is an RPC service, deregister it. * Close the log. */ void svc_deactivate( struct service *sp ) { if ( ! SVC_IS_AVAILABLE( sp ) ) return ; deactivate( sp ) ; ps.rws.descriptors_free++ ; if ( SVC_IS_ACTIVE( sp ) ) { #ifdef HAVE_POLL SVC_EVENTS( sp ) = 0; SVC_FD( sp ) = 0; #else FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif /* HAVE_POLL */ ps.rws.active_services-- ; } ps.rws.available_services-- ; DISABLE( sp ) ; } /* * Suspend a service */ void svc_suspend( struct service *sp ) { const char *func = "svc_suspend" ; if ( ! SVC_IS_ACTIVE( sp ) ) { msg( LOG_ERR, func, "service %s is not active", SVC_ID( sp ) ) ; return ; } #ifdef HAVE_POLL /* * don't reap the pfd from pfd_array, since we must have it allocated for * SVC_FD( sp ) */ SVC_EVENTS( sp ) = 0; #else FD_CLR( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif ps.rws.active_services-- ; if ( debug.on ) msg( LOG_DEBUG, func, "Suspended service %s", SVC_ID( sp ) ) ; SUSPEND( sp ) ; } /* * Resume a suspended service. */ void svc_resume( struct service *sp ) { const char *func = "svc_resume" ; #ifdef HAVE_POLL SVC_EVENTS( sp ) = POLLIN ; #else FD_SET( SVC_FD( sp ), &ps.rws.socket_mask ) ; #endif ps.rws.active_services++ ; if ( debug.on ) msg( LOG_DEBUG, func, "Resumed service %s", SVC_ID( sp ) ) ; RESUME( sp ) ; } /* * Steps: * 1. Deactivate the service * 2. Free all memory used by the service and free the service itself * * Since this function may free all memory associated with the service as * well as the memory pointed by sp, only the value of sp should be used * after this call if the return value is 0 (i.e. no dereferencing of sp). * * Special services are never deactivated. */ int svc_release( struct service *sp ) { char *sid = SVC_ID( sp ) ; const char *func = "svc_release" ; if ( SVC_REFCOUNT(sp) == 0 ) { msg( LOG_ERR, func, "%s: svc_release with 0 count", sid ) ; return( 0 ) ; } SVC_REFCOUNT(sp)-- ; if ( SVC_REFCOUNT(sp) == 0 ) { if ( debug.on ) msg( LOG_DEBUG, func, "ref count of service %s dropped to 0", sid ) ; if ( ! SC_IS_SPECIAL( SVC_CONF( sp ) ) ) { if ( SVC_LOG(sp) ) log_end( SC_LOG( SVC_CONF( sp ) ), SVC_LOG(sp) ) ; svc_deactivate( sp ) ; svc_free( sp ) ; sp = NULL; } else /* this shouldn't happen */ msg( LOG_WARNING, func, "ref count of special service %s dropped to 0", sid ) ; return( 0 ) ; } else return( SVC_REFCOUNT(sp) ) ; } void svc_dump( const struct service *sp, int fd ) { tabprint( fd, 0, "Service = %s\n", SC_NAME( SVC_CONF( sp ) ) ) ; tabprint( fd, 1, "State = %s\n", nv_get_name( service_states, (int) SVC_STATE(sp) ) ) ; sc_dump( SVC_CONF( sp ), fd, 1, FALSE ) ; if ( SVC_IS_ACTIVE(sp) ) { tabprint( fd, 1, "running servers = %d\n", SVC_RUNNING_SERVERS(sp) ) ; tabprint( fd, 1, "retry servers = %d\n", SVC_RETRIES(sp) ) ; tabprint( fd, 1, "attempts = %d\n", SVC_ATTEMPTS(sp) ) ; tabprint( fd, 1, "service fd = %d\n", SVC_FD(sp) ) ; } Sputchar( fd, '\n' ) ; } void svc_request( struct service *sp ) { connection_s *cp ; status_e ret_code; cp = conn_new( sp ) ; if ( cp == CONN_NULL ) return ; /* * Output the banner now that the connection is established. The * other banners come later. */ banner_always(sp, cp); if (SVC_NOT_GENERIC(sp)) ret_code = spec_service_handler(sp, cp); else ret_code = svc_generic_handler(sp, cp); if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) ) drain( cp->co_descriptor ) ; /* Prevents looping next time */ if ( ret_code != OK ) { if ( SVC_LOGS_USERID_ON_FAILURE( sp ) ) { if( spec_service_handler( LOG_SERVICE( ps ), cp ) == FAILED ) conn_free( cp, 1 ) ; else if (!SC_WAITS( SVC_CONF( sp ) ) ) { /* The logging service will gen SIGCHLD thus freeing connection */ CONN_CLOSE(cp) ; } return; } if (!SC_WAITS( SVC_CONF( sp ) )) conn_free( cp, 1 ); else { if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) ) drain( cp->co_descriptor ) ; /* Prevents looping next time */ free( cp ); } } else if ((SVC_NOT_GENERIC(sp)) || (!SC_FORKS( SVC_CONF( sp ) ) ) ) free( cp ); } status_e svc_generic_handler( struct service *sp, connection_s *cp ) { if ( svc_parent_access_control( sp, cp ) == OK ) { return( server_run( sp, cp ) ) ; } return( FAILED ) ; } #define TMPSIZE 1024 /* Print the banner that is supposed to always be printed */ static int banner_always( const struct service *sp, const connection_s *cp ) { const char *func = "banner_always"; const struct service_config *scp = SVC_CONF( sp ) ; /* print the banner regardless of access control */ if ( SC_BANNER(scp) != NULL ) { char tmpbuf[TMPSIZE]; ssize_t retval; int bannerfd = open(SC_BANNER(scp), O_RDONLY); if( bannerfd < 0 ) { msg( LOG_ERR, func, "service = %s, open of banner %s failed", SVC_ID( sp ), SC_BANNER(scp)); return(-1); } while( (retval = read(bannerfd, tmpbuf, sizeof(tmpbuf))) ) { if (retval == (ssize_t)-1) { if (errno == EINTR) continue; else { msg(LOG_ERR, func, "service %s, Error %m reading banner %s", SVC_ID( sp ), SC_BANNER(scp)); break; } } Swrite(cp->co_descriptor, tmpbuf, retval); } Sclose(bannerfd); Sflush ( cp->co_descriptor ); } return(0); } static int banner_fail( const struct service *sp, const connection_s *cp ) { const char *func = "banner_fail"; const struct service_config *scp = SVC_CONF( sp ) ; if ( SC_BANNER_FAIL(scp) != NULL ) { char tmpbuf[TMPSIZE]; int retval; int bannerfd = open(SC_BANNER_FAIL(scp), O_RDONLY); if( bannerfd < 0 ) { msg( LOG_ERR, func, "service = %s, open of banner %s failed", SVC_ID( sp ), SC_BANNER_FAIL(scp)); return(-1); } while( (retval = read(bannerfd, tmpbuf, sizeof(tmpbuf))) ) { if (retval == -1) { if (errno == EINTR) continue; else { msg(LOG_ERR, func, "service %s, Error %m reading banner %s", SVC_ID( sp ), SC_BANNER(scp)); break; } } Swrite(cp->co_descriptor, tmpbuf, retval); } Sclose(bannerfd); Sflush ( cp->co_descriptor ); } return(0); } static int banner_success( const struct service *sp, const connection_s *cp ) { const char *func = "banner_success"; const struct service_config *scp = SVC_CONF( sp ) ; /* print the access granted banner */ if ( SC_BANNER_SUCCESS(scp) != NULL ) { char tmpbuf[TMPSIZE]; int retval; int bannerfd = open(SC_BANNER_SUCCESS(scp), O_RDONLY); if( bannerfd < 0 ) { msg( LOG_ERR, func, "service = %s, open of banner %s failed", SVC_ID( sp ), SC_BANNER_SUCCESS(scp)); return(-1); } while( (retval = read(bannerfd, tmpbuf, sizeof(tmpbuf))) ) { if (retval == -1) { if (errno == EINTR) continue; else { msg(LOG_ERR, func, "service %s, Error %m reading banner %s", SVC_ID( sp ), SC_BANNER(scp)); break; } } Swrite(cp->co_descriptor, tmpbuf, retval); } Sclose(bannerfd); Sflush ( cp->co_descriptor ); } return(0); } static status_e failed_service(struct service *sp, connection_s *cp, access_e result) { struct service_config *scp = SVC_CONF( sp ) ; if ( result != AC_OK ) { bool_int report_failure = TRUE ; /* * Try to avoid reporting multiple times a failed attempt to access * a datagram-based service from a bad address. We do this because * the clients of such services usually send multiple datagrams * before reporting a timeout (we have no way of telling them that * their request has been denied). */ if ( result == AC_ADDRESS && SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) { if( SC_IPV4( scp ) ) { struct sockaddr_in *sinp = SAIN(CONN_ADDRESS( cp )) ; struct sockaddr_in *last = SAIN(SVC_LAST_DGRAM_ADDR(sp)) ; time_t current_time ; if (sinp == NULL ) return FAILED; if ( last == NULL ) { last = SAIN( calloc( 1, sizeof(union xsockaddr) ) ); SVC_LAST_DGRAM_ADDR(sp) = (union xsockaddr *)last; } (void) time( ¤t_time ) ; if ( sinp->sin_addr.s_addr == last->sin_addr.s_addr && sinp->sin_port == last->sin_port ) { if( current_time - SVC_LAST_DGRAM_TIME(sp) <= DGRAM_IGNORE_TIME ) report_failure = FALSE ; else SVC_LAST_DGRAM_TIME(sp) = current_time ; } else { memcpy(SVC_LAST_DGRAM_ADDR(sp), sinp,sizeof(struct sockaddr_in)); SVC_LAST_DGRAM_TIME(sp) = current_time ; } } else if( SC_IPV6( scp ) ) { struct sockaddr_in6 *sinp = SAIN6(CONN_ADDRESS( cp )) ; struct sockaddr_in6 *last = SAIN6(SVC_LAST_DGRAM_ADDR(sp)) ; time_t current_time ; if (sinp == NULL ) return FAILED; if( last == NULL ) { last = SAIN6(calloc( 1, sizeof(union xsockaddr) ) ); SVC_LAST_DGRAM_ADDR( sp ) = (union xsockaddr *)last; } (void) time( ¤t_time ) ; if ( IN6_ARE_ADDR_EQUAL(&(sinp->sin6_addr), &(last->sin6_addr)) && sinp->sin6_port == last->sin6_port ) { if((current_time - SVC_LAST_DGRAM_TIME(sp)) <= DGRAM_IGNORE_TIME) report_failure = FALSE ; else SVC_LAST_DGRAM_TIME(sp) = current_time ; } else { memcpy(SVC_LAST_DGRAM_ADDR(sp),sinp,sizeof(struct sockaddr_in6)); SVC_LAST_DGRAM_TIME(sp) = current_time ; } } } if ( report_failure ) svc_log_failure( sp, cp, result ) ; banner_fail(sp, cp); return( FAILED ) ; } return( OK ); } /* Do the "light weight" access control here */ status_e svc_parent_access_control( struct service *sp, connection_s *cp ) { access_e result; result = parent_access_control( sp, cp ); if( failed_service(sp, cp, result) == FAILED ) return(FAILED); return (OK); } status_e svc_child_access_control( struct service *sp, connection_s *cp ) { access_e result ; result = access_control( sp, cp, MASK_NULL ) ; if( failed_service(sp, cp, result) == FAILED ) return(FAILED); banner_success(sp, cp); return( OK ) ; } /* * Invoked when a server of the specified service dies */ void svc_postmortem( struct service *sp, struct server *serp ) { struct service *co_sp = SERVER_CONNSERVICE( serp ) ; connection_s *cp = SERVER_CONNECTION( serp ) ; const char *func = "svc_postmortem" ; SVC_DEC_RUNNING_SERVERS( sp ) ; /* * Log information about the server that died */ if ( SVC_IS_LOGGING( sp ) ) { if ( SERVER_WRITES_TO_LOG(serp) ) { if ( debug.on ) msg( LOG_DEBUG, func, "Checking log size of %s service", SVC_ID( sp ) ) ; xlog_control( SVC_LOG( sp ), XLOG_SIZECHECK ) ; } svc_log_exit( sp, serp ) ; } /* * Now check if we have to check the log size of the service that owns * the connection */ if ( co_sp != sp && SVC_IS_LOGGING( co_sp ) ) xlog_control( SVC_LOG( co_sp ), XLOG_SIZECHECK ) ; if (!SVC_WAITS(sp)) { conn_free( cp, 1 ) ; cp = NULL; } else { if (cp) { if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) drain( cp->co_descriptor ) ; free(cp); cp = NULL; if( SVC_RELE( sp ) == 0 ) svc_release( sp ); /* shouldn't be 0, but should remove from * pset if it is... */ } svc_resume(sp); } } /* * This function closes all service descriptors. This should be called * for all child processes that fork, but do not exec. This includes * redirect, builtins, and tcpmux. The close on exec flag takes care of * child processes that call exec. Without calling this, the listening * fd's are not closed and reconfig will fail. */ void close_all_svc_descriptors(void) { psi_h iter ; struct service *osp ; /* Have to close all other descriptors here */ iter = psi_create( SERVICES( ps ) ) ; if ( iter == NULL ) { out_of_memory( "close_all_svc_descriptors" ) ; exit( 1 ); } for ( osp = SP( psi_start( iter ) ) ; osp ; osp = SP( psi_next( iter ) ) ) { #ifdef HAVE_POLL if ( osp && SVC_POLLFD( osp ) ) #endif (void) Sclose( SVC_FD( osp ) ) ; } psi_destroy( iter ) ; } xinetd-2.3.15.4/src/service.h000066400000000000000000000153571333055217000156510ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef SERVICE_H #define SERVICE_H #include "config.h" #include #include #include #include "defs.h" #include "pset.h" #include "xlog.h" #include "server.h" #ifdef HAVE_POLL #include "xpoll.h" #endif /* * $Id$ */ /* * NOTE: A service can be disabled but not deleted if it has any servers * running */ typedef enum /* service states */ { SVC_NOT_STARTED = 0, /* no attempt to start it yet */ SVC_ACTIVE, /* service is available */ SVC_SUSPENDED, /* service is suspended */ SVC_DISABLED /* service disabled */ } state_e ; /* * NOTE: Clearing the structure will give all its fields their default values */ struct service { state_e svc_state ; int svc_ref_count ; /* # of pters to this struct */ int svc_pfd_index; /* index of pfd in pfd_array */ struct service_config *svc_conf ; /* service configuration */ #ifdef HAVE_POLL struct pollfd *svc_pfd ; /* pointer to the pollfd */ #else int svc_fd ; /* The Listening FD for the service */ #endif /* HAVE_POLL */ unsigned svc_running_servers ; unsigned svc_retry_servers ; unsigned svc_attempts ; /* # of attempts to start server */ int svc_not_generic ; /* 1 spec_service, 0 generic */ /* * These fields are used to avoid generating too many messages when * receiving datagrams from a bad address. */ union xsockaddr *svc_last_dgram_addr ; time_t svc_last_dgram_time ; xlog_h svc_log ; } ; #define SP( p ) ( (struct service *) (p) ) #define SUSPEND( sp ) (sp)->svc_state = SVC_SUSPENDED #define RESUME( sp ) (sp)->svc_state = SVC_ACTIVE /* * Field access macros */ #define SVC_CONF( sp ) ( (sp)->svc_conf ) #ifdef HAVE_POLL #define SVC_POLLFD( sp ) ( (sp)->svc_pfd ) #define SVC_POLLFD_OFF( sp ) ( SVC_POLLFD( sp )-ps.rws.pfd_array ) #define SVC_EVENTS( sp ) ( POLLFD_EVENTS( SVC_POLLFD( sp ) ) ) #define SVC_REVENTS( sp ) ( POLLFD_REVENTS( SVC_POLLFD( sp ) ) ) #define SVC_FD( sp ) ( POLLFD_FD( SVC_POLLFD( sp ) ) ) #else #define SVC_FD( sp ) ( (sp)->svc_fd ) #endif /* HAVE_POLL */ #define SVC_RUNNING_SERVERS( sp ) (sp)->svc_running_servers #define SVC_RETRIES( sp ) (sp)->svc_retry_servers #define SVC_LOG( sp ) (sp)->svc_log #define SVC_REFCOUNT( sp ) (sp)->svc_ref_count #define SVC_ID( sp ) SC_ID( SVC_CONF( sp ) ) #define SVC_SOCKET_TYPE( sp ) SC_SOCKET_TYPE( SVC_CONF( sp ) ) #define SVC_STATE( sp ) (sp)->svc_state #define SVC_ATTEMPTS( sp ) (sp)->svc_attempts #define SVC_LAST_DGRAM_ADDR( sp ) (sp)->svc_last_dgram_addr #define SVC_LAST_DGRAM_TIME( sp ) (sp)->svc_last_dgram_time #define SVC_NOT_GENERIC( sp ) (sp)->svc_not_generic #define SVC_IS_ACTIVE( sp ) ( (sp)->svc_state == SVC_ACTIVE ) #define SVC_IS_SUSPENDED( sp ) ( (sp)->svc_state == SVC_SUSPENDED ) #define SVC_IS_AVAILABLE( sp ) ( SVC_IS_ACTIVE(sp) || SVC_IS_SUSPENDED(sp) ) #define SVC_IS_DISABLED( sp ) ( (sp)->svc_state == SVC_DISABLED ) #define SVC_IS_MUXCLIENT( sp ) ( SC_IS_MUXCLIENT( SVC_CONF ( sp ) ) ) #define SVC_IS_MUXPLUSCLIENT(sp) ( SC_IS_MUXPLUSCLIENT( SVC_CONF ( sp ) ) ) #define SVC_IS_TCPMUX( sp ) ( SC_IS_TCPMUX( SVC_CONF ( sp ) ) ) #define TCPMUX_ACK "+Go\r\n" #define TCPMUX_NOT_FOUND "-Service name not found\r\n" /* * Predicate checking macros */ #define SVC_FORKS( sp ) SC_FORKS( SVC_CONF( sp ) ) #define SVC_RETRY( sp ) SC_RETRY( SVC_CONF( sp ) ) #define SVC_WAITS( sp ) SC_WAITS( SVC_CONF( sp ) ) #define SVC_IS_INTERCEPTED( sp ) SC_IS_INTERCEPTED( SVC_CONF( sp ) ) #define SVC_ACCEPTS_CONNECTIONS( sp ) \ SC_ACCEPTS_CONNECTIONS( SVC_CONF( sp ) ) #define SVC_IS_LOGGING( sp ) ( (sp)->svc_log != NULL ) #define SVC_LOGS_ON_SUCCESS( sp ) \ ( SVC_IS_LOGGING( sp ) && SC_LOGS_ON_SUCCESS( SVC_CONF( sp ) ) ) #define SVC_LOGS_ON_FAILURE( sp ) \ ( SVC_IS_LOGGING( sp ) && SC_LOGS_ON_FAILURE( SVC_CONF( sp ) ) ) #define SVC_LOGS_ON_EXIT( sp ) \ ( SVC_IS_LOGGING( sp ) && SC_LOGS_ON_EXIT( SVC_CONF( sp ) ) ) #define SVC_LOGS_USERID_ON_SUCCESS( sp ) \ ( SVC_IS_LOGGING( sp ) && SC_LOGS_USERID_ON_SUCCESS( SVC_CONF( sp ) ) ) #define SVC_LOGS_USERID_ON_FAILURE( sp ) \ ( SVC_IS_LOGGING( sp ) && SC_LOGS_USERID_ON_FAILURE( SVC_CONF( sp ) ) ) /* * Reference counting macros */ #define SVC_HOLD( sp ) (sp)->svc_ref_count++ #define SVC_RELE( sp ) ( --(sp)->svc_ref_count ) #define SVC_INTERNAL( sp, serp ) SC_INTERNAL( SVC_CONF( sp ), serp ) #define SVC_MAKE_EXTERNAL( sp ) SC_MAKE_EXTERNAL( SVC_CONF( sp ) ) #define SVC_DEC_RUNNING_SERVERS( sp ) \ { \ if ( SVC_RUNNING_SERVERS( sp ) != 0 ) \ (sp)->svc_running_servers-- ; \ else \ msg( LOG_ERR, func, \ "Service %s: server exit with 0 running servers", SVC_ID( sp ) ) ;\ } #define SVC_INC_RUNNING_SERVERS( sp ) (sp)->svc_running_servers++ #define SVC_INC_RETRIES( sp ) (sp)->svc_retry_servers++ #define SVC_DEC_RETRIES( sp ) (sp)->svc_retry_servers-- struct service *svc_new(struct service_config *scp); struct service *svc_make_special(struct service_config *scp); void svc_free(struct service *sp); status_e svc_activate(struct service *sp); void svc_deactivate(struct service *sp); void svc_suspend(struct service *sp); void svc_resume(struct service *sp); int svc_release(struct service *sp); void svc_dump(const struct service *sp,int fd); void svc_request(struct service *sp); status_e svc_generic_handler( struct service *sp, connection_s *cp ); status_e svc_parent_access_control(struct service *sp,connection_s *cp); status_e svc_child_access_control(struct service *sp,connection_s *cp); void svc_postmortem(struct service *sp,struct server *serp); void close_all_svc_descriptors(void); #endif /* SERVICE_H */ xinetd-2.3.15.4/src/signals.c000066400000000000000000000322321333055217000156330ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILIO_H #include #endif #include "str.h" #include "signals.h" #include "xconfig.h" #include "msg.h" #include "main.h" #include "xtimer.h" #include "child.h" #include "retry.h" #include "reconfig.h" #include "internals.h" #ifdef NO_POSIX_TYPES /* * XXX: here we assume that in the case that NO_POSIX_TYPES is not defined * (i.e. the system has posix types) the sigset_t is also typedef'd * to 'int'. Our goal is to work with systems that have defined * sigset_t but do not yet support the posix signal interface. */ typedef int sigset_t ; struct sigaction { void (*sa_handler)(int) ; sigset_t sa_mask ; int sa_flags ; } ; #endif /* NO_POSIX_TYPES */ static void my_handler( int sig ); static void general_handler( int sig ); typedef void sigfunc( int ); #define SIGSET_NULL ((sigset_t *)0) #define SIGVEC_NULL ((struct sigvec *)0) #define SIGACTION_NULL ((struct sigaction *)0) #ifdef NO_POSIX_SIGS #ifdef NO_SIGVEC #define sigmask( sig ) ( 1 << ( (sig) -1 ) ) typedef int (*sighandler_type)() ; #define sigpause( x ) #define sigsetmask( x ) #endif /* NO_SIGVEC */ #define sigsuspend( set ) sigpause( *set ) #define sigemptyset( set ) (*set) = 0 #define sigaddset( set, sig ) ( ( (*set) |= sigmask( sig ) ), 0 ) #define sigismember( set, sig ) ( ( (*set) & sigmask( sig ) ) != 0 ) /* * Only works for SIG_SETMASK and SIG_UNBLOCK. Also oset must be NULL. */ int sigprocmask( int how, sigset_t *set, sigset_t *oset ) { if ( how == SIG_BLOCK || oset != NULL ) { msg( LOG_ERR, "sigprocmask", "Bad args: how = %d, oset = %p", how, oset ) ; return( -1 ) ; } if ( how == SIG_SETMASK ) { (void) sigsetmask( *set ) ; return( 0 ) ; } if ( how == SIG_UNBLOCK ) { int current_mask = sigblock( 0 ) ; (void) sigsetmask( current_mask & ~*set ) ; return( 0 ) ; } /* NOTREACHED */ } /* * NOTE: This is not a complete imitation of sigaction; in particular it * expects that sap is never NULL and that osap is always NULL. */ int sigaction( int sig, struct sigaction *sap, struct sigaction *osap ) { if ( sap == NULL || osap != NULL ) { msg( LOG_ERR, "sigaction", "Bad args: sap = %p, osap = %p", sap, osap ) ; return( -1 ) ; } #ifndef NO_SIGVEC { struct sigvec sv ; sv.sv_handler = sap->sa_handler ; sv.sv_mask = sap->sa_mask ; sv.sv_flags = sap->sa_flags ; return( sigvec( sig, &sv, SIGVEC_NULL ) ) ; } #else /* NO_SIGVEC */ { sighandler_type new_handler ; new_handler = sa.sa_handler ; return( signal( sig, new_handler ) ? 0 : -1 ) ; } #endif /* ! NO_SIGVEC */ } #endif /* NO_POSIX_SIGS */ /* * reset_sigs is the list of signals that we need to reset to SIG_DFL. * Currently, these are the signals whose actions we set to SIG_IGN. * In general, we should also include any signals that have a handler * that does anything other than setting a flag. We need to do this * in case such a signal occurs while a forked process is providing * an internal service. */ static sigset_t reset_sigs ; /* * nsig is equal to the greatest signal number supported plus 1 */ static int nsig ; /* * When this function returns FAILED, we check the errno to determine * if it failed because the signal number specified was invalid. * This allows us to determine the number of supported signals. */ static status_e handle_signal( int sig ) { struct sigaction sa ; sigfunc *sig_handler ; sa.sa_flags = 0 ; switch ( sig ) { case RECONFIG_HARD_SIG: case OLD_RECONFIG_HARD_SIG: case TERMINATION_SIG: case STATE_DUMP_SIG: case CONSISTENCY_CHECK_SIG: case SERVER_EXIT_SIG: case QUIT_SIG: sig_handler = my_handler ; break ; case SIGTTIN: case SIGTTOU: case SIGTSTP: if ( debug.on ) return( OK ) ; /* FALL THROUGH */ case SIGCONT: /* FALL THROUGH */ /* * We may receive a SIGPIPE when handling an internal stream * service and the other end closes the connection. * We only care about internal services that don't require forking. */ case SIGPIPE: sig_handler = SIG_IGN ; sigaddset( &reset_sigs, sig ) ; break ; case SIGKILL: case SIGSTOP: return( OK ) ; /* we can't catch these two */ /* * If the following two cases are included, SIGSEGV and SIGBUS will * cause core dumps. We want that to happen when we are debugging * xinetd (i.e. DEBUG is defined) and we are not debugging the * signal recovery code (i.e. DEBUG_SIGNALS is not defined). */ case SIGSEGV: case SIGBUS: #if defined( DEBUG ) && !defined( DEBUG_SIGNALS ) return( OK ) ; #else sig_handler = general_handler ; break; #endif case SIGTRAP: if ( debug.on ) return( OK ) ; default: sig_handler = general_handler ; } sigemptyset( &sa.sa_mask ) ; sa.sa_handler = sig_handler ; return( ( sigaction( sig, &sa, SIGACTION_NULL ) == -1 ) ? FAILED : OK ) ; } /* * Install signal handlers for all signals that can be caught. * This implies that no core dumps are generated by default. */ status_e signal_init(void) { int sig ; const char *func = "install_signal_handlers" ; sigemptyset( &reset_sigs ) ; if ( pipe(signals_pending) || fcntl(signals_pending[0], F_SETFD, FD_CLOEXEC) || fcntl(signals_pending[1], F_SETFD, FD_CLOEXEC) ) { msg( LOG_CRIT, func, "Failed to create signal pipe: %m" ); return( FAILED ); } for ( sig = 1 ;; sig++ ) if ( handle_signal( sig ) == FAILED ) { if ( errno == EINVAL ) { nsig = sig ; break ; } else { msg( LOG_CRIT, func, "Failed to install signal handler for signal %s: %m", sig_name( sig ) ) ; return( FAILED ) ; } } return( OK ) ; } #define MAX_SIGNAL_COUNT 50 #define MAX_INTERVAL_SIGNAL_COUNT 10 #define SIGNAL_INTERVAL 1 /* second */ /* * This function handles SIGSEGV and SIGBUS. * Emergency action is taken if a certain number (MAX_SIGNAL_COUNT) of * these signals is received over the lifetime of the program OR * if a certain number (MAX_INTERVAL_SIGNAL_COUNT) of these signals * is received within a certain time interval (SIGNAL_INTERVAL). * * The action depends on the type of the emergency: * Case 1: MAX_INTERVAL_SIGNAL_COUNT is exceeded * If a setjmp environment is available, do a longjmp, otherwise exit * Case 2: MAX_SIGNAL_COUNT is exceeded * Exit * * NOTE: We try to send a message to the log only once to avoid * looping in this function (in case there is a bug in msg()) */ static void bad_signal(void) { static time_t interval_start ; static volatile int interval_signal_count ; static volatile int total_signal_count ; time_t current_time ; const char *func = "bad_signal" ; total_signal_count++ ; if ( total_signal_count == MAX_SIGNAL_COUNT ) { msg( LOG_CRIT, func, "Received %d bad signals. Exiting...", total_signal_count ) ; exit( 1 ) ; } else if ( total_signal_count > MAX_SIGNAL_COUNT ) _exit( 1 ) ; /* in case of a problem in exit(3) */ (void) time( ¤t_time ) ; if ( interval_signal_count > 0 && current_time - interval_start <= SIGNAL_INTERVAL ) { interval_signal_count++ ; if ( interval_signal_count == MAX_INTERVAL_SIGNAL_COUNT ) { if ( ps.rws.env_is_valid ) { interval_start = current_time ; interval_signal_count = 1 ; msg( LOG_ERR, func, "Resetting..." ) ; siglongjmp( ps.rws.env, 1 ) ; /* NOTREACHED */ } msg( LOG_CRIT, func, "Received %d signals in %d seconds. Exiting...", interval_signal_count, SIGNAL_INTERVAL ) ; exit( 1 ) ; } else if ( interval_signal_count > MAX_INTERVAL_SIGNAL_COUNT ) _exit( 1 ) ; /* shouldn't happen */ } else { interval_start = current_time ; interval_signal_count = 1 ; } } char *sig_name( int sig ) { static char signame_buf[ 30 ] ; /* Use strsignal and remove the old sys_siglist stuff */ if ( sig < NSIG ) return( strx_sprint( signame_buf, sizeof( signame_buf ) - 1, "%d (%s)", sig, strsignal(sig) ) ) ; return( strx_sprint( signame_buf, sizeof( signame_buf ) - 1, "%d", sig ) ) ; } /* * For SIGSEGV and SIGBUS we invoke the bad_signal() function * * For other signals, we just log the fact that they occured. * SIGINT is a special case since in debug.on mode, it will * cause termination. */ static void general_handler( int sig ) { sigset_t badsigs ; const char *func = "general_handler" ; /* * Do this here to catch problems like SIGSEGV in msg() */ sigemptyset( &badsigs ) ; sigaddset( &badsigs, sig ) ; (void) sigprocmask( SIG_UNBLOCK, &badsigs, SIGSET_NULL ) ; switch ( sig ) { case SIGBUS: case SIGSEGV: msg( LOG_CRIT, func, "(%d) Unexpected signal: %s", getpid(), sig_name( sig ) ) ; if ( debug.on ) { /* Generate a core dump */ signal(SIGABRT, SIG_DFL); abort(); } else bad_signal() ; break ; default: /* This will cause a dead lock if the signal happens when the * daemon is writing / logging something. The message is not * important, so comment it out. */ // msg( LOG_NOTICE, func, "Unexpected signal %s", sig_name( sig ) ) ; if ( debug.on && sig == SIGINT ) exit( 1 ) ; } } /* * The job of this function is to write the signal received to the * pipe of pending signals, which is in the main select loop. */ static void my_handler( int sig ) { ssize_t ret_val; int saved_errno = errno; #if NSIG < 256 unsigned char sig_byte; if (signals_pending[1] < 0) return; if (sig >= 256) return; sig_byte = sig; do { ret_val = write(signals_pending[1], &sig_byte, 1); } while (ret_val == (ssize_t)-1 && errno == EINTR); #else if (signals_pending[1] < 0) return; do { ret_val = write(signals_pending[1], &sig, sizeof(int)); } while (ret_val == (ssize_t)-1 && errno == EINTR); #endif errno = saved_errno; } /* * Reset all signals to default action. Reset the signal mask * * This function is invoked from a forked process. That is why we * invoke _exit instead of exit (to avoid the possible stdio buffer flushes) */ void signal_default_state(void) { int sig ; sigset_t empty ; for ( sig = 1 ; sig < nsig ; sig++ ) if ( sigismember( &reset_sigs, sig ) == 1 ) if ( signal( sig, SIG_DFL ) == SIG_ERR ) { msg( LOG_ERR, "reset_signals", "signal failed for signal %s: %m", sig_name( sig ) ) ; if ( debug.on ) _exit( 1 ) ; } sigemptyset( &empty ) ; (void) sigprocmask( SIG_SETMASK, &empty, SIGSET_NULL ) ; } void check_pipe(void) { int i; #if NSIG < 256 unsigned char sig; #else int sig; #endif const char *func = "check_pipe"; if (signals_pending[0] < 0) return; if( ioctl(signals_pending[0], FIONREAD, &i) != 0 ) { msg(LOG_ERR, func, "Can't get the number of pending signals: %m"); return; } #if NSIG >= 256 i /= sizeof(int); #endif while( --i >= 0 ) { ssize_t ret_val; do { ret_val = read(signals_pending[0], &sig, sizeof(sig)); } while (ret_val == (ssize_t)-1 && errno == EINTR); if (ret_val != (ssize_t)sizeof(sig) ) { msg(LOG_ERR, func, "Error retrieving pending signal: %m"); return; } if( debug.on ) { msg(LOG_DEBUG, func, "Got signal %s", sig_name(sig)); } switch(sig) { case SERVER_EXIT_SIG: child_exit(); break; case RECONFIG_HARD_SIG: hard_reconfig(); break; case OLD_RECONFIG_HARD_SIG: hard_reconfig(); break; case TERMINATION_SIG: terminate_program(); break; case STATE_DUMP_SIG: dump_internal_state(); break; case CONSISTENCY_CHECK_SIG: user_requested_check(); break; case QUIT_SIG: quit_program(); break; default: msg(LOG_ERR, func, "unexpected signal: %s in signal pipe", sig_name(sig)); } } } xinetd-2.3.15.4/src/signals.h000066400000000000000000000007501333055217000156400ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef _X_SIGNALS #define _X_SIGNALS #include "defs.h" #if defined(NO_POSIX_SIGS) int sigprocmask(int how,sigset_t *set,sigset_t *oset); int sigaction(int sig,struct sigaction *sap,struct sigaction *osap); #endif status_e signal_init(void); char *sig_name(int sig); void signal_default_state(void); void check_pipe(void); #endif xinetd-2.3.15.4/src/sio/000077500000000000000000000000001333055217000146175ustar00rootroot00000000000000xinetd-2.3.15.4/src/sio/impl.h000066400000000000000000000073451333055217000157420ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id$ */ #ifndef SIO_BUFFER_SIZE #include "sioconf.h" #include "sio.h" #ifdef HAVE_MMAP #include /* * A struct map_unit describes a memory mapped area of a file. * * addr is the address where the file is mapped. If addr is NULL * the other fields are meaningless. * valid_bytes indicates how many bytes are _valid_ in that unit * mapped_bytes of a unit is how many bytes are mapped in that * unit ( valid <= mapped ). * Normally mapped_bytes will be equal to valid_bytes until * we reach the end of the file. Then if the file size is not a multiple * of the unit size, only the rest of the file will be mapped at the * unit, leaving part of what was mapped at the unit before still * visible (this happens because I chose not to unmap a unit before * remapping it). In that case valid_bytes shows the size of the "new" * mapping and mapped_bytes shows how many bytes are really mapped. * mapped_bytes is used in Sdone() to unmap the units. */ struct map_unit { caddr_t addr ; size_t valid_bytes ; size_t mapped_bytes ; } ; /* * Meaning of fields used when memory mapping: * * file_offset: it is the offset in the file where the next * mapping should be done * * file_size: size of the file (obtained from stat(2)) */ struct mmap_descriptor { off_t file_offset ; off_t file_size ; struct map_unit first_unit ; struct map_unit second_unit ; } ; typedef struct mmap_descriptor mapd_s ; #endif /* HAVE_MMAP */ typedef enum { FAILURE, SUCCESS } sio_status_e ; /* * Descriptor management: convert a descriptor pointer to an input or * output descriptor pointer */ #define IDP( dp ) (&(dp)->descriptor.input_descriptor) #define ODP( dp ) (&(dp)->descriptor.output_descriptor) #define DESCRIPTOR_INITIALIZED( dp ) ((dp)->initialized) /* * Internal constants */ #define SIO_BUFFER_SIZE 8192 #define SIO_NO_TIED_FD (-1) typedef enum { NO = 0, YES = 1 } boolean_e ; #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif #ifndef NULL #define NULL 0 #endif #ifdef MIN #undef MIN #endif #define MIN( a, b ) ( (a) < (b) ? (a) : (b) ) #define NUL '\0' #ifdef DEBUG static char *itoa( num ) unsigned num ; { #define NUMBUF_SIZE 15 static char numbuf[ NUMBUF_SIZE ] ; char *p = &numbuf[ NUMBUF_SIZE ] ; *--p = '\0' ; do { *--p = num % 10 + '0' ; num /= 10 ; } while ( num ) ; return( p ) ; } # define ASSERT( expr ) \ if ( ! (expr) ) \ { \ char *s1 = "SIO assertion << expr >> failed: File: " ; \ char *s2 = __FILE__ ; \ char *s3 = ", line: " ; \ char *s4 = itoa( __LINE__ ) ; \ char *s5 = "\n" ; \ (void) write( 2, s1, strlen( s1 ) ) ; \ (void) write( 2, s2, strlen( s2 ) ) ; \ (void) write( 2, s3, strlen( s3 ) ) ; \ (void) write( 2, s4, strlen( s4 ) ) ; \ (void) write( 2, s5, strlen( s5 ) ) ; \ exit ( 1 ) ; \ } #else # define ASSERT( expr ) #endif #include /* * Internal functions that are visible */ int __sio_writef( __sio_od_t *odp, int fd ) ; ssize_t __sio_extend_buffer( __sio_id_t *idp, int fd, size_t b_left ) ; int __sio_init( __sio_descriptor_t *dp, int fd, enum __sio_stream stream_type ); ssize_t __sio_more( __sio_id_t *idp, int fd ) ; sio_status_e __sio_switch( __sio_id_t *idp, int fd ) ; #include #define sio_memcopy( from, to, nbytes ) (void) memcpy( to, from, nbytes ) #define sio_memscan( from, nbytes, ch ) memchr( from, ch, nbytes ) #endif /* SIO_BUFFER_SIZE */ xinetd-2.3.15.4/src/sio/sio.c000066400000000000000000000233061333055217000155610ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * (c) Portions Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #ifndef linux #include #endif #ifdef _APPLE_ #undef HAVE_MMAP #endif #include #include #include #include "sio.h" #include "impl.h" /* * SIO WRITE FUNCTIONS: Swrite, Sputc */ /* * Stream write call: arguments same as those of write(2) */ ssize_t Swrite( int fd, const char *addr, size_t nbytes ) { __sio_descriptor_t *dp ; __sio_od_t *odp ; size_t b_transferred ; size_t b_avail ; ssize_t total_b_transferred ; ssize_t b_written ; ssize_t b_in_buffer ; if ( nbytes > SSIZE_MAX ) return( SIO_ERR ); if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR ) return( SIO_ERR ); odp = ODP( dp ) ; ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ; b_avail = odp->buf_end - odp->nextb ; b_transferred = MIN( nbytes, b_avail ) ; sio_memcopy( addr, odp->nextb, b_transferred ) ; odp->nextb += b_transferred ; /* * check if we are done */ if ( b_transferred == nbytes ) return( b_transferred ) ; /* * at this point we know that the buffer is full */ b_in_buffer = odp->buf_end - odp->start ; b_written = __sio_writef( odp, fd ) ; if ( b_written != b_in_buffer ) return( (b_written >= (ssize_t)nbytes) ? (ssize_t)nbytes : b_written ) ; total_b_transferred = b_transferred ; addr += b_transferred ; nbytes -= b_transferred ; for ( ;; ) { b_transferred = MIN( nbytes, odp->buffer_size ) ; sio_memcopy( addr, odp->nextb, b_transferred ) ; odp->nextb += b_transferred ; nbytes -= b_transferred ; if ( nbytes == 0 ) { total_b_transferred += b_transferred ; break ; } /* * the buffer is full */ b_written = __sio_writef( odp, fd ) ; if ( b_written != (ssize_t)odp->buffer_size ) { if ( b_written != SIO_ERR ) { total_b_transferred += b_written ; odp->nextb += b_written ; } break ; } /* * everything is ok */ total_b_transferred += b_transferred ; addr += b_transferred ; } return( total_b_transferred ) ; } /* * Add a character to a file */ static int Sputc( int fd, char c ) { __sio_descriptor_t *dp ; __sio_od_t *odp ; if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM) == SIO_ERR ) return( SIO_ERR ); odp = ODP( dp ) ; ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ; /* * The following is a weak check since we should really be * checking that nextb == buf_end (it would be an error for * nextb to exceed buf_end; btw, the assertion above, when * enabled makes sure this does not occur). * * NOTE: __sio_writef NEVER uses data beyond the end of buffer. */ if ( odp->nextb >= odp->buf_end ) { int b_in_buffer = odp->buf_end - odp->start ; /* * There is nothing we can do if __sio_writef does not manage * to write the whole buffer */ if ( __sio_writef( odp, fd ) != b_in_buffer ) return( SIO_ERR ) ; } *odp->nextb++ = c ; if ( __SIO_MUST_FLUSH( *odp, c ) && __sio_writef( odp, fd ) == SIO_ERR ) return( SIO_ERR ) ; return ( c ) ; } /* * SIO READ FUNCTIONS */ /* * Read a line from a file * Returns a pointer to the beginning of the line or NULL */ char *Srdline( int fd ) { __sio_descriptor_t *dp ; __sio_id_t *idp ; char *cp ; char *line_start ; size_t b_left ; ssize_t extension ; if( sio_setup( fd, &dp, __SIO_INPUT_STREAM ) == SIO_ERR ) return( NULL ); idp = IDP( dp ) ; ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; #ifdef HAVE_MMAP if ( idp->memory_mapped && __sio_switch( idp, fd ) == FAILURE ) return( NULL ) ; #endif b_left = idp->end - idp->nextb ; /* * Look for a '\n'. If the search fails, extend the buffer * and search again (the extension is performed by copying the * bytes that were searched to the auxiliary buffer and reading * new input in the main buffer). * If the new input still does not contain a '\n' and there is * more space in the main buffer (this can happen with network * connections), read more input until either the buffer is full * or a '\n' is found. * Finally, set cp to point to the '\n', and line_start to * the beginning of the line */ if ( b_left && ( cp = sio_memscan( idp->nextb, b_left, '\n' ) ) != NULL ) { line_start = idp->nextb ; idp->nextb = cp + 1 ; } else { extension = __sio_extend_buffer( idp, fd, b_left ) ; if ( extension > 0 ) { ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ; line_start = idp->start ; cp = sio_memscan( idp->nextb, (size_t)extension, '\n' ) ; if ( cp != NULL ) idp->nextb = cp + 1 ; else for ( ;; ) { idp->nextb = idp->end ; extension = __sio_more( idp, fd ) ; if ( extension > 0 ) { cp = sio_memscan( idp->nextb, (size_t)extension, '\n' ) ; if ( cp == NULL ) continue ; idp->nextb = cp + 1 ; break ; } else { /* * If there is spare room in the buffer avoid trashing * the last character */ if ( idp->end < &idp->buf[ idp->buffer_size ] ) cp = idp->end ; else cp = &idp->buf[ idp->buffer_size - 1 ] ; break ; } } } else /* buffer could not be extended */ if ( b_left == 0 ) { /* * Set errno to 0 if EOF has been reached */ if ( extension == 0 ) errno = 0 ; return( NULL ) ; } else { line_start = idp->start ; cp = idp->end ; /* * By setting idp->nextb to be equal to idp->end, * subsequent calls to Srdline will return NULL because * __sio_extend_buffer will be invoked and it will return 0. */ idp->nextb = idp->end ; } } *cp = NUL ; idp->line_length = cp - line_start ; return( line_start ) ; } /* * SIO CONTROL FUNCTIONS */ /* * Flush the buffer associated with the given file descriptor * The special value, SIO_FLUSH_ALL flushes all buffers * * Return value: * 0 : if fd is SIO_FLUSH_ALL or if the flush is successful * SIO_ERR: if fd is not SIO_FLUSH_ALL and * the flush is unsuccessful * or the descriptor is not initialized or it is not * an output descriptor */ int Sflush( int fd ) { __sio_descriptor_t *dp ; int b_in_buffer ; if ( fd == SIO_FLUSH_ALL ) { for ( fd = 0, dp = __sio_descriptors ; fd < __sio_n_descriptors ; dp++, fd++ ) if ( DESCRIPTOR_INITIALIZED( dp ) && dp->stream_type == __SIO_OUTPUT_STREAM ) (void) __sio_writef( ODP( dp ), fd ) ; return( 0 ) ; } else { if ( fd >= __sio_n_descriptors ) { errno = EBADF ; return( SIO_ERR ) ; } dp = &__sio_descriptors[ fd ] ; if( (! DESCRIPTOR_INITIALIZED( dp )) || (dp->stream_type != __SIO_OUTPUT_STREAM) ) return( SIO_ERR ); b_in_buffer = ODP( dp )->nextb - ODP( dp )->start ; if ( __sio_writef( ODP( dp ), fd ) != b_in_buffer ) return( SIO_ERR ) ; else return( 0 ) ; } } /* * Close the file descriptor. This call is provided because * a file descriptor may be closed and then reopened. There is * no easy way for SIO to identify such a situation, so Sclose * must be used. * * Sclose invokes Sdone which finalizes the buffer. * There is no SIO_CLOSE_ALL value for fd because such a thing * would imply that the program will exit very soon, therefore * the closing of all file descriptors will be done in the kernel * (and the finalization will be done by the finalization function * NOTE: not true if the OS does not support a finalization function) * * There is no need to invoke SETUP; Sdone will do it. */ int Sclose( int fd ) { if ( __SIO_FD_INITIALIZED( fd ) ) { if ( Sdone( fd ) == SIO_ERR ) { close( fd ); return( SIO_ERR ) ; } } return( close( fd ) ) ; } /* * Changes the type of buffering on the specified descriptor. * As a side-effect, it initializes the descriptor as an output stream. */ int Sbuftype( int fd, int type ) { __sio_descriptor_t *dp ; /* * Check for a valid type */ if ( type != SIO_LINEBUF && type != SIO_FULLBUF && type != SIO_NOBUF ) { errno = EINVAL ; return( SIO_ERR ) ; } if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR ) return( SIO_ERR ); ODP( dp )->buftype = type ; return( 0 ) ; } int sio_setup(int fd, __sio_descriptor_t **dp, unsigned int type) { if ( fd >= __sio_n_descriptors ) { if( Smorefds(fd) != 0 ) { errno = EBADF; return(SIO_ERR); } } *dp = &__sio_descriptors[ fd ]; if( DESCRIPTOR_INITIALIZED( *dp ) ) { if( (*dp)->stream_type != type ) { errno = EBADF; return( SIO_ERR ); } } else if( __sio_init(*dp, fd, type) == SIO_ERR ) return( SIO_ERR ); return( 0 ); } /* * The Sputchar function depends on the fact that the fields * nextb, buf_end, end * are 0 if a stream descriptor is not being used or has not yet been * initialized. * This is true initially because of the static allocation of the * descriptor array, and Sdone must make sure that it is true * after I/O on a descriptor is over. */ int Sputchar( int fd, char c ) { int ret = 0; if( __SIO_OD( fd ).nextb < __SIO_OD( fd ).buf_end ) { if( __SIO_OD(fd).buftype == SIO_FULLBUF ) { ret = *(__SIO_OD(fd).nextb)++ = (unsigned char) (c); } else if ( __SIO_OD(fd).buftype == SIO_LINEBUF ) { if( (*(__SIO_OD(fd).nextb) = (unsigned char)(c)) != '\n' ) { ret = *(__SIO_OD(fd).nextb)++; } else { ret = Sputc( fd, *(__SIO_OD(fd).nextb) ); } } else { ret = Sputc( fd, c ); } } else { ret = Sputc( fd, c ); } return ret; } xinetd-2.3.15.4/src/sio/sio.h000066400000000000000000000107761333055217000155750ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id$ */ #ifndef __SIO_H #define __SIO_H #include #include #include #include /* * Naming conventions: * 1) SIO functions and macros have names starting with a capital S * 2) SIO constants meant to be used by user programs have * names starting with SIO_ * 3) Internal functions, struct identifiers, enum identifiers * etc. have names starting with __sio * 4) Internal constants and macros have names starting with __SIO */ /* * external constants * * SIO_FLUSH_ALL: flush all output streams * SIO_ERR: operation failed * SIO_EOF: eof on stream * #define SIO_EOF (-2) */ #define SIO_FLUSH_ALL (-1) #define SIO_ERR (-1) /* * Buffering types */ #define SIO_FULLBUF 0 #define SIO_LINEBUF 1 #define SIO_NOBUF 2 /* * Descriptor for an input stream */ struct __sio_input_descriptor { /* * buf: points to the buffer area. * When doing memory mapping, it is equal to the unit * from which we are reading. When doing buffered I/O * it points to the primary buffer. The auxiliary * buffer is right below buf and is of the same size. */ char *buf ; size_t buffer_size ; char *start ; /* start of valid buffer contents */ char *end ; /* end of valid buffer contents + 1 */ char *nextb ; /* pointer to next byte to read/write */ /* Always: start <= nextb < end */ unsigned line_length ; int max_line_length ; int tied_fd ; int memory_mapped ; /* flag to denote if we use */ /* memory mapping */ } ; typedef struct __sio_input_descriptor __sio_id_t ; /* * Descriptor for an output stream */ struct __sio_output_descriptor { /* * buf: points to the buffer area. * buf_end: is equal to buf + buffer_size */ char *buf ; char *buf_end ; unsigned buffer_size ; char *start ; /* start of valid buffer contents */ /* (used by the R and W functions) */ char *nextb ; /* pointer to next byte to read/write */ /* Always: start <= nextb < buf_end */ int buftype ; /* type of buffering */ } ; typedef struct __sio_output_descriptor __sio_od_t ; /* * Stream types */ enum __sio_stream { __SIO_INPUT_STREAM, __SIO_OUTPUT_STREAM } ; /* * General descriptor */ struct __sio_descriptor { union { __sio_id_t input_descriptor ; __sio_od_t output_descriptor ; } descriptor ; enum __sio_stream stream_type ; int initialized ; } ; typedef struct __sio_descriptor __sio_descriptor_t ; /* * The array of descriptors (as many as available file descriptors) */ extern int __sio_n_descriptors ; extern __sio_descriptor_t *__sio_descriptors ; /* * Internally used macros */ #define __SIO_FD_INITIALIZED( fd ) \ (fd >= 0 && fd < __sio_n_descriptors && __sio_descriptors[ fd ].initialized) #define __SIO_ID( fd ) (__sio_descriptors[ fd ].descriptor.input_descriptor) #define __SIO_OD( fd ) (__sio_descriptors[ fd ].descriptor.output_descriptor) #define __SIO_MUST_FLUSH( od, ch ) \ ( (od).buftype != SIO_FULLBUF && \ ( (od).buftype == SIO_NOBUF || ch == '\n' ) ) /* * SIO Macros: * * SIOLINELEN( fd ) * * NOTE: The maximum line size depends on whether the descriptor * was originally memory mapped. If it was, then the maximum * line size will be the map_unit_size (a function of the system * page size and PAGES_MAPPED). Otherwise, it will be either the * optimal block size as reported by stat(2) or SIO_BUFFER_SIZE. */ #define SIOLINELEN( fd ) __SIO_ID( fd ).line_length /* * The Read functions */ char *Srdline ( int fd ) ; /* * The Write functions */ ssize_t Swrite ( int fd, const char *buf, size_t ); ssize_t Sprint ( int fd, const char *format, ... ) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))); #else ; #endif int Sputchar( int fd, char c ); ssize_t Sprintv ( int fd, const char *format, va_list ap ) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 0))); #else ; #endif /* * other functions */ int Sdone ( int fd ) ; int Sflush ( int fd ) ; int Sclose ( int fd ) ; int Sbuftype ( int fd, int type ) ; int Smorefds ( int ) ; ssize_t __sio_converter( __sio_od_t *, int , const char *, va_list ); int sio_setup(int fd, __sio_descriptor_t **dp, unsigned int type ); #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void terminate(const char *); #endif /* __SIO_H */ xinetd-2.3.15.4/src/sio/sioconf.h000066400000000000000000000075461333055217000164440ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id$ */ /* * This file has 2 sections: * 1. a OS-specific section * 2. a CPU/compiler-specific section * * You can override/redefine any of the constants/macros in this file. * by uncommenting the inclusion of customconf.h and placing your own * definitions in that file. */ /* #include "customconf.h" */ /* * OS-specific section. * * Features here use the flag HAVE_. * List of flags (check the following for macros that can be overridden): * * HAVE_MMAP (overridable macros) * * HAVE_ATEXIT * HAVE_ONEXIT * HAVE_OTHER_FINALIZER (must define macros) */ /* * Memory mapping. * The library requires 3 macros: SIO_MMAP, SIO_MUNMAP, SIO_MNEED. * You can selectively override any of them. * Notice that the SIO_MNEED macro is not required. If your system * does not have madvise, you can define the macro as: * #define SIO_MNEED( addr, len ) */ #ifdef HAVE_MMAP #if !defined( SIO_MMAP ) || !defined( SIO_MUNMAP ) || !defined( SIO_MNEED ) #include #include #endif #ifndef SIO_MMAP #define SIO_MMAP( addr, len, fd, off ) \ mmap( addr, len, PROT_READ, \ ( addr == 0 ) ? MAP_PRIVATE : MAP_PRIVATE + MAP_FIXED, \ fd, off ) #endif #ifndef SIO_MUNMAP #define SIO_MUNMAP( addr, len ) munmap( addr, len ) #endif #ifndef SIO_MNEED #if defined(linux) #define SIO_MNEED( addr, len ) #else #define SIO_MNEED( addr, len ) (void) madvise( addr, len, MADV_WILLNEED ) #endif #endif #endif /* HAVE_MMAP */ /* * N_SIO_DESCRIPTORS is the maximum number of file descriptors * supported by the OS */ #include #ifdef OPEN_MAX #define N_SIO_DESCRIPTORS OPEN_MAX #else #define N_SIO_DESCRIPTORS NOFILE #endif /* * Finalization function. * * The purpose of this function is to do work after your program has * called exit(3). In the case of SIO, this means flushing the SIO * output buffers. * * If your system does not support atexit or onexit but has some other * way of installing a finalization function, you define the flag * HAVE_FINALIZER. Then you must define the macros * SIO_FINALIZE and SIO_DEFINE_FIN * * SIO_FINALIZE attempts to install a finalization function and returns TRUE * if successful, FALSE if unsuccessful. * SIO_DEFINE_FIN defines the finalization function (the reason for this macro * s that different systems pass different number/type of arguments to the * finalization function; the SIO finalization function does not use any * arguments). */ #if defined(HAVE_ONEXIT) || defined(HAVE_ATEXIT) || defined(HAVE_FINALIZER) #define HAVE_FINALIZATION_FUNCTION #if defined( HAVE_ONEXIT ) && defined( HAVE_ATEXIT ) #undef HAVE_ONEXIT #endif #ifdef HAVE_ONEXIT #define SIO_FINALIZE( func ) ( on_exit( func, (caddr_t) 0 ) == 0 ) #define SIO_DEFINE_FIN( func ) static void func ( exit_status, arg ) \ int exit_status ; \ caddr_t arg ; #endif /* HAVE_ONEXIT */ #ifdef HAVE_ATEXIT #define SIO_FINALIZE( func ) ( atexit( func ) == 0 ) #define SIO_DEFINE_FIN( func ) static void func () #endif /* HAVE_ATEXIT */ #endif /* HAVE_ONEXIT || HAVE_ATEXIT || HAVE_FINALIZER */ /* * CPU/compiler-specific section. * * The following constant affects the behavior of Sprint. * * Sprint performs integer->string conversions by first converting * the integer to the widest int type supported by the CPU/compiler. * By default, this is the "long int" type. If your machine has * a wider type, you can specify it by defining the WIDE_INT constant. * For example: * #define WIDE_INT long long */ xinetd-2.3.15.4/src/sio/siosup.c000066400000000000000000000540221333055217000163100ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #ifdef _APPLE_ #undef HAVE_MMAP #endif #include "impl.h" #include "sio.h" int __sio_n_descriptors = 0 ; __sio_descriptor_t *__sio_descriptors = NULL ; static sio_status_e setup_read_buffer( __sio_id_t *idp, unsigned buf_size ); #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif /* * Code for finalization */ #ifdef HAVE_FINALIZATION_FUNCTION static int finalizer_installed ; SIO_DEFINE_FIN( sio_cleanup ) { (void) Sflush( SIO_FLUSH_ALL ) ; } #endif /* HAVE_FINALIZATION_FUNCTION */ #ifdef HAVE_MMAP #define CHAR_NULL ((char *)0) /* * PAGES_MAPPED gives the size of each map unit in pages */ #define PAGES_MAPPED 2 static size_t map_unit_size = 0 ; /* bytes */ static size_t page_size = 0 ; /* bytes */ static mapd_s *mmap_descriptors = NULL ; #define MDP( fd ) ( mmap_descriptors + (fd) ) /* * NOTES ON MEMORY MAPPING: * * 1. Memory mapping works only for file descriptors opened for input * 2. Mapping an object to a part of the address space where another * object is mapped will cause the old mapping to disappear (i.e. mmap * will not fail) * * Memory mapping interface: * SIO_MMAP : maps a file into a portion of the address space. * SIO_MUNMAP: unmap a portion of the address space * SIO_MNEED: indicate to the OS that we will need a portion of * our address space. * * The map_unit_size variable defines how much of the file is mapped at * a time. It is a multiple of the operating system page size. It is * not less than SIO_BUFFER_SIZE unless SIO_BUFFER_SIZE is not a * multiple of the page size (so the SIO_BUFFER_SIZE overrides * PAGES_MAPPED). * * NOTE: All memory mapping code is in this file only */ /* * Macros used by the memory mapping code */ #define FIRST_TIME( dp ) ( dp->buf == NULL ) /* * Functions to support memory mapping: * * try_memory_mapping * buffer_setup * __sio_switch * initial_map * map_unit */ /* * try_memory_mapping attempts to setup the specified descriptor * for memory mapping. * It returns FAILURE if it fails and SUCCESS if it is successful. * If HAVE_MMAP is not defined, the function is defined to be FAILURE. * * Sets fields: * memory_mapped: TRUE or FALSE * * Also sets the following fields if memory_mapped is TRUE: * file_offset, file_size, buffer_size * */ static sio_status_e try_memory_mapping( int fd, __sio_id_t *idp, const struct stat *stp ) { int access_f ; /* * Do not try memory mapping if: * 1) The file is not a regular file * 2) The file is a regular file but has zero-length * 3) The file pointer is not positioned at the beginning of the file * 4) The fcntl to obtain the file descriptor flags fails * 5) The access mode is not O_RDONLY or O_RDWR * * The operations are done in this order to avoid the system calls * if possible. */ if ( ( ( stp->st_mode & S_IFMT ) != S_IFREG ) || ( stp->st_size == 0 ) || ( lseek( fd, (long)0, 1 ) != 0 ) || ( ( access_f = fcntl( fd, F_GETFL, 0 ) ) == -1 ) || ( ( access_f &= 0x3 ) != O_RDONLY && access_f != O_RDWR ) ) { idp->memory_mapped = FALSE ; return( FAILURE ) ; } /* * Determine page_size and map_unit_size. * Note that the code works even if PAGES_MAPPED is 0. */ if ( page_size == 0 ) { page_size = getpagesize() ; map_unit_size = page_size * PAGES_MAPPED ; if ( map_unit_size < SIO_BUFFER_SIZE ) { if ( map_unit_size != 0 && SIO_BUFFER_SIZE % map_unit_size == 0 ) map_unit_size = SIO_BUFFER_SIZE ; else map_unit_size = page_size ; } } MDP(fd)->file_offset = 0 ; MDP(fd)->file_size = stp->st_size ; idp->buffer_size = map_unit_size ; idp->buf = CHAR_NULL ; idp->memory_mapped = TRUE ; return( SUCCESS ) ; } /* * Copy the current_unit to the primary buffer * * Sets fields: start, end, nextb * Also sets the file pointer */ static void buffer_setup( __sio_id_t *idp, int fd, const struct map_unit *mu_cur, const struct map_unit *mu_next ) { off_t new_offset ; sio_memcopy( mu_cur->addr, idp->buf, mu_cur->valid_bytes ) ; idp->start = idp->buf ; idp->end = idp->buf + mu_cur->valid_bytes ; idp->nextb = idp->buf + ( idp->nextb - mu_cur->addr ) ; if ( mu_next->addr != CHAR_NULL ) new_offset = MDP(fd)->file_offset - mu_next->valid_bytes ; else new_offset = MDP(fd)->file_offset ; (void) lseek( fd, new_offset, 0 ) ; } /* * Switch from memory mapping to buffered I/O * If any mapping has occurred, then the current unit is * copied into the buffer that is allocated. * Any data in the next unit is ignored. * We rely on idp->buf to identify the current unit (so it * better be equal to the address of one of the units). * * Sets fields: * start, end, nextb */ sio_status_e __sio_switch( __sio_id_t *idp, int fd ) { mapd_s *mdp = MDP( fd ) ; struct map_unit *mu_cur, *mu_next ; unsigned buffer_size = idp->buffer_size ; char *buf_addr = idp->buf ; int first_time = FIRST_TIME( idp ) ; /* * Initialize stream for buffering */ if ( setup_read_buffer( idp, buffer_size ) == FAILURE ) return( FAILURE ) ; if ( ! first_time ) { /* * Find current, next unit */ if ( buf_addr == mdp->first_unit.addr ) { mu_cur = &mdp->first_unit ; mu_next = &mdp->second_unit ; } else { mu_cur = &mdp->second_unit ; mu_next = &mdp->first_unit ; } buffer_setup( idp, fd, mu_cur, mu_next ) ; /* * Destroy all mappings */ (void) SIO_MUNMAP( mu_cur->addr, mu_cur->mapped_bytes ) ; if ( mu_next->addr != NULL ) (void) SIO_MUNMAP( mu_next->addr, mu_next->mapped_bytes ) ; } else idp->start = idp->end = idp->nextb = idp->buf ; idp->memory_mapped = FALSE ; return( SUCCESS ) ; } /* * initial_map does the first memory map on the file descriptor. * It attempts to map both units. * The mapping always starts at file offset 0. * * SETS FIELDS: * first_unit.*, second_unit.* * file_offset * * Returns: * number of bytes mapped in first_unit * or * 0 to indicate that mmap failed. */ static int initial_map( mapd_s *mdp, int fd ) { caddr_t addr ; size_t requested_length = 2 * map_unit_size ; size_t mapped_length = MIN( (size_t)mdp->file_size, requested_length ) ; size_t bytes_left ; size_t bytes_in_unit ; addr = SIO_MMAP( CHAR_NULL, mapped_length, fd, 0 ) ; if ( addr == MAP_FAILED ) return( 0 ) ; SIO_MNEED( addr, mapped_length ) ; /* * Map as much as possible in the first unit */ bytes_in_unit = MIN( mapped_length, map_unit_size ) ; mdp->first_unit.addr = addr ; mdp->first_unit.mapped_bytes = bytes_in_unit ; mdp->first_unit.valid_bytes = bytes_in_unit ; /* * If there is more, map it in the second unit. */ bytes_left = mapped_length - bytes_in_unit ; if ( bytes_left != 0 ) { mdp->second_unit.addr = addr + bytes_in_unit ; mdp->second_unit.mapped_bytes = bytes_left ; mdp->second_unit.valid_bytes = bytes_left ; } else mdp->second_unit.addr = CHAR_NULL ; mdp->file_offset = mapped_length ; return( mdp->first_unit.valid_bytes ) ; } /* * ALGORITHM: * * if ( there are more bytes in the file ) * { * map them at the given unit * update offset * issue SIO_MNEED() * } * else * unmap the unit */ static sio_status_e map_unit( mapd_s *mdp, int fd, struct map_unit *mup ) { size_t bytes_left = mdp->file_size - mdp->file_offset ; size_t bytes_to_map = MIN( bytes_left, map_unit_size ) ; if ( bytes_to_map != 0 ) { if ( SIO_MMAP( mup->addr, bytes_to_map, fd, mdp->file_offset ) == MAP_FAILED ) return( FAILURE ) ; /* XXX: need to do more ? */ mup->valid_bytes = bytes_to_map ; ASSERT( mup->valid_bytes <= mup->mapped_bytes ) ; mdp->file_offset += bytes_to_map ; SIO_MNEED( mup->addr, mup->valid_bytes ) ; } else { (void) SIO_MUNMAP( mup->addr, mup->mapped_bytes ) ; mup->addr = CHAR_NULL ; } return( SUCCESS ) ; } #else #define try_memory_mapping( x, y, z ) FAILURE #endif /* HAVE_MMAP */ static sio_status_e setup_read_buffer( __sio_id_t *idp, unsigned buf_size ) { char *buf ; /* * First allocate space for 2 buffers: primary and auxiliary */ buf = malloc( buf_size * 2 ) ; if ( buf == NULL ) return( FAILURE ) ; /* * The descriptor buf field should point to the start of the main buffer */ idp->buf = buf + buf_size ; idp->buffer_size = buf_size ; return( SUCCESS ) ; } static sio_status_e init_input_stream( __sio_id_t *idp, int fd, const struct stat *stp ) { /* * First initialize the fields relevant to buffering: buf, buffer_size */ if ( try_memory_mapping( fd, idp, stp ) == FAILURE ) { /* * Try to use normal buffering */ unsigned buf_size = (unsigned) ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ; if ( setup_read_buffer( idp, buf_size ) == FAILURE ) return( FAILURE ) ; } /* * Initialize remaining descriptor fields */ idp->max_line_length = 2 * idp->buffer_size - 1 ; idp->start = idp->end = idp->nextb = idp->buf ; idp->tied_fd = SIO_NO_TIED_FD ; return( SUCCESS ) ; } static sio_status_e init_output_stream( __sio_od_t *odp, int fd, const struct stat *stp ) { unsigned buf_size ; char *buf ; buf_size = (unsigned) ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ; buf = malloc( buf_size ) ; if ( buf == NULL ) return( FAILURE ) ; /* * Initialize buffering fields */ odp->buf = buf ; odp->buffer_size = buf_size ; odp->buf_end = odp->buf + buf_size ; /* * Initialize remaining fields */ odp->start = odp->nextb = odp->buf ; if ( isatty( fd ) ) odp->buftype = SIO_LINEBUF ; if ( fd == 2 ) odp->buftype = SIO_NOBUF ; return( SUCCESS ) ; } /* * Initialize stream I/O for a file descriptor. * * Arguments: * fd: file descriptor * dp: descriptor pointer * stream_type: either __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM * * Returns * 0 if successful * SIO_ERR if the file descriptor is not valid (sets errno) * exits if stream_type is not __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM */ int __sio_init( __sio_descriptor_t *dp, int fd, enum __sio_stream stream_type ) { struct stat st ; memset(dp, 0, sizeof(__sio_descriptor_t)); if ( fd >= __sio_n_descriptors ) { errno = EBADF ; return( SIO_ERR ) ; } if ( fstat( fd, &st ) == -1 ) return( SIO_ERR ) ; switch ( stream_type ) { case __SIO_INPUT_STREAM: if ( init_input_stream( IDP( dp ), fd, &st ) == FAILURE ) return( SIO_ERR ) ; break ; case __SIO_OUTPUT_STREAM: if ( init_output_stream( ODP( dp ), fd, &st ) == FAILURE ) return( SIO_ERR ) ; break ; default: terminate( "SIO __sio_init: bad stream type (internal error).\n" ) ; /* NOTREACHED */ } dp->stream_type = stream_type ; dp->initialized = TRUE ; #ifdef HAVE_FINALIZATION_FUNCTION if ( ! finalizer_installed ) { if ( ! SIO_FINALIZE( sio_cleanup ) ) { char *s = "SIO __sio_init: finalizer installation failed\n" ; (void) write( 2, s, strlen( s ) ) ; } else finalizer_installed = TRUE ; } #endif /* HAVE_FINALIZATION_FUNCTION */ return( 0 ) ; } /* * __sio_writef writes the data in the buffer to the file descriptor. * * It tries to write as much data as possible until either all data * are written or an error occurs. EINTR is the only error that is * ignored. * In case an error occurs but some data were written, that number * is returned instead of SIO_ERR. * * Fields modified: * When successful: start, nextb * When not successful: start * * Return value: * Number of bytes written * SIO_ERR, if write(2) fails and no data were written */ int __sio_writef( __sio_od_t *odp, int fd ) { int b_in_buffer ; int cc_total = 0 ; /* * Make sure we don't exceed the buffer limits * Maybe we should log this ? XXX */ if ( odp->nextb > odp->buf_end ) odp->nextb = odp->buf_end ; b_in_buffer = odp->nextb - odp->start ; if ( b_in_buffer == 0 ) return( 0 ) ; for ( ;; ) { int cc ; cc = write( fd, odp->start, (size_t)b_in_buffer ) ; if ( cc == b_in_buffer ) { odp->start = odp->nextb = odp->buf ; cc_total += cc ; break ; } else if ( cc == -1 ) { if ( errno == EINTR ) continue ; else /* * If some bytes were written, return that number, otherwise * return SIO_ERR */ return( ( cc_total != 0 ) ? cc_total : SIO_ERR ) ; } else /* some bytes were written */ { odp->start += cc ; /* advance start of buffer */ b_in_buffer -= cc ; /* decrease number bytes left in buffer */ cc_total += cc ; /* count the bytes that were written */ } } return( cc_total ) ; } /* * __sio_readf reads data from the file descriptor into the buffer. * Unlike __sio_writef it does NOT try to read as much data as will fit * in the buffer. It ignores EINTR. * * Returns: # of bytes read or SIO_ERR * * Fields set: * If it does not return SIO_ERR, it sets start, nextb, end * If it returns SIO_ERR, it does not change anything */ static ssize_t __sio_readf( __sio_id_t *idp, int fd ) { ssize_t cc ; /* * First check for a tied fd and flush the stream if necessary * * XXX the return value of __sio_writef is not checked. * Is that right ? */ if ( idp->tied_fd != SIO_NO_TIED_FD ) (void) __sio_writef( &__SIO_OD( idp->tied_fd ), idp->tied_fd ) ; #ifdef HAVE_MMAP if ( idp->memory_mapped ) { mapd_s *mdp = MDP( fd ) ; /* * The functions initial_map and map_unit may fail. * In either case, we switch to buffered I/O. * If initial_map fails, we have read no data, so we * should perform a read(2). * If map_unit fails (for the next unit), we still have * the data in the current unit, so we can return. */ if ( FIRST_TIME( idp ) ) { cc = initial_map( mdp, fd ) ; if ( cc > 0 ) idp->buf = mdp->first_unit.addr ; else { if ( __sio_switch( idp, fd ) == FAILURE ) return( SIO_ERR ) ; cc = (ssize_t)-1 ; } } else { struct map_unit *mu_cur, *mu_next ; if ( idp->buf == mdp->first_unit.addr ) { mu_cur = &mdp->first_unit ; mu_next = &mdp->second_unit ; } else { mu_cur = &mdp->second_unit ; mu_next = &mdp->first_unit ; } if ( mu_next->addr != NULL ) { idp->buf = mu_next->addr ; cc = mu_next->valid_bytes ; /* * XXX: Here we may return SIO_ERR even though there * are data in the current unit because the switch * fails (possibly because malloc failed). */ if ( map_unit( mdp, fd, mu_cur ) == FAILURE && __sio_switch( idp, fd ) == FAILURE ) return( SIO_ERR ) ; } else cc = 0 ; } if ( cc >= 0 ) { idp->end = idp->buf + cc ; idp->start = idp->nextb = idp->buf ; return( cc ) ; } } #endif /* HAVE_MMAP */ for ( ;; ) { cc = read( fd, idp->buf, idp->buffer_size ) ; if ( cc == (ssize_t)-1 ) if ( errno == EINTR ) continue ; else return( SIO_ERR ) ; else break ; } idp->end = idp->buf + cc ; idp->start = idp->nextb = idp->buf ; return( cc ) ; } /* * __sio_extend_buffer is used by Srdline to extend the buffer * If successful, it returns the number of bytes that have been read. * If it fails (because of end-of-file or I/O error), it returns 0 or -1. * * Fields modified: * idp->start points to the start of the buffer area (which is in the * auxiliary buffer) * Also, if successful, idp->nextb is set to idp->buf, idp->end is modified. */ ssize_t __sio_extend_buffer( __sio_id_t *idp, int fd, size_t b_left ) { ssize_t b_read ; /* * copy to auxiliary buffer */ if ( b_left ) sio_memcopy( idp->nextb, idp->buf - b_left, b_left ) ; b_read = __sio_readf( idp, fd ) ; idp->start = idp->buf - b_left ; return( b_read ) ; } /* * __sio_more tries to read more data from the given file descriptor iff * there is free space in the buffer. * __sio_more is used only by Srdline and only AFTER __sio_extend_buffer * has been called. This implies that * a) this is not a memory mapped file * b) __sio_readf has been called (so we don't need to check for tied fd's * * Fields modified (only if successful): * idp->end * * Return value: the number of bytes read. */ ssize_t __sio_more( __sio_id_t *idp, int fd ) { int b_left = &idp->buf[ idp->buffer_size ] - idp->end ; ssize_t cc ; if ( b_left <= 0 ) return( 0 ) ; for ( ;; ) { cc = read( fd, idp->end, (size_t)b_left ) ; if ( cc >= 0 ) { idp->end += cc ; return( cc ) ; } else if ( errno == EINTR ) continue ; else return( SIO_ERR ) ; } } /* * Finalize a buffer by unmapping the file or freeing the malloc'ed memory. * This function is only called by Sclose. We always free memory even if * SIO_ERR is returned as long as the descriptor was initialized. */ int Sdone( int fd ) { __sio_descriptor_t *dp ; int ret_val = 0; if ( fd < 0 || fd >= __sio_n_descriptors ) { errno = EBADF ; return( SIO_ERR ) ; } dp = &__sio_descriptors[ fd ] ; if ( ! DESCRIPTOR_INITIALIZED( dp ) ) { errno = EBADF ; return( SIO_ERR ) ; } switch ( dp->stream_type ) { case __SIO_INPUT_STREAM: { __sio_id_t *idp = IDP( dp ) ; #ifdef HAVE_MMAP if ( idp->memory_mapped ) { mapd_s *mdp = MDP( fd ) ; if ( mdp->first_unit.addr != CHAR_NULL ) (void) SIO_MUNMAP( mdp->first_unit.addr, mdp->first_unit.mapped_bytes ) ; if ( mdp->second_unit.addr != CHAR_NULL ) (void) SIO_MUNMAP( mdp->second_unit.addr, mdp->second_unit.mapped_bytes ) ; idp->memory_mapped = FALSE ; } #endif /* HAVE_MMAP */ free( idp->buf - idp->buffer_size ) ; idp->nextb = idp->end = NULL ; } break ; case __SIO_OUTPUT_STREAM: { __sio_od_t *odp = ODP( dp ) ; if ( Sflush( fd ) == SIO_ERR ) ret_val = SIO_ERR; free( odp->buf ) ; odp->nextb = odp->buf_end = NULL ; } break ; default: terminate( "SIO Sdone: bad stream type\n" ) ; } memset( dp, 0, sizeof(__sio_descriptor_t) ); dp->initialized = FALSE ; return ret_val; } static char *sioexpand( char *area, unsigned old_size, unsigned new_size, int is_static ) { char *new_area ; if ( is_static ) { if ( ( new_area = malloc( new_size ) ) == NULL ) return( NULL ) ; sio_memcopy( area, new_area, old_size ) ; } else if ( ( new_area = realloc( area, new_size ) ) == NULL ) return( NULL ) ; return( new_area ) ; } /* * Expand the descriptor array (and if we use memory mapping the * memory mapping descriptors). We first expand the memory mapping * descriptors. * There is no problem if the expansion of the SIO descriptors fails * (i.e. there is no need to undo anything). */ int Smorefds(int fd) { char *p ; int is_static ; unsigned new_size, old_size ; int n_fds = 4; /* Let's bump 4 at a time for hysteresis */ /* If the fd is out of range of the proposed size, make n_fds big enough */ if (fd >= (__sio_n_descriptors+n_fds)) n_fds += fd - __sio_n_descriptors; #ifdef HAVE_MMAP old_size = __sio_n_descriptors * sizeof( mapd_s ) ; new_size = n_fds * sizeof( mapd_s ) ; new_size += old_size; is_static = ( mmap_descriptors == NULL ) ; p = sioexpand( (char *)mmap_descriptors, old_size, new_size, is_static ) ; if ( p == NULL ) return( SIO_ERR ) ; memset(p+old_size, 0, new_size-old_size); mmap_descriptors = (mapd_s *) p ; #endif /* HAVE_MMAP */ old_size = __sio_n_descriptors * sizeof( __sio_descriptor_t ) ; new_size = n_fds * sizeof( __sio_descriptor_t ) ; new_size += old_size; is_static = ( __sio_descriptors == NULL ) ; p = sioexpand( (char *)__sio_descriptors, old_size, new_size, is_static ) ; if ( p == NULL ) return( SIO_ERR ) ; memset(p+old_size, 0, new_size-old_size); __sio_descriptors = (__sio_descriptor_t *) p ; __sio_n_descriptors += n_fds ; return( 0 ) ; } void terminate(const char *msg) { syslog(LOG_CRIT, "%s", msg); (void) abort() ; _exit( 1 ) ; /* NOT REACHED */ } xinetd-2.3.15.4/src/sio/sprint.c000066400000000000000000000410061333055217000163030ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include "sio.h" #include "impl.h" #include "libportable.h" typedef long long wide_int ; typedef unsigned long long u_wide_int ; typedef int bool_int ; static char *conv_10( wide_int num, bool_int is_unsigned, bool_int *is_negative, char *buf_end, ssize_t *len ); #define S_NULL_LEN 6 static char S_NULL[S_NULL_LEN+1] = "(null)"; #define FLOAT_DIGITS 6 #define MAX_FLOAT_DIGITS 17 #define EXPONENT_LENGTH 10 /* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * XXX: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512 /* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer * bep points to the end-of-buffer+1 * While using this macro, note that the nextb pointer is NOT updated. * * No I/O is performed if fd is not positive. Negative fd values imply * conversion with the output directed to a string. Excess characters * are discarded if the string overflows. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR( c, sp, bep, odp, cc, fd ) \ { \ if ( sp < bep ) \ { \ *sp++ = c ; \ cc++ ; \ } \ else \ { \ if ( fd >= 0 ) \ { \ odp->nextb = sp ; \ if ( __sio_writef( odp, fd ) != bep - odp->start ) \ return( ( cc != 0 ) ? cc : SIO_ERR ) ; \ sp = odp->nextb ; \ *sp++ = c ; \ cc++ ; \ } \ } \ if ( __SIO_MUST_FLUSH( *odp, c ) && fd >= 0 ) \ { \ ssize_t b_in_buffer = sp - odp->start ; \ \ odp->nextb = sp ; \ if ( __sio_writef( odp, fd ) != b_in_buffer ) \ return( cc ) ; \ sp = odp->nextb ; \ } \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( isdigit( *str ) ) \ { \ num *= 10 ; \ num += NUM( *str++ ) ; \ } /* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) \ while ( s_len < precision && s_len < NUM_BUF_SIZE - 2 ) \ { \ *--s = '0' ; \ s_len++ ; \ } /* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD( width, len, ch ) \ do \ { \ INS_CHAR( ch, sp, bep, odp, cc, fd ) ; \ width-- ; \ } while ( width > len ) /* * Sprint is the equivalent of printf for SIO. * It returns the # of chars written * Assumptions: * - all floating point arguments are passed as doubles */ /* VARARGS2 */ ssize_t Sprint( int fd, const char *fmt, ...) { __sio_descriptor_t *dp ; __sio_od_t *odp ; ssize_t cc ; va_list ap ; if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR ) return( SIO_ERR ); odp = ODP( dp ) ; va_start( ap, fmt ) ; cc = __sio_converter( odp, fd, fmt, ap ) ; va_end( ap ) ; return( cc ) ; } /* * This is the equivalent of vfprintf for SIO */ ssize_t Sprintv( int fd, const char *fmt, va_list ap) { __sio_descriptor_t *dp ; __sio_od_t *odp ; if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR ) return( SIO_ERR ); odp = ODP( dp ) ; return( __sio_converter( odp, fd, fmt, ap ) ) ; } /* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ static char *conv_fp( char format, double num, boolean_e add_dp, int precision, bool_int *is_negative, char buf[], ssize_t *len ) /* always add decimal point if YES */ { char *s = buf ; char *p = NULL ; int decimal_point ; if ( precision > MAX_FLOAT_DIGITS ) precision = MAX_FLOAT_DIGITS ; if ( format == 'f' ) p = (char *)fcvt( num, precision, &decimal_point, is_negative ) ; else /* either e or E format */ p = (char *)ecvt( num, precision+1, &decimal_point, is_negative ) ; /* * Check for Infinity and NaN */ if ( isalpha( *p ) ) { *len = strlen( strcpy( buf, p ) ) ; *is_negative = FALSE ; return( buf ) ; } if ( format == 'f' ) if ( decimal_point <= 0 ) { *s++ = '0' ; if ( precision > 0 ) { *s++ = '.' ; while ( decimal_point++ < 0 ) *s++ = '0' ; } else if ( add_dp ) *s++ = '.' ; } else { while ( decimal_point-- > 0 ) *s++ = *p++ ; if ( precision > 0 || add_dp ) *s++ = '.' ; } else { *s++ = *p++ ; if ( precision > 0 || add_dp ) *s++ = '.' ; } /* * copy the rest of p, the NUL is NOT copied */ while ( *p ) *s++ = *p++ ; if ( format != 'f' ) { char temp[ EXPONENT_LENGTH ] ; /* for exponent conversion */ ssize_t t_len ; bool_int exponent_is_negative ; *s++ = format ; /* either e or E */ decimal_point-- ; if ( decimal_point != 0 ) { p = conv_10( (wide_int)decimal_point, FALSE, &exponent_is_negative, &temp[ EXPONENT_LENGTH ], &t_len ) ; *s++ = exponent_is_negative ? '-' : '+' ; /* * Make sure the exponent has at least 2 digits */ if ( t_len == 1 ) *s++ = '0' ; while ( t_len-- ) *s++ = *p++ ; } else { *s++ = '+' ; *s++ = '0' ; *s++ = '0' ; } } *len = s - buf ; return( buf ) ; } /* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char *conv_p2( u_wide_int num, int nbits, char format, char *buf_end, ssize_t *len ) { int mask = ( 1 << nbits ) - 1 ; char *p = buf_end ; static const char low_digits[] = "0123456789abcdef" ; static const char upper_digits[] = "0123456789ABCDEF" ; const char *digits = ( format == 'X' ) ? upper_digits : low_digits ; do { *--p = digits[ num & mask ] ; num >>= nbits ; } while( num ) ; *len = buf_end - p ; return( p ) ; } /* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) */ static char *conv_10( wide_int num, bool_int is_unsigned, bool_int *is_negative, char *buf_end, ssize_t *len ) { char *p = buf_end ; u_wide_int magnitude ; if ( is_unsigned ) { magnitude = (u_wide_int) num ; *is_negative = FALSE ; } else { *is_negative = ( num < 0 ) ; /* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if ( *is_negative ) { wide_int t = num + 1 ; magnitude = ( (u_wide_int) -t ) + 1 ; } else magnitude = (u_wide_int) num ; } /* * We use a do-while loop so that we write at least 1 digit */ do { u_wide_int new_magnitude = magnitude / 10 ; *--p = magnitude - new_magnitude*10 + '0' ; magnitude = new_magnitude ; } while ( magnitude ) ; *len = buf_end - p ; return( p ) ; } /* * Do format conversion placing the output in odp. * Note: we do not support %n for security reasons. */ ssize_t __sio_converter( __sio_od_t *odp, int fd, const char *fmt, va_list ap ) { char *sp = NULL; char *bep = NULL; ssize_t cc = 0 ; size_t i ; char *s = NULL; char *q = NULL; ssize_t s_len ; int min_width = 0 ; int precision = 0 ; enum { LEFT, RIGHT } adjust ; char pad_char ; char prefix_char ; double fp_num ; wide_int i_num = 0 ; u_wide_int ui_num ; char num_buf[ NUM_BUF_SIZE ] ; char char_buf[ 2 ] ; /* for printing %% and % */ /* * Flag variables */ boolean_e is_long = NO; boolean_e is_quad = NO; boolean_e alternate_form ; boolean_e print_sign ; boolean_e print_blank ; boolean_e adjust_precision ; boolean_e adjust_width ; bool_int is_negative ; sp = odp->nextb ; bep = odp->buf_end ; while ( *fmt ) { if ( *fmt != '%' ) { INS_CHAR( *fmt, sp, bep, odp, cc, fd ) ; } else { /* * Default variable settings */ adjust = RIGHT ; alternate_form = print_sign = print_blank = NO ; pad_char = ' ' ; prefix_char = NUL ; fmt++ ; /* * Try to avoid checking for flags, width or precision */ if ( isascii( *fmt ) && ! islower( *fmt ) ) { /* * Recognize flags: -, #, BLANK, + */ for ( ;; fmt++ ) { if ( *fmt == '-' ) adjust = LEFT ; else if ( *fmt == '+' ) print_sign = YES ; else if ( *fmt == '#' ) alternate_form = YES ; else if ( *fmt == ' ' ) print_blank = YES ; else if ( *fmt == '0' ) pad_char = '0' ; else break ; } /* * Check if a width was specified */ if ( isdigit( *fmt ) ) { STR_TO_DEC( fmt, min_width ) ; adjust_width = YES ; } else if ( *fmt == '*' ) { min_width = va_arg( ap, int ) ; fmt++ ; adjust_width = YES ; if ( min_width < 0 ) { adjust = LEFT ; min_width = -min_width ; } } else adjust_width = NO ; /* * Check if a precision was specified. */ if ( *fmt == '.' ) { adjust_precision = YES ; fmt++ ; if ( isdigit( *fmt ) ) { STR_TO_DEC( fmt, precision ) ; } else if ( *fmt == '*' ) { precision = va_arg( ap, int ) ; fmt++ ; if ( precision < 0 ) precision = 0 ; } else precision = 0 ; } else adjust_precision = NO ; } else adjust_precision = adjust_width = NO ; /* * Modifier check */ if ( *fmt == 'l' ) { if( *(fmt+1) == 'l' ) { is_quad = YES; fmt++; } else { is_quad = NO; is_long = YES; } fmt++ ; } else { is_long = NO; is_quad = NO; } if ( *fmt == 'q' ) { is_quad = YES; fmt++; } /* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch( *fmt ) { case 'd': case 'i': case 'u': if ( is_long ) i_num = va_arg( ap, long ) ; else i_num = (wide_int) va_arg( ap, int ) ; s = conv_10( i_num, (*fmt) == 'u', &is_negative, &num_buf[ NUM_BUF_SIZE ], &s_len ) ; FIX_PRECISION( adjust_precision, precision, s, s_len ) ; if ( *fmt != 'u' ) { if ( is_negative ) prefix_char = '-' ; else if ( print_sign ) prefix_char = '+' ; else if ( print_blank ) prefix_char = ' ' ; } break ; case 'o': if ( is_long ) ui_num = va_arg( ap, u_wide_int ) ; else ui_num = (u_wide_int) va_arg( ap, unsigned int ) ; s = conv_p2( ui_num, 3, *fmt, &num_buf[ NUM_BUF_SIZE ], &s_len ) ; FIX_PRECISION( adjust_precision, precision, s, s_len ) ; if ( alternate_form && *s != '0' ) { *--s = '0' ; s_len++ ; } break ; case 'x': case 'X': if ( is_long ) ui_num = (u_wide_int) va_arg( ap, unsigned long ) ; else if ( is_quad ) ui_num = (u_wide_int) va_arg( ap, u_wide_int ) ; else ui_num = (u_wide_int) va_arg( ap, unsigned int ) ; s = conv_p2( ui_num, 4, *fmt, &num_buf[ NUM_BUF_SIZE ], &s_len ) ; FIX_PRECISION( adjust_precision, precision, s, s_len ) ; if ( alternate_form && i_num != 0 ) { *--s = *fmt ; /* 'x' or 'X' */ *--s = '0' ; s_len += 2 ; } break ; case 's': s = va_arg( ap, char * ) ; if ( s != NULL ) { s_len = strlen( s ) ; if ( adjust_precision && precision < s_len ) s_len = precision ; } else { s = S_NULL ; s_len = S_NULL_LEN ; } pad_char = ' ' ; break ; case 'f': case 'e': case 'E': fp_num = va_arg( ap, double ) ; s = conv_fp( *fmt, fp_num, alternate_form, ( adjust_precision == NO ) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[ 1 ], &s_len ) ; if ( is_negative ) prefix_char = '-' ; else if ( print_sign ) prefix_char = '+' ; else if ( print_blank ) prefix_char = ' ' ; break ; case 'g': case 'G': if ( adjust_precision == NO ) precision = FLOAT_DIGITS ; else if ( precision == 0 ) precision = 1 ; if ( precision > MAX_FLOAT_DIGITS ) precision = MAX_FLOAT_DIGITS ; /* * We use &num_buf[ 1 ], so that we have room for the sign */ s = gcvt( va_arg( ap, double ), precision, &num_buf[ 1 ] ) ; if ( *s == '-' ) prefix_char = *s++ ; else if ( print_sign ) prefix_char = '+' ; else if ( print_blank ) prefix_char = ' ' ; s_len = strlen( s ) ; if ( alternate_form && ( q = strchr( s, '.' ) ) == NULL ) s[ s_len++ ] = '.' ; if ( *fmt == 'G' && ( q = strchr( s, 'e' ) ) != NULL ) *q = 'E' ; break ; case 'c': char_buf[ 0 ] = (char) (va_arg( ap, int )) ; s = &char_buf[ 0 ] ; s_len = 1 ; pad_char = ' ' ; break ; case '%': char_buf[ 0 ] = '%' ; s = &char_buf[ 0 ] ; s_len = 1 ; pad_char = ' ' ; break ; /* * Always extract the argument as a "char *" pointer. We * should be using "void *" but there are still machines * that don't understand it. * If the pointer size is equal to the size of an unsigned * integer we convert the pointer to a hex number, otherwise * we print "%p" to indicate that we don't handle "%p". */ case 'p': /* Assume that pointers are of same size as "unsigned long" */ ui_num = (u_wide_int)(unsigned long)va_arg( ap, char * ) ; if ( sizeof( char * ) <= sizeof( u_wide_int ) ) s = conv_p2( ui_num, 4, 'x', &num_buf[ NUM_BUF_SIZE ], &s_len ) ; else { char_buf[ 0 ] = '%' ; char_buf[ 1 ] = 'p' ; s = char_buf ; s_len = 2 ; } pad_char = ' ' ; break ; case NUL: /* * The last character of the format string was %. * We ignore it. */ continue ; /* * The default case is for unrecognized %'s. * We print % to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of __sio_converter to another function * that understands some other % (like syslog). * Note that we can't point s inside fmt because the * unknown could be preceded by width etc. */ default: char_buf[ 0 ] = '%' ; char_buf[ 1 ] = *fmt ; s = char_buf ; s_len = 2 ; pad_char = ' ' ; break ; } if ( prefix_char != NUL ) { *--s = prefix_char ; s_len++ ; } if ( adjust_width && adjust == RIGHT && min_width > s_len ) { if ( pad_char == '0' && prefix_char != NUL ) { INS_CHAR( *s, sp, bep, odp, cc, fd ) s++ ; s_len-- ; min_width-- ; } PAD( min_width, s_len, pad_char ) ; } /* * Print the string s. */ for ( i = s_len ; i != 0 ; i-- ) { INS_CHAR( *s, sp, bep, odp, cc, fd ) ; s++ ; } /* * Ignore pad_char for right padding, as padding * with zeroes would produce a different number. */ if ( adjust_width && adjust == LEFT && min_width > s_len ) PAD( min_width, s_len, ' ' ) ; } fmt++ ; } odp->nextb = sp ; return( cc ) ; } xinetd-2.3.15.4/src/special.c000066400000000000000000000056361333055217000156230ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include "special.h" #include "server.h" #include "msg.h" #include "sconst.h" #include "int.h" #include "util.h" #include "nvlists.h" #include "service.h" #include "state.h" #include "main.h" #include "connection.h" #include "sconf.h" #include "options.h" #include "xconfig.h" #include "ident.h" static void stream_logging( struct server *) ; static const struct builtin_service special_services[] = { { LOG_SERVICE_NAME, SOCK_STREAM, { stream_logging, FORK } }, { INTERCEPT_SERVICE_NAME, SOCK_STREAM, { intercept, FORK } }, { INTERCEPT_SERVICE_NAME, SOCK_DGRAM, { intercept, FORK } }, { NULL, 0, { NULL, 0 } } } ; const builtin_s *spec_find( const char *service_name, int type ) { const builtin_s *bp ; const struct name_value *nvp ; const char *func = "spec_find" ; if ( (bp = builtin_lookup( special_services, service_name, type )) ) return( bp ) ; nvp = nv_find_name( socket_types, type ) ; if ( nvp == NULL ) { msg( LOG_ERR, func, "unknown socket type: %d", type ) ; return( NULL ) ; } msg( LOG_ERR, func, "special service %s,%s not supported", service_name, nvp->name ) ; return( NULL ) ; } status_e spec_service_handler( struct service *sp, connection_s *cp ) { return(server_run( sp, cp )); } static struct service *spec_setup( const char *name, int socket_type, int instances ) { const builtin_s *bp ; struct service_config *scp ; bp = spec_find( name, socket_type ) ; if ( bp == NULL ) return( NULL ) ; if ( ( scp = sc_make_special( name, bp, instances ) ) == NULL ) return( NULL ) ; return( svc_make_special( scp ) ) ; } /* * Initialize the special services and the corresponding entries in * the program state structure. */ void spec_include(void) { int instances ; instances = logprocs_option ? logprocs_option_arg : DEFAULT_LOGPROCS ; LOG_SERVICE( ps ) = spec_setup( LOG_SERVICE_NAME, SOCK_STREAM, instances ) ; } static void stream_logging( struct server *serp ) { const char *func = "stream_logging" ; idresult_e result ; #ifdef DEBUG_LOGGING if ( debug.on ) { msg( LOG_DEBUG, func, "%d is sleeping", getpid() ) ; sleep( 10 ) ; } #endif result = log_remote_user( serp, LOGUSER_FAILURE_TIMEOUT ) ; if ( (result != IDR_OK) && (result != IDR_NOSERVER) ) msg( LOG_ERR, func, "Failed to contact identity server at %s: %s", conn_addrstr( SERVER_CONNECTION( serp ) ), idresult_explain( result ) ) ; } xinetd-2.3.15.4/src/special.h000066400000000000000000000003671333055217000156240ustar00rootroot00000000000000#ifndef SPECIAL_H #define SPECIAL_H #include "defs.h" #include "builtins.h" const builtin_s *spec_find(const char *service_name,int type); status_e spec_service_handler( struct service *sp, connection_s *cp ); void spec_include(void); #endif xinetd-2.3.15.4/src/state.h000066400000000000000000000053471333055217000153270ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef STATE_H #define STATE_H /* * $Id$ */ #include "config.h" #include #include #include #include #include "libportable.h" #include #ifdef HAVE_POLL #include #endif #include "xlog.h" #include "pset.h" #include "defs.h" #include "mask.h" struct read_only_state { rlim_t max_descriptors ; /* original hard rlimit or OPEN_MAX */ rlim_t process_limit ; /* if 0, there is no limit */ int cc_interval ; /* # of seconds the cc gets invoked. */ const char *pid_file ; /* where the pidfile is located */ const char *config_file ; int is_superuser ; char **Argv ; int Argc ; } ; struct defaults { struct service_config *def_settings ; xlog_h def_log ; bool_int def_log_creation_failed ; } ; struct read_write_state { int descriptors_free ; /* may be negative (reserved) */ int available_services ; /* # of available services */ int active_services ; /* services with descriptors set */ /* in socket mask */ #ifdef HAVE_POLL struct pollfd *pfd_array; /* array passed to poll(2) */ int pfds_last; /* index of last fd in the array */ int pfds_allocated; /* size of the array */ #else fd_set socket_mask ; int mask_max ; #endif /* HAVE_POLL */ pset_h servers ; /* table of running servers */ pset_h retries ; /* table of servers to retry */ pset_h services ; /* table of services */ struct service *logging ; struct defaults defs ; xlog_h program_log ; sigjmp_buf env ; bool_int env_is_valid ; } ; struct program_state { struct read_only_state ros ; struct read_write_state rws ; } ; #define DEFAULTS( ps ) (ps).rws.defs.def_settings #define DEFAULT_LOG( ps ) (ps).rws.defs.def_log #define DEFAULT_LOG_ERROR( ps ) (ps).rws.defs.def_log_creation_failed #define LOG_SERVICE( ps ) (ps).rws.logging #define SERVICES( ps ) (ps).rws.services #define SERVERS( ps ) (ps).rws.servers #define RETRIES( ps ) (ps).rws.retries #endif /* STATE_H */ xinetd-2.3.15.4/src/str/000077500000000000000000000000001333055217000146355ustar00rootroot00000000000000xinetd-2.3.15.4/src/str/str.h000066400000000000000000000032751333055217000156250ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef __STR_H #define __STR_H /* * $Id$ */ #include #ifdef __GNUC__ #define PRINTF_FORMAT(n, m) \ __attribute__ ((format (printf, n, m))) #else #define PRINTF_FORMAT(n, m) #endif typedef void *str_h ; /* * strprint(3) functions */ char *strx_sprint ( char *buf, int len, const char *fmt, ... ) PRINTF_FORMAT(3, 4); int strx_nprint ( char *buf, int len, const char *fmt, ... ) PRINTF_FORMAT(3, 4); void strx_print ( int *count, char *buf, int len, const char *fmt, ... ) PRINTF_FORMAT(4, 5); int strx_nprintv ( char *buf, int len, const char *fmt, va_list ) PRINTF_FORMAT(3, 0); void strx_printv ( int *cnt, char *buf, int len, const char *fmt, va_list ) PRINTF_FORMAT(4, 0); /* * strparse(3) functions */ int str_setstr( str_h handle, char *newstr ); /* * Return values */ #define STR_OK 0 #define STR_ERR (-1) /* * Flags for the string parsing functions */ #define STR_NOFLAGS 0x0 #define STR_RETURN_ERROR 0x1 #define STR_NULL_START 0x2 #define STR_NULL_END 0x4 #define STR_MALLOC 0x8 /* * Error values */ #define STR_ENULLSEPAR 1 #define STR_ENULLSTRING 2 #define STR_ENOMEM 3 char *new_string(const char *) ; str_h str_parse ( char *str, const char *separ, int flags, int *errnop ) ; void str_endparse ( str_h handle ) ; char *str_component ( str_h handle ) ; /* * strutil(3) functions */ char *str_casefind ( char *s1, const char *s2 ) ; void str_fill ( char *s, char c ) ; #endif /* __STR_H */ xinetd-2.3.15.4/src/str/strparse.c000066400000000000000000000102041333055217000166410ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include "str.h" #include "strparse.h" #include "sio.h" static int str_errno ; #define HANDLE_ERROR( flags, retval, errp, errval, msg ) \ if ( flags & STR_RETURN_ERROR ) \ { \ *errp = errval ; \ return( retval ) ; \ } \ else \ terminate( msg ); char *new_string( const char *s ) { if ( s ) return strdup( s ) ; else return 0; } str_h str_parse( register char *str, const char *separ, int flags, int *errnop ) { register struct str_handle *hp ; int *errp = ( errnop == NULL ) ? &str_errno : errnop ; if ( separ == NULL ) { HANDLE_ERROR( flags, NULL, errp, STR_ENULLSEPAR, "STR str_parse: NULL separator" ) ; } hp = (struct str_handle *) malloc( sizeof( struct str_handle ) ) ; if ( hp == NULL ) { HANDLE_ERROR( flags, NULL, errp, STR_ENOMEM, "STR str_parse: malloc failed" ) ; } hp->string = str ; hp->pos = str ; hp->separator = new_string( separ ) ; if ( hp->separator == NULL ) { if ( flags & STR_RETURN_ERROR ) { free( (char *) hp ) ; *errp = STR_ENOMEM ; return( NULL ) ; } else { terminate( "STR str_parse: malloc failed" ) ; } } hp->flags = flags ; hp->errnop = errp ; hp->no_more = ( str == NULL ) ; return( (str_h) hp ) ; } void str_endparse( str_h handle ) { register struct str_handle *hp = (struct str_handle *) handle ; if( hp->separator != NULL ) free( hp->separator ) ; free( (char *) handle ) ; } /* * Change the string */ int str_setstr( str_h handle, char *newstr ) { register struct str_handle *hp = (struct str_handle *) handle ; if ( newstr == NULL ) { HANDLE_ERROR( hp->flags, STR_ERR, hp->errnop, STR_ENULLSTRING, "STR str_setstr: NULL string" ) ; } hp->string = newstr ; hp->pos = newstr ; hp->no_more = FALSE ; return( STR_OK ) ; } char *str_component( str_h handle ) { register char *start ; register char *last ; register unsigned int sep_count ; char *retval ; int last_char ; register struct str_handle *hp = (struct str_handle *) handle ; register int first_call = ( hp->pos == hp->string ) ; if ( hp->no_more ) return( NULL ) ; /* * Get number of separator characters. * Find beginning of component. */ sep_count = strspn( hp->pos, hp->separator ) ; /* * If this is the first call, and there are separator characters * at the beginning of the string and the STR_NULL_START flag is set * we return a 0-length string. */ if ( first_call && ( sep_count > 0 ) && ( hp->flags & STR_NULL_START )) { start = hp->pos ; last = hp->pos ; } else { start = hp->pos + sep_count ; if ( *start == '\0' ) { last = start ; hp->no_more = TRUE ; if ( ! ( hp->flags & STR_NULL_END ) ) return( NULL ) ; } else { last = strpbrk( start, hp->separator ) ; if ( last == NULL ) last = start + strlen( start ) ; } } /* * At this point, the following variables must be set: * start: beginning of component * last: end of component + 1 * * If STR_MALLOC is set, allocate space for the new string. * * NOTE: If STR_MALLOC is not set, the processed string is trashed. */ last_char = *last ; if ( hp->flags & STR_MALLOC ) { int len = last - start ; retval = malloc( (unsigned)len + 1 ) ; if ( retval == NULL ) { HANDLE_ERROR( hp->flags, NULL, hp->errnop, STR_ENOMEM, "STR str_component: malloc failed" ) ; } strncpy( retval, start, (size_t)len )[ len ] = '\0' ; } else { retval = start ; *last = '\0' ; } /* * Check if last_char is NUL to avoid setting hp->pos past the * end of the string */ hp->pos = ( last_char == '\0' ) ? last : last+1 ; return( retval ) ; } xinetd-2.3.15.4/src/str/strparse.h000066400000000000000000000007611333055217000166550ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef __STRPARSE_H #define __STRPARSE_H /* * $Id$ */ struct str_handle { char *string ; char *separator ; char *pos ; int flags ; int *errnop ; int no_more ; } ; #ifndef NULL #define NULL 0 #endif #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif #endif /* __STRPARSE_H */ xinetd-2.3.15.4/src/str/strprint.c000066400000000000000000000043761333055217000167000ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #ifndef NO_SIO #include "sio.h" #endif #include "str.h" #include "strparse.h" #define INT_NULL ((int *)0) /* * The strx_* functions will never over-run the buffer * The str_* functions may over-run the buffer */ /* * Group 1: the strx_* functions */ /* * This is the general purpose conversion function. It is invoked * by all the other str[x]_* functions */ void strx_printv( int *ccp, char *buf, int len, const char *format, va_list ap ) { #ifndef NO_SIO __sio_od_t od ; int cc ; /* * First initialize the descriptor * Notice that if no length is given, we initialize buf_end to the * highest possible address. */ od.buf = buf ; /* NOT NEEDED */ od.buf_end = len ? &buf[ len ] : (char *) ~0 ; /* NEEDED */ od.buffer_size = 0 ; /* NOT NEEDED */ od.start = buf ; /* NOT NEEDED */ od.nextb = buf ; /* NEEDED */ od.buftype = 0 ; /* NOT NEEDED */ /* * Do the conversion */ cc = __sio_converter( &od, -1, format, ap ) ; if ( len == 0 || od.nextb < od.buf_end ) *(od.nextb) = '\0' ; if ( ccp ) *ccp = cc ; #endif /* ! NO_SIO */ } void strx_print( int *ccp, char *buf, int len, const char *format, ... ) { va_list ap ; if (len <= 0) { if( ccp ) *ccp = 0; return; } va_start( ap, format ) ; strx_printv( ccp, buf, len, format, ap ) ; va_end( ap ) ; } char *strx_sprint( char *buf, int len, const char *format, ... ) { va_list ap ; if (len <= 0) { return buf; } va_start( ap, format ) ; strx_printv( INT_NULL, buf, len, format, ap ) ; va_end( ap ) ; return( buf ) ; } int strx_nprint( char *buf, int len, const char *format, ...) { int cc ; va_list ap ; if (len <= 0) { return 0; } va_start( ap, format ) ; strx_printv( &cc, buf, len, format, ap ) ; va_end( ap ) ; return( cc ) ; } int strx_nprintv( char *buf, int len, const char *format, va_list ap ) { int cc ; if (len <= 0) { return 0; } strx_printv( &cc, buf, len, format, ap ) ; return( cc ) ; } xinetd-2.3.15.4/src/str/strutil.c000066400000000000000000000042321333055217000165100ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #ifndef NULL #define NULL 0 #endif #define TRIVIAL_STR_FIND 1 #ifndef TRIVIAL_STR_FIND #define LOWER_CASE( c ) ( (c) + 'a' - 'A' ) /* * look for an instance of sstr in str * Returns a pointer to the beginning of sstr in str. * It ignores the case of the alphabetic characters */ char *str_casefind( register char *str, char *sstr ) { register int ssfc = *sstr++ ; /* sub-string first char */ if ( ssfc == 0 ) return( str ) ; if ( isalpha( ssfc ) && isupper( ssfc ) ) ssfc = LOWER_CASE( ssfc ) ; while ( *str ) { char *current = str ; register int strc = *str++ ; char *sp ; /* string pointer */ char *ssp ; /* sub-string pointer */ if ( isalpha( strc ) && isupper( strc ) ) strc = LOWER_CASE( strc ) ; if ( strc != ssfc ) continue ; for ( sp = str, ssp = sstr ;; sp++, ssp++ ) { register int sc = *sp ; /* string char */ register int ssc = *ssp ; /* substring char */ /* * End-of-substring means we got a match */ if ( ssc == 0 ) return( current ) ; /* * Convert to lower case if alphanumeric */ if ( isalpha( sc ) && isupper( sc ) ) sc = LOWER_CASE( sc ) ; if ( isalpha( ssc ) && isupper( ssc ) ) ssc = LOWER_CASE( ssc ) ; if ( sc != ssc ) break ; } } return( 0 ) ; } #else /* defined( TRIVIAL_STR_FIND ) */ /* * look for an instance of s2 in s1 * Returns a pointer to the beginning of s2 in s1. * It ignores the case of the alphabetic characters */ char *str_casefind( char *s1, const char *s2 ) { unsigned int i ; unsigned long l1 = strlen( s1 ) ; unsigned long l2 = strlen( s2 ) ; if ( l2 > l1 ) return( NULL ) ; for ( i = 0 ; i < l1 - l2 + 1 ; i++ ) if ( strncasecmp( &s1[ i ], s2, l2 ) == 0 ) return( (char *) &s1[ i ] ) ; return( NULL ) ; } #endif /* TRIVIAL_STR_FIND */ /* * Fill string s with character c */ void str_fill( char *s, char c ) { while ( *s ) *s++ = c ; } xinetd-2.3.15.4/src/tcpint.c000066400000000000000000000302331333055217000154730ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "tcpint.h" #include "intcommon.h" #include "msg.h" #include "log.h" #include "xconfig.h" #include "sconf.h" typedef enum { S_OK, S_SERVER_ERR, S_CLIENT_ERR } stream_status_e ; struct istream_private { unsigned accepted_connections ; } ; #define SIP( p ) ((struct istream_private *)(p)) static struct istream_private istream ; static struct intercept_s stream_intercept_state ; static void si_mux(void) ; static struct intercept_ops istream_ops = { si_mux, si_exit } ; struct intercept_s *si_init( struct server *serp ) { struct intercept_s *ip = &stream_intercept_state ; ip->int_socket_type = SOCK_STREAM ; ip->int_priv = (void *) &istream ; ip->int_ops = &istream_ops ; int_init( ip, serp ) ; if ( signal( SIGPIPE, SIG_IGN ) == SIG_ERR ) int_fail( ip, "signal" ) ; return( ip ) ; } #ifdef HAVE_POLL static status_e handle_io( psi_h iter, channel_s *chp, struct pollfd *pfd_handled, struct pollfd *pfd_array, int *pfds_last, stream_status_e (*iofunc)() ); #else static status_e handle_io( psi_h iter, channel_s *chp, fd_set *maskp, stream_status_e (*iofunc)() ); #endif static stream_status_e tcp_local_to_remote( channel_s *chp ); static stream_status_e tcp_remote_to_local( channel_s *chp ); static void connection_request( struct intercept_s *ip, channel_s **chpp ); /* Unfortunatly, this can't be private... */ void si_exit(void) { struct intercept_s *ip = &stream_intercept_state ; if ( SIP( ip->int_priv )->accepted_connections == 0 ) (void) accept( INT_REMOTE( ip ), SA( NULL ), NULL ) ; int_exit( ip ) ; } static void si_mux(void) { struct intercept_s *ip = &stream_intercept_state ; #ifdef HAVE_POLL struct pollfd *pfd_array; int pfds_last = 0; #else fd_set socket_mask ; int mask_max ; #endif psi_h iter ; const char *func = "si_mux" ; #ifdef HAVE_POLL pfd_array = calloc(sizeof(struct pollfd),MAX_FDS); pfd_array[ pfds_last ].fd = INT_REMOTE( ip ) ; pfd_array[ pfds_last++ ].events = POLLIN | POLLOUT; #else FD_ZERO( &socket_mask ) ; FD_SET( INT_REMOTE( ip ), &socket_mask ) ; mask_max = INT_REMOTE( ip ) ; #endif iter = psi_create( INT_CONNECTIONS( ip ) ) ; if ( iter == NULL ) { msg( LOG_ERR, func, ES_NOMEM ) ; #ifdef HAVE_POLL free( pfd_array ) ; #endif return ; } for ( ;; ) { channel_s *chp ; #ifndef HAVE_POLL fd_set read_mask ; #endif int n_ready ; #ifdef HAVE_POLL n_ready = int_poll( pfds_last, pfd_array ) ; #else read_mask = socket_mask ; n_ready = int_select( mask_max+1, &read_mask ) ; #endif if ( n_ready == -1 ) goto free_iter ; #ifdef HAVE_POLL if ( pfd_array[0].revents & ( POLLIN | POLLOUT ) ) #else if ( FD_ISSET( INT_REMOTE( ip ), &read_mask ) ) #endif { connection_request( ip, &chp ) ; if ( chp != NULL ) { #ifdef HAVE_POLL pfd_array[ pfds_last ].fd = chp->ch_local_socket ; pfd_array[ pfds_last++ ].events = POLLIN | POLLOUT ; pfd_array[ pfds_last ].fd = chp->ch_remote_socket ; pfd_array[ pfds_last++ ].events = POLLIN | POLLOUT ; #else FD_SET( chp->ch_local_socket, &socket_mask ) ; if ( chp->ch_local_socket > mask_max ) mask_max = chp->ch_local_socket ; FD_SET( chp->ch_remote_socket, &socket_mask ) ; if ( chp->ch_remote_socket > mask_max ) mask_max = chp->ch_remote_socket ; #endif } if ( --n_ready == 0 ) continue ; } for ( chp = CHP( psi_start(iter) ) ; chp ; chp = CHP( psi_next(iter) ) ) { #ifdef HAVE_POLL int i; struct pollfd *local_pfd = NULL, *remote_pfd = NULL; /* TODO: detection with O(n)=1 */ for (i = 0 ; i < pfds_last ; i++ ) if (pfd_array[i].fd == chp->ch_local_socket) local_pfd = &pfd_array[i]; else if (pfd_array[i] .fd== chp->ch_remote_socket) remote_pfd = &pfd_array[i]; if ( local_pfd != NULL && local_pfd->revents & ( POLLIN | POLLOUT) ) #else if ( FD_ISSET( chp->ch_local_socket, &read_mask ) ) #endif { #ifdef DEBUG_TCPINT if ( debug.on ) msg( LOG_DEBUG, func, "Input available on local socket %d", chp->ch_local_socket ) ; #endif #ifdef HAVE_POLL if ( handle_io( iter, chp, local_pfd, pfd_array, &pfds_last, tcp_local_to_remote ) == FAILED ) #else if ( handle_io( iter, chp, &socket_mask, tcp_local_to_remote ) == FAILED ) #endif goto free_iter ; if ( --n_ready == 0 ) goto free_iter ; } #ifdef HAVE_POLL if ( remote_pfd != NULL && remote_pfd->revents & ( POLLIN | POLLOUT) ) #else if ( FD_ISSET( chp->ch_remote_socket, &read_mask ) ) #endif { #ifdef DEBUG_TCPINT msg( LOG_DEBUG, func, "Input available on remote socket %d", chp->ch_remote_socket ) ; #endif #ifdef HAVE_POLL if ( handle_io( iter, chp, remote_pfd, pfd_array, &pfds_last, tcp_remote_to_local ) == FAILED ) #else if ( handle_io( iter, chp, &socket_mask, tcp_remote_to_local ) == FAILED ) #endif goto free_iter ; if ( --n_ready == 0 ) goto free_iter ; } } } free_iter: psi_destroy( iter ) ; return ; } #ifdef HAVE_POLL static status_e handle_io( psi_h iter, channel_s *chp, struct pollfd *pfd_handled, struct pollfd *pfd_array, int *pfds_last, stream_status_e (*iofunc)() ) #else static status_e handle_io( psi_h iter, channel_s *chp, fd_set *maskp, stream_status_e (*iofunc)() ) #endif { const char *func = "handle_io" ; switch ( (*iofunc)( chp ) ) { case S_SERVER_ERR: return( FAILED ) ; case S_CLIENT_ERR: if ( debug.on ) msg( LOG_DEBUG, func, "Closing channel to %s,%d using sockets %d(l),%d(r)", xaddrname( &chp->ch_from ), ntohs(xaddrport( &chp->ch_from )), chp->ch_local_socket, chp->ch_remote_socket ) ; #ifdef HAVE_POLL if ( pfd_handled != NULL) *pfd_handled = pfd_array[ --( *pfds_last ) ]; #else FD_CLR( chp->ch_local_socket, maskp ) ; FD_CLR( chp->ch_remote_socket, maskp ) ; #endif (void) Sclose( chp->ch_remote_socket ) ; (void) Sclose( chp->ch_local_socket ) ; psi_remove( iter ) ; FREE_CHANNEL( chp ) ; break ; case S_OK: break ; } return( OK ) ; } static void connection_request( struct intercept_s *ip, channel_s **chpp ) { union xsockaddr csin ; socklen_t sin_len = 0; channel_s *chp ; int sd ; bool_int addr_checked ; const char *func = "connection_request" ; *chpp = NULL ; if( SC_IPV4( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) ) sin_len = sizeof(struct sockaddr_in); if( SC_IPV6( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) ) sin_len = sizeof(struct sockaddr_in6); if ( ( sd = accept( INT_REMOTE( ip ), SA( &csin ), &sin_len ) ) == -1 ) return ; SIP( ip->int_priv )->accepted_connections++ ; if ( debug.on ) msg( LOG_DEBUG, func, "connection request from %s,%d", xaddrname( &csin ), ntohs( xaddrport( &csin ) ) ) ; chp = int_lookupconn( ip, &csin, &addr_checked ) ; if ( chp == NULL ) { struct server *serp = INT_SERVER( ip ) ; struct service *sp = SERVER_SERVICE( serp ) ; connection_s *cop = SERVER_CONNECTION( serp ) ; CONN_SETADDR( cop, &csin ) ; if ( INTERCEPT( ip ) ) { mask_t check_mask ; access_e result ; M_OR( check_mask, XMASK( CF_ADDRESS ), XMASK( CF_TIME ) ) ; result = access_control( sp, cop, &check_mask ) ; if ( result != AC_OK ) { svc_log_failure( sp, cop, result ) ; (void) Sclose( sd ) ; return ; } } if ( ( chp = int_newconn( ip, &csin, sd ) ) == NULL ) { (void) Sclose( sd ) ; return ; } if ( ! addr_checked ) svc_log_success( sp, cop, SERVER_PID( serp ) ) ; #if defined( TCP_NODELAY ) { int on = 1 ; (void) setsockopt( chp->ch_local_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof( on ) ) ; (void) setsockopt( chp->ch_remote_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof( on ) ) ; } #endif /* TCP_NODELAY */ *chpp = chp ; } else msg( LOG_ERR, func, "Received another connection request from %s,%d", xaddrname( &csin ), ntohs( xaddrport( &csin ) ) ) ; } static stream_status_e tcp_local_to_remote( channel_s *chp ) { char buf[ DATAGRAM_SIZE ] ; ssize_t rcc, wcc ; char *p ; int left ; const char *func = "tcp_local_to_remote" ; for ( ;; ) { rcc = recv( chp->ch_local_socket, buf, sizeof( buf ), 0 ) ; if ( rcc == 0 ) return( S_SERVER_ERR ) ; else if ( rcc == (ssize_t)-1 ) { if ( errno != EINTR ) { msg( LOG_ERR, func, "recv: %m" ) ; return( S_SERVER_ERR ) ; } } else break ; } for ( p = buf, left = rcc ; left ; p += wcc, left -= wcc ) { wcc = send( chp->ch_remote_socket, p, left, 0 ) ; if ( wcc == 0 ) return( S_CLIENT_ERR ) ; else if ( wcc == (ssize_t)-1 ) { if ( errno == EINTR ) wcc = 0 ; else { msg( LOG_ERR, func, "send: %m" ) ; return( S_CLIENT_ERR ) ; } } } #ifdef DEBUG_TCPINT if ( debug.on ) msg( LOG_DEBUG, func, "Transferred %d bytes from local socket %d to remote socket %d", rcc, chp->ch_local_socket, chp->ch_remote_socket ) ; #endif return( S_OK ) ; } static stream_status_e tcp_remote_to_local( channel_s *chp ) { char buf[ DATAGRAM_SIZE ] ; ssize_t rcc, wcc ; int left ; char *p ; const char *func = "tcp_remote_to_local" ; for ( ;; ) { rcc = recv( chp->ch_remote_socket, buf, sizeof( buf ), 0 ) ; if ( rcc == 0 ) return( S_CLIENT_ERR ) ; else if ( rcc == (ssize_t)-1 ) { if ( errno != EINTR ) { msg( LOG_ERR, func, "recv: %m" ) ; return( S_CLIENT_ERR ) ; } } else break ; } for ( p = buf, left = rcc ; left ; p += wcc, left -= wcc ) { wcc = send( chp->ch_local_socket, p, left, 0 ) ; if ( wcc == 0 ) { return( S_SERVER_ERR ) ; } else if ( wcc == (ssize_t)-1 ) { if ( errno == EINTR ) { #ifdef DEBUG_TCPINT rcc = 0 ; #endif } else { msg( LOG_ERR, func, "send: %m" ) ; return( S_SERVER_ERR ) ; } } } #ifdef DEBUG_TCPINT if ( debug.on ) msg( LOG_DEBUG, func, "Transferred %d bytes from remote socket %d to local socket %d", rcc, chp->ch_remote_socket, chp->ch_local_socket ) ; #endif return( S_OK ) ; } xinetd-2.3.15.4/src/tcpint.h000066400000000000000000000003071333055217000154770ustar00rootroot00000000000000#ifndef TCPINT_H #define TCPINT_H #include "defs.h" #include "int.h" #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void si_exit(void); struct intercept_s *si_init(struct server *serp); #endif xinetd-2.3.15.4/src/time.c000066400000000000000000000074211333055217000151330ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include "sio.h" #include "timex.h" #include "msg.h" #include "util.h" #define IN_RANGE( val, low, high ) ( (low) <= (val) && (val) <= (high) ) struct time_interval { int16_t min_start ; int16_t min_end ; } ; #define TIP( p ) ( (struct time_interval *) (p) ) #define NEW_TI() NEW( struct time_interval ) #define FREE_TI( tip ) FREE( tip ) /* * Returns TRUE if the current time is within at least one of the intervals */ bool_int ti_current_time_check( const pset_h intervals ) { time_t current_time ; unsigned u ; int16_t min_current ; struct tm *tmp ; (void) time( ¤t_time ) ; tmp = localtime( ¤t_time ) ; min_current = tmp->tm_hour * 60 + tmp->tm_min ; for ( u = 0 ; u < pset_count( intervals ) ; u++ ) { struct time_interval *tip ; tip = TIP( pset_pointer( intervals, u ) ) ; if ( IN_RANGE( min_current, tip->min_start, tip->min_end ) ) return( TRUE ) ; } return( FALSE ) ; } static int get_num( int *nump, int min_val, int max_val, const char *s, char stop_char ) { const char *func = "get_num" ; int i = 0; for ( *nump = 0 ; isdigit( s[i] ) ; i++ ) { *nump *= 10 ; *nump += s[i] - '0' ; } if ( s[i] != stop_char ) { parsemsg( LOG_ERR, func, "incorrect time interval" ) ; return( -1 ); } if ( ! IN_RANGE( *nump, min_val, max_val ) ) { parsemsg( LOG_ERR, func, "invalid time interval" ) ; return( -1 ) ; } return( i ) ; } /* * Each interval should have the form: * hour:min-hour:min * Example: 2:30-4:15 */ status_e ti_add( pset_h iset, const char *interval_str ) { struct time_interval *tip ; int hours ; int minutes ; int min_start ; int min_end ; int p, r = 0 ; const char *func = "add_interval" ; while (interval_str[r] == ' ') r++; /* Eat white space */ if ( ( p = get_num( &hours, 0, 23, interval_str+r, ':' ) ) == -1 ) return( FAILED ) ; r += p; r++; /* Get past : */ if ( ( p = get_num( &minutes, 0, 59, interval_str+r, '-' ) ) == -1 ) return( FAILED ) ; min_start = hours * 60 + minutes ; r += p; r++; /* Get past - */ if ( ( p = get_num( &hours, 0, 23, interval_str+r, ':' ) ) == -1 ) return( FAILED ) ; r += p; r++; /* Get past : */ if ( get_num( &minutes, 0, 59, interval_str+r, NUL ) == -1 ) return( FAILED ) ; min_end = hours * 60 + minutes ; if ( min_start >= min_end ) { parsemsg( LOG_ERR, func, "invalid time interval: %s", interval_str ) ; return( FAILED ) ; } tip = NEW_TI() ; if ( tip == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } tip->min_start = min_start ; tip->min_end = min_end ; if ( pset_add( iset, tip ) == NULL ) { FREE_TI( tip ) ; out_of_memory( func ) ; return( FAILED ) ; } return( OK ) ; } void ti_dump( pset_h iset, int fd ) { unsigned u ; for ( u = 0 ; u < pset_count( iset ) ; u++ ) { struct time_interval *tip = TIP( pset_pointer( iset, u ) ) ; Sprint( fd, " %02d:%02d-%02d:%02d", tip->min_start / 60, tip->min_start % 60, tip->min_end / 60, tip->min_end % 60 ) ; } } void ti_free( pset_h iset ) { pset_apply( iset, free, NULL ) ; } xinetd-2.3.15.4/src/timex.h000066400000000000000000000006251333055217000153270ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef _X_TIME #define _X_TIME #include "pset.h" #include "defs.h" bool_int ti_current_time_check(const pset_h intervals); status_e ti_add(pset_h iset,const char *interval_str); void ti_dump(pset_h iset,int fd); void ti_free(pset_h iset); #endif xinetd-2.3.15.4/src/udpint.c000066400000000000000000000220301333055217000154710ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL #include #endif #include "udpint.h" #include "intcommon.h" #include "util.h" #include "connection.h" #include "access.h" #include "log.h" #include "msg.h" #include "sconf.h" /* * Datagrams greater than this will be truncated */ #define MAX_DATAGRAM_SIZE ( 32 * 1024 ) struct packet { union xsockaddr from ; char *data ; int size ; } ; typedef struct packet packet_s ; struct idgram_private { unsigned received_packets ; } ; #define IDP( p ) ((struct idgram_private *)(p)) static struct idgram_private idgram ; static void di_mux(void) ; static void udp_remote_to_local( struct intercept_s *ip, channel_s **chpp ); static status_e udp_local_to_remote( channel_s *chp ); static void send_data( int sd, char *buf, int len, union xsockaddr *addr ); static status_e get_incoming_packet( struct intercept_s *ip, packet_s *pp ); static const struct intercept_ops idgram_ops = { di_mux, di_exit } ; static struct intercept_s dgram_intercept_state ; struct intercept_s *di_init( struct server *serp ) { struct intercept_s *ip = &dgram_intercept_state ; ip->int_socket_type = SOCK_DGRAM ; ip->int_priv = (void *) &idgram ; ip->int_ops = &idgram_ops ; int_init( ip, serp ) ; return( ip ) ; } void di_exit(void) { struct intercept_s *ip = &dgram_intercept_state ; if ( IDP( ip->int_priv )->received_packets == 0 ) drain( INT_REMOTE( ip ) ) ; int_exit( ip ) ; } /* * Returns only if there is an I/O error while communicating with the server */ static void di_mux(void) { struct intercept_s *ip = &dgram_intercept_state ; #ifdef HAVE_POLL struct pollfd *pfd_array; int pfds_last = 0; #else fd_set socket_mask ; int mask_max ; #endif #ifdef HAVE_POLL pfd_array = (struct pollfd *)calloc(sizeof(struct pollfd),MAX_FDS); pfd_array[ pfds_last ].fd = INT_REMOTE( ip ); pfd_array[ pfds_last++ ].events = POLLIN | POLLOUT; #else FD_ZERO( &socket_mask ) ; FD_SET( INT_REMOTE( ip ), &socket_mask ) ; mask_max = INT_REMOTE( ip ) ; #endif for ( ;; ) { unsigned u ; channel_s *chp ; #ifndef HAVE_POLL fd_set read_mask ; #endif int n_ready ; #ifdef HAVE_POLL n_ready = int_poll( pfds_last, pfd_array ) ; #else read_mask = socket_mask ; n_ready = int_select( mask_max+1, &read_mask ) ; #endif if ( n_ready == -1 ) return ; #ifdef HAVE_POLL if ( pfd_array[0].revents & ( POLLIN | POLLOUT ) ) #else if ( FD_ISSET( INT_REMOTE( ip ), &read_mask ) ) #endif { udp_remote_to_local( ip, &chp ) ; if ( chp != NULL ) { #ifdef HAVE_POLL pfd_array[ pfds_last ].fd = chp->ch_local_socket ; pfd_array[ pfds_last++ ].events = POLLIN | POLLOUT ; #else FD_SET( chp->ch_local_socket, &socket_mask ) ; if ( chp->ch_local_socket > mask_max ) mask_max = chp->ch_local_socket ; #endif } if ( --n_ready == 0 ) continue ; } for ( u = 0 ; u < pset_count( INT_CONNECTIONS( ip ) ) ; u++ ) { chp = CHP( pset_pointer( INT_CONNECTIONS( ip ), u ) ) ; #ifdef HAVE_POLL int i; /* TODO: detection with O(n)=1 */ for (i = 0 ; i < pfds_last ; i++) if (pfd_array[i].fd == chp->ch_local_socket) break; if (pfd_array[i].fd == chp->ch_local_socket && (pfd_array[i].revents & ( POLLIN | POLLOUT ))) #else if ( FD_ISSET( chp->ch_local_socket, &read_mask ) ) #endif { if ( udp_local_to_remote( chp ) == FAILED ) return ; if ( --n_ready == 0 ) break ; } } } } /* * Read data from the remote socket and send it to the appropriate local * socket. * If this is a new connection, insert it in the connection table and * place its handle in *chpp. */ static void udp_remote_to_local( struct intercept_s *ip, channel_s **chpp ) { char buf[ MAX_DATAGRAM_SIZE ] ; packet_s packet ; channel_s *chp ; bool_int addr_checked ; *chpp = CHANNEL_NULL ; packet.data = buf ; packet.size = sizeof( buf ) ; if ( get_incoming_packet( ip, &packet ) == FAILED ) return ; chp = int_lookupconn( ip, &packet.from, &addr_checked ) ; if ( chp == CHANNEL_NULL ) { struct server *serp = INT_SERVER( ip ) ; struct service *sp = SERVER_SERVICE( serp ) ; connection_s *cop = SERVER_CONNECTION( serp ) ; if ( ( chp = int_newconn( ip, &packet.from, INT_REMOTE( ip ) ) ) == NULL ) return ; CONN_SETADDR( cop, &packet.from ) ; /* for logging */ if ( INTERCEPT( ip ) ) { mask_t check_mask ; access_e result ; M_OR( check_mask, XMASK( CF_ADDRESS ), XMASK( CF_TIME ) ) ; result = access_control( sp, cop, &check_mask ) ; if ( result != AC_OK ) { svc_log_failure( sp, cop, result ) ; chp->ch_state = BAD_CHANNEL ; return ; } } /* * Since we don't distinguish ports, there is no point to log * another successful attempt from the same address */ if ( ! addr_checked ) svc_log_success( sp, cop, SERVER_PID( serp ) ) ; *chpp = chp ; } else if ( chp->ch_state == BAD_CHANNEL ) return ; #ifdef DEBUG_UDPINT if ( debug.on ) msg( LOG_DEBUG, "udp_remote_to_local", "sending %d bytes to server on port %d", packet.size, ntohs( INT_LOCALADDR( ip )->sa_in.sin_port ) ) ; #endif send_data( chp->ch_local_socket, packet.data, packet.size, NULL ) ; } /* * Send the data in buf to destination addr using the socket sd. * If addr is NULL, use the default socket destination */ static void send_data( int sd, char *buf, int len, union xsockaddr *addr ) { char *p ; int left ; ssize_t cc ; const char *func = "send_data" ; for ( p = buf, left = len ; left > 0 ; left -= cc, p+= cc ) { if ( addr == NULL ) cc = send( sd, p, left, 0 ) ; else cc = sendto( sd, p, left, 0, SA( addr ), sizeof( *addr ) ) ; if ( cc == (ssize_t)-1 ) { if ( errno == EINTR ) { cc = 0 ; continue ; } else { msg( LOG_ERR, func, "%s: %m", addr ? "sendto" : "send" ) ; return ; } } } } static status_e get_incoming_packet( struct intercept_s *ip, packet_s *pp ) { socklen_t from_len = 0; const char *func = "get_incoming_packet" ; if( SC_IPV4( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) ) from_len = sizeof( struct sockaddr_in ); if( SC_IPV6( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) ) from_len = sizeof( struct sockaddr_in6 ); for ( ;; ) { ssize_t cc ; from_len = sizeof( pp->from ) ; cc = recvfrom( INT_REMOTE( ip ), pp->data, pp->size, 0, SA( &pp->from ), &from_len ) ; if ( cc == (ssize_t)-1 ) { if ( errno != EINTR ) { msg( LOG_ERR, func, "recvfrom error: %m" ) ; return( FAILED ) ; } } else if ( cc == 0 ) return( FAILED ) ; else { pp->size = cc ; IDP( ip->int_priv )->received_packets++ ; break ; } } if ( from_len == 0 ) { msg( LOG_ERR, func, "incoming packet had 0 length address" ) ; return( FAILED ) ; } #ifdef DEBUG_UDPINT if ( debug.on ) msg( LOG_DEBUG, func, "Received %d bytes from address: %s,%d", pp->size, xaddrname( &pp->from ), ntohs( xaddrport(&pp->from) ) ); #endif return( OK ) ; } static status_e udp_local_to_remote( channel_s *chp ) { char buf[ MAX_DATAGRAM_SIZE ] ; ssize_t cc ; const char *func = "udp_local_to_remote" ; for ( ;; ) { cc = recv( chp->ch_local_socket, buf, sizeof( buf ), 0 ) ; if ( cc == (ssize_t)-1 ) { if ( errno != EINTR ) { msg( LOG_ERR, func, "recv from daemon: %m" ) ; return( FAILED ) ; } } else if ( cc == 0 ) return( FAILED ) ; else break ; } #ifdef DEBUG_UDPINT if ( debug.on ) msg( LOG_DEBUG, func, "sending %d bytes to address %s,%d", cc, xaddrname( &chp->ch_from ), ntohs( xaddrport(&chp->ch_from) ) ) ; #endif send_data( chp->ch_remote_socket, buf, cc, &chp->ch_from ) ; return( OK ) ; } xinetd-2.3.15.4/src/udpint.h000066400000000000000000000003071333055217000155010ustar00rootroot00000000000000#ifndef UDPINT_H #define UDPINT_H #include "defs.h" #include "int.h" #ifdef __GNUC__ __attribute__ ((noreturn)) #endif void di_exit(void); struct intercept_s *di_init(struct server *serp); #endif xinetd-2.3.15.4/src/util.c000066400000000000000000000155101333055217000151500ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include /* * The following ifdef is for TIOCNOTTY */ #ifndef NO_TERMIOS #include #include #else #include #endif #include #include #include #include #include #include #include "sio.h" #include "str.h" #include "util.h" #include "msg.h" void out_of_memory( const char *func ) { msg( LOG_CRIT, func, ES_NOMEM ) ; } const struct name_value *nv_find_value( const struct name_value nv_array[], const char *name ) { const struct name_value *nvp ; for ( nvp = nv_array ; nvp->name ; nvp++ ) { if ( EQ( name, nvp->name ) ) return( nvp ) ; } return( NULL ) ; } const struct name_value *nv_find_name( const struct name_value nv_array[], int value ) { const struct name_value *nvp ; for ( nvp = nv_array ; nvp->name ; nvp++ ) { if ( value == nvp->value ) return( nvp ) ; } return( NULL ) ; } /* * A name-value list is exactly what its name says. * The functions nv_get_name() and nv_get_value() return a pointer to * the entry with the specified value or name respectively. * * The list ends when an antry with a NULL name is encountered. * The value field of that entry is treated in a special manner: if it * is non-zero, it is assumed that there exists one more entry whose * name field will be returned by the nv_get_name function if it can't * find an entry whose value field is equal to its 2nd parameter. * If the value field of the NULL entry is 0, then nv_get_name() will * return NULL. */ const char *nv_get_name( const struct name_value nv_array[], int value ) { const struct name_value *nvp ; for ( nvp = nv_array ; nvp->name ; nvp++ ) { if ( value == nvp->value ) return( nvp->name ) ; } return( nvp->value ? (nvp+1)->name : NULL ) ; } char **argv_alloc( unsigned count ) { unsigned argv_size = (count + 1) * sizeof( char *) ; char **argv ; const char *func = "new_argv" ; argv = (char **) malloc( argv_size ) ; if ( argv == NULL ) { out_of_memory( func ) ; return( NULL ) ; } (void) memset( (char *)argv, 0, argv_size ) ; return( argv ) ; } /* * If size is 0, the pset holds strings */ status_e copy_pset( const pset_h from, pset_h *to, unsigned size ) { unsigned u ; const char *func = "copy_pset" ; if ( *to == NULL ) { *to = pset_create( pset_count( from ), 0 ) ; if ( *to == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } } for ( u = 0 ; u < pset_count( from ) ; u++ ) { char *p = (char *) pset_pointer( from, u ) ; char *new_s ; if ( size == 0 ) new_s = new_string( p ) ; else new_s = (char *)malloc( size ) ; if ( new_s == NULL ) { out_of_memory( func ) ; return( FAILED ) ; } if ( size != 0 ) (void) memcpy( new_s, p, size ) ; if ( pset_add( *to, new_s ) == NULL ) { free( new_s ) ; out_of_memory( func ) ; return( FAILED ) ; } } return( OK ) ; } /* * Disassociate from controlling terminal */ void no_control_tty(void) { (void) setsid() ; } /* * Write the whole buffer to the given file descriptor ignoring interrupts */ status_e write_buf( int fd, const char *buf, int len ) { int i ; ssize_t cc; for ( i = 0 ; len > 0 ; i += cc, len -= cc ) { cc = write( fd, buf+i, len ) ; if ( cc == -1 ) { if ( errno != EINTR ) return( FAILED ) ; cc = 0 ; } } return( OK ) ; } void tabprint( int fd, int tab_level, const char *fmt, ...) { va_list ap ; int i ; for ( i = 0 ; i < tab_level ; i++ ) Sputchar( fd, '\t' ) ; va_start( ap, fmt ) ; Sprintv( fd, fmt, ap ) ; va_end( ap ) ; } /* * Empty the socket receive buffers of all data. */ void drain( int sd ) { char buf[ 256 ] ; /* This size is arbitrarily chosen */ ssize_t ret ; int old_val ; /* Put in non-blocking mode so we don't hang. */ old_val = fcntl( sd, F_GETFL, FNDELAY ); if ( fcntl( sd, F_SETFL, FNDELAY ) < 0 ) { if ( debug.on ) msg( LOG_DEBUG, "drain", "UDP socket could not be made non-blocking: %m" ) ; return; } do { ret = recv( sd, buf, sizeof( buf ), 0 ) ; } while (ret > 0); /* Restore the value since the connection will be freed, not closed. */ if (old_val >= 0) fcntl( sd, F_SETFL, old_val ); if ( debug.on ) msg( LOG_DEBUG, "drain", "UDP socket should be empty" ) ; } /* * Convert string to an int detecting errors. */ int parse_int(const char *str, int base, int term, int *res) { char *endptr; long strtol_res; /* SUSv2 says: * "Because 0, LONG_MIN and LONG_MAX are returned on error and are also * valid returns on success, an application wishing to check for error * situations should set errno to 0, then call strtol(), then check errno." */ errno = 0; strtol_res = strtol(str, (char **)&endptr, base); if (errno == 0 && *str != NUL) { /* Special case: -1 means allow trailing whitespace */ if (term == -1) { while (*endptr != NUL && isspace(*endptr)) endptr++; term = NUL; } if (*endptr == term) { *res = strtol_res; return 0; } } *res = 0; return -1; } int parse_uint(const char *str, int base, int term, unsigned int *res) { unsigned long long tmp; int ret; ret = parse_ull(str, base, term, &tmp); *res = (unsigned int)tmp; return ret; } int parse_ull(const char *str, int base, int term, unsigned long long *res) { char *endptr; unsigned long long strtol_res; /* SUSv2 says: * "Because 0, LONG_MIN and LONG_MAX are returned on error and are also * valid returns on success, an application wishing to check for error * situations should set errno to 0, then call strtol(), then check errno." */ errno = 0; strtol_res = strtoull(str, (char **)&endptr, base); if (errno == 0 && *str != NUL) { /* Special case: -1 means allow trailing whitespace */ if (term == -1) { while (*endptr != NUL && isspace(*endptr)) endptr++; term = NUL; } if (*endptr == term) { *res = strtol_res; return 0; } } *res = 0; return -1; } int parse_ubase10(const char *str, unsigned int *res) { return parse_uint(str, 10, -1, res); } int parse_base10(const char *str, int *res) { return parse_int(str, 10, -1, res); } bool_int parse_all_digits(const char *ptr) { size_t num=0, len = strlen(ptr); while (isdigit(*ptr++)) num++; if (num == len) return TRUE; else return FALSE; } xinetd-2.3.15.4/src/util.h000066400000000000000000000017521333055217000151600ustar00rootroot00000000000000#ifndef UTIL_H #define UTIL_H #include "pset.h" #include "defs.h" void out_of_memory(const char *func); const struct name_value *nv_find_value(const struct name_value nv_array[],const char *name); const struct name_value *nv_find_name(const struct name_value nv_array[],int value); const char *nv_get_name(const struct name_value nv_array[],int value); char **argv_alloc(unsigned count); status_e copy_pset(const pset_h from,pset_h *to,unsigned size); void no_control_tty(void); status_e write_buf(int fd,const char *buf,int len); void tabprint(int fd, int tab_level, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))); #else ; #endif void drain(int sd); int parse_int(const char *, int , int , int *); int parse_uint(const char *, int , int , unsigned int *); int parse_ull(const char *, int , int , unsigned long long *); int parse_base10(const char *, int *); int parse_ubase10(const char *, unsigned int *); bool_int parse_all_digits(const char *ptr); #endif xinetd-2.3.15.4/src/xconfig.h000066400000000000000000000116201333055217000156330ustar00rootroot00000000000000/* * (c) Copyright 1992 by Panagiotis Tsirigotis * (c) Sections Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef XCONFIG_H #define XCONFIG_H /* * $Id$ */ /* * Deal with stupid Compaq (DEC) Unix problem ... */ #ifdef __osf__ #undef DUMP_FILE #endif /* * The purpose of customconf.h is to allow the override of * constants defined in this file. These include all constants that * cannot be overriden from the command line via some option. */ #ifdef CUSTOMCONF #include "customconf.h" #endif #define DEFAULT_CONFIG_FILE "/etc/xinetd.conf" /* * This is the facility used by xinetd to log syslog messages */ #define DEFAULT_SYSLOG_FACILITY LOG_DAEMON /* * This is the level used for log messages when a service logs to syslog */ #define DEFAULT_SERVICE_SYSLOG_LEVEL LOG_INFO /* * Max number of concurrently running processes forked to get * the user id from the remote host */ #define DEFAULT_LOGPROCS 15 /* * The loop rate is a bound on the rate of forking servers for a * particular service. If that rate is exceeded, the service is deactivated. */ #define DEFAULT_LOOP_RATE 50 /* * The number of seconds to wait before re-enabling a looping service. */ #define DEFAULT_LOOP_TIME 10 /* * Signal-to-action mapping */ #ifndef RECONFIG_HARD_SIG #define RECONFIG_HARD_SIG SIGHUP #endif #ifndef OLD_RECONFIG_HARD_SIG #define OLD_RECONFIG_HARD_SIG SIGUSR2 #endif #ifndef TERMINATION_SIG #define TERMINATION_SIG SIGTERM #endif #ifndef STATE_DUMP_SIG #define STATE_DUMP_SIG SIGUSR1 #endif #ifndef CONSISTENCY_CHECK_SIG #define CONSISTENCY_CHECK_SIG SIGIOT #endif #ifndef SERVER_EXIT_SIG #define SERVER_EXIT_SIG SIGCHLD #endif #ifndef QUIT_SIG #define QUIT_SIG SIGQUIT #endif /* * This is the file where the internal state of xinetd is dumped upon * receipt of STATE_DUMP_SIG */ #ifndef DUMP_FILE #define DUMP_FILE "/var/run/xinetd.dump" #endif /* * There are 2 timeouts (in seconds) when trying to get the user id from * the remote host. Any timeout value specified as 0 implies an infinite * timeout. * LOGUSER_SUCCESS_TIMEOUT is the timeout when access control has been passed * and a new process has been forked to exec the server. * LOGUSER_FAILURE_TIMEOUT is the timeout when the attempt to access the * service has been rejected. * * Both timeouts should be at least 30 seconds as suggested in RFC 1413 * (assuming they are not 'infinite' timeouts). */ #ifndef LOGUSER_SUCCESS_TIMEOUT #define LOGUSER_SUCCESS_TIMEOUT 30 #endif #ifndef LOGUSER_FAILURE_TIMEOUT #define LOGUSER_FAILURE_TIMEOUT 30 #endif /* * This is used when an instance limit is not specified for a service * and the defaults entry does not specify an instance limit either. * It can be a positive number or "UNLIMITED". */ #ifndef DEFAULT_INSTANCE_LIMIT #define DEFAULT_INSTANCE_LIMIT UNLIMITED #endif /* * This is the interval (in seconds) over which we check if the service * loop rate has been exceeded. */ #ifndef LOOP_INTERVAL #define LOOP_INTERVAL 2 #endif /* * LOG_OPEN_FLAGS are the flags used to open a log file (this is used as * the 3rd argument of open(2)) */ #ifndef LOG_OPEN_FLAGS #define LOG_OPEN_FLAGS O_CREAT+O_APPEND+O_WRONLY #endif /* * Number of consecutive fork failures that we are willing to put up with * before giving up. */ #ifndef MAX_FORK_FAILURES #define MAX_FORK_FAILURES 5 #endif /* * This is the time period during which we will not log subsequest attempts * to access a datagram-based service from the same bad address after logging * the first one. * For example, if we receive a datagram at time t from address A which * has a match in the no_access list, we will log the failed attempt and * during the interval (t, t+DGRAM_IGNORE_TIME) we will not log attempts * from address A (for the same service). * In this context, the address is defined as (IP address, port number). */ #ifndef DGRAM_IGNORE_TIME #define DGRAM_IGNORE_TIME 60 /* seconds */ #endif #ifndef DUMP_FILE_MODE #define DUMP_FILE_MODE 0644 #endif #ifndef LOG_FILE_MODE #define LOG_FILE_MODE 0644 #endif /* * The DATAGRAM_SIZE should be big enough for an ethernet packet */ #ifndef DATAGRAM_SIZE #define DATAGRAM_SIZE 2048 #endif /* * Time interval between retry attempts */ #ifndef RETRY_INTERVAL #define RETRY_INTERVAL 5 /* seconds */ #endif /* * LOG_EXTRA_MIN, LOG_EXTRA_MAX define the limits by which the hard limit * on the log size can exceed the soft limit */ #ifndef LOG_EXTRA_MIN #define LOG_EXTRA_MIN ( 5 * 1024 ) #endif #ifndef LOG_EXTRA_MAX #define LOG_EXTRA_MAX ( 20 * 1024 ) #endif /* * If SENSORS are used and someone trips it, they are added to the * global_no_access table for whatever the configured time is. This * define determines the size of the table to use. */ #define MAX_GLOBAL_NO_ACCESS 10240 #endif /* CONFIG_H */ xinetd-2.3.15.4/src/xconv.pl000077500000000000000000000060751333055217000155320ustar00rootroot00000000000000#!/usr/bin/perl #/* # * (c) Copyright 1998-2001 by Rob Braun # * All rights reserved. The file named COPYRIGHT specifies the terms # * and conditions for redistribution. # */ # $RCSid = "$Id$"; sub print_header; sub print_defaults; print_header; print_defaults; while( ) { chomp; # Remove comment lines if( grep /^#/, $_ ) { next; } @command = split ; if( !defined $command[0] ) { next; } if( grep /rpc/, $command[2] ) { print STDERR "Warning: Service $command[0] not added because\n"; print STDERR "xinetd does not handle rpc services well\n"; next; } print "service $command[0]\n"; print "{\n"; print "\tflags = NAMEINARGS\n"; print "\tsocket_type = $command[1]\n"; print "\tprotocol = $command[2]\n"; if( grep /no/, $command[3] ) { print "\twait = no\n"; } else { print "\twait = yes\n"; } @user = split /[:\.]/, $command[4]; print "\tuser = $user[0]\n"; if( defined $user[1] ) { print "\tgroup = $user[1]\n"; } if( grep /internal/, $command[5] ) { print "\ttype = INTERNAL\n"; print "\tid = $command[0]-$command[1]\n"; } else { print "\tserver = $command[5]\n"; print "\tserver_args = "; $i = 6; while( defined $command[$i] ) { print "$command[$i] "; $i++; } print "\n"; } print "}\n"; print "\n"; } sub print_defaults { print "# The defaults section sets some information for all services\n"; print "defaults\n"; print "{\n"; print "\t#The maximum number of requests a particular service may handle\n"; print "\t# at once.\n"; print "\tinstances = 25\n"; print "\n"; print "\t# The type of logging. This logs to a file that is specified.\n"; print "\t# Another option is: SYSLOG syslog_facility [syslog_level]\n"; print "\tlog_type = FILE /var/log/servicelog\n"; print "\n"; print "\t# What to log when the connection succeeds.\n"; print "\t# PID logs the pid of the server processing the request.\n"; print "\t# HOST logs the remote host's ip address.\n"; print "\t# USERID logs the remote user (using RFC 1413)\n"; print "\t# EXIT logs the exit status of the server.\n"; print "\t# DURATION logs the duration of the session.\n"; print "\tlog_on_success = HOST PID\n"; print "\n"; print "\t# What to log when the connection fails. Same options as above\n"; print "\tlog_on_failure = HOST\n"; print "\n"; print "\t# The maximum number of connections a specific IP address can\n"; print "\t# have to a specific service. \n"; print "\tper_source = 5\n"; print "}\n"; print "\n"; } sub print_header { print "# This file generated by xconv.pl, included with the xinetd\n"; print "# package. xconv.pl was written by Rob Braun (bbraun\@synack.net)\n"; print "#\n"; print "# The file is merely a translation of your inetd.conf file into\n"; print "# the equivalent in xinetd.conf syntax. xinetd has many \n"; print "# features that may not be taken advantage of with this translation.\n"; print "# Please refer to the xinetd.conf man page for more information \n"; print "# on how to properly configure xinetd.\n"; print "\n"; print "\n"; } xinetd-2.3.15.4/src/xgetloadavg.c000066400000000000000000000044311333055217000165000ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* This file contains OS dependant implementations of xgetloadavg(). * xgetloadavg takes no arguments and simply returns the 1 minute * load average of the machine as a double. * Feel free to add implementations here, please update configure.in * to define HAVE_LOADAVG. Defining this macro enables the option * in the rest of the code. * --Rob */ #include "config.h" #ifdef HAVE_LOADAVG #ifdef linux #include #include "xgetloadavg.h" #define LFILE "/proc/loadavg" #define MAX 32 double xgetloadavg(void) { FILE *fd; double ret = 0; fd = fopen(LFILE, "r"); if( fd == NULL ) { return -1; } if( fscanf(fd, "%lf", &ret) != 1 ) { perror("fscanf"); ret = -1; } fclose(fd); return ret; } #endif /* linux */ #ifdef solaris #ifdef HAVE_KSTAT_H #include double xgetloadavg(void) { kstat_ctl_t *kc = NULL; kstat_t *ks = NULL; kstat_named_t *kn = NULL; kc = kstat_open(); if( kc == NULL ) { return -1; } ks = kstat_lookup(kc, "unix", 0, "system_misc"); if( ks == NULL ) { return -1; } if( kstat_read(kc, ks, 0) == -1 ) { return -1; } kn = kstat_data_lookup(ks, "avenrun_1min"); if( kn == NULL ) { return -1; } if( ks->ks_type == KSTAT_TYPE_NAMED ) { kn = ks->ks_data; if( kn == NULL ) { return -1; } return (double)(kn->value.ui32)/100; } kstat_close(kc); } #endif /* HAVE_KSTAT */ #endif /* solaris */ #if defined(bsdi) || defined(__APPLE__) #include double xgetloadavg(void) { double loadavg[3]; if (getloadavg(loadavg, 1) == -1) { return -1; } else { return loadavg[0]; } } #endif /* bsdi || __APPLE__ */ #ifdef __osf__ #include double xgetloadavg(void) { struct tbl_loadavg labuf; if (table(TBL_LOADAVG, 0, &labuf, 1, sizeof(labuf)) < 0) { perror("TBL_LOADAVG"); return (-1); } if (labuf.tl_lscale) { return ((double)labuf.tl_avenrun.l[2] / (double)labuf.tl_lscale); } return (labuf.tl_avenrun.d[2]); } #endif /* __osf__ */ #endif /* HAVE_LOADAVG */ xinetd-2.3.15.4/src/xgetloadavg.h000066400000000000000000000001211333055217000164750ustar00rootroot00000000000000#ifndef XGETLOADAVG_H #define XGETLOADAVG_H double xgetloadavg(void); #endif xinetd-2.3.15.4/src/xlog/000077500000000000000000000000001333055217000147765ustar00rootroot00000000000000xinetd-2.3.15.4/src/xlog/filelog.c000066400000000000000000000147111333055217000165670ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include #include #include #include #include "config.h" #ifndef NO_SYSLOG #include #else #define LOG_ALERT 0 #endif #include "sio.h" #include "str.h" #include "xlog.h" #include "filelog.h" static int filelog_init(xlog_s *, va_list) ; static void filelog_fini(xlog_s *) ; static int filelog_control(xlog_s *, xlog_cmd_e, va_list) ; static int filelog_write(xlog_s *, const char buf[], int, int, va_list) ; static int filelog_parms(xlog_e, va_list) ; static int limit_checks(const xlog_s *) ; struct xlog_ops __xlog_filelog_ops = { filelog_init, filelog_fini, filelog_write, filelog_control, filelog_parms } ; static int filelog_init( xlog_s *xp, va_list ap ) { int fd ; struct filelog_s *flp ; char *filename; int flags; filename = va_arg(ap, char *); flags = va_arg(ap, int); flp = NEW( struct filelog_s ) ; if ( flp == NULL ) return( XLOG_ENOMEM ) ; if ( flags & O_CREAT ) fd = open( filename, flags, va_arg( ap, int ) ) ; else fd = open( filename, flags ) ; if ( fd == -1 ) { free( flp ) ; return( XLOG_EOPEN ) ; } FILELOG_DISABLE_SIZE_CONTROL( flp ) ; (void) Sbuftype( fd, SIO_LINEBUF ) ; flp->fl_fd = fd ; flp->fl_state = FL_OPEN ; xp->xl_data = flp ; return( XLOG_ENOERROR ) ; } static void filelog_fini( xlog_s *xp ) { struct filelog_s *flp = FILELOG( xp ) ; if ( flp->fl_state != FL_CLOSED ) { (void) Sclose( flp->fl_fd ) ; flp->fl_state = FL_CLOSED ; } free( flp ) ; xp->xl_data = NULL ; } static int filelog_control( xlog_s *xp, xlog_cmd_e cmd, va_list ap ) { struct stat st ; struct filelog_s *flp = FILELOG( xp ) ; int status = XLOG_ENOERROR ; if ( flp->fl_state == FL_ERROR ) return( flp->fl_error ) ; switch ( cmd ) { case XLOG_GETFD: if ( flp->fl_state == FL_OPEN ) *va_arg( ap, int * ) = flp->fl_fd ; else status = XLOG_ENOERROR ; break ; case XLOG_LIMITS: flp->fl_soft_limit = va_arg( ap, unsigned ) ; flp->fl_hard_limit = va_arg( ap, unsigned ) ; flp->fl_issued_warning = FALSE ; FILELOG_ENABLE_SIZE_CONTROL( flp ) ; flp->fl_state = FL_OPEN ; /* FALL THROUGH */ case XLOG_SIZECHECK: if ( ! FILELOG_SIZE_CONTROL( flp ) ) break ; if ( fstat( flp->fl_fd, &st ) == -1 ) { FILELOG_DISABLE_SIZE_CONTROL( flp ) ; flp->fl_state = FL_ERROR ; flp->fl_error = status = XLOG_EFSTAT ; } else { flp->fl_size = st.st_size ; if ( flp->fl_size > flp->fl_soft_limit ) status = limit_checks( xp ) ; } break ; case XLOG_LINK: case XLOG_CALLBACK: case XLOG_GETFLAG: case XLOG_SETFLAG: case XLOG_LEVEL: case XLOG_FACILITY: case XLOG_PREEXEC: case XLOG_POSTEXEC: break; } return( status ) ; } static int limit_checks( const xlog_s *xp ) { struct filelog_s *flp = FILELOG( xp ) ; char buf[ 100 ] ; if ( ! flp->fl_issued_warning ) { if ( xp->xl_use != NULL ) xlog_write( (xlog_h) xp->xl_use, buf, strx_nprint( buf, sizeof( buf ), "soft limit exceeded on '%s'", xp->xl_id ), XLOG_NOFLAGS, LOG_ALERT ) ; flp->fl_issued_warning = TRUE ; } if ( flp->fl_size <= flp->fl_hard_limit ) return( XLOG_ENOERROR ) ; if ( xp->xl_use != NULL ) xlog_write( (xlog_h) xp->xl_use, buf, strx_nprint( buf, sizeof( buf ), "hard limit exceeded on '%s'; log closed", xp->xl_id ), XLOG_NOFLAGS, LOG_ALERT ) ; flp->fl_state = FL_ERROR ; return( XLOG_ESIZE ) ; } static int filelog_write( xlog_s *xp, const char buf[], int len, int flags, va_list ap ) { struct filelog_s *flp = FILELOG( xp ) ; int action_flags = ( xp->xl_flags | flags ) ; int msglen = 0 ; int percent_m_pos = 0 ; int cc ; int status ; time_t current_time ; struct tm *tmp ; if ( flp->fl_state != FL_OPEN ) return( flp->fl_error ) ; (void) time( ¤t_time ) ; tmp = localtime( ¤t_time ) ; cc = Sprint( flp->fl_fd, "%02d/%d/%d@%02d:%02d:%02d", tmp->tm_year%100, tmp->tm_mon+1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec ) ; if ( cc == SIO_ERR ) return XLOG_EWRITE; msglen += cc ; if ( action_flags & XLOG_PRINT_ID ) { cc = Sprint( flp->fl_fd, " %s", xp->xl_id ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; } if ( action_flags & XLOG_PRINT_PID ) { cc = Sprint( flp->fl_fd, "[%d]", getpid() ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; } cc = Sprint( flp->fl_fd, ": " ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; if ( ( action_flags & XLOG_NO_ERRNO ) || ( percent_m_pos = __xlog_add_errno( buf, len ) ) == -1 ) { cc = Swrite( flp->fl_fd, buf, len ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; } else { char errno_buf[ 100 ] ; unsigned size = sizeof( errno_buf ) ; char *ep ; /* * The reason for the repetition of "msglen += cc ;" is that * in the future we may want to check cc for SIO_ERR */ ep = __xlog_explain_errno( errno_buf, &size ) ; cc = Swrite( flp->fl_fd, buf, percent_m_pos ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; cc = Swrite( flp->fl_fd, ep, size ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; cc = Swrite( flp->fl_fd, buf+percent_m_pos+2, len-percent_m_pos-2 ) ; if ( cc == SIO_ERR ) { flp->fl_size += msglen ; return XLOG_EWRITE; } msglen += cc ; } /* * Writing a newline will cause a buffer flush since we asked for * line-buffered output */ if ( Sputchar( flp->fl_fd, '\n' ) != SIO_ERR ) msglen++ ; /* * NOTE: we don't check if XLOG_NO_SIZECHECK is set in xp->xl_flags * because size control is off by default and in order to * be enabled XLOG_LIMITS must be used which overrides xp->xl_flags */ if ( ! FILELOG_SIZE_CONTROL( flp ) || ( flags & XLOG_NO_SIZECHECK ) ) return( XLOG_ENOERROR ) ; flp->fl_size += msglen ; if ( flp->fl_size <= flp->fl_soft_limit || ( status = limit_checks( xp ) ) == XLOG_ENOERROR ) return( XLOG_ENOERROR ) ; flp->fl_state = FL_SIZE ; return( status ) ; } static int filelog_parms( xlog_e type, va_list ap) { return( XLOG_ENOERROR ) ; } xinetd-2.3.15.4/src/xlog/filelog.h000066400000000000000000000023561333055217000165760ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id$ */ #ifndef __FILELOG_H #define __FILELOG_H #include "impl.h" /* * The file can be either open or closed. * When the file is closed, the state is always FL_CLOSED. * When the file is open, the state is: * FL_OPEN: if everything is ok * FL_SIZE: if the hard limit was exceeded * FL_ERROR: if an error occured */ typedef enum { FL_CLOSED = 0, FL_OPEN, FL_SIZE, FL_ERROR } filelog_state_e ; struct filelog_s { int fl_fd ; filelog_state_e fl_state ; int fl_error ; /* error code when in FL_ERROR */ bool_int fl_size_control ; /* enabled or not */ bool_int fl_issued_warning ; /* when the soft limit was exceeded */ unsigned fl_size ; /* current size */ unsigned fl_soft_limit ; unsigned fl_hard_limit ; } ; #define FILELOG_ENABLE_SIZE_CONTROL( flp ) (flp)->fl_size_control = TRUE #define FILELOG_DISABLE_SIZE_CONTROL( flp ) (flp)->fl_size_control = FALSE #define FILELOG_SIZE_CONTROL( flp ) ( (flp)->fl_size_control ) #define FILELOG( xp ) ((struct filelog_s *)xp->xl_data) #endif xinetd-2.3.15.4/src/xlog/impl.h000066400000000000000000000051411333055217000161110ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id$ */ #ifndef __XLOG_IMPL_H #define __XLOG_IMPL_H #include /* For malloc() & free() prototypes */ #include #define DEFINE_LINK_TYPE( type, name ) struct { type *next, *prev ; } name #define NEXT( obj, field ) (obj)->field.next #define PREV( obj, field ) (obj)->field.prev #define INIT_LINKS( obj, field ) \ { \ NEXT( obj, field ) = obj ; \ PREV( obj, field ) = obj ; \ } /* * Link new object after object using the specified field */ #define LINK( obj, new_obj, field ) \ { \ NEXT( new_obj, field ) = NEXT( obj, field ) ; \ PREV( new_obj, field ) = obj ; \ NEXT( obj, field ) = new_obj ; \ PREV( NEXT( obj, field ), field ) = new_obj ; \ } #define UNLINK( obj, field ) \ { \ NEXT( PREV( obj, field ), field ) = NEXT( obj, field ) ; \ PREV( NEXT( obj, field ), field ) = PREV( obj, field ) ; \ } /* * xlog linking: * When xlog1 is linked to xlog2 (i.e. errors on xlog1 are reported to * xlog2) we use the xl_clients field on xlog2 and the xl_other_users * field on xlog1 */ struct xlog { xlog_e xl_type ; char *xl_id ; int xl_flags ; void (*xl_callback)() ; void *xl_callback_arg ; struct xlog *xl_use ; /* xlog we report errors to */ struct xlog *xl_clients ; /* linked list of xlogs that use */ /* this xlog to report errors */ DEFINE_LINK_TYPE( struct xlog, xl_other_users ) ; struct xlog_ops { int (*init) ( struct xlog *, va_list ) ; void (*fini) ( struct xlog * ) ; int (*write) ( struct xlog *, const char *, int, int, va_list ) ; int (*control) ( struct xlog *, xlog_cmd_e, va_list ) ; int (*parms) ( xlog_e, va_list ) ; } *xl_ops ; void *xl_data ; } ; #define XL_INIT( xp, ap ) (*(xp)->xl_ops->init)( (xp), (ap) ) #define XL_FINI( xp ) (*(xp)->xl_ops->fini)( xp ) #define XL_WRITE( xp, buf, size, flags, ap ) \ (*(xp)->xl_ops->write)( (xp), (buf), (size), (flags), (ap ) ) #define XL_CONTROL( xp, cmd, ap ) \ (*(xp)->xl_ops->control)( (xp), (cmd), (ap) ) typedef struct xlog xlog_s ; typedef void (*voidfunc)() ; typedef int bool_int ; #define XP( p ) ((struct xlog *)(p)) #define XLOG_NULL XP( NULL ) #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif #ifndef NULL #define NULL 0 #endif #define NEW( type ) (type *) malloc( sizeof( type ) ) int __xlog_add_errno(const char *, int) ; char *__xlog_explain_errno(char *, unsigned *) ; char *__xlog_new_string(const char *) ; #endif xinetd-2.3.15.4/src/xlog/slog.c000066400000000000000000000104501333055217000161060ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #ifndef NO_SYSLOG #include #endif #include "xlog.h" #include "impl.h" #include "slog.h" #include "str.h" #include #define MSGBUFSIZE 2048 static int syslog_init(xlog_s *, va_list) ; static void syslog_fini(xlog_s *) ; static int syslog_control(xlog_s *, xlog_cmd_e, va_list ) ; static int syslog_write(xlog_s *, const char buf[], int , int , va_list ) ; static int syslog_parms(xlog_e, va_list) ; static struct syslog_parms parms = { 0, #ifndef NO_SYSLOG LOG_PID + LOG_NOWAIT, LOG_USER, #else 0, 0, #endif "XLOG", } ; struct xlog_ops __xlog_syslog_ops = { syslog_init, syslog_fini, syslog_write, syslog_control, syslog_parms } ; #ifdef NO_SYSLOG /* * Notice that the following functions will never be invoked since * the xlog_* functions will not call them. However, we need to define * them so that we don't have any unresolved references; and we define * them without any arguments. */ static void syslog() { } static void openlog() { } static void closelog() { } #endif /* NO_SYSLOG */ /* * Expected arguments: * facility, level */ static int syslog_init( xlog_s *xp, va_list ap ) { struct syslog_parms *slp = &parms ; struct syslog_s *sp ; sp = NEW( struct syslog_s ) ; if ( sp == NULL ) return( XLOG_ENOMEM ) ; sp->sl_facility = va_arg( ap, int ) ; sp->sl_default_level = va_arg( ap, int ) ; if ( slp->slp_n_xlogs++ == 0 ) openlog( slp->slp_ident, slp->slp_logopts, slp->slp_facility ) ; xp->xl_data = sp ; return( XLOG_ENOERROR ) ; } static void syslog_fini( xlog_s *xp ) { if ( --parms.slp_n_xlogs == 0 ) closelog() ; free( SYSLOG( xp ) ) ; xp->xl_data = NULL ; } static int syslog_control( xlog_s *xp, xlog_cmd_e cmd, va_list ap ) { switch ( cmd ) { case XLOG_LEVEL: SYSLOG( xp )->sl_default_level = va_arg( ap, int ) ; break ; case XLOG_FACILITY: SYSLOG( xp )->sl_facility = va_arg( ap, int ) ; break ; case XLOG_PREEXEC: closelog() ; break ; case XLOG_POSTEXEC: if ( parms.slp_n_xlogs ) openlog( parms.slp_ident, parms.slp_logopts, parms.slp_facility ) ; break ; /* These fall through ? */ case XLOG_LINK: case XLOG_CALLBACK: case XLOG_GETFLAG: case XLOG_SETFLAG: case XLOG_SIZECHECK: case XLOG_GETFD: case XLOG_LIMITS: break ; } return( XLOG_ENOERROR ) ; } static int syslog_write( xlog_s *xp, const char buf[], int len, int flags, va_list ap ) { int level ; int syslog_arg ; char prefix[ MSGBUFSIZE ] ; int prefix_size = sizeof( prefix ) ; int prefix_len = 0 ; int cc ; int percent_m_pos ; int action_flags = ( flags | xp->xl_flags ) ; if ( flags & XLOG_SET_LEVEL ) level = va_arg( ap, int ) ; else level = SYSLOG( xp )->sl_default_level ; syslog_arg = SYSLOG( xp )->sl_facility + level ; if ( action_flags & XLOG_PRINT_ID ) { cc = strx_nprint( &prefix[ prefix_len ], prefix_size, "%s: ", xp->xl_id ) ; prefix_len += cc ; prefix_size -= cc ; } if ( ( action_flags & XLOG_NO_ERRNO ) || ( percent_m_pos = __xlog_add_errno( buf, len ) ) == -1 ) syslog( syslog_arg, "%.*s%.*s", prefix_len, prefix, len, buf ) ; else { char *ep ; char errno_buf[ 100 ] ; unsigned size = sizeof( errno_buf ) ; ep = __xlog_explain_errno( errno_buf, &size ) ; syslog( syslog_arg, "%.*s%.*s%.*s%.*s", prefix_len, prefix, percent_m_pos, buf, (int)size, ep, len - percent_m_pos - 2, buf + percent_m_pos + 2 ) ; } return( XLOG_ENOERROR ) ; } static int syslog_parms( xlog_e type, va_list ap ) { char *id = NULL; id = __xlog_new_string( va_arg( ap, char * ) ); if ( id == NULL ) return( XLOG_ENOMEM ) ; parms.slp_ident = id ; parms.slp_logopts = va_arg( ap, int ) ; parms.slp_facility = va_arg( ap, int ) ; return( XLOG_ENOERROR ) ; } xinetd-2.3.15.4/src/xlog/slog.h000066400000000000000000000011551333055217000161150ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ /* * $Id$ */ #ifndef __SLOG_H #define __SLOG_H struct syslog_s { int sl_facility ; int sl_default_level ; } ; struct syslog_parms { int slp_n_xlogs ; /* # of xlogs using syslog */ int slp_logopts ; /* used at openlog */ int slp_facility ; const char *slp_ident ; /* used at openlog */ /* bool_int slp_ident_is_malloced ; */ } ; #define SYSLOG( xp ) ((struct syslog_s *)xp->xl_data) #endif xinetd-2.3.15.4/src/xlog/util.c000066400000000000000000000022411333055217000161160ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include #include #include "str.h" #include "libportable.h" /* * Search the given buffer for an occurrence of "%m" */ int __xlog_add_errno( const char *buf, int len ) { int i; for ( i = 0 ; i < len-1 ; i++ ) if ( (buf[i] == '%') && (buf[i+1] == 'm') ) return( i ) ; return( -1 ) ; } /* __xlog_explain_errno: * Description: generates a string to explain the value of errno. * On entry: buf must point to space already allocated by the caller, * and size points to the amount of memory buf is allocated. * On exit: buf contains the NULL terminated string, size now points * to the length of the string, and the return value is buf. */ char *__xlog_explain_errno( char *buf, unsigned *size ) { strx_print((int *)size, buf, *size, "%s (errno = %d)", strerror(errno), errno); return( buf ) ; } char *__xlog_new_string( const char *s ) { if ( s ) return strdup( s ) ; else return 0; } xinetd-2.3.15.4/src/xlog/xlog.c000066400000000000000000000107751333055217000161250ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #include #include #include "xlog.h" #include "impl.h" extern struct xlog_ops __xlog_filelog_ops ; #ifndef NO_SYSLOG extern struct xlog_ops __xlog_syslog_ops ; #endif struct lookup_table { struct xlog_ops *ops ; xlog_e type ; } ; static struct lookup_table ops_lookup_table[] = { { &__xlog_filelog_ops, XLOG_FILELOG }, #ifndef NO_SYSLOG { &__xlog_syslog_ops, XLOG_SYSLOG }, #endif { NULL, 0 } } ; #define CALLBACK( xp, status ) \ if ( (xp)->xl_callback ) \ (*(xp)->xl_callback)( (xlog_h)(xp), status, (xp)->xl_callback_arg ) static void xlog_link( xlog_s *client, xlog_s *server ) ; static void xlog_unlink( xlog_s *xp ) ; static struct xlog_ops *xlog_ops_lookup( xlog_e type ) { struct lookup_table *ltp ; for ( ltp = &ops_lookup_table[ 0 ] ; ltp->ops ; ltp++ ) if ( ltp->type == type ) break ; return( ltp->ops ) ; } /* VARARGS3 */ xlog_h xlog_create( xlog_e type, const char *id, int flags, ... ) { xlog_s *xp ; va_list ap ; struct xlog_ops *xops ; int status ; if ( ( xp = NEW( xlog_s ) ) == NULL ) return( NULL ) ; if ( id == NULL || ( xp->xl_id = __xlog_new_string( id ) ) == NULL ) { free( xp ) ; return( NULL ) ; } xops = xlog_ops_lookup( type ) ; if ( xops != NULL ) { va_start( ap, flags ) ; xp->xl_ops = xops ; status = XL_INIT( xp, ap ) ; va_end( ap ) ; if ( status == XLOG_ENOERROR ) { xp->xl_flags = flags ; xp->xl_type = type ; xp->xl_clients = XLOG_NULL ; xp->xl_use = XLOG_NULL ; return( (xlog_h) xp ) ; } } free( xp->xl_id ) ; free( xp ) ; return( NULL ) ; } static void xlog_link( xlog_s *client, xlog_s *server ) { client->xl_use = server ; if ( server == NULL ) return ; if ( server->xl_clients == XLOG_NULL ) { INIT_LINKS( client, xl_other_users ) ; server->xl_clients = client ; } else LINK( server, client, xl_other_users ) ; } static void xlog_unlink( xlog_s *xp ) { xlog_s *server = xp->xl_use ; /* * Step 1: remove from server chain */ if ( server != XLOG_NULL ) { if ( server->xl_clients == xp ) if ( NEXT( xp, xl_other_users ) == xp ) server->xl_clients = XLOG_NULL ; else server->xl_clients = NEXT( xp, xl_other_users ) ; else UNLINK( xp, xl_other_users ) ; } /* * Step 2: If we have users, clear their link to us. */ if ( xp->xl_clients != NULL ) { xlog_s *xp2 = xp->xl_clients ; do { xp2->xl_use = XLOG_NULL ; xp2 = NEXT( xp2, xl_other_users ) ; } while ( xp2 != xp->xl_clients ) ; } } static void xlog_flags( xlog_s *xp, xlog_cmd_e cmd, ... ) { va_list ap; int flag; int old_value; int *valp; va_start(ap, cmd); flag = va_arg( ap, int ); old_value = ((xp->xl_flags & flag) != 0); valp = va_arg( ap, int * ); va_end(ap); if ( cmd == XLOG_SETFLAG ) { if ( *valp ) xp->xl_flags |= flag ; else xp->xl_flags &= ~flag ; } *valp = old_value ; } void xlog_destroy( xlog_h pxlog ) { xlog_s *xp = XP( pxlog ) ; xlog_unlink( xp ) ; XL_FINI( xp ) ; free( xp->xl_id ) ; free( xp ) ; } /* VARARGS4 */ void xlog_write( xlog_h pxlog, const char buf[], int len, int flags, ... ) { xlog_s *xp = XP( pxlog ) ; va_list ap ; int status ; va_start( ap, flags ) ; status = XL_WRITE( xp, buf, len, flags, ap ) ; va_end( ap ) ; if ( status != XLOG_ENOERROR ) { CALLBACK( xp, status ) ; } } /* VARARGS2 */ int xlog_control( xlog_h pxlog, xlog_cmd_e cmd, ... ) { va_list ap ; xlog_s *xp = XP( pxlog ) ; int status = XLOG_ENOERROR ; va_start( ap, cmd ) ; switch ( cmd ) { case XLOG_LINK: xlog_unlink( xp ) ; xlog_link( xp, va_arg( ap, xlog_s * ) ) ; xp->xl_callback_arg = va_arg( ap, void * ) ; break ; case XLOG_CALLBACK: xp->xl_callback = va_arg( ap, voidfunc ) ; xp->xl_callback_arg = va_arg( ap, void * ); break ; case XLOG_GETFLAG: case XLOG_SETFLAG: xlog_flags( xp, cmd, ap ) ; break ; default: status = XL_CONTROL( xp, cmd, ap ) ; } va_end( ap ) ; return( status ) ; } int xlog_parms( xlog_e type, ... ) { va_list ap ; int status ; va_start( ap, type ) ; switch ( type ) { #ifndef NO_SYSLOG case XLOG_SYSLOG: status = (*__xlog_syslog_ops.parms)( type, ap ) ; break ; #endif case XLOG_FILELOG: status = (*__xlog_filelog_ops.parms)( type, ap ) ; break ; default: status = XLOG_ENOERROR ; } va_end( ap ) ; return( status ) ; } xinetd-2.3.15.4/src/xlog/xlog.h000066400000000000000000000033131333055217000161200ustar00rootroot00000000000000/* * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef __XLOG_H #define __XLOG_H /* * $Id$ */ /* * Flags */ #define XLOG_NOFLAGS 0x0 #define XLOG_SET_LEVEL 0x1 #define XLOG_NO_SIZECHECK 0x2 #define XLOG_NO_ERRNO 0x4 /* #define XLOG_PRINT_TIMESTAMP 0x8 */ #define XLOG_PRINT_ID 0x10 #define XLOG_PRINT_PID 0x20 /* * Errors */ #define XLOG_ENOERROR 0 #define XLOG_ESIZE 1 #define XLOG_EOPEN 2 #define XLOG_EFSTAT 3 #define XLOG_ENOMEM 4 #define XLOG_EWRITE 5 /* * Interface */ typedef enum { XLOG_SYSLOG, XLOG_FILELOG } xlog_e ; typedef enum { XLOG_LINK, /* generic: link this log to another log */ XLOG_CALLBACK, /* generic: call this function in case of error */ XLOG_GETFLAG, /* generic: get value of specified flag */ XLOG_SETFLAG, /* generic: set value of specified flag */ XLOG_LEVEL, /* syslog: set the default syslog level */ XLOG_FACILITY, /* syslog: set the default syslog facility */ XLOG_PREEXEC, /* syslog: prepare the log for an exec(2) */ XLOG_POSTEXEC, /* syslog: exec(2) failed */ XLOG_SIZECHECK, /* filelog: check file size */ XLOG_GETFD, /* filelog: get file descriptor of log file */ XLOG_LIMITS /* filelog: set (new) soft/hard limits */ } xlog_cmd_e ; typedef void *xlog_h ; xlog_h xlog_create ( xlog_e type, const char *id, int flags, ... ) ; void xlog_destroy ( xlog_h ) ; void xlog_write ( xlog_h, const char *buf, int len, int flags, ... ) ; int xlog_control ( xlog_h, xlog_cmd_e, ... ) ; int xlog_parms ( xlog_e type, ... ) ; #endif /* __XLOG_H */ xinetd-2.3.15.4/src/xpoll.h000066400000000000000000000011221333055217000153300ustar00rootroot00000000000000/* * (c) Copyright 2009 by Red Hat Inc. * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include "config.h" #ifdef HAVE_POLL #ifndef _X_POLL_H #define _X_POLL_H #include #include "defs.h" /* Field accessor methods for pollfd in defined in poll.h */ #define POLLFD_FD( pfd ) ( (pfd)->fd ) #define POLLFD_EVENTS( pfd ) ( (pfd)->events ) #define POLLFD_REVENTS( pfd ) ( (pfd)->revents ) /* TODO: write memory management stuff in xpoll.c if needed */ #endif /* _X_POLL_H */ #endif /* HAVE_POLL */ xinetd-2.3.15.4/src/xtimer.c000066400000000000000000000075441333055217000155130ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #include #include "config.h" #include "xtimer.h" #include "pset.h" #include "msg.h" /* A note on the usage of timers in these functions: * The timers are composed of 3 elements, a pointer to a callback function, * the expire time of the timer, and a unique (pseudo-monotomically increasing) * identifier for the timer. * Timers are kept in a pset_h (linked list) and are sorted at insertion * time, with the nearest expire time first. The sort at insertion is to * avoid examining non-expired timers in the xtimer_poll() function, and * to be able to easily retrieve the next expiring timer. * The timers are set in the main event loop, using select() as the * timing device. select() sleeps until the next timer is set to expire * (or until an FD is ready, in which case it also examines the timer list). */ static pset_h xtimer_list = NULL; static int xtimer_init( void ) { if( xtimer_list != NULL ) return 0; xtimer_list = pset_create( 0, 0 ); if( xtimer_list == NULL ) return -1; return 0; } static int xtimer_compfunc( const void *_a, const void *_b ) { xtime_h * const *a = (xtime_h * const *)_a; xtime_h * const *b = (xtime_h * const *)_b; return ((*a)->when - (*b)->when); } /* xtimer_add: * Adds a timer to the pset_h of timers, and sorts the timer list (least time * remaining first). * Return values: * Success: the timer ID which can be used to later remove the timer (>0) * Failure: -1 */ int xtimer_add( void (*func)(void), time_t secs ) { xtime_h *new_xtimer = NULL; time_t tmptime; unsigned count; if( xtimer_list == NULL ) { if( xtimer_init() < 0 ) return -1; } new_xtimer = (xtime_h *)malloc(sizeof(xtime_h)); if( new_xtimer == NULL ) { return -1; } tmptime = time(NULL); if( tmptime == -1 ) { free( new_xtimer ); return -1; } new_xtimer->timerfunc = func; new_xtimer->when = tmptime + secs; if( (count = pset_count( xtimer_list )) == 0 ) { new_xtimer->xtid = 1; } else { new_xtimer->xtid = ((xtime_h *)(pset_pointer(xtimer_list, count-1)))->xtid + 1; } if( pset_add( xtimer_list, new_xtimer ) == NULL ) { free( new_xtimer ); return -1; } pset_sort( xtimer_list, xtimer_compfunc ); return(new_xtimer->xtid); } /* xtimer_poll: * Traverses the pset_h of timers, and executes the callback for expired * timers. Timers that are expired, and have their callback executed * are removed from the list of timers. */ int xtimer_poll(void) { unsigned int i; if( xtimer_list == NULL ) return(0); for( i = 0; i < pset_count( xtimer_list ); i++ ) { xtime_h *cur_timer = pset_pointer( xtimer_list, i ); time_t cur_time = time(NULL); /* The list is sorted, low to high. If there's no * timers left, return. */ if( cur_timer->when > cur_time ) { return(0); } cur_timer->timerfunc(); pset_delete( xtimer_list, cur_timer ); free(cur_timer); i--; cur_timer = NULL; } return(0); } /* xtimer_remove: * Removes a timer from the list of timers. Takes the timer id as an argument * Returns: * Success: 0 * Failure: -1 */ int xtimer_remove(int xtid) { unsigned int i; int ret = -1; for( i = 0; i < pset_count( xtimer_list ); i++ ) { xtime_h *cur_timer = pset_pointer( xtimer_list, i ); if( cur_timer->xtid == xtid ) { pset_delete( xtimer_list, cur_timer ); free( cur_timer ); cur_timer = NULL; ret = 0; } } return(ret); } /* xtimer_nexttime: * Returns the number of seconds until the next timer expires. * Returns -1 when no timers are active. */ time_t xtimer_nexttime(void) { time_t ret; if(xtimer_list == NULL) return -1; if( pset_count(xtimer_list) == 0 ) return -1; ret = ((xtime_h *)pset_pointer(xtimer_list, 0))->when - time(NULL) ; if( ret < 0 ) ret = 0; return( ret ); } xinetd-2.3.15.4/src/xtimer.h000066400000000000000000000007551333055217000155150ustar00rootroot00000000000000/* * (c) Copyright 1998-2001 by Rob Braun * All rights reserved. The file named COPYRIGHT specifies the terms * and conditions for redistribution. */ #ifndef _X_TIMER_H #define _X_TIMER_H #include #include struct xtime { void (*timerfunc)(void); time_t when; int xtid; }; typedef struct xtime xtime_h; int xtimer_add( void (*func)(void), time_t ); int xtimer_poll(void); int xtimer_remove(int); time_t xtimer_nexttime(void); #endif /* _X_TIMER_H */