spiped-1.6.2/000755 001751 001751 00000000000 14161420052 014433 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/spipe/000755 001751 001751 00000000000 14161420052 015553 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/liball/000755 001751 001751 00000000000 14161417743 015707 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/Makefile000644 001751 001751 00000005527 14157142752 016121 0ustar00cpercivacperciva000000 000000 .POSIX: PROGS= spipe \ spiped TESTS= perftests/recv-zeros \ perftests/send-zeros \ perftests/standalone-enc \ tests/dnsthread-resolve \ tests/nc-client \ tests/nc-server \ tests/pthread_create_blocking_np \ tests/pushbits \ tests/valgrind BINDIR_DEFAULT= /usr/local/bin CFLAGS_DEFAULT= -O2 LIBCPERCIVA_DIR= libcperciva TEST_CMD= tests/test_spiped.sh ### Shared code between Tarsnap projects. all: toplevel export CFLAGS="$${CFLAGS:-${CFLAGS_DEFAULT}}"; \ . ./posix-flags.sh; \ . ./cpusupport-config.h; \ . ./cflags-filter.sh; \ export HAVE_BUILD_FLAGS=1; \ for D in ${PROGS} ${TESTS}; do \ ( cd $${D} && ${MAKE} all ) || exit 2; \ done .PHONY: toplevel toplevel: cflags-filter.sh cpusupport-config.h \ liball posix-flags.sh # For "loop-back" building of a subdirectory buildsubdir: toplevel . ./posix-flags.sh; \ . ./cpusupport-config.h; \ . ./cflags-filter.sh; \ export HAVE_BUILD_FLAGS=1; \ cd ${BUILD_SUBDIR} && ${MAKE} ${BUILD_TARGET} # For "loop-back" building of the library .PHONY: liball liball: cflags-filter.sh cpusupport-config.h posix-flags.sh . ./posix-flags.sh; \ . ./cpusupport-config.h; \ . ./cflags-filter.sh; \ export HAVE_BUILD_FLAGS=1; \ ( cd liball && make all ) || exit 2; posix-flags.sh: if [ -d ${LIBCPERCIVA_DIR}/POSIX/ ]; then \ export CC="${CC}"; \ cd ${LIBCPERCIVA_DIR}/POSIX; \ printf "export \"LDADD_POSIX="; \ command -p sh posix-l.sh "$$PATH"; \ printf "\"\n"; \ printf "export \"CFLAGS_POSIX="; \ command -p sh posix-cflags.sh "$$PATH"; \ printf "\"\n"; \ fi > $@ if [ ! -s $@ ]; then \ printf "#define POSIX_COMPATIBILITY_NOT_CHECKED 1\n"; \ fi >> $@ cflags-filter.sh: if [ -d ${LIBCPERCIVA_DIR}/POSIX/ ]; then \ export CC="${CC}"; \ cd ${LIBCPERCIVA_DIR}/POSIX; \ command -p sh posix-cflags-filter.sh "$$PATH"; \ fi > $@ if [ ! -s $@ ]; then \ printf "# Compiler understands normal flags; "; \ printf "nothing to filter out\n"; \ fi >> $@ cpusupport-config.h: if [ -d ${LIBCPERCIVA_DIR}/cpusupport/ ]; then \ export CC="${CC}"; \ command -p sh \ ${LIBCPERCIVA_DIR}/cpusupport/Build/cpusupport.sh \ "$$PATH"; \ fi > $@ if [ ! -s $@ ]; then \ printf "#define CPUSUPPORT_NONE 1\n"; \ fi >> $@ install: all export BINDIR=$${BINDIR:-${BINDIR_DEFAULT}}; \ for D in ${PROGS}; do \ ( cd $${D} && ${MAKE} install ) || exit 2; \ done clean: test-clean rm -f cflags-filter.sh cpusupport-config.h posix-flags.sh for D in liball ${PROGS} ${TESTS}; do \ ( cd $${D} && ${MAKE} clean ) || exit 2; \ done .PHONY: test test-clean test: all ${TEST_CMD} test-clean: rm -rf tests-output/ tests-valgrind/ # Developer targets: These only work with BSD make Makefiles: ${MAKE} -f Makefile.BSD Makefiles publish: ${MAKE} -f Makefile.BSD publish spiped-1.6.2/perftests/000755 001751 001751 00000000000 13762775424 016500 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/BUILDING000644 001751 001751 00000006447 13723620112 015570 0ustar00cpercivacperciva000000 000000 Installing ---------- To build and install spiped, run: # make BINDIR=/path/to/target/directory install To install man pages, add MAN1DIR=/path/to/man.1/directory to the command line (e.g., MAN1DIR=/usr/local/man/man1 on FreeBSD). Spiped should build and run on any IEEE Std 1003.1 (POSIX) compliant system which 1. Includes the Software Development Utilities option, 2. Has OpenSSL available via -lcrypto and #include , and 3. Provides /dev/urandom. Platform-specific notes ----------------------- - On OS X, the version of OpenSSL included with the operating system is outdated (0.9.8) and deprecated, and it is recommended that spiped be built with an updated version of OpenSSL. On OS X 10.11 "El Capitan", OpenSSL was removed entirely. After installing a newer version of OpenSSL, use CFLAGS="-I /path/to/openssl/headers" LDADD_EXTRA="-L /path/to/openssl/lib" to build spiped. Note that spiped will still build (on pre-10.11) if you set these options wrong: If you see warning: 'AES_set_encrypt_key' is deprecated during the build then spiped is still using the outdated version of OpenSSL from OS X. - On Cygwin the following command must be run before building spiped in order order to work around a bug in Cygwin's C library: sed -i.orig s/-D_POSIX_C_SOURCE=200809L// */Makefile Without this command, spiped will still build but it will crash. - On some platforms (Solaris, maybe others), additional compiler and/or linker options are required to find OpenSSL or system libraries; these can be provided by adding e.g., CFLAGS="-I/path/to/openssl/headers" (compiler option) or LDADD_EXTRA="-L/usr/sfw/lib -lsocket -lnsl" (linker option) to the make command line. - On Solaris, the command-line utilities in /usr/bin are not necessarily POSIX-compatible. According to standards(7), in order to use POSIX.1-2008 standard-conforming utilities, you must set your PATH so that certain directories (such as /usr/xpg4/bin/) take precedence over /usr/bin. Please check the documentation in your version of Solaris by: $ man standards and carefully read the "Utilities" section. - On some platforms (OpenBSD prior to 5.4, and possibly others) you will need to add #include at the start of lib/dnsthread/dnsthread.c libcperciva/util/sock_util.c proto/proto_conn.c spipe/main.c spipe/pushbits.c due to a POSIX-compliance bug on those platforms. - On some platforms (mostly Linuxes) it is possible to install OpenSSL libraries without the associated header files; the header files are usually in packages named "openssl-devel", "libssl-dev", or similar. - If your OS provides random bytes via some mechanism other than /dev/urandom, please make local changes to lib/util/entropy.c and notify the author. If spiped fails to build or run for other reasons, please notify the author. Updating build code and releasing --------------------------------- The POSIX-compatible Makefiles are generated via `make Makefiles` from the included (far more readable) BSD Makefiles. To run this target, you will need to have a BSD `make(1)` utility; NetBSD's `make(1)` is available for many operating systems as `bmake`. Release tarballs are generated via `make VERSION=x.y.z publish`, subject to the same caveat of needing a BSD-compatible make. spiped-1.6.2/lib/000755 001751 001751 00000000000 14070374447 015220 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/STYLE000644 001751 001751 00000015500 14070374447 015276 0ustar00cpercivacperciva000000 000000 Code style ========== In general, FreeBSD style(9) should be followed unless it is irrelevant (e.g., $FreeBSD$ tags). Functions with external linkage are declared like this: /** * module_func(arg1, arg2): * Description of what the function does, referring to arguments as * ${arg1} or suchlike. */ int module_func(void *, int); The identical comment appears in the C file where the function is defined. Static functions may have the above form of comment, or simply a /* Brief description of what the function does. */ line before the function. "Unrewrappable" comments starting in the first column should be /** * Written like this. * * Because (some of) the line-breaks are important. */ whereas when such comments are indented, they should be /*- * Written like this. * * Because (some of) the line-breaks are important. */ Line lengths should generally be 78 characters, and not more than 80 characters. In general, functions should return (int)(-1) or NULL to indicate error. Errors should be printed via warnp (if errno is relevant) or warn0 (if errno is not relevant) when they are first detected and also at higher levels where useful. As an exception to this, malloc failures (i.e., errno = ENOMEM) can result in failure being passed back up the call chain without being printed immediately. (Naturally, other errors can be passed back where a function definition so specifies; e.g., ENOENT in cases where a file not existing is not erroneous.) The first statement in main(), after variable declarations, should be "WARNP_INIT;" in order to set the program name used for printing warnings. In general, functions should be structured with one return statement per status, e.g., one return() for success and one return() for failure. Errors should be handled by using goto to enter the error return path, e.g., int foo(int bar) { if (something fails) goto err0; /* ... */ if (something else fails) goto err1; /* ... */ if (yet another operation fails) goto err2; /* Success! */ return (0); err2: /* Clean up something. */ err1: /* Clean up something else. */ err0: /* Failure! */ return (-1); } As an exception to the above, if there is only one way for the function to fail, the idioms return (baz(bar)); and int rc; rc = baz(bar); /* ... cleanup code here ... */ return (rc); are allowed; furthermore, in cases such as foo_free(), the idiom if (we shouldn't do anything) return; is preferred over if (we shouldn't do anything) goto done; at the start of a function. Headers should be included in the following groups, with a blank line after each (non-empty) group: 1. , with first followed by others alphabetically. 2. , in alphabetical order. 3. <*.h>, in alphabetical order. 4. header files from /lib/, in alphabetical order. 5. header files from the program being built, in alphabetical order. 6. header files (usually just one) defining the interface for this C file. If ssize_t is needed, should be included to provide it. If size_t is needed, should be included to provide it unless , , , or is already required. If the C99 integer types (uint8_t, int64_t, etc.) are required, should be included to provide them unless is already required. The type 'char' should only be used to represent human-readable characters (input from users, output to users, pathnames, et cetera). The type 'char *' should normally be a NUL-terminated string. The types 'signed char' and 'unsigned char' should never be used; C99 integer types should be used instead. When a variable is declared to have a pointer type, there should be a space between the '*' and the variable name, e.g., int main(int argc, char * argv[]) { char * opt_p = NULL; Note that this is inconsistent with FreeBSD style(9). When used as a unary operator, '*' is not separated from its argument, e.g., while (*p != '\0') p++; When a struct is referenced, the idiom /* Opaque types. */ struct foo; struct bar * bar_from_foo(struct foo *); is preferable to #include "foo.h" /* needed for struct foo */ struct bar * bar_from_foo(struct foo *); unless there is some reason why the internal layout of struct foo is needed (e.g., if struct bar contains a struct foo rather than a struct foo *). Such struct declarations should be sorted alphabetically. The file foo.c should only export symbols of the following forms: foo_* -- most symbols should be of this form. FOO_* / BAR_FOO_* -- allowed in cases where FOO or BAR_FOO is idiomatic (e.g., MD5, HMAC_SHA256). foo() / defoo() / unfoo() -- where "foo" is a verb and this improves code clarity. Functions named foo_free should return void, and foo_free(NULL) should have no effect. The right way to spell a comment about this is /* Behave consistently with free(NULL). */ If static variables need to be initialized to 0 (or NULL) then they should be explicitly declared that way; implicit initialization should not be used. In non-trivial code, comments should be included which describe in English what is being done by the surrounding code with sufficient detail that if the code were removed, it could be replaced based on reading the comments without requiring any significant creativity. Comments and documentation should be written in en-GB-oed; i.e., with the 'u' included in words such as "honour", "colour", and "neighbour", and the ending '-ize' in words such as "organize" and "realize". The Oxford (aka. serial) comma should be used in lists. Quotation marks should be placed logically, i.e., not including punctuation marks which do not form a logical part of the quoted text. Two spaces should be used after a period which ends a sentence. The first local variable declaration in cookie-using functions should be struct foo * bar = cookie; When versions of functions are written to exploit special CPU features (using the cpusupport framework), that code should be placed into a separate file (e.g., crypto_aes_aesni.c) so that it can be compiled with different compiler flags. Such a file should start with #include "cpusupport.h" #ifdef CPUSUPPORT_FOO_BAR /** * CPUSUPPORT CFLAGS: FOO_BAR FOO_BAZ */ and end with #endif /* CPUSUPPORT_FOO_BAR */ For example, we could have #if defined(CPUSUPPORT_X86_SHANI) && defined(CPUSUPPORT_X86_SSSE3) /** * CPUSUPPORT CFLAGS: X86_SHANI X86_SSSE3 */ Functions for which special CPU-feature-exploiting variants exist should take the form { /* Variable declarations here. */ /* Asserts here, if any. */ #ifdef CPUSUPPORT_FOO_BAR if (/* We've decided we can use the variant code */) { /* Call variant code and return. */ } #endif /* Normal implementation of the function. */ } If there are multiple CPU-feature-exploiting variants, the `if` could instead be a `switch` which invokes the appropriate variant function. spiped-1.6.2/CHANGELOG000644 001751 001751 00000006263 14161417507 015667 0ustar00cpercivacperciva000000 000000 spiped-1.6.2 * Warn if the maximum number of connections is reached in spiped. * Add --syslog (spiped) to send warnings to syslog when daemonized. * Significantly improve performance of AES-CTR and SHA256 on amd64 and aarch64. * Add ability to suppress POSIX runtime checks during compilation to simplify cross-compiling. spiped-1.6.1 * New option -u username:groupname (spiped): change the user and/or group ownership of the process. * Use RDRAND as an additional source of entropy on CPUs which support it. * Use SHANI instructions on CPUs which support them. * Warn about failed connections and exit with non-zero status (spipe). spiped-1.6.0 * The -n option (spiped) is no longer limited to a maximum limit of 500 simultaneous connections. * The -k option now accepts "-" as a synonym for standard input. * New option -v (spipe/spiped): Print version number. * Add workaround for docker signal-handling bug in spiped. * Perform a graceful shutdown on SIGTERM. spiped-1.5.0 * Attempt to set the TCP_NODELAY socket option on connections, in order to avoid punishing latencies from TCP nagling. spiped-1.4.2 * Fix crash on platforms which support AESNI (i386, amd64) but do not automatically provide 16-byte alignment to large memory allocations (glibc, possibly others). spiped-1.4.1 * Fix build on OS X, and improve strict POSIX compliance. * Improved zeroing of sensitive cryptographic data. spiped-1.4.0 * Add automatic detection of compiler support (at compile-time) and CPU support (at run-time) for x86 "AES New Instructions"; and when available, use these to improve cryptographic performance. * Add support for -g option, which makes {spiped, spipe} require perfect forward secrecy by dropping connections if the peer endpoint is detected to be running using the -f option. spiped-1.3.1 * Fix build by adding missing #include. * Minor code cleanups. spiped-1.3.0 * Bug fix: spiped now correctly closes connections which have been reset; in earlier versions spiped could erroneously hold "dead" connections open as long as they remained idle. * Man pages added. * Protocol-layer keep-alives are now enabled by default. * New option -j (spipe/spiped): Disable protocol-layer keep-alives. * In spiped the target address is now re-resolved every 60 seconds by default. * New option -R (spiped): Do not re-resolve target address. * New option -r (spiped): Re-resolve target address every seconds. spiped-1.2.2 * Build fixes for some strictly POSIX-conforming platforms. * Detect and work around compilers which are POSIX-noncompliant in their handling of -rt and -lxnet options. * Minor documentation and typo fixes. spiped-1.2.1 * Fix build by adding missing #include. spiped-1.2.0 * New utility "spipe": A client for the spiped protocol, handling a single connection with standard input/output as one end. * Code rearrangement with no functional consequences. * Minor bug and documentation fixes. spiped-1.1.0 * New option -D: Wait until DNS lookups succeed. * New option -F: Don't daemonize. * Use SO_REUSEADDR to avoid 'socket address already in use' error (most importantly, if spiped is killed and restarted). * Minor bug and style fixes. spiped-1.0.0 * Initial release spiped-1.6.2/README.md000644 001751 001751 00000007056 13561473171 015737 0ustar00cpercivacperciva000000 000000 spiped ====== Official signed releases are published at: > https://www.tarsnap.com/spiped.html **`spiped`** (pronounced "ess-pipe-dee") is a utility for creating symmetrically encrypted and authenticated pipes between socket addresses, so that one may connect to one address (e.g., a UNIX socket on localhost) and transparently have a connection established to another address (e.g., a UNIX socket on a different system). This is similar to `ssh -L` functionality, but does not use SSH and requires a pre-shared symmetric key. **`spipe`** (pronounced "ess-pipe") is a utility which acts as an spiped protocol client (i.e., connects to an spiped daemon), taking input from the standard input and writing data read back to the standard output. Note that spiped: 1. Requires a strong key file: The file specified via the `-k` option should have at least 256 bits of entropy. (`dd if=/dev/urandom bs=32 count=1` is your friend.) 2. Requires strong entropy from `/dev/urandom`. (Make sure your kernel's random number generator is seeded at boot time!) 3. Does not provide any protection against information leakage via packet timing: Running telnet over spiped will protect a password from being directly read from the network, but will not obscure the typing rhythm. 4. Can significantly increase bandwidth usage for interactive sessions: It sends data in packets of 1024 bytes, and pads smaller messages up to this length, so a 1 byte write could be expanded to 1024 bytes if it cannot be coalesced with adjacent bytes. 5. Uses a symmetric key -- so anyone who can connect to an spiped "server" is also able to impersonate it. Example usage ------------- Examples of spiped protecting SMTP and SSH are given on: > https://www.tarsnap.com/spiped.html For a detailed list of the command-line options to spiped and spipe, see the man pages. Security requirements --------------------- The user is responsible for ensuring that: 1. The key file contains 256 or more bits of entropy. 2. The same key file is not used for more than 2^64 connections. 3. Any individual connection does not transmit more than 2^64 bytes. Building -------- The official releases should build and install on almost any POSIX-compliant operating system, using the included Makefiles: make BINDIR=/path/to/target/directory install See the [BUILDING](BUILDING) file for more details (e.g. how to install man pages). Testing ------- A small test suite can be run with: make test Memory-testing with valgrind (takes approximately twice as long as without valgrind) can be enabled with: make test USE_VALGRIND=1 Code layout ----------- ``` spiped/* -- Code specific to the spiped utility. main.c -- Command-line parsing, initialization, and event loop. dispatch.c -- Accepts connections and hands them off to protocol code. spipe/* -- Code specific to the spipe utility. main.c -- Command-line parsing, initialization, and event loop. pushbits.c -- Copies data between standard input/output and a socket. proto/* -- Implements the spiped protocol. _conn.c -- Manages the lifecycle of a connection. _handshake.c -- Performs the handshaking portion of the protocol. _pipe.c -- Performs the data-shuttling portion of the protocol. _crypt.c -- Does the cryptographic bits needed by _handshake and _pipe. lib/dnsthread -- Spawns a thread for background DNS (re)resolution. libcperciva/* -- Library code from libcperciva ``` More info --------- For more details about spiped, read the [DESIGN.md](DESIGN.md) file. spiped-1.6.2/tests/000755 001751 001751 00000000000 14154500003 015572 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/000755 001751 001751 00000000000 14154500003 016713 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/COPYRIGHT000644 001751 001751 00000003236 14154567140 015746 0ustar00cpercivacperciva000000 000000 The included code and documentation ("spiped") is distributed under the following terms: Copyright 2005-2021 Colin Percival. All rights reserved. Copyright 2011-2021 Tarsnap Backup Inc. All rights reserved. Copyright 2014 Sean Kelly. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. In addition to the above, some files are: Copyright 2012 Andreas Olsson Copyright 2016 Tim Duesterhus and distributed under the same terms. Such files contain individual copyright statements and licenses. spiped-1.6.2/spiped/000755 001751 001751 00000000000 14161420052 015717 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/DESIGN.md000644 001751 001751 00000011520 13042745752 015743 0ustar00cpercivacperciva000000 000000 spiped design ============= Encrypted protocol ------------------ The client and server share a key file with 256 or more bits of entropy. On launch, they read the key file and compute K = SHA256(key file). When a connection is established: - C1. The client generates a 256-bit random value nonce_C and sends it. S1. The server generates a 256-bit random value nonce_S and sends it. - C2. The client receives a 256-bit value nonce_S. S2. The server receives a 256-bit value nonce_C. - C3/S3. Both parties now compute the 512-bit value dk_1 = PBKDF2-SHA256(K, nonce_C || nonce_S, 1) and parse it as a pair of 256-bit values dhmac_C || dhmac_S = dk_1. - C4. The client picks* a value x_C and computes** y_C = 2^x_C mod p, where p is the Diffie-Hellman "group #14" modulus, and h_C = HMAC-SHA256(dhmac_C, y_C). The client sends y_C || h_C to the server. S4. The server receives a 2304-bit value which it parses as y_C || h_C, where y_C is 2048 bits and h_C is 256 bits; and drops the connection if h_C is not equal to HMAC-SHA256(dhmac_C, y_C) or y_C >= p. - S5. The server picks\* a value x_S and computes\*\* y_S = 2^x_S mod p and h_S = HMAC-SHA256(dhmac_S, y_S). The server sends y_S || h_S to the client. C5. The client receives a 2304-bit value which it parses as y_S || h_S, where y_S is 2048 bits and h_S is 256 bits; and drops the connection if h_S is not equal to HMAC-SHA256(dhmac_S, y_S) or y_S >= p. - C6. The client computes\*\* y_SC = y_S^x_C mod p. S6. The server computes\*\* y_SC = y_C^x_S mod p. (Note that these two compute values are identical.) - C7/S7. Both parties now compute the 1024-bit value dk_2 = PBKDF2-SHA256(K, nonce_C || nonce_S || y_SC, 1) and parse it as a 4-tuple of 256-bit values E_C || H_C || E_S || H_S. Thereafter, the client and server exchange 1060-byte packets P generated from plaintext messages M of 1--1024 bytes msg_padded = M || ( 0x00 x (1024 - length(M))) || bigendian32(length(M)) msg_encrypted = AES256-CTR(E, msg_padded, packet#) P = msg_encrypted || HMAC-SHA256(H, msg_encrypted || bigendian64(packet#)) where E and H are E_C and H_C or E_S and H_S depending on whether the packet is being sent by the client or the server, and AES256-CTR is computed with nonce equal to the packet #, which starts at zero and increments for each packet sent in the same direction. \* The values x_C, x_S picked must either be 0 (if perfect forward secrecy is not desired) or have 256 bits of entropy (if perfect forward secrecy is desired). \*\* The values y_C, y_S, and y_SC are 2048 bits and big-endian. Security proof -------------- 1. Under the random oracle model, K has at least 255 bits of entropy (it's a 256-bit hash computed from a value with at least 256 bits of entropy). 2. Provided that at least one party is following the protocol and the key file has been used for fewer than 2^64 connections, the probability that the tuple (K, nonce_C, nonce_S) has occurred before is less than 2^(-192). 3. Under the random oracle model, the probability of an attacker without access to K guessing either of dhmac_C and dhmac_S is less than P(attacker guesses K) + P(the tuple has been input to the oracle before) + P(the attacker directly guesses), which is less than 2^(-255) + 2^(-192) + 2^(-255) = 2^(-192) + 2^(-254). 4. Consequently, in order for an attacker to convince a protocol-obeying party that a tuple (y, h) is legitimate, the attacker must do at least 2^190 expected work (which we consider to be computationally infeasible and do not consider any further). 5. If one of the parties opts to not have perfect forward secrecy, then the value y_SC will be equal to 1 and dk_2 will have the same security properties as dk_1, i.e., it will be computationally infeasible for an attacker without access to K to compute dk_2. 6. If both parties opt for perfect forward secrecy, an attacker who can compute y_SC has solved a Diffie-Hellman problem over the 2048-bit group #14, which is (under the CDH assumption) computationally infeasible. 7. Consequently, if both parties opt for perfect forward secrecy, an attacker who obtains access to K after the handshake has completed will continue to be unable to compute dk_2 from information exchanged during the handshake. 8. Under the random oracle model, the packets P are indistinguishable from random 1060-byte packets; thus no information about the keys used or the plaintext being transmitted is revealed by post-key-exchange communications. 9. Because the values (msg_encrypted || bigendian(packet#)) are distinct for each packet, under the random oracle model it is infeasible for an attacker without access to the value H to generate a packet which will be accepted as valid. spiped-1.6.2/spiped/spiped.1000644 001751 001751 00000012167 14161420052 017274 0ustar00cpercivacperciva000000 000000 .\"- .\" Copyright (c) 2012 Andreas Olsson .\" Copyright (c) 2016 Tim Duesterhus .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .TH SPIPED 1 "December 24, 2021" "spiped 1.6.2" "spiped README" .SH NAME spiped - secure pipe daemon .SH SYNOPSIS .B spiped {\-e | \-d} \-s \-t \-k .br [\-DFj] [\-f | \-g] [\-n ] [\-o ] .br [\-p ] [\-r | \-R] [\-\-syslog] .br [\-u | <:groupname> | ] .br .B spiped \-v .SH OPTIONS .TP .B \-e Take unencrypted connections from the .I source socket and send encrypted connections to the .IR "target socket" . .TP .B \-d Take encrypted connections from the .I source socket and send unencrypted connections to the .IR "target socket" . .TP .B \-s Address on which spiped should listen for incoming connections. The accepted formats are the same as the ones accepted by .IR "target socket" . Note that contrary to .I target socket hostnames are resolved when .B spiped is launched and are not re-resolved later; thus if DNS entries change .B spiped will continue to accept connections at the expired address. .TP .B \-t Address to which .B spiped should connect. Must be in one of the following formats: .IP \(bu /absolute/path/to/unix/socket .IP \(bu host.name:port .IP \(bu [ip.v4.ad.dr]:port .IP \(bu [ipv6::addr]:port .IP Hostnames are re-resolved every .I rtime seconds. .TP .B \-k Use the provided key file to authenticate and encrypt. Pass "\-" to read from standard input. .TP .B \-D Wait for DNS. Normally when .B spiped is launched it resolves addresses and binds to its .I source socket before the parent process returns; with this option it will daemonize first and retry failed DNS lookups until they succeed. This allows .B spiped to launch even if DNS isn't set up yet, but at the expense of losing the guarantee that once .B spiped has finished launching it will be ready to create pipes. .TP .B \-f Use fast/weak handshaking: This reduces the CPU time spent in the initial connection setup by disabling the Diffie-Hellman handshake, at the expense of losing perfect forward secrecy. .TP .B \-g Require perfect forward secrecy by dropping connections if the other host is using the \-f option. .TP .B \-F Run in foreground. This can be useful with systems like daemontools. .TP .B \-j Disable transport layer keep-alives. (By default they are enabled.) .TP .B \-n Limit on the number of simultaneous connections allowed. A value of 0 indicates that no limit should be imposed; this may be inadvisable in some circumstances, since .B spiped will terminate if it fails to allocate memory for handling a new connection. Defaults to 100 connections. .TP .B \-o Timeout, in seconds, after which an attempt to connect to the target or a protocol handshake will be aborted (and the connection dropped) if not completed. Defaults to 5s. .TP .B \-p File to which .BR spiped 's process ID should be written. Defaults to .IR "source socket" .pid (in the current directory if .I source socket is not an absolute path). No file will be written if -F (run in foreground) is used. .TP .B \-r Re-resolve the address of .I target socket every .I rtime seconds. Defaults to re-resolution every 60 seconds. .TP .B \-R Disable target address re-resolution. .TP .B \-\-syslog After daemonizing, send warnings to syslog instead of stderr. Has no effect if -F (run in foreground) is used. .TP .B \-u | <:groupname> | After binding a socket, change the user to .I username and/or the group to .IR groupname . .TP .B \-v Print version number. .SH SIGNALS spiped provides special treatment of the following signals: .TP .B SIGTERM On receipt of the .I SIGTERM signal .B spiped will stop accepting new connections and exit once there are no active connections left. .SH SEE ALSO .BR spipe (1). spiped-1.6.2/spiped/dispatch.h000644 001751 001751 00000003355 13762775424 017723 0ustar00cpercivacperciva000000 000000 #ifndef _DISPATCH_H_ #define _DISPATCH_H_ #include /* Opaque structures. */ struct proto_secret; struct sock_addr; /** * dispatch_accept(s, tgt, rtime, sas, decr, nopfs, requirepfs, nokeepalive, K, * nconn_max, timeo, conndone): * Start accepting connections on the socket ${s}. Connect to the target * ${tgt}, re-resolving it every ${rtime} seconds if ${rtime} > 0; on address * resolution failure use the most recent successfully obtained addresses, or * the addresses ${sas}. If ${decr} is 0, encrypt the outgoing connections; if * ${decr} is non-zero, decrypt the incoming connections. Don't accept more * than ${nconn_max} connections. If ${nopfs} is non-zero, don't use perfect * forward secrecy. If ${requirepfs} is non-zero, require that both ends use * perfect forward secrecy. Enable transport layer keep-alives (if applicable) * if and only if ${nokeepalive} is zero. Drop connections if the handshake or * connecting to the target takes more than ${timeo} seconds. If * dispatch_request_shutdown() is called then ${conndone} is set to a non-zero * value as soon as there are no active connections. Return a cookie which can * be passed to dispatch_shutdown() and dispatch_request_shutdown(). */ void * dispatch_accept(int, const char *, double, struct sock_addr **, int, int, int, int, const struct proto_secret *, size_t, double, int *); /** * dispatch_shutdown(dispatch_cookie): * Stop the server, free memory, and close the listening socket. */ void dispatch_shutdown(void *); /** * dispatch_request_shutdown(dispatch_cookie): * Request a shutdown: Stop accepting new connections and notify once * every existing connection ended. */ void dispatch_request_shutdown(void *); #endif /* !_DISPATCH_H_ */ spiped-1.6.2/spiped/Makefile000644 001751 001751 00000004200 14161420051 017352 0ustar00cpercivacperciva000000 000000 .POSIX: # AUTOGENERATED FILE, DO NOT EDIT PROG=spiped MAN1=spiped.1 SRCS=main.c dispatch.c IDIRS=-I../libcperciva/crypto -I../libcperciva/events -I../libcperciva/network -I../libcperciva/util -I../lib/dnsthread -I../lib/proto -I../lib/util LDADD_REQ=-lcrypto -lpthread SUBDIR_DEPTH=.. RELATIVE_DIR=spiped LIBALL=../liball/liball.a all: if [ -z "$${HAVE_BUILD_FLAGS}" ]; then \ cd ${SUBDIR_DEPTH}; \ ${MAKE} BUILD_SUBDIR=${RELATIVE_DIR} \ BUILD_TARGET=${PROG} buildsubdir; \ else \ ${MAKE} ${PROG}; \ fi install:${PROG} mkdir -p ${BINDIR} cp ${PROG} ${BINDIR}/_inst.${PROG}.$$$$_ && \ strip ${BINDIR}/_inst.${PROG}.$$$$_ && \ chmod 0555 ${BINDIR}/_inst.${PROG}.$$$$_ && \ mv -f ${BINDIR}/_inst.${PROG}.$$$$_ ${BINDIR}/${PROG} if ! [ -z "${MAN1DIR}" ]; then \ mkdir -p ${MAN1DIR}; \ for MPAGE in ${MAN1}; do \ cp $$MPAGE ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ chmod 0444 ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ mv -f ${MAN1DIR}/_inst.$$MPAGE.$$$$_ ${MAN1DIR}/$$MPAGE; \ done; \ fi clean: rm -f ${PROG} ${SRCS:.c=.o} ${PROG}:${SRCS:.c=.o} ${LIBALL} ${CC} -o ${PROG} ${SRCS:.c=.o} ${LIBALL} ${LDFLAGS} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} main.o: main.c ../libcperciva/util/asprintf.h ../libcperciva/util/daemonize.h ../libcperciva/events/events.h ../libcperciva/util/getopt.h ../lib/util/graceful_shutdown.h ../libcperciva/util/parsenum.h ../libcperciva/util/setuidgid.h ../libcperciva/util/sock.h ../libcperciva/util/warnp.h dispatch.h ../lib/proto/proto_crypt.h ../libcperciva/crypto/crypto_dh.h ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c main.c -o main.o dispatch.o: dispatch.c ../lib/dnsthread/dnsthread.h ../libcperciva/events/events.h ../libcperciva/network/network.h ../libcperciva/util/sock.h ../libcperciva/util/sock_util.h ../libcperciva/util/warnp.h ../lib/proto/proto_conn.h dispatch.h ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c dispatch.c -o dispatch.o spiped-1.6.2/spiped/main.c000644 001751 001751 00000017474 14161420052 017024 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include #include "asprintf.h" #include "daemonize.h" #include "events.h" #include "getopt.h" #include "graceful_shutdown.h" #include "parsenum.h" #include "setuidgid.h" #include "sock.h" #include "warnp.h" #include "dispatch.h" #include "proto_crypt.h" static void usage(void) { fprintf(stderr, "usage: spiped {-e | -d} -s " "-t -k \n" " [-DFj] [-f | -g] [-n ] " "[-o ]\n" " [-p ] [-r | -R] [--syslog]\n" " [-u { | <:groupname> | }]\n" " spiped -v\n"); exit(1); } static int callback_graceful_shutdown(void * dispatch_cookie) { dispatch_request_shutdown(dispatch_cookie); /* Success! */ return (0); } /* * Signal handler for SIGINT to perform a hard shutdown. */ static void diediedie_handler(int signo) { (void)signo; /* UNUSED */ _exit(0); } /* Simplify error-handling in command-line parse loop. */ #define OPT_EPARSE(opt, arg) do { \ warnp("Error parsing argument: %s %s", opt, arg); \ exit(1); \ } while (0) int main(int argc, char * argv[]) { /* Command-line parameters. */ int opt_d = 0; int opt_D = 0; int opt_e = 0; int opt_f = 0; int opt_g = 0; int opt_F = 0; int opt_j = 0; const char * opt_k = NULL; int opt_n_set = 0; size_t opt_n = 0; int opt_o_set = 0; double opt_o = 0.0; const char * opt_p = NULL; int opt_r_set = 0; double opt_r = 0.0; int opt_R = 0; int opt_syslog = 0; const char * opt_s = NULL; const char * opt_t = NULL; const char * opt_u = NULL; /* Working variables. */ struct sock_addr ** sas_s; struct sock_addr ** sas_t; struct proto_secret * K; const char * ch; char * pidfilename = NULL; int s; void * dispatch_cookie = NULL; int conndone = 0; WARNP_INIT; /* Parse the command line. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPT("-d"): if (opt_d || opt_e) usage(); opt_d = 1; break; GETOPT_OPT("-D"): if (opt_D) usage(); opt_D = 1; break; GETOPT_OPT("-e"): if (opt_d || opt_e) usage(); opt_e = 1; break; GETOPT_OPT("-f"): if (opt_f) usage(); opt_f = 1; break; GETOPT_OPT("-F"): if (opt_F) usage(); opt_F = 1; break; GETOPT_OPT("-g"): if (opt_g) usage(); opt_g = 1; break; GETOPT_OPT("-j"): if (opt_j) usage(); opt_j = 1; break; GETOPT_OPTARG("-k"): if (opt_k) usage(); opt_k = optarg; break; GETOPT_OPTARG("-n"): if (opt_n_set) usage(); opt_n_set = 1; if (PARSENUM(&opt_n, optarg)) OPT_EPARSE(ch, optarg); if (opt_n == 0) opt_n = SIZE_MAX; break; GETOPT_OPTARG("-o"): if (opt_o_set) usage(); opt_o_set = 1; if (PARSENUM(&opt_o, optarg, 0, INFINITY)) OPT_EPARSE(ch, optarg); break; GETOPT_OPTARG("-p"): if (opt_p) usage(); opt_p = optarg; break; GETOPT_OPTARG("-r"): if (opt_r_set) usage(); opt_r_set = 1; if (PARSENUM(&opt_r, optarg, 0, INFINITY)) OPT_EPARSE(ch, optarg); break; GETOPT_OPT("-R"): if (opt_R) usage(); opt_R = 1; break; GETOPT_OPTARG("-s"): if (opt_s) usage(); opt_s = optarg; break; GETOPT_OPT("--syslog"): if (opt_syslog) usage(); opt_syslog = 1; break; GETOPT_OPTARG("-t"): if (opt_t) usage(); opt_t = optarg; break; GETOPT_OPTARG("-u"): if (opt_u != NULL) usage(); opt_u = optarg; break; GETOPT_OPT("-v"): fprintf(stderr, "spiped 1.6.2\n"); exit(0); GETOPT_MISSING_ARG: warn0("Missing argument to %s", ch); usage(); GETOPT_DEFAULT: warn0("illegal option -- %s", ch); usage(); } } argc -= optind; argv += optind; /* We should have processed all the arguments. */ if (argc != 0) usage(); (void)argv; /* argv is not used beyond this point. */ /* Set defaults. */ if (!opt_n_set) opt_n = 100; if (opt_o == 0.0) opt_o = 5.0; if (opt_r == 0.0) opt_r = 60.0; /* Sanity-check options. */ if (!opt_d && !opt_e) usage(); if (opt_f && opt_g) usage(); if (opt_k == NULL) usage(); if (!(opt_o > 0.0)) usage(); if ((opt_r != 60.0) && opt_R) usage(); if (opt_s == NULL) usage(); if (opt_t == NULL) usage(); /* * A limit of SIZE_MAX connections is equivalent to any larger limit; * we'll be unable to allocate memory for socket bookkeeping before we * reach either. */ if (opt_n > SIZE_MAX) opt_n = SIZE_MAX; /* Figure out where our pid should be written. */ if (asprintf(&pidfilename, (opt_p != NULL) ? "%s" : "%s.pid", (opt_p != NULL) ? opt_p : opt_s) == -1) { warnp("asprintf"); goto err0; } /* Check whether we are running as init (e.g., inside a container). */ if (getpid() == 1) { /* https://github.com/docker/docker/issues/7086 */ warn0("WARNING: Applying workaround for Docker signal-handling bug"); /* Bind an explicit signal handler for SIGINT. */ if (signal(SIGINT, diediedie_handler) == SIG_ERR) { warnp("Failed to bind SIGINT signal handler"); } } /* Daemonize early if we're going to wait for DNS to be ready. */ if (opt_D && !opt_F) { if (daemonize(pidfilename)) { warnp("Failed to daemonize"); goto err1; } /* Send to syslog (if applicable). */ if (opt_syslog) warnp_syslog(1); } /* Resolve source address. */ while ((sas_s = sock_resolve(opt_s)) == NULL) { if (!opt_D) { warnp("Error resolving socket address: %s", opt_s); goto err1; } sleep(1); } if (sas_s[0] == NULL) { warn0("No addresses found for %s", opt_s); goto err2; } /* Resolve target address. */ while ((sas_t = sock_resolve(opt_t)) == NULL) { if (!opt_D) { warnp("Error resolving socket address: %s", opt_t); goto err2; } sleep(1); } if (sas_t[0] == NULL) { warn0("No addresses found for %s", opt_t); goto err3; } /* Load the keying data. */ if ((K = proto_crypt_secret(opt_k)) == NULL) { warnp("Error reading shared secret"); goto err3; } /* Create and bind a socket, and mark it as listening. */ if (sas_s[1] != NULL) warn0("Listening on first of multiple addresses found for %s", opt_s); if ((s = sock_listener(sas_s[0])) == -1) goto err4; /* Daemonize and write pid. */ if (!opt_D && !opt_F) { if (daemonize(pidfilename)) { warnp("Failed to daemonize"); goto err5; } /* Send to syslog (if applicable). */ if (opt_syslog) warnp_syslog(1); } /* Drop privileges (if applicable). */ if (opt_u && setuidgid(opt_u, SETUIDGID_SGROUP_LEAVE_WARN)) { warnp("Failed to drop privileges"); goto err5; } /* Start accepting connections. */ if ((dispatch_cookie = dispatch_accept(s, opt_t, opt_R ? 0.0 : opt_r, sas_t, opt_d, opt_f, opt_g, opt_j, K, opt_n, opt_o, &conndone)) == NULL) { warnp("Failed to initialize connection acceptor"); goto err5; } /* dispatch is now maintaining sas_t and s. */ sas_t = NULL; s = -1; /* Register a handler for SIGTERM. */ if (graceful_shutdown_initialize(&callback_graceful_shutdown, dispatch_cookie)) { warn0("Failed to start graceful_shutdown timer"); goto err6; } /* * Loop until an error occurs, or a connection closes if the * command-line argument -1 was given. */ if (events_spin(&conndone)) { warnp("Error running event loop"); goto err6; } /* Stop accepting connections and shut down the dispatcher. */ dispatch_shutdown(dispatch_cookie); /* Free the protocol secret structure. */ proto_crypt_secret_free(K); /* Free arrays of resolved addresses. */ sock_addr_freelist(sas_s); /* Free pid filename. */ free(pidfilename); /* Success! */ exit(0); err6: dispatch_shutdown(dispatch_cookie); err5: if (s != -1) close(s); err4: proto_crypt_secret_free(K); err3: sock_addr_freelist(sas_t); err2: sock_addr_freelist(sas_s); err1: free(pidfilename); err0: /* Failure! */ exit(1); } spiped-1.6.2/spiped/dispatch.c000644 001751 001751 00000020741 13762775424 017714 0ustar00cpercivacperciva000000 000000 #include #include #include #include "dnsthread.h" #include "events.h" #include "network.h" #include "sock.h" #include "sock_util.h" #include "warnp.h" #include "proto_conn.h" #include "dispatch.h" struct accept_state { int s; const char * tgt; struct sock_addr ** sas; double rtime; int decr; int nopfs; int requirepfs; int nokeepalive; int * conndone; int shutdown_requested; const struct proto_secret * K; size_t nconn; size_t nconn_max; double timeo; void * accept_cookie; void * dnstimer_cookie; struct conn_list_node * conn_cookies; DNSTHREAD T; }; /* Doubly linked list. */ struct conn_list_node { void * conn_cookie; struct conn_list_node * prev; struct conn_list_node * next; struct accept_state * A; }; static int callback_gotconn(void *, int); static int callback_resolveagain(void *); /* Callback from address resolution. */ static int callback_resolve(void * cookie, struct sock_addr ** sas) { struct accept_state * A = cookie; /* If the address resolution succeeded... */ if (sas != NULL) { /* Free the old addresses. */ sock_addr_freelist(A->sas); /* Use the new addresses. */ A->sas = sas; } /* Wait a while before resolving again. */ if ((A->dnstimer_cookie = events_timer_register_double( callback_resolveagain, A, A->rtime)) == NULL) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Timer callback to trigger another address resolution. */ static int callback_resolveagain(void * cookie) { struct accept_state * A = cookie; /* This timer is expired. */ A->dnstimer_cookie = NULL; /* Re-resolve the target address. */ return (dnsthread_resolveone(A->T, A->tgt, callback_resolve, A)); } /* Non-blocking accept, if we can have more connections. */ static int doaccept(struct accept_state * A) { int rc = 0; /* Warn about reaching nconn_max. */ if (A->nconn >= A->nconn_max) { warn0("Maximum number of connections (%zu) reached", A->nconn_max); } /* If we can, accept a new connection. */ if ((A->nconn < A->nconn_max) && (A->accept_cookie == NULL) && !A->shutdown_requested) { if ((A->accept_cookie = network_accept(A->s, callback_gotconn, A)) == NULL) rc = -1; } /* Return success/fail status. */ return (rc); } /* A connection has closed. Accept more if necessary. */ static int callback_conndied(void * cookie, int reason) { struct conn_list_node * node_ptr = cookie; struct accept_state * A = node_ptr->A; (void)reason; /* UNUSED */ /* We should always have a non-empty list of conn_cookies. */ assert(A->conn_cookies != NULL); /* We've lost a connection. */ A->nconn -= 1; /* Remove the closed connection from the list of conn_cookies. */ if (node_ptr == A->conn_cookies) { /* Closed conn_cookie is first in the list. */ A->conn_cookies = node_ptr->next; if (node_ptr->next != NULL) node_ptr->next->prev = NULL; } else { /* Closed conn_cookie is in the middle of list. */ assert(node_ptr->prev != NULL); node_ptr->prev->next = node_ptr->next; if (node_ptr->next != NULL) node_ptr->next->prev = node_ptr->prev; } /* Clean up the now-unused node. */ free(node_ptr); /* If requested to do so, indicate that all connections are closed. */ if (A->shutdown_requested && (A->nconn == 0)) *A->conndone = 1; /* Maybe accept more connections. */ return (doaccept(A)); } /* Handle an incoming connection. */ static int callback_gotconn(void * cookie, int s) { struct accept_state * A = cookie; struct sock_addr ** sas; struct conn_list_node * node_new; /* This accept is no longer in progress. */ A->accept_cookie = NULL; /* If we got a -1 descriptor, something went seriously wrong. */ if (s == -1) { warnp("network_accept failed"); goto err0; } /* We have gained a connection. */ A->nconn += 1; /* Duplicate the target address list. */ if ((sas = sock_addr_duplist(A->sas)) == NULL) goto err1; /* Create new conn_list_node. */ if ((node_new = malloc(sizeof(struct conn_list_node))) == NULL) goto err2; node_new->prev = NULL; node_new->next = NULL; node_new->A = A; /* Create a new connection. */ if ((node_new->conn_cookie = proto_conn_create(s, sas, A->decr, A->nopfs, A->requirepfs, A->nokeepalive, A->K, A->timeo, callback_conndied, node_new)) == NULL) { warnp("Failure setting up new connection"); goto err3; } /* Link node_new to the beginning of the conn_cookies list. */ if (A->conn_cookies != NULL) { node_new->next = A->conn_cookies; node_new->next->prev = node_new; } /* Insert node_new to the beginning of the conn_cookies list. */ A->conn_cookies = node_new; /* Accept another connection if we can. */ if (doaccept(A)) goto err0; /* Success! */ return (0); err3: free(node_new); err2: sock_addr_freelist(sas); err1: A->nconn -= 1; close(s); err0: /* Failure! */ return (-1); } /** * dispatch_accept(s, tgt, rtime, sas, decr, nopfs, requirepfs, nokeepalive, K, * nconn_max, timeo, conndone): * Start accepting connections on the socket ${s}. Connect to the target * ${tgt}, re-resolving it every ${rtime} seconds if ${rtime} > 0; on address * resolution failure use the most recent successfully obtained addresses, or * the addresses ${sas}. If ${decr} is 0, encrypt the outgoing connections; if * ${decr} is non-zero, decrypt the incoming connections. Don't accept more * than ${nconn_max} connections. If ${nopfs} is non-zero, don't use perfect * forward secrecy. If ${requirepfs} is non-zero, require that both ends use * perfect forward secrecy. Enable transport layer keep-alives (if applicable) * if and only if ${nokeepalive} is zero. Drop connections if the handshake or * connecting to the target takes more than ${timeo} seconds. If * dispatch_request_shutdown() is called then ${conndone} is set to a non-zero * value as soon as there are no active connections. Return a cookie which can * be passed to dispatch_shutdown() and dispatch_request_shutdown(). */ void * dispatch_accept(int s, const char * tgt, double rtime, struct sock_addr ** sas, int decr, int nopfs, int requirepfs, int nokeepalive, const struct proto_secret * K, size_t nconn_max, double timeo, int * conndone) { struct accept_state * A; /* Bake a cookie. */ if ((A = malloc(sizeof(struct accept_state))) == NULL) goto err0; A->s = s; A->tgt = tgt; A->sas = sas; A->rtime = rtime; A->decr = decr; A->nopfs = nopfs; A->requirepfs = requirepfs; A->nokeepalive = nokeepalive; A->conndone = conndone; A->shutdown_requested = 0; A->K = K; A->nconn = 0; A->nconn_max = nconn_max; A->timeo = timeo; A->T = NULL; A->accept_cookie = NULL; A->dnstimer_cookie = NULL; A->conn_cookies = NULL; /* If address re-resolution is enabled... */ if (rtime > 0.0) { /* Launch an address resolution thread. */ if ((A->T = dnsthread_spawn()) == NULL) goto err1; /* Re-resolve the target address after a while. */ if ((A->dnstimer_cookie = events_timer_register_double( callback_resolveagain, A, A->rtime)) == NULL) goto err2; } /* Accept a connection. */ if (doaccept(A)) goto err3; /* Success! */ return (A); err3: if (A->dnstimer_cookie != NULL) events_timer_cancel(A->dnstimer_cookie); err2: if (A->T != NULL) dnsthread_kill(A->T); err1: free(A); err0: /* Failure! */ return (NULL); } /** * dispatch_shutdown(dispatch_cookie): * Stop the server, free memory, and close the listening socket. */ void dispatch_shutdown(void * dispatch_cookie) { struct accept_state * A = dispatch_cookie; /* * Shutdown any open connections. proto_conn_drop() will call * callback_conndied(), which removes the relevant conn_list_node from * the list of conn_cookies. */ while (A->conn_cookies != NULL) proto_conn_drop(A->conn_cookies->conn_cookie, PROTO_CONN_CANCELLED); if (A->accept_cookie != NULL) network_accept_cancel(A->accept_cookie); if (A->dnstimer_cookie != NULL) events_timer_cancel(A->dnstimer_cookie); if (A->T != NULL) dnsthread_kill(A->T); sock_addr_freelist(A->sas); close(A->s); free(A); } /** * dispatch_request_shutdown(dispatch_cookie): * Request a shutdown: Stop accepting new connections and notify once * every existing connection ended. */ void dispatch_request_shutdown(void * dispatch_cookie) { struct accept_state * A = dispatch_cookie; A->shutdown_requested = 1; /* Cancel any further accepts. */ if (A->accept_cookie != NULL) { network_accept_cancel(A->accept_cookie); A->accept_cookie = NULL; } /* If no connections are open... */ if (A->nconn == 0) { /* Indicate that all connections are closed. */ *A->conndone = 1; } } spiped-1.6.2/libcperciva/POSIX/000755 001751 001751 00000000000 14161417573 017636 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/cpusupport/000755 001751 001751 00000000000 14101630450 021141 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/crypto/000755 001751 001751 00000000000 14154500003 020233 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/events/000755 001751 001751 00000000000 13723620112 020224 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/alg/000755 001751 001751 00000000000 14154500003 017456 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/netbuf/000755 001751 001751 00000000000 14154500003 020176 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/network/000755 001751 001751 00000000000 14154500003 020404 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/util/000755 001751 001751 00000000000 14157142752 017710 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/datastruct/000755 001751 001751 00000000000 13723620112 021076 5ustar00cpercivacperciva000000 000000 spiped-1.6.2/libcperciva/datastruct/ptrheap.h000644 001751 001751 00000006336 13723620112 022722 0ustar00cpercivacperciva000000 000000 #ifndef _PTRHEAP_H_ #define _PTRHEAP_H_ #include /** * Pointer-heap data structure. Arbitrary pointers can be inserted and are * compared using a provided callback; the usual heapy getmin / increasemin / * deletemin algorithms are supported. To use three additional functions, * ptrheap_{delete, increase, decrease}, a setreccookie callback needs to be * provided. These functions require a record cookie to identify the element * to operate upon; each time a record's record cookie changes, the * setreccookie callback will be called. Functions return NULL or (int)(-1) * on error and set errno; other return types indicate that failure is not * possible. On error, the heap will be unmodified. */ /* Opaque pointer-heap type. */ struct ptrheap; /** * ptrheap_init(compar, setreccookie, cookie): * Create and return an empty heap. The function ${compar}(${cookie}, x, y) * should return less than, equal to, or greater than 0 depending on whether * x is less than, equal to, or greater than y; and if ${setreccookie} is * non-zero it will be called as ${setreccookie}(${cookie}, ${ptr}, ${rc}) to * indicate that the value ${rc} is the current record cookie for the pointer * ${ptr}. The function ${setreccookie} may not make any ptrheap_* calls. */ struct ptrheap * ptrheap_init(int (*)(void *, const void *, const void *), void (*)(void *, void *, size_t), void *); /** * ptrheap_create(compar, setreccookie, cookie, N, ptrs): * Create and return a heap, as in ptrheap_init(), but with the ${N} pointers * in ${ptrs} as heap elements. This is faster than creating an empty heap * and adding the elements individually. */ struct ptrheap * ptrheap_create(int (*)(void *, const void *, const void *), void (*)(void *, void *, size_t), void *, size_t, void **); /** * ptrheap_add(H, ptr): * Add the pointer ${ptr} to the heap ${H}. */ int ptrheap_add(struct ptrheap *, void *); /** * ptrheap_getmin(H): * Return the minimum pointer in the heap ${H}. If the heap is empty, NULL * is returned. */ void * ptrheap_getmin(struct ptrheap *); /** * ptrheap_delete(H, rc): * Delete from the heap ${H} the element ptr for which the function call * setreccookie(cookie, ptr, ${rc}) was most recently made. */ void ptrheap_delete(struct ptrheap *, size_t); /** * ptrheap_deletemin(H): * Delete the minimum element in the heap ${H}. The heap must not be empty. */ void ptrheap_deletemin(struct ptrheap *); /** * ptrheap_decrease(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has decreased. */ void ptrheap_decrease(struct ptrheap *, size_t); /** * ptrheap_increase(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has increased. */ void ptrheap_increase(struct ptrheap *, size_t); /** * ptrheap_increasemin(H): * Adjust the heap ${H} to account for the fact that the (formerly) minimum * element has increased. */ void ptrheap_increasemin(struct ptrheap *); /** * ptrheap_free(H): * Free the pointer heap ${H}. */ void ptrheap_free(struct ptrheap *); #endif /* !_PTRHEAP_H_ */ spiped-1.6.2/libcperciva/datastruct/elasticarray.h000644 001751 001751 00000017101 13723620112 023732 0ustar00cpercivacperciva000000 000000 #ifndef _ELASTICARRAY_H_ #define _ELASTICARRAY_H_ #include /** * Elastic Arrays are dynamically resizing arrays which remain within a * factor of 4 of the optimal size for the data they contain and have (within * a constant factor) amortized optimal running time providing that all of * the allocated space is accessed at some point. Functions return NULL or * (int)(-1) on error and set errno; other return types indicate that failure * is not possible. On error, the array will be unmodified. * * The ELASTICARRAY_DECL(type, prefix, rectype) macro can be used to create a * more friendly interface, at the expense of restricting the array to only * holding a single data type. */ /* Opaque elastic array type. */ struct elasticarray; /** * elasticarray_init(nrec, reclen): * Create and return an elastic array holding ${nrec} (uninitialized) records * of length ${reclen}. Takes O(nrec * reclen) time. The value ${reclen} * must be positive. */ struct elasticarray * elasticarray_init(size_t, size_t); /** * elasticarray_resize(EA, nrec, reclen): * Resize the elastic array pointed to by ${EA} to hold ${nrec} records of * length ${reclen}. If ${nrec} exceeds the number of records previously * held by the array, the additional records will be uninitialized. Takes * O(nrec * reclen) time. The value ${reclen} must be positive. */ int elasticarray_resize(struct elasticarray *, size_t, size_t); /** * elasticarray_getsize(EA, reclen): * Return the number of length-${reclen} records in the array, rounding down * if there is a partial record (which can only occur if elasticarray_* * functions have been called with different values of reclen). The value * ${reclen} must be positive. */ size_t elasticarray_getsize(struct elasticarray *, size_t); /** * elasticarray_append(EA, buf, nrec, reclen): * Append to the elastic array ${EA} the ${nrec} records of length ${reclen} * stored in ${buf}. Takes O(nrec * reclen) amortized time. The value * ${reclen} must be positive. */ int elasticarray_append(struct elasticarray *, const void *, size_t, size_t); /** * elasticarray_shrink(EA, nrec, reclen): * Delete the final ${nrec} records of length ${reclen} from the elastic * array ${EA}. If there are fewer than ${nrec} records, all records * present will be deleted. The value ${reclen} must be positive. * * As an exception to the normal rule, an elastic array may occupy more than * 4 times the optimal storage immediately following an elasticarray_shrink() * call; but only if realloc(3) failed to shrink a memory allocation. */ void elasticarray_shrink(struct elasticarray *, size_t, size_t); /** * elasticarray_truncate(EA): * Release any spare space in the elastic array ${EA}. */ int elasticarray_truncate(struct elasticarray *); /** * elasticarray_get(EA, pos, reclen): * Return a pointer to record number ${pos} of length ${reclen} in the * elastic array ${EA}. Takes O(1) time. */ void * elasticarray_get(struct elasticarray *, size_t, size_t); /** * elasticarray_iter(EA, reclen, fp): * Run the ${fp} function on every member of the array. The value ${reclen} * must be positive. */ void elasticarray_iter(struct elasticarray *, size_t, void(*)(void *)); /** * elasticarray_free(EA): * Free the elastic array ${EA}. Takes O(1) time. */ void elasticarray_free(struct elasticarray *); /** * elasticarray_export(EA, buf, nrec, reclen): * Return the data in the elastic array ${EA} as a buffer ${buf} containing * ${nrec} records of length ${reclen}. Free the elastic array ${EA}. * The value ${reclen} must be positive. */ int elasticarray_export(struct elasticarray *, void **, size_t *, size_t); /** * elasticarray_exportdup(EA, buf, nrec, reclen): * Duplicate the data in the elastic array ${EA} into a buffer ${buf} * containing ${nrec} records of length ${reclen}. (Same as _export, except * that the elastic array remains intact.) The value ${reclen} must be * positive. */ int elasticarray_exportdup(struct elasticarray *, void **, size_t *, size_t); /** * ELASTICARRAY_DECL(type, prefix, rectype): * Declare the type ${type} and the following functions: * ${type} ${prefix}_init(size_t nrec); * int ${prefix}_resize(${type} EA, size_t nrec); * size_t ${prefix}_getsize(${type} EA); * int ${prefix}_append(${type} EA, const void * buf, size_t nrec); * void ${prefix}_shrink(${type} EA, size_t nrec); * int ${prefix}_truncate(${type} EA); * ${rectype} * ${prefix}_get(${type} EA, size_t pos); * void ${prefix}_iter(${type} EA, void(*)(void *)); * void ${prefix}_free(${type} EA); */ #define ELASTICARRAY_DECL(type, prefix, rectype) \ static inline struct prefix##_struct * \ prefix##_init(size_t nrec) \ { \ struct elasticarray * EA; \ \ EA = elasticarray_init(nrec, sizeof(rectype)); \ return ((struct prefix##_struct *)EA); \ } \ static inline int \ prefix##_resize(struct prefix##_struct * EA, size_t nrec) \ { \ return (elasticarray_resize((struct elasticarray *)EA, \ nrec, sizeof(rectype))); \ } \ static inline size_t \ prefix##_getsize(struct prefix##_struct * EA) \ { \ return (elasticarray_getsize((struct elasticarray *)EA, \ sizeof(rectype))); \ } \ static inline int \ prefix##_append(struct prefix##_struct * EA, \ rectype const * buf, size_t nrec) \ { \ return (elasticarray_append((struct elasticarray *)EA, \ buf, nrec, sizeof(rectype))); \ } \ static inline void \ prefix##_shrink(struct prefix##_struct * EA, size_t nrec) \ { \ elasticarray_shrink((struct elasticarray *)EA, \ nrec, sizeof(rectype)); \ } \ static inline int \ prefix##_truncate(struct prefix##_struct * EA) \ { \ return (elasticarray_truncate( \ (struct elasticarray *)EA)); \ } \ static inline rectype * \ prefix##_get(struct prefix##_struct * EA, size_t pos) \ { \ rectype * rec; \ \ rec = elasticarray_get((struct elasticarray *)EA, \ pos, sizeof(rectype)); \ return (rec); \ } \ static inline void \ prefix##_iter(struct prefix##_struct * EA, \ void(* free_fp)(rectype *)) \ { \ elasticarray_iter((struct elasticarray *)EA, \ sizeof(rectype), (void (*)(void *))free_fp); \ } \ static inline void \ prefix##_free(struct prefix##_struct * EA) \ { \ elasticarray_free((struct elasticarray *)EA); \ } \ static inline int \ prefix##_export(struct prefix##_struct * EA, rectype ** buf, \ size_t * nrec) \ { \ return (elasticarray_export((struct elasticarray *)EA, \ (void **)buf, nrec, sizeof(rectype))); \ } \ static inline int \ prefix##_exportdup(struct prefix##_struct * EA, rectype ** buf, \ size_t * nrec) \ { \ return ( \ elasticarray_exportdup((struct elasticarray *)EA, \ (void **)buf, nrec, sizeof(rectype))); \ } \ static void (* prefix##_dummyptr)(void); \ static inline void \ prefix##_dummy(void) \ { \ \ (void)prefix##_init; \ (void)prefix##_resize; \ (void)prefix##_getsize; \ (void)prefix##_append; \ (void)prefix##_shrink; \ (void)prefix##_truncate; \ (void)prefix##_get; \ (void)prefix##_iter; \ (void)prefix##_free; \ (void)prefix##_export; \ (void)prefix##_exportdup; \ (void)prefix##_dummyptr; \ } \ static void (* prefix##_dummyptr)(void) = prefix##_dummy; \ typedef struct prefix##_struct * type #endif /* !_ELASTICARRAY_H_ */ spiped-1.6.2/libcperciva/datastruct/timerqueue.h000644 001751 001751 00000003336 13723620112 023441 0ustar00cpercivacperciva000000 000000 #ifndef _TIMERQUEUE_H_ #define _TIMERQUEUE_H_ #include /* Timer priority queue. Contains (timeval, ptr) pairs. */ /* Opaque timer priority queue type. */ struct timerqueue; /** * timerqueue_init(void): * Create and return an empty timer priority queue. */ struct timerqueue * timerqueue_init(void); /** * timerqueue_add(Q, tv, ptr): * Add the pair (${tv}, ${ptr}) to the priority queue ${Q}. Return a cookie * which can be passed to timerqueue_delete() or timerqueue_increase(). */ void * timerqueue_add(struct timerqueue *, const struct timeval *, void *); /** * timerqueue_delete(Q, cookie): * Delete the (timeval, ptr) pair associated with the cookie ${cookie} from * the priority queue ${Q}. */ void timerqueue_delete(struct timerqueue *, void *); /** * timerqueue_increase(Q, cookie, tv): * Increase the timer associated with the cookie ${cookie} in the priority * queue ${Q} to ${tv}. */ void timerqueue_increase(struct timerqueue *, void *, const struct timeval *); /** * timerqueue_getmin(Q): * Return a pointer to the least timeval in ${Q}, or NULL if the priority * queue is empty. The pointer will remain valid until the next call to a * timerqueue_* function. This function cannot fail. */ const struct timeval * timerqueue_getmin(struct timerqueue *); /** * timerqueue_getptr(Q, tv): * If the least timeval in ${Q} is less than or equal to ${tv}, return the * associated pointer and remove the pair from the priority queue. If not, * return NULL. This function cannot fail. */ void * timerqueue_getptr(struct timerqueue *, const struct timeval *); /** * timerqueue_free(Q): * Free the timer priority queue ${Q}. */ void timerqueue_free(struct timerqueue *); #endif /* !_TIMERQUEUE_H_ */ spiped-1.6.2/libcperciva/datastruct/mpool.h000644 001751 001751 00000005777 13556104127 022424 0ustar00cpercivacperciva000000 000000 #ifndef _MPOOL_H_ #define _MPOOL_H_ #include #include #include /** * Memory allocator cache. Memory allocations can be returned to the pool * and reused by a subsequent allocation without returning all the way to * free/malloc. In effect, this is an optimization for the case where we * know we will want another allocation of the same size soon, at the expense * of keeping memory allocated (and thus preventing any other code from * allocating the same memory). */ /* Internal data. */ struct mpool { size_t stacklen; size_t allocsize; void ** allocs; uint64_t nallocs; uint64_t nempties; int state; void ** allocs_static; void (*atexitfunc)(void); }; static inline void mpool_atexit(struct mpool * M) { while (M->stacklen) free(M->allocs[--M->stacklen]); if (M->allocs != M->allocs_static) free(M->allocs); } static inline void * mpool_malloc(struct mpool * M, size_t len) { M->nallocs++; if (M->stacklen) return (M->allocs[--(M->stacklen)]); M->nempties++; if (M->state == 0) { atexit(M->atexitfunc); M->state = 1; } return (malloc(len)); } static inline void mpool_free(struct mpool * M, void * p) { void ** allocs_new; if (p == NULL) return; if (M->stacklen < M->allocsize) { M->allocs[M->stacklen++] = p; return; } if (M->nempties > (M->nallocs >> 8)) { allocs_new = (void **)malloc(M->allocsize * 2 * sizeof(void *)); if (allocs_new) { memcpy(allocs_new, M->allocs, M->allocsize * sizeof(void *)); if (M->allocs != M->allocs_static) free(M->allocs); M->allocs = allocs_new; M->allocsize = M->allocsize * 2; M->allocs[M->stacklen++] = p; } else free(p); } else free(p); M->nempties = 0; M->nallocs = 0; } /** * MPOOL(name, type, size): * Define the functions * * ${type} * mpool_${name}_malloc(void); * void mpool_${name}_free(${type} *); * * which allocate and free structures of type ${type}. A minimum of ${size} * such structures are kept cached after _free is called in order to allow * future _malloc calls to be rapidly serviced; this limit will be autotuned * upwards depending on the allocation/free pattern. * * Cached structures will be freed at program exit time in order to aid * in the detection of memory leaks. */ #define MPOOL(name, type, size) \ static void mpool_##name##_atexit(void); \ static void * mpool_##name##_static[size]; \ static struct mpool mpool_##name##_rec = \ {0, size, mpool_##name##_static, 0, 0, 0, \ mpool_##name##_static, mpool_##name##_atexit}; \ \ static void \ mpool_##name##_atexit(void) \ { \ \ mpool_atexit(&mpool_##name##_rec); \ } \ \ static inline type * \ mpool_##name##_malloc(void) \ { \ \ return (mpool_malloc(&mpool_##name##_rec, sizeof(type))); \ } \ \ static inline void \ mpool_##name##_free(type * p) \ { \ \ mpool_free(&mpool_##name##_rec, p); \ } \ \ struct mpool_##name##_dummy #endif /* !_MPOOL_H_ */ spiped-1.6.2/libcperciva/datastruct/elasticarray.c000644 001751 001751 00000017717 13723620112 023742 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "elasticarray.h" struct elasticarray { size_t size; size_t alloc; void * buf; }; /** * resize(EA, nsize): * Resize the virtual buffer for ${EA} to length ${nsize} bytes. The actual * buffer may or may not need to be resized. On failure, the buffer will be * unmodified. */ static int resize(struct elasticarray * EA, size_t nsize) { size_t nalloc; void * nbuf; /* Figure out how large an allocation we want. */ if (EA->alloc < nsize) { /* We need to enlarge the buffer. */ nalloc = EA->alloc * 2; if (nalloc < nsize) nalloc = nsize; } else if (EA->alloc > nsize * 4) { /* We need to shrink the buffer. */ nalloc = nsize * 2; } else { nalloc = EA->alloc; } /* * Resizing to zero needs special handling due to some strange * interpretations of what realloc(p, 0) should do if it fails * to allocate zero bytes of memory. */ if (nalloc == 0) { /* We can only get here if we set nsize to 0. */ assert(nsize == 0); free(EA->buf); EA->buf = NULL; EA->alloc = 0; } else if (nalloc != EA->alloc) { /* Reallocate to a nonzero size if necessary. */ if ((nbuf = realloc(EA->buf, nalloc)) == NULL) goto err0; EA->buf = nbuf; EA->alloc = nalloc; } /* Record the new array size. */ EA->size = nsize; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_init(nrec, reclen): * Create and return an elastic array holding ${nrec} (uninitialized) records * of length ${reclen}. Takes O(nrec * reclen) time. The value ${reclen} * must be positive. */ struct elasticarray * elasticarray_init(size_t nrec, size_t reclen) { struct elasticarray * EA; /* Sanity check. */ assert(reclen > 0); /* Allocate structure. */ if ((EA = malloc(sizeof(struct elasticarray))) == NULL) goto err0; /* The array is empty for now. */ EA->size = EA->alloc = 0; EA->buf = NULL; /* Reallocate to the requested length. */ if (elasticarray_resize(EA, nrec, reclen)) goto err1; /* Success! */ return (EA); err1: elasticarray_free(EA); err0: /* Failure! */ return (NULL); } /** * elasticarray_resize(EA, nrec, reclen): * Resize the elastic array pointed to by ${EA} to hold ${nrec} records of * length ${reclen}. If ${nrec} exceeds the number of records previously * held by the array, the additional records will be uninitialized. Takes * O(nrec * reclen) time. The value ${reclen} must be positive. */ int elasticarray_resize(struct elasticarray * EA, size_t nrec, size_t reclen) { /* Check for overflow. */ if (nrec > SIZE_MAX / reclen) { errno = ENOMEM; goto err0; } /* Resize the buffer. */ if (resize(EA, nrec * reclen)) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_getsize(EA, reclen): * Return the number of length-${reclen} records in the array, rounding down * if there is a partial record (which can only occur if elasticarray_* * functions have been called with different values of reclen). The value * ${reclen} must be positive. */ size_t elasticarray_getsize(struct elasticarray * EA, size_t reclen) { return (EA->size / reclen); } /** * elasticarray_append(EA, buf, nrec, reclen): * Append to the elastic array ${EA} the ${nrec} records of length ${reclen} * stored in ${buf}. Takes O(nrec * reclen) amortized time. The value * ${reclen} must be positive. */ int elasticarray_append(struct elasticarray * EA, const void * buf, size_t nrec, size_t reclen) { size_t bufpos = EA->size; size_t nsize; /* Check for overflow. */ if ((nrec > SIZE_MAX / reclen) || (nrec * reclen > SIZE_MAX - EA->size)) { errno = ENOMEM; goto err0; } /* Resize the buffer. */ nsize = EA->size + nrec * reclen; if (resize(EA, nsize)) goto err0; /* Copy bytes in. */ if (nrec > 0) { /* We shouldn't have requested a 0-size array. */ assert(nsize > 0); /* Copy bytes in. */ memcpy((uint8_t *)(EA->buf) + bufpos, buf, nrec * reclen); } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_shrink(EA, nrec, reclen): * Delete the final ${nrec} records of length ${reclen} from the elastic * array ${EA}. If there are fewer than ${nrec} records, all records * present will be deleted. The value ${reclen} must be positive. * * As an exception to the normal rule, an elastic array may occupy more than * 4 times the optimal storage immediately following an elasticarray_shrink() * call; but only if realloc(3) failed to shrink a memory allocation. */ void elasticarray_shrink(struct elasticarray * EA, size_t nrec, size_t reclen) { size_t nsize; /* Figure out how much to keep. */ if ((nrec > SIZE_MAX / reclen) || (nrec * reclen > EA->size)) nsize = 0; else nsize = EA->size - nrec * reclen; /* Resize the buffer... */ if (resize(EA, nsize)) { /* * ... and if we fail to reallocate, just record the new * length and continue using the old buffer. */ EA->size = nsize; } } /** * elasticarray_truncate(EA): * Release any spare space in the elastic array ${EA}. */ int elasticarray_truncate(struct elasticarray * EA) { void * nbuf; /* * Truncating down to zero needs special handling due to some strange * interpretations of what realloc(p, 0) should do if it fails to * allocate zero bytes of memory. */ if (EA->size == 0) { free(EA->buf); EA->buf = NULL; EA->alloc = 0; } else if (EA->alloc > EA->size) { /* Reallocate to eliminate spare space if necessary. */ if ((nbuf = realloc(EA->buf, EA->size)) == NULL) goto err0; EA->buf = nbuf; EA->alloc = EA->size; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_get(EA, pos, reclen): * Return a pointer to record number ${pos} of length ${reclen} in the * elastic array ${EA}. Takes O(1) time. */ void * elasticarray_get(struct elasticarray * EA, size_t pos, size_t reclen) { return ((uint8_t *)(EA->buf) + pos * reclen); } /** * elasticarray_iter(EA, reclen, fp): * Run the ${fp} function on every member of the array. The value ${reclen} * must be positive. */ void elasticarray_iter(struct elasticarray * EA, size_t reclen, void(* fp)(void *)) { size_t i; /* Apply the function to every item in the list. */ for (i = 0; i < elasticarray_getsize(EA, reclen); i++) fp(elasticarray_get(EA, i, reclen)); } /** * elasticarray_free(EA): * Free the elastic array ${EA}. Takes O(1) time. */ void elasticarray_free(struct elasticarray * EA) { /* Behave consistently with free(NULL). */ if (EA == NULL) return; free(EA->buf); free(EA); } /** * elasticarray_export(EA, buf, nrec, reclen): * Return the data in the elastic array ${EA} as a buffer ${buf} containing * ${nrec} records of length ${reclen}. Free the elastic array ${EA}. * The value ${reclen} must be positive. */ int elasticarray_export(struct elasticarray * EA, void ** buf, size_t * nrec, size_t reclen) { /* Remove any spare space. */ if (elasticarray_truncate(EA)) goto err0; /* Return the buffer and number of records. */ *buf = EA->buf; *nrec = elasticarray_getsize(EA, reclen); /* * Free the elastic array structure -- but not its buffer, since we've * passed the buffer out to the caller. */ free(EA); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * elasticarray_exportdup(EA, buf, nrec, reclen): * Duplicate the data in the elastic array ${EA} into a buffer ${buf} * containing ${nrec} records of length ${reclen}. (Same as _export, except * that the elastic array remains intact.) The value ${reclen} must be * positive. */ int elasticarray_exportdup(struct elasticarray * EA, void ** buf, size_t * nrec, size_t reclen) { /* Allocate buffer for the caller's copy of the data. */ if ((*buf = malloc(EA->size)) == NULL) goto err0; /* Copy the data in. */ memcpy(*buf, EA->buf, EA->size); /* Tell the caller how many records we have. */ *nrec = elasticarray_getsize(EA, reclen); /* Success! */ return (0); err0: /* Failure! */ return (-1); } spiped-1.6.2/libcperciva/datastruct/timerqueue.c000644 001751 001751 00000011520 13723620112 023426 0ustar00cpercivacperciva000000 000000 #include #include #include #include "ptrheap.h" #include "timerqueue.h" struct timerqueue { struct ptrheap * H; }; struct timerrec { struct timeval tv; size_t rc; void * ptr; }; /* Compare two timevals. */ static int tvcmp(const struct timeval * x, const struct timeval * y) { /* Does one have more seconds? */ if (x->tv_sec > y->tv_sec) return (1); if (x->tv_sec < y->tv_sec) return (-1); /* Does one have more microseconds? */ if (x->tv_usec > y->tv_usec) return (1); if (x->tv_usec < y->tv_usec) return (-1); /* They must be equal. */ return (0); } /* Record-comparison callback from ptrheap. */ static int compar(void * cookie, const void * x, const void * y) { const struct timerrec * _x = x; const struct timerrec * _y = y; (void)cookie; /* UNUSED */ return (tvcmp(&_x->tv, &_y->tv)); } /* Cookie-recording callback from ptrheap. */ static void setreccookie(void * cookie, void * ptr, size_t rc) { struct timerrec * rec = ptr; (void)cookie; /* UNUSED */ rec->rc = rc; } /** * timerqueue_init(void): * Create and return an empty timer priority queue. */ struct timerqueue * timerqueue_init(void) { struct timerqueue * Q; /* Allocate structure. */ if ((Q = malloc(sizeof(struct timerqueue))) == NULL) goto err0; /* Allocate heap. */ if ((Q->H = ptrheap_init(compar, setreccookie, Q)) == NULL) goto err1; /* Success! */ return (Q); err1: free(Q); err0: /* Failure! */ return (NULL); } /** * timerqueue_add(Q, tv, ptr): * Add the pair (${tv}, ${ptr}) to the priority queue ${Q}. Return a cookie * which can be passed to timerqueue_delete() or timerqueue_increase(). */ void * timerqueue_add(struct timerqueue * Q, const struct timeval * tv, void * ptr) { struct timerrec * r; /* Allocate (timeval, ptr) pair record. */ if ((r = malloc(sizeof(struct timerrec))) == NULL) goto err0; /* Fill in values. */ memcpy(&r->tv, tv, sizeof(struct timeval)); r->ptr = ptr; /* * Add the record to the heap. The value r->rc will be filled in * by setreccookie which will be called by ptrheap_add. */ if (ptrheap_add(Q->H, r)) goto err1; /* Success! */ return (r); err1: free(r); err0: /* Failure! */ return (NULL); } /** * timerqueue_delete(Q, cookie): * Delete the (timeval, ptr) pair associated with the cookie ${cookie} from * the priority queue ${Q}. */ void timerqueue_delete(struct timerqueue * Q, void * cookie) { struct timerrec * r = cookie; /* Remove the record from the heap. */ ptrheap_delete(Q->H, r->rc); /* Free the record. */ free(r); } /** * timerqueue_increase(Q, cookie, tv): * Increase the timer associated with the cookie ${cookie} in the priority * queue ${Q} to ${tv}. */ void timerqueue_increase(struct timerqueue * Q, void * cookie, const struct timeval * tv) { struct timerrec * r = cookie; /* Adjust timer value. */ memcpy(&r->tv, tv, sizeof(struct timeval)); /* Inform the heap that the record value has increased. */ ptrheap_increase(Q->H, r->rc); } /** * timerqueue_getmin(Q): * Return a pointer to the least timeval in ${Q}, or NULL if the priority * queue is empty. The pointer will remain valid until the next call to a * timerqueue_* function. This function cannot fail. */ const struct timeval * timerqueue_getmin(struct timerqueue * Q) { struct timerrec * r; /* Get the minimum element from the heap. */ r = ptrheap_getmin(Q->H); /* If we have an element, return its timeval; otherwise, NULL. */ if (r != NULL) return (&r->tv); else return (NULL); } /** * timerqueue_getptr(Q, tv): * If the least timeval in ${Q} is less than or equal to ${tv}, return the * associated pointer and remove the pair from the priority queue. If not, * return NULL. This function cannot fail. */ void * timerqueue_getptr(struct timerqueue * Q, const struct timeval * tv) { struct timerrec * r; void * ptr; /* * Get the minimum element from the heap. Return NULL if the heap * has no minimum element (i.e., is empty). */ if ((r = ptrheap_getmin(Q->H)) == NULL) return (NULL); /* If the minimum timeval is greater than ${tv}, return NULL. */ if (tvcmp(&r->tv, tv) > 0) return (NULL); /* Remove this record from the heap. */ ptrheap_deletemin(Q->H); /* Extract its pointer. */ ptr = r->ptr; /* Free the record. */ free(r); /* * And finally return the pointer which was associated with the * (formerly) minimum timeval in the heap. */ return (ptr); } /** * timerqueue_free(Q): * Free the timer priority queue ${Q}. */ void timerqueue_free(struct timerqueue * Q) { struct timerrec * r; /* Behave consistently with free(NULL). */ if (Q == NULL) return; /* Extract elements from the heap and free them one by one. */ while ((r = ptrheap_getmin(Q->H)) != NULL) { free(r); ptrheap_deletemin(Q->H); } /* Free the heap. */ ptrheap_free(Q->H); /* Free the timer priority queue structure. */ free(Q); } spiped-1.6.2/libcperciva/datastruct/ptrheap.c000644 001751 001751 00000021340 13723620112 022705 0ustar00cpercivacperciva000000 000000 #include #include "elasticarray.h" #include "ptrheap.h" ELASTICARRAY_DECL(PTRLIST, ptrlist, void *); struct ptrheap { int (* compar)(void *, const void *, const void *); void (* setreccookie)(void *, void *, size_t); void * cookie; PTRLIST elems; size_t nelems; }; /** * swap(elems, i, j, setreccookie, cookie): * Swap elements ${i} and ${j} in ${elems}. If ${setreccookie} is non-NULL, * invoke ${setreccookie}(${cookie}, elem, pos) for each of the elements and * their new positions in the tree. */ static void swap(PTRLIST elems, size_t i, size_t j, void (* setreccookie)(void *, void *, size_t), void * cookie) { void * tmp; /* Swap the elements. */ tmp = *ptrlist_get(elems, i); *ptrlist_get(elems, i) = *ptrlist_get(elems, j); *ptrlist_get(elems, j) = tmp; /* Notify about the moved elements. */ if (setreccookie != NULL) { setreccookie(cookie, *ptrlist_get(elems, i), i); setreccookie(cookie, *ptrlist_get(elems, j), j); } } /** * heapifyup(elems, i, compar, setreccookie, cookie): * Sift up element ${i} of the elements ${elems}, using the comparison * function ${compar} and the cookie ${cookie}. If elements move and * ${setreccookie} is non-NULL, use it to notify about the updated position * of elements in the heap. */ static void heapifyup(PTRLIST elems, size_t i, int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie) { /* Iterate up the tree. */ do { /* If we're at the root, we have nothing to do. */ if (i == 0) break; /* If this is >= its parent, we're done. */ if (compar(cookie, *ptrlist_get(elems, i), *ptrlist_get(elems, (i - 1) / 2)) >= 0) break; /* Swap with the parent. */ swap(elems, i, (i - 1) / 2, setreccookie, cookie); /* Move up the tree. */ i = (i - 1) / 2; } while (1); } /** * heapify(elems, i, N, compar, setreccookie, cookie): * Sift down element number ${i} out of ${N} of the elements ${elems}, using * the comparison function ${compar} and the cookie ${cookie}. If elements * move and ${setreccookie} is non-NULL, use it to notify about the updated * position of elements in the heap. */ static void heapify(PTRLIST elems, size_t i, size_t N, int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie) { size_t min; /* Iterate down the tree. */ do { /* Look for the minimum element out of {i, 2i+1, 2i+2}. */ min = i; /* Is this bigger than element 2i+1? */ if ((2 * i + 1 < N) && (compar(cookie, *ptrlist_get(elems, min), *ptrlist_get(elems, 2 * i + 1)) > 0)) min = 2 * i + 1; /* Is this bigger than element 2i+2? */ if ((2 * i + 2 < N) && (compar(cookie, *ptrlist_get(elems, min), *ptrlist_get(elems, 2 * i + 2)) > 0)) min = 2 * i + 2; /* If the minimum is i, we have heap-property. */ if (min == i) break; /* Move the minimum into position i. */ swap(elems, min, i, setreccookie, cookie); /* Move down the tree. */ i = min; } while (1); } /** * ptrheap_init(compar, setreccookie, cookie): * Create and return an empty heap. The function ${compar}(${cookie}, x, y) * should return less than, equal to, or greater than 0 depending on whether * x is less than, equal to, or greater than y; and if ${setreccookie} is * non-zero it will be called as ${setreccookie}(${cookie}, ${ptr}, ${rc}) to * indicate that the value ${rc} is the current record cookie for the pointer * ${ptr}. The function ${setreccookie} may not make any ptrheap_* calls. */ struct ptrheap * ptrheap_init(int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie) { /* Let ptrheap_create handle this. */ return (ptrheap_create(compar, setreccookie, cookie, 0, NULL)); } /** * ptrheap_create(compar, setreccookie, cookie, N, ptrs): * Create and return a heap, as in ptrheap_init(), but with the ${N} pointers * in ${ptrs} as heap elements. This is faster than creating an empty heap * and adding the elements individually. */ struct ptrheap * ptrheap_create(int (* compar)(void *, const void *, const void *), void (* setreccookie)(void *, void *, size_t), void * cookie, size_t N, void ** ptrs) { struct ptrheap * H; size_t i; /* Allocate structure. */ if ((H = malloc(sizeof(struct ptrheap))) == NULL) goto err0; /* Store parameters. */ H->compar = compar; H->setreccookie = setreccookie; H->cookie = cookie; /* We will have N elements. */ H->nelems = N; /* Allocate space for N heap elements. */ if ((H->elems = ptrlist_init(N)) == NULL) goto err1; /* Copy the heap elements in. */ for (i = 0; i < N; i++) *ptrlist_get(H->elems, i) = ptrs[i]; /* Turn this into a heap. */ for (i = N - 1; i < N; i--) heapify(H->elems, i, N, H->compar, NULL, H->cookie); /* Advise the caller about the record cookies. */ if (H->setreccookie != NULL) for (i = 0; i < N; i++) (H->setreccookie)(H->cookie, *ptrlist_get(H->elems, i), i); /* Success! */ return (H); err1: free(H); err0: /* Failure! */ return (NULL); } /** * ptrheap_add(H, ptr): * Add the pointer ${ptr} to the heap ${H}. */ int ptrheap_add(struct ptrheap * H, void * ptr) { /* Add the element to the end of the heap. */ if (ptrlist_append(H->elems, &ptr, 1)) goto err0; H->nelems += 1; /* Advise the caller about the current location of this record. */ if (H->setreccookie != NULL) (H->setreccookie)(H->cookie, ptr, H->nelems - 1); /* Move the new element up in the tree if necessary. */ heapifyup(H->elems, H->nelems - 1, H->compar, H->setreccookie, H->cookie); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * ptrheap_getmin(H): * Return the minimum pointer in the heap ${H}. If the heap is empty, NULL * is returned. */ void * ptrheap_getmin(struct ptrheap * H) { /* If we have any elements, the minimum is in position 0. */ if (H->nelems) return (*ptrlist_get(H->elems, 0)); else return (NULL); } /** * ptrheap_delete(H, rc): * Delete from the heap ${H} the element ptr for which the function call * setreccookie(cookie, ptr, ${rc}) was most recently made. */ void ptrheap_delete(struct ptrheap * H, size_t rc) { /* * If the element we're deleting is not at the end of the heap, * replace it with the element which is currently at the end. */ if (rc != H->nelems - 1) { /* Move ptr from position H->nelems - 1 into position rc. */ *ptrlist_get(H->elems, rc) = *ptrlist_get(H->elems, H->nelems - 1); if (H->setreccookie != NULL) (H->setreccookie)(H->cookie, *ptrlist_get(H->elems, rc), rc); /* Is this too small to be in position ${rc}? */ if ((rc > 0) && (H->compar(H->cookie, *ptrlist_get(H->elems, rc), *ptrlist_get(H->elems, (rc - 1) / 2)) < 0)) { /* Swap with the parent, and keep moving up. */ swap(H->elems, rc, (rc - 1) / 2, H->setreccookie, H->cookie); heapifyup(H->elems, (rc - 1) / 2, H->compar, H->setreccookie, H->cookie); } else { /* Maybe we need to move it down instead? */ heapify(H->elems, rc, H->nelems, H->compar, H->setreccookie, H->cookie); } } /* * We've got everything we want to keep in positions 0 .. nelems - 2, * and we have heap-nature, so all we need to do is strip off the * final pointer. */ ptrlist_shrink(H->elems, 1); H->nelems--; } /** * ptrheap_deletemin(H): * Delete the minimum element in the heap ${H}. The heap must not be empty. */ void ptrheap_deletemin(struct ptrheap * H) { /* Let ptrheap_delete handle this. */ ptrheap_delete(H, 0); } /** * ptrheap_decrease(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has decreased. */ void ptrheap_decrease(struct ptrheap * H, size_t rc) { /* Move the element up if necessary. */ heapifyup(H->elems, rc, H->compar, H->setreccookie, H->cookie); } /** * ptrheap_increase(H, rc): * Adjust the heap ${H} to account for the fact that the element ptr for * which the function call setreccookie(cookie, ptr, ${rc}) was most recently * made has increased. */ void ptrheap_increase(struct ptrheap * H, size_t rc) { /* Move the element down if necessary. */ heapify(H->elems, rc, H->nelems, H->compar, H->setreccookie, H->cookie); } /** * ptrheap_increasemin(H): * Adjust the heap ${H} to account for the fact that the (formerly) minimum * element has increased. */ void ptrheap_increasemin(struct ptrheap * H) { /* Move the element down if necessary. */ heapify(H->elems, 0, H->nelems, H->compar, H->setreccookie, H->cookie); } /** * ptrheap_free(H): * Free the pointer heap ${H}. */ void ptrheap_free(struct ptrheap * H) { /* Behave consistently with free(NULL). */ if (H == NULL) return; ptrlist_free(H->elems); free(H); } spiped-1.6.2/libcperciva/util/asprintf.c000644 001751 001751 00000001426 12530545060 021675 0ustar00cpercivacperciva000000 000000 #include #include #include #include "asprintf.h" /** * asprintf(ret, format, ...): * Do asprintf(3) like GNU and BSD do. */ int asprintf(char ** ret, const char * format, ...) { va_list ap; int len; size_t buflen; /* Figure out how long the string needs to be. */ va_start(ap, format); len = vsnprintf(NULL, 0, format, ap); va_end(ap); /* Did we fail? */ if (len < 0) goto err0; buflen = (size_t)(len) + 1; /* Allocate memory. */ if ((*ret = malloc(buflen)) == NULL) goto err0; /* Actually generate the string. */ va_start(ap, format); len = vsnprintf(*ret, buflen, format, ap); va_end(ap); /* Did we fail? */ if (len < 0) goto err1; /* Success! */ return (len); err1: free(*ret); err0: /* Failure! */ return (-1); } spiped-1.6.2/libcperciva/util/ctassert.h000644 001751 001751 00000001117 13723620112 021676 0ustar00cpercivacperciva000000 000000 #ifndef _CTASSERT_H_ #define _CTASSERT_H_ /* * CTASSERT(foo) will produce a compile-time error if "foo" is not a constant * expression which evaluates to a non-zero value. */ /* Kill any existing definition, just in case it's different. */ #ifdef CTASSERT #undef CTASSERT #endif /* Define using libcperciva namespace to avoid collisions. */ #define CTASSERT(x) libcperciva_CTASSERT(x, __LINE__) #define libcperciva_CTASSERT(x, y) libcperciva__CTASSERT(x, y) #define libcperciva__CTASSERT(x, y) \ extern char libcperciva__assert ## y[(x) ? 1 : -1] #endif /* !_CTASSERT_H_ */ spiped-1.6.2/libcperciva/util/getopt.c000644 001751 001751 00000020642 13723620112 021347 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include "getopt.h" /* * Standard getopt global variables. optreset starts as non-zero in order to * trigger initialization behaviour. */ const char * optarg = NULL; int optind = 1; int opterr = 1; int optreset = 1; /* * Quasi-internal global variables -- these are used via GETOPT macros. */ const char * getopt_dummy = "(dummy)"; int getopt_initialized = 0; /* * Internal variables. */ static const char * cmdname = NULL; static struct opt { const char * os; size_t olen; int hasarg; } * opts = NULL; /* Probably a sparse array: some values, some NULLs. */ static size_t nopts; /* Maximum number of options. */ static size_t opt_missing; static size_t opt_default; static size_t opt_found; static const char * packedopts; static char popt[3]; static int atexit_registered = 0; /* Print a message. */ #define PRINTMSG(...) do { \ if (cmdname != NULL) \ fprintf(stderr, "%s: ", cmdname); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) /* Print an error message and die. */ #define DIE(...) do { \ PRINTMSG(__VA_ARGS__); \ abort(); \ } while (0) /* Print a warning, if warnings are enabled. */ #define WARN(...) do { \ if (opterr == 0) \ break; \ if (opt_missing != opt_default) \ break; \ PRINTMSG(__VA_ARGS__); \ } while (0) /* Free allocated options array. */ static void atexit_handler(void) { free(opts); opts = NULL; } /* Reset internal state. */ static void reset(int argc, char * const argv[]) { const char * p; /* If we have arguments, stash argv[0] for error messages. */ if (argc > 0) { /* Find the basename, without leading directories. */ for (p = cmdname = argv[0]; *p != '\0'; p++) { if (*p == '/') cmdname = p + 1; } } /* Discard any registered command-line options. */ free(opts); opts = NULL; /* Register atexit handler if we haven't done so already. */ if (!atexit_registered) { atexit(atexit_handler); atexit_registered = 1; } /* We will start scanning from the first option. */ optind = 1; /* We're not in the middle of any packed options. */ packedopts = NULL; /* We haven't found any option yet. */ opt_found = (size_t)(-1); /* We're not initialized yet. */ getopt_initialized = 0; /* Finished resetting state. */ optreset = 0; } /* Search for an option string. */ static size_t searchopt(const char * os) { size_t i; /* Scan the array of options. */ for (i = 0; i < nopts; i++) { /* Is there an option in this slot? */ if (opts[i].os == NULL) continue; /* Does this match up to the length of the option string? */ if (strncmp(opts[i].os, os, opts[i].olen)) continue; /* Do we have