spiped-1.6.0/000755 001751 001751 00000000000 13101223046 014426 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/spiped/000755 001751 001751 00000000000 13101223046 015712 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/spipe/000755 001751 001751 00000000000 13101223046 015546 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/BUILDING000644 001751 001751 00000005576 13042745752 015604 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 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.0/DESIGN.md000644 001751 001751 00000011520 13042745752 015741 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.0/CHANGELOG000644 001751 001751 00000005066 13074276273 015672 0ustar00cpercivacperciva000000 000000 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.0/COPYRIGHT000644 001751 001751 00000003236 12735033123 015734 0ustar00cpercivacperciva000000 000000 The included code and documentation ("spiped") is distributed under the following terms: Copyright 2005-2016 Colin Percival. All rights reserved. Copyright 2005-2016 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.0/README.md000644 001751 001751 00000011500 13042745752 015723 0ustar00cpercivacperciva000000 000000 spiped ====== Official signed releases are published at: > 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 ------------- To set up an encrypted and authenticated pipe for sending email between two systems (in the author's case, from many systems around the internet to his central SMTP server, which then relays email to the rest of the world), one might run dd if=/dev/urandom bs=32 count=1 of=keyfile spiped -d -s '[0.0.0.0]:8025' -t '[127.0.0.1]:25' -k keyfile on a server and after copying keyfile to the local system, run spiped -e -s '[127.0.0.1]:25' -t $SERVERNAME:8025 -k keyfile at which point mail delivered via localhost:25 on the local system will be securely transmitted to port 25 on the server. You can also use spiped to protect SSH servers from attackers: Since data is authenticated before being forwarded to the target, this can allow you to SSH to a host while protecting you in the event that someone finds an exploitable bug in the SSH daemon -- this serves the same purpose as port knocking or a firewall which restricts source IP addresses which can connect to SSH. On the SSH server, run dd if=/dev/urandom bs=32 count=1 of=/etc/ssh/spiped.key spiped -d -s '[0.0.0.0]:8022' -t '[127.0.0.1]:22' -k /etc/ssh/spiped.key then copy the server's `/etc/ssh/spiped.key` to `~/.ssh/spiped_HOSTNAME_key` on your local system and add the lines Host HOSTNAME ProxyCommand spipe -t %h:8022 -k ~/.ssh/spiped_%h_key to the `~/.ssh/config file`. This will cause `ssh HOSTNAME` to automatically connect using the spipe client via the spiped daemon; you can then firewall off all incoming traffic on port tcp/22. 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 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.0/STYLE000644 001751 001751 00000012516 12735033123 015265 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. 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. 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; spiped-1.6.0/Makefile000644 001751 001751 00000002152 13076550724 016107 0ustar00cpercivacperciva000000 000000 .POSIX: PROGS= spiped spipe TESTS= tests/nc-client tests/nc-server tests/valgrind BINDIR_DEFAULT= /usr/local/bin CFLAGS_DEFAULT= -O2 all: cpusupport-config.h export CFLAGS="$${CFLAGS:-${CFLAGS_DEFAULT}}"; \ export LDADD_POSIX=`export CC="${CC}"; cd libcperciva/POSIX && command -p sh posix-l.sh "$$PATH"`; \ export CFLAGS_POSIX=`export CC="${CC}"; cd libcperciva/POSIX && command -p sh posix-cflags.sh "$$PATH"`; \ . ./cpusupport-config.h; \ for D in ${PROGS} ${TESTS}; do \ ( cd $${D} && ${MAKE} all ) || exit 2; \ done cpusupport-config.h: ( export CC="${CC}"; command -p sh libcperciva/cpusupport/Build/cpusupport.sh "$$PATH" ) > cpusupport-config.h install: all export BINDIR=$${BINDIR:-${BINDIR_DEFAULT}}; \ for D in ${PROGS}; do \ ( cd $${D} && ${MAKE} install ) || exit 2; \ done clean: rm -f cpusupport-config.h for D in ${PROGS} ${TESTS}; do \ ( cd $${D} && ${MAKE} clean ) || exit 2; \ done .PHONY: test test: VERBOSE=1 tests/test_spiped.sh # Developer targets: These only work with BSD make Makefiles: ${MAKE} -f Makefile.BSD Makefiles publish: ${MAKE} -f Makefile.BSD publish spiped-1.6.0/lib/000755 001751 001751 00000000000 13074276273 015217 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/000755 001751 001751 00000000000 12655635610 016732 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/proto/000755 001751 001751 00000000000 13042745752 015612 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/tests/000755 001751 001751 00000000000 13101221163 015566 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/tests/nc-client/000755 001751 001751 00000000000 13076550742 017465 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/tests/lorem-send.txt000644 001751 001751 00000000676 12735033123 020416 0ustar00cpercivacperciva000000 000000 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. spiped-1.6.0/tests/shared_test_functions.sh000644 001751 001751 00000021110 13101221163 022512 0ustar00cpercivacperciva000000 000000 #!/bin/sh ### Definitions # # This test suite uses the following terminology: # - scenario: a series of commands to test. Each must be in a # separate file, and must be completely self-contained # (other than the variables listed below). # - check: a series of commands that produces an exit code which # the test suite should check. A scenario may contain any # number of checks. # ### Design # # The main function is scenario_runner(scenario_filename), which # takes a scenario file as the argument, and runs a # scenario_cmd() # function which was defined in that file. # ### Variables # # Wherever possible, this suite uses local variables and # explicitly-passed arguments, with the following exceptions: # - s_basename: this is the basename for the scenario's temporary # and log files. # - s_val_basename: this is the basename for the scenario's # valgrind log files. # - s_count: this is the count of the scenario's checks (so that # each check can have distinct files). # - s_retval: this is the overall exit code of the scenario. # - c_exitfile: this contains the exit code of each check. # - c_valgrind_min: this is the minimum value of USE_VALGRIND # which will enable valgrind checking for this check. # - c_valgrind_cmd: this is the valgrind command (including # appropriate log file) if necessary, or is "" otherwise. set -o nounset ### Constants out="tests-output" out_valgrind="tests-valgrind" valgrind_suppressions="${out_valgrind}/suppressions" valgrind_suppressions_log="${out_valgrind}/suppressions.pre" # Print output about test failures. VERBOSE=${VERBOSE:-0} # Keep the user-specified ${USE_VALGRIND}, or initialize to 0. USE_VALGRIND=${USE_VALGRIND:-0} # A non-zero value unlikely to be used as an exit code by the programs being # tested. valgrind_exit_code=108 ## prepare_directories(): # Delete any old directories, and create new ones as necessary. Must be run # after check_optional_valgrind(). prepare_directories() { # Clean up previous directories. if [ -d "${out}" ]; then rm -rf ${out} fi if [ -d "${out_valgrind}" ]; then rm -rf ${out_valgrind} fi # Make new directories. mkdir ${out} if [ "$USE_VALGRIND" -gt 0 ]; then mkdir ${out_valgrind} fi } ## find_system (cmd, args...): # Look for ${cmd} in the $PATH, and ensure that it supports ${args}. find_system() { cmd=$1 cmd_with_args=$@ # Look for ${cmd}; the "|| true" and -} make this work with set -e. system_binary=`command -v ${cmd}` || true if [ -z "${system_binary-}" ]; then system_binary="" printf "System ${cmd} not found.\n" 1>&2 # If the command exists, check it ensures the ${args}. elif ${cmd_with_args} 2>&1 >/dev/null | \ grep -qE "(invalid|illegal) option"; then system_binary="" printf "Cannot use system ${cmd}; does not" 1>&2 printf " support necessary arguments.\n" 1>&2 fi echo "${system_binary}" } ## has_pid (cmd): # Look for ${cmd} in ps; return 0 if ${cmd} exists. has_pid() { cmd=$1 pid=`ps -Aopid,command | grep "${cmd}" | grep -v "grep"` || true if [ -n "${pid}" ]; then return 0 fi return 1 } ## check_optional_valgrind (): # Return a $USE_VALGRIND variable defined; if it was previously defined and # was greater than 0, then check that valgrind is available in the $PATH. check_optional_valgrind() { if [ "$USE_VALGRIND" -gt 0 ]; then # Look for valgrind in $PATH. if ! command -v valgrind >/dev/null 2>&1; then printf "valgrind not found\n" 1>&2 exit 1 fi fi } ## ensure_valgrind_suppresssion (potential_memleaks_binary): # Run the ${potential_memleaks_binary} through valgrind, keeping # track of any apparent memory leak in order to suppress reporting # those leaks when testing other binaries. ensure_valgrind_suppression() { potential_memleaks_binary=$1 # Quit if we're not using valgrind. if [ ! "$USE_VALGRIND" -gt 0 ]; then return fi; printf "Generating valgrind suppressions... " # Run valgrind on the binary, sending it a "\n" so that # a test which uses STDIN will not wait for user input. printf "\n" | (valgrind --leak-check=full --show-leak-kinds=all \ --gen-suppressions=all \ --log-file=${valgrind_suppressions_log} \ ${potential_memleaks_binary}) # Strip out useless parts from the log file, as well as # removing references to the main and "pl_*" ("potential # loss") functions so that the suppressions can apply to # other binaries. (grep -v "^==" ${valgrind_suppressions_log} \ | grep -v " fun:pl_" - \ | grep -v " fun:main" - \ > ${valgrind_suppressions} ) # Clean up rm -f ${valgrind_suppressions_log} printf "done.\n" } ## setup_check_variables (): # Set up the "check" variables ${c_exitfile} and ${c_valgrind_cmd}, the # latter depending on the previously-defined ${c_valgrind_min}. # Advances the number of checks ${s_count} so that the next call to this # function will set up new filenames. setup_check_variables() { # Set up the "exit" file. c_exitfile="${s_basename}-`printf %02d ${s_count}`.exit" # If we don't have a suppressions file, don't try to use it. if [ ! -e ${valgrind_suppressions} ]; then valgrind_suppressions=/dev/null fi # Set up the valgrind command if $USE_VALGRIND is greater # than or equal to ${valgrind_min}; otherwise, produce an # empty string. Using --error-exitcode means that if # there is a serious problem (such that scrypt calls # exit(1)) *and* a memory leak, the test suite reports an # exit value of ${valgrind_exit_code}. However, if there # is a serious problem but no memory leak, we still # receive a non-zero exit code. The most important thing # is that we only receive an exit code of 0 if both the # program and valgrind are happy. if [ "$USE_VALGRIND" -ge "${c_valgrind_min}" ]; then val_logfilename=${s_val_basename}-`printf %02d ${s_count}`.log c_valgrind_cmd="valgrind \ --log-file=${val_logfilename} \ --leak-check=full --show-leak-kinds=all \ --errors-for-leak-kinds=all \ --suppressions=${valgrind_suppressions} \ --error-exitcode=${valgrind_exit_code} " else c_valgrind_cmd="" fi # Advances the number of checks. s_count=$((s_count + 1)) } ## get_val_logfile (val_basename, exitfile): # Return the valgrind logfile corresponding to ${exitfile}. get_val_logfile() { val_basename=$1 exitfile=$2 num=`echo "${exitfile}" | rev | cut -c 1-7 | rev | cut -c 1-2 ` echo "${val_basename}-${num}.log" } ## notify_success_or_fail (log_basename, val_log_basename): # Examine all "exit code" files beginning with ${log_basename} and # print "SUCCESS!" or "FAILED!" as appropriate. If the test failed # with the code ${valgrind_exit_code}, output the appropriate # valgrind logfile to stdout. notify_success_or_fail() { log_basename=$1 val_log_basename=$2 # Bail if there's no exitfiles. exitfiles=`ls ${log_basename}-*.exit` || true if [ -z "$exitfiles" ]; then echo "FAILED" s_retval=1 return fi # Check each exitfile. for exitfile in `echo $exitfiles | sort`; do ret=`cat ${exitfile}` if [ "${ret}" -lt 0 ]; then echo "SKIP!" return fi if [ "${ret}" -gt 0 ]; then echo "FAILED!" retval=${ret} if [ ${VERBOSE} -ne 0 ]; then printf "File ${exitfile} contains exit" 1>&2 printf " code ${ret}.\n" 1>&2 fi if [ "${ret}" -eq "${valgrind_exit_code}" ]; then val_logfilename=$( get_val_logfile \ ${val_log_basename} ${exitfile} ) cat ${val_logfilename} fi s_retval=${ret} return fi done echo "SUCCESS!" } ## scenario_runner (scenario_filename): # Run a test scenario from ${scenario_filename}. scenario_runner() { scenario_filename=$1 basename=`basename ${scenario_filename} .sh` printf " ${basename}... " 1>&2 # Initialize "scenario" and "check" variables. s_basename=${out}/${basename} s_val_basename=${out_valgrind}/${basename} s_count=0 c_exitfile=/dev/null c_valgrind_min=9 c_valgrind_cmd="" # Load scenario_cmd() from the scenario file. unset scenario_cmd . ${scenario_filename} if ! command -v scenario_cmd 1>/dev/null ; then printf "ERROR: scenario_cmd() is not defined in\n" printf " ${scenario_filename}\n" exit 1 fi # Run the scenario command. scenario_cmd # Print PASS or FAIL, and return result. s_retval=0 notify_success_or_fail ${s_basename} ${s_val_basename} return "${s_retval}" } ## run_scenarios (scenario_filenames): # Run all scenarios matching ${scenario_filenames}. run_scenarios() { printf -- "Running tests\n" printf -- "-------------\n" scenario_filenames=$@ for scenario in ${scenario_filenames}; do # We can't call this function with $( ... ) because we # want to allow it to echo values to stdout. scenario_runner ${scenario} retval=$? if [ ${retval} -gt 0 ]; then exit ${retval} fi done } spiped-1.6.0/tests/test_spiped.sh000755 001751 001751 00000010743 13101221163 020455 0ustar00cpercivacperciva000000 000000 #!/bin/sh set -o noclobber -o nounset -e # Build directory (we don't allow out-of-tree builds in spiped). bindir=. # This test script requires three ports. src_port=8000 mid_port=8001 dst_port=8002 # Constants out="output-tests-spiped" ################################ Setup variables from the command-line # Find script directory and load helper functions. scriptdir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) . ${scriptdir}/shared_test_functions.sh # Find relative spiped binary paths. spiped_binary=${scriptdir}/../spiped/spiped spipe_binary=${scriptdir}/../spipe/spipe nc_client_binary=${scriptdir}/../tests/nc-client/nc-client nc_server_binary=${scriptdir}/../tests/nc-server/nc-server # Find system spiped system_spiped_binary=$( find_system spiped ) # Check for required commands. if ! command -v ${spiped_binary} >/dev/null 2>&1; then echo "spiped not detected; did you forget to run 'make all'?" exit 1 fi if ! command -v ${spipe_binary} >/dev/null 2>&1; then echo "spiped not detected; did you forget to run 'make all'?" exit 1 fi # Clean up previous directories, and create new ones. prepare_directories ################################ Helper functions ## check_leftover_servers(): # Repeated testing, especially when doing ctrl-c to break out of (suspected) # hanging, can leave a nc_server or spiped process(es) floating around, which # is problematic for the next testing run. Checking for this shouldn't be # necessary for normal testing (as opposed to test-script development), but # there's no harm in checking anyway. Once identified, the user is notified # (instead of automatically removing them) -- a failing test will require # manual attention anyway. check_leftover_servers() { # Find old nc-server on ${dst_port}. if $( has_pid "${nc_server_binary} ${dst_port}" ); then echo "Error: Left-over nc-server from previous run." exit 1 fi # Find old spiped {-d, -e} servers on {${mid_port}, ${src_port}}. if $( has_pid "spiped -d -s \[127.0.0.1\]:${mid_port}" ); then echo "Error: Left-over spiped -d from previous run." exit 1 fi if $( has_pid "spiped -e -s \[127.0.0.1\]:${src_port}" ); then echo "Error: Left-over spiped -e from previous run." exit 1 fi } ################################ Server setup ## setup_spiped_decryption_server(nc_output=/dev/null, use_system_spiped=0): # Set up a spiped decryption server, translating from ${mid_port} # to ${dst_port}, saving the exit code to ${c_exitfile}. Also set # up a nc-server listening to ${dst_port}, saving output to # ${nc_output}. Uses the system's spiped (instead of the # version in this source tree) if ${use_system_spiped} is 1. setup_spiped_decryption_server () { nc_output=${1:-/dev/null} use_system_spiped=${2:-0} check_leftover_servers # Select system or compiled spiped. if [ "${use_system_spiped}" -gt 0 ]; then spiped_cmd=${system_spiped_binary} else spiped_cmd=${spiped_binary} fi # Start backend server. ${nc_server_binary} ${dst_port} ${nc_output} & nc_pid=${!} # Start spiped to connect middle port to backend. ${spiped_cmd} -d \ -s [127.0.0.1]:${mid_port} \ -t [127.0.0.1]:${dst_port} \ -p ${s_basename}-spiped-d.pid \ -k /dev/null -o 1 } ## setup_spiped_decryption_server(basename): # Set up a spiped encryption server, translating from ${src_port} # to ${mid_port}, saving the exit code to ${c_exitfile}. setup_spiped_encryption_server () { # Start spiped to connect source port to middle. ${spiped_cmd} -e \ -s [127.0.0.1]:${src_port} \ -t [127.0.0.1]:${mid_port} \ -p ${s_basename}-spiped-e.pid \ -k /dev/null -o 1 } ## servers_stop(): # Stops the various servers. servers_stop() { # Signal spiped servers to stop if [ -e ${s_basename}-spiped-e.pid ]; then kill `cat ${s_basename}-spiped-e.pid` fi if [ -e ${s_basename}-spiped-d.pid ]; then kill `cat ${s_basename}-spiped-d.pid` fi # Give servers a chance to stop without fuss. sleep 1 # Waiting for servers to stop while $( has_pid "spiped -e -s \[127.0.0.1\]:${src_port}" ); do if [ ${VERBOSE} -ne 0 ]; then echo "Waiting to stop: spiped -e" fi sleep 1 done while $( has_pid "spiped -d -s \[127.0.0.1\]:${mid_port}" ); do if [ ${VERBOSE} -ne 0 ]; then echo "Waiting to stop: spiped -d" fi sleep 1 done while $( has_pid "${nc_server_binary} ${dst_port}" ); do if [ ${VERBOSE} -ne 0 ]; then echo "Waiting to stop: ncat" fi sleep 1 done } #################################################### # Run the test scenarios; this will exit on the first failure. run_scenarios ${scriptdir}/??-*.sh spiped-1.6.0/tests/01-connection-open-close-single.sh000644 001751 001751 00000000750 13074276273 024047 0ustar00cpercivacperciva000000 000000 #!/bin/sh # Goal of this test: # - establish a connection to a spiped server. # - open a connection, but don't send anything. # - close the connection. ### Constants ### Actual command scenario_cmd() { # Set up infrastructure. setup_spiped_decryption_server setup_spiped_encryption_server # Open and close a connection. setup_check_variables ( echo "" | ${nc_client_binary} [127.0.0.1]:${src_port} echo $? > ${c_exitfile} ) # Wait for server(s) to quit. servers_stop } spiped-1.6.0/tests/02-connection-open-timeout-single.sh000644 001751 001751 00000001144 13074276273 024427 0ustar00cpercivacperciva000000 000000 #!/bin/sh # Goal of this test: # - establish a connection to a spiped server. # - open a connection, but don't send anything. # - wait; the connection should be closed automatically (because we # gave it -o 1). ### Constants ### Actual command scenario_cmd() { # Set up infrastructure. setup_spiped_decryption_server setup_spiped_encryption_server # Open and close a connection, keeping it open for 2 seconds. setup_check_variables ( ( echo "" ; sleep 2 ) | \ ${nc_client_binary} [127.0.0.1]:${src_port} echo $? > ${c_exitfile} ) & sleep 3 # Wait for server(s) to quit. servers_stop } spiped-1.6.0/tests/03-connection-open-close-double.sh000644 001751 001751 00000001261 13074276273 024040 0ustar00cpercivacperciva000000 000000 #!/bin/sh # Goal of this test: # - create a pair of spiped servers (encryption, decryption). # - open two connections, but don't send anything, # - close one of the connections. ### Constants ### Actual command scenario_cmd() { # Set up infrastructure. setup_spiped_decryption_server setup_spiped_encryption_server # Open and close a connection, keeping it open for 2 seconds. setup_check_variables ( ( echo "" ; sleep 2 ) | \ ${nc_client_binary} [127.0.0.1]:${src_port} echo $? > ${c_exitfile} ) & setup_check_variables ( echo "" | ${nc_client_binary} [127.0.0.1]:${src_port} echo $? > ${c_exitfile} ) sleep 3 # Wait for server(s) to quit. servers_stop } spiped-1.6.0/tests/04-send-data-spipe.sh000644 001751 001751 00000001521 13074276273 021345 0ustar00cpercivacperciva000000 000000 #!/bin/sh # Goal of this test: # - establish a connection to a spiped server. # - send data via spipe # - the received file should match lorem-send.txt ### Constants ncat_output="${s_basename}-ncat-output.txt" ### Actual command scenario_cmd() { # Set up infrastructure. setup_spiped_decryption_server ${ncat_output} # Send data. setup_check_variables ( cat ${scriptdir}/lorem-send.txt | ${spipe_binary} \ -t [127.0.0.1]:${mid_port} -k /dev/null echo $? > ${c_exitfile} ) # Wait for server(s) to quit. servers_stop setup_check_variables if ! cmp -s ${ncat_output} ${scriptdir}/lorem-send.txt; then if [ ${VERBOSE} -ne 0 ]; then printf "Test output does not match input;" 1>&2 printf -- " output is:\n----\n" 1>&2 cat ${ncat_output} 1>&2 printf -- "----\n" 1>&2 fi echo 1 else echo 0 fi > ${c_exitfile} } spiped-1.6.0/tests/05-send-data-spiped.sh000644 001751 001751 00000001753 13074276273 021521 0ustar00cpercivacperciva000000 000000 #!/bin/sh # Goal of this test: # - create a pair of spiped servers (encryption, decryption) # - establish a connection to the encryption spiped server # - open one connection, send lorem-send.txt, close the connection # - the received file should match lorem-send.txt ### Constants ncat_output="${s_basename}-ncat-output.txt" ### Actual command scenario_cmd() { # Set up infrastructure. setup_spiped_decryption_server ${ncat_output} setup_spiped_encryption_server # Open and close a connection. setup_check_variables ( cat ${scriptdir}/lorem-send.txt | \ ${nc_client_binary} [127.0.0.1]:${src_port} echo $? > ${c_exitfile} ) # Wait for server(s) to quit. servers_stop setup_check_variables if ! cmp -s ${ncat_output} ${scriptdir}/lorem-send.txt; then if [ ${VERBOSE} -ne 0 ]; then printf "Test output does not match input;" 1>&2 printf -- " output is:\n----\n" 1>&2 cat ${ncat_output} 1>&2 printf -- "----\n" 1>&2 fi echo 1 else echo 0 fi > ${c_exitfile} } spiped-1.6.0/tests/06-send-data-system-spiped.sh000644 001751 001751 00000002355 13076320372 023033 0ustar00cpercivacperciva000000 000000 #!/bin/sh # Goal of this test: # - create a pair of spiped servers (encryption, decryption), where # the decryption server uses the system-installed spiped binary # - establish a connection to the encryption spiped server # - open one connection, send lorem-send.txt, close the connection # - the received file should match lorem-send.txt ### Constants ncat_output="${s_basename}-ncat-output.txt" ### Actual command scenario_cmd() { if [ ! -n "${system_spiped_binary}" ]; then printf "no system spiped, or it is too old... " # Suppress warning setup_check_variables echo "-1" > ${c_exitfile} return; fi # Set up infrastructure. setup_spiped_decryption_server ${ncat_output} 1 setup_spiped_encryption_server # Open and close a connection. setup_check_variables ( cat ${scriptdir}/lorem-send.txt | \ ${nc_client_binary} [127.0.0.1]:${src_port} echo $? > ${c_exitfile} ) # Wait for server(s) to quit. servers_stop setup_check_variables if ! cmp -s ${ncat_output} ${scriptdir}/lorem-send.txt; then if [ ${VERBOSE} -ne 0 ]; then printf "Test output does not match input;" 1>&2 printf -- " output is:\n----\n" 1>&2 cat ${ncat_output} 1>&2 printf -- "----\n" 1>&2 fi echo 1 else echo 0 fi > ${c_exitfile} } spiped-1.6.0/tests/valgrind/000755 001751 001751 00000000000 13076550736 017422 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/tests/nc-server/000755 001751 001751 00000000000 13076550742 017515 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/tests/nc-server/Makefile000644 001751 001751 00000013176 13101223045 021143 0ustar00cpercivacperciva000000 000000 .POSIX: # AUTOGENERATED FILE, DO NOT EDIT PROG=nc-server MAN1= SRCS=main.c simple_server.c elasticarray.c ptrheap.c timerqueue.c events_immediate.c events_network.c events_network_selectstats.c events_timer.c events.c network_accept.c network_read.c asprintf.c monoclock.c sock.c warnp.c IDIRS=-I../../libcperciva/datastruct -I../../libcperciva/events -I../../libcperciva/network -I../../libcperciva/util LDADD_REQ= all: ${PROG} clean: rm -f ${PROG} ${SRCS:.c=.o} ${PROG}:${SRCS:.c=.o} ${CC} -o ${PROG} ${SRCS:.c=.o} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} main.o: main.c ../../libcperciva/util/warnp.h simple_server.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c main.c -o main.o simple_server.o: simple_server.c ../../libcperciva/util/asprintf.h ../../libcperciva/events/events.h ../../libcperciva/network/network.h ../../libcperciva/util/sock.h ../../libcperciva/util/warnp.h simple_server.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c simple_server.c -o simple_server.o elasticarray.o: ../../libcperciva/datastruct/elasticarray.c ../../libcperciva/datastruct/elasticarray.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/datastruct/elasticarray.c -o elasticarray.o ptrheap.o: ../../libcperciva/datastruct/ptrheap.c ../../libcperciva/datastruct/elasticarray.h ../../libcperciva/datastruct/ptrheap.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/datastruct/ptrheap.c -o ptrheap.o timerqueue.o: ../../libcperciva/datastruct/timerqueue.c ../../libcperciva/datastruct/ptrheap.h ../../libcperciva/datastruct/timerqueue.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/datastruct/timerqueue.c -o timerqueue.o events_immediate.o: ../../libcperciva/events/events_immediate.c ../../libcperciva/datastruct/mpool.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_immediate.c -o events_immediate.o events_network.o: ../../libcperciva/events/events_network.c ../../libcperciva/util/ctassert.h ../../libcperciva/datastruct/elasticarray.h ../../libcperciva/util/warnp.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_network.c -o events_network.o events_network_selectstats.o: ../../libcperciva/events/events_network_selectstats.c ../../libcperciva/util/monoclock.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_network_selectstats.c -o events_network_selectstats.o events_timer.o: ../../libcperciva/events/events_timer.c ../../libcperciva/util/monoclock.h ../../libcperciva/datastruct/timerqueue.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_timer.c -o events_timer.o events.o: ../../libcperciva/events/events.c ../../libcperciva/datastruct/mpool.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events.c -o events.o network_accept.o: ../../libcperciva/network/network_accept.c ../../libcperciva/events/events.h ../../libcperciva/network/network.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/network/network_accept.c -o network_accept.o network_read.o: ../../libcperciva/network/network_read.c ../../libcperciva/events/events.h ../../libcperciva/network/network.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/network/network_read.c -o network_read.o asprintf.o: ../../libcperciva/util/asprintf.c ../../libcperciva/util/asprintf.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/asprintf.c -o asprintf.o monoclock.o: ../../libcperciva/util/monoclock.c ../../libcperciva/util/warnp.h ../../libcperciva/util/monoclock.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/monoclock.c -o monoclock.o sock.o: ../../libcperciva/util/sock.c ../../libcperciva/util/imalloc.h ../../libcperciva/util/warnp.h ../../libcperciva/util/sock.h ../../libcperciva/util/sock_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/sock.c -o sock.o warnp.o: ../../libcperciva/util/warnp.c ../../libcperciva/util/warnp.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/warnp.c -o warnp.o spiped-1.6.0/tests/nc-server/main.c000644 001751 001751 00000003121 13074276273 020604 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "warnp.h" #include "simple_server.h" #define MAX_CONNECTIONS 2 #define SHUTDOWN_AFTER 1 struct nc_cookie { FILE * out; }; /* A client sent a message. */ static int callback_snc_response(void * cookie, uint8_t * buf, size_t buflen) { struct nc_cookie * C = cookie; /* Write buffer to the previously-opened file. */ if (fwrite(buf, sizeof(uint8_t), buflen, C->out) != buflen) { warnp("fwrite"); goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } int main(int argc, char ** argv) { struct nc_cookie cookie; struct nc_cookie * C = &cookie; unsigned long port_long; in_port_t port; const char * filename; WARNP_INIT; /* Parse command-line arguments. */ if (argc < 3) { fprintf(stderr, "usage: %s PORT FILENAME\n", argv[0]); goto err0; } /* Parse port number. */ errno = 0; port_long = strtoul(argv[1], NULL, 0); if (errno || port_long > UINT16_MAX) { warnp("strtoul"); goto err0; } port = (in_port_t)port_long; /* Get output filename. */ filename = argv[2]; /* Open the output file; can be /dev/null. */ if ((C->out = fopen(filename, "wb")) == NULL) { warnp("fopen"); goto err0; } /* Run the server. */ if (simple_server(port, MAX_CONNECTIONS, SHUTDOWN_AFTER, &callback_snc_response, C)) { warn0("simple_server failed"); goto err1; } /* Write the output file. */ if (fclose(C->out) != 0) { warnp("fclose"); goto err1; } /* Success! */ exit(0); err1: fclose(C->out); err0: /* Failure! */ exit(1); } spiped-1.6.0/tests/nc-server/simple_server.c000644 001751 001751 00000020103 13074276273 022536 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "asprintf.h" #include "events.h" #include "network.h" #include "sock.h" #include "warnp.h" #include "simple_server.h" #define BUFLEN 8192 struct accept_state { int s; int conndone; int shutdown_requested; size_t shutdown_after; size_t shutdown_current; size_t nconn; size_t nconn_max; void * accept_cookie; struct conn_list_node * conn_cookies; void * caller_cookie; int (* callback_nc_message)(void *, uint8_t *, size_t); }; /* Doubly linked list. */ struct conn_list_node { /* General "dispatch"-level info. */ struct conn_list_node * prev; struct conn_list_node * next; struct accept_state * A; /* Reading a network message. */ int sock_read; uint8_t buf[BUFLEN]; void * network_read_cookie; }; /* Forward definitions. */ static int callback_read(void *, ssize_t); static int callback_gotconn(void *, int); static int conndied(struct conn_list_node *); static int doaccept(struct accept_state *); static int drop(struct conn_list_node *); static void simple_server_shutdown(void * cookie); /* Non-blocking accept, if we can have more connections. */ static int doaccept(struct accept_state * A) { int rc = 0; /* 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) { warnp("network_accept"); rc = -1; } } /* Return success/fail status. */ return (rc); } /* A connection has closed. Accept more if necessary. */ static int conndied(struct conn_list_node * node_ptr) { struct accept_state * A = node_ptr->A; /* We should always have a non-empty list of conn_cookies. */ assert(A->conn_cookies != NULL); /* We've lost a connection. */ A->nconn -= 1; /* Adjust shutdown counter, if relevant. */ if (A->shutdown_after > 0) { A->shutdown_current++; if (A->shutdown_current >= A->shutdown_after) A->shutdown_requested = 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 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"); goto err0; } /* We have gained a connection. */ A->nconn += 1; /* Create new conn_list_node. */ if ((node_new = malloc(sizeof(struct conn_list_node))) == NULL) { warn0("Out of memory"); goto err1; } node_new->prev = NULL; node_new->next = NULL; node_new->A = A; node_new->sock_read = s; /* Schedule reading from this connection. */ if ((node_new->network_read_cookie = network_read(node_new->sock_read, node_new->buf, BUFLEN, 1, callback_read, node_new)) == NULL) { warnp("network_read"); goto err2; } /* 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)) { warn0("doaccept"); goto err0; } /* Success! */ return (0); err2: free(node_new); err1: A->nconn -= 1; close(s); err0: /* Failure! */ return (-1); } /* We received a message. */ static int callback_read(void * cookie, ssize_t lenread) { struct conn_list_node * R = cookie; struct accept_state * A = R->A; /* Cookie is no longer valid. */ R->network_read_cookie = NULL; /* If we have a message. */ if (lenread > 0) { /* Handle it with the parent code. */ A->callback_nc_message(A->caller_cookie, R->buf, (size_t)lenread); /* Try to read some more data. */ if ((R->network_read_cookie = network_read(R->sock_read, R->buf, BUFLEN, 1, callback_read, R)) == NULL) { warnp("network_read"); goto err0; } } else if (lenread == 0) { if (drop(R)) { warn0("drop"); goto err0; } } else { warn0("Failed to read from network"); A->conndone = 1; goto err0; } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Drop connection. */ static int drop(struct conn_list_node * node_ptr) { /* If we still have an active read cookie, cancel it. */ if (node_ptr->network_read_cookie != NULL) network_read_cancel(node_ptr->network_read_cookie); /* Close the incoming connection. */ if (close(node_ptr->sock_read) == -1) { warn0("close"); goto err0; } /* Clean up the node. */ conndied(node_ptr); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * simple_server_shutdown(cookie): * Stop and free memory associated with the ${cookie}. */ static void simple_server_shutdown(void * cookie) { struct accept_state * A = cookie; struct conn_list_node * node_ptr; /* Cancel any further accepts. */ if (A->accept_cookie != NULL) network_accept_cancel(A->accept_cookie); /* * Shut down any open connections. drop() will call * conndied(), which removes the relevant conn_list_node * from the list of conn_cookies. */ while (A->conn_cookies != NULL) { /* Remove nodes from the list. */ node_ptr = A->conn_cookies; if (drop(A->conn_cookies)) warn0("drop"); /* * Force the clang static analyzer to realize that * the A->conn_cookies pointer changed. */ assert(node_ptr != A->conn_cookies); } /* Close socket and free memory. */ if (close(A->s) == -1) warn0("close"); free(A); } /** * simple_server(port, nconn_max, shutdown_after, callback, caller_cookie): * Run a server which accepts up to ${nconn_max} connections to port ${port}. * After receiving a message, call ${callback} and pass it the * ${caller_cookie}, along with the message. Automatically shuts itself down * after ${shutdown_after} connections have been dropped. */ int simple_server(in_port_t port, size_t nconn_max, size_t shutdown_after, int (* callback_nc_message)(void *, uint8_t *, size_t), void * caller_cookie) { struct accept_state * A; char * addr; struct sock_addr ** sas; int sock; /* Create an address string suitable for sock_resolve. */ if (asprintf(&addr, "[0.0.0.0]:%" PRIu16, port) == -1) { warn0("Out of memory"); goto err0; } /* Resolve the address. */ if ((sas = sock_resolve(addr)) == NULL) { warn0("sock_resolve"); goto err1; } /* Create a socket, bind it, mark it as listening. */ if ((sock = sock_listener(sas[0])) == -1) { warn0("sock_listener"); goto err2; } /* Bake a cookie for the server. */ if ((A = malloc(sizeof(struct accept_state))) == NULL) { warnp("Out of memory"); goto err3; } A->s = sock; A->conndone = 0; A->shutdown_requested = 0; A->shutdown_after = shutdown_after; A->shutdown_current = 0; A->nconn = 0; A->nconn_max = nconn_max; A->accept_cookie = NULL; A->conn_cookies = NULL; A->callback_nc_message = callback_nc_message; A->caller_cookie = caller_cookie; /* Accept a connection. */ if (doaccept(A)) { warn0("doaccept"); goto err4; } /* Loop until we die. */ if (events_spin(&A->conndone)) { warnp("Error running event loop"); goto err4; } /* Clean up. */ sock_addr_freelist(sas); free(addr); simple_server_shutdown(A); events_shutdown(); /* Success! */ return (0); err4: free(A); err3: close(sock); err2: sock_addr_freelist(sas); err1: free(addr); events_shutdown(); err0: /* Failure! */ return (-1); } spiped-1.6.0/tests/nc-server/simple_server.h000644 001751 001751 00000001102 13074276273 022541 0ustar00cpercivacperciva000000 000000 #ifndef SIMPLE_SERVER_H #define SIMPLE_SERVER_H #include #include #include /** * simple_server(port, nconn_max, shutdown_after, callback, caller_cookie): * Run a server which accepts up to ${nconn_max} connections to port ${port}. * After receiving a message, call ${callback} and pass it the * ${caller_cookie}, along with the message. Automatically shuts itself down * after ${shutdown_after} connections have been dropped. */ int simple_server(in_port_t, size_t, size_t, int (*)(void *, uint8_t *, size_t), void *); #endif spiped-1.6.0/tests/valgrind/Makefile000644 001751 001751 00000000740 13101223046 021037 0ustar00cpercivacperciva000000 000000 .POSIX: # AUTOGENERATED FILE, DO NOT EDIT PROG=potential-memleaks MAN1= SRCS=potential-memleaks.c IDIRS= LDADD_REQ= all: ${PROG} clean: rm -f ${PROG} ${SRCS:.c=.o} ${PROG}:${SRCS:.c=.o} ${CC} -o ${PROG} ${SRCS:.c=.o} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} potential-memleaks.o: potential-memleaks.c ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c potential-memleaks.c -o potential-memleaks.o spiped-1.6.0/tests/valgrind/potential-memleaks.c000644 001751 001751 00000000360 13042745752 023355 0ustar00cpercivacperciva000000 000000 #include /* Problem with FreeBSD 11.0 merely linking with -lrt. */ static void pl_freebsd_link_lrt() { /* Do nothing. */ } int main() { /* Test potential memory leaks. */ pl_freebsd_link_lrt(); /* Success! */ exit(0); } spiped-1.6.0/tests/nc-client/Makefile000644 001751 001751 00000012720 13101223045 021105 0ustar00cpercivacperciva000000 000000 .POSIX: # AUTOGENERATED FILE, DO NOT EDIT PROG=nc-client MAN1= SRCS=main.c elasticarray.c ptrheap.c timerqueue.c events_immediate.c events_network.c events_network_selectstats.c events_timer.c events.c network_connect.c network_read.c network_write.c monoclock.c sock.c warnp.c IDIRS=-I../../libcperciva/datastruct -I../../libcperciva/events -I../../libcperciva/network -I../../libcperciva/util LDADD_REQ= all: ${PROG} clean: rm -f ${PROG} ${SRCS:.c=.o} ${PROG}:${SRCS:.c=.o} ${CC} -o ${PROG} ${SRCS:.c=.o} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} main.o: main.c ../../libcperciva/events/events.h ../../libcperciva/network/network.h ../../libcperciva/util/sock.h ../../libcperciva/util/warnp.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c main.c -o main.o elasticarray.o: ../../libcperciva/datastruct/elasticarray.c ../../libcperciva/datastruct/elasticarray.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/datastruct/elasticarray.c -o elasticarray.o ptrheap.o: ../../libcperciva/datastruct/ptrheap.c ../../libcperciva/datastruct/elasticarray.h ../../libcperciva/datastruct/ptrheap.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/datastruct/ptrheap.c -o ptrheap.o timerqueue.o: ../../libcperciva/datastruct/timerqueue.c ../../libcperciva/datastruct/ptrheap.h ../../libcperciva/datastruct/timerqueue.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/datastruct/timerqueue.c -o timerqueue.o events_immediate.o: ../../libcperciva/events/events_immediate.c ../../libcperciva/datastruct/mpool.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_immediate.c -o events_immediate.o events_network.o: ../../libcperciva/events/events_network.c ../../libcperciva/util/ctassert.h ../../libcperciva/datastruct/elasticarray.h ../../libcperciva/util/warnp.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_network.c -o events_network.o events_network_selectstats.o: ../../libcperciva/events/events_network_selectstats.c ../../libcperciva/util/monoclock.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_network_selectstats.c -o events_network_selectstats.o events_timer.o: ../../libcperciva/events/events_timer.c ../../libcperciva/util/monoclock.h ../../libcperciva/datastruct/timerqueue.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events_timer.c -o events_timer.o events.o: ../../libcperciva/events/events.c ../../libcperciva/datastruct/mpool.h ../../libcperciva/events/events.h ../../libcperciva/events/events_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/events/events.c -o events.o network_connect.o: ../../libcperciva/network/network_connect.c ../../libcperciva/events/events.h ../../libcperciva/util/sock.h ../../libcperciva/network/network.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/network/network_connect.c -o network_connect.o network_read.o: ../../libcperciva/network/network_read.c ../../libcperciva/events/events.h ../../libcperciva/network/network.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/network/network_read.c -o network_read.o network_write.o: ../../libcperciva/network/network_write.c ../../libcperciva/events/events.h ../../libcperciva/util/warnp.h ../../libcperciva/network/network.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/network/network_write.c -o network_write.o monoclock.o: ../../libcperciva/util/monoclock.c ../../libcperciva/util/warnp.h ../../libcperciva/util/monoclock.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/monoclock.c -o monoclock.o sock.o: ../../libcperciva/util/sock.c ../../libcperciva/util/imalloc.h ../../libcperciva/util/warnp.h ../../libcperciva/util/sock.h ../../libcperciva/util/sock_internal.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/sock.c -o sock.o warnp.o: ../../libcperciva/util/warnp.c ../../libcperciva/util/warnp.h ${CC} ${CFLAGS} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} -c ../../libcperciva/util/warnp.c -o warnp.o spiped-1.6.0/tests/nc-client/main.c000644 001751 001751 00000011202 13074276273 020553 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include #include "events.h" #include "network.h" #include "sock.h" #include "warnp.h" struct senddata { char * buffer; ssize_t nchars; int socket; int conndone; void * connect_cookie; void * write_cookie; void * read_cookie; uint8_t dummybuf[1]; }; /* Forward declaration. */ static int callback_wrote(void *, ssize_t); static int callback_stopping(void * cookie, ssize_t lenread) { struct senddata * send = (struct senddata *)cookie; /* We are no longer reading. */ send->read_cookie = NULL; /* Check results. */ if (lenread == -1) { warnp("network_read received"); goto err0; } else { /* We should have received nothing. */ if (lenread != 0) { warn0("network_read received non-zero data"); goto err0; } } /* Close connection. */ send->conndone = 1; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Send data from stdin to a socket, or close the connection. */ static int send_input(void * cookie) { struct senddata * send = (struct senddata *)cookie; size_t len = 0; /* Read data from stdin. */ if ((send->nchars = getline(&send->buffer, &len, stdin)) != -1) { /* Send data to server. */ if ((send->write_cookie = network_write(send->socket, (uint8_t *)send->buffer, (size_t)send->nchars, (size_t)send->nchars, callback_wrote, cookie)) == NULL) { warn0("network_write failure"); goto err0; } } else { /* If we didn't get an EOF, then there was an error. */ if (!feof(stdin)) { warn0("getline(..., ..., stdin)"); goto err0; } /* We're not going to send anything else. */ if (shutdown(send->socket, SHUT_WR)) { warnp("shutdown"); goto err0; } /* * The server should not send any data back, but attempting to * read will detect when the other end of the socket is closed. */ if ((send->read_cookie = network_read(send->socket, send->dummybuf, 1, 1, callback_stopping, cookie)) == NULL) { warn0("network_read initialize"); goto err0; } } /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* Finished writing data; look for more from stdin. */ static int callback_wrote(void * cookie, ssize_t lenwrit) { struct senddata * send = (struct senddata *)cookie; /* We are no longer writing. */ send->write_cookie = NULL; /* Check results. */ if (lenwrit == -1) { warnp("network_write send"); goto err0; } else { /* We should have sent everything. */ assert(lenwrit == send->nchars); } /* Clear the buffer that was used. */ free(send->buffer); send->buffer = NULL; return send_input(cookie); err0: /* Failure! */ return (-1); } /* Got a connection; look for data from stdin. */ static int callback_connected(void * cookie, int socket) { struct senddata * send = (struct senddata *)cookie; /* We are no longer connecting. */ send->connect_cookie = NULL; /* Check that the connection did not fail. */ if (socket == -1) { warn0("failed to connect"); goto err0; } /* Record socket for future use. */ send->socket = socket; return send_input(cookie); err0: /* Failure! */ return (-1); } int main(int argc, char ** argv) { /* Command-line parameter. */ const char * addr; /* Working variables. */ struct sock_addr ** sas_t; struct senddata send_allocated; struct senddata * send = &send_allocated; WARNP_INIT; /* Parse command-line arguments. */ if (argc < 2) { fprintf(stderr, "%s ADDRESS\n", argv[0]); goto err0; } addr = argv[1]; /* Initialize cookie. */ send->buffer = NULL; send->conndone = 0; send->connect_cookie = NULL; send->write_cookie = NULL; send->read_cookie = NULL; /* Resolve target address. */ if ((sas_t = sock_resolve(addr)) == NULL) { warnp("Error resolving socket address: %s", addr); goto err1; } if (sas_t[0] == NULL) { warn0("No addresses found for %s", addr); goto err2; } /* Connect to target. */ if ((send->connect_cookie = network_connect(sas_t, callback_connected, send)) == NULL) { warn0("Error connecting"); goto err2; } /* Loop until we're done with the connection. */ if (events_spin(&send->conndone)) { warn0("Error running event loop"); goto err3; } /* Clean up. */ events_shutdown(); sock_addr_freelist(sas_t); free(send->buffer); /* Success! */ exit(0); err3: if (send->connect_cookie != NULL) network_connect_cancel(send->connect_cookie); if (send->write_cookie != NULL) network_write_cancel(send->write_cookie); if (send->read_cookie != NULL) network_read_cancel(send->read_cookie); events_shutdown(); err2: sock_addr_freelist(sas_t); err1: free(send->buffer); err0: /* Failure! */ exit(1); } spiped-1.6.0/proto/proto_conn.c000644 001751 001751 00000025023 13042745752 020140 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include #include "events.h" #include "network.h" #include "sock.h" #include "proto_handshake.h" #include "proto_pipe.h" #include "proto_crypt.h" #include "proto_conn.h" struct conn_state { int (* callback_dead)(void *); void * cookie; struct sock_addr ** sas; int decr; int nopfs; int requirepfs; int nokeepalive; const struct proto_secret * K; double timeo; int s; int t; void * connect_cookie; void * connect_timeout_cookie; void * handshake_cookie; void * handshake_timeout_cookie; struct proto_keys * k_f; struct proto_keys * k_r; void * pipe_f; void * pipe_r; int stat_f; int stat_r; }; static int callback_connect_done(void *, int); static int callback_connect_timeout(void *); static int callback_handshake_done(void *, struct proto_keys *, struct proto_keys *); static int callback_handshake_timeout(void *); static int callback_pipestatus(void *); /* Start a handshake. */ static int starthandshake(struct conn_state * C, int s, int decr) { /* Start the handshake timer. */ if ((C->handshake_timeout_cookie = events_timer_register_double( callback_handshake_timeout, C, C->timeo)) == NULL) goto err0; /* Start the handshake. */ if ((C->handshake_cookie = proto_handshake(s, decr, C->nopfs, C->requirepfs, C->K, callback_handshake_done, C)) == NULL) goto err1; /* Success! */ return (0); err1: events_timer_cancel(C->handshake_timeout_cookie); C->handshake_timeout_cookie = NULL; err0: /* Failure! */ return (-1); } /* Launch the two pipes. */ static int launchpipes(struct conn_state * C) { int on = C->nokeepalive ? 0 : 1; int one = 1; /* * Attempt to turn keepalives on or off as requested. We ignore * failures here since the sockets might not be of a type for which * SO_KEEPALIVE is valid -- it is a socket level option, but protocol * specific. In particular, it has no sensible meaning for UNIX * sockets. */ (void)setsockopt(C->s, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); (void)setsockopt(C->t, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); /** * Attempt to turn off nagling on both sockets. If the TCP stack has * enough window space that it is always able to send packets, then on * the encrypted end this will result in every 1060-byte spiped packet * getting its own TCP segment, including 40 bytes of TCP/IP headers; * this is fine. On the unencrypted end, we might send a single byte * of data with 40 bytes of TCP/IP headers; this is not so good. * * However, a write over the unencrypted connection will only happen * after an spiped packet has been read from the encrypted connection, * so the worst case is 80 bytes of TCP/IP headers per 1061 bytes of * TCP/IP payload (this may still be only a single byte of spiped * payload, but that is not relevant to the question of overhead from * small TCP/IP segments); and while the two sockets might not be on * the same network, if they are on different networks it is almost * guaranteed that the network over which the encrypted connection is * passing would be a wider-area network which is both less secure and * more expensive. Consequently, the maximum TCP/IP overhead ratio of * 80/1061 is almost certain to hold even with weighted byte costs. * * We ignore errors since (as with keep-alives) we may be dealing with * a non-TCP socket; and also because while POSIX requires TCP_NODELAY * to be defined, it is not required to be implemented as a socket * option. */ (void)setsockopt(C->s, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); (void)setsockopt(C->t, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); /* Create two pipes. */ if ((C->pipe_f = proto_pipe(C->s, C->t, C->decr, C->k_f, &C->stat_f, callback_pipestatus, C)) == NULL) goto err0; if ((C->pipe_r = proto_pipe(C->t, C->s, !C->decr, C->k_r, &C->stat_r, callback_pipestatus, C)) == NULL) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * proto_conn_drop(conn_cookie): * Drop connection and frees memory associated with ${conn_cookie}. Return * success or failure. */ int proto_conn_drop(void * conn_cookie) { struct conn_state * C = conn_cookie; int rc; /* Close the incoming connection. */ close(C->s); /* Close the outgoing connection if it is open. */ if (C->t != -1) close(C->t); /* Stop connecting if a connection is in progress. */ if (C->connect_cookie != NULL) network_connect_cancel(C->connect_cookie); /* Free the target addresses if we haven't already done so. */ sock_addr_freelist(C->sas); /* Stop handshaking if a handshake is in progress. */ if (C->handshake_cookie != NULL) proto_handshake_cancel(C->handshake_cookie); /* Kill timeouts if they are pending. */ if (C->connect_timeout_cookie != NULL) events_timer_cancel(C->connect_timeout_cookie); if (C->handshake_timeout_cookie != NULL) events_timer_cancel(C->handshake_timeout_cookie); /* Free protocol keys. */ proto_crypt_free(C->k_f); proto_crypt_free(C->k_r); /* Shut down pipes. */ if (C->pipe_f != NULL) proto_pipe_cancel(C->pipe_f); if (C->pipe_r != NULL) proto_pipe_cancel(C->pipe_r); /* Notify the upstream that we've dropped a connection. */ rc = (C->callback_dead)(C->cookie); /* Free the connection cookie. */ free(C); /* Return success/fail status. */ return (rc); } /** * proto_conn_create(s, sas, decr, nopfs, requirepfs, nokeepalive, K, timeo, * callback_dead, cookie): * Create a connection with one end at ${s} and the other end connecting to * the target addresses ${sas}. If ${decr} is 0, encrypt the outgoing data; * if ${decr} is nonzero, decrypt the incoming data. If ${nopfs} is non-zero, * don't use perfect forward secrecy. If ${requirepfs} is non-zero, drop * the connection if the other end tries to disable perfect forward secrecy. * Enable transport layer keep-alives (if applicable) on both sockets if and * only if ${nokeepalive} is zero. Drop the connection if the handshake or * connecting to the target takes more than ${timeo} seconds. When the * connection is dropped, invoke ${callback_dead}(${cookie}). Free ${sas} * once it is no longer needed. Return a cookie which can be passed to * proto_conn_drop. */ void * proto_conn_create(int s, struct sock_addr ** sas, int decr, int nopfs, int requirepfs, int nokeepalive, const struct proto_secret * K, double timeo, int (* callback_dead)(void *), void * cookie) { struct conn_state * C; /* Bake a cookie for this connection. */ if ((C = malloc(sizeof(struct conn_state))) == NULL) goto err0; C->callback_dead = callback_dead; C->cookie = cookie; C->sas = sas; C->decr = decr; C->nopfs = nopfs; C->requirepfs = requirepfs; C->nokeepalive = nokeepalive; C->K = K; C->timeo = timeo; C->s = s; C->t = -1; C->connect_cookie = NULL; C->connect_timeout_cookie = NULL; C->handshake_cookie = NULL; C->handshake_timeout_cookie = NULL; C->k_f = C->k_r = NULL; C->pipe_f = C->pipe_r = NULL; C->stat_f = C->stat_r = 1; /* Start the connect timer. */ if ((C->connect_timeout_cookie = events_timer_register_double( callback_connect_timeout, C, C->timeo)) == NULL) goto err1; /* Connect to target. */ if ((C->connect_cookie = network_connect(C->sas, callback_connect_done, C)) == NULL) goto err2; /* If we're decrypting, start the handshake. */ if (C->decr) { if (starthandshake(C, C->s, C->decr)) goto err3; } /* Success! */ return (C); err3: network_connect_cancel(C->connect_cookie); err2: events_timer_cancel(C->connect_timeout_cookie); err1: free(C); err0: /* Failure! */ return (NULL); } /* We have connected to the target. */ static int callback_connect_done(void * cookie, int t) { struct conn_state * C = cookie; /* This connection attempt is no longer pending. */ C->connect_cookie = NULL; /* Don't need the target address any more. */ sock_addr_freelist(C->sas); C->sas = NULL; /* We beat the clock. */ events_timer_cancel(C->connect_timeout_cookie); C->connect_timeout_cookie = NULL; /* Did we manage to connect? */ if ((C->t = t) == -1) return (proto_conn_drop(C)); /* If we're encrypting, start the handshake. */ if (!C->decr) { if (starthandshake(C, C->t, C->decr)) goto err1; } /* If the handshake already finished, start shuttling data. */ if ((C->t != -1) && (C->k_f != NULL) && (C->k_r != NULL)) { if (launchpipes(C)) goto err1; } /* Success! */ return (0); err1: proto_conn_drop(C); /* Failure! */ return (-1); } /* Connecting to the target took too long. */ static int callback_connect_timeout(void * cookie) { struct conn_state * C = cookie; /* This timeout is no longer pending. */ C->connect_timeout_cookie = NULL; /* * We could free C->sas here, but from a semantic point of view it * could still be in use by the not-yet-cancelled connect operation. * Instead, we free it in proto_conn_drop, after cancelling the * connect. */ /* Drop the connection. */ return (proto_conn_drop(C)); } /* We have performed the protocol handshake. */ static int callback_handshake_done(void * cookie, struct proto_keys * f, struct proto_keys * r) { struct conn_state * C = cookie; /* The handshake is no longer in progress. */ C->handshake_cookie = NULL; /* We beat the clock. */ events_timer_cancel(C->handshake_timeout_cookie); C->handshake_timeout_cookie = NULL; /* If the protocol handshake failed, drop the connection. */ if ((f == NULL) && (r == NULL)) return (proto_conn_drop(C)); /* We should have two keys. */ assert(f != NULL); assert(r != NULL); /* Record the keys so we can free them later. */ C->k_f = f; C->k_r = r; /* If we already connected to the target, start shuttling data. */ if ((C->t != -1) && (C->k_f != NULL) && (C->k_r != NULL)) { if (launchpipes(C)) goto err1; } /* Success! */ return (0); err1: proto_conn_drop(C); /* Failure! */ return (-1); } /* The protocol handshake took too long. */ static int callback_handshake_timeout(void * cookie) { struct conn_state * C = cookie; /* This timeout is no longer pending. */ C->handshake_timeout_cookie = NULL; /* Drop the connection. */ return (proto_conn_drop(C)); } /* The status of one of the directions has changed. */ static int callback_pipestatus(void * cookie) { struct conn_state * C = cookie; /* If we have an error in either direction, kill the connection. */ if ((C->stat_f == -1) || (C->stat_r == -1)) return (proto_conn_drop(C)); /* If both directions have been shut down, kill the connection. */ if ((C->stat_f == 0) && (C->stat_r == 0)) return (proto_conn_drop(C)); /* Nothing to do. */ return (0); } spiped-1.6.0/proto/proto_conn.h000644 001751 001751 00000002503 13042745752 020143 0ustar00cpercivacperciva000000 000000 #ifndef _PROTO_CONN_H_ #define _PROTO_CONN_H_ /* Opaque structures. */ struct proto_secret; struct sock_addr; /** * proto_conn_create(s, sas, decr, nopfs, requirepfs, nokeepalive, K, timeo, * callback_dead, cookie): * Create a connection with one end at ${s} and the other end connecting to * the target addresses ${sas}. If ${decr} is 0, encrypt the outgoing data; * if ${decr} is nonzero, decrypt the outgoing data. If ${nopfs} is non-zero, * don't use perfect forward secrecy. If ${requirepfs} is non-zero, drop * the connection if the other end tries to disable perfect forward secrecy. * Enable transport layer keep-alives (if applicable) on both sockets if and * only if ${nokeepalive} is zero. Drop the connection if the handshake or * connecting to the target takes more than ${timeo} seconds. When the * connection is dropped, invoke ${callback_dead}(${cookie}). Free ${sas} * once it is no longer needed. Return a cookie which can be passed to * proto_conn_drop. */ void * proto_conn_create(int, struct sock_addr **, int, int, int, int, const struct proto_secret *, double, int (*)(void *), void *); /** * proto_conn_drop(conn_cookie): * Drop connection and frees memory associated with ${conn_cookie}. Return * success or failure. */ int proto_conn_drop(void * conn_cookie); #endif /* !_CONN_H_ */ spiped-1.6.0/proto/proto_crypt.c000644 001751 001751 00000023750 13042745752 020351 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "crypto_aes.h" #include "crypto_aesctr.h" #include "crypto_verify_bytes.h" #include "sha256.h" #include "sysendian.h" #include "warnp.h" #include "proto_crypt.h" struct proto_secret { uint8_t K[32]; }; struct proto_keys { struct crypto_aes_key * k_aes; uint8_t k_hmac[32]; uint64_t pnum; }; /** * mkkeypair(kbuf): * Convert the 64 bytes of ${kbuf} into a protocol key structure. */ static struct proto_keys * mkkeypair(uint8_t kbuf[64]) { struct proto_keys * k; /* Allocate a structure. */ if ((k = malloc(sizeof(struct proto_keys))) == NULL) goto err0; /* Expand the AES key. */ if ((k->k_aes = crypto_aes_key_expand(&kbuf[0], 32)) == NULL) goto err1; /* Fill in HMAC key. */ memcpy(k->k_hmac, &kbuf[32], 32); /* The first packet will be packet number zero. */ k->pnum = 0; /* Success! */ return (k); err1: free(k); err0: /* Failure! */ return (NULL); } /** * proto_crypt_secret(filename): * Read the key file ${filename} and return a protocol secret structure. */ struct proto_secret * proto_crypt_secret(const char * filename) { SHA256_CTX ctx; FILE * f; struct proto_secret * K; uint8_t buf[BUFSIZ]; size_t lenread; /* Allocate a protocol secret structure. */ if ((K = malloc(sizeof(struct proto_secret))) == NULL) goto err0; /* Open the file, or use stdin if requested. */ if (strcmp(filename, STDIN_FILENAME) == 0) { f = stdin; } else if ((f = fopen(filename, "r")) == NULL) { warnp("Cannot open file: %s", filename); goto err1; } /* Initialize the SHA256 hash context. */ SHA256_Init(&ctx); /* Read the file until we hit EOF. */ while ((lenread = fread(buf, 1, BUFSIZ, f)) > 0) SHA256_Update(&ctx, buf, lenread); /* Did we hit EOF? */ if (!feof(f)) { if (f == stdin) { warnp("Error reading from stdin"); } else { warnp("Error reading file: %s", filename); } goto err2; } /* Close the file if it isn't stdin. */ if (f != stdin) fclose(f); /* Compute the final hash. */ SHA256_Final(K->K, &ctx); /* Success! */ return (K); err2: if (f != stdin) fclose(f); err1: free(K); err0: /* Failure! */ return (NULL); } /** * proto_crypt_dhmac(K, nonce_l, nonce_r, dhmac_l, dhmac_r, decr): * Using the key file hash ${K}, and the local and remote nonces ${nonce_l} * and ${nonce_r}, compute the local and remote diffie-hellman parameter MAC * keys ${dhmac_l} and ${dhmac_r}. If ${decr} is non-zero, "local" == "S" * and "remote" == "C"; otherwise the assignments are opposite. */ void proto_crypt_dhmac(const struct proto_secret * K, const uint8_t nonce_l[PCRYPT_NONCE_LEN], const uint8_t nonce_r[PCRYPT_NONCE_LEN], uint8_t dhmac_l[PCRYPT_DHMAC_LEN], uint8_t dhmac_r[PCRYPT_DHMAC_LEN], int decr) { uint8_t nonce_CS[PCRYPT_NONCE_LEN * 2]; uint8_t dk_1[PCRYPT_DHMAC_LEN * 2]; const uint8_t * nonce_c, * nonce_s; uint8_t * dhmac_c, * dhmac_s; /* Figure out how {c, s} maps to {l, r}. */ nonce_c = decr ? nonce_r : nonce_l; dhmac_c = decr ? dhmac_r : dhmac_l; nonce_s = decr ? nonce_l : nonce_r; dhmac_s = decr ? dhmac_l : dhmac_r; /* Copy in nonces (in the right order). */ memcpy(&nonce_CS[0], nonce_c, PCRYPT_NONCE_LEN); memcpy(&nonce_CS[PCRYPT_NONCE_LEN], nonce_s, PCRYPT_NONCE_LEN); /* Compute dk_1. */ PBKDF2_SHA256(K->K, 32, nonce_CS, PCRYPT_NONCE_LEN * 2, 1, dk_1, PCRYPT_DHMAC_LEN * 2); /* Copy out diffie-hellman parameter MAC keys (in the right order). */ memcpy(dhmac_c, &dk_1[0], PCRYPT_DHMAC_LEN); memcpy(dhmac_s, &dk_1[PCRYPT_DHMAC_LEN], PCRYPT_DHMAC_LEN); } /** * is_not_one(x, len): * Return non-zero if the big-endian value stored at (${x}, ${len}) is not * equal to 1. */ static int is_not_one(const uint8_t * x, size_t len) { size_t i; char y; for (i = 0, y = 0; i < len - 1; i++) { y |= x[i]; } return (y | (x[len - 1] - 1)); } /** * proto_crypt_dh_validate(yh_r, dhmac_r, requirepfs): * Return non-zero if the value ${yh_r} received from the remote party is not * correctly MACed using the diffie-hellman parameter MAC key ${dhmac_r}, or * if the included y value is >= the diffie-hellman group modulus, or if * ${requirepfs} is non-zero and the included y value is 1. */ int proto_crypt_dh_validate(const uint8_t yh_r[PCRYPT_YH_LEN], const uint8_t dhmac_r[PCRYPT_DHMAC_LEN], int requirepfs) { uint8_t hbuf[32]; /* Compute HMAC. */ HMAC_SHA256_Buf(dhmac_r, PCRYPT_DHMAC_LEN, yh_r, CRYPTO_DH_PUBLEN, hbuf); /* Check that the MAC matches. */ if (crypto_verify_bytes(&yh_r[CRYPTO_DH_PUBLEN], hbuf, 32)) return (1); /* Sanity-check the diffie-hellman value. */ if (crypto_dh_sanitycheck(&yh_r[0])) return (1); /* If necessary, enforce that the diffie-hellman value is != 1. */ if (requirepfs) { if (! is_not_one(&yh_r[0], CRYPTO_DH_PUBLEN)) return (1); } /* Everything is good. */ return (0); } /** * proto_crypt_dh_generate(yh_l, x, dhmac_l, nopfs): * Using the MAC key ${dhmac_l}, generate the MACed diffie-hellman handshake * parameter ${yh_l}. Store the diffie-hellman private value in ${x}. If * ${nopfs} is non-zero, skip diffie-hellman generation and use y = 1. */ int proto_crypt_dh_generate(uint8_t yh_l[PCRYPT_YH_LEN], uint8_t x[PCRYPT_X_LEN], const uint8_t dhmac_l[PCRYPT_DHMAC_LEN], int nopfs) { /* Are we skipping the diffie-hellman generation? */ if (nopfs) { /* Set y_l to a big-endian 1. */ memset(yh_l, 0, CRYPTO_DH_PUBLEN - 1); yh_l[CRYPTO_DH_PUBLEN - 1] = 1; } else { /* Generate diffie-hellman parameters x and y. */ if (crypto_dh_generate(yh_l, x)) goto err0; } /* Append an HMAC. */ HMAC_SHA256_Buf(dhmac_l, PCRYPT_DHMAC_LEN, yh_l, CRYPTO_DH_PUBLEN, &yh_l[CRYPTO_DH_PUBLEN]); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * proto_crypt_mkkeys(K, nonce_l, nonce_r, yh_r, x, nopfs, decr, eh_c, eh_s): * Using the protocol secret ${K}, the local and remote nonces ${nonce_l} and * ${nonce_r}, the remote MACed diffie-hellman handshake parameter ${yh_r}, * and the local diffie-hellman secret ${x}, generate the keys ${eh_c} and * ${eh_s}. If ${nopfs} is non-zero, we are performing weak handshaking and * y_SC is set to 1 rather than being computed. If ${decr} is non-zero, * "local" == "S" and "remote" == "C"; otherwise the assignments are opposite. */ int proto_crypt_mkkeys(const struct proto_secret * K, const uint8_t nonce_l[PCRYPT_NONCE_LEN], const uint8_t nonce_r[PCRYPT_NONCE_LEN], const uint8_t yh_r[PCRYPT_YH_LEN], const uint8_t x[PCRYPT_X_LEN], int nopfs, int decr, struct proto_keys ** eh_c, struct proto_keys ** eh_s) { uint8_t nonce_y[PCRYPT_NONCE_LEN * 2 + CRYPTO_DH_KEYLEN]; uint8_t dk_2[128]; const uint8_t * nonce_c, * nonce_s; /* Copy in nonces (in the right order). */ nonce_c = decr ? nonce_r : nonce_l; nonce_s = decr ? nonce_l : nonce_r; memcpy(&nonce_y[0], nonce_c, PCRYPT_NONCE_LEN); memcpy(&nonce_y[PCRYPT_NONCE_LEN], nonce_s, PCRYPT_NONCE_LEN); /* Are we bypassing the diffie-hellman computation? */ if (nopfs) { /* We sent y_l = 1, so y_SC is also 1. */ memset(&nonce_y[PCRYPT_NONCE_LEN * 2], 0, CRYPTO_DH_KEYLEN - 1); nonce_y[PCRYPT_NONCE_LEN * 2 + CRYPTO_DH_KEYLEN - 1] = 1; } else { /* Perform the diffie-hellman computation. */ if (crypto_dh_compute(yh_r, x, &nonce_y[PCRYPT_NONCE_LEN * 2])) goto err0; } /* Compute dk_2. */ PBKDF2_SHA256(K->K, 32, nonce_y, PCRYPT_NONCE_LEN * 2 + CRYPTO_DH_KEYLEN, 1, dk_2, 128); /* Create key structures. */ if ((*eh_c = mkkeypair(&dk_2[0])) == NULL) goto err0; if ((*eh_s = mkkeypair(&dk_2[64])) == NULL) goto err1; /* Success! */ return (0); err1: proto_crypt_free(*eh_c); err0: /* Failure! */ return (-1); } /** * proto_crypt_enc(ibuf, len, obuf, k): * Encrypt ${len} bytes from ${ibuf} into PCRYPT_ESZ bytes using the keys in * ${k}, and write the result into ${obuf}. */ void proto_crypt_enc(uint8_t * ibuf, size_t len, uint8_t obuf[PCRYPT_ESZ], struct proto_keys * k) { HMAC_SHA256_CTX ctx; uint8_t pnum_exp[8]; /* Sanity-check the length. */ assert(len <= PCRYPT_MAXDSZ); /* Copy the decrypted data into the encrypted buffer. */ memcpy(obuf, ibuf, len); /* Pad up to PCRYPT_MAXDSZ with zeroes. */ memset(&obuf[len], 0, PCRYPT_MAXDSZ - len); /* Add the length. */ be32enc(&obuf[PCRYPT_MAXDSZ], (uint32_t)len); /* Encrypt the buffer in-place. */ crypto_aesctr_buf(k->k_aes, k->pnum, obuf, obuf, PCRYPT_MAXDSZ + 4); /* Append an HMAC. */ be64enc(pnum_exp, k->pnum); HMAC_SHA256_Init(&ctx, k->k_hmac, 32); HMAC_SHA256_Update(&ctx, obuf, PCRYPT_MAXDSZ + 4); HMAC_SHA256_Update(&ctx, pnum_exp, 8); HMAC_SHA256_Final(&obuf[PCRYPT_MAXDSZ + 4], &ctx); /* Increment packet number. */ k->pnum += 1; } /** * proto_crypt_dec(ibuf, obuf, k): * Decrypt PCRYPT_ESZ bytes from ${ibuf} using the keys in ${k}. If the data * is valid, write it into ${obuf} and return the length; otherwise, return * -1. */ ssize_t proto_crypt_dec(uint8_t ibuf[PCRYPT_ESZ], uint8_t * obuf, struct proto_keys * k) { HMAC_SHA256_CTX ctx; uint8_t hbuf[32]; uint8_t pnum_exp[8]; size_t len; /* Verify HMAC. */ be64enc(pnum_exp, k->pnum); HMAC_SHA256_Init(&ctx, k->k_hmac, 32); HMAC_SHA256_Update(&ctx, ibuf, PCRYPT_MAXDSZ + 4); HMAC_SHA256_Update(&ctx, pnum_exp, 8); HMAC_SHA256_Final(hbuf, &ctx); if (crypto_verify_bytes(hbuf, &ibuf[PCRYPT_MAXDSZ + 4], 32)) return (-1); /* Decrypt the buffer in-place. */ crypto_aesctr_buf(k->k_aes, k->pnum, ibuf, ibuf, PCRYPT_MAXDSZ + 4); /* Increment packet number. */ k->pnum += 1; /* Parse length. */ len = be32dec(&ibuf[PCRYPT_MAXDSZ]); /* Make sure nobody is being evil here... */ if ((len == 0) || (len > PCRYPT_MAXDSZ)) return (-1); /* Copy the bytes into the output buffer. */ memcpy(obuf, ibuf, len); /* Return the decrypted length. */ return ((ssize_t)len); } /** * proto_crypt_free(k): * Free the protocol key structure ${k}. */ void proto_crypt_free(struct proto_keys * k) { /* Be compatible with free(NULL). */ if (k == NULL) return; /* Free the AES key. */ crypto_aes_key_free(k->k_aes); /* Free the key structure. */ free(k); } spiped-1.6.0/proto/proto_crypt.h000644 001751 001751 00000007251 13042745752 020354 0ustar00cpercivacperciva000000 000000 #ifndef _PCRYPT_H_ #define _PCRYPT_H_ #include #include #include "crypto_dh.h" struct proto_keys; struct proto_secret; /* Size of nonce. */ #define PCRYPT_NONCE_LEN 32 /* Size of temporary MAC keys used for Diffie-Hellman parameters. */ #define PCRYPT_DHMAC_LEN 32 /* Size of private Diffie-Hellman value. */ #define PCRYPT_X_LEN CRYPTO_DH_PRIVLEN /* Size of MACed Diffie-Hellman parameter. */ #define PCRYPT_YH_LEN (CRYPTO_DH_PUBLEN + 32) /* Filename for stdin. */ #define STDIN_FILENAME "-" /** * proto_crypt_secret(filename): * Read the key file ${filename} and return a protocol secret structure. */ struct proto_secret * proto_crypt_secret(const char *); /** * proto_crypt_dhmac(K, nonce_l, nonce_r, dhmac_l, dhmac_r, decr): * Using the protocol secret ${K}, and the local and remote nonces ${nonce_l} * and ${nonce_r}, compute the local and remote diffie-hellman parameter MAC * keys ${dhmac_l} and ${dhmac_r}. If ${decr} is non-zero, "local" == "S" * and "remote" == "C"; otherwise the assignments are opposite. */ void proto_crypt_dhmac(const struct proto_secret *, const uint8_t[PCRYPT_NONCE_LEN], const uint8_t[PCRYPT_NONCE_LEN], uint8_t[PCRYPT_DHMAC_LEN], uint8_t[PCRYPT_DHMAC_LEN], int); /** * proto_crypt_dh_validate(yh_r, dhmac_r, requirepfs): * Return non-zero if the value ${yh_r} received from the remote party is not * correctly MACed using the diffie-hellman parameter MAC key ${dhmac_r}, or * if the included y value is >= the diffie-hellman group modulus, or if * ${requirepfs} is non-zero and the included y value is 1. */ int proto_crypt_dh_validate(const uint8_t[PCRYPT_YH_LEN], const uint8_t[PCRYPT_DHMAC_LEN], int); /** * proto_crypt_dh_generate(yh_l, x, dhmac_l, nopfs): * Using the MAC key ${dhmac_l}, generate the MACed diffie-hellman handshake * parameter ${yh_l}. Store the diffie-hellman private value in ${x}. If * ${nopfs} is non-zero, skip diffie-hellman generation and use y = 1. */ int proto_crypt_dh_generate(uint8_t[PCRYPT_YH_LEN], uint8_t[PCRYPT_X_LEN], const uint8_t[PCRYPT_DHMAC_LEN], int); /** * proto_crypt_mkkeys(K, nonce_l, nonce_r, yh_r, x, nopfs, decr, eh_c, eh_s): * Using the protocol secret ${K}, the local and remote nonces ${nonce_l} and * ${nonce_r}, the remote MACed diffie-hellman handshake parameter ${yh_r}, * and the local diffie-hellman secret ${x}, generate the keys ${eh_c} and * ${eh_s}. If ${nopfs} is non-zero, we are performing weak handshaking and * y_SC is set to 1 rather than being computed. If ${decr} is non-zero, * "local" == "S" and "remote" == "C"; otherwise the assignments are opposite. */ int proto_crypt_mkkeys(const struct proto_secret *, const uint8_t[PCRYPT_NONCE_LEN], const uint8_t[PCRYPT_NONCE_LEN], const uint8_t[PCRYPT_YH_LEN], const uint8_t[PCRYPT_X_LEN], int, int, struct proto_keys **, struct proto_keys **); /* Maximum size of an unencrypted packet. */ #define PCRYPT_MAXDSZ 1024 /* Size of an encrypted packet. */ #define PCRYPT_ESZ (PCRYPT_MAXDSZ + 4 /* len */ + 32 /* hmac */) /** * proto_crypt_enc(ibuf, len, obuf, k): * Encrypt ${len} bytes from ${ibuf} into PCRYPT_ESZ bytes using the keys in * ${k}, and write the result into ${obuf}. */ void proto_crypt_enc(uint8_t *, size_t, uint8_t[PCRYPT_ESZ], struct proto_keys *); /** * proto_crypt_dec(ibuf, obuf, k): * Decrypt PCRYPT_ESZ bytes from ${ibuf} using the keys in ${k}. If the data * is valid, write it into ${obuf} and return the length; otherwise, return * -1. */ ssize_t proto_crypt_dec(uint8_t[PCRYPT_ESZ], uint8_t *, struct proto_keys *); /** * proto_crypt_free(k): * Free the protocol key structure ${k}. */ void proto_crypt_free(struct proto_keys *); #endif /* !_PCRYPT_H_ */ spiped-1.6.0/proto/proto_handshake.c000644 001751 001751 00000017321 13042745752 021133 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include "crypto_entropy.h" #include "network.h" #include "proto_crypt.h" #include "proto_handshake.h" struct handshake_cookie { int (* callback)(void *, struct proto_keys *, struct proto_keys *); void * cookie; int s; int decr; int nopfs; int requirepfs; const struct proto_secret * K; uint8_t nonce_local[PCRYPT_NONCE_LEN]; uint8_t nonce_remote[PCRYPT_NONCE_LEN]; uint8_t dhmac_local[PCRYPT_DHMAC_LEN]; uint8_t dhmac_remote[PCRYPT_DHMAC_LEN]; uint8_t x[PCRYPT_X_LEN]; uint8_t yh_local[PCRYPT_YH_LEN]; uint8_t yh_remote[PCRYPT_YH_LEN]; void * read_cookie; void * write_cookie; }; static int callback_nonce_write(void *, ssize_t); static int callback_nonce_read(void *, ssize_t); static int gotnonces(struct handshake_cookie *); static int dhread(struct handshake_cookie *); static int callback_dh_read(void *, ssize_t); static int dhwrite(struct handshake_cookie *); static int callback_dh_write(void *, ssize_t); static int handshakedone(struct handshake_cookie *); /* The handshake failed. Call back and clean up. */ static int handshakefail(struct handshake_cookie * H) { int rc; /* Cancel any pending network read or write. */ if (H->read_cookie != NULL) network_read_cancel(H->read_cookie); if (H->write_cookie != NULL) network_write_cancel(H->write_cookie); /* Perform the callback. */ rc = (H->callback)(H->cookie, NULL, NULL); /* Free the cookie. */ free(H); /* Return status from callback. */ return (rc); } /** * proto_handshake(s, decr, nopfs, requirepfs, K, callback, cookie): * Perform a protocol handshake on socket ${s}. If ${decr} is non-zero we are * at the receiving end of the connection; otherwise at the sending end. If * ${nopfs} is non-zero, perform a "weak" handshake without perfect forward * secrecy. If ${requirepfs} is non-zero, drop the connection if the other * end attempts to perform a "weak" handshake. The shared protocol secret is * ${K}. Upon completion, invoke ${callback}(${cookie}, f, r), where f * contains the keys needed for the forward direction and r contains the keys * needed for the reverse direction; or f = r = NULL if the handshake failed. * Return a cookie which can be passed to proto_handshake_cancel to cancel the * handshake. */ void * proto_handshake(int s, int decr, int nopfs, int requirepfs, const struct proto_secret * K, int (* callback)(void *, struct proto_keys *, struct proto_keys *), void * cookie) { struct handshake_cookie * H; /* Bake a cookie. */ if ((H = malloc(sizeof(struct handshake_cookie))) == NULL) goto err0; H->callback = callback; H->cookie = cookie; H->s = s; H->decr = decr; H->nopfs = nopfs; H->requirepfs = requirepfs; H->K = K; /* Generate a 32-byte connection nonce. */ if (crypto_entropy_read(H->nonce_local, 32)) goto err1; /* Send our nonce. */ if ((H->write_cookie = network_write(s, H->nonce_local, 32, 32, callback_nonce_write, H)) == NULL) goto err1; /* Read the other party's nonce. */ if ((H->read_cookie = network_read(s, H->nonce_remote, 32, 32, callback_nonce_read, H)) == NULL) goto err2; /* Success! */ return (H); err2: network_write_cancel(H->write_cookie); err1: free(H); err0: /* Failure! */ return (NULL); } /* We've written our nonce. */ static int callback_nonce_write(void * cookie, ssize_t len) { struct handshake_cookie * H = cookie; /* This write is no longer pending. */ H->write_cookie = NULL; /* Did we successfully write? */ if (len < 32) return (handshakefail(H)); /* If the nonce read is also done, move on to the next step. */ if (H->read_cookie == NULL) return (gotnonces(H)); /* Nothing to do. */ return (0); } /* We've read a nonce. */ static int callback_nonce_read(void * cookie, ssize_t len) { struct handshake_cookie * H = cookie; /* This read is no longer pending. */ H->read_cookie = NULL; /* Did we successfully read? */ if (len < 32) return (handshakefail(H)); /* If the nonce write is also done, move on to the next step. */ if (H->write_cookie == NULL) return (gotnonces(H)); /* Nothing to do. */ return (0); } /* We have two nonces. Start the DH exchange. */ static int gotnonces(struct handshake_cookie * H) { /* Compute the diffie-hellman parameter MAC keys. */ proto_crypt_dhmac(H->K, H->nonce_local, H->nonce_remote, H->dhmac_local, H->dhmac_remote, H->decr); /* * If we're the server, we need to read the client's diffie-hellman * parameter. If we're the client, we need to generate and send our * diffie-hellman parameter. */ if (H->decr) return (dhread(H)); else return (dhwrite(H)); /* NOTREACHED */ } /* Read a diffie-hellman parameter. */ static int dhread(struct handshake_cookie * H) { /* Read the remote signed diffie-hellman parameter. */ if ((H->read_cookie = network_read(H->s, H->yh_remote, PCRYPT_YH_LEN, PCRYPT_YH_LEN, callback_dh_read, H)) == NULL) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* We have read a diffie-hellman parameter. */ static int callback_dh_read(void * cookie, ssize_t len) { struct handshake_cookie * H = cookie; /* This read is no longer pending. */ H->read_cookie = NULL; /* Did we successfully read? */ if (len < PCRYPT_YH_LEN) return (handshakefail(H)); /* Is the value we read valid? */ if (proto_crypt_dh_validate(H->yh_remote, H->dhmac_remote, H->requirepfs)) return (handshakefail(H)); /* * If we're the server, we need to send our diffie-hellman parameter * next. If we're the client, move on to the final computation. */ if (H->decr) return (dhwrite(H)); else return (handshakedone(H)); /* NOTREACHED */ } /* Generate and write a diffie-hellman parameter. */ static int dhwrite(struct handshake_cookie * H) { /* Generate a signed diffie-hellman parameter. */ if (proto_crypt_dh_generate(H->yh_local, H->x, H->dhmac_local, H->nopfs)) goto err0; /* Write our signed diffie-hellman parameter. */ if ((H->write_cookie = network_write(H->s, H->yh_local, PCRYPT_YH_LEN, PCRYPT_YH_LEN, callback_dh_write, H)) == NULL) goto err0; /* Success! */ return (0); err0: /* Failure! */ return (-1); } /* We have written our diffie-hellman parameter. */ static int callback_dh_write(void * cookie, ssize_t len) { struct handshake_cookie * H = cookie; /* This write is no longer pending. */ H->write_cookie = NULL; /* Did we successfully write? */ if (len < PCRYPT_YH_LEN) return (handshakefail(H)); /* * If we're the server, move on to the final computation. If we're * the client, we need to read the server's parameter next. */ if (H->decr) return (handshakedone(H)); else return (dhread(H)); /* NOTREACHED */ } /* We've got all the bits; do the final computation and callback. */ static int handshakedone(struct handshake_cookie * H) { struct proto_keys * c; struct proto_keys * s; int rc; /* Sanity-check: There should be no callbacks in progress. */ assert(H->read_cookie == NULL); assert(H->write_cookie == NULL); /* Perform the final computation. */ if (proto_crypt_mkkeys(H->K, H->nonce_local, H->nonce_remote, H->yh_remote, H->x, H->nopfs, H->decr, &c, &s)) goto err0; /* Perform the callback. */ rc = (H->callback)(H->cookie, c, s); /* Free the cookie. */ free(H); /* Return status code from callback. */ return (rc); err0: /* Failure! */ return (-1); } /** * proto_handshake_cancel(cookie): * Cancel the handshake for which proto_handshake returned ${cookie}. */ void proto_handshake_cancel(void * cookie) { struct handshake_cookie * H = cookie; /* Cancel any in-progress network operations. */ if (H->read_cookie != NULL) network_read_cancel(H->read_cookie); if (H->write_cookie != NULL) network_write_cancel(H->write_cookie); /* Free the cookie. */ free(H); } spiped-1.6.0/proto/proto_handshake.h000644 001751 001751 00000002325 13042745752 021136 0ustar00cpercivacperciva000000 000000 #ifndef _PROTO_HANDSHAKE_H_ #define _PROTO_HANDSHAKE_H_ /* Opaque structures. */ struct proto_keys; struct proto_secret; /** * proto_handshake(s, decr, nopfs, requirepfs, K, callback, cookie): * Perform a protocol handshake on socket ${s}. If ${decr} is non-zero we are * at the receiving end of the connection; otherwise at the sending end. If * ${nopfs} is non-zero, perform a "weak" handshake without perfect forward * secrecy. If ${requirepfs} is non-zero, drop the connection if the other * end attempts to perform a "weak" handshake. The shared protocol secret is * ${K}. Upon completion, invoke ${callback}(${cookie}, f, r), where f * contains the keys needed for the forward direction and r contains the keys * needed for the reverse direction; or f = r = NULL if the handshake failed. * Return a cookie which can be passed to proto_handshake_cancel to cancel the * handshake. */ void * proto_handshake(int, int, int, int, const struct proto_secret *, int (*)(void *, struct proto_keys *, struct proto_keys *), void *); /** * proto_handshake_cancel(cookie): * Cancel the handshake for which proto_handshake returned ${cookie}. */ void proto_handshake_cancel(void *); #endif /* !_PROTO_HANDSHAKE_H_ */ spiped-1.6.0/proto/proto_pipe.c000644 001751 001751 00000010521 13042745735 020136 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include "network.h" #include "proto_crypt.h" #include "proto_pipe.h" struct pipe_cookie { int (* callback)(void *); void * cookie; int * status; int s_in; int s_out; int decr; struct proto_keys * k; uint8_t dbuf[PCRYPT_MAXDSZ]; uint8_t ebuf[PCRYPT_ESZ]; void * read_cookie; void * write_cookie; ssize_t wlen; }; static int callback_pipe_read(void *, ssize_t); static int callback_pipe_write(void *, ssize_t); /** * proto_pipe(s_in, s_out, decr, k, status, callback, cookie): * Read bytes from ${s_in} and write them to ${s_out}. If ${decr} is non-zero * then use ${k} to decrypt the bytes; otherwise use ${k} to encrypt them. * If EOF is read, set ${status} to 0, and if an error is encountered set * ${status} to -1; in either case, invoke ${callback}(${cookie}). Return a * cookie which can be passed to proto_pipe_cancel. */ void * proto_pipe(int s_in, int s_out, int decr, struct proto_keys * k, int * status, int (* callback)(void *), void * cookie) { struct pipe_cookie * P; /* Bake a cookie. */ if ((P = malloc(sizeof(struct pipe_cookie))) == NULL) goto err0; P->callback = callback; P->cookie = cookie; P->status = status; P->s_in = s_in; P->s_out = s_out; P->decr = decr; P->k = k; P->read_cookie = NULL; P->write_cookie = NULL; /* Start reading. */ if (P->decr) { if ((P->read_cookie = network_read(P->s_in, P->ebuf, PCRYPT_ESZ, PCRYPT_ESZ, callback_pipe_read, P)) == NULL) goto err1; } else { if ((P->read_cookie = network_read(P->s_in, P->dbuf, PCRYPT_MAXDSZ, 1, callback_pipe_read, P)) == NULL) goto err1; } /* Success! */ return (P); err1: free(P); err0: /* Failure! */ return (NULL); } /* Some data has been read. */ static int callback_pipe_read(void * cookie, ssize_t len) { struct pipe_cookie * P = cookie; /* This read is no longer in progress. */ P->read_cookie = NULL; /* Did we read EOF? */ if (len == 0) goto eof; /* Did the read fail? */ if (len == -1) goto fail; /* Did a packet read end prematurely? */ if ((P->decr) && (len < PCRYPT_ESZ)) goto fail; /* Encrypt or decrypt the data. */ if (P->decr) { if ((P->wlen = proto_crypt_dec(P->ebuf, P->dbuf, P->k)) == -1) goto fail; } else { proto_crypt_enc(P->dbuf, (size_t)len, P->ebuf, P->k); P->wlen = PCRYPT_ESZ; } /* Write the encrypted or decrypted data. */ if (P->decr) { if ((P->write_cookie = network_write(P->s_out, P->dbuf, (size_t)P->wlen, (size_t)P->wlen, callback_pipe_write, P)) == NULL) goto err0; } else { if ((P->write_cookie = network_write(P->s_out, P->ebuf, (size_t)P->wlen, (size_t)P->wlen, callback_pipe_write, P)) == NULL) goto err0; } /* Success! */ return (0); fail: /* Record that this connection is broken. */ *(P->status) = -1; /* Inform the upstream that our status has changed. */ return ((P->callback)(P->cookie)); eof: /* We aren't going to write any more. */ shutdown(P->s_out, SHUT_WR); /* Record that we have reached EOF. */ *(P->status) = 0; /* Inform the upstream that our status has changed. */ return ((P->callback)(P->cookie)); err0: /* Failure! */ return (-1); } static int callback_pipe_write(void * cookie, ssize_t len) { struct pipe_cookie * P = cookie; /* This write is no longer in progress. */ P->write_cookie = NULL; /* Did we fail to write everything? */ if (len < P->wlen) goto fail; /* Launch another read. */ if (P->decr) { if ((P->read_cookie = network_read(P->s_in, P->ebuf, PCRYPT_ESZ, PCRYPT_ESZ, callback_pipe_read, P)) == NULL) goto err0; } else { if ((P->read_cookie = network_read(P->s_in, P->dbuf, PCRYPT_MAXDSZ, 1, callback_pipe_read, P)) == NULL) goto err0; } /* Success! */ return (0); fail: /* Record that this connection is broken. */ *(P->status) = -1; /* Inform the upstream that our status has changed. */ return ((P->callback)(P->cookie)); err0: /* Failure! */ return (-1); } /** * proto_pipe_cancel(cookie): * Shut down the pipe created by proto_pipe for which ${cookie} was returned. */ void proto_pipe_cancel(void * cookie) { struct pipe_cookie * P = cookie; /* If a read or write is in progress, cancel it. */ if (P->read_cookie) network_read_cancel(P->read_cookie); if (P->write_cookie) network_write_cancel(P->write_cookie); /* Free the cookie. */ free(P); } spiped-1.6.0/proto/proto_pipe.h000644 001751 001751 00000001375 12530545060 020140 0ustar00cpercivacperciva000000 000000 #ifndef _PROTO_PIPE_H_ #define _PROTO_PIPE_H_ struct proto_keys; /** * proto_pipe(s_in, s_out, decr, k, status, callback, cookie): * Read bytes from ${s_in} and write them to ${s_out}. If ${decr} is non-zero * then use ${k} to decrypt the bytes; otherwise use ${k} to encrypt them. * If EOF is read, set ${status} to 0, and if an error is encountered set * ${status} to -1; in either case, invoke ${callback}(${cookie}). Return a * cookie which can be passed to proto_pipe_cancel. */ void * proto_pipe(int, int, int, struct proto_keys *, int *, int (*)(void *), void *); /** * proto_pipe_cancel(cookie): * Shut down the pipe created by proto_pipe for which ${cookie} was returned. */ void proto_pipe_cancel(void *); #endif /* !_PROTO_PIPE_H_ */ spiped-1.6.0/libcperciva/alg/000755 001751 001751 00000000000 13042745752 017475 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/cpusupport/000755 001751 001751 00000000000 13042745752 021156 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/crypto/000755 001751 001751 00000000000 13042745752 020252 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/datastruct/000755 001751 001751 00000000000 13042745752 021110 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/events/000755 001751 001751 00000000000 13074276273 020240 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/network/000755 001751 001751 00000000000 13042745735 020424 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/util/000755 001751 001751 00000000000 13076320372 017701 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/POSIX/000755 001751 001751 00000000000 13076317716 017636 5ustar00cpercivacperciva000000 000000 spiped-1.6.0/libcperciva/POSIX/README000644 001751 001751 00000000775 12655635610 020525 0ustar00cpercivacperciva000000 000000 POSIX compatibility code ------------------------ This code exists to work around some common POSIX compatibility issues. POSIX specifies that if the first line of a Makefile is ".POSIX:" then the Makefile should be processed according to POSIX rules, including with CC=c99. Further, c99 is required to understand the -lrt and -lxnet options (and ignore them if the routines they specify linkage for are already in the standard C library). Unfortunately some systems fail or one or both of these accounts. spiped-1.6.0/libcperciva/POSIX/posix-cflags.sh000755 001751 001751 00000001767 12735033123 022573 0ustar00cpercivacperciva000000 000000 # Should be sourced by `command -p sh posix-cflags.sh "$PATH"` from within a Makefile if ! [ ${PATH} = "$1" ]; then echo "WARNING: POSIX violation: $SHELL's command -p resets \$PATH" 1>&2 PATH=$1 fi FIRST=YES if ! ${CC} -D_POSIX_C_SOURCE=200809L posix-msg_nosignal.c 2>/dev/null; then [ ${FIRST} = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_MSG_NOSIGNAL" echo "WARNING: POSIX violation: not defining MSG_NOSIGNAL" 1>&2 fi if ! ${CC} -D_POSIX_C_SOURCE=200809L posix-clock_realtime.c 2>/dev/null; then [ ${FIRST} = "NO" ] && printf " "; FIRST=NO printf %s "-DPOSIXFAIL_CLOCK_REALTIME" echo "WARNING: POSIX violation: not defining CLOCK_REALTIME" 1>&2 fi if ! ${CC} -D_POSIX_C_SOURCE=200809L posix-restrict.c 2>/dev/null; then echo "WARNING: POSIX violation: ${CC} does not accept 'restrict' keyword" 1>&2 if ${CC} -D_POSIX_C_SOURCE=200809L -std=c99 posix-restrict.c 2>/dev/null; then [ ${FIRST} = "NO" ] && printf " "; FIRST=NO printf %s "-std=c99" fi fi rm -f a.out spiped-1.6.0/libcperciva/POSIX/posix-clock_realtime.c000644 001751 001751 00000000071 13042745735 024114 0ustar00cpercivacperciva000000 000000 #include int main() {return (CLOCK_REALTIME);} spiped-1.6.0/libcperciva/POSIX/posix-l.c000644 001751 001751 00000000031 13042745735 021366 0ustar00cpercivacperciva000000 000000 int main() {return (0);} spiped-1.6.0/libcperciva/POSIX/posix-l.sh000755 001751 001751 00000000730 12735033123 021554 0ustar00cpercivacperciva000000 000000 # Should be sourced by `command -p sh posix-l.sh "$PATH"` from within a Makefile. if ! [ ${PATH} = "$1" ]; then echo "WARNING: POSIX violation: $SHELL's command -p resets \$PATH" 1>&2 PATH=$1 fi FIRST=YES for LIB in rt xnet; do if ${CC} -l${LIB} posix-l.c 2>/dev/null; then if [ ${FIRST} = "NO" ]; then printf " "; fi printf "%s" "-l${LIB}"; FIRST=NO; else echo "WARNING: POSIX violation: make's CC doesn't understand -l${LIB}" 1>&2 fi rm -f a.out done spiped-1.6.0/libcperciva/POSIX/posix-msg_nosignal.c000644 001751 001751 00000000075 13042745735 023623 0ustar00cpercivacperciva000000 000000 #include int main() {return (MSG_NOSIGNAL);} spiped-1.6.0/libcperciva/POSIX/posix-restrict.c000644 001751 001751 00000000214 12735033123 022762 0ustar00cpercivacperciva000000 000000 static int foo(char * restrict x, char * restrict y) { return (x == y); } int main() { char x[10]; char y[10]; return (foo(x, y)); } spiped-1.6.0/libcperciva/util/asprintf.c000644 001751 001751 00000001426 12530545060 021673 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.0/libcperciva/util/asprintf.h000644 001751 001751 00000000505 12530545060 021675 0ustar00cpercivacperciva000000 000000 #ifndef _ASPRINTF_H_ #define _ASPRINTF_H_ /* Avoid namespace collisions with BSD/GNU asprintf. */ #ifdef asprintf #undef asprintf #endif #define asprintf libcperciva_asprintf /** * asprintf(ret, format, ...): * Do asprintf(3) like GNU and BSD do. */ int asprintf(char **, const char *, ...); #endif /* !_ASPRINTF_H_ */ spiped-1.6.0/libcperciva/util/daemonize.c000644 001751 001751 00000004541 12530545060 022021 0ustar00cpercivacperciva000000 000000 #include #include #include #include "noeintr.h" #include "warnp.h" #include "daemonize.h" /** * daemonize(spid): * Daemonize and write the process ID in decimal to a file named ${spid}. * The parent process will exit only after the child has written its pid. * On success, the child will return 0; on failure, the parent will return * -1. */ int daemonize(const char * spid) { FILE * f; int fd[2]; char dummy = 0; /* * Create a pipe for the child to notify the parent when it has * finished daemonizing. */ if (pipe(fd)) { warnp("pipe"); goto err0; } /* * Fork into the parent process (which waits for a poke and exits) * and the child process (which keeps going). */ switch (fork()) { case -1: /* Fork failed. */ warnp("fork"); goto err2; case 0: /* In child process. */ break; default: /* * In parent process. Close write end of pipe so that if the * client dies we will notice the pipe being reset. */ while (close(fd[1])) { if (errno == EINTR) continue; warnp("close"); goto err1; } do { switch (read(fd[0], &dummy, 1)) { case -1: /* Error in read. */ break; case 0: /* EOF -- the child died without poking us. */ goto err1; case 1: /* We have been poked by the child. Exit. */ _exit(0); } /* Anything other than EINTR is bad. */ if (errno != EINTR) { warnp("read"); goto err1; } } while (1); } /* Set ourselves to be a session leader. */ if (setsid() == -1) { warnp("setsid"); goto die; } /* Write out our pid file. */ if ((f = fopen(spid, "w")) == NULL) { warnp("fopen(%s)", spid); goto die; } if (fprintf(f, "%d", getpid()) < 0) { warnp("fprintf"); goto die; } if (fclose(f)) { warnp("fclose"); goto die; } /* Tell the parent to suicide. */ if (noeintr_write(fd[1], &dummy, 1) == -1) { warnp("write"); goto die; } /* Close the pipe. */ while (close(fd[0])) { if (errno == EINTR) continue; warnp("close"); goto die; } while (close(fd[1])) { if (errno == EINTR) continue; warnp("close"); goto die; } /* Success! */ return (0); err2: close(fd[1]); err1: close(fd[0]); err0: /* Failure! */ return (-1); die: /* * We're in the child and something bad happened; the parent will be * notified when we die thanks to the pipe being closed. */ _exit(0); } spiped-1.6.0/libcperciva/util/daemonize.h000644 001751 001751 00000000551 13074276273 022036 0ustar00cpercivacperciva000000 000000 #ifndef _DAEMONIZE_H_ #define _DAEMONIZE_H_ /** * daemonize(spid): * Daemonize and write the process ID in decimal to a file named ${spid}. * The parent process will exit only after the child has written its pid. * On success, the child will return 0; on failure, the parent will return * -1. */ int daemonize(const char *); #endif /* !_DAEMONIZE_H_ */ spiped-1.6.0/libcperciva/util/entropy.c000644 001751 001751 00000003006 13042745735 021553 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "warnp.h" #include "entropy.h" /** * XXX Portability * XXX We obtain random bytes from the operating system by opening * XXX /dev/urandom and reading them from that device; this works on * XXX modern UNIX-like operating systems but not on systems like * XXX win32 where there is no concept of /dev/urandom. */ /** * entropy_read(buf, buflen): * Fill the given buffer with random bytes provided by the operating system. */ int entropy_read(uint8_t * buf, size_t buflen) { int fd; ssize_t lenread; /* Sanity-check the buffer size. */ if (buflen > SSIZE_MAX) { warn0("Programmer error: " "Trying to read insane amount of random data: %zu", buflen); goto err0; } /* Open /dev/urandom. */ if ((fd = open("/dev/urandom", O_RDONLY)) == -1) { warnp("open(/dev/urandom)"); goto err0; } /* Read bytes until we have filled the buffer. */ while (buflen > 0) { if ((lenread = read(fd, buf, buflen)) == -1) { warnp("read(/dev/urandom)"); goto err1; } /* The random device should never EOF. */ if (lenread == 0) { warn0("EOF on /dev/urandom?"); goto err1; } /* We've filled a portion of the buffer. */ buf += (size_t)lenread; buflen -= (size_t)lenread; } /* Close the device. */ while (close(fd) == -1) { if (errno != EINTR) { warnp("close(/dev/urandom)"); goto err0; } } /* Success! */ return (0); err1: close(fd); err0: /* Failure! */ return (-1); } spiped-1.6.0/libcperciva/util/entropy.h000644 001751 001751 00000000405 12530545060 021546 0ustar00cpercivacperciva000000 000000 #ifndef _ENTROPY_H_ #define _ENTROPY_H_ #include #include /** * entropy_read(buf, buflen): * Fill the given buffer with random bytes provided by the operating system. */ int entropy_read(uint8_t *, size_t); #endif /* !_ENTROPY_H_ */ spiped-1.6.0/libcperciva/util/imalloc.h000644 001751 001751 00000001246 12530545060 021472 0ustar00cpercivacperciva000000 000000 #ifndef _IMALLOC_H_ #define _IMALLOC_H_ #include #include #include /** * imalloc(nrec, reclen): * Allocate ${nrec} records of length ${reclen}. Check for size_t overflow. */ static inline void * imalloc(size_t nrec, size_t reclen) { if (nrec > SIZE_MAX / reclen) { errno = ENOMEM; return (NULL); } else { return (malloc(nrec * reclen)); } } /** * IMALLOC(p, nrec, type): * Allocate ${nrec} records of type ${type} and store the pointer in ${p}. * Return non-zero on failure. */ #define IMALLOC(p, nrec, type) \ ((((p) = (type *)imalloc((nrec), sizeof(type))) == NULL) && \ ((nrec) > 0)) #endif /* !_IMALLOC_H_ */ spiped-1.6.0/libcperciva/util/insecure_memzero.c000644 001751 001751 00000000624 12530545060 023417 0ustar00cpercivacperciva000000 000000 #include #include #include "insecure_memzero.h" /* Function which does the zeroing. */ static void insecure_memzero_func(volatile void * buf, size_t len) { volatile uint8_t * _buf = buf; size_t i; for (i = 0; i < len; i++) _buf[i] = 0; } /* Pointer to memory-zeroing function. */ void (* volatile insecure_memzero_ptr)(volatile void *, size_t) = insecure_memzero_func; spiped-1.6.0/libcperciva/util/insecure_memzero.h000644 001751 001751 00000002724 12551151333 023426 0ustar00cpercivacperciva000000 000000 #ifndef _INSECURE_MEMZERO_H_ #define _INSECURE_MEMZERO_H_ #include /* Pointer to memory-zeroing function. */ extern void (* volatile insecure_memzero_ptr)(volatile void *, size_t); /** * insecure_memzero(buf, len): * Attempt to zero ${len} bytes at ${buf} in spite of optimizing compilers' * best (standards-compliant) attempts to remove the buffer-zeroing. In * particular, to avoid performing the zeroing, a compiler would need to * use optimistic devirtualization; recognize that non-volatile objects do not * need to be treated as volatile, even if they are accessed via volatile * qualified pointers; and perform link-time optimization; in addition to the * dead-code elimination which often causes buffer-zeroing to be elided. * * Note however that zeroing a buffer does not guarantee that the data held * in the buffer is not stored elsewhere; in particular, there may be copies * held in CPU registers or in anonymous allocations on the stack, even if * every named variable is successfully sanitized. Solving the "wipe data * from the system" problem will require a C language extension which does not * yet exist. * * For more information, see: * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html * http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html */ static inline void insecure_memzero(volatile void * buf, size_t len) { (insecure_memzero_ptr)(buf, len); } #endif /* !_INSECURE_MEMZERO_H_ */ spiped-1.6.0/libcperciva/util/monoclock.c000644 001751 001751 00000002063 12735033123 022026 0ustar00cpercivacperciva000000 000000 #include #include #include #include "warnp.h" #include "monoclock.h" /** * monoclock_get(tv): * Store the current time in ${tv}. If CLOCK_MONOTONIC is available, use * that clock; if CLOCK_MONOTONIC is unavailable, use CLOCK_REALTIME (if * available) or gettimeofday(2). */ int monoclock_get(struct timeval * tv) { #if defined(CLOCK_MONOTONIC) || !defined(POSIXFAIL_CLOCK_REALTIME) struct timespec tp; #endif #ifdef CLOCK_MONOTONIC if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { tv->tv_sec = tp.tv_sec; tv->tv_usec = tp.tv_nsec / 1000; } else if ((errno != ENOSYS) && (errno != EINVAL)) { warnp("clock_gettime(CLOCK_MONOTONIC)"); goto err0; } else #endif #ifndef POSIXFAIL_CLOCK_REALTIME if (clock_gettime(CLOCK_REALTIME, &tp) == 0) { tv->tv_sec = tp.tv_sec; tv->tv_usec = tp.tv_nsec / 1000; } else { warnp("clock_gettime(CLOCK_REALTIME)"); goto err0; } #else if (gettimeofday(tv, NULL)) { warnp("gettimeofday"); goto err0; } #endif /* Success! */ return (0); err0: /* Failure! */ return (-1); } spiped-1.6.0/libcperciva/util/monoclock.h000644 001751 001751 00000000531 12530545060 022032 0ustar00cpercivacperciva000000 000000 #ifndef _MONOCLOCK_H_ #define _MONOCLOCK_H_ #include /** * monoclock_get(tv): * Store the current time in ${tv}. If CLOCK_MONOTONIC is available, use * that clock; if CLOCK_MONOTONIC is unavailable, use CLOCK_REALTIME (if * available) or gettimeofday(2). */ int monoclock_get(struct timeval *); #endif /* !_MONOCLOCK_H_ */ spiped-1.6.0/libcperciva/util/noeintr.c000644 001751 001751 00000002212 13042745735 021527 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "noeintr.h" /** * noeintr_write(d, buf, nbytes): * Write ${nbytes} bytes of data from ${buf} to the file descriptor ${d} per * the write(2) system call, but looping until completion if interrupted by * a signal. Return ${nbytes} on success or -1 on error. */ ssize_t noeintr_write(int d, const void * buf, size_t nbyte) { const uint8_t * p = buf; size_t len = nbyte; ssize_t lenwrit; /* Implementation-defined: Don't allow oversized writes. */ assert(nbyte <= SSIZE_MAX); /* Loop until we have no data left to write. */ while (len > 0) { if ((lenwrit = write(d, p, len)) == -1) { /* EINTR is harmless. */ if (errno == EINTR) continue; /* Anything else isn't. */ goto err0; } /* Sanity check. */ assert(lenwrit >= 0); assert(lenwrit <= (ssize_t)len); /* * We might have done a partial write; advance the buffer * pointer and adjust the remaining write length. */ p += (size_t)lenwrit; len -= (size_t)lenwrit; } /* Success! */ return (ssize_t)(nbyte); err0: /* Failure! */ return (-1); } spiped-1.6.0/libcperciva/util/noeintr.h000644 001751 001751 00000000607 12530545060 021530 0ustar00cpercivacperciva000000 000000 #ifndef _NOEINTR_H_ #define _NOEINTR_H_ #include /** * noeintr_write(d, buf, nbytes): * Write ${nbytes} bytes of data from ${buf} to the file descriptor ${d} per * the write(2) system call, but looping until completion if interrupted by * a signal. Return ${nbytes} on success or -1 on error. */ ssize_t noeintr_write(int, const void *, size_t); #endif /* _NOEINTR_H_ */ spiped-1.6.0/libcperciva/util/sock.c000644 001751 001751 00000023566 13042745752 021026 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include #include #include #include #include #include "imalloc.h" #include "warnp.h" #include "sock.h" #include "sock_internal.h" /* Convert a path into a socket address. */ static struct sock_addr ** sock_resolve_unix(const char * addr) { struct sock_addr ** sas; struct sock_addr * sa; struct sockaddr_un * sa_un; /* Allocate and populate a sockaddr_un structure. */ if ((sa_un = calloc(1, sizeof(struct sockaddr_un))) == NULL) goto err0; sa_un->sun_family = AF_UNIX; if (strlen(addr) >= sizeof(sa_un->sun_path)) { warn0("socket path too long: %s", addr); goto err1; } strcpy(sa_un->sun_path, addr); /* Allocate and populate our wrapper. */ if ((sa = malloc(sizeof(struct sock_addr))) == NULL) goto err1; sa->ai_family = AF_UNIX; sa->ai_socktype = SOCK_STREAM; sa->name = (struct sockaddr *)sa_un; sa->namelen = sizeof(struct sockaddr_un); /* Allocate and populate an array of pointers. */ if ((sas = malloc(2 * sizeof(struct sock_addr *))) == NULL) goto err2; sas[0] = sa; sas[1] = NULL; /* Success! */ return (sas); err2: free(sa); err1: free(sa_un); err0: /* Failure! */ return (NULL); } /* Resolve a host into a list of socket addresses. */ static struct sock_addr ** sock_resolve_host(const char * addr, const char * ports) { struct addrinfo hints; struct addrinfo * res; struct addrinfo * r; struct sock_addr ** sas; size_t n; int error; /* Create hints structure. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; /* Perform DNS lookup. */ if ((error = getaddrinfo(addr, ports, &hints, &res)) != 0) { warn0("Error looking up %s: %s", addr, gai_strerror(error)); goto err0; } /* Count addresses returned. */ for (n = 0, r = res; r != NULL; r = r->ai_next) n++; /* Allocate our response array. */ if (IMALLOC(sas, n + 1, struct sock_addr *)) goto err1; /* Create address structures. */ for (n = 0, r = res; r != NULL; n++, r = r->ai_next) { /* Allocate a structure. */ if ((sas[n] = malloc(sizeof(struct sock_addr))) == NULL) goto err2; /* Copy in the address metadata. */ sas[n]->ai_family = r->ai_family; sas[n]->ai_socktype = r->ai_socktype; sas[n]->namelen = r->ai_addrlen; /* Duplicate the address. */ if ((sas[n]->name = malloc(sas[n]->namelen)) == NULL) goto err3; memcpy(sas[n]->name, r->ai_addr, sas[n]->namelen); } /* Terminate array with a NULL. */ sas[n] = NULL; /* Free the linked list of addresses returned by getaddrinfo. */ freeaddrinfo(res); /* Success! */ return (sas); err3: free(sas[n]); err2: for (; n > 0; n--) sock_addr_free(sas[n - 1]); free(sas); err1: freeaddrinfo(res); err0: /* Failure! */ return (NULL); } /* Parse an IPv6 address into a socket address. */ static struct sock_addr ** sock_resolve_ipv6(const char * addr, in_port_t p) { struct sock_addr ** sas; struct sock_addr * sa; struct sockaddr_in6 * sin6; /* Allocate and populate a sockaddr_in6 structure. */ if ((sin6 = calloc(1, sizeof(struct sockaddr_in6))) == NULL) goto err0; sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(p); if (inet_pton(AF_INET6, addr, &sin6->sin6_addr) != 1) { warn0("Error parsing IP address: %s", addr); goto err1; } /* Allocate and populate our wrapper. */ if ((sa = malloc(sizeof(struct sock_addr))) == NULL) goto err1; sa->ai_family = AF_INET6; sa->ai_socktype = SOCK_STREAM; sa->name = (struct sockaddr *)sin6; sa->namelen = sizeof(struct sockaddr_in6); /* Allocate and populate an array of pointers. */ if ((sas = malloc(2 * sizeof(struct sock_addr *))) == NULL) goto err2; sas[0] = sa; sas[1] = NULL; /* Success! */ return (sas); err2: free(sa); err1: free(sin6); err0: /* Failure! */ return (NULL); } /* Parse an IPv4 address into a socket address. */ static struct sock_addr ** sock_resolve_ipv4(const char * addr, in_port_t p) { struct sock_addr ** sas; struct sock_addr * sa; struct sockaddr_in * sin; /* Allocate and populate a sockaddr_in structure. */ if ((sin = calloc(1, sizeof(struct sockaddr_in))) == NULL) goto err0; sin->sin_family = AF_INET; sin->sin_port = htons(p); if (inet_pton(AF_INET, addr, &sin->sin_addr) != 1) { warn0("Error parsing IP address: %s", addr); goto err1; } /* Allocate and populate our wrapper. */ if ((sa = malloc(sizeof(struct sock_addr))) == NULL) goto err1; sa->ai_family = AF_INET; sa->ai_socktype = SOCK_STREAM; sa->name = (struct sockaddr *)sin; sa->namelen = sizeof(struct sockaddr_in); /* Allocate and populate an array of pointers. */ if ((sas = malloc(2 * sizeof(struct sock_addr *))) == NULL) goto err2; sas[0] = sa; sas[1] = NULL; /* Success! */ return (sas); err2: free(sa); err1: free(sin); err0: /* Failure! */ return (NULL); } /** * sock_resolve(addr): * Return a NULL-terminated array of pointers to sock_addr structures. */ struct sock_addr ** sock_resolve(const char * addr) { struct sock_addr ** res; char * s; char * ports; char * ips; long p; /* If the address starts with '/', it's a Unix domain socket. */ if (addr[0] == '/') { res = sock_resolve_unix(addr); goto done0; } /* Copy the address so that we can mangle it. */ if ((s = strdup(addr)) == NULL) goto err0; /* The address should end with :port. Look for the last ':'. */ if ((ports = strrchr(s, ':')) == NULL) { warn0("Address must contain port number: %s", s); goto err1; } *ports++ = '\0'; /* If the address doesn't start with '[', it's a host name. */ if (s[0] != '[') { res = sock_resolve_host(s, ports); goto done1; } /* The address (sans :port) should end with ']'. */ if (s[strlen(s) - 1] != ']') { warn0("Invalid [IP address]: %s", s); goto err1; } /* Extract the IP address string. */ ips = &s[1]; ips[strlen(ips) - 1] = '\0'; /* * Parse the port number. If strtol fails to parse the port number, * it will return 0; but that's fine since port 0 is invalid anyway. */ p = strtol(ports, NULL, 10); if ((p <= 0) || (p >= 65536)) { warn0("Invalid port number: %s", ports); goto err1; } /* If the IP address contains ':', it's IPv6; otherwise, IPv4. */ if (strchr(ips, ':') != NULL) res = sock_resolve_ipv6(ips, (in_port_t)p); else res = sock_resolve_ipv4(ips, (in_port_t)p); done1: /* Free string allocated by strdup. */ free(s); done0: /* Return result from sock_resolve_foo. */ return (res); err1: free(s); err0: /* Failure! */ return (NULL); } /** * sock_listener(sa): * Create a socket, set SO_REUSEADDR, bind it to the socket address ${sa}, * mark it for listening, and mark it as non-blocking. */ int sock_listener(const struct sock_addr * sa) { int s; int val = 1; /* Create a socket. */ if ((s = socket(sa->ai_family, sa->ai_socktype, 0)) == -1) { warnp("socket(%d, %d)", sa->ai_family, sa->ai_socktype); goto err0; } /* Set SO_REUSEADDR. */ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { warnp("setsockopt(SO_REUSEADDR)"); goto err1; } /* Bind the socket. */ if (bind(s, sa->name, sa->namelen)) { warnp("Error binding socket"); goto err1; } /* Mark the socket as listening. */ if (listen(s, 10)) { warnp("Error marking socket as listening"); goto err1; } /* Mark the socket as non-blocking. */ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { warnp("Error marking socket as non-blocking"); goto err1; } /* Success! */ return (s); err1: close(s); err0: /* Failure! */ return (-1); } /** * sock_connect(sas): * Iterate through the addresses in ${sas}, attempting to create a socket and * connect (blockingly). Once connected, stop iterating, mark the socket as * non-blocking, and return it. */ int sock_connect(struct sock_addr * const * sas) { int s = -1; /* Iterate through the addresses provided. */ for (; sas[0] != NULL; sas++) { /* Create a socket. */ if ((s = socket(sas[0]->ai_family, sas[0]->ai_socktype, 0)) == -1) continue; /* Attempt to connect. */ if (connect(s, sas[0]->name, sas[0]->namelen) == 0) break; /* Close the socket; this address didn't work. */ close(s); } /* Did we manage to connect? */ if (sas[0] == NULL) { warn0("Could not connect"); goto err0; } /* Mark the socket as non-blocking. */ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { warnp("Cannot make connection non-blocking"); goto err1; } /* Success! */ return (s); err1: close(s); err0: /* Failure! */ return (-1); } /** * sock_connect_nb(sa): * Create a socket, mark it as non-blocking, and attempt to connect to the * address ${sa}. Return the socket (connected or in the process of * connecting) or -1 on error. */ int sock_connect_nb(const struct sock_addr * sa) { int s; /* Create a socket. */ if ((s = socket(sa->ai_family, sa->ai_socktype, 0)) == -1) goto err0; /* Mark the socket as non-blocking. */ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { warnp("Cannot make socket non-blocking"); goto err1; } /* Attempt to connect. */ if ((connect(s, sa->name, sa->namelen) == -1) && (errno != EINPROGRESS) && (errno != EINTR)) goto err1; /* We have a connect(ed|ing) socket. */ return (s); err1: close(s); err0: /* We failed to connect to this address. */ return (-1); } /** * sock_addr_free(sa): * Free the provided sock_addr structure. */ void sock_addr_free(struct sock_addr * sa) { /* Behave consistently with free(NULL). */ if (sa == NULL) return; /* Free the protocol-specific address structure and our struct. */ free(sa->name); free(sa); } /** * sock_addr_freelist(sas): * Free the provided NULL-terminated array of sock_addr structures. */ void sock_addr_freelist(struct sock_addr ** sas) { struct sock_addr ** p; /* Behave consistently with free(NULL). */ if (sas == NULL) return; /* Free structures until we hit NULL. */ for (p = sas; *p != NULL; p++) sock_addr_free(*p); /* Free the list. */ free(sas); } spiped-1.6.0/libcperciva/util/sock.h000644 001751 001751 00000002564 12530545060 021015 0ustar00cpercivacperciva000000 000000 #ifndef _SOCK_H_ #define _SOCK_H_ /** * Address strings are of the following forms: * /path/to/unix/socket * [ip.v4.ad.dr]:port * [ipv6:add::ress]:port * host.name:port */ /* Opaque address structure. */ struct sock_addr; /** * sock_resolve(addr): * Return a NULL-terminated array of pointers to sock_addr structures. */ struct sock_addr ** sock_resolve(const char *); /** * sock_listener(sa): * Create a socket, set SO_REUSEADDR, bind it to the socket address ${sa}, * mark it for listening, and mark it as non-blocking. */ int sock_listener(const struct sock_addr *); /** * sock_connect(sas): * Iterate through the addresses in ${sas}, attempting to create a socket and * connect (blockingly). Once connected, stop iterating, mark the socket as * non-blocking, and return it. */ int sock_connect(struct sock_addr * const *); /** * sock_connect_nb(sa): * Create a socket, mark it as non-blocking, and attempt to connect to the * address ${sa}. Return the socket (connected or in the process of * connecting) or -1 on error. */ int sock_connect_nb(const struct sock_addr *); /** * sock_addr_free(sa): * Free the provided sock_addr structure. */ void sock_addr_free(struct sock_addr *); /** * sock_addr_freelist(sas): * Free the provided NULL-terminated array of sock_addr structures. */ void sock_addr_freelist(struct sock_addr **); #endif /* !_SOCK_H_ */ spiped-1.6.0/libcperciva/util/sock_internal.h000644 001751 001751 00000000364 12530545060 022705 0ustar00cpercivacperciva000000 000000 #ifndef _SOCK_INTERNAL_H_ #define _SOCK_INTERNAL_H_ #include /* Socket address structure. */ struct sock_addr { int ai_family; int ai_socktype; struct sockaddr * name; socklen_t namelen; }; #endif /* !_SOCK_INTERNAL_H_ */ spiped-1.6.0/libcperciva/util/sock_util.c000644 001751 001751 00000014021 12613533553 022042 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include #include "asprintf.h" #include "sock.h" #include "sock_internal.h" #include "sock_util.h" /** * sock_addr_cmp(sa1, sa2): * Return non-zero iff the socket addresses ${sa1} and ${sa2} are different. */ int sock_addr_cmp(const struct sock_addr * sa1, const struct sock_addr * sa2) { /* Family, socket type, and name length must match. */ if ((sa1->ai_family != sa2->ai_family) || (sa1->ai_socktype != sa2->ai_socktype) || (sa1->namelen != sa2->namelen)) return (1); /* The required length of the sockaddr must match. */ if (memcmp(sa1->name, sa2->name, sa1->namelen) != 0) return (1); /* Everything matched. */ return (0); } /** * sock_addr_dup(sa): * Duplicate the provided socket address. */ struct sock_addr * sock_addr_dup(const struct sock_addr * sa) { struct sock_addr * sa2; /* Allocate a struct sock_addr and copy fields. */ if ((sa2 = malloc(sizeof(struct sock_addr))) == NULL) goto err0; sa2->ai_family = sa->ai_family; sa2->ai_socktype = sa->ai_socktype; sa2->namelen = sa->namelen; /* Allocate and copy the sockaddr. */ if ((sa2->name = malloc(sa2->namelen)) == NULL) goto err1; memcpy(sa2->name, sa->name, sa2->namelen); /* Success! */ return (sa2); err1: free(sa2); err0: /* Failure! */ return (NULL); } /** * sock_addr_duplist(sas): * Duplicate the provided list of socket addresses. */ struct sock_addr ** sock_addr_duplist(struct sock_addr * const * sas) { struct sock_addr ** sas2; size_t i; /* Count socket addresses. */ for (i = 0; sas[i] != NULL; i++) continue; /* Allocate the list to hold addresses plus a NULL terminator. */ if ((sas2 = malloc((i + 1) * sizeof(struct sock_addr *))) == NULL) goto err0; /* Duplicate addresses and NULL-terminate. */ for (i = 0; sas[i] != NULL; i++) { if ((sas2[i] = sock_addr_dup(sas[i])) == NULL) goto err1; } sas2[i] = NULL; /* Success! */ return (sas2); err1: /* * Regardless of how many addresses we managed to duplicate before * failing and being sent here, we have a valid socket address list, * since the erroring sock_addr_dup call NULL-terminated it for us; * so we can free it and its constituent addresses easily. */ sock_addr_freelist(sas2); err0: /* Failure! */ return (NULL); } /** * sock_addr_serialize(sa, buf, buflen): * Allocate a buffer and serialize the socket address ${sa} into it. Return * the buffer via ${buf} and its length via ${buflen}. The serialization is * machine and operating system dependent. */ int sock_addr_serialize(const struct sock_addr * sa, uint8_t ** buf, size_t * buflen) { uint8_t * p; /* Compute buffer length and allocate buffer. */ *buflen = 2 * sizeof(int) + sizeof(socklen_t) + sa->namelen; if ((p = *buf = malloc(*buflen)) == NULL) goto err0; /* Copy in data. */ memcpy(p, &sa->ai_family, sizeof(int)); p += sizeof(int); memcpy(p, &sa->ai_socktype, sizeof(int)); p += sizeof(int); memcpy(p, &sa->namelen, sizeof(socklen_t)); p += sizeof(socklen_t); memcpy(p, sa->name, sa->namelen); /* Success! */ return (0); err0: /* Failure! */ return (-1); } /** * sock_addr_deserialize(buf, buflen): * Deserialize the ${buflen}-byte serialized socket address from ${buf}. */ struct sock_addr * sock_addr_deserialize(const uint8_t * buf, size_t buflen) { struct sock_addr * sa; /* Sanity check. */ if (buflen < 2 * sizeof(int) + sizeof(socklen_t)) goto err0; /* Allocate a structure and copy in fields. */ if ((sa = malloc(sizeof(struct sock_addr))) == NULL) goto err0; memcpy(&sa->ai_family, buf, sizeof(int)); buf += sizeof(int); memcpy(&sa->ai_socktype, buf, sizeof(int)); buf += sizeof(int); memcpy(&sa->namelen, buf, sizeof(socklen_t)); buf += sizeof(socklen_t); /* Allocate and copy the sockaddr. */ if (buflen != 2 * sizeof(int) + sizeof(socklen_t) + sa->namelen) goto err1; if ((sa->name = malloc(sa->namelen)) == NULL) goto err1; memcpy(sa->name, buf, sa->namelen); /* Success! */ return (sa); err1: free(sa); err0: /* Failure! */ return (NULL); } /* Prettyprint an IPv4 address. */ static char * prettyprint_ipv4(struct sockaddr * name, size_t namelen) { struct sockaddr_in sa_in; char addr[INET_ADDRSTRLEN]; char * s; /* Check name length. */ if (namelen != sizeof(struct sockaddr_in)) return (NULL); /* Copy into buffer for alignment. */ memcpy(&sa_in, name, namelen); /* Convert IP address to string. */ if (inet_ntop(AF_INET, &sa_in.sin_addr, addr, sizeof(addr)) == NULL) return (NULL); /* Construct address string. */ if (asprintf(&s, "[%s]:%d", addr, ntohs(sa_in.sin_port)) == -1) return (NULL); /* Success! */ return (s); } /* Prettyprint an IPv6 address. */ static char * prettyprint_ipv6(struct sockaddr * name, size_t namelen) { struct sockaddr_in6 sa_in6; char addr[INET6_ADDRSTRLEN]; char * s; /* Check name length. */ if (namelen != sizeof(struct sockaddr_in6)) return (NULL); /* Copy into buffer for alignment. */ memcpy(&sa_in6, name, namelen); /* Convert IPv6 address to string. */ if (inet_ntop(AF_INET6, &sa_in6.sin6_addr, addr, sizeof(addr)) == NULL) return (NULL); /* Construct address string. */ if (asprintf(&s, "[%s]:%d", addr, ntohs(sa_in6.sin6_port)) == -1) return (NULL); /* Success! */ return (s); } /* Prettyprint a UNIX address. */ static char * prettyprint_unix(struct sockaddr_un * name) { /* Just strdup the path. */ return (strdup(name->sun_path)); } /** * sock_addr_prettyprint(sa): * Allocate and return a string in one of the forms * /path/to/unix/socket * [ip.v4.ad.dr]:port * [ipv6:add::ress]:port * representing the provided socket address. */ char * sock_addr_prettyprint(const struct sock_addr * sa) { /* Handle different types of addresses differently. */ switch (sa->ai_family) { case AF_INET: return (prettyprint_ipv4(sa->name, sa->namelen)); case AF_INET6: return (prettyprint_ipv6(sa->name, sa->namelen)); case AF_UNIX: return (prettyprint_unix((struct sockaddr_un *)(sa->name))); default: return (strdup("Unknown address")); } } spiped-1.6.0/libcperciva/util/sock_util.h000644 001751 001751 00000002574 12735033123 022052 0ustar00cpercivacperciva000000 000000 #ifndef _SOCK_UTIL_H_ #define _SOCK_UTIL_H_ #include #include /* Opaque address structure. */ struct sock_addr; /** * sock_addr_cmp(sa1, sa2): * Return non-zero iff the socket addresses ${sa1} and ${sa2} are different. */ int sock_addr_cmp(const struct sock_addr *, const struct sock_addr *); /** * sock_addr_dup(sa): * Duplicate the provided socket address. */ struct sock_addr * sock_addr_dup(const struct sock_addr *); /** * sock_addr_duplist(sas): * Duplicate the provided list of socket addresses. */ struct sock_addr ** sock_addr_duplist(struct sock_addr * const *); /** * sock_addr_serialize(sa, buf, buflen): * Allocate a buffer and serialize the socket address ${sa} into it. Return * the buffer via ${buf} and its length via ${buflen}. The serialization is * machine and operating system dependent. */ int sock_addr_serialize(const struct sock_addr *, uint8_t **, size_t *); /** * sock_addr_deserialize(buf, buflen): * Deserialize the ${buflen}-byte serialized socket address from ${buf}. */ struct sock_addr * sock_addr_deserialize(const uint8_t *, size_t); /** * sock_addr_prettyprint(sa): * Allocate and return a string in one of the forms * /path/to/unix/socket * [ip.v4.ad.dr]:port * [ipv6:add::ress]:port * representing the provided socket address. */ char * sock_addr_prettyprint(const struct sock_addr *); #endif /* !_SOCK_H_ */ spiped-1.6.0/libcperciva/util/sysendian.h000644 001751 001751 00000006137 12735033123 022052 0ustar00cpercivacperciva000000 000000 #ifndef _SYSENDIAN_H_ #define _SYSENDIAN_H_ #include /* Avoid namespace collisions with BSD . */ #define be16dec libcperciva_be16dec #define be16enc libcperciva_be16enc #define be32dec libcperciva_be32dec #define be32enc libcperciva_be32enc #define be64dec libcperciva_be64dec #define be64enc libcperciva_be64enc #define le16dec libcperciva_le16dec #define le16enc libcperciva_le16enc #define le32dec libcperciva_le32dec #define le32enc libcperciva_le32enc #define le64dec libcperciva_le64dec #define le64enc libcperciva_le64enc static inline uint16_t be16dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; return (uint16_t)((uint16_t)(p[1]) + ((uint16_t)(p[0]) << 8)); } static inline void be16enc(void * pp, uint16_t x) { uint8_t * p = (uint8_t *)pp; p[1] = x & 0xff; p[0] = (x >> 8) & 0xff; } static inline uint32_t be32dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); } static inline void be32enc(void * pp, uint32_t x) { uint8_t * p = (uint8_t *)pp; p[3] = x & 0xff; p[2] = (x >> 8) & 0xff; p[1] = (x >> 16) & 0xff; p[0] = (x >> 24) & 0xff; } static inline uint64_t be64dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); } static inline void be64enc(void * pp, uint64_t x) { uint8_t * p = (uint8_t *)pp; p[7] = x & 0xff; p[6] = (x >> 8) & 0xff; p[5] = (x >> 16) & 0xff; p[4] = (x >> 24) & 0xff; p[3] = (x >> 32) & 0xff; p[2] = (x >> 40) & 0xff; p[1] = (x >> 48) & 0xff; p[0] = (x >> 56) & 0xff; } static inline uint16_t le16dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; return (uint16_t)((uint16_t)(p[0]) + ((uint16_t)(p[1]) << 8)); } static inline void le16enc(void * pp, uint16_t x) { uint8_t * p = (uint8_t *)pp; p[0] = x & 0xff; p[1] = (x >> 8) & 0xff; } static inline uint32_t le32dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); } static inline void le32enc(void * pp, uint32_t x) { uint8_t * p = (uint8_t *)pp; p[0] = x & 0xff; p[1] = (x >> 8) & 0xff; p[2] = (x >> 16) & 0xff; p[3] = (x >> 24) & 0xff; } static inline uint64_t le64dec(const void * pp) { const uint8_t * p = (uint8_t const *)pp; return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); } static inline void le64enc(void * pp, uint64_t x) { uint8_t * p = (uint8_t *)pp; p[0] = x & 0xff; p[1] = (x >> 8) & 0xff; p[2] = (x >> 16) & 0xff; p[3] = (x >> 24) & 0xff; p[4] = (x >> 32) & 0xff; p[5] = (x >> 40) & 0xff; p[6] = (x >> 48) & 0xff; p[7] = (x >> 56) & 0xff; } #endif /* !_SYSENDIAN_H_ */ spiped-1.6.0/libcperciva/util/warnp.c000644 001751 001751 00000002477 12530545060 021203 0ustar00cpercivacperciva000000 000000 #include #include #include #include #include #include "warnp.h" static int initialized = 0; static char * name = NULL; /* Free the name string. */ static void done(void) { free(name); name = NULL; } /** * warnp_setprogname(progname): * Set the program name to be used by warn() and warnx() to ${progname}. */ void warnp_setprogname(const char * progname) { const char * p; /* Free the name if we already have one. */ free(name); /* Find the last segment of the program name. */ for (p = progname; progname[0] != '\0'; progname++) if (progname[0] == '/') p = progname + 1; /* Copy the name string. */ name = strdup(p); /* If we haven't already done so, register our exit handler. */ if (initialized == 0) { atexit(done); initialized = 1; } } void warn(const char * fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)"); if (fmt != NULL) { fprintf(stderr, ": "); vfprintf(stderr, fmt, ap); } fprintf(stderr, ": %s\n", strerror(errno)); va_end(ap); } void warnx(const char * fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)"); if (fmt != NULL) { fprintf(stderr, ": "); vfprintf(stderr, fmt, ap); } fprintf(stderr, "\n"); va_end(ap); } spiped-1.6.0/libcperciva/util/warnp.h000644 001751 001751 00000002551 12735033123 021200 0ustar00cpercivacperciva000000 000000 #ifndef _WARNP_H_ #define _WARNP_H_ #include #include /* Avoid namespace collisions with BSD . */ #define warn libcperciva_warn #define warnx libcperciva_warnx /** * warnp_setprogname(progname): * Set the program name to be used by warn() and warnx() to ${progname}. */ void warnp_setprogname(const char *); #define WARNP_INIT do { \ if (argv[0] != NULL) \ warnp_setprogname(argv[0]); \ } while (0) /* As in BSD . */ void warn(const char *, ...); void warnx(const char *, ...); /* * If compiled with DEBUG defined, print __FILE__ and __LINE__. */ #ifdef DEBUG #define warnline do { \ warnx("%s, %d", __FILE__, __LINE__); \ } while (0) #else #define warnline #endif /* * Call warn(3) or warnx(3) depending upon whether errno == 0; and clear * errno (so that the standard error message isn't repeated later). */ #define warnp(...) do { \ warnline; \ if (errno != 0) { \ warn(__VA_ARGS__); \ errno = 0; \ } else \ warnx(__VA_ARGS__); \ } while (0) /* * Call warnx(3) and set errno == 0. Unlike warnp, this should be used * in cases where we're reporting a problem which we discover ourselves * rather than one which is reported to us from a library or the kernel. */ #define warn0(...) do { \ warnline; \ warnx(__VA_ARGS__); \ errno = 0; \ } while (0) #endif /* !_WARNP_H_ */ spiped-1.6.0/libcperciva/util/getopt.c000644 001751 001751 00000020031 12735033123 021337 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; static size_t nopts; 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